@ -1,124 +0,0 @@ | |||
%%%----------------------------------- | |||
%%% @Author : gaomeng 595088880@qq.com | |||
%%% @Created : 2017/10/17 | |||
%%% @Doc : APP启动相关 | |||
%%%----------------------------------- | |||
-module(config). | |||
-include("common.hrl"). | |||
-define(APP, server). | |||
-export([ | |||
config/1 | |||
, server_ids/0 | |||
, listen_port/0 | |||
, get_ticket/0 | |||
, get_enter_ticket/0 | |||
, get_platform/0 | |||
, get_create_max_num/0 | |||
, is_gm_open/0 | |||
, set_gm_open/1 | |||
, get_white_ip/0 | |||
, add_white_ip/1 | |||
, del_white_ip/1 | |||
, open_date/0 | |||
, merge_date/0 | |||
, set_open_date/1 | |||
, set_merge_date/1 | |||
, print_cmd/0 | |||
, set_print_cmd/1 | |||
, version/0 | |||
, is_https/0 | |||
]). | |||
%% @doc 是否是HTTPS; return true|false | |||
is_https() -> | |||
case catch config(is_https) of | |||
{'EXIT', _} -> | |||
false; | |||
Val -> | |||
Val | |||
end. | |||
%% @doc 获取版本 | |||
version() -> | |||
case catch config(version) of | |||
{'EXIT', _} -> | |||
"未知版本"; | |||
Val -> | |||
Val | |||
end. | |||
%% @doc 获取协议打印状态 | |||
print_cmd() -> | |||
config(print_cmd). | |||
%% @doc 设置协议打印状态 | |||
set_print_cmd(Flag) -> | |||
application:set_env(?APP, print_cmd, Flag). | |||
%% @doc 设置开服时间 | |||
set_open_date(Date) -> | |||
application:set_env(?APP, open_date, Date, [{persistent, true}]). | |||
%% @doc 设置合服时间 | |||
set_merge_date(Date) -> | |||
application:set_env(?APP, merge_date, Date, [{persistent, true}]). | |||
%% @spec open_date() -> {Year, Month, Day} | |||
%%@ doc 获取开服日期 | |||
open_date() -> | |||
config(open_date). | |||
%% @spec merge_date() -> {Year, Month, Day} | |||
%%@ doc 获取合服日期 | |||
merge_date() -> | |||
config(merge_date). | |||
%% @doc 添加白名单 | |||
add_white_ip(Ip) -> | |||
List = get_white_ip(), | |||
List1 = [Ip | List], | |||
application:set_env(?APP, white_ip, List1). | |||
%% @doc 删除白名单 | |||
del_white_ip(Ip) -> | |||
List = get_white_ip(), | |||
List1 = lists:delete(Ip, List), | |||
application:set_env(?APP, white_ip, List1). | |||
%% @doc 获取白名单 | |||
get_white_ip() -> | |||
config(white_ip). | |||
set_gm_open(Flag) -> | |||
application:set_env(?APP, gm, Flag). | |||
is_gm_open() -> | |||
config(gm). | |||
get_enter_ticket() -> | |||
config(enter_ticket). | |||
get_ticket() -> | |||
config(ticket). | |||
listen_port() -> | |||
config(listen_port). | |||
server_ids() -> | |||
config(server_ids). | |||
get_platform() -> | |||
config(platform). | |||
get_create_max_num() -> | |||
config(create_max_num). | |||
%% @doc 根据KEY获取对应的值 | |||
config(Key) -> | |||
case application:get_env(?APP, Key) of | |||
{ok, Val} -> | |||
Val; | |||
_ -> | |||
throw(undefined) | |||
end. |
@ -1,202 +0,0 @@ | |||
%%%----------------------------------- | |||
%%% @Author : gaomeng 595088880@qq.com | |||
%%% @Created : 2017/1/30 | |||
%%% @Doc : 发送相关 | |||
%%%----------------------------------- | |||
-module(lib_send). | |||
-include("common.hrl"). | |||
-include("scene.hrl"). | |||
-include("move.hrl"). | |||
-include("guild.hrl"). | |||
-include_lib("stdlib/include/ms_transform.hrl"). | |||
-export([ | |||
send/2, | |||
pack_send/3, | |||
pack_send/4, | |||
pack_send_msg/2, | |||
send_to_scene/1, | |||
send_to_scene/2, | |||
async_send_scene/2, | |||
async_send_scene/3, | |||
send_to_world/1, | |||
send_to_world/2, | |||
send_to_world/3, | |||
send_to_guild/3, | |||
async_online/3, | |||
async_online/4 | |||
]). | |||
%% @doc 全服异步在线玩家执行函数 | |||
async_online(M, F, Args) -> | |||
async_online(0, M, F, Args). | |||
async_online(RoleId, M, F, Args) -> | |||
spawn(fun() -> | |||
case ets:info(?ETS_ONLINE) of | |||
undefined -> | |||
ok; | |||
_ -> | |||
L = ets:tab2list(?ETS_ONLINE), | |||
[svr_role:apply(async, Pid, {M, F, Args}) || #role{pid = Pid, id = TemRoleId} <- L, RoleId =/= TemRoleId] | |||
end | |||
end). | |||
%% @doc 公会广播 | |||
send_to_guild(GuildId, Cmd, List) when is_integer(GuildId) -> | |||
svr_guild:apply(async, GuildId, {lib_send, send_to_guild, [Cmd, List]}); | |||
send_to_guild(#guild{members = Members}, Cmd, List) -> | |||
case catch prot:encode(Cmd, List) of | |||
{ok, Bin} -> | |||
F = fun(#guild_member{role_id = RoleId}) -> | |||
case lib_account:get_role_pid(RoleId) of | |||
Pid when is_pid(Pid) -> | |||
send(Pid, Bin); | |||
_ -> | |||
ok | |||
end | |||
end, | |||
[F(M) || M <- Members]; | |||
Ret -> | |||
?ERR("Cmd:~w use pack_send, reason:~w", [Cmd, Ret]), | |||
ok | |||
end, | |||
ok. | |||
%%世界广播 | |||
send_to_world(RoleId, Bin) -> | |||
spawn(fun() -> | |||
[send(Sid, Bin) || #role{id = Id, sid = Sid} <- ets:tab2list(?ETS_ONLINE), Id =/= RoleId] | |||
end). | |||
send_to_world(Bin) -> | |||
spawn(fun() -> | |||
[send(Sid, Bin) || #role{sid = Sid} <- ets:tab2list(?ETS_ONLINE)] | |||
end). | |||
%% @doc 世界广播(有等级限制) | |||
send_to_world(level, ReqLevel, Bin) -> | |||
spawn(fun() -> | |||
[send(Sid, Bin) || #role{level = Level, sid = Sid} <- ets:tab2list(?ETS_ONLINE), Level >= ReqLevel] | |||
end); | |||
send_to_world(RoleId, Cmd, List) -> | |||
case catch prot:encode(Cmd, List) of | |||
{ok, Bin} -> | |||
spawn(fun() -> | |||
[send(Sid, Bin) || #role{id = Id, sid = Sid} <- ets:tab2list(?ETS_ONLINE), Id =/= RoleId] | |||
end); | |||
Ret -> | |||
?ERR("Cmd:~w use pack_send, reason:~w", [Cmd, Ret]), | |||
ok | |||
end. | |||
%%场景广播 | |||
send_to_scene(Rid, BinData) -> | |||
AllRole = svr_scene_role:get_all(), | |||
F = fun(Role) -> | |||
case Role#scene_role.id =/= Rid of | |||
true -> | |||
send(Role#scene_role.sid, BinData); | |||
_ -> | |||
ok | |||
end | |||
end, | |||
[F(P) || P <- AllRole]. | |||
send_to_scene(BinData) -> | |||
AllRole = svr_scene_role:get_all(), | |||
F = fun(Role) -> | |||
send(Role#scene_role.sid, BinData) | |||
end, | |||
[F(P) || P <- AllRole]. | |||
%% @doc 其他进程发送场景广播 | |||
async_send_scene(ScenePid, BinData) -> | |||
async_send_scene(ScenePid, BinData, 0). | |||
async_send_scene(ScenePid, BinData, RoleId) when is_pid(ScenePid) -> | |||
svr_scene:apply(async, ScenePid, {svr_scene_info, async_send_scene, [RoleId, BinData]}); | |||
async_send_scene(_, _, _) -> | |||
ok. | |||
%% @doc 打包 | |||
pack_send(Node, Rid = #node_rid{id = RoleId}, Cmd, List) -> | |||
case Node =:= node() of | |||
true -> | |||
pack_send(RoleId, Cmd, List); | |||
_ -> | |||
svr_kfnode:send_role(Node, [Rid], lib_send, pack_send, [Cmd, List]) | |||
end. | |||
pack_send(RoleId, Cmd, List) when is_integer(RoleId) -> | |||
case lib_account:get_role_pid(RoleId) of | |||
Pid when is_pid(Pid) -> | |||
pack_send(Pid, Cmd, List); | |||
_ -> | |||
ok | |||
end; | |||
pack_send(#node_rid{id = RoleId}, Cmd, List) -> | |||
pack_send(RoleId, Cmd, List); | |||
pack_send(#role{sid = Sid}, Cmd, List) -> | |||
pack_send(Sid, Cmd, List); | |||
pack_send(Sid, Cmd, List) -> | |||
case misc:is_process_alive(Sid) of | |||
true -> | |||
case catch prot:encode(Cmd, List) of | |||
{ok, Bin} -> | |||
send(Sid, Bin); | |||
Ret -> | |||
?ERR("Cmd:~w use pack_send, reason:~w", [Cmd, Ret]), | |||
ok | |||
end; | |||
_R -> | |||
ok | |||
end, | |||
ok. | |||
send(#role{sid = Sid}, Bin) -> | |||
send(Sid, Bin); | |||
send(Sid, Bin) -> | |||
case catch config:config(print_send_cmd) of | |||
true -> | |||
<<_L:32, Cmd:32, _Data/binary>> = Bin, | |||
svr_handle_mgr:listen_cmd(Cmd, byte_size(Bin)); | |||
_ -> | |||
ok | |||
end, | |||
Sid ! {'send', Bin}, | |||
ok. | |||
pack_send_msg(#node_rid{id = RoleId}, List) -> | |||
pack_send_msg(RoleId, List); | |||
pack_send_msg(RoleId, List) when is_integer(RoleId) -> | |||
case ets:lookup(?ETS_ONLINE, RoleId) of | |||
[#role{sid = Sid} | _] -> | |||
case misc:is_process_alive(Sid) of | |||
true -> | |||
pack_send_msg(Sid, List); | |||
_ -> | |||
ok | |||
end; | |||
_ -> | |||
ok | |||
end; | |||
pack_send_msg(#role{sid = Sid}, List) -> | |||
pack_send_msg(Sid, List); | |||
pack_send_msg(Sid, [Type, {Code, Vals}]) -> | |||
pack_send_msg(Sid, [Type, 0, Code, Vals]); | |||
pack_send_msg(Sid, [Type, Code]) -> | |||
pack_send_msg(Sid, [Type, 0, Code, ""]); | |||
pack_send_msg(Sid, [Type, SubType, {Code, Vals}]) -> | |||
pack_send_msg(Sid, [Type, SubType, Code, Vals]); | |||
pack_send_msg(Sid, [Type, SubType, Code]) -> | |||
pack_send_msg(Sid, [Type, SubType, Code, ""]); | |||
pack_send_msg(Sid, List) -> | |||
pack_send(Sid, 10100, List), | |||
ok. |
@ -1,24 +0,0 @@ | |||
%%%----------------------------------- | |||
%%% @Author : gaomeng 595088880@qq.com | |||
%%% @Created : 2017/10/17 | |||
%%% @Doc : 系统数据库保存相关信息 | |||
%%%----------------------------------- | |||
-module(lib_sys). | |||
-include("common.hrl"). | |||
-export([ | |||
get_var/1 | |||
]). | |||
%% @doc 获取对应数据库字段配置 | |||
get_var(TarName) when is_list(TarName) -> | |||
Sql = io_lib:format(<<"SELECT `value` FROM `sys_var` WHERE `name` = '~s' limit 1;">>, [TarName]), | |||
case db:get_one(Sql) of | |||
[] -> | |||
util:term_to_bitstring([]); | |||
<<>> -> | |||
util:term_to_bitstring([]); | |||
Ret -> | |||
Ret | |||
end; | |||
get_var(Obj) -> | |||
get_var(util:to_list(Obj)). |
@ -1,332 +0,0 @@ | |||
-module(config). | |||
-include("record.hrl"). | |||
-include("common.hrl"). | |||
-include_lib("stdlib/include/ms_transform.hrl"). | |||
-compile(export_all). | |||
%% 获取 .config里的配置信息 | |||
get_log_path(App) -> | |||
case application:get_env(App, log_path) of | |||
{ok, LogPath} -> LogPath; | |||
_ -> "logs" | |||
end. | |||
%% 获取 .config里的配置信息 | |||
get_log_level(App) -> | |||
case application:get_env(App, log_level) of | |||
{ok, LogLevel} -> LogLevel; | |||
_ -> 3 | |||
end. | |||
%% 获取 .config里的配置信息 | |||
get_server_node(App) -> | |||
case application:get_env(App, game_server_node) of | |||
{ok, Node} -> Node; | |||
_ -> '' | |||
end. | |||
%% 获取平台重置的key | |||
get_charge_key(App) -> | |||
case application:get_env(App, charge_key) of | |||
{ok, Node} -> Node; | |||
_ -> '' | |||
end. | |||
%% 获取 .config里的配置信息 | |||
get_md5_key(App) -> | |||
case application:get_env(App, md5_key) of | |||
{ok, Node} -> Node; | |||
_ -> '' | |||
end. | |||
%% 获取 .config里的配置信息 | |||
get_platform_key(App) -> | |||
case application:get_env(App, platform_key) of | |||
{ok, Node} -> Node; | |||
_ -> '' | |||
end. | |||
get_infant_ctrl(App) -> | |||
%% 防沉迷开关读取 | |||
case application:get_env(App, infant_ctrl) of | |||
{ok, Mode} -> tool:to_integer(Mode); | |||
_ -> 0 | |||
end. | |||
get_tcp_listener(App) -> | |||
case application:get_env(App, tcp_listener) of | |||
{ok, false} -> throw(undefined); | |||
{ok, Tcp_listener} -> | |||
try | |||
{_, Port} = lists:keyfind(port, 1, Tcp_listener), | |||
{_, Acceptor_num} = lists:keyfind(acceptor_num, 1, Tcp_listener), | |||
{_, Max_connections} = lists:keyfind(max_connections, 1, Tcp_listener), | |||
[Port, Acceptor_num, Max_connections] | |||
catch | |||
_:_ -> exit({bad_config, {server, {tcp_listener, config_error}}}) | |||
end; | |||
undefined -> throw(undefined) | |||
end. | |||
get_tcp_listener_ip(App) -> | |||
case application:get_env(App, tcp_listener_ip) of | |||
{ok, false} -> throw(undefined); | |||
{ok, Tcp_listener_ip} -> | |||
try | |||
{_, Ip} = lists:keyfind(ip, 1, Tcp_listener_ip), | |||
[Ip] | |||
catch | |||
_:_ -> exit({bad_config, {server, {tcp_listener, config_error}}}) | |||
end; | |||
undefined -> throw(undefined) | |||
end. | |||
get_mysql_config(App) -> | |||
case application:get_env(App, mysql_config) of | |||
{ok, false} -> throw(undefined); | |||
{ok, Mysql_config} -> | |||
{_, Host} = lists:keyfind(host, 1, Mysql_config), | |||
{_, Port} = lists:keyfind(port, 1, Mysql_config), | |||
{_, User} = lists:keyfind(user, 1, Mysql_config), | |||
{_, Password} = lists:keyfind(password, 1, Mysql_config), | |||
{_, DB} = lists:keyfind(db, 1, Mysql_config), | |||
{_, Encode} = lists:keyfind(encode, 1, Mysql_config), | |||
{_, Conns} = lists:keyfind(conns, 1, Mysql_config), | |||
[Host, Port, User, Password, DB, Encode, Conns]; | |||
undefined -> throw(undefined) | |||
end. | |||
get_mysql_log_config(App) -> | |||
case application:get_env(App, mysql_log_config) of | |||
{ok, false} -> throw(undefined); | |||
{ok, Mysql_config} -> | |||
{_, Host} = lists:keyfind(host, 1, Mysql_config), | |||
{_, Port} = lists:keyfind(port, 1, Mysql_config), | |||
{_, User} = lists:keyfind(user, 1, Mysql_config), | |||
{_, Password} = lists:keyfind(password, 1, Mysql_config), | |||
{_, DB} = lists:keyfind(db, 1, Mysql_config), | |||
{_, Encode} = lists:keyfind(encode, 1, Mysql_config), | |||
{_, Conns} = lists:keyfind(conns, 1, Mysql_config), | |||
[Host, Port, User, Password, DB, Encode, Conns]; | |||
undefined -> throw(undefined) | |||
end. | |||
get_mongo_config(App) -> | |||
case application:get_env(App, emongo_config) of | |||
{ok, false} -> throw(undefined); | |||
{ok, Emongo_config} -> | |||
{_, PoolId} = lists:keyfind(poolId, 1, Emongo_config), | |||
{_, EmongoSize} = lists:keyfind(emongoSize, 1, Emongo_config), | |||
{_, EmongoHost} = lists:keyfind(emongoHost, 1, Emongo_config), | |||
{_, EmongoPort} = lists:keyfind(emongoPort, 1, Emongo_config), | |||
{_, EmongoDatabase} = lists:keyfind(emongoDatabase, 1, Emongo_config), | |||
[PoolId, EmongoHost, EmongoPort, EmongoDatabase, EmongoSize]; | |||
undefined -> throw(undefined) | |||
end. | |||
get_slave_mongo_config(App) -> | |||
case application:get_env(App, slave_emongo_config) of | |||
{ok, false} -> throw(undefined); | |||
{ok, Emongo_config} -> | |||
{_, PoolId} = lists:keyfind(poolId, 1, Emongo_config), | |||
{_, EmongoSize} = lists:keyfind(emongoSize, 1, Emongo_config), | |||
{_, EmongoHost} = lists:keyfind(emongoHost, 1, Emongo_config), | |||
{_, EmongoPort} = lists:keyfind(emongoPort, 1, Emongo_config), | |||
{_, EmongoDatabase} = lists:keyfind(emongoDatabase, 1, Emongo_config), | |||
[PoolId, EmongoHost, EmongoPort, EmongoDatabase, EmongoSize]; | |||
undefined -> get_mongo_config(App) | |||
end. | |||
has_slave_mongo_config() -> | |||
case application:get_env(server, slave_emongo_config) of | |||
{ok, false} -> false; | |||
{ok, _Emongo_config} -> true | |||
end. | |||
get_read_data_mode(App) -> | |||
case application:get_env(App, base_data_from_db) of | |||
{ok, Mode} -> tool:to_integer(Mode); | |||
_ -> 0 | |||
end. | |||
get_gateway_node(App) -> | |||
case application:get_env(App, gateway_node) of | |||
{ok, Gateway_node} -> Gateway_node; | |||
_ -> undefined | |||
end. | |||
get_gateway_async_time() -> | |||
case application:get_env(gateway, gateway_async_time) of | |||
{ok, Async_time} -> Async_time; | |||
_ -> undefined | |||
end. | |||
get_scene_here(App) -> | |||
case application:get_env(App, scene_here) of | |||
undefined -> []; | |||
{ok, all} -> | |||
MS = ets:fun2ms(fun(S) when S#temp_scene.type =< 3 -> S#temp_scene.sid end), | |||
ets:select(?ETS_TEMP_SCENE, MS); | |||
{ok, SL} when is_list(SL) -> | |||
SL1 = lists:filter(fun(SId) -> | |||
case ets:lookup(?ETS_TEMP_SCENE, SId) of | |||
[] -> false; | |||
[S] -> | |||
if S#temp_scene.type =< 3 -> | |||
true; | |||
true -> | |||
false | |||
end | |||
end | |||
end, | |||
SL), | |||
SL1; | |||
_ -> [] | |||
end. | |||
get_guest_account_url(App) -> | |||
case application:get_env(App, guest_account_url) of | |||
{ok, Guest_account_url} -> Guest_account_url; | |||
_ -> "" | |||
end. | |||
%% GM指令开关读取 | |||
get_can_gmcmd() -> | |||
case application:get_env(server, can_gmcmd) of | |||
{ok, Mode} -> tool:to_integer(Mode); | |||
_ -> throw(undefined) | |||
end. | |||
get_strict_md5(App) -> | |||
case application:get_env(App, strict_md5) of | |||
{ok, Strict_md5} -> Strict_md5; | |||
_ -> 1 | |||
end. | |||
get_http_ips(App) -> | |||
case application:get_env(App, http_ips) of | |||
{ok, Http_ips} -> Http_ips; | |||
_ -> [] | |||
end. | |||
%% 获取 .config里的配置信息 | |||
get_server_number(App) -> | |||
case application:get_env(App, server_number) of | |||
{ok, Server_number} -> | |||
case is_integer(Server_number) == true of | |||
false -> 0; | |||
true -> Server_number | |||
end; | |||
_ -> 0 | |||
end. | |||
%% 获取 .config里的配置信息 | |||
get_max_id(App) -> | |||
case application:get_env(App, max_id) of | |||
{ok, Max_id} -> | |||
case is_integer(Max_id) == true of | |||
false -> 0; | |||
true -> Max_id | |||
end; | |||
_ -> 0 | |||
end. | |||
%% 获取平台名称 | |||
get_platform_name() -> | |||
case application:get_env(server, platform) of | |||
{ok, Name} -> | |||
Name; | |||
_ -> | |||
undefined | |||
end. | |||
%% 获取加密串号 | |||
get_card_key() -> | |||
case application:get_env(server, card_key) of | |||
{ok, Crypto} -> | |||
Crypto; | |||
_ -> | |||
undefined | |||
end. | |||
%% 获取服号 | |||
get_server_num() -> | |||
case application:get_env(server, server_num) of | |||
{ok, N} -> | |||
N; | |||
_ -> | |||
undefined | |||
end. | |||
%%获取数据库日志路径 | |||
get_db_log_path() -> | |||
case application:get_env(server, db_log_path) of | |||
{ok, N} -> | |||
N; | |||
_ -> | |||
undefined | |||
end. | |||
%%获取开服时间 | |||
get_opening_time() -> | |||
case application:get_env(server, open_time) of | |||
{ok, N} -> | |||
N; | |||
_ -> | |||
0 | |||
end. | |||
%%获取节点1的id | |||
get_domain() -> | |||
case application:get_env(server, domain) of | |||
{ok, N} -> | |||
N; | |||
_ -> | |||
1 | |||
end. | |||
%%获取节点2的id | |||
get_node_id_2() -> | |||
case application:get_env(server, node_id_2) of | |||
{ok, N} -> | |||
N; | |||
_ -> | |||
2 | |||
end. | |||
%%获取客户端操作系统 | |||
get_client_os(Os) -> | |||
case Os of | |||
1 -> | |||
<<"Android">>; | |||
2 -> | |||
<<"iphone">>; | |||
_ -> | |||
<<"未知">> | |||
end. | |||
%%获取客户端设备类型 | |||
get_client_device_type(DeviceType) -> | |||
case DeviceType of | |||
1 -> <<"Android">>; | |||
2 -> <<"iphone">>; | |||
3 -> <<"ipad">>; | |||
_ -> <<"未知">> | |||
end. | |||
%%获取客户端移动网络类型 | |||
get_client_isp(Isp) -> | |||
case Isp of | |||
1 -> <<"中国移动">>; | |||
2 -> <<"中国电信">>; | |||
3 -> <<"中国联通">>; | |||
_ -> "未知" | |||
end. | |||
%%获取客户端上网方式 | |||
get_client_net_type(Net) -> | |||
case Net of | |||
1 -> <<"3G">>; | |||
2 -> <<"WIFI">>; | |||
3 -> <<"2G">>; | |||
_ -> <<"未知">> | |||
end. |
@ -1,270 +0,0 @@ | |||
%% Copyright (c) 2007 | |||
%% Mats Cronqvist <mats.cronqvist@ericsson.com> | |||
%% Chris Newcombe <chris.newcombe@gmail.com> | |||
%% Jacob Vorreuter <jacob.vorreuter@gmail.com> | |||
%% | |||
%% Permission is hereby granted, free of charge, to any person | |||
%% obtaining a copy of this software and associated documentation | |||
%% files (the "Software"), to deal in the Software without | |||
%% restriction, including without limitation the rights to use, | |||
%% copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
%% copies of the Software, and to permit persons to whom the | |||
%% Software is furnished to do so, subject to the following | |||
%% conditions: | |||
%% | |||
%% The above copyright notice and this permission notice shall be | |||
%% included in all copies or substantial portions of the Software. | |||
%% | |||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |||
%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |||
%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |||
%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |||
%% OTHER DEALINGS IN THE SOFTWARE. | |||
%%%------------------------------------------------------------------- | |||
%%% File : dynamic_compile.erl | |||
%%% Description : | |||
%%% Authors : Mats Cronqvist <mats.cronqvist@ericsson.com> | |||
%%% Chris Newcombe <chris.newcombe@gmail.com> | |||
%%% Jacob Vorreuter <jacob.vorreuter@gmail.com> | |||
%%% - add support for limit include-file depth (and prevent circular references) | |||
%%% prevent circular macro expansion set FILE correctly when -module() is found | |||
%%% -include_lib support $ENVVAR in include filenames | |||
%%% substitute-stringize (??MACRO) | |||
%%% -undef/-ifdef/-ifndef/-else/-endif | |||
%%% -file(File, Line) | |||
%%%------------------------------------------------------------------- | |||
-module(dynamic_compile). | |||
%% API | |||
-export([from_string/1, from_string/2]). | |||
-import(lists, [reverse/1, keyreplace/4]). | |||
%%==================================================================== | |||
%% API | |||
%%==================================================================== | |||
%%-------------------------------------------------------------------- | |||
%% Function: | |||
%% Description: | |||
%% Returns a binary that can be used with | |||
%% code:load_binary(Module, ModuleFilenameForInternalRecords, Binary). | |||
%%-------------------------------------------------------------------- | |||
from_string(CodeStr) -> | |||
from_string(CodeStr, []). | |||
% takes Options as for compile:forms/2 | |||
from_string(CodeStr, CompileFormsOptions) -> | |||
%% Initialise the macro dictionary with the default predefined macros, | |||
%% (adapted from epp.erl:predef_macros/1 | |||
Filename = "compiled_from_string", | |||
%%Machine = list_to_atom(erlang:system_info(machine)), | |||
Ms0 = dict:new(), | |||
% Ms1 = dict:store('FILE', {[], "compiled_from_string"}, Ms0), | |||
% Ms2 = dict:store('LINE', {[], 1}, Ms1), % actually we might add special code for this | |||
% Ms3 = dict:store('MODULE', {[], undefined}, Ms2), | |||
% Ms4 = dict:store('MODULE_STRING', {[], undefined}, Ms3), | |||
% Ms5 = dict:store('MACHINE', {[], Machine}, Ms4), | |||
% InitMD = dict:store(Machine, {[], true}, Ms5), | |||
InitMD = Ms0, | |||
%% From the docs for compile:forms: | |||
%% When encountering an -include or -include_dir directive, the compiler searches for header files in the following directories: | |||
%% 1. ".", the current working directory of the file server; | |||
%% 2. the base name of the compiled file; | |||
%% 3. the directories specified using the i option. The directory specified last is searched first. | |||
%% In this case, #2 is meaningless. | |||
IncludeSearchPath = ["." | reverse([Dir || {i, Dir} <- CompileFormsOptions])], | |||
{RevForms, _OutMacroDict} = scan_and_parse(CodeStr, Filename, 1, [], InitMD, IncludeSearchPath), | |||
Forms = reverse(RevForms), | |||
%% note: 'binary' is forced as an implicit option, whether it is provided or not. | |||
case compile:forms(Forms, CompileFormsOptions) of | |||
{ok, ModuleName, CompiledCodeBinary} when is_binary(CompiledCodeBinary) -> | |||
{ModuleName, CompiledCodeBinary}; | |||
{ok, ModuleName, CompiledCodeBinary, []} when is_binary(CompiledCodeBinary) -> % empty warnings list | |||
{ModuleName, CompiledCodeBinary}; | |||
{ok, _ModuleName, _CompiledCodeBinary, Warnings} -> | |||
throw({?MODULE, warnings, Warnings}); | |||
Other -> | |||
throw({?MODULE, compile_forms, Other}) | |||
end. | |||
%%==================================================================== | |||
%% Internal functions | |||
%%==================================================================== | |||
%%% Code from Mats Cronqvist | |||
%%% See http://www.erlang.org/pipermail/erlang-questions/2007-March/025507.html | |||
%%%## 'scan_and_parse' | |||
%%% | |||
%%% basically we call the OTP scanner and parser (erl_scan and | |||
%%% erl_parse) line-by-line, but check each scanned line for (or | |||
%%% definitions of) macros before parsing. | |||
%% returns {ReverseForms, FinalMacroDict} | |||
scan_and_parse([], _CurrFilename, _CurrLine, RevForms, MacroDict, _IncludeSearchPath) -> | |||
{RevForms, MacroDict}; | |||
scan_and_parse(RemainingText, CurrFilename, CurrLine, RevForms, MacroDict, IncludeSearchPath) -> | |||
case scanner(RemainingText, CurrLine, MacroDict) of | |||
{tokens, NLine, NRemainingText, Toks} -> | |||
{ok, Form} = erl_parse:parse_form(Toks), | |||
scan_and_parse(NRemainingText, CurrFilename, NLine, [Form | RevForms], MacroDict, IncludeSearchPath); | |||
{macro, NLine, NRemainingText, NMacroDict} -> | |||
scan_and_parse(NRemainingText, CurrFilename, NLine, RevForms, NMacroDict, IncludeSearchPath); | |||
{include, NLine, NRemainingText, IncludeFilename} -> | |||
IncludeFileRemainingTextents = read_include_file(IncludeFilename, IncludeSearchPath), | |||
%%io:format("include file ~p contents: ~n~p~nRemainingText = ~p~n", [IncludeFilename,IncludeFileRemainingTextents, RemainingText]), | |||
%% Modify the FILE macro to reflect the filename | |||
%%IncludeMacroDict = dict:store('FILE', {[],IncludeFilename}, MacroDict), | |||
IncludeMacroDict = MacroDict, | |||
%% Process the header file (inc. any nested header files) | |||
{RevIncludeForms, IncludedMacroDict} = scan_and_parse(IncludeFileRemainingTextents, IncludeFilename, 1, [], IncludeMacroDict, IncludeSearchPath), | |||
%io:format("include file results = ~p~n", [R]), | |||
%% Restore the FILE macro in the NEW MacroDict (so we keep any macros defined in the header file) | |||
%%NMacroDict = dict:store('FILE', {[],CurrFilename}, IncludedMacroDict), | |||
NMacroDict = IncludedMacroDict, | |||
%% Continue with the original file | |||
scan_and_parse(NRemainingText, CurrFilename, NLine, RevIncludeForms ++ RevForms, NMacroDict, IncludeSearchPath); | |||
done -> | |||
scan_and_parse([], CurrFilename, CurrLine, RevForms, MacroDict, IncludeSearchPath) | |||
end. | |||
scanner(Text, Line, MacroDict) -> | |||
case erl_scan:tokens([], Text, Line) of | |||
{done, {ok, Toks, NLine}, LeftOverChars} -> | |||
case pre_proc(Toks, MacroDict) of | |||
{tokens, NToks} -> {tokens, NLine, LeftOverChars, NToks}; | |||
{macro, NMacroDict} -> {macro, NLine, LeftOverChars, NMacroDict}; | |||
{include, Filename} -> {include, NLine, LeftOverChars, Filename} | |||
end; | |||
{more, _Continuation} -> | |||
%% This is supposed to mean "term is not yet complete" (i.e. a '.' has | |||
%% not been reached yet). | |||
%% However, for some bizarre reason we also get this if there is a comment after the final '.' in a file. | |||
%% So we check to see if Text only consists of comments. | |||
case is_only_comments(Text) of | |||
true -> | |||
done; | |||
false -> | |||
throw({incomplete_term, Text, Line}) | |||
end | |||
end. | |||
is_only_comments(Text) -> is_only_comments(Text, not_in_comment). | |||
is_only_comments([], _) -> true; | |||
is_only_comments([$ | T], not_in_comment) -> | |||
is_only_comments(T, not_in_comment); % skipping whitspace outside of comment | |||
is_only_comments([$\t | T], not_in_comment) -> | |||
is_only_comments(T, not_in_comment); % skipping whitspace outside of comment | |||
is_only_comments([$\n | T], not_in_comment) -> | |||
is_only_comments(T, not_in_comment); % skipping whitspace outside of comment | |||
is_only_comments([$% | T], not_in_comment) -> is_only_comments(T, in_comment); % found start of a comment | |||
is_only_comments(_, not_in_comment) -> false; | |||
% found any significant char NOT in a comment | |||
is_only_comments([$\n | T], in_comment) -> is_only_comments(T, not_in_comment); % found end of a comment | |||
is_only_comments([_ | T], in_comment) -> is_only_comments(T, in_comment). % skipping over in-comment chars | |||
%%%## 'pre-proc' | |||
%%% | |||
%%% have to implement a subset of the pre-processor, since epp insists | |||
%%% on running on a file. | |||
%%% only handles 2 cases; | |||
%% -define(MACRO, something). | |||
%% -define(MACRO(VAR1,VARN),{stuff,VAR1,more,stuff,VARN,extra,stuff}). | |||
pre_proc([{'-', _}, {atom, _, define}, {'(', _}, {_, _, Name} | DefToks], MacroDict) -> | |||
false = dict:is_key(Name, MacroDict), | |||
case DefToks of | |||
[{',', _} | Macro] -> | |||
{macro, dict:store(Name, {[], macro_body_def(Macro, [])}, MacroDict)}; | |||
[{'(', _} | Macro] -> | |||
{macro, dict:store(Name, macro_params_body_def(Macro, []), MacroDict)} | |||
end; | |||
pre_proc([{'-', _}, {atom, _, include}, {'(', _}, {string, _, Filename}, {')', _}, {dot, _}], _MacroDict) -> | |||
{include, Filename}; | |||
pre_proc(Toks, MacroDict) -> | |||
{tokens, subst_macros(Toks, MacroDict)}. | |||
macro_params_body_def([{')', _}, {',', _} | Toks], RevParams) -> | |||
{reverse(RevParams), macro_body_def(Toks, [])}; | |||
macro_params_body_def([{var, _, Param} | Toks], RevParams) -> | |||
macro_params_body_def(Toks, [Param | RevParams]); | |||
macro_params_body_def([{',', _}, {var, _, Param} | Toks], RevParams) -> | |||
macro_params_body_def(Toks, [Param | RevParams]). | |||
macro_body_def([{')', _}, {dot, _}], RevMacroBodyToks) -> | |||
reverse(RevMacroBodyToks); | |||
macro_body_def([Tok | Toks], RevMacroBodyToks) -> | |||
macro_body_def(Toks, [Tok | RevMacroBodyToks]). | |||
subst_macros(Toks, MacroDict) -> | |||
reverse(subst_macros_rev(Toks, MacroDict, [])). | |||
%% returns a reversed list of tokes | |||
subst_macros_rev([{'?', _}, {_, LineNum, 'LINE'} | Toks], MacroDict, RevOutToks) -> | |||
%% special-case for ?LINE, to avoid creating a new MacroDict for every line in the source file | |||
subst_macros_rev(Toks, MacroDict, [{integer, LineNum, LineNum}] ++ RevOutToks); | |||
subst_macros_rev([{'?', _}, {_, _, Name}, {'(', _} = Paren | Toks], MacroDict, RevOutToks) -> | |||
case dict:fetch(Name, MacroDict) of | |||
{[], MacroValue} -> | |||
%% This macro does not have any vars, so ignore the fact that the invocation is followed by "(...stuff" | |||
%% Recursively expand any macro calls inside this macro's value | |||
%% TODO: avoid infinite expansion due to circular references (even indirect ones) | |||
RevExpandedOtherMacrosToks = subst_macros_rev(MacroValue, MacroDict, []), | |||
subst_macros_rev([Paren | Toks], MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks); | |||
ParamsAndBody -> | |||
%% This macro does have vars. | |||
%% Collect all of the passe arguments, in an ordered list | |||
{NToks, Arguments} = subst_macros_get_args(Toks, []), | |||
%% Expand the varibles | |||
ExpandedParamsToks = subst_macros_subst_args_for_vars(ParamsAndBody, Arguments), | |||
%% Recursively expand any macro calls inside this macro's value | |||
%% TODO: avoid infinite expansion due to circular references (even indirect ones) | |||
RevExpandedOtherMacrosToks = subst_macros_rev(ExpandedParamsToks, MacroDict, []), | |||
subst_macros_rev(NToks, MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks) | |||
end; | |||
subst_macros_rev([{'?', _}, {_, _, Name} | Toks], MacroDict, RevOutToks) -> | |||
%% This macro invocation does not have arguments. | |||
%% Therefore the definition should not have parameters | |||
{[], MacroValue} = dict:fetch(Name, MacroDict), | |||
%% Recursively expand any macro calls inside this macro's value | |||
%% TODO: avoid infinite expansion due to circular references (even indirect ones) | |||
RevExpandedOtherMacrosToks = subst_macros_rev(MacroValue, MacroDict, []), | |||
subst_macros_rev(Toks, MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks); | |||
subst_macros_rev([Tok | Toks], MacroDict, RevOutToks) -> | |||
subst_macros_rev(Toks, MacroDict, [Tok | RevOutToks]); | |||
subst_macros_rev([], _MacroDict, RevOutToks) -> RevOutToks. | |||
subst_macros_get_args([{')', _} | Toks], RevArgs) -> | |||
{Toks, reverse(RevArgs)}; | |||
subst_macros_get_args([{',', _}, {var, _, ArgName} | Toks], RevArgs) -> | |||
subst_macros_get_args(Toks, [ArgName | RevArgs]); | |||
subst_macros_get_args([{var, _, ArgName} | Toks], RevArgs) -> | |||
subst_macros_get_args(Toks, [ArgName | RevArgs]). | |||
subst_macros_subst_args_for_vars({[], BodyToks}, []) -> | |||
BodyToks; | |||
subst_macros_subst_args_for_vars({[Param | Params], BodyToks}, [Arg | Args]) -> | |||
NBodyToks = keyreplace(Param, 3, BodyToks, {var, 1, Arg}), | |||
subst_macros_subst_args_for_vars({Params, NBodyToks}, Args). | |||
read_include_file(Filename, IncludeSearchPath) -> | |||
case file:path_open(IncludeSearchPath, Filename, [read, raw, binary]) of | |||
{ok, IoDevice, FullName} -> | |||
{ok, Data} = file:read(IoDevice, filelib:file_size(FullName)), | |||
file:close(IoDevice), | |||
binary_to_list(Data); | |||
{error, Reason} -> | |||
throw({failed_to_read_include_file, Reason, Filename, IncludeSearchPath}) | |||
end. |
@ -1,41 +0,0 @@ | |||
%%%---------------------------------------- | |||
%%% @Module : game_alarm_handler | |||
%%% @Author : csj | |||
%%% @Created : 2010.10.05 | |||
%%% @Description: 警报 | |||
%%%---------------------------------------- | |||
-module(game_alarm_handler). | |||
-behaviour(gen_event). | |||
-include("common.hrl"). | |||
%%gen_envent callbacks. | |||
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]). | |||
init(Args) -> | |||
?DEBUG("game_alarm_handler init : ~p.", [Args]), | |||
{ok, Args}. | |||
handle_event({set_alarm, From}, _State) -> | |||
?DEBUG("csj depot clear by ~p started.", [From]), | |||
{ok, _State}; | |||
handle_event({clear_alarm, From}, _State) -> | |||
?DEBUG("csj depot clear by ~p done.", [From]), | |||
{ok, _State}; | |||
handle_event(Event, State) -> | |||
?DEBUG("unmatched event: ~p.", [Event]), | |||
{ok, State}. | |||
handle_call(_Req, State) -> | |||
{ok, State, State}. | |||
handle_info(_Info, State) -> | |||
{ok, State}. | |||
terminate(_Reason, _State) -> | |||
ok. | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. |
@ -1,131 +0,0 @@ | |||
%%%---------------------------------------------------------------------- | |||
%%% File : game_timer.erl | |||
%%% Author : csj | |||
%%% Created : 2010-10-17 | |||
%%% Description: 时间生成器 | |||
%%%---------------------------------------------------------------------- | |||
-module(game_timer). | |||
-behaviour(gen_server). | |||
%% -------------------------------------------------------------------- | |||
%% Include files | |||
%% -------------------------------------------------------------------- | |||
-include("common.hrl"). | |||
%% -------------------------------------------------------------------- | |||
%% External exports | |||
-export([now/0, now_seconds/0, cpu_time/0, start_link/0, start/1, info/0]). | |||
%% gen_server callbacks | |||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). | |||
%% ==================================================================== | |||
%% External functions | |||
%% ==================================================================== | |||
now() -> | |||
[{timer, {Now, _}}] = ets:lookup(ets_timer, timer), | |||
Now. | |||
now_seconds() -> | |||
[{timer, {Now, _}}] = ets:lookup(ets_timer, timer), | |||
{MegaSecs, Secs, _MicroSecs} = Now, | |||
lists:concat([MegaSecs, Secs]). | |||
cpu_time() -> | |||
[{timer, {_, Wallclock_Time_Since_Last_Call}}] = ets:lookup(ets_timer, timer), | |||
Wallclock_Time_Since_Last_Call. | |||
info() -> | |||
[ | |||
ets:info(ets_timer), | |||
ets:tab2list(ets_timer) | |||
]. | |||
-define(CLOCK, 100). | |||
start(Sup) -> | |||
supervisor:start_child(Sup, | |||
{game_timer, | |||
{game_timer, start_link, []}, | |||
permanent, brutal_kill, worker, [game_timer]}). | |||
%% ==================================================================== | |||
%% Server functions | |||
%% ==================================================================== | |||
start_link() -> | |||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). | |||
%% -------------------------------------------------------------------- | |||
%% Function: init/1 | |||
%% Description: Initiates the server | |||
%% Returns: {ok, State} | | |||
%% {ok, State, Timeout} | | |||
%% ignore | | |||
%% {stop, Reason} | |||
%% -------------------------------------------------------------------- | |||
init([]) -> | |||
ets:new(ets_timer, [set, protected, named_table]), | |||
ets:insert(ets_timer, {timer, {erlang:now(), 0}}), | |||
erlang:send_after(?CLOCK, self(), {event, clock}), | |||
misc:write_monitor_pid(self(), ?MODULE, {}), | |||
{ok, []}. | |||
%% -------------------------------------------------------------------- | |||
%% Function: handle_call/3 | |||
%% Description: Handling call messages | |||
%% Returns: {reply, Reply, State} | | |||
%% {reply, Reply, State, Timeout} | | |||
%% {noreply, State} | | |||
%% {noreply, State, Timeout} | | |||
%% {stop, Reason, Reply, State} | (terminate/2 is called) | |||
%% {stop, Reason, State} (terminate/2 is called) | |||
%% -------------------------------------------------------------------- | |||
handle_call(_Request, _From, State) -> | |||
{reply, State, State}. | |||
%% -------------------------------------------------------------------- | |||
%% Function: handle_cast/2 | |||
%% Description: Handling cast messages | |||
%% Returns: {noreply, State} | | |||
%% {noreply, State, Timeout} | | |||
%% {stop, Reason, State} (terminate/2 is called) | |||
%% -------------------------------------------------------------------- | |||
handle_cast(_Msg, State) -> | |||
{noreply, State}. | |||
%% -------------------------------------------------------------------- | |||
%% Function: handle_info/2 | |||
%% Description: Handling all non call/cast messages | |||
%% Returns: {noreply, State} | | |||
%% {noreply, State, Timeout} | | |||
%% {stop, Reason, State} (terminate/2 is called) | |||
%% -------------------------------------------------------------------- | |||
handle_info({event, clock}, State) -> | |||
{_Total_Run_Time, Time_Since_Last_Call} = statistics(runtime), | |||
ets:insert(ets_timer, {timer, {erlang:now(), Time_Since_Last_Call}}), | |||
erlang:send_after(?CLOCK, self(), {event, clock}), | |||
{noreply, State}; | |||
handle_info(_Info, State) -> | |||
{noreply, State}. | |||
%% -------------------------------------------------------------------- | |||
%% Function: terminate/2 | |||
%% Description: Shutdown the server | |||
%% Returns: any (ignored by gen_server) | |||
%% -------------------------------------------------------------------- | |||
terminate(_Reason, _State) -> | |||
misc:delete_monitor_pid(self()), | |||
ok. | |||
%% -------------------------------------------------------------------- | |||
%% Func: code_change/3 | |||
%% Purpose: Convert process state when code is changed | |||
%% Returns: {ok, NewState} | |||
%% -------------------------------------------------------------------- | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. | |||
%% -------------------------------------------------------------------- | |||
%%% Internal functions | |||
%% -------------------------------------------------------------------- |
@ -1,238 +0,0 @@ | |||
%%%---------------------------------------------------------------------- | |||
%%% File : logger_h.erl | |||
%%% Created : 2010-08-20 | |||
%%%---------------------------------------------------------------------- | |||
-module(logger_h). | |||
-behaviour(gen_event). | |||
-include("common.hrl"). | |||
-include("debug.hrl"). | |||
-export([start_link/2, log_level/0]). | |||
%% gen_event callbacks | |||
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, | |||
code_change/3, reopen_log/0, rotate_log/1]). | |||
-record(state, {fd, file, level}). | |||
-define(LOG_FILE_INTERVAL, 24 * 3600000). % 1小时新开1个log文件 | |||
start_link(Log_file, Log_level) -> | |||
case gen_event:start_link({local, ?LOGMODULE}) of | |||
Ret = {ok, _Pid} -> | |||
gen_event:add_handler(?LOGMODULE, ?MODULE, [Log_file, Log_level]), | |||
Ret; | |||
Other -> | |||
Other | |||
end. | |||
log_level() -> | |||
gen_event:call({local, ?LOGMODULE}, ?MODULE, log_level). | |||
%%%---------------------------------------------------------------------- | |||
%%% Callback functions from gen_event | |||
%%%---------------------------------------------------------------------- | |||
%%---------------------------------------------------------------------- | |||
%% Func: init/1 | |||
%% Returns: {ok, State} | | |||
%% Other | |||
%%---------------------------------------------------------------------- | |||
init([File, Level]) -> | |||
FileName = gen_full_log_filename(File, erlang:localtime()), | |||
case file:open(FileName, [append, raw]) of | |||
{ok, Fd} -> | |||
erlang:send_after(?LOG_FILE_INTERVAL, self(), change_log_filename), | |||
{ok, #state{fd = Fd, file = File, level = Level}}; | |||
Error -> | |||
Error | |||
end. | |||
%%---------------------------------------------------------------------- | |||
%% Func: handle_event/2 | |||
%% Returns: {ok, State} | | |||
%% {swap_handler, Args1, State1, Mod2, Args2} | | |||
%% remove_handler | |||
%%---------------------------------------------------------------------- | |||
handle_event(Event, State) -> | |||
write_event(State#state.fd, {erlang:localtime(), Event}), | |||
{ok, State}. | |||
%%---------------------------------------------------------------------- | |||
%% Func: handle_call/2 | |||
%% Returns: {ok, Reply, State} | | |||
%% {swap_handler, Reply, Args1, State1, Mod2, Args2} | | |||
%% {remove_handler, Reply} | |||
%%---------------------------------------------------------------------- | |||
handle_call(log_level, State) -> | |||
{ok, State#state.level, State}; | |||
handle_call(_Request, State) -> | |||
Reply = ok, | |||
{ok, Reply, State}. | |||
%%---------------------------------------------------------------------- | |||
%% Func: handle_info/2 | |||
%% Returns: {ok, State} | | |||
%% {swap_handler, Args1, State1, Mod2, Args2} | | |||
%% remove_handler | |||
%%---------------------------------------------------------------------- | |||
handle_info({'EXIT', _Fd, _Reason}, _State) -> | |||
remove_handler; | |||
handle_info({emulator, _GL, reopen}, State) -> | |||
file:close(State#state.fd), | |||
rotate_log(State#state.file), | |||
case file:open(State#state.file, [append, raw]) of | |||
{ok, Fd} -> | |||
{ok, State#state{fd = Fd}}; | |||
Error -> | |||
Error | |||
end; | |||
handle_info({emulator, GL, Chars}, State) -> | |||
write_event(State#state.fd, {erlang:localtime(), {emulator, GL, Chars}}), | |||
{ok, State}; | |||
handle_info(change_log_filename, State) -> | |||
file:close(State#state.fd), | |||
FileName = gen_full_log_filename(State#state.file, erlang:localtime()), | |||
NewState = case file:open(FileName, [append, raw]) of | |||
{ok, Fd} -> | |||
State#state{fd = Fd}; | |||
Error -> | |||
?TRACE("open ~s fail, reason:~p~n", [FileName, Error]), | |||
State | |||
end, | |||
erlang:send_after(?LOG_FILE_INTERVAL, self(), change_log_filename), | |||
{ok, NewState}; | |||
handle_info(_Info, State) -> | |||
{ok, State}. | |||
%%---------------------------------------------------------------------- | |||
%% Func: terminate/2 | |||
%% Purpose: Shutdown the server | |||
%% Returns: any | |||
%%---------------------------------------------------------------------- | |||
terminate(_Reason, _State) -> | |||
ok. | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. | |||
reopen_log() -> | |||
?LOGMODULE ! {emulator, noproc, reopen}. | |||
%%%---------------------------------------------------------------------- | |||
%%% Internal functions | |||
%%%---------------------------------------------------------------------- | |||
% Copied from erlang_logger_file_h.erl | |||
write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) -> | |||
T = write_time(Time), | |||
case catch io_lib:format(add_node(Format, Pid), Args) of | |||
S when is_list(S) -> | |||
file:write(Fd, io_lib:format(T ++ S, [])); | |||
_ -> | |||
F = add_node("ERROR: ~p - ~p~n", Pid), | |||
file:write(Fd, io_lib:format(T ++ F, [Format, Args])) | |||
end; | |||
write_event(Fd, {Time, {emulator, _GL, Chars}}) -> | |||
T = write_time(Time), | |||
case catch io_lib:format(Chars, []) of | |||
S when is_list(S) -> | |||
file:write(Fd, io_lib:format(T ++ S, [])); | |||
_ -> | |||
file:write(Fd, io_lib:format(T ++ "ERROR: ~p ~n", [Chars])) | |||
end; | |||
write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) -> | |||
T = write_time(Time), | |||
file:write(Fd, io_lib:format(T ++ add_node("~p~n", Pid), [Info])); | |||
write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) -> | |||
T = write_time(Time), | |||
S = format_report(Rep), | |||
file:write(Fd, io_lib:format(T ++ S ++ add_node("", Pid), [])); | |||
write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) -> | |||
T = write_time(Time, "INFO REPORT"), | |||
S = format_report(Rep), | |||
file:write(Fd, io_lib:format(T ++ S ++ add_node("", Pid), [])); | |||
write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) -> | |||
T = write_time(Time, "INFO REPORT"), | |||
case catch io_lib:format(add_node(Format, Pid), Args) of | |||
S when is_list(S) -> | |||
file:write(Fd, io_lib:format(T ++ S, [])); | |||
_ -> | |||
F = add_node("ERROR: ~p - ~p~n", Pid), | |||
file:write(Fd, io_lib:format(T ++ F, [Format, Args])) | |||
end; | |||
write_event(_, _) -> | |||
ok. | |||
format_report(Rep) when is_list(Rep) -> | |||
case string_p(Rep) of | |||
true -> | |||
io_lib:format("~s~n", [Rep]); | |||
_ -> | |||
format_rep(Rep) | |||
end; | |||
format_report(Rep) -> | |||
io_lib:format("~p~n", [Rep]). | |||
format_rep([{Tag, Data} | Rep]) -> | |||
io_lib:format(" ~p: ~p~n", [Tag, Data]) ++ format_rep(Rep); | |||
format_rep([Other | Rep]) -> | |||
io_lib:format(" ~p~n", [Other]) ++ format_rep(Rep); | |||
format_rep(_) -> | |||
[]. | |||
add_node(X, Pid) when is_atom(X) -> | |||
add_node(atom_to_list(X), Pid); | |||
add_node(X, Pid) when node(Pid) /= node() -> | |||
lists:concat([X, "** at node ", node(Pid), " **~n"]); | |||
add_node(X, _) -> | |||
X. | |||
string_p([]) -> | |||
false; | |||
string_p(Term) -> | |||
string_p1(Term). | |||
string_p1([H | T]) when is_integer(H), H >= $\s, H < 255 -> | |||
string_p1(T); | |||
string_p1([$\n | T]) -> string_p1(T); | |||
string_p1([$\r | T]) -> string_p1(T); | |||
string_p1([$\t | T]) -> string_p1(T); | |||
string_p1([$\v | T]) -> string_p1(T); | |||
string_p1([$\b | T]) -> string_p1(T); | |||
string_p1([$\f | T]) -> string_p1(T); | |||
string_p1([$\e | T]) -> string_p1(T); | |||
string_p1([H | T]) when is_list(H) -> | |||
case string_p1(H) of | |||
true -> string_p1(T); | |||
_ -> false | |||
end; | |||
string_p1([]) -> true; | |||
string_p1(_) -> false. | |||
write_time(Time) -> write_time(Time, "ERROR REPORT"). | |||
write_time({{Y, Mo, D}, {H, Mi, S}}, Type) -> | |||
io_lib:format("~n=~s==== ~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w ===", | |||
[Type, Y, Mo, D, H, Mi, S]). | |||
gen_full_log_filename(File, {{Y, Mo, D}, {H, Mi, S}}) -> | |||
io_lib:format("~s_~w.~.2.0w.~.2.0w_~.2.0w-~.2.0w-~.2.0w", [File, Y, Mo, D, H, Mi, S]). | |||
%% @doc Rename the log file if exists, to "*-old.log". | |||
%% This is needed in systems when the file must be closed before rotation (Windows). | |||
%% On most Unix-like system, the file can be renamed from the command line and | |||
%% the log can directly be reopened. | |||
%% @spec (Filename::string()) -> ok | |||
rotate_log(Filename) -> | |||
case file:read_file_info(Filename) of | |||
{ok, _FileInfo} -> | |||
RotationName = filename:rootname(Filename), | |||
file:rename(Filename, [RotationName, "-old.log"]), | |||
ok; | |||
{error, _Reason} -> | |||
ok | |||
end. |
@ -1,114 +0,0 @@ | |||
%%%---------------------------------------------------------------------- | |||
%%% File : loglevel.erl | |||
%%% Created : 2010-08-30 | |||
%%%---------------------------------------------------------------------- | |||
-module(loglevel). | |||
-export([set/1, get/0]). | |||
-include("common.hrl"). | |||
-include("debug.hrl"). | |||
%% Error levels: | |||
-define(LOG_LEVELS, [{0, no_log, "No log"} | |||
, {1, critical, "Critical"} | |||
, {2, error, "Error"} | |||
, {3, warning, "Warning"} | |||
, {4, info, "Info"} | |||
, {5, debug, "Debug"} | |||
, {6, test, "Test"} | |||
]). | |||
get() -> | |||
Level = logger:get(), | |||
case lists:keysearch(Level, 1, ?LOG_LEVELS) of | |||
{value, Result} -> Result; | |||
_ -> erlang:error({no_such_loglevel, Level}) | |||
end. | |||
set(LogLevel) when is_atom(LogLevel) -> | |||
set(level_to_integer(LogLevel)); | |||
set(Loglevel) when is_integer(Loglevel) -> | |||
try | |||
io:format("$$$ loglevel:set, level is ~p~n", [Loglevel]), | |||
{Mod, Code} = dynamic_compile:from_string(logger_src(Loglevel)), | |||
code:load_binary(Mod, "logger_h.erl", Code) | |||
catch | |||
Type:Error -> ?CRITICAL_MSG("Error compiling logger (~p): ~p~n", [Type, Error]) | |||
end; | |||
set(_) -> | |||
exit("Loglevel must be an integer"). | |||
level_to_integer(Level) -> | |||
case lists:keysearch(Level, 2, ?LOG_LEVELS) of | |||
{value, {Int, Level, _Desc}} -> Int; | |||
_ -> erlang:error({no_such_loglevel, Level}) | |||
end. | |||
%% -------------------------------------------------------------- | |||
%% Code of the mcs logger, dynamically compiled and loaded | |||
%% This allows to dynamically change log level while keeping a | |||
%% very efficient code. | |||
logger_src(Loglevel) -> | |||
L = integer_to_list(Loglevel), | |||
"-module(logger). | |||
-export([test_msg/4, | |||
debug_msg/4, | |||
info_msg/4, | |||
warning_msg/4, | |||
error_msg/4, | |||
critical_msg/4, | |||
get/0]). | |||
get() -> " ++ L ++ ". | |||
%% Helper functions | |||
test_msg(Module, Line, Format, Args) when " ++ L ++ " >= 6 -> | |||
notify(info_msg, | |||
\"T(~p:~p:~p) : \"++Format++\"~n\", | |||
[self(), Module, Line]++Args); | |||
test_msg(_,_,_,_) -> ok. | |||
debug_msg(Module, Line, Format, Args) when " ++ L ++ " >= 5 -> | |||
notify(info_msg, | |||
\"D(~p:~p:~p) : \"++Format++\"~n\", | |||
[self(), Module, Line]++Args); | |||
debug_msg(_,_,_,_) -> ok. | |||
info_msg(Module, Line, Format, Args) when " ++ L ++ " >= 4 -> | |||
notify(info_msg, | |||
\"I(~p:~p:~p) : \"++Format++\"~n\", | |||
[self(), Module, Line]++Args); | |||
info_msg(_,_,_,_) -> ok. | |||
warning_msg(Module, Line, Format, Args) when " ++ L ++ " >= 3 -> | |||
notify(error, | |||
\"W(~p:~p:~p) : \"++Format++\"~n\", | |||
[self(), Module, Line]++Args); | |||
warning_msg(_,_,_,_) -> ok. | |||
error_msg(Module, Line, Format, Args) when " ++ L ++ " >= 2 -> | |||
case Args of | |||
%% start with : ** Node php | |||
[42,42,32,78,111,100,101,32,112,104,112|_] -> | |||
ok; | |||
_ -> | |||
notify(error, | |||
\"E(~p:~p:~p) : \"++Format++\"~n\", | |||
[self(), Module, Line]++Args) | |||
end; | |||
error_msg(_,_,_,_) -> ok. | |||
critical_msg(Module, Line, Format, Args) when " ++ L ++ " >= 1 -> | |||
notify(error, | |||
\"C(~p:~p:~p) : \"++Format++\"~n\", | |||
[self(), Module, Line]++Args); | |||
critical_msg(_,_,_,_) -> ok. | |||
%% Distribute the message to the Erlang error logger | |||
notify(Type, Format, Args) -> | |||
LoggerMsg = {Type, group_leader(), {self(), Format, Args}}, | |||
gen_event:notify(logger_mgr, LoggerMsg). | |||
". |