@ -1,186 +0,0 @@ | |||
-module(excel2mysql). | |||
%% | |||
%% Include files | |||
%% | |||
-include("common111.hrl"). | |||
-compile(export_all). | |||
-define(CONFIG_FILE, "../config/gateway.config"). | |||
-define(TMP_TABLE_PATH, "./tmptable/"). | |||
-define(SRC_TABLE_PATH, "../src/table/"). | |||
-define(RECORD_FILENAME, "../include/table_to_record.hrl"). | |||
-define(BEAM_PATH, "./"). | |||
-define(EXCEL_PATH, "../temp/excel2mysql/"). | |||
%% | |||
%% API Functions | |||
%% | |||
start() -> | |||
case get_db_config(?CONFIG_FILE) of | |||
[Host, Port, User, Password, DB, Encode] -> | |||
start_erlydb(Host, Port, User, Password, DB), | |||
mysql:start_link(?DB_POOL, Host, Port, User, Password, DB, fun(_, _, _, _) -> ok end, Encode), | |||
mysql:connect(?DB_POOL, Host, Port, User, Password, DB, Encode, true), | |||
excel_to_mysql_base_goods_practise(); | |||
_ -> mysql_config_fail | |||
end, | |||
ok. | |||
get_db_config(Config_file) -> | |||
try | |||
{ok, [L]} = file:consult(Config_file), | |||
{_, C} = lists:keyfind(gateway, 1, L), | |||
{_, Mysql_config} = lists:keyfind(mysql_config, 1, C), | |||
{_, 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), | |||
[Host, Port, User, Password, DB, Encode] | |||
catch | |||
_:_ -> no_config | |||
end. | |||
%% | |||
%% Local Functions | |||
%% | |||
start_erlydb(IP, Port, User, Password, Db) -> | |||
erlydb:start(mysql, [{pool_id, erlydb_mysql}, | |||
{hostname, IP}, | |||
{port, Port}, | |||
{username, User}, | |||
{password, Password}, | |||
{database, Db}, | |||
{encoding, utf8}, | |||
{pool_size, 10}]). | |||
excel_to_mysql_base_goods_practise() -> | |||
Practise_path = lists:concat([?EXCEL_PATH, 'base_goods_practise']), | |||
case file:list_dir(Practise_path) of | |||
{ok, Filenames} -> | |||
lists:foreach(fun(F) -> | |||
case string:rstr(F, ".txt") > 0 of | |||
true -> | |||
base_goods_practise_0(Practise_path ++ "/" ++ F), | |||
ok; | |||
_ -> no_action | |||
end, | |||
ok | |||
end, | |||
lists:sort(Filenames)); | |||
{error, _} -> ignore | |||
end, | |||
ok. | |||
base_goods_practise_0(Filename) -> | |||
io:format("__1__~p~n", [Filename]), | |||
{ok, IoDevice} = file:open(Filename, [read]), | |||
try | |||
%% 神弓(09)_绿色(1)_ | |||
%% 攻击力 单属性 | |||
%% 最大 最小 命中 敏捷 体质 | |||
%% 9/1 | |||
%% max_attack/min_attack/hit/agile/physique | |||
{ok, _Line1} = file:read_line(IoDevice), | |||
{ok, _Line2} = file:read_line(IoDevice), | |||
{ok, _Line3} = file:read_line(IoDevice), | |||
{ok, Line4} = file:read_line(IoDevice), | |||
{ok, Line5} = file:read_line(IoDevice), | |||
[Line4_0] = string:tokens(Line4, "\n"), | |||
[Line5_0] = string:tokens(Line5, "\n"), | |||
%% io:format(" ~p~n ~p~n", [Line4_0, Line5_0]), | |||
[Subtype, Color] = string:tokens(Line4_0, "/"), | |||
Attrs = string:tokens(Line5_0, "/"), | |||
base_goods_practise_1(IoDevice, | |||
tool:to_integer(Subtype), | |||
tool:to_integer(Color), | |||
Attrs | |||
), | |||
file:close(IoDevice), | |||
ok | |||
catch | |||
_:_ -> file:close(IoDevice) | |||
end, | |||
ok. | |||
base_goods_practise_1(IoDevice, Subtype, Color, Attrs) -> | |||
%% io:format(" ~p~n", [[Subtype, Color, Attrs, Att_num]]), | |||
case file:read_line(IoDevice) of | |||
{ok, Line} -> | |||
[Line_0] = string:tokens(Line, "\n"), | |||
Vals0 = string:tokens(Line_0, ","), | |||
[Grade0 | Vals] = Vals0, | |||
Grade = tool:to_integer(Grade0), | |||
%% io:format(" ~p~n", [Grade]), | |||
base_goods_practise_2(Subtype, Color, Attrs, Grade, Vals, 10), | |||
base_goods_practise_1(IoDevice, Subtype, Color, Attrs); | |||
eof -> | |||
{ok, finshed}; | |||
{error, Reason} -> | |||
{error, Reason} | |||
end. | |||
base_goods_practise_2(_Subtype, _Color, _Attrs, _Grade, [], _Step) -> | |||
ok; | |||
base_goods_practise_2(Subtype, Color, Attrs, Grade, Vals, Step) -> | |||
%% io:format(" ~p/~p/~p~n", [Grade, Step, length(Vals)]), | |||
case length(Attrs) of | |||
5 -> Att_num = 1, | |||
{L1, L2} = lists:split(length(Vals) - 5, Vals), | |||
%% 神弓(09)_绿色(1)_ | |||
%% 攻击力 单属性 | |||
%% 最大 最小 命中 敏捷 体质 | |||
%% 9/1 | |||
%% max_attack/min_attack/hit/agile/physique | |||
io:format("Here_1_~n", []), | |||
[Field1, Field2, Field3, Field4, Field5] = Attrs, | |||
io:format("Here_2_~n", []), | |||
[Val1, Val2, Val3, Val4, Val5] = L2, | |||
io:format("Here_3_~n", []), | |||
Field_Value_List = [{att_num, Att_num}, {subtype, Subtype}, {step, Step}, {color, Color}, {grade, Grade}] | |||
++ [{tool:to_atom(Field1), tool:to_integer(Val1)}] | |||
++ [{tool:to_atom(Field2), tool:to_integer(Val2)}] | |||
++ [{tool:to_atom(Field3), tool:to_integer(Val3)}] | |||
++ [{tool:to_atom(Field4), tool:to_integer(Val4)}] | |||
++ [{tool:to_atom(Field5), tool:to_integer(Val5)}], | |||
io:format("Here_4_/~p/~n", [Field_Value_List]), | |||
Sql = make_replace_sql(base_goods_practise, Field_Value_List), | |||
%% io:format(" ~p/~p/~p/~p~n", [Grade, Step, length(Vals), Sql]), | |||
db_sql:execute(Sql), | |||
base_goods_practise_2(Subtype, Color, Attrs, Grade, L1, Step - 1), | |||
ok; | |||
7 -> _Att_num = 2, | |||
{L1, _L2} = lists:split(length(Vals) - 7, Vals), | |||
base_goods_practise_2(Subtype, Color, Attrs, Grade, L1, Step - 1), | |||
ok | |||
end, | |||
ok. | |||
make_replace_sql(Table_name, Field_Value_List) -> | |||
{Vsql, _Count1} = | |||
lists:mapfoldl( | |||
fun(Field_value, Sum) -> | |||
Expr = case Field_value of | |||
{Field, Val} -> | |||
case is_binary(Val) orelse is_list(Val) of | |||
true -> | |||
io_lib:format("`~s`='~s'", [Field, re:replace(Val, "'", "''", [global, {return, binary}])]); | |||
_ -> io_lib:format("`~s`='~p'", [Field, Val]) | |||
end | |||
end, | |||
S1 = if Sum == length(Field_Value_List) -> io_lib:format("~s ", [Expr]); | |||
true -> io_lib:format("~s,", [Expr]) | |||
end, | |||
{S1, Sum + 1} | |||
end, | |||
1, Field_Value_List), | |||
lists:concat(["replace into `", Table_name, "` set ", | |||
lists:flatten(Vsql) | |||
]). |
@ -1,45 +0,0 @@ | |||
-module(mysql_test). | |||
-compile(export_all). | |||
-define(DB, mysql_conn_poll). | |||
-define(DB_HOST, "localhost"). | |||
-define(DB_PORT, 3386). | |||
-define(DB_USER, "root"). | |||
-define(DB_PASS, "root"). | |||
-define(DB_NAME, "csj"). | |||
-define(DB_ENCODE, utf8). | |||
conn() -> | |||
mysql:start_link(?DB, ?DB_HOST, ?DB_PORT, ?DB_USER, ?DB_PASS, ?DB_NAME, fun(_, _, _, _) -> ok end, ?DB_ENCODE), | |||
mysql:connect(?DB, ?DB_HOST, ?DB_PORT, ?DB_USER, ?DB_PASS, ?DB_NAME, ?DB_ENCODE, true), | |||
% mysql:fetch(?DB, <<"drop table if exists test">>), | |||
% mysql:fetch(?DB, <<"create table test (id int not null auto_increment,row varchar(50) not null,r int not null, primary key (id)) engine = myisam">>), | |||
mysql:fetch(?DB, <<"truncate table test">>), | |||
ok. | |||
test() -> | |||
mysql:fetch(?DB, <<"truncate table test">>), | |||
mysql:fetch(?DB, <<"begin">>), | |||
F = fun() -> | |||
db_sql:execute(io_lib:format(<<"insert into `test` (`row`,`r`) values ('~s',~p)">>, ["我是来测试性能的", 123])), | |||
db_sql:execute(io_lib:format(<<"update `test` set `row` = '~s' where id = ~p">>, ["我是来测试性能的", 1])) | |||
% mysql:fetch(?DB, io_lib:format(<<"insert into `test` (`row`,`r`) values ('~s',~p)">>,["我是来测试性能的",123])) | |||
end, | |||
prof:run(F, 10000), | |||
mysql:fetch(?DB, <<"commit">>), | |||
% mysql:fetch(?DB, <<"begin">>), | |||
% | |||
% F1 = fun() -> | |||
% mysql:fetch(?DB, io_lib:format(<<"update `test` set `row` = '~s' where id = ~p">>,["我是来测试性能的",123])) | |||
% end, | |||
% prof:run(F1, 10000), | |||
% mysql:fetch(?DB, <<"commit">>), | |||
% | |||
% F2 = fun() -> | |||
% mysql:fetch(?DB, <<"select * from `test` where id = 1">>) | |||
% end, | |||
% prof:run(F2, 10000), | |||
ok. | |||
@ -1,396 +0,0 @@ | |||
-module(mysql_to_emongo). | |||
-compile([export_all]). | |||
-include("common111.hrl"). | |||
-define(CONFIG_FILE, "../config/gateway.config"). | |||
-define(PoolId, mysql_conn_for_mongodb). | |||
-define(AUTO_IDS, "auto_ids"). | |||
%% 启动转换程序,只转换基础数据,不会对其它非基础数据产生影响 | |||
start_base() -> | |||
start("base_"), | |||
ok. | |||
%% 启动转换程序注意,会将所有的表数据删除再转换数据 ,在正式的数据中不要调用此方法 | |||
start_all() -> | |||
start(""), | |||
ok. | |||
start_single([Atom]) -> | |||
Prefix = util:term_to_string(Atom), | |||
start(Prefix), | |||
ok. | |||
start_single2([Atom]) -> | |||
Prefix = util:term_to_string(Atom), | |||
start2(Prefix), | |||
ok. | |||
%%清档,删除原有的非基础数据和管理员数据 | |||
start_clear() -> | |||
TableList = lib_player_rw:get_all_tables(), | |||
F = fun(TableName) -> | |||
TableName1 = util:term_to_string(TableName), | |||
case TableName1 =/= "cards" andalso TableName1 =/= "sys_acm" andalso string:str(TableName1, "admin") =/= 1 | |||
andalso TableName1 =/= "auto_ids" andalso TableName1 =/= "shop" andalso string:str(TableName1, "base") =/= 1 of | |||
false -> skip; | |||
true -> | |||
emongo:delete(tool:to_list(?MASTER_POOLID), TableName1, []) | |||
end | |||
end, | |||
[F(TableName) || TableName <- TableList], | |||
ok. | |||
%% 启动转换程序 | |||
start(Prefix) -> | |||
case get_mysql_config(?CONFIG_FILE) of | |||
[Host, Port, User, Password, DB_name, Encode] -> | |||
mysql:start_link(?PoolId, Host, Port, User, Password, DB_name, fun(_, _, _, _) -> ok end, Encode), | |||
mysql:connect(?PoolId, Host, Port, User, Password, DB_name, Encode, true), | |||
case get_mongo_config(?CONFIG_FILE) of | |||
[MongoPoolId, EmongoHost, EmongoPort, EmongoDatabase, EmongoSize] -> | |||
init_mongo([MongoPoolId, EmongoHost, EmongoPort, EmongoDatabase, EmongoSize]), | |||
io:format("Mysql~p ==>Mongo ~p~n", [[Host, Port, User, Password, DB_name], [EmongoHost, EmongoPort, EmongoDatabase]]), | |||
io:format("Prefix ==>Mongo ~p~n", [Prefix]), | |||
read_write_tables(DB_name, Prefix), | |||
ok; | |||
_ -> emongo_config_fail | |||
end, | |||
ok; | |||
_ -> mysql_config_fail | |||
end, | |||
halt(), | |||
ok. | |||
%% 启动转换程序 | |||
start2(Prefix) -> | |||
case get_mysql_config(?CONFIG_FILE) of | |||
[Host, Port, User, Password, DB_name, Encode] -> | |||
mysql:start_link(?PoolId, Host, Port, User, Password, DB_name, fun(_, _, _, _) -> ok end, Encode), | |||
mysql:connect(?PoolId, Host, Port, User, Password, DB_name, Encode, true), | |||
case get_mongo_config(?CONFIG_FILE) of | |||
[MongoPoolId, EmongoHost, EmongoPort, EmongoDatabase, EmongoSize] -> | |||
init_mongo([MongoPoolId, EmongoHost, EmongoPort, EmongoDatabase, EmongoSize]), | |||
io:format("Mysql~p ==>Mongo ~p~n", [[Host, Port, User, Password, DB_name], [EmongoHost, EmongoPort, EmongoDatabase]]), | |||
io:format("Prefix ==>Mongo ~p~n", [Prefix]), | |||
read_write_tables(DB_name, Prefix), | |||
ok; | |||
_ -> emongo_config_fail | |||
end, | |||
ok; | |||
_ -> mysql_config_fail | |||
end, | |||
ok. | |||
get_mysql_config(Config_file) -> | |||
try | |||
{ok, [L]} = file:consult(Config_file), | |||
{_, C} = lists:keyfind(gateway, 1, L), | |||
{_, Mysql_config} = lists:keyfind(mysql_config, 1, C), | |||
{_, 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), | |||
[Host, Port, User, Password, DB, Encode] | |||
catch | |||
_:_ -> no_config | |||
end. | |||
get_mongo_config(Config_file) -> | |||
try | |||
{ok, [L]} = file:consult(Config_file), | |||
{_, C} = lists:keyfind(gateway, 1, L), | |||
{_, Emongo_config} = lists:keyfind(emongo_config, 1, C), | |||
{_, MongoPoolId} = lists:keyfind(poolId, 1, Emongo_config), | |||
{_, EmongoHost} = lists:keyfind(emongoHost, 1, Emongo_config), | |||
{_, EmongoPort} = lists:keyfind(emongoPort, 1, Emongo_config), | |||
{_, EmongoDatabase} = lists:keyfind(emongoDatabase, 1, Emongo_config), | |||
{_, EmongoSize} = lists:keyfind(emongoSize, 1, Emongo_config), | |||
[MongoPoolId, EmongoHost, EmongoPort, EmongoDatabase, EmongoSize] | |||
catch | |||
_:_ -> no_config | |||
end. | |||
%%初始化emongoDB链接 | |||
init_mongo([MongoPoolId, EmongoHost, EmongoPort, EmongoDatabase, EmongoSize]) -> | |||
emongo_sup:start_link(), | |||
emongo_app:initialize_pools([MongoPoolId, EmongoHost, EmongoPort, EmongoDatabase, EmongoSize]), | |||
%% emongo:insert(tool:to_list(?MASTER_POOLID),"b",[{id, 111},{name,"111ls"},{age,130}]), | |||
%% Bin1 = emongo:find_one(tool:to_list(?MASTER_POOLID), "player", [{"id", 1424}]), | |||
ok. | |||
%%读写操作,将mysql数据转换为emongo文档对象 | |||
%% SELECT column_name,data_type, column_key, extra FROM information_schema.columns WHERE table_schema='csj_dev' and table_name='adminkind' | |||
read_write_tables(DB_name, Prefix) -> | |||
timer:sleep(5 * 1000), | |||
if Prefix =:= "" -> | |||
emongo:delete(tool:to_list(?MASTER_POOLID), ?AUTO_IDS, []); | |||
true -> | |||
no_action | |||
end, | |||
Sql = "SELECT table_name FROM information_schema.tables WHERE table_schema='" ++ DB_name ++ "' and table_type ='BASE TABLE'", | |||
emongo:ensure_index(tool:to_list(?MASTER_POOLID), "auto_ids", [{<<"id">>, 1}]), | |||
emongo:ensure_index(tool:to_list(?MASTER_POOLID), "auto_ids", [{<<"name">>, 1}]), | |||
Data = mysql:fetch(?PoolId, list_to_binary(Sql)), | |||
R = handleResult(Data),%%R is [[<<"adminchange">>],[<<"admingroup">>],[<<"adminkind">>]] | |||
F = fun(D) -> | |||
[R1] = D, %%R1 is <<"adminchange">> | |||
Index = string:str(binary_to_list(R1), Prefix), | |||
if Prefix =:= "" orelse Index =:= 1 orelse | |||
((Prefix =:= "" orelse Prefix =:= "base_") andalso R1 =:= <<"shop">>) | |||
orelse R1 =:= <<"adminkind">> -> | |||
Sql1 = "SELECT column_name,data_type,column_key,extra FROM information_schema.columns WHERE table_schema= '" ++ DB_name ++ "' AND table_name= '" ++ binary_to_list(R1) ++ "'", | |||
Sql2 = "SELECT * FROM " ++ binary_to_list(R1), | |||
Result1 = mysql:fetch(?PoolId, list_to_binary(Sql1)), | |||
Result2 = mysql:fetch(?PoolId, list_to_binary(Sql2)), | |||
ColumnAndType = handleResult(Result1),%%ColumnAndType is [[<<"id">>,<<"int">>],[<<"name">>,<<"varchar">>],[<<"pid">>,<<"varchar">>],[<<"url">>,<<"varchar">>]], | |||
TableRecord = handleResult(Result2), | |||
records_to_documents(DB_name, binary_to_list(R1), ColumnAndType, TableRecord), | |||
if R1 =:= <<"adminkind">> -> | |||
%% 转换后,设置“资源管理”为不显示 | |||
emongo:update(tool:to_list(?MASTER_POOLID), <<"adminkind">>, | |||
[{<<"name">>, <<"资源管理">>}], [{"$set", [{<<"show">>, <<"NO">>}]}]), | |||
ok; | |||
true -> no_action | |||
end; | |||
true -> skip | |||
end | |||
end, | |||
[F(D) || D <- R], | |||
%%更新base数据时同步更新其它非base表的索引 | |||
if Prefix =:= "base_" -> | |||
add_other_table_index(DB_name, R); | |||
true -> | |||
skip | |||
end, | |||
ok. | |||
%%当调用start_base时将其它非base表的索引也同步过来,base表已在前面同步过 | |||
%%R is [[<<"adminchange">>],[<<"admingroup">>],[<<"adminkind">>]] | |||
add_other_table_index(DB_name, R) -> | |||
F = fun(D) -> | |||
[R1] = D, %%R1 is <<"adminchange">> | |||
binary_to_list(R1)%%R1 is "adminchange" | |||
end, | |||
TableList = [F(D) || D <- R], | |||
OtherTables = [T || T <- TableList, string:str(T, "base") =/= 1],%%除掉所有base开头的表 | |||
F1 = fun(TableName) -> | |||
Sql1 = "SELECT column_name,data_type,column_key,extra FROM information_schema.columns WHERE table_schema= '" ++ DB_name ++ "' AND table_name= '" ++ TableName ++ "'", | |||
Result1 = mysql:fetch(?PoolId, list_to_binary(Sql1)), | |||
ColumnAndType = handleResult(Result1),%%ColumnAndType is [[<<"id">>,<<"int">>],[<<"name">>,<<"varchar">>],[<<"pid">>,<<"varchar">>],[<<"url">>,<<"varchar">>]], | |||
KeyList = [Key || [_Name, _Type, Key, _Extra] <- ColumnAndType, Key =:= <<"UNI">> orelse Key =:= <<"MUL">> orelse Key =:= <<"PRI">>], | |||
case length(KeyList) of | |||
0 -> | |||
io:format("~s Warning...No Key Table: [~p]\n", [misc:time_format(now()), TableName]); | |||
_ -> | |||
skip | |||
end, | |||
%%添加主键索引和唯一索引 | |||
lists:foreach(fun(FieldAndType) -> | |||
[Name, _Type, Key, Extra] = FieldAndType, | |||
if Key =:= <<"PRI">>, Extra =:= <<"auto_increment">> -> | |||
emongo:ensure_index(tool:to_list(?MASTER_POOLID), TableName, [{Name, 1}]); | |||
Key =:= <<"UNI">> orelse Key =:= <<"MUL">> orelse Key =:= <<"PRI">> -> | |||
emongo:ensure_index(tool:to_list(?MASTER_POOLID), TableName, [{Name, 1}]); | |||
true -> | |||
ok | |||
end | |||
end, | |||
ColumnAndType), | |||
%%添加处理联合索引 | |||
IndexSql = "SELECT column_name FROM information_schema.columns WHERE table_schema= '" ++ DB_name ++ "' AND table_name= '" ++ (TableName) ++ "' AND column_key ='MUL'", | |||
IndexResult = handleResult(mysql:fetch(?PoolId, list_to_binary(IndexSql))), | |||
case IndexResult of | |||
[] -> skip; | |||
_ -> | |||
IndexResultSize = length(IndexResult), | |||
F3 = fun(II) -> | |||
[RR] = lists:nth(II, IndexResult), | |||
{binary_to_list(RR), 1} | |||
end, | |||
LL = [F3(II) || II <- lists:seq(1, IndexResultSize)], | |||
emongo:ensure_index(tool:to_list(?MASTER_POOLID), TableName, LL) | |||
end | |||
end, | |||
[F1(Table) || Table <- OtherTables], | |||
ok. | |||
%%["a"] -> "a" | |||
term_to_string(Term) -> | |||
binary_to_list(list_to_binary(io_lib:format("~p", [Term]))). | |||
string_to_term(String) -> | |||
case erl_scan:string(String ++ ".") of | |||
{ok, Tokens, _} -> | |||
case erl_parse:parse_term(Tokens) of | |||
{ok, Term} -> Term; | |||
_Err -> undefined | |||
end; | |||
_Error -> | |||
undefined | |||
end. | |||
%%将mysql记录转换为emongo的document | |||
%%TableName like "test" | |||
%%ColumnAndType like [[<<"id">>,<<"int">>], | |||
%% [<<"row">>,<<"varchar">>], | |||
%% [<<"r">>,<<"int">>]] | |||
%%TableRecord like [[1,111111,<<"111111">>], | |||
%% [2,9898,<<"9898bf">>]] | |||
records_to_documents(DB_name, TableName, ColumnAndType, TableRecord) -> | |||
ErrList = [Head || [Head | Tail] <- TableRecord, len_chk([Head | Tail]) =/= true], | |||
if | |||
length(ErrList) > 0 andalso TableName =/= "base_talk" -> | |||
io:format("Waring for length!!!!! Table Name ~p.....Head:~p....... ~n", [TableName, ErrList]); | |||
true -> | |||
skip | |||
end, | |||
case length(TableRecord) of | |||
0 -> | |||
H0 = lists:map(fun(FieldAndType) -> | |||
[Name, _, _, _] = FieldAndType, | |||
{tool:to_atom(Name), 0} | |||
end, | |||
ColumnAndType), | |||
EmptyCollection = true, | |||
H = [H0]; | |||
_ -> | |||
EmptyCollection = false, | |||
F = fun(R) -> | |||
CtList = mergeList(ColumnAndType), | |||
ColumnSize = length(CtList), | |||
[{lists:nth(I, CtList), lists:nth(I, R)} || I <- lists:seq(1, ColumnSize)] | |||
end, | |||
H = [F(Record) || Record <- TableRecord] %% H like [[{<<"id">>,1},{<<"name">>,<<"zs">>}],[[{<<"id">>,2},{<<"name">>,<<"ls">>}]] | |||
end, | |||
%%不删除cards表及管理员表 | |||
case TableName =/= "cards" | |||
andalso TableName =/= "base_com_gift" %%补偿奖励配置表不清除 | |||
%% start_all 删除sys_acm表 | |||
%% andalso TableName =/= "sys_acm" | |||
andalso string:str(TableName, "admin") =/= 1 of | |||
false -> skip; | |||
true -> | |||
emongo:delete(tool:to_list(?MASTER_POOLID), TableName, []), | |||
Mysql_count = length(TableRecord), | |||
io:format("handle: ~p ...", [TableName]), | |||
insert_to_emongo(TableName, H), | |||
case EmptyCollection of | |||
true -> emongo:delete(tool:to_list(?MASTER_POOLID), TableName, []); | |||
false -> no_action | |||
end, | |||
KeyList = [Key || [_Name, _Type, Key, _Extra] <- ColumnAndType, Key =:= <<"UNI">> orelse Key =:= <<"MUL">> orelse Key =:= <<"PRI">>], | |||
case length(KeyList) of | |||
0 -> | |||
io:format("\n ############## Warning...No Key Table: [~p] #################\n", [TableName]); | |||
_ -> | |||
skip | |||
end, | |||
%%添加主键索引和唯一索引 | |||
lists:foreach(fun(FieldAndType) -> | |||
[Name, _Type, Key, Extra] = FieldAndType, | |||
if Key =:= <<"PRI">>, Extra =:= <<"auto_increment">> -> | |||
emongo:ensure_index(tool:to_list(?MASTER_POOLID), TableName, [{Name, 1}]), | |||
create_max_id(TableName, Name); | |||
Key =:= <<"UNI">> orelse Key =:= <<"MUL">> orelse Key =:= <<"PRI">> -> | |||
emongo:ensure_index(tool:to_list(?MASTER_POOLID), TableName, [{Name, 1}]); | |||
true -> | |||
ok | |||
end | |||
end, | |||
ColumnAndType), | |||
%%添加处理联合索引 | |||
IndexSql = "SELECT column_name FROM information_schema.columns WHERE table_schema= '" ++ DB_name ++ "' AND table_name= '" ++ (TableName) ++ "' AND column_key ='MUL'", | |||
IndexResult = handleResult(mysql:fetch(?PoolId, list_to_binary(IndexSql))), | |||
case IndexResult of | |||
[] -> skip; | |||
_ -> | |||
IndexResultSize = length(IndexResult), | |||
F3 = fun(II) -> | |||
[RR] = lists:nth(II, IndexResult), | |||
{binary_to_list(RR), 1} | |||
end, | |||
LL = [F3(II) || II <- lists:seq(1, IndexResultSize)], | |||
emongo:ensure_index(tool:to_list(?MASTER_POOLID), TableName, LL) | |||
end, | |||
Mongo_count = | |||
case emongo:count(tool:to_list(?MASTER_POOLID), tool:to_list(TableName), []) of | |||
undefined -> 0; | |||
Val -> Val | |||
end, | |||
if Mysql_count =:= Mongo_count -> | |||
io:format(" [~p]==>[~p] finished! ~n", [Mysql_count, Mongo_count]); | |||
true -> | |||
io:format(" [~p]==>[~p] ERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!! ~n", [Mysql_count, Mongo_count]) | |||
end | |||
end, | |||
ok. | |||
%%将mysql:fetch(?DB,Bin)查询结果转换为[[A]]形式, | |||
handleResult(Data) -> | |||
{_, Bin} = Data, | |||
{_, _, R, _, _} = Bin, %%R is [[<<"adminchange">>],[<<"admingroup">>],[<<"adminkind">>]] | |||
R. | |||
%%将列表转换形式[[<<"id">>,<<"int">>],[[<<"name">>,<<"varchar">>],<<"age">>,<<"int">>]] -> [<<"id">>,<<"name">>,<<"age">>] | |||
mergeList(List) -> | |||
F = fun(L1) -> | |||
[Name, _Type, _Key, _Extra] = L1, | |||
Name | |||
end, | |||
[F(L) || L <- List]. | |||
%%插入数据 | |||
insert_to_emongo(TableName, H) -> | |||
%% emongo:insert(tool:to_list(?MASTER_POOLID),"b",[{id, 111},{name,"111ls"},{age,130}]), | |||
F = fun(R) -> | |||
%% io:format("R is ~p~n",[R]), | |||
emongo:insert(tool:to_list(?MASTER_POOLID), TableName, R) | |||
end, | |||
[F(R) || R <- H], | |||
ok. | |||
%% 创建最大自增id | |||
create_max_id(TableName, Name) -> | |||
try | |||
%% io:format("create_max_id_0_~p~n",[[TableName, Name]]), | |||
Sql = "select max(" ++ tool:to_list(Name) ++ ") from " ++ tool:to_list(TableName), | |||
%% io:format("create_max_id_1_~p~n",[Sql]), | |||
Result = mysql:fetch(?PoolId, list_to_binary(Sql)), | |||
[[MaxId]] = handleResult(Result), | |||
%% io:format("create_max_id_1_~p~n",[MaxId]), | |||
MaxId_1 = | |||
case MaxId of | |||
null -> 0; | |||
undefined -> 0; | |||
_ -> MaxId | |||
end, | |||
%% io:format("create_max_id_2_~p~n",[[TableName, Name, MaxId]]), | |||
emongo:delete(tool:to_list(?MASTER_POOLID), ?AUTO_IDS, [{name, TableName}]), | |||
emongo:insert(tool:to_list(?MASTER_POOLID), ?AUTO_IDS, [{name, TableName}, {Name, MaxId_1}]) | |||
catch | |||
_:_ -> error | |||
end, | |||
ok. | |||
%%mysql字段长度检测 | |||
len_chk([]) -> | |||
true; | |||
len_chk(ChkList) -> | |||
[H | T] = ChkList, | |||
Length = length(tool:to_list(H)), | |||
if | |||
Length > 250 -> | |||
%% io:format("len_chk_2_~p~n",[Length]), | |||
false; | |||
true -> | |||
len_chk(T) | |||
end. |
@ -1,139 +0,0 @@ | |||
-module(random_test). | |||
%% | |||
%% Include files | |||
%% | |||
-include("common111.hrl"). | |||
%% | |||
%% Exported Functions | |||
%% | |||
-compile(export_all). | |||
%% | |||
%% API Functions | |||
%% | |||
random_test() -> | |||
?DEBUG("Start ~p", [[0]]), | |||
random_test_loop(1000000), | |||
?DEBUG("Finish ~p", [[0]]). | |||
random_test_loop(T) -> | |||
case T < 1 of | |||
true -> | |||
[]; | |||
_ -> | |||
R = util:rand(1, 10000), | |||
io:format("~p\n", [R]), | |||
random_test_loop(T - 1) | |||
end. | |||
%% acid_to_player() -> | |||
%% PlayerDB = ?DB_MODULE:select_all(player, "*", []), | |||
%% F = fun(CD) -> | |||
%% Cin = list_to_tuple([player | CD]), | |||
%% case Cin#player.acid of | |||
%% 0 -> | |||
%% case ?DB_MODULE:select_row(user,"*",[{acnm,Cin#player.acnm}]) of | |||
%% [] -> | |||
%% ACId = ?DB_MODULE:insert(user, [acid,acnm,state,idcrs,sn], [0,Cin#player.acnm,0,0,0]), | |||
%% ?DB_MODULE:update(user, [{acid,ACId}], [{id,ACId}]); | |||
%% D -> | |||
%% [ACId,_acid,_acnm,_st,_idc,_sn] = D | |||
%% end, | |||
%% ?DB_MODULE:update(player,[{acid, ACId}],[{id, Cin#player.id}]); | |||
%% _ -> | |||
%% [] | |||
%% end | |||
%% end, | |||
%% lists:foreach(F, PlayerDB). | |||
idcrs_clear() -> | |||
UsrDB = ?DB_MODULE:select_all(user, "*", []), | |||
F = fun(CD) -> | |||
[Id, _ACId, _Acnm, _St, _Id, _sn] = CD, | |||
?DB_MODULE:update(user, [{idcrs, 0}], [{id, Id}]) | |||
end, | |||
lists:foreach(F, UsrDB). | |||
%% relationship2relaTrans() -> | |||
%% Relationship = ?DB_MODULE:select_all(relationship, "*", [{rela,0}]), | |||
%% F = fun(CD) -> | |||
%% [Id,IDA,IDB,Re,Al,Bsex,Bnick,Bcar,Bg,Bj,Asex,Anick,Acar,Ag,Aj] = CD, | |||
%% RD = db_agent_rela:insert_rela(IDA,0,Anick,Asex,Acar,0,"",1), | |||
%% Cin = list_to_tuple([ets_rela | RD]), | |||
%% NFR = {IDB,Bnick,Bsex,Bcar,0,[]}, | |||
%% NFRALL = [NFR|Cin#ets_rela.frid], | |||
%% NCin = Cin#ets_rela{frid = NFRALL,fn = Cin#ets_rela.fn+1}, | |||
%% db_agent_rela:update_rela_by_uid(NCin) | |||
%% end, | |||
%% lists:foreach(F, Relationship). | |||
fore_test() -> | |||
lists:foreach(fun(D) -> | |||
io:format(" ~p ", [D]) | |||
end, [1, 2, 3, 4]). | |||
log_player() -> | |||
D = ?DB_MODULE:select_all(player, "id,acid,acnm,nick,sex,crr", []), | |||
%% ?DEBUG("~p",[D]), | |||
F = fun(CD) -> | |||
[Uid, Acid, Acnm, Nick, Sex, Crr] = CD, | |||
?DB_LOG_MODULE:insert(log_player, [uid, acid, acnm, nick, sex, crr], [Uid, Acid, Acnm, Nick, Sex, Crr]) | |||
end, | |||
lists:foreach(F, D). | |||
lists_find() -> | |||
Lis = data_name_list:get_list(), | |||
Start = util:unixtime(), | |||
io:format("Start Time ~p~n", [Start]), | |||
find_test(Lis, 100000), | |||
End = util:unixtime(), | |||
io:format("End Time ~p~n", [End]). | |||
find_test(Lis, Num) -> | |||
if Num < 1 -> | |||
ok; | |||
true -> | |||
FindNum = 100001 + random:uniform(1000), | |||
lists:keyfind(FindNum, 1, Lis), | |||
find_test(Lis, Num - 1) | |||
end. | |||
get_player(Lv) -> | |||
D = ?DB_MODULE:select_all(player, "acnm,lv", [{lv, ">=", Lv}]), | |||
%% ?DEBUG("~p",[D]). | |||
F = fun(CD) -> | |||
[Acnm, NLv] = CD, | |||
Acm = util:string_to_term(tool:to_list(Acnm)), | |||
{Acm, NLv} | |||
%% ?DB_LOG_MODULE:insert(log_player,[uid,acid,acnm,nick], [Uid,Acid,Acnm,Nick]) | |||
end, | |||
Res = lists:map(F, D), | |||
Start = util:unixtime(), | |||
FileName = io_lib:format("./~p.txt", [Start]), | |||
{ok, FileIo} = file:open(FileName, [write]), | |||
File = io_lib:format("~p", [Res]), | |||
file:write(FileIo, File), | |||
file:close(FileIo). | |||
@ -1,49 +0,0 @@ | |||
%% @author Administrator | |||
%% @doc @todo Add description to chat_script. | |||
-module(chat_script). | |||
-define(HEADER_LENGTH, 4). % | |||
%% ==================================================================== | |||
%% API functions | |||
%% ==================================================================== | |||
-compile(export_all). | |||
call(11010, {Content}, Sokcet) -> | |||
Len1 = byte_size(Content), | |||
Data = <<Len1:16, Content/binary>>, | |||
gen_tcp:send(Sokcet, pack(11010, Data)); | |||
call(11070, {Id, Content}, Sokcet) -> | |||
Len1 = byte_size(Content), | |||
Data = <<Id:32, Len1:16, Content/binary>>, | |||
gen_tcp:send(Sokcet, pack(11070, Data)). | |||
%% <<Id:32, Len:16, Nick1/binary, Len1:16, Bin1/binary>> | |||
handle_socket(11010, BinData) -> | |||
io:format("rec package ~p~n", [11010]), | |||
<<Id:32, Len1:16, Rest1/binary>> = BinData, | |||
<<Nick:Len1/binary-unit:8, Len2:16, Rest2/binary>> = Rest1, | |||
<<Content:Len2/binary-unit:8>> = Rest2, | |||
Nick1 = binary_to_list(Nick), | |||
Content1 = binary_to_list(Content), | |||
io:format(" nick name ~p , content ~p~n", [Nick1, Content1]), | |||
BinData; | |||
handle_socket(11070, BinData) -> | |||
io:format("rec package ~p~n", [11070]), | |||
<<Id:32, Len1:16, Rest1/binary>> = BinData, | |||
<<Nick:Len1/binary-unit:8, Len2:16, Rest2/binary>> = Rest1, | |||
<<Content:Len2/binary-unit:8>> = Rest2, | |||
Nick1 = binary_to_list(Nick), | |||
Content1 = binary_to_list(Content), | |||
io:format(" nick name ~p , content ~p~n", [Nick1, Content1]), | |||
BinData; | |||
handle_socket(_, _) -> | |||
void. | |||
%% ==================================================================== | |||
%% Internal functions | |||
%% ==================================================================== | |||
%%打包数据 | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. |
@ -1,84 +0,0 @@ | |||
%% @author Johanthe_Yip | |||
%% @doc 旧项目邮件模块接口调试 ,由于本项目之前用于两个项目中,故包换两种邮件协议,该脚本 | |||
%% 对两项目协议进行调试 | |||
-module(mail_script). | |||
%% ==================================================================== | |||
%% API functions | |||
%% ==================================================================== | |||
-compile(export_all). | |||
-define(HEADER_LENGTH, 4). % | |||
%%-----------旧版邮件协议请求服务器 1900x c_2_s ------------ | |||
%%==========获取信件列表及内容============= | |||
call(19004, {Mail_type, Mail_page}, Sokcet) -> | |||
gen_tcp:send(Sokcet, pack(19004, <<Mail_type:8, Mail_page:8>>)); | |||
%%==========提取附件========== | |||
call(19006, {Mail_id}, Sokcet) -> | |||
gen_tcp:send(Sokcet, pack(19006, <<Mail_id:32>>)); | |||
%%-----------新版邮件协议请求服务器 190x c_2_s--------------- | |||
%%==========获取信件列表及内容========= | |||
call(19051, {Page_index}, Sokcet) -> | |||
gen_tcp:send(Sokcet, pack(19051, <<Page_index:16>>)). | |||
%%-----------旧版邮件协议接收信息 1900x s_2_c ------------ | |||
%%==========获取信件列表及内容============= | |||
handle_socket(19004, BinData) -> | |||
<<Result:8, Rest/binary>> = BinData, | |||
if Result =/= 0 -> | |||
<<Mail_num:8, Mail_page:8, MailNum:16, BinList/binary>> = Rest, | |||
io:format("rec package ~p~n", [19004]), | |||
{Maillist, _} = get_list([], BinList, MailNum), | |||
io:format("package info: ~p~n", [Maillist]); | |||
true -> | |||
io:format("rec package but no data ~p ~n", [19004]) end, | |||
BinData; | |||
%%==========提取附件========== | |||
handle_socket(19006, BinData) -> | |||
io:format("rec package ~p~n", [19006]), | |||
<<Result:16, MailId:32>> = BinData, | |||
io:format("package info Result:~p MailId:~p~n", [Result, MailId]); | |||
%%-----------新版邮件协议请求服务器 190x s_2_c--------------- | |||
%%==========获取信件列表及内容========= | |||
handle_socket(19051, BinData) -> | |||
io:format("rec package ~p~n", [19051]), | |||
BinData; | |||
handle_socket(_, _) -> | |||
void. | |||
%% 读取列表,列表每项:[Id, Type,State, Timestamp, SName, Uid,Title, Content,Goods_list,Coin,Gold] | |||
%% 对应:<<Id:32, Type:8, State:8, Timestamp:32, Len1:16, SName/binary, Len2:16, Title/binary, Len3:16, Content/binary, GLen:16, GoodsBin/binary, Gold:32, Coin:32>> | |||
%% AccList 列表累加器,使用时初始为[] | |||
get_list(AccList, Bin, N) when N > 0 -> | |||
case Bin of | |||
<<Id:32, Type:8, State:8, Timestamp:32, Len1:16, Rest/binary>> -> | |||
<<SName:Len1/binary-unit:8, Len2:16, Rest2/binary>> = Rest, | |||
<<Title:Len2/binary-unit:8, Len3:16, Rest3/binary>> = Rest2, | |||
<<Content:Len3/binary-unit:8, Len4:16, Rest4/binary>> = Rest3, | |||
<<GoodsBin:Len4/binary-unit:8, Gold:32, Coin:32, Rest5/binary>> = Rest4, | |||
SName1 = binary_to_list(SName), | |||
Title1 = binary_to_list(Title), | |||
Content1 = binary_to_list(Content), | |||
GoodsBin1 = binary_to_list(GoodsBin), | |||
Item = [Id, Type, State, Timestamp, SName, Title, Content, GoodsBin, Coin, Gold], | |||
%io:format("Item: ~p~n", [Item]), | |||
NewList = [Item | AccList], | |||
get_list(NewList, Rest5, N - 1); | |||
_R1 -> | |||
error | |||
end; | |||
get_list(AccList, Bin, _) -> | |||
{lists:reverse(AccList), Bin}. | |||
%%打包数据 | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. |
@ -1,850 +0,0 @@ | |||
%%% ------------------------------------------------------------------- | |||
%%% @Author : Johanathe_Yip | |||
%%% @Created : 2013.01.13 | |||
%%% ------------------------------------------------------------------- | |||
-module(new_robot). | |||
-behaviour(gen_server). | |||
-compile(export_all). | |||
-include("common111.hrl"). | |||
-include_lib("stdlib/include/ms_transform.hrl"). | |||
-define(CONFIG_FILE, "../config/gateway.config"). | |||
%% 连接网关端口,不读取gateway配置 | |||
-define(GATEWAY_ADD, "127.0.0.1"). | |||
%% -define(GATEWAY_ADD,"192.168.51.131"). | |||
-define(GATEWAY_PORT, 7777). | |||
-define(ACTION_SPEED_CONTROL, 10). | |||
-define(ACTION_INTERVAL, ?ACTION_SPEED_CONTROL * 1000). % 自动行为最大时间间隔 | |||
-define(ACTION_MIN, 3000). % 自动行为最小时间间隔 | |||
-define(TCP_OPTS, [ | |||
binary, | |||
{packet, 0}, % no packaging | |||
{reuseaddr, true}, % allow rebind without waiting | |||
{nodelay, false}, | |||
{delay_send, true}, | |||
{active, false}, | |||
{exit_on_close, false} | |||
]). | |||
-define(ETS_ROBOT, ets_robot). | |||
-define(CHECK_ROBOT_STATUS, 1 * 60 * 1000). | |||
%% -define(debug,1). | |||
%% 断言以及打印调试信息宏 | |||
-ifdef(debug). | |||
-define(TRACE(Str), io:format(Str)). | |||
-define(TRACE(Str, Args), io:format(Str, Args)). | |||
% unicode版 | |||
-define(TRACE_W(Str), io:format("~ts", [list_to_binary(io_lib:format(Str, []))])). | |||
-define(TRACE_W(Str, Args), io:format("~ts", [list_to_binary(io_lib:format(Str, Args))])). | |||
-else. | |||
-define(TRACE(Str), void). | |||
-define(TRACE(Str, Args), void). | |||
-define(TRACE_W(Str), void). | |||
-define(TRACE_W(Str, Args), void). | |||
-endif. | |||
%% gen_server callbacks | |||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). | |||
-record(robot, { | |||
orig_n, | |||
login, | |||
acid, %%account id | |||
socket, %%socket | |||
socket2, | |||
socket3, | |||
pid, %%process id | |||
x, %%x坐标 | |||
y, %%y坐标 | |||
scene, | |||
tox, | |||
toy, | |||
hp, | |||
id, %% id | |||
act, %% 动作 | |||
status, %% 当前状态 | |||
dstScene, | |||
step, | |||
frda, %% 好友信息 | |||
bkda, %% 黑名单信息, | |||
sgda %% 陌生人信息 | |||
}). | |||
%%% | |||
%%% API | |||
start() -> | |||
start(30000, 1), | |||
ok. | |||
%%StartId 起始AccountID | |||
%%Num int 数量 | |||
%%Mod 跑步模式 1 ,2 | |||
start(StartId, Num) -> | |||
sleep(100), | |||
F = fun(N) -> | |||
?TRACE("start robot-~p~n", [N]), | |||
sleep(100), | |||
?MODULE:start_link(StartId + N) | |||
end, | |||
for(0, Num, F), | |||
ok. | |||
%%创建 一个ROBOT 进程 | |||
start_link(N) -> | |||
case gen_server:start(?MODULE, [N], []) of | |||
{ok, _Pid} -> | |||
?TRACE("--robot~p start finish!-~n", [N]); | |||
%gen_server:cast(Pid, {start_action}); | |||
_ -> | |||
fail | |||
end. | |||
%% -------------------------------------------------------------------- | |||
%% Function: init/1 | |||
%% Description: Initiates the server | |||
%% Returns: {ok, State} | | |||
%% {ok, State, Timeout} | | |||
%% ignore | | |||
%% {stop, Reason} | |||
%% -------------------------------------------------------------------- | |||
%%初始化玩家数据 | |||
init([N]) -> | |||
process_flag(trap_exit, true), | |||
Pid = self(), | |||
case login(N, Pid) of | |||
{ok, Socket} -> | |||
Scene = 10101, | |||
Robot = #robot{socket = Socket, | |||
login = 0, | |||
acid = N, | |||
id = 0, | |||
pid = Pid, | |||
act = none, | |||
status = none, | |||
scene = Scene, | |||
dstScene = Scene, | |||
tox = rand(1, 40), | |||
toy = rand(1, 20), | |||
orig_n = N, | |||
step = 0, | |||
frda = [], %% 好友信息 | |||
bkda = [], %% 黑名单信息, | |||
sgda = []%% 陌生人信息 | |||
}, | |||
%%登陆成功后开始动作 | |||
%% %%旧协议收邮件 | |||
%% mail_script:call(19004,{1,1},Socket), | |||
%%旧协议收附件 | |||
%% mail_script:call(19006,{1},Socket), | |||
%% chat_script:call(11010,{<<"11">>},Socket), | |||
%% chat_script:call(11070,{1,<<"11">>},Socket), | |||
%% task_script:call(30003,1,Socket), | |||
%% task_script:call(30003,2,Socket), | |||
{ok, Robot}; | |||
_Error -> | |||
?TRACE("init: error, reason: ~p~n", [_Error]), | |||
{stop, normal, {}} | |||
end. | |||
%% -------------------------------------------------------------------- | |||
%% 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({act}, _From, State) -> | |||
%%act有跑步run或者静止undefined | |||
handle(State#robot.act, a, State#robot.socket), | |||
{reply, ok, State}; | |||
handle_call({get_state}, _From, State) -> | |||
{reply, State, State}; | |||
handle_call({get_socket}, _From, State) -> | |||
Reply = State#robot.socket, | |||
{reply, Reply, State}; | |||
handle_call(_Request, _From, State) -> | |||
Reply = ok, | |||
{reply, Reply, State}. | |||
%% -------------------------------------------------------------------- | |||
%% Function: handle_cast/2 | |||
%% Description: Handling cast messages | |||
%% Returns: {noreply, State} | | |||
%% {noreply, State, Timeout} | | |||
%% {stop, Reason, State} (terminate/2 is called) | |||
%% -------------------------------------------------------------------- | |||
handle_cast({login_ok, _Code}, State) -> | |||
?TRACE("login successful~n"), | |||
NewState = State#robot{login = 1}, | |||
{noreply, NewState}; | |||
handle_cast(login_failed, State) -> | |||
?TRACE("login failed~n"), | |||
{stop, normal, State}; | |||
handle_cast({playerid, Id}, State) -> | |||
NewState = State#robot{id = Id}, | |||
{noreply, NewState}; | |||
handle_cast(enter_ok, State) -> | |||
NewState = State#robot{act = run, status = standing}, | |||
gen_server:cast(self(), {start_action}), | |||
{noreply, NewState}; | |||
handle_cast({start_action}, State) -> | |||
if is_port(State#robot.socket) -> | |||
%%心跳进程 | |||
spawn_link(fun() -> handle(heart, a, State#robot.socket) end), | |||
%%Pid= self(), | |||
%%获取个人信息 | |||
handle(get_self_info, 0, State#robot.socket), | |||
%handle(chat,"-加经验 1000000",State#robot.socket), | |||
%handle(chat,"-全功能",State#robot.socket), | |||
{noreply, State}; | |||
true -> | |||
?TRACE("start_action stop_1: /~p/,~n", [State]), | |||
{stop, normal, State} | |||
end; | |||
handle_cast({add_child_socket, N, Socket}, State) -> | |||
NewState = | |||
if | |||
is_pid(State#robot.pid) andalso is_port(Socket) -> | |||
case N of | |||
2 -> State#robot{socket2 = Socket}; | |||
3 -> State#robot{socket3 = Socket}; | |||
_ -> State | |||
end; | |||
true -> | |||
?TRACE(" start_child_socket err : /~p/,~n", [State]), | |||
State | |||
end, | |||
{noreply, NewState}; | |||
handle_cast({upgrade_state, NewState}, _State) -> | |||
{noreply, NewState}; | |||
handle_cast({get_state_13001}, State) -> | |||
handle(get_self_info, a, State#robot.socket), | |||
{noreply, State}; | |||
handle_cast({upgrade_state_13001, [Scene, X, Y, Hp]}, State) -> | |||
NewState = State#robot{x = X, y = Y, hp = Hp, scene = Scene}, | |||
{noreply, NewState}; | |||
handle_cast({upgrade_state_13099, [IdLists]}, State) -> | |||
IdLists1 = [[State#robot.id] | IdLists], | |||
NewState = State#robot{frda = IdLists1}, | |||
{noreply, NewState}; | |||
handle_cast({run}, State) -> | |||
State2 = State#robot{act = run}, | |||
{noreply, State2}; | |||
handle_cast({stop}, State) -> | |||
State2 = State#robot{act = undefined}, | |||
{noreply, State2}; | |||
handle_cast({stop, Reason}, State) -> | |||
?TRACE("~s_quit_2: /~p/~p/~p/,~n", [misc:time_format(now()), State#robot.acid, State#robot.id, Reason]), | |||
{stop, normal, State}; | |||
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({stop, Reason}, State) -> | |||
?TRACE("~s ------ robot stop: /~p/~p/~p/,~n", [misc:time_format(now()), State#robot.acid, State#robot.id, Reason]), | |||
{stop, normal, State}; | |||
handle_info({event, action_random, PlayerId, Socket}, State) -> | |||
Random_interval = random:uniform(?ACTION_INTERVAL * 2) + ?ACTION_MIN * 6, | |||
%% ?TRACE("~s_action_random: ~p~n", [misc:time_format(now()), Random_interval]), | |||
handle_action_random(PlayerId, Socket), | |||
%% 好友机器人测试 | |||
NewState = handle_action_friend(State), | |||
erlang:send_after(Random_interval, self(), {event, action_random, PlayerId, Socket}), | |||
{noreply, NewState}; | |||
handle_info(close, State) -> | |||
gen_tcp:close(State#robot.socket), | |||
{noreply, State}; | |||
handle_info(_Info, State) -> | |||
{noreply, State}. | |||
%% -------------------------------------------------------------------- | |||
%% Function: terminate/2 | |||
%% Description: Shutdown the server | |||
%% Returns: any (ignored by gen_server) | |||
%% -------------------------------------------------------------------- | |||
terminate(Reason, State) -> | |||
?TRACE(" ----------terminate-----------~s_quit_4: /~p/~p/~p/,~n", [misc:time_format(now()), State#robot.acid, State#robot.id, Reason]), | |||
if is_port(State#robot.socket) -> | |||
gen_tcp:close(State#robot.socket); | |||
true -> no_socket | |||
end, | |||
ok. | |||
%% -------------------------------------------------------------------- | |||
%% Func: code_change/3 | |||
%% Purpose: Convert process state when code is changed | |||
%% Returns: {ok, NewState} | |||
%% -------------------------------------------------------------------- | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. | |||
%%========================================================================= | |||
%% 业务处理函数 | |||
%%========================================================================= | |||
%%登录游戏服务器 | |||
login(N, Pid) -> | |||
case get_game_server() of | |||
{Ip, Port} -> | |||
case connect_server(Ip, Port) of | |||
{ok, Socket} -> | |||
?TRACE("~s ---connect to IP:~p Port: ~p ok...~n", [misc:time_format(now()), Ip, Port]), | |||
Accid = N, | |||
AccName = "Guest" ++ integer_to_list(Accid), | |||
handle(login, {Accid, AccName}, Socket), | |||
spawn_link(fun() -> do_parse_packet(Socket, Pid) end), | |||
{ok, Socket}; | |||
_Reason2 -> | |||
?TRACE("Connect to server failed: ~p~n", [_Reason2]), | |||
error | |||
end; | |||
_Reason1 -> | |||
?TRACE("get server failed: ~p~n", [_Reason1]), | |||
error_110 | |||
end. | |||
%% 获取网关服务器参数 | |||
get_gateway_config(Config_file) -> | |||
try | |||
{ok, [L]} = file:consult(Config_file), | |||
{_, C} = lists:keyfind(gateway, 1, L), | |||
{_, Mysql_config} = lists:keyfind(tcp_listener, 1, C), | |||
{_, Ip} = lists:keyfind(ip, 1, Mysql_config), | |||
{_, Port} = lists:keyfind(port, 1, Mysql_config), | |||
[Ip, Port] | |||
catch | |||
_:_ -> [?GATEWAY_ADD, ?GATEWAY_PORT] | |||
end. | |||
%%连接网关服务器 | |||
get_game_server() -> | |||
[Gateway_Ip, Gateway_Port] = [?GATEWAY_ADD, ?GATEWAY_PORT], | |||
case gen_tcp:connect(Gateway_Ip, Gateway_Port, ?TCP_OPTS, 10000) of | |||
{ok, Socket} -> | |||
?TRACE("get_game_server connected to gateway: Ip:~p, Port: ~p ~n", [Gateway_Ip, Gateway_Port]), | |||
Data = pack(60000, <<>>), | |||
gen_tcp:send(Socket, Data), | |||
try | |||
case gen_tcp:recv(Socket, ?HEADER_LENGTH) of | |||
{ok, <<Len:16, 60000:16>>} -> | |||
BodyLen = Len - ?HEADER_LENGTH, | |||
case gen_tcp:recv(Socket, BodyLen, 3000) of | |||
{ok, <<Bin/binary>>} -> | |||
<<Rlen:16, RB/binary>> = Bin, | |||
case Rlen of | |||
1 -> | |||
<<Bin1/binary>> = RB, | |||
{IP, Bin2} = pt:read_string(Bin1), | |||
<<Port:16, _Num:8>> = Bin2, | |||
?TRACE("get_game_server IP, Port: /~p/~p/~n", [IP, Port]), | |||
{IP, Port}; | |||
_Len -> | |||
?TRACE("recv 60000: Unknown Len: ~p~n", [_Len]), | |||
no_gameserver | |||
end; | |||
_Reason1 -> | |||
gen_tcp:close(Socket), | |||
?TRACE("error when recv 60000: Reason:~p ~n", [_Reason1]), | |||
error_10 | |||
end; | |||
{error, _Reason2} -> | |||
?TRACE("get_game_server error:~p/~n", [_Reason2]), | |||
gen_tcp:close(Socket), | |||
error_20 | |||
end | |||
catch | |||
_:_ -> gen_tcp:close(Socket), | |||
error_30 | |||
end; | |||
{error, _Reason3} -> | |||
?TRACE("get_game_server--------------error:~p/~n", [_Reason3]), | |||
error_40 | |||
end. | |||
%%连接服务端 | |||
connect_server(Ip, Port) -> | |||
gen_tcp:connect(Ip, Port, ?TCP_OPTS, 10000). | |||
%% 接受信息 | |||
async_recv(Sock, Length, Timeout) when is_port(Sock) -> | |||
case prim_inet:async_recv(Sock, Length, Timeout) of | |||
{error, Reason} -> throw({Reason}); | |||
{ok, Res} -> Res; | |||
Res -> Res | |||
end. | |||
%%接收来自服务器的数据 - 登陆后进入游戏逻辑 | |||
%%Socket:socket id | |||
%%Client: client记录 | |||
do_parse_packet(Socket, Pid) -> | |||
Ref = async_recv(Socket, ?HEADER_LENGTH, ?HEART_TIMEOUT), | |||
receive | |||
{inet_async, Socket, Ref, {ok, <<Len:16, Cmd:16>>}} -> | |||
?TRACE("receive command: ~p, length: ~p", [Cmd, Len]), | |||
BodyLen = Len - ?HEADER_LENGTH, | |||
RecvData = | |||
case BodyLen > 0 of | |||
true -> | |||
Ref1 = async_recv(Socket, BodyLen, ?TCP_TIMEOUT), | |||
receive | |||
{inet_async, Socket, Ref1, {ok, Binary}} -> | |||
?TRACE("Data: ~p~n", [Binary]), | |||
{ok, Binary}; | |||
Other -> | |||
?TRACE("Data recv Error: ~p~n", [Other]), | |||
{fail, Other} | |||
end; | |||
false -> | |||
{ok, <<>>} | |||
end, | |||
case RecvData of | |||
{ok, BinData} -> | |||
case Cmd of | |||
10000 -> | |||
<<Code:8, _Bin1/binary>> = BinData, | |||
case Code of | |||
0 -> | |||
gen_server:cast(Pid, {login_ok, 0}), | |||
<<_:32, PlayerId:64, _Bin2/binary>> = _Bin1, | |||
handle(enter_player, {PlayerId}, Socket), | |||
ok; | |||
1 -> | |||
<<Accid:32, _Bin2/binary>> = _Bin1, | |||
gen_server:cast(Pid, {login_ok, 1}), | |||
handle(select_role, Accid, Socket), | |||
ok; | |||
_ -> | |||
gen_server:cast(Pid, login_failed), | |||
?TRACE("login failed: Code: ~p~n", [Code]), | |||
failed | |||
end; | |||
10003 -> | |||
<<Code:8, PlayerId:64, _Bin/binary>> = BinData, | |||
?TRACE("10003: Code: ~p PlayerId~p~n", [Code, PlayerId]), | |||
if Code =:= 1 -> | |||
handle(enter_player, {PlayerId}, Socket), | |||
gen_server:cast(Pid, {playerid, PlayerId}); | |||
true -> | |||
gen_server:cast(Pid, {stop}) | |||
end; | |||
10004 -> | |||
<<Code:8, _Bin/binary>> = BinData, | |||
?TRACE("10004: Code: ~p ~n", [Code]), | |||
if Code =/= 0 -> | |||
gen_server:cast(Pid, enter_ok); | |||
true -> | |||
gen_server:cast(Pid, {stop}) | |||
end; | |||
13001 -> | |||
<<Uid:64, Gender:8, Level:8, Speed:8, Scene:16, X:8, Y:8, Hp:32, _Other/binary>> = BinData, | |||
?TRACE("13001: Uid:~p, Gender ~p, Level ~p, Speed ~p, Scene ~p, X ~p, Y ~p, Hp ~p~n", [Uid, Gender, Level, Speed, Scene, X, Y, Hp]), | |||
%%更新信息 | |||
gen_server:cast(Pid, {upgrade_state_13001, [Scene, X, Y, Hp]}), | |||
{ok, Data} = ptr_30:write(30003, [4]), | |||
gen_tcp:send(Socket, Data), | |||
ok; | |||
10007 -> | |||
<<_Code:16>> = BinData, | |||
ok; | |||
30501 -> | |||
Result = ptr_30:read(30501, BinData), | |||
?TRACE("~p : ~p", [30501, Result]), | |||
{ok, List} = Result, | |||
lists:foreach(fun(Tid) -> | |||
{ok, Data} = ptr_30:write(30004, [Tid]), | |||
gen_tcp:send(Socket, Data) | |||
end, | |||
List); | |||
O -> | |||
Result = ptr_30:read(O, BinData), | |||
?TRACE("~p : ~p", [O, Result]) | |||
end, | |||
do_parse_packet(Socket, Pid); | |||
{fail, _} -> | |||
?TRACE("do_parse_packet recv data failed:/~p/~p/~n~p~n", [Socket, Pid, RecvData]), | |||
gen_tcp:close(Socket), | |||
gen_server:cast(Pid, {stop, socket_error_1}) | |||
end; | |||
%%超时处理 | |||
{inet_async, Socket, Ref, {error, timeout}} -> | |||
?TRACE("do_parse_packet timeout:/~p/~p/~n", [Socket, Pid]), | |||
do_parse_packet(Socket, Pid); | |||
%%用户断开连接或出错 | |||
Reason -> | |||
?TRACE("do_parse_packet: Error Reason:/~p/~p/~n", [Socket, Reason]), | |||
gen_tcp:close(Socket), | |||
gen_server:cast(Pid, {stop, socket_error_3}) | |||
end. | |||
%% 随机事件处理 | |||
handle_action_random(PlayerId, Socket) -> | |||
Actions = [chat], | |||
Action = lists:nth(random:uniform(length(Actions)), Actions), | |||
Module = list_to_atom(lists:concat(["robot_", Action])), | |||
catch Module:handle(PlayerId, Socket), | |||
ok. | |||
handle_action_friend(State) -> | |||
Socket = State#robot.socket, | |||
Friend = State#robot.frda, | |||
case Friend of | |||
[] -> | |||
gen_tcp:send(Socket, pack(13099, <<40:8, 200:8>>)), | |||
State; | |||
_ -> | |||
Index = random:uniform(length(Friend)), | |||
PlayerId = lists:nth(Index, Friend), | |||
Fri = lists:delete(PlayerId, Friend), | |||
Actions = [friend], | |||
Action = lists:nth(random:uniform(length(Actions)), Actions), | |||
Module = list_to_atom(lists:concat(["robot_", Action])), | |||
catch Module:handle(PlayerId, Socket), | |||
State#robot{frda = Fri} | |||
end. | |||
%%游戏相关操作%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%%心跳包 | |||
handle(heart, _, Socket) -> | |||
case gen_tcp:send(Socket, pack(10006, <<>>)) of | |||
ok -> | |||
sleep(24 * 1000), | |||
handle(heart, a, Socket); | |||
_ -> | |||
error | |||
end; | |||
%%子socket链接 | |||
handle(start_child_socket, {State, N}, _) -> | |||
sleep(5000), | |||
case get_game_server() of | |||
{Ip, Port} -> | |||
case connect_server(Ip, Port - N * 100) of | |||
{ok, Socket} -> | |||
Accid = State#robot.acid, | |||
Pid = State#robot.pid, | |||
Data = pack(10008, <<9999:16, Accid:32, N:8>>), | |||
gen_tcp:send(Socket, Data), | |||
try | |||
Ref = async_recv(Socket, ?HEADER_LENGTH, ?TCP_TIMEOUT), | |||
receive | |||
{inet_async, Socket, Ref, {ok, <<Len:16, Cmd:16>>}} -> | |||
%%?TRACE("--------------------------cmd:~p~n",[Cmd]), | |||
BodyLen = Len - ?HEADER_LENGTH, | |||
case BodyLen > 0 of | |||
true -> | |||
Ref1 = async_recv(Socket, BodyLen, ?TCP_TIMEOUT), | |||
receive | |||
{inet_async, Socket, Ref1, {ok, Binary}} when Cmd =:= 10008 -> | |||
%%?TRACE("----------------------rev--10008~n",[]), | |||
<<Code:16, N:8>> = Binary, | |||
%%?TRACE("----------------------rev--10008:~p~n",[Code]), | |||
if | |||
Code == 1 -> | |||
%%spawn_link(fun()->do_parse_packet(Socket, Pid) end), | |||
gen_server:cast(Pid, {add_child_socket, N, Socket}), | |||
{ok, N}; | |||
true -> | |||
error_50 | |||
end; | |||
Other -> | |||
?TRACE("---------------child-----------cmd--other:~p~n", [Other]), | |||
gen_tcp:close(Socket), | |||
error_60 | |||
end; | |||
false -> | |||
error_70 | |||
end; | |||
%%用户断开连接或出错 | |||
Other -> | |||
?TRACE("---------------------child------------other-----err---------~p~n", [Other]), | |||
gen_tcp:close(Socket), | |||
error_80 | |||
end | |||
catch | |||
_:_ -> gen_tcp:close(Socket), | |||
error_90 | |||
end; | |||
_ -> | |||
error_100 | |||
end; | |||
_ -> error_110 | |||
end; | |||
%%登陆 | |||
handle(login, {Accid, AccName}, Socket) -> | |||
?TRACE("sending login request entry socket: ~p ~p ~p~n", [Accid, AccName, Socket]), | |||
AccStamp = 1273027133, | |||
Tick = integer_to_list(Accid) ++ AccName ++ integer_to_list(AccStamp) ++ ?TICKET, | |||
TickMd5 = util:md5(Tick), | |||
TickMd5Bin = list_to_binary(TickMd5), | |||
TLen = byte_size(TickMd5Bin), | |||
AccNameLen = byte_size(list_to_binary(AccName)), | |||
AccNameBin = list_to_binary(AccName), | |||
Data = <<9999:16, Accid:32, AccStamp:32, AccNameLen:16, AccNameBin/binary, TLen:16, TickMd5Bin/binary>>, | |||
?TRACE("sending login request: ~p ~p~n", [Accid, AccName]), | |||
gen_tcp:send(Socket, pack(10000, Data)), | |||
ok; | |||
%%玩家列表 | |||
handle(list_player, _, Socket) -> | |||
gen_tcp:send(Socket, pack(10002, <<1:16>>)), | |||
ok; | |||
%%选择角色进入 | |||
handle(select_role, Accid, Socket) -> | |||
NickName = "GUEST-" ++ integer_to_list(Accid), | |||
NameBin = list_to_binary(NickName), | |||
TLen = byte_size(NameBin), | |||
gen_tcp:send(Socket, pack(10003, <<9999:16, 1:8, 1:8, TLen:16, NameBin/binary>>)), | |||
ok; | |||
%%选择角色进入 | |||
handle(enter_player, {PlayerId}, Socket) -> | |||
gen_tcp:send(Socket, pack(10004, <<9999:16, PlayerId:64, 30:8, 20:8>>)), | |||
ok; | |||
%%跑步 | |||
handle(run, a, Socket) -> | |||
X = util:rand(15, 45), | |||
Y = util:rand(15, 45), | |||
gen_tcp:send(Socket, pack(12001, <<X:16, Y:16>>)); | |||
%%ai模式跑步 | |||
handle(run, {X, Y, SX, SY}, Socket) -> | |||
?TRACE("----running:[~p][~p]~n", [X, Y]), | |||
gen_tcp:send(Socket, pack(12001, <<X:8, Y:8, SX:8, SY:8>>)); | |||
%%进入场景 | |||
handle(enter_scene, Sid, Socket) -> | |||
gen_tcp:send(Socket, pack(12005, <<Sid:32>>)), | |||
gen_tcp:send(Socket, pack(12002, <<>>)); %%换场景还要发送12002加载场景, 不然看不到角色的。 | |||
%% 聊天模块 | |||
handle(chat1, PlayerId, Socket) -> | |||
Actions = [chat], | |||
Action = lists:nth(random:uniform(length(Actions)), Actions), | |||
Module = list_to_atom(lists:concat(["robot_", Action])), | |||
catch Module:handle(PlayerId, Socket), | |||
ok; | |||
%%聊天 | |||
handle(chat, Data, Socket) -> | |||
Bin = list_to_binary(Data), | |||
L = byte_size(Bin), | |||
gen_tcp:send(Socket, pack(11010, <<L:16, Bin/binary>>)); | |||
%%静止 | |||
handle(undefined, a, _Socket) -> | |||
ok; | |||
%%获取其他玩家信息 | |||
handle(get_player_info, Id, Socket) -> | |||
gen_tcp:send(Socket, pack(13004, <<Id:16>>)); | |||
%%获取自己信息 | |||
handle(get_self_info, _, Socket) -> | |||
?TRACE("get_self_info: sending 13001~n"), | |||
gen_tcp:send(Socket, pack(13001, <<>>)); | |||
%%复活 | |||
handle(revive, _, Socket) -> | |||
gen_tcp:send(Socket, pack(20004, <<3:8>>)), | |||
Action = tool:to_binary("-加血 100000"), | |||
ActionLen = byte_size(Action), | |||
Data = <<ActionLen:16, Action/binary>>, | |||
Packet = pack(11020, Data), | |||
gen_tcp:send(Socket, Packet); | |||
handle(Handle, Data, Socket) -> | |||
?TRACE("handle error: /~p/~p/~n", [Handle, Data]), | |||
{reply, handle_no_match, Socket}. | |||
%%玩家列表 | |||
read(<<L:32, 10002:16, Num:16, Bin/binary>>) -> | |||
?TRACE("client read: ~p ~p ~p~n", [L, 10002, Num]), | |||
F = fun(Bin1) -> | |||
<<Id:32, S:16, C:16, Sex:16, Lv:16, Bin2/binary>> = Bin1, | |||
{Name, Rest} = read_string(Bin2), | |||
?TRACE("player list: Id=~p Status=~p Pro=~p Sex=~p Lv=~p Name=~p~n", [Id, S, C, Sex, Lv, Name]), | |||
Rest | |||
end, | |||
for(0, Num, F, Bin), | |||
?TRACE("player list end.~n"); | |||
read(<<L:32, Cmd:16>>) -> | |||
?TRACE("client read: ~p ~p~n", [L, Cmd]); | |||
read(<<L:32, Cmd:16, Status:16>>) -> | |||
?TRACE("client read: ~p ~p ~p~n", [L, Cmd, Status]); | |||
read(<<L:32, Cmd:16, Bin/binary>>) -> | |||
?TRACE("client read: ~p ~p ~p~n", [L, Cmd, Bin]); | |||
read(Bin) -> | |||
?TRACE("client rec: ~p~n", [Bin]). | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%%辅助函数 | |||
%%读取字符串 | |||
read_string(Bin) -> | |||
case Bin of | |||
<<Len:16, Bin1/binary>> -> | |||
case Bin1 of | |||
<<Str:Len/binary-unit:8, Rest/binary>> -> | |||
{binary_to_list(Str), Rest}; | |||
_R1 -> | |||
{[], <<>>} | |||
end; | |||
_R1 -> | |||
{[], <<>>} | |||
end. | |||
random_sleep(T) -> | |||
N = random:uniform(T), | |||
timer:sleep(N * 100). | |||
sleep(T) -> | |||
receive | |||
after T -> ok | |||
end. | |||
for(Max, Max, _F) -> | |||
[]; | |||
for(Min, Max, F) -> | |||
[F(Min) | for(Min + 1, Max, F)]. | |||
for(Max, Max, _F, X) -> | |||
X; | |||
for(Min, Max, F, X) -> | |||
F(X), | |||
for(Min + 1, Max, F, X). | |||
sleep_send({T, S}) -> | |||
receive | |||
after T -> handle(run, a, S) | |||
end. | |||
get_pid(Name) -> | |||
case whereis(Name) of | |||
undefined -> | |||
err; | |||
Pid -> Pid | |||
end. | |||
ping(Node) -> | |||
case net_adm:ping(Node) of | |||
pang -> | |||
?TRACE("ping ~p error.~n", [Node]); | |||
pong -> | |||
?TRACE("ping ~p success.~n", [Node]); | |||
Error -> | |||
?TRACE("error: ~p ~n", [Error]) | |||
end. | |||
do_act(Pid) -> | |||
State = gen_server:call(Pid, {get_state}), | |||
handle(State#robot.act, a, State#robot.socket), | |||
sleep(2000), | |||
do_act(Pid). | |||
%%根据机器人状态进行动作 | |||
%%根据机器人状态进行动作 | |||
ai(Pid) -> | |||
%%?TRACE("start ai ~p.~n",[Pid]), | |||
%%更新信息 | |||
gen_server:cast(Pid, {get_state_13001}), | |||
Random_interval = random:uniform(6000) + 3000, | |||
sleep(Random_interval), | |||
State = gen_server:call(Pid, {get_state}), | |||
case State#robot.act of | |||
run -> | |||
case State#robot.hp > 0 of | |||
true -> | |||
case State#robot.status of | |||
standing -> | |||
if State#robot.step == 0 -> | |||
Tox = rand(5, 27), | |||
Toy = rand(30, 45), | |||
New_step = 1; | |||
true -> | |||
Tox = rand(5, 27),%%State#robot.tox, | |||
Toy = rand(30, 45),%%State#robot.toy, | |||
New_step = 0 | |||
end, | |||
State2 = State#robot{tox = Tox, toy = Toy, step = New_step, status = running}, | |||
gen_server:cast(State#robot.pid, {upgrade_state, State2}); | |||
running -> | |||
if State#robot.x =/= State#robot.tox orelse State#robot.y =/= State#robot.toy -> %%当前坐标不等于目的坐标 | |||
handle(run, {State#robot.x, State#robot.y, State#robot.tox, State#robot.toy}, State#robot.socket), | |||
Random_interval2 = round(abs(State#robot.tox - State#robot.x) / 4) * 1000, | |||
sleep(Random_interval2), | |||
handle(run, {State#robot.tox, State#robot.toy, State#robot.tox, State#robot.toy}, State#robot.socket); | |||
true -> | |||
State2 = State#robot{status = standing}, %%到达目的地, 换个状态为站 | |||
gen_server:cast(State#robot.pid, {upgrade_state, State2}) %%更新机器人状态 | |||
end; | |||
_ -> | |||
?TRACE("robot status error!~n") | |||
end; | |||
false -> | |||
ok%handle(revive,a,State#robot.socket) | |||
end; | |||
undefined -> | |||
ok | |||
end, | |||
ai(Pid). | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. | |||
rand(Same, Same) -> Same; | |||
rand(Min, Max) -> | |||
M = Min - 1, | |||
if | |||
Max - M =< 0 -> | |||
0; | |||
true -> | |||
random:uniform(Max - M) + M | |||
end. |
@ -1,218 +0,0 @@ | |||
%%-------------------------------------- | |||
%% @Module: ptr_30 | |||
%% Author: Auto Generated | |||
%% Created: Thu Feb 28 15:17:18 2013 | |||
%% Description: | |||
%%-------------------------------------- | |||
-module(ptr_30). | |||
%%-------------------------------------- | |||
%% Include files | |||
%%-------------------------------------- | |||
-include("common111.hrl"). | |||
%%-------------------------------------- | |||
%% Exported Functions | |||
%%-------------------------------------- | |||
-compile(export_all). | |||
%%-------------------------------------- | |||
%%Protocol:30003 接受任务 | |||
%%-------------------------------------- | |||
read(30003, <<Result:16>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol 30004 完成任务并挑选奖励 | |||
%%-------------------------------------- | |||
read(30004, <<Result:16>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol:30005 查询任务npc状态 | |||
%%-------------------------------------- | |||
read(30005, <<BinData/binary>>) -> | |||
<<NpcListLen:16, NpcListBin/binary>> = BinData, | |||
Fun_NpcList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<NpcId:16, NpcState:8, _NpcList_RestBin/binary>> = RestBin, | |||
{_NpcList_RestBin, [[NpcId, NpcState] | ResultList]} | |||
end, | |||
{_NpcList_DoneBin, NpcList} = lists:foldl(Fun_NpcList, {NpcListBin, []}, lists:seq(1, NpcListLen)), | |||
{ok, [lists:reverse(NpcList)]}; | |||
%%-------------------------------------- | |||
%%Protocol:30006 获取指定长度任务列表 | |||
%%-------------------------------------- | |||
read(30006, <<BinData/binary>>) -> | |||
<<TaskListLen:16, TaskListBin/binary>> = BinData, | |||
Fun_TaskList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<TaskId:16, TaskState:8, TaskProcess:32, _TaskList_RestBin/binary>> = RestBin, | |||
{_TaskList_RestBin, [[TaskId, TaskState, TaskProcess] | ResultList]} | |||
end, | |||
{_TaskList_DoneBin, TaskList} = lists:foldl(Fun_TaskList, {TaskListBin, []}, lists:seq(1, TaskListLen)), | |||
{ok, lists:reverse(TaskList)}; | |||
%%-------------------------------------- | |||
%%Protocol:30007 消耗元宝自动完成任务 | |||
%%-------------------------------------- | |||
read(30007, <<Result:16>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 30501 服务端通知客户端任务的完成条件已满足 | |||
%%-------------------------------------- | |||
read(30501, <<BinData/binary>>) -> | |||
<<TaskListLen:16, TaskListBin/binary>> = BinData, | |||
Fun_TaskList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<TaskId:16, _TaskList_RestBin/binary>> = RestBin, | |||
{_TaskList_RestBin, [TaskId | ResultList]} | |||
end, | |||
{_TaskList_DoneBin, TaskList} = lists:foldl(Fun_TaskList, {TaskListBin, []}, lists:seq(1, TaskListLen)), | |||
{ok, lists:reverse(TaskList)}; | |||
%%-------------------------------------- | |||
%%Protocol: 30502 服务器向客户端发送新的任务进度 | |||
%%-------------------------------------- | |||
read(30502, <<BinData/binary>>) -> | |||
<<TaskListLen:16, TaskListBin/binary>> = BinData, | |||
Fun_TaskList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<TaskId:16, FinNum:8, NowNum:8, _TaskList_RestBin/binary>> = RestBin, | |||
{_TaskList_RestBin, [[TaskId, FinNum, NowNum] | ResultList]} | |||
end, | |||
{_TaskList_DoneBin, TaskList} = lists:foldl(Fun_TaskList, {TaskListBin, []}, lists:seq(1, TaskListLen)), | |||
{ok, lists:reverse(TaskList)}; | |||
%%-------------------------------------- | |||
%%Protocol:30503 通知客户端服务器为玩家触发了自动触发任务(列表) | |||
%%-------------------------------------- | |||
read(30503, <<BinData/binary>>) -> | |||
<<TaskListLen:16, TaskListBin/binary>> = BinData, | |||
Fun_TaskList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<TaskId:16, _TaskList_RestBin/binary>> = RestBin, | |||
{_TaskList_RestBin, [TaskId | ResultList]} | |||
end, | |||
{_TaskList_DoneBin, TaskList} = lists:foldl(Fun_TaskList, {TaskListBin, []}, lists:seq(1, TaskListLen)), | |||
{ok, [lists:reverse(TaskList)]}; | |||
%%-------------------------------------- | |||
%%Protocol:30505 通知客户端服务器为玩家自动触发了某个任务 | |||
%%-------------------------------------- | |||
read(30505, <<TaskId:16>>) -> | |||
{ok, [TaskId]}; | |||
%%-------------------------------------- | |||
%%Protocol:30506 通知客户端服务器为玩家自动完成了某个任务 | |||
%%-------------------------------------- | |||
read(30506, <<TaskId:16>>) -> | |||
{ok, [TaskId]}; | |||
%%-------------------------------------- | |||
%%Protocol:30507 通知客户端日常任务重置 | |||
%%-------------------------------------- | |||
read(30507, <<BinData/binary>>) -> | |||
<<TaskListLen:16, TaskListBin/binary>> = BinData, | |||
Fun_TaskList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<Type:8, _TaskList_RestBin/binary>> = RestBin, | |||
{_TaskList_RestBin, [Type | ResultList]} | |||
end, | |||
{_TaskList_DoneBin, TaskList} = lists:foldl(Fun_TaskList, {TaskListBin, []}, lists:seq(1, TaskListLen)), | |||
{ok, [lists:reverse(TaskList)]}; | |||
%%-------------------------------------- | |||
%% undefined command | |||
%%-------------------------------------- | |||
read(_Cmd, _R) -> | |||
{error, no_match}. | |||
%%-------------------------------------- | |||
%%Protocol:30003 接受任务 | |||
%%-------------------------------------- | |||
write(30003, [TaskId]) -> | |||
{ok, pt:pack(30003, <<TaskId:16>>)}; | |||
%%-------------------------------------- | |||
%%Protocol:协议号:30004 完成任务并挑选奖励 | |||
%%-------------------------------------- | |||
write(30004, [TaskId]) -> | |||
{ok, pt:pack(30004, <<TaskId:16>>)}; | |||
%%-------------------------------------- | |||
%%Protocol:30005 查询任务npc状态 | |||
%%-------------------------------------- | |||
write(30005, [NpcList]) -> | |||
Fun_NpcList = fun([NpcId]) -> | |||
<<NpcId:16>> | |||
end, | |||
NpcList_Len = length(NpcList), | |||
NpcList_ABin = any_to_binary(lists:map(Fun_NpcList, NpcList)), | |||
NpcList_ABinData = <<NpcList_Len:16, NpcList_ABin/binary>>, | |||
{ok, pt:pack(30005, <<NpcList_ABinData/binary>>)}; | |||
%%-------------------------------------- | |||
%%Protocol:30006 获取指定长度任务列表 | |||
%%-------------------------------------- | |||
write(30006, [Len]) -> | |||
{ok, pt:pack(30006, <<Len:8>>)}; | |||
%%-------------------------------------- | |||
%%Protocol:30007 消耗元宝自动完成任务 | |||
%%-------------------------------------- | |||
write(30007, [TaskId]) -> | |||
{ok, pt:pack(30007, <<TaskId:16>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 30501 服务端通知客户端任务的完成条件已满足 | |||
%%-------------------------------------- | |||
write(30501, _) -> | |||
{ok, pt:pack(30501, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 30502 服务器向客户端发送新的任务进度 | |||
%%-------------------------------------- | |||
write(30502, _) -> | |||
{ok, pt:pack(30502, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol:30503 通知客户端服务器为玩家触发了自动触发任务(列表) | |||
%%-------------------------------------- | |||
write(30503, _) -> | |||
{ok, pt:pack(30503, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol:30505 通知客户端服务器为玩家自动触发了某个任务 | |||
%%-------------------------------------- | |||
write(30505, _) -> | |||
{ok, pt:pack(30505, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol:30506 通知客户端服务器为玩家自动完成了某个任务 | |||
%%-------------------------------------- | |||
write(30506, _) -> | |||
{ok, pt:pack(30506, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol:30507 通知客户端日常任务重置 | |||
%%-------------------------------------- | |||
write(30507, _) -> | |||
{ok, pt:pack(30507, <<>>)}; | |||
%%-------------------------------------- | |||
%% undefined command | |||
%%-------------------------------------- | |||
write(Cmd, _R) -> | |||
?ERROR_MSG("~s_errorcmd_[~p] ", [misc:time_format(game_timer:now()), Cmd]), | |||
{ok, pt:pack(0, <<>>)}. | |||
%%------------------------------------ | |||
%% internal function | |||
%%------------------------------------ | |||
pack_string(Str) -> | |||
BinData = tool:to_binary(Str), | |||
Len = byte_size(BinData), | |||
<<Len:16, BinData/binary>>. | |||
any_to_binary(Any) -> | |||
tool:to_binary(Any). | |||
@ -1,30 +0,0 @@ | |||
%% @author Administrator | |||
%% @doc @todo Add description to task_script. | |||
-module(task_script). | |||
-define(HEADER_LENGTH, 4). % | |||
%% ==================================================================== | |||
%% API functions | |||
%% ==================================================================== | |||
-export([call/3]). | |||
%% ==================================================================== | |||
%% Internal functions | |||
%% ==================================================================== | |||
call(30003, TaskId, Socket) -> | |||
gen_tcp:send(Socket, pack(30003, <<TaskId:16>>)); | |||
call(30004, TaskId, Socket) -> | |||
gen_tcp:send(Socket, pack(30004, <<TaskId:16>>)); | |||
call(30007, TaskId, Socket) -> | |||
gen_tcp:send(Socket, pack(30007, <<TaskId:16>>)); | |||
call(30005, NpcList, Socket) -> | |||
gen_tcp:send(Socket, pack(30005, NpcList)); | |||
call(30006, Size, Socket) -> | |||
gen_tcp:send(Socket, pack(30006, <<Size:8>>)). | |||
%%打包数据 | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. |
@ -1,144 +0,0 @@ | |||
%%-------------------------------------- | |||
%% @Module: ptr_11 | |||
%% Author: Auto Generated | |||
%% Created: Fri Mar 01 19:14:40 2013 | |||
%% Description: | |||
%%-------------------------------------- | |||
-module(ptr_11). | |||
%%-------------------------------------- | |||
%% Include files | |||
%%-------------------------------------- | |||
-include("common111.hrl"). | |||
%%-------------------------------------- | |||
%% Exported Functions | |||
%%-------------------------------------- | |||
-compile(export_all). | |||
%%-------------------------------------- | |||
%%Protocol: 11000 聊天信息 | |||
%%-------------------------------------- | |||
read(11000, <<Uid:64, BinData/binary>>) -> | |||
{Name, _Name_DoneBin} = pt:read_string(BinData), | |||
<<Type:8, _Type_DoneBin/binary>> = _Name_DoneBin, | |||
{Content, _Content_DoneBin} = pt:read_string(_Type_DoneBin), | |||
{ok, [Uid, Name, Type, Content]}; | |||
%%-------------------------------------- | |||
%%Protocol: 11001 发送世界信息 | |||
%%-------------------------------------- | |||
read(11001, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 11002 发送场景信息 | |||
%%-------------------------------------- | |||
read(11002, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 11003 发送帮派信息 | |||
%%-------------------------------------- | |||
read(11003, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 11004 发送私聊信息 | |||
%%-------------------------------------- | |||
read(11004, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 11005 GM指令 | |||
%%-------------------------------------- | |||
read(11005, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 11010 系统信息/广播 | |||
%%-------------------------------------- | |||
read(11010, <<Type:8, BinData/binary>>) -> | |||
{Content, _Content_DoneBin} = pt:read_string(BinData), | |||
{ok, [Type, Content]}; | |||
%%-------------------------------------- | |||
%%Protocol: 11099 调试信息 | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%% undefined command | |||
%%-------------------------------------- | |||
read(_Cmd, _R) -> | |||
{error, no_match}. | |||
%%-------------------------------------- | |||
%%Protocol: 11000 聊天信息 | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%%Protocol: 11001 发送世界信息 | |||
%%-------------------------------------- | |||
write(11001, [ShowState, Content]) -> | |||
Content_StrBin = pack_string(Content), | |||
{ok, pt:pack(11001, <<ShowState:8, Content_StrBin/binary>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 11002 发送场景信息 | |||
%%-------------------------------------- | |||
write(11002, [Content]) -> | |||
Content_StrBin = pack_string(Content), | |||
{ok, pt:pack(11002, <<Content_StrBin/binary>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 11003 发送帮派信息 | |||
%%-------------------------------------- | |||
write(11003, [Content]) -> | |||
Content_StrBin = pack_string(Content), | |||
{ok, pt:pack(11003, <<Content_StrBin/binary>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 11004 发送私聊信息 | |||
%%-------------------------------------- | |||
write(11004, [PeerId, Content]) -> | |||
Content_StrBin = pack_string(Content), | |||
{ok, pt:pack(11004, <<PeerId:64, Content_StrBin/binary>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 11005 GM指令 | |||
%%-------------------------------------- | |||
write(11005, [Type, Content]) -> | |||
Content_StrBin = pack_string(Content), | |||
{ok, pt:pack(11005, <<Type:8, Content_StrBin/binary>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 11010 系统信息/广播 | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%%Protocol: 11099 调试信息 | |||
%%-------------------------------------- | |||
write(11099, [Content]) -> | |||
Content_StrBin = pack_string(Content), | |||
{ok, pt:pack(11099, <<Content_StrBin/binary>>)}; | |||
%%-------------------------------------- | |||
%% undefined command | |||
%%-------------------------------------- | |||
write(Cmd, _R) -> | |||
?ERROR_MSG("~s_errorcmd_[~p] ", [misc:time_format(game_timer:now()), Cmd]), | |||
{ok, pt:pack(0, <<>>)}. | |||
%%------------------------------------ | |||
%% internal function | |||
%%------------------------------------ | |||
pack_string(Str) -> | |||
BinData = tool:to_binary(Str), | |||
Len = byte_size(BinData), | |||
<<Len:16, BinData/binary>>. | |||
any_to_binary(Any) -> | |||
tool:to_binary(Any). | |||
@ -1,197 +0,0 @@ | |||
%%-------------------------------------- | |||
%% @Module: ptr_19 | |||
%% Author: Auto Generated | |||
%% Created: Tue Mar 05 09:35:33 2013 | |||
%% Description: | |||
%%-------------------------------------- | |||
-module(ptr_19). | |||
%%-------------------------------------- | |||
%% Include files | |||
%%-------------------------------------- | |||
-include("common111.hrl"). | |||
%%-------------------------------------- | |||
%% Exported Functions | |||
%%-------------------------------------- | |||
-compile(export_all). | |||
%%-------------------------------------- | |||
%%Protocol: 19001 玩家反馈到GM | |||
%%-------------------------------------- | |||
read(19001, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 19002 获取GM反馈 | |||
%%-------------------------------------- | |||
read(19002, <<BinData/binary>>) -> | |||
<<FbListLen:16, FbListBin/binary>> = BinData, | |||
Fun_FbList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<FbId:32, Type:8, State:8, _ContentList_RestBin/binary>> = RestBin, | |||
<<ContentListLen:16, ContentListBin/binary>> = _ContentList_RestBin, | |||
Fun_ContentList = fun(_Idx, {RestBin, ResultList}) -> | |||
{Name, _Name_DoneBin} = pt:read_string(RestBin), | |||
{Content, _Content_DoneBin} = pt:read_string(_Name_DoneBin), | |||
<<Date:32, _ContentList_RestBin/binary>> = _Content_DoneBin, | |||
{_ContentList_RestBin, [[Name, Content, Date] | ResultList]} | |||
end, | |||
{_ContentList_DoneBin, ContentList} = lists:foldl(Fun_ContentList, {ContentListBin, []}, lists:seq(1, ContentListLen)), | |||
{_ContentList_DoneBin, [[FbId, Type, State, lists:reverse(ContentList)] | ResultList]} | |||
end, | |||
{_FbList_DoneBin, FbList} = lists:foldl(Fun_FbList, {FbListBin, []}, lists:seq(1, FbListLen)), | |||
{ok, [lists:reverse(FbList)]}; | |||
%%-------------------------------------- | |||
%%Protocol: 19010 是否有未读邮件 | |||
%%-------------------------------------- | |||
read(19010, <<Num:8>>) -> | |||
{ok, [Num]}; | |||
%%-------------------------------------- | |||
%%Protocol: 19011 邮件列表 | |||
%%-------------------------------------- | |||
read(19011, <<BinData/binary>>) -> | |||
<<MailListLen:16, MailListBin/binary>> = BinData, | |||
Fun_MailList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<MailId:32, Type:8, State:8, Date:32, _SName_RestBin/binary>> = RestBin, | |||
{SName, _SName_DoneBin} = pt:read_string(_SName_RestBin), | |||
{Title, _Title_DoneBin} = pt:read_string(_SName_DoneBin), | |||
{_Title_DoneBin, [[MailId, Type, State, Date, SName, Title] | ResultList]} | |||
end, | |||
{_MailList_DoneBin, MailList} = lists:foldl(Fun_MailList, {MailListBin, []}, lists:seq(1, MailListLen)), | |||
{ok, [lists:reverse(MailList)]}; | |||
%%-------------------------------------- | |||
%%Protocol: 19012 邮件具体内容 | |||
%%-------------------------------------- | |||
read(19012, <<StCode:8, MailId:32, BinData/binary>>) -> | |||
{Content, _Content_DoneBin} = pt:read_string(BinData), | |||
<<GoodListLen:16, GoodListBin/binary>> = _Content_DoneBin, | |||
Fun_GoodList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<GoodTypeId:32, GoodsNum:8, Exist:8, _GoodList_RestBin/binary>> = RestBin, | |||
{_GoodList_RestBin, [[GoodTypeId, GoodsNum, Exist] | ResultList]} | |||
end, | |||
{_GoodList_DoneBin, GoodList} = lists:foldl(Fun_GoodList, {GoodListBin, []}, lists:seq(1, GoodListLen)), | |||
{ok, [StCode, MailId, Content, lists:reverse(GoodList)]}; | |||
%%-------------------------------------- | |||
%%Protocol: 19013 回复邮件 | |||
%%-------------------------------------- | |||
read(19013, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 19014 收取附件 | |||
%%-------------------------------------- | |||
read(19014, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 19015 删除邮件 | |||
%%-------------------------------------- | |||
read(19015, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 19016 发送邮件 | |||
%%-------------------------------------- | |||
read(19016, <<Result:8, BinData/binary>>) -> | |||
<<ErrRecvListLen:16, ErrRecvListBin/binary>> = BinData, | |||
Fun_ErrRecvList = fun(_Idx, {RestBin, ResultList}) -> | |||
{ErrName, _ErrName_DoneBin} = pt:read_string(RestBin), | |||
{_ErrName_DoneBin, [ErrName | ResultList]} | |||
end, | |||
{_ErrRecvList_DoneBin, ErrRecvList} = lists:foldl(Fun_ErrRecvList, {ErrRecvListBin, []}, lists:seq(1, ErrRecvListLen)), | |||
{ok, [Result, lists:reverse(ErrRecvList)]}; | |||
%%-------------------------------------- | |||
%% undefined command | |||
%%-------------------------------------- | |||
read(_Cmd, _R) -> | |||
{error, no_match}. | |||
%%-------------------------------------- | |||
%%Protocol: 19001 玩家反馈到GM | |||
%%-------------------------------------- | |||
write(19001, [Type, Content]) -> | |||
Content_StrBin = pack_string(Content), | |||
{ok, pt:pack(19001, <<Type:8, Content_StrBin/binary>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 19002 获取GM反馈 | |||
%%-------------------------------------- | |||
write(19002, _) -> | |||
{ok, pt:pack(19002, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 19010 是否有未读邮件 | |||
%%-------------------------------------- | |||
write(19010, _) -> | |||
{ok, pt:pack(19010, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 19011 邮件列表 | |||
%%-------------------------------------- | |||
write(19011, _) -> | |||
{ok, pt:pack(19011, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 19012 邮件具体内容 | |||
%%-------------------------------------- | |||
write(19012, [MailId]) -> | |||
{ok, pt:pack(19012, <<MailId:32>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 19013 回复邮件 | |||
%%-------------------------------------- | |||
write(19013, [MailId, Content]) -> | |||
Content_StrBin = pack_string(Content), | |||
{ok, pt:pack(19013, <<MailId:32, Content_StrBin/binary>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 19014 收取附件 | |||
%%-------------------------------------- | |||
write(19014, [MailId]) -> | |||
{ok, pt:pack(19014, <<MailId:32>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 19015 删除邮件 | |||
%%-------------------------------------- | |||
write(19015, [MailId]) -> | |||
{ok, pt:pack(19015, <<MailId:32>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 19016 发送邮件 | |||
%%-------------------------------------- | |||
write(19016, [Title, Content, RecvList]) -> | |||
Title_StrBin = pack_string(Title), | |||
Content_StrBin = pack_string(Content), | |||
Fun_RecvList = fun([Name]) -> | |||
Name_StrBin = pack_string(Name), | |||
<<Name_StrBin/binary>> | |||
end, | |||
RecvList_Len = length(RecvList), | |||
RecvList_ABin = any_to_binary(lists:map(Fun_RecvList, RecvList)), | |||
RecvList_ABinData = <<RecvList_Len:16, RecvList_ABin/binary>>, | |||
{ok, pt:pack(19016, <<Title_StrBin/binary, Content_StrBin/binary, RecvList_ABinData/binary>>)}; | |||
%%-------------------------------------- | |||
%% undefined command | |||
%%-------------------------------------- | |||
write(Cmd, _R) -> | |||
?ERROR_MSG("~s_errorcmd_[~p] ", [misc:time_format(game_timer:now()), Cmd]), | |||
{ok, pt:pack(0, <<>>)}. | |||
%%------------------------------------ | |||
%% internal function | |||
%%------------------------------------ | |||
pack_string(Str) -> | |||
BinData = tool:to_binary(Str), | |||
Len = byte_size(BinData), | |||
<<Len:16, BinData/binary>>. | |||
any_to_binary(Any) -> | |||
tool:to_binary(Any). | |||
@ -1,426 +0,0 @@ | |||
%%-------------------------------------- | |||
%% @Module: ptr_40 | |||
%% Author: Auto Generated | |||
%% Created: Wed Mar 06 20:35:00 2013 | |||
%% Description: | |||
%%-------------------------------------- | |||
-module(ptr_40). | |||
%%-------------------------------------- | |||
%% Include files | |||
%%-------------------------------------- | |||
-include("common111.hrl"). | |||
%%-------------------------------------- | |||
%% Exported Functions | |||
%%-------------------------------------- | |||
-compile(export_all). | |||
%%-------------------------------------- | |||
%%Protocol: 40001 查询帮派(分页待定) | |||
%%-------------------------------------- | |||
%read(40001,<<CurPageNo:8,TotalPage:8,BinData/binary>>) -> | |||
read(40001, Data) -> | |||
NewData = zlib:uncompress(Data), | |||
<<CurPageNo:8, TotalPage:8, BinData/binary>> = NewData, | |||
%?TRACE("read 40001 CurPageNo= ~p ,TotalPage=~p ~n", [CurPageNo,TotalPage]), | |||
<<GuildListLen:16, GuildListBin/binary>> = BinData, | |||
%?TRACE("read 40001 GuildListLen: ~p ~n", [GuildListLen]), | |||
Fun_GuildList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<GuildId:32, _GuildName_RestBin/binary>> = RestBin, | |||
%?TRACE("read 40001 GuildId: ~p ~n", [GuildId]), | |||
{GuildName, _GuildName_DoneBin} = pt:read_string(_GuildName_RestBin), | |||
<<CurNum:8, MaxNum:8, Level:8, Uid:64, _Name_RestBin/binary>> = _GuildName_DoneBin, | |||
{Name, _Name_DoneBin} = pt:read_string(_Name_RestBin), | |||
{Announce, _Announce_DoneBin} = pt:read_string(_Name_DoneBin), | |||
{_Announce_DoneBin, [[GuildId, GuildName, CurNum, MaxNum, Level, Uid, Name, Announce] | ResultList]} | |||
end, | |||
{_GuildList_DoneBin, GuildList} = lists:foldl(Fun_GuildList, {GuildListBin, []}, lists:seq(1, GuildListLen)), | |||
%?TRACE("read 40001 GuildList: ~p", [GuildList]), | |||
{ok, [CurPageNo, TotalPage, lists:reverse(GuildList)]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40002 创建帮派 | |||
%%-------------------------------------- | |||
read(40002, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40003 加入帮派 | |||
%%-------------------------------------- | |||
read(40003, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40004 退出所在帮派 | |||
%%-------------------------------------- | |||
read(40004, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40005 查询帮派成员 | |||
%%-------------------------------------- | |||
read(40005, <<StCode:8>>) -> | |||
{ok, [StCode]}; | |||
read(40005, <<StCode:8, BinData/binary>>) -> | |||
<<MemListLen:16, MemListBin/binary>> = BinData, | |||
Fun_MemList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<Uid:64, _Name_RestBin/binary>> = RestBin, | |||
{Name, _Name_DoneBin} = pt:read_string(_Name_RestBin), | |||
<<Level:8, Career:8, Gender:8, Position:8, Contrib:32, LastLoginTime:32, Online:8, _MemList_RestBin/binary>> = _Name_DoneBin, | |||
{_MemList_RestBin, [[Uid, Name, Level, Career, Gender, Position, Contrib, LastLoginTime, Online] | ResultList]} | |||
end, | |||
{_MemList_DoneBin, MemList} = lists:foldl(Fun_MemList, {MemListBin, []}, lists:seq(1, MemListLen)), | |||
{ok, [StCode, lists:reverse(MemList)]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40006 发起弹劾 | |||
%%-------------------------------------- | |||
read(40006, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40007 弹劾操作 | |||
%%-------------------------------------- | |||
read(40007, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40008 获取弹劾信息 | |||
%%-------------------------------------- | |||
read(40008, <<StCode:8>>) -> | |||
{ok, [StCode]}; | |||
read(40008, <<StCode:8, BinData/binary>>) -> | |||
<<RejectListLen:16, RejectListBin/binary>> = BinData, | |||
Fun_RejectList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<Uid:64, Pos:8, State:8, AgreeNum:8, DisagreeNum:8, RemainTime:32, _RejectList_RestBin/binary>> = RestBin, | |||
{_RejectList_RestBin, [[Uid, Pos, State, AgreeNum, DisagreeNum, RemainTime] | ResultList]} | |||
end, | |||
{_RejectList_DoneBin, RejectList} = lists:foldl(Fun_RejectList, {RejectListBin, []}, lists:seq(1, RejectListLen)), | |||
{ok, [StCode, lists:reverse(RejectList)]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40009 帮派日志 | |||
%%-------------------------------------- | |||
read(40009, <<BinData/binary>>) -> | |||
<<LogListLen:16, LogListBin/binary>> = BinData, | |||
Fun_LogList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<Uid:32, _Name_RestBin/binary>> = RestBin, | |||
{Name, _Name_DoneBin} = pt:read_string(_Name_RestBin), | |||
<<TimeStamp:32, _Content_RestBin/binary>> = _Name_DoneBin, | |||
{Content, _Content_DoneBin} = pt:read_string(_Content_RestBin), | |||
{_Content_DoneBin, [[Uid, Name, TimeStamp, Content] | ResultList]} | |||
end, | |||
{_LogList_DoneBin, LogList} = lists:foldl(Fun_LogList, {LogListBin, []}, lists:seq(1, LogListLen)), | |||
{ok, [lists:reverse(LogList)]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40030 邀请玩家加入帮派(帮主/副帮主/长老) | |||
%%-------------------------------------- | |||
read(40030, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40031 帮派申请列表(帮主/副帮主) | |||
%%-------------------------------------- | |||
read(40031, <<BinData/binary>>) -> | |||
<<ApplyListLen:16, ApplyListBin/binary>> = BinData, | |||
Fun_ApplyList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<Uid:64, _Name_RestBin/binary>> = RestBin, | |||
{Name, _Name_DoneBin} = pt:read_string(_Name_RestBin), | |||
<<Level:8, Career:8, Gender:8, Force:32, TimeStamp:32, _ApplyList_RestBin/binary>> = _Name_DoneBin, | |||
{_ApplyList_RestBin, [[Uid, Name, Level, Career, Gender, Force, TimeStamp] | ResultList]} | |||
end, | |||
{_ApplyList_DoneBin, ApplyList} = lists:foldl(Fun_ApplyList, {ApplyListBin, []}, lists:seq(1, ApplyListLen)), | |||
{ok, [lists:reverse(ApplyList)]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40032 通过或拒绝加入申请(帮主/副帮主) | |||
%%-------------------------------------- | |||
read(40032, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40033 提升职务(帮主) | |||
%%-------------------------------------- | |||
read(40033, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40034 解散帮派(帮主) | |||
%%-------------------------------------- | |||
read(40034, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40035 踢出成员(帮主/副帮主) | |||
%%-------------------------------------- | |||
read(40035, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40036 帮派升级(帮主/副帮主/长老) | |||
%%-------------------------------------- | |||
read(40036, <<Result:8, UplevelCd:32>>) -> | |||
{ok, [Result, UplevelCd]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40037 帮主让位 | |||
%%-------------------------------------- | |||
read(40037, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40039 帮派公告设置 | |||
%%-------------------------------------- | |||
read(40039, <<Result:8>>) -> | |||
{ok, [Result]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40070 帮派新增成员信息(广播) | |||
%%-------------------------------------- | |||
read(40070, <<Uid:64, BinData/binary>>) -> | |||
{Name, _Name_DoneBin} = pt:read_string(BinData), | |||
<<Level:8, _Level_DoneBin/binary>> = _Name_DoneBin, | |||
<<Career:8, _Career_DoneBin/binary>> = _Level_DoneBin, | |||
<<Gender:8, _Gender_DoneBin/binary>> = _Career_DoneBin, | |||
{ok, [Uid, Name, Level, Career, Gender]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40071 被踢通知(接收玩家) | |||
%%-------------------------------------- | |||
read(40071, <<GuildId:32, BinData/binary>>) -> | |||
{GuildName, _GuildName_DoneBin} = pt:read_string(BinData), | |||
{ok, [GuildId, GuildName]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40072 帮派邀请 | |||
%%-------------------------------------- | |||
read(40072, <<Uid:64, BinData/binary>>) -> | |||
{Name, _Name_DoneBin} = pt:read_string(BinData), | |||
<<GuildId:32, _GuildId_DoneBin/binary>> = _Name_DoneBin, | |||
<<MemNum:8, _MemNum_DoneBin/binary>> = _GuildId_DoneBin, | |||
<<Level:8, _Level_DoneBin/binary>> = _MemNum_DoneBin, | |||
{GuildName, _GuildName_DoneBin} = pt:read_string(_Level_DoneBin), | |||
<<Uid:64, _Uid_DoneBin/binary>> = _GuildName_DoneBin, | |||
{Name, _Name_DoneBin} = pt:read_string(_Uid_DoneBin), | |||
{ok, [Uid, Name, GuildId, MemNum, Level, GuildName, Uid, Name]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40073 职位变化通告(广播) | |||
%%-------------------------------------- | |||
read(40073, <<Uid:64, BinData/binary>>) -> | |||
{Name, _Name_DoneBin} = pt:read_string(BinData), | |||
<<OldPos:8, _OldPos_DoneBin/binary>> = _Name_DoneBin, | |||
<<NewPos:8, _NewPos_DoneBin/binary>> = _OldPos_DoneBin, | |||
{ok, [Uid, Name, OldPos, NewPos]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40074 帮主让位通知(广播) | |||
%%-------------------------------------- | |||
read(40074, <<OldUid:64, BinData/binary>>) -> | |||
{OldName, _OldName_DoneBin} = pt:read_string(BinData), | |||
<<NewUid:64, _NewUid_DoneBin/binary>> = _OldName_DoneBin, | |||
{NewName, _NewName_DoneBin} = pt:read_string(_NewUid_DoneBin), | |||
{ok, [OldUid, OldName, NewUid, NewName]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40075 帮派升级通知(广播) | |||
%%-------------------------------------- | |||
read(40075, <<OldLevel:8, NewLevel:8>>) -> | |||
{ok, [OldLevel, NewLevel]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40076 拒绝申请通知(仅玩家) | |||
%%-------------------------------------- | |||
read(40076, <<GuildId:32, BinData/binary>>) -> | |||
{GuildName, _GuildName_DoneBin} = pt:read_string(BinData), | |||
{ok, [GuildId, GuildName]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40077 新帮派公告 | |||
%%-------------------------------------- | |||
read(40077, <<BinData/binary>>) -> | |||
{Content, _Content_DoneBin} = pt:read_string(BinData), | |||
{ok, [Content]}; | |||
%%-------------------------------------- | |||
%%Protocol: 40078 申请加入批准通知(仅玩家) | |||
%%-------------------------------------- | |||
read(40078, <<GuildId:32, BinData/binary>>) -> | |||
{GuildName, _GuildName_DoneBin} = pt:read_string(BinData), | |||
{ok, [GuildId, GuildName]}; | |||
%%-------------------------------------- | |||
%% undefined command | |||
%%-------------------------------------- | |||
read(_Cmd, _R) -> | |||
{error, no_match}. | |||
%%-------------------------------------- | |||
%%Protocol: 40001 查询帮派(分页待定) | |||
%%-------------------------------------- | |||
write(40001, [PageNo, IsNotFull, IsSameGroup]) -> | |||
{ok, pt:pack(40001, <<PageNo:8, IsNotFull:8, IsSameGroup:8>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40002 创建帮派 | |||
%%-------------------------------------- | |||
write(40002, [Name, Announce]) -> | |||
Name_StrBin = pack_string(Name), | |||
Announce_StrBin = pack_string(Announce), | |||
{ok, pt:pack(40002, <<Name_StrBin/binary, Announce_StrBin/binary>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40003 加入帮派 | |||
%%-------------------------------------- | |||
write(40003, [GuildId]) -> | |||
{ok, pt:pack(40003, <<GuildId:32>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40004 退出所在帮派 | |||
%%-------------------------------------- | |||
write(40004, _) -> | |||
{ok, pt:pack(40004, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40005 查询帮派成员 | |||
%%<<GuildId:32,IsOnline:8>> | |||
%%-------------------------------------- | |||
write(40005, [GuildId, IsOnline]) -> | |||
{ok, pt:pack(40005, <<GuildId:32, IsOnline:8>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40006 发起弹劾 | |||
%%-------------------------------------- | |||
write(40006, _) -> | |||
{ok, pt:pack(40006, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40007 弹劾操作 | |||
%%-------------------------------------- | |||
write(40007, [Ops]) -> | |||
{ok, pt:pack(40007, <<Ops:8>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40008 获取弹劾信息 | |||
%%-------------------------------------- | |||
write(40008, _) -> | |||
{ok, pt:pack(40008, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40009 帮派日志 | |||
%%-------------------------------------- | |||
write(40009, _) -> | |||
{ok, pt:pack(40009, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40030 邀请玩家加入帮派(帮主/副帮主/长老) | |||
%%-------------------------------------- | |||
write(40030, [PlayerId]) -> | |||
{ok, pt:pack(40030, <<PlayerId:64>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40031 帮派申请列表(帮主/副帮主) | |||
%%-------------------------------------- | |||
write(40031, _) -> | |||
{ok, pt:pack(40031, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40032 通过或拒绝加入申请(帮主/副帮主) | |||
%%-------------------------------------- | |||
write(40032, [Uid, Ops]) -> | |||
{ok, pt:pack(40032, <<Uid:64, Ops:8>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40033 提升职务(帮主) | |||
%%-------------------------------------- | |||
write(40033, [Uid]) -> | |||
{ok, pt:pack(40033, <<Uid:64>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40034 解散帮派(帮主) | |||
%%-------------------------------------- | |||
write(40034, _) -> | |||
{ok, pt:pack(40034, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40035 踢出成员(帮主/副帮主) | |||
%%-------------------------------------- | |||
write(40035, [PlayerId]) -> | |||
{ok, pt:pack(40035, <<PlayerId:64>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40036 帮派升级(帮主/副帮主/长老) | |||
%%-------------------------------------- | |||
write(40036, _) -> | |||
{ok, pt:pack(40036, <<>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40037 帮主让位 | |||
%%-------------------------------------- | |||
write(40037, [Uid]) -> | |||
{ok, pt:pack(40037, <<Uid:64>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40039 帮派公告设置 | |||
%%-------------------------------------- | |||
write(40039, [Content]) -> | |||
Content_StrBin = pack_string(Content), | |||
{ok, pt:pack(40039, <<Content_StrBin/binary>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 40070 帮派新增成员信息(广播) | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%%Protocol: 40071 被踢通知(接收玩家) | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%%Protocol: 40072 帮派邀请 | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%%Protocol: 40073 职位变化通告(广播) | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%%Protocol: 40074 帮主让位通知(广播) | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%%Protocol: 40075 帮派升级通知(广播) | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%%Protocol: 40076 拒绝申请通知(仅玩家) | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%%Protocol: 40077 新帮派公告 | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%%Protocol: 40078 申请加入批准通知(仅玩家) | |||
%%-------------------------------------- | |||
%%-------------------------------------- | |||
%% undefined command | |||
%%-------------------------------------- | |||
write(Cmd, _R) -> | |||
?ERROR_MSG("~s_errorcmd_[~p] ", [misc:time_format(game_timer:now()), Cmd]), | |||
{ok, pt:pack(0, <<>>)}. | |||
%%------------------------------------ | |||
%% internal function | |||
%%------------------------------------ | |||
pack_string(Str) -> | |||
BinData = tool:to_binary(Str), | |||
Len = byte_size(BinData), | |||
<<Len:16, BinData/binary>>. | |||
any_to_binary(Any) -> | |||
tool:to_binary(Any). | |||
@ -1,54 +0,0 @@ | |||
%%-------------------------------------- | |||
%% @Module: ptr_11 | |||
%% Author: Auto Generated | |||
%% Created: Fri Mar 01 19:14:40 2013 | |||
%% Description: | |||
%%-------------------------------------- | |||
-module(ptr_44). | |||
%%-------------------------------------- | |||
%% Include files | |||
%%-------------------------------------- | |||
-include("common111.hrl"). | |||
%%-------------------------------------- | |||
%% Exported Functions | |||
%%-------------------------------------- | |||
-compile(export_all). | |||
%%-------------------------------------- | |||
%%Protocol: 44001 升级技能 | |||
%%-------------------------------------- | |||
write(44001, [UpgradeType]) -> | |||
{ok, pt:pack(44001, <<UpgradeType:8>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 44006 升星 | |||
%%-------------------------------------- | |||
write(44006, [AutoBuy, BatchUpgrade]) -> | |||
{ok, pt:pack(44006, <<AutoBuy:8, BatchUpgrade:8>>)}; | |||
%%-------------------------------------- | |||
%%Protocol: 44007 升阶 | |||
%%-------------------------------------- | |||
write(44007, [AutoBuy]) -> | |||
{ok, pt:pack(44007, <<AutoBuy:8>>)}; | |||
%%-------------------------------------- | |||
%% undefined command | |||
%%-------------------------------------- | |||
write(Cmd, _R) -> | |||
?ERROR_MSG("~s_errorcmd_[~p] ", [misc:time_format(game_timer:now()), Cmd]), | |||
{ok, pt:pack(0, <<>>)}. | |||
%%------------------------------------ | |||
%% internal function | |||
%%------------------------------------ | |||
pack_string(Str) -> | |||
BinData = tool:to_binary(Str), | |||
Len = byte_size(BinData), | |||
<<Len:16, BinData/binary>>. | |||
any_to_binary(Any) -> | |||
tool:to_binary(Any). | |||
@ -1,916 +0,0 @@ | |||
-module(robot). | |||
-behaviour(gen_server). | |||
-include("robot.hrl"). | |||
-compile(export_all). | |||
%% gen_server callbacks | |||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). | |||
-define(START_ROBOT_GOODS, false). | |||
%%% | |||
%%% API | |||
start() -> | |||
start([1, 1500, ?SERVER_PORT, ?SERVER_ADD]), % 默认端口 IP | |||
ok. | |||
%%StartId 起始AccountID | |||
%%Num int 数量 | |||
%%Mod 跑步模式 1 ,2 指定端口和ip | |||
start([StartId0, Num0, Port0, IP0]) -> | |||
io:format("--Start"), | |||
StartId = list_to_integer(atom_to_list(StartId0)), | |||
Num = list_to_integer(atom_to_list(Num0)), | |||
Port = list_to_integer(atom_to_list(Port0)), | |||
IP = atom_to_list(IP0), | |||
%io:format("--StartId:~p, Num:~p, Port:~p, IP:~p ~n",[StartId, Num, Port, IP]), | |||
ets:new(?ETS_ZIP_PROTO, [named_table, public, set, {read_concurrency, true}]), %%压缩协议ets表 | |||
ets:new(player_mon_info, [named_table, public, set, {read_concurrency, true}]), %%压缩协议ets表 | |||
ets:new(off_line_static, [named_table, public, set, {read_concurrency, true}]), | |||
ets:insert(off_line_static, {1, 0}), | |||
ets:insert(off_line_static, {2, 0}), | |||
io:format("--StartId:~p, Num:~p, Port:~p, IP:~p ~n", [StartId, Num, Port, IP]), | |||
sleep(500), | |||
F = fun(N) -> | |||
io:format("start robot-~p~n", [N]), | |||
sleep(150), | |||
robot:start_link(N, Port, IP) | |||
%io:format("----start robot-~p end ~n",[N]) | |||
end, | |||
MaxNum = Num + StartId, | |||
for(StartId, MaxNum, F), | |||
[{_, NUM}] = ets:lookup(off_line_static, 2), | |||
io:format("start finish total attr ~p ~n", [NUM]), | |||
keep_alive(). | |||
keep_alive() -> | |||
sleep(100000), | |||
keep_alive(). | |||
%%创建 一个ROBOT 进程 | |||
start_link(N, Port, IP) -> | |||
io:format("--N:~p, Port:~p, IP:~p ~n", [N, Port, IP]), | |||
case gen_server:start(?MODULE, [N, Port, IP], []) of | |||
{ok, Pid} -> | |||
io:format("--robot~p start finish!-~n", [N]), | |||
case ?START_ROBOT_GOODS of | |||
true -> | |||
gen_server:cast(Pid, {startGoodsTest}); | |||
_ -> | |||
ok | |||
end, | |||
%gen_server:cast(Pid, {start_action}); | |||
Pid, | |||
ok; | |||
_ -> | |||
io:format("--robot error!-~n"), | |||
fail | |||
end. | |||
%% -------------------------------------------------------------------- | |||
%% Function: init/1 | |||
%% Description: Initiates the server | |||
%% Returns: {ok, State} | | |||
%% {ok, State, Timeout} | | |||
%% ignore | | |||
%% {stop, Reason} | |||
%% -------------------------------------------------------------------- | |||
%%初始化玩家数据 | |||
init([N, Port, IP]) -> | |||
io:format("**********200130916 001 robot init N=~p,Port=~p,IP=~p ~n", [N, Port, IP]), | |||
process_flag(trap_exit, true), | |||
Pid = self(), | |||
case login(N, Pid, Port, IP) of | |||
{ok, Socket} -> | |||
random:seed(erlang:now()), | |||
Flag = random:uniform(2), | |||
io:format("--robot init ~p start finish! runing ~p -~n", [N, Flag > 1]), | |||
if Flag > 1 -> | |||
[{_, NUM}] = ets:lookup(off_line_static, 2), | |||
ets:insert(off_line_static, {2, NUM + 1}), | |||
Act = run; | |||
true -> | |||
Act = other | |||
end, | |||
Scene = 10101, | |||
Robot = #robot{socket = Socket, | |||
login = 0, | |||
acid = N, | |||
id = 0, | |||
pid = Pid, | |||
act = chat,%%任务压测 | |||
status = none, | |||
scene = Scene, | |||
dstScene = Scene, | |||
tox = rand(1, 40), | |||
toy = rand(1, 20), | |||
orig_n = N, | |||
step = 0, | |||
guild = 0, | |||
guild_post = 0, | |||
frda = [], %% 好友信息 | |||
bkda = [], %% 黑名单信息, | |||
sgda = [] %% 陌生人信息 | |||
}, | |||
%%登陆成功后开始动作 | |||
io:format("**********200130916 002 robot init finish ~n"), | |||
case ?START_ROBOT_GOODS of | |||
true -> | |||
NewRobot = Robot#robot{act = test_goods}; | |||
_ -> | |||
NewRobot = Robot | |||
end, | |||
{ok, NewRobot}; | |||
_Error -> | |||
?TRACE("init: error, reason: ~p~n", [_Error]), | |||
{stop, normal, {}} | |||
end. | |||
%% -------------------------------------------------------------------- | |||
%% 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({get_state}, _From, State) -> | |||
{reply, State, State}; | |||
%%更新玩家的任务列表信息(模拟前端) | |||
handle_call({upgrade_state_30006, TaskList}, _From, State) -> | |||
io:format("VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV:::~p~n", [TaskList]), | |||
NewState = State#robot{task_list = TaskList}, | |||
{reply, State, NewState}; | |||
%%处理模块发到某个模块的消息 | |||
handle_call({Mod, Msg}, _From, State) -> | |||
case lists:member(Mod, ?RANDOM_MODULE) of | |||
true -> | |||
Module = list_to_atom(lists:concat(["robot_", Mod])), | |||
case catch Module:handle_call(State, Msg) of | |||
{reply, Reply, NewState} when is_record(NewState, robot) -> | |||
{reply, Reply, NewState}; | |||
_ -> | |||
{reply, noreply, State} | |||
end; | |||
false -> | |||
?TRACE("Error cast call: Mod:~p Msg: ~p~n", [Mod, Msg]), | |||
{reply, error, State} | |||
end; | |||
handle_call(_Request, _From, State) -> | |||
Reply = ok, | |||
{reply, Reply, State}. | |||
%% -------------------------------------------------------------------- | |||
%% Function: handle_cast/2 | |||
%% Description: Handling cast messages | |||
%% Returns: {noreply, State} | | |||
%% {noreply, State, Timeout} | | |||
%% {stop, Reason, State} (terminate/2 is called) | |||
%% -------------------------------------------------------------------- | |||
handle_cast({login_ok, _Code}, State) -> | |||
?TRACE("login successful~n"), | |||
NewState = State#robot{login = 1}, | |||
{noreply, NewState}; | |||
handle_cast(login_failed, State) -> | |||
?TRACE("login failed~n"), | |||
{stop, normal, State}; | |||
handle_cast(startGoodsTest, State) -> | |||
io:format("******20130916 robot handle_cast startGoodsTest socket = ~p~n ~n", [State#robot.socket]), | |||
Pid = self(), | |||
spawn_link(fun() -> ai(Pid) end), | |||
{noreply, State}; | |||
handle_cast(enter_ok, State) -> | |||
io:format("========enter_ok:~p ~p~n", [State#robot.act, State#robot.status]), | |||
robot_task:handle(get_task, {}, State#robot.socket),%%获取任务列表 | |||
gen_tcp:send(State#robot.socket, pack(21000, <<>>)), | |||
gen_server:cast(self(), {start_action}), | |||
{noreply, State}; | |||
handle_cast({playerid, Id}, State) -> | |||
NewState = State#robot{id = Id}, | |||
{noreply, NewState}; | |||
handle_cast({after_fight, Len, TargetBin}, State) -> | |||
DataList = get_robot_status(Len, TargetBin, []), | |||
case lists:keyfind(State#robot.id, 1, DataList) of | |||
{_, CurHp} -> | |||
case CurHp > 0 of | |||
true -> | |||
NewState = State; | |||
false -> | |||
NewState = State#robot{status = dead} | |||
end; | |||
_ -> | |||
NewState = State | |||
end, | |||
{noreply, NewState}; | |||
handle_cast({start_action}, State) -> | |||
if is_port(State#robot.socket) -> | |||
%%心跳进程 | |||
spawn_link(fun() -> handle(heart, a, State#robot.socket) end), | |||
Pid = self(), | |||
spawn_link(fun() -> ai(Pid) end), | |||
if ?INITIAL_GM >= 1 -> | |||
spawn(fun() -> robot_gm:handle(State) end); | |||
true -> skip end, | |||
erlang:send_after(1000, Pid, {random}), | |||
{noreply, State}; | |||
true -> | |||
?TRACE("start_action stop_1: /~p/,~n", [State]), | |||
{stop, normal, State} | |||
end; | |||
handle_cast({upgrade_state, NewState}, _State) -> | |||
%%io:format("====upgrade_state ~p~n",[NewState#robot.status]) , | |||
{noreply, NewState}; | |||
handle_cast({get_state_13001}, State) -> | |||
handle(get_self_info, a, State#robot.socket), | |||
{noreply, State}; | |||
handle_cast({init_skill_list, SkillList_ABin}, _State) -> | |||
NewSkillList = robot_battle:make_skill_list(SkillList_ABin, []), | |||
{noreply, _State#robot{skill_list = NewSkillList}}; | |||
handle_cast({upgrade_state_13001, [Scene, X, Y, Hp]}, State) -> | |||
random:seed(erlang:now()), | |||
EnterX = 20 + random:uniform(10) - 5, | |||
EnterY = 10 + random:uniform(10) - 5, | |||
case Hp =< 0 of | |||
true -> | |||
NewState = State#robot{x = EnterX, y = EnterY, scene = Scene, hp = Hp, status = dead}; | |||
%%NewState = State#robot{x=X, y=Y,scene=Scene,hp = Hp, act = run, status = dead} ; | |||
false -> | |||
NewState = State#robot{x = EnterX, y = EnterY, scene = Scene, hp = Hp, status = standing} | |||
%%NewState = State#robot{x=X, y=Y,scene=Scene,hp = Hp, act = %%run, status = standing} | |||
end, | |||
%% io:format("========upgrade_state_13001 enter scene:~p , ~p , ~p , ~p ~n", [Scene,Hp,NewState#robot.act,NewState#robot.status]) , | |||
handle(enter_scene, [Scene, EnterX, EnterY], NewState#robot.socket), | |||
%%handle(enter_scene, [Scene,X,Y] ,NewState#robot.socket), | |||
{noreply, NewState}; | |||
handle_cast({upgrade_state_revive, [NewSceneId, ReviveX, ReviveY]}, State) -> | |||
NewState = State#robot{status = standing, x = ReviveX, y = ReviveY}, | |||
handle(enter_scene, [NewSceneId, ReviveX, ReviveY], State#robot.socket), | |||
{noreply, NewState}; | |||
handle_cast({upgrade_state_13099, [IdLists]}, State) -> | |||
IdLists1 = [[State#robot.id] | IdLists], | |||
NewState = State#robot{frda = IdLists1}, | |||
{noreply, NewState}; | |||
handle_cast({run}, State) -> | |||
State2 = State#robot{act = run}, | |||
{noreply, State2}; | |||
handle_cast({stop}, State) -> | |||
State2 = State#robot{act = undefined}, | |||
{noreply, State2}; | |||
handle_cast({stop, _Reason}, State) -> | |||
?TRACE("~s_quit_2: /~p/~p/~p/,~n", [misc:time_format(now()), State#robot.acid, State#robot.id, _Reason]), | |||
{stop, normal, State}; | |||
handle_cast({get_bag_list, [Location]}, State) -> | |||
io:format("*********20130916 robot handle_cast get_bag_list Location = ~p~n", [Location]), | |||
{noreply, State}; | |||
%%处理模块发到某个模块的消息 | |||
handle_cast({Mod, Msg}, State) -> | |||
case lists:member(Mod, ?RANDOM_MODULE) of | |||
true -> | |||
Module = list_to_atom(lists:concat(["robot_", Mod])), | |||
case catch Module:handle_cast(Msg, State) of | |||
{noreply, NewState} when is_record(NewState, robot) -> | |||
NewState; | |||
_ -> | |||
State | |||
end; | |||
false -> | |||
?TRACE("Error cast call: Mod:~p Msg: ~p~n", [Mod, Msg]), | |||
State | |||
end, | |||
{noreply, State}; | |||
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({random}, State) -> | |||
NewState = handle_action_random(State), | |||
erlang:send_after(?RANDOM_INTERVAL, self(), {random}), | |||
{noreply, NewState}; | |||
handle_info({stop, _Reason}, State) -> | |||
?TRACE("~s ------ robot stop: /~p/~p/~p/,~n", [misc:time_format(now()), State#robot.acid, State#robot.id, _Reason]), | |||
{stop, normal, State}; | |||
handle_info(close, State) -> | |||
gen_tcp:close(State#robot.socket), | |||
{noreply, State}; | |||
%%处理模块发到某个模块的消息 | |||
handle_info({Mod, Msg}, State) -> | |||
case lists:member(Mod, ?RANDOM_MODULE) of | |||
true -> | |||
Module = list_to_atom(lists:concat(["robot_", Mod])), | |||
case catch Module:handle_info(Msg, State) of | |||
{noreply, NewState} when is_record(NewState, robot) -> | |||
NewState; | |||
_ -> | |||
State | |||
end; | |||
false -> | |||
?TRACE("Error msg call: Mod:~p Msg: ~p~n", [Mod, Msg]), | |||
State | |||
end, | |||
{noreply, State}; | |||
handle_info(_Info, State) -> | |||
{noreply, State}. | |||
%% -------------------------------------------------------------------- | |||
%% Function: terminate/2 | |||
%% Description: Shutdown the server | |||
%% Returns: any (ignored by gen_server) | |||
%% -------------------------------------------------------------------- | |||
terminate(_Reason, State) -> | |||
?TRACE(" ----------terminate-----------~s_quit_4: /~p/~p/~p/,~n", [misc:time_format(now()), State#robot.acid, State#robot.id, _Reason]), | |||
if is_port(State#robot.socket) -> | |||
gen_tcp:close(State#robot.socket); | |||
true -> no_socket | |||
end, | |||
ok. | |||
%% -------------------------------------------------------------------- | |||
%% Func: code_change/3 | |||
%% Purpose: Convert process state when code is changed | |||
%% Returns: {ok, NewState} | |||
%% -------------------------------------------------------------------- | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. | |||
%%========================================================================= | |||
%% 业务处理函数 | |||
%%========================================================================= | |||
%%登录游戏服务器 | |||
login(N, Pid, Port, IP) -> | |||
case connect_server(IP, Port) of | |||
{ok, Socket} -> | |||
?TRACE("~s ---connect to IP:~p Port: ~p ok...~n", [misc:time_format(now()), IP, Port]), | |||
Accid = N, | |||
AccName = "ROBOT" ++ integer_to_list(Accid), | |||
handle(login, {Accid, AccName}, Socket), | |||
spawn_link(fun() -> do_parse_packet(Socket, Pid) end), | |||
{ok, Socket}; | |||
_Reason2 -> | |||
?TRACE("Connect to server failed: ~p~n", [_Reason2]), | |||
error | |||
end. | |||
%%连接服务端 | |||
connect_server(Ip, Port) -> | |||
gen_tcp:connect(Ip, Port, ?TCP_OPTS, 10000). | |||
%% 接受信息 | |||
async_recv(Sock, Length, Timeout) when is_port(Sock) -> | |||
case prim_inet:async_recv(Sock, Length, Timeout) of | |||
{error, Reason} -> throw({Reason}); | |||
{ok, Res} -> Res; | |||
Res -> Res | |||
end. | |||
%%接收来自服务器的数据 - 登陆后进入游戏逻辑 | |||
%%Socket:socket id | |||
%%Client: client记录 | |||
do_parse_packet(Socket, Pid) -> | |||
Ref = async_recv(Socket, ?HEADER_LENGTH, ?HEART_TIMEOUT), | |||
receive | |||
{inet_async, Socket, Ref, {ok, <<Len:16, Cmd:16>>}} -> | |||
?TRACE("receive command: ~p, length: ~p~n", [Cmd, Len]), | |||
BodyLen = Len - ?HEADER_LENGTH, | |||
RecvData = | |||
case BodyLen > 0 of | |||
true -> | |||
Ref1 = async_recv(Socket, BodyLen, ?TCP_TIMEOUT), | |||
receive | |||
{inet_async, Socket, Ref1, {ok, Binary}} -> | |||
{ok, Binary}; | |||
Other -> | |||
?TRACE("Data recv Error: ~p~n", [Other]), | |||
{fail, Other} | |||
end; | |||
false -> | |||
{ok, <<>>} | |||
end, | |||
case RecvData of | |||
{ok, BinData} -> | |||
case Cmd of | |||
10000 -> | |||
<<Code:8, _Bin1/binary>> = BinData, | |||
case Code of | |||
0 -> | |||
gen_server:cast(Pid, {login_ok, 0}), | |||
<<_:32, PlayerId:64, _Bin2/binary>> = _Bin1, | |||
handle(enter_player, {PlayerId}, Socket), | |||
ok; | |||
1 -> | |||
<<Accid:32, _Bin2/binary>> = _Bin1, | |||
gen_server:cast(Pid, {login_ok, 1}), | |||
handle(select_role, Accid, Socket), | |||
ok; | |||
_ -> | |||
gen_server:cast(Pid, login_failed), | |||
?TRACE("login failed: Code: ~p~n", [Code]), | |||
failed | |||
end; | |||
10003 -> | |||
<<Code:8, PlayerId:64, _Bin/binary>> = BinData, | |||
?TRACE("10003: Code: ~p PlayerId~p~n", [Code, PlayerId]), | |||
if Code =:= 1 -> | |||
handle(enter_player, {PlayerId}, Socket), | |||
gen_server:cast(Pid, {playerid, PlayerId}); | |||
true -> | |||
gen_server:cast(Pid, {stop}) | |||
end; | |||
10004 -> | |||
<<Code:8, _Bin/binary>> = BinData, | |||
if | |||
Code =/= 0 -> | |||
%% 选择玩家信息以进入场景 | |||
gen_server:cast(Pid, {get_state_13001}); | |||
true -> | |||
gen_server:cast(Pid, {stop}) | |||
end; | |||
13001 -> | |||
?TRACE("hahhaha ~n", []), | |||
NewData = zlib:uncompress(BinData), | |||
<<_Uid:64, _Gender:8, _Level:8, _Career:8, _Speed:8, SceneId:16, X:8, Y:8, Hp:32, _Other/binary>> = NewData, | |||
%%更新信息 | |||
gen_server:cast(Pid, {upgrade_state_13001, [SceneId, X, Y, Hp]}), | |||
ok; | |||
12001 -> | |||
<<SceneId:16, _Other/binary>> = BinData, | |||
%% io:format("========receive 12001:~p ~n", [SceneId]) , | |||
if | |||
SceneId > 0 -> | |||
gen_tcp:send(Socket, pack(12005, <<>>)), | |||
%% 在场景中走路 | |||
gen_server:cast(Pid, enter_ok); | |||
true -> | |||
gen_server:cast(Pid, {stop}) | |||
end, | |||
ok; | |||
12002 -> %% 更新场景怪物 | |||
NewData = zlib:uncompress(BinData), | |||
State = gen_server:call(Pid, {get_state}), | |||
robot_battle:reflesh_monster(State#robot.acid, NewData); | |||
20003 -> %%人物被攻击 | |||
?TRACE("==20003 ~p~n", [BinData]), | |||
<<_Id1:32, _Hp1:32, _Mp1:32, _Sid1:32, _Slv1:8, _X1:8, _Y1:8, _:32, DLen:16, TarBin/binary>> = BinData, | |||
gen_server:cast(Pid, {after_fight, DLen, TarBin}), | |||
ok; | |||
21000 -> | |||
?TRACE("==21000 ~p~n", [BinData]), | |||
NewData = zlib:uncompress(BinData), | |||
<<_:16, SkillList_ABin/binary>> = NewData, | |||
gen_server:cast(Pid, {init_skill_list, SkillList_ABin}); | |||
12021 -> | |||
<<Code:8, NewSceneId:16, ReviveX:8, ReviveY:8, _Other/binary>> = BinData, | |||
case Code of | |||
1 -> | |||
gen_server:cast(Pid, {upgrade_state_revive, [NewSceneId, ReviveX, ReviveY]}); | |||
_ -> | |||
gen_server:cast(Pid, {stop}) | |||
end, | |||
ok; | |||
10007 -> | |||
<<_Code:8>> = BinData, | |||
?TRACE("==10007 ~p~n", [_Code]), | |||
ok; | |||
30006 -> | |||
NewData = zlib:uncompress(BinData), | |||
<<DataLen:16, Data/binary>> = NewData, | |||
TaskList = robot_task:parse_task_data(Data, []), | |||
gen_server:call(Pid, {upgrade_state_30006, TaskList}), | |||
ok; | |||
_Chat when _Chat >= 11000 andalso _Chat < 12000 -> | |||
skip; | |||
%% robot_chat:do_parse_packet(Socket, Pid, Cmd, BinData); | |||
_Guild when _Guild >= 40000 andalso _Guild < 41000 -> | |||
?TRACE("==_Guild= ~p~n", [_Guild]), | |||
robot_guild:do_parse_packet(Socket, Pid, Cmd, BinData); | |||
%no_action | |||
13021 ->%%修改机器人帮派属性 | |||
<<GuildId:32, _GuildName_RestBin/binary>> = BinData, | |||
{GuildName, _GuildName_DoneBin} = pt:read_string(_GuildName_RestBin), | |||
<<Position:8>> = _GuildName_DoneBin, | |||
gen_server:cast(Pid, {refresh_robot_guild_state, [GuildId, Position]}); | |||
15002 -> | |||
io:format("******20130916 robot recv 15002 ~n"), | |||
gen_server:cast(Pid, {get_bag_list, [0]}); | |||
_ -> | |||
no_action | |||
end, | |||
do_parse_packet(Socket, Pid); | |||
{fail, _} -> | |||
[{1, Num}] = ets:lookup(off_line_static, 1), | |||
NewNum = Num + 1, | |||
ets:insert(off_line_static, {1, NewNum}), | |||
?TRACE("do_parse_packet total off ~p recv data failed:/~p/~p/~n~p~n", [NewNum, Socket, Pid, RecvData]), | |||
gen_tcp:close(Socket), | |||
gen_server:cast(Pid, {stop, socket_error_1}) | |||
end; | |||
%%超时处理 | |||
{inet_async, Socket, Ref, {error, timeout}} -> | |||
io:format("do_parse_packet timeout:/~p/~p/~n", [Socket, Pid]), | |||
do_parse_packet(Socket, Pid); | |||
%%用户断开连接或出错 | |||
Reason -> | |||
[{1, Num}] = ets:lookup(off_line_static, 1), | |||
NewNum = Num + 1, | |||
ets:insert(off_line_static, {1, NewNum}), | |||
io:format("do_parse_packet: total off ~p Error Reason:/~p/~p/~n", [NewNum, Socket, Reason]), | |||
gen_tcp:close(Socket), | |||
gen_server:cast(Pid, {stop, socket_error_3}) | |||
end. | |||
%% 随机事件处理 | |||
handle_action_random(State) -> | |||
Actions = ?RANDOM_MODULE, | |||
if Actions =/= [] -> | |||
Action = lists:nth(random:uniform(length(Actions)), Actions), | |||
Module = list_to_atom(lists:concat(["robot_", Action])), | |||
case catch Module:handle(State) of | |||
NewState when is_record(NewState, robot) -> | |||
NewState; | |||
_Error -> | |||
io:format("ERROR: ~p~n", [_Error]), | |||
State | |||
end; | |||
true -> | |||
State | |||
end. | |||
%%游戏相关操作%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%%心跳包 | |||
handle(heart, _, Socket) -> | |||
case gen_tcp:send(Socket, pack(10006, <<>>)) of | |||
ok -> | |||
sleep(24 * 1000), | |||
handle(heart, a, Socket); | |||
_ -> | |||
error | |||
end; | |||
%%登陆 | |||
handle(login, {Accid, AccName}, Socket) -> | |||
?TRACE("sending login request entry socket: ~p ~p ~p~n", [Accid, AccName, Socket]), | |||
AccStamp = 1273027133, | |||
Tick = integer_to_list(Accid) ++ AccName ++ integer_to_list(AccStamp) ++ ?TICKET, | |||
TickMd5 = util:md5(Tick), | |||
TickMd5Bin = list_to_binary(TickMd5), | |||
TLen = byte_size(TickMd5Bin), | |||
AccNameLen = byte_size(list_to_binary(AccName)), | |||
AccNameBin = list_to_binary(AccName), | |||
Data = <<9999:16, Accid:32, AccStamp:32, AccNameLen:16, AccNameBin/binary, TLen:16, TickMd5Bin/binary>>, | |||
?TRACE("sending login request: ~p ~p~n", [Accid, AccName]), | |||
gen_tcp:send(Socket, pack(10000, Data)), | |||
ok; | |||
%%选择角色进入 | |||
handle(select_role, Accid, Socket) -> | |||
NickName = "GUEST" ++ integer_to_list(Accid), | |||
NameBin = list_to_binary(NickName), | |||
TLen = byte_size(NameBin), | |||
random:seed(erlang:now()), | |||
Gender = random:uniform(2), | |||
Career = random:uniform(3), | |||
StrOsVersion = pt:pack_string("2.3.4"), | |||
Device = pt:pack_string("test"), | |||
Screen = pt:pack_string("test"), | |||
gen_tcp:send(Socket, pack(10003, <<9999:16, Career:8, Gender:8, TLen:16, NameBin/binary, 0:8, StrOsVersion/binary, Device/binary, 0:8, 0:8, 0:8, Screen/binary>>)), | |||
ok; | |||
%%选择角色进入 | |||
handle(enter_player, {PlayerId}, Socket) -> | |||
StrOsVersion = pt:pack_string("2.3.4"), | |||
Device = pt:pack_string("test"), | |||
Screen = pt:pack_string("test"), | |||
%0:8,StrOsVersion/binary,Device/binary,0:8,Screen/binary,0:8,0:8 | |||
gen_tcp:send(Socket, pack(10004, <<9999:16, PlayerId:64, 30:8, 20:8, 0:8, StrOsVersion/binary, Device/binary, 0:8, 0:8, 0:8, Screen/binary>>)), | |||
ok; | |||
%%跑步 | |||
handle(run, {DestX, DestY}, Socket) -> | |||
gen_tcp:send(Socket, pack(12011, <<DestX:16, DestY:16>>)); | |||
%%跑步 | |||
handle(broad_path, {DestX, DestY, Path}, Socket) -> | |||
Len = length(Path), | |||
Fun = fun({X, Y}) -> | |||
<<X:16, Y:16>> | |||
end, | |||
MoveBin = tool:to_binary([Fun(M) || M <- Path]), | |||
gen_tcp:send(Socket, pack(12010, <<DestX:16, DestY:16, Len:16, MoveBin/binary>>)); | |||
%% %%ai模式跑步 | |||
%% handle(run, {X,Y, SX, SY}, Socket) -> | |||
%% ?TRACE("----running:[~p][~p]~n",[X,Y]), | |||
%% gen_tcp:send(Socket, pack(12001, <<X:8, Y:8, SX:8, SY:8>>)); | |||
%%进入场景 | |||
handle(enter_scene, [SceneId, Posx, Posy], Socket) -> | |||
%% Posx = random:uniform(30) , | |||
%% Posy = random:uniform(20) , | |||
%%io:format("========handle(enter_scene:~p ~n", [SceneId]) , | |||
gen_tcp:send(Socket, pack(12001, <<SceneId:16, Posx:16, Posy:16>>)); | |||
%%静止 | |||
handle(undefined, a, _Socket) -> | |||
ok; | |||
%%获取其他玩家信息 | |||
handle(get_player_info, Id, Socket) -> | |||
gen_tcp:send(Socket, pack(13004, <<Id:16>>)); | |||
%%获取自己信息 | |||
handle(get_self_info, _, Socket) -> | |||
?TRACE("get_self_info: sending 13001~n"), | |||
gen_tcp:send(Socket, pack(13001, <<>>)); | |||
%%原地复活 | |||
handle(revive, _, Socket) -> | |||
%% gen_tcp:send(Socket, pack(20004, <<3:8>>)), | |||
%% Action = tool:to_binary("-加血 100000"), | |||
%% ActionLen= byte_size(Action), | |||
%% Data = <<ActionLen:16, Action/binary>>, | |||
%% Packet = pack(11020, Data), | |||
%% gen_tcp:send(Socket, Packet); | |||
io:format("====handle(revive ~p~n", [revive]), | |||
gen_tcp:send(Socket, pack(12021, <<0:16>>)); | |||
handle(_Handle, _Data, Socket) -> | |||
?TRACE("handle error: /~p/~p/~n", [_Handle, _Data]), | |||
{reply, handle_no_match, Socket}. | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%%辅助函数 | |||
%%读取字符串 | |||
read_string(Bin) -> | |||
case Bin of | |||
<<Len:16, Bin1/binary>> -> | |||
case Bin1 of | |||
<<Str:Len/binary-unit:8, Rest/binary>> -> | |||
{binary_to_list(Str), Rest}; | |||
_R1 -> | |||
{[], <<>>} | |||
end; | |||
_R1 -> | |||
{[], <<>>} | |||
end. | |||
random_sleep(T) -> | |||
N = random:uniform(T), | |||
timer:sleep(N * 100). | |||
sleep(T) -> | |||
receive | |||
after T -> ok | |||
end. | |||
for(Max, Max, _F) -> | |||
[]; | |||
for(Min, Max, F) -> | |||
[F(Min) | for(Min + 1, Max, F)]. | |||
for(Max, Max, _F, X) -> | |||
X; | |||
for(Min, Max, F, X) -> | |||
F(X), | |||
for(Min + 1, Max, F, X). | |||
sleep_send({T, S}) -> | |||
receive | |||
after T -> handle(run, a, S) | |||
end. | |||
get_pid(Name) -> | |||
case whereis(Name) of | |||
undefined -> | |||
err; | |||
Pid -> Pid | |||
end. | |||
ping(Node) -> | |||
case net_adm:ping(Node) of | |||
pang -> | |||
?TRACE("ping ~p error.~n", [Node]); | |||
pong -> | |||
?TRACE("ping ~p success.~n", [Node]); | |||
_Error -> | |||
?TRACE("error: ~p ~n", [_Error]) | |||
end. | |||
get_robot_status(0, _TargetBin, DataList) -> | |||
DataList; | |||
get_robot_status(Len, TargetBin, DataList) -> | |||
<<_:8, UId:64, CurHp:32, _:32, _:32, _:32, _:8, OtherBin/binary>> = TargetBin, | |||
NewDataList = DataList ++ [{UId, CurHp}], | |||
get_robot_status(Len - 1, OtherBin, NewDataList). | |||
%%根据机器人状态进行动作 | |||
ai(Pid) -> | |||
%%更新信息 | |||
%% gen_server:cast(Pid,{get_state_13001}), | |||
Random_interval = random:uniform(1000) + 100, | |||
sleep(Random_interval), | |||
State = gen_server:call(Pid, {get_state}), | |||
io:format("========ai(Pid):~p ~p~n", [State#robot.act, State#robot.status]), | |||
case State#robot.act of | |||
run -> | |||
case State#robot.status of | |||
standing -> | |||
io:format("====ai(Pid)standing ~p~n", [standing]), | |||
State2 = robot_battle:stand_call_back(State), | |||
gen_server:cast(State2#robot.pid, {upgrade_state, State2}), | |||
sleep(800); | |||
running -> | |||
io:format("====ai(Pid)running ~p~n", [running]), | |||
if State#robot.step =/= [] -> %%当前坐标不等于目的坐标 | |||
[{NextX, NextY} | LeftPath] = State#robot.step, | |||
handle(run, {NextX, NextY}, State#robot.socket), | |||
State2 = State#robot{x = NextX, y = NextY, step = LeftPath, status = running}, | |||
gen_server:cast(State#robot.pid, {upgrade_state, State2}); | |||
true -> | |||
State2 = State#robot{status = standing}, %%到达目的地, 换个状态为站 | |||
gen_server:cast(State#robot.pid, {upgrade_state, State2}) %%更新机器人状态 | |||
end; | |||
dead -> | |||
%io:format("====ai(Pid)dead ~p~n",[dead]) , | |||
handle(revive, a, State#robot.socket); %%让其复活 | |||
fighting -> | |||
%io:format("====ai(Pid)fighting ~p~n",[fighting]) , | |||
robot_battle:begin_attrack(State), | |||
sleep(800); | |||
_ -> | |||
?TRACE("robot status error!~n") | |||
end, | |||
ai(Pid); | |||
test_goods -> | |||
case ?START_ROBOT_GOODS of | |||
true -> | |||
io:format("***********20130916 test_goods ~n"), | |||
robot_goods:start_robot_test(State), | |||
sleep(800), | |||
ai(Pid); | |||
_ -> | |||
ok | |||
end; | |||
do_task -> | |||
TargetTask = robot_task:get_rand_taskPid(State#robot.task_list), | |||
%%RandTid = rand(1,?MAX_TASK_NUM), | |||
if | |||
is_record(TargetTask, task_list) -> | |||
RandAction = rand(1, 4), | |||
if | |||
RandAction =:= 1 -> | |||
robot_task:accept_task(State#robot.socket, TargetTask#task_list.id); | |||
RandAction =:= 2 -> | |||
robot_task:finish_task(State#robot.socket, rand(1, ?MAX_TASK_NUM)); | |||
RandAction =:= 3 -> | |||
robot_task:submit_task(State#robot.socket, TargetTask#task_list.id); | |||
true -> | |||
robot_task:handle(get_task, {}, State#robot.socket)%%获取任务列表 | |||
end; | |||
true -> | |||
skip | |||
end, | |||
ai(Pid); | |||
chat -> | |||
{X, Y, Z} = erlang:now(), | |||
LastChatTime = get(last_chat_time), | |||
if | |||
LastChatTime == undefined -> | |||
IsHandle = true; | |||
true -> | |||
io:format("Y = ~p LastChatTime = ~p~n", [Y, LastChatTime]), | |||
if | |||
Y - LastChatTime > 5 -> | |||
IsHandle = true; | |||
true -> | |||
IsHandle = false | |||
end | |||
end, | |||
if | |||
IsHandle == true -> | |||
robot_chat:handle(State), | |||
put(last_chat_time, Y); | |||
true -> | |||
skip | |||
end, | |||
ai(Pid); | |||
mail -> | |||
robot_mail:handle(State), | |||
ai(Pid); | |||
mount -> | |||
skip, | |||
ai(Pid); | |||
openfunc -> | |||
skip, | |||
ai(Pid); | |||
newbie -> | |||
skip, | |||
ai(Pid); | |||
_ -> | |||
ok | |||
end. | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. | |||
rand(Same, Same) -> Same; | |||
rand(Min, Max) -> | |||
M = Min - 1, | |||
if | |||
Max - M =< 0 -> | |||
0; | |||
true -> | |||
random:uniform(Max - M) + M | |||
end. | |||
%%@spec 获取怪物追击路径 | |||
make_move_path(StartX, StartY, EndX, EndY, Path) -> | |||
if | |||
StartX =:= EndX andalso StartY =:= EndY -> | |||
Path; | |||
StartX =:= EndX -> | |||
NextX = StartX, | |||
NextY = make_next_step(StartY, EndY), | |||
NewPath = Path ++ [{NextX, NextY}], | |||
make_move_path(NextX, NextY, EndX, EndY, NewPath); | |||
StartY =:= EndY -> | |||
NextX = make_next_step(StartX, EndX), | |||
NextY = EndY, | |||
NewPath = Path ++ [{NextX, NextY}], | |||
make_move_path(NextX, NextY, EndX, EndY, NewPath); | |||
true -> | |||
NextX = make_next_step(StartX, EndX), | |||
NextY = make_next_step(StartY, EndY), | |||
NewPath = Path ++ [{NextX, NextY}], | |||
make_move_path(NextX, NextY, EndX, EndY, NewPath) | |||
end. | |||
make_next_step(Current, Target) -> | |||
if Current > Target -> | |||
if Current - Target > 1 -> | |||
Current - 1; | |||
true -> | |||
Target | |||
end; | |||
true -> | |||
if Target - Current > 1 -> | |||
Current + 1; | |||
true -> | |||
Target | |||
end | |||
end. | |||
rand(Min) when Min =< 0 -> | |||
0; | |||
rand(Max) -> | |||
case get("rand_seed") of | |||
undefined -> | |||
RandSeed = now(), | |||
random:seed(RandSeed), | |||
put("rand_seed", RandSeed); | |||
_ -> skip | |||
end, | |||
random:uniform(Max). | |||
@ -1,97 +0,0 @@ | |||
-include("common111.hrl"). | |||
-include_lib("stdlib/include/ms_transform.hrl"). | |||
-define(CONFIG_FILE, "../config/gateway.config"). | |||
%%连接服务器端口 | |||
-define(GATEWAY_ADD, "192.168.43.135"). | |||
-define(GATEWAY_PORT, 7777). | |||
%% -define(SERVER_ADD,"192.168.51.175"). | |||
%% -define(SERVER_PORT,7777). | |||
-define(SERVER_ADD, "192.168.43.135"). | |||
-define(SERVER_PORT, 7788). | |||
-define(ACTION_SPEED_CONTROL, 10). | |||
-define(ACTION_INTERVAL, ?ACTION_SPEED_CONTROL * 1000). % 自动行为最大时间间隔 | |||
-define(ACTION_MIN, 3000). % 自动行为最小时间间隔 | |||
%%需要随机调用模块列表如robot_chat, | |||
-define(RANDOM_MODULE, [guild]). | |||
-define(RANDOM_INTERVAL, 1000). %%随机操作触发的间隔(毫秒) | |||
-define(MAX_TASK_NUM, 200). %% 任务的个数 | |||
%%设为1, 登录后发GM指令加钱等 | |||
-define(INITIAL_GM, 1). | |||
%%TCP Socket的参数 | |||
-define(TCP_OPTS, [ | |||
binary, | |||
{packet, 0}, % no packaging | |||
{reuseaddr, true}, % allow rebind without waiting | |||
{nodelay, false}, | |||
{delay_send, true}, | |||
{active, false}, | |||
{exit_on_close, false} | |||
]). | |||
%%断言以及打印调试信息宏 | |||
%%不需要时启用 -undefine行 | |||
%% -define(debug,1). | |||
%-undefine(debug). | |||
-ifdef(debug). | |||
-define(TRACE(Str), io:format(Str)). | |||
-define(TRACE(Str, Args), io:format(Str, Args)). | |||
% unicode版 | |||
-define(TRACE_W(Str), io:format("~ts", [list_to_binary(io_lib:format(Str, []))])). | |||
-define(TRACE_W(Str, Args), io:format("~ts", [list_to_binary(io_lib:format(Str, Args))])). | |||
-else. | |||
-define(TRACE(Str), void). | |||
-define(TRACE(Str, Args), void). | |||
-define(TRACE_W(Str), void). | |||
-define(TRACE_W(Str, Args), void). | |||
-endif. | |||
%%机器进程状态 | |||
-record(robot, { | |||
orig_n, | |||
login, | |||
acid, %%account id | |||
accname, %%account id | |||
socket, %%socket | |||
pid, %%process id | |||
rpid, | |||
count = 0, | |||
x, %%x坐标 | |||
y, %%y坐标 | |||
scene, | |||
tox, | |||
toy, | |||
hp, | |||
id, %% ID | |||
act, %% 动作 | |||
status, %% 当前状态 | |||
dstScene, | |||
skill_list = [], | |||
attr_target = 0, | |||
step, | |||
guild, %%0不在帮派中 | |||
guild_post, %%1帮主 | |||
frda, %% 好友信息 | |||
bkda, %% 黑名单信息, | |||
sgda, %% 陌生人信息 | |||
task_list, | |||
task_cd = 0, | |||
move_cd = 0, | |||
skill_cd = 0, | |||
completeId = 0 | |||
}). | |||
-record(task_list, { | |||
id, | |||
taskId, | |||
state, | |||
mark, | |||
grade | |||
}). |
@ -1,130 +0,0 @@ | |||
%% Add description to robot_battle. | |||
-module(robot_battle). | |||
-include("robot.hrl"). | |||
%% ==================================================================== | |||
%% API functions | |||
%% ==================================================================== | |||
-compile(export_all). | |||
%-------------------------- | |||
% 逻辑处理 | |||
%-------------------------- | |||
%初始化玩家技能 | |||
make_skill_list(<<SkillId:8, _:8, Rest/binary>>, List) -> | |||
make_skill_list(Rest, [SkillId | List]); | |||
make_skill_list(<<>>, List) -> | |||
List. | |||
%获取周围怪物信息 | |||
reflesh_monster(AId, BinData) -> | |||
{ok, [_, _, MonList, _]} = read(BinData), | |||
NewMonsterList = lists:map(fun(MonItem) -> | |||
[MonId, _, PosX, PosY, _, _, CurHp, MaxHp, _, _, _] = MonItem, | |||
{MonId, PosX, PosY, CurHp, MaxHp} | |||
end | |||
, MonList), | |||
ets:insert(player_mon_info, {AId, NewMonsterList}). | |||
%%获取默认路径 | |||
make_default_path(State) -> | |||
random:seed(erlang:now()), | |||
DestX = State#robot.x + random:uniform(10) - 3, | |||
DestY = State#robot.y + random:uniform(10) - 3, | |||
Path = robot:make_move_path(State#robot.x, State#robot.y, DestX, DestY, []), | |||
robot:handle(broad_path, {DestX, DestY, Path ++ [{DestX, DestY}]}, State#robot.socket), | |||
State#robot{tox = DestX, toy = DestY, step = Path, status = running}. | |||
%%获取到目标怪物的路径 | |||
make_battle_path(State) -> | |||
case ets:lookup(player_mon_info, State#robot.acid) of | |||
[] -> | |||
make_default_path(State); | |||
[{_, []}] -> | |||
make_default_path(State); | |||
[{_, MonsterLists}] -> | |||
Len = length(MonsterLists), | |||
random:seed(erlang:now()), | |||
Index = random:uniform(Len), | |||
MonInfo = lists:nth(Index, MonsterLists), | |||
{MonId, PosX, PosY, _, _} = MonInfo, | |||
DestX = PosX + random:uniform(5), | |||
DestY = PosY + random:uniform(5), | |||
Path = robot:make_move_path(State#robot.x, State#robot.y, DestX, DestY, []), | |||
robot:handle(broad_path, {DestX, DestY, Path ++ [{DestX, DestY}]}, State#robot.socket), | |||
State#robot{tox = DestX, toy = DestY, step = Path, status = running, attr_target = MonId} | |||
end. | |||
%%漫游完成后回调 | |||
stand_call_back(State) -> | |||
case State#robot.attr_target of | |||
0 -> | |||
make_battle_path(State); | |||
TargetId -> | |||
case ets:lookup(player_mon_info, State#robot.acid) of | |||
[] -> | |||
make_battle_path(State); | |||
[{_, List}] -> | |||
case lists:keyfind(TargetId, 1, List) of | |||
{_, PosX, PosY, CurHp, _} -> | |||
random:seed(erlang:now()), | |||
Flag = random:uniform(10), | |||
if CurHp > 0 andalso abs(State#robot.x - PosX) =< 2 andalso abs(State#robot.y - PosY) =< 2 andalso Flag > 7 -> | |||
begin_attrack(State), | |||
State#robot{status = fighting}; | |||
true -> | |||
make_battle_path(State#robot{attr_target = 0}) | |||
end; | |||
_ -> | |||
make_battle_path(State#robot{attr_target = 0}) | |||
end | |||
end | |||
end. | |||
%%攻击逻辑 | |||
begin_attrack(State) -> | |||
SkillId = get_random_skill(State), | |||
MonId = State#robot.attr_target, | |||
gen_tcp:send(State#robot.socket, robot:pack(21003, <<SkillId:8, 0:8, 123456:32, 0:16, 0:16, 2:8, MonId:32>>)). | |||
%%随机使用技能 | |||
get_random_skill(State) -> | |||
case State#robot.skill_list of | |||
[] -> | |||
0; | |||
List -> | |||
Len = length(List), | |||
Index = random:uniform(Len), | |||
lists:nth(Index, List) | |||
end. | |||
%% ==================================================================== | |||
%% 相关协议解析 | |||
%% ==================================================================== | |||
%%解析12002包 | |||
read(<<ScenedId:16, BinData/binary>>) -> | |||
<<PlayerListLen:16, PlayerListBin/binary>> = BinData, | |||
Fun_PlayerList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<PosX:16, PosY:16, UId:64, _NmBin_RestBin/binary>> = RestBin, | |||
{NmBin, _NmBin_DoneBin} = pt:read_string(_NmBin_RestBin), | |||
<<Stts:8, Sex:8, Crr:8, CurHp:32, MaxHp:32, Magic:32, MagicMax:32, Weapon:32, Armor:32, Fashion:32, WwaponAcc:32, Wing:32, Mount:32, WeaponStrenLv:8, ArmorStrenLv:8, FashionStrenLv:8 | |||
, WaponAccStrenLv:8, WingStrenLv:8, PetStatus:8, PetQualityLv:8, PetFacade:16, _:16, _PetName_RestBin/binary>> = _NmBin_DoneBin, | |||
{PetName, _PetName_DoneBin} = pt:read_string(_PetName_RestBin), | |||
<<Level:8, _:8, _:8, _:16, _:8, _:32, GulidRestBin/binary>> = _PetName_DoneBin, | |||
{_, _PlayerList_RestBin} = pt:read_string(GulidRestBin), | |||
{_PlayerList_RestBin, [[PosX, PosY, UId, NmBin, Stts, Sex, Crr, CurHp, MaxHp, Magic, MagicMax, Weapon, Armor, Fashion, WwaponAcc, Wing, Mount, WeaponStrenLv, ArmorStrenLv, FashionStrenLv, WaponAccStrenLv, WingStrenLv, PetStatus, PetQualityLv, PetFacade, PetName, Level] | ResultList]} | |||
end, | |||
{_PlayerList_DoneBin, PlayerList} = lists:foldl(Fun_PlayerList, {PlayerListBin, []}, lists:seq(1, PlayerListLen)), | |||
<<MonListLen:16, MonListBin/binary>> = _PlayerList_DoneBin, | |||
Fun_MonList = fun(_Idx, {RestBin, ResultList}) -> | |||
<<MonId:32, MonTId:32, PosX:16, PosY:16, Towards:16, Stts:8, CurHp:32, MaxHp:32, Magic:32, MagicMax:32, _:8, _:16, _BuffList_RestBin/binary>> = RestBin, | |||
<<BuffListLen:16, BuffListBin/binary>> = _BuffList_RestBin, | |||
Fun_BuffList = fun(_Idx, {RestBin1, ResultList}) -> | |||
<<BuffId:16, ExpirTime:32, _BuffList_RestBin/binary>> = RestBin1, | |||
{_BuffList_RestBin, [[BuffId, ExpirTime] | ResultList]} | |||
end, | |||
{_BuffList_DoneBin, BuffList} = lists:foldl(Fun_BuffList, {BuffListBin, []}, lists:seq(1, BuffListLen)), | |||
{_BuffList_DoneBin, [[MonId, MonTId, PosX, PosY, Towards, Stts, CurHp, MaxHp, Magic, MagicMax, lists:reverse(BuffList)] | ResultList]} | |||
end, | |||
{_MonList_DoneBin, MonList} = lists:foldl(Fun_MonList, {MonListBin, []}, lists:seq(1, MonListLen)), | |||
<<DropListLen:16, DropListBin/binary>> = _MonList_DoneBin, | |||
Fun_DropList = fun(_Idx, {RestBin2, ResultList}) -> | |||
<<DropId:32, MonId:32, GoodsId:32, GoodsNum:32, DropX:16, DropY:16, EftTime:16, _DropList_RestBin/binary>> = RestBin2, | |||
{_DropList_RestBin, [[DropId, MonId, GoodsId, GoodsNum, DropX, DropY, EftTime] | ResultList]} | |||
end, | |||
{_DropList_DoneBin, DropList} = lists:foldl(Fun_DropList, {DropListBin, []}, lists:seq(1, DropListLen)), | |||
{ok, [ScenedId, lists:reverse(PlayerList), lists:reverse(MonList), lists:reverse(DropList)]}. |
@ -1,90 +0,0 @@ | |||
-module(robot_chat). | |||
-compile(export_all). | |||
-include("robot.hrl"). | |||
%%---------------------- 自动聊天机数据 ---------------------- | |||
-define(AUTO_CHAT_LIST, [ | |||
<<"伟大的中国共产党">>, | |||
<<"跟随失败,队长不能自己跟随自己.">>, | |||
<<"石家庄在下雪">>, | |||
<<"是鹅毛大雪">>, | |||
<<"像是宰了一群鹅">>, | |||
<<"拔了好多鹅毛">>, | |||
<<"也不装进袋子里">>, | |||
<<"像是羽绒服破了">>, | |||
<<"也不缝上">>, | |||
<<"北京也在下雪">>, | |||
<<"不是鹅毛大雪">>, | |||
<<"是白沙粒">>, | |||
<<"有些像白砂糖">>, | |||
<<"有些像碘盐">>, | |||
<<"廊坊夹在石家庄和北京之间">>, | |||
<<"廊坊什么雪也不下">>, | |||
<<"看不到鹅毛">>, | |||
<<"也看不到白砂糖和碘盐">>, | |||
<<"廊坊只管阴着天">>, | |||
<<"像一个女人吊着脸">>, | |||
<<"说话尖酸、刻薄">>, | |||
<<"还冷飕飕的">>, | |||
<<"汉皇①重色思倾国,御宇②多年求不得。杨家有女初长成,养在深闺人未识。">>, | |||
<<"天生丽质难自弃,一朝选在君王侧。回眸一笑百媚生,六宫粉黛无颜色。 ">>, | |||
<<"春寒赐浴华清池,温泉水滑洗凝脂。侍儿扶起娇无力,始是新承恩泽时。 ">>, | |||
<<"云鬓花颜金步摇,芙蓉帐暖度春宵。春宵苦短日高起,从此君王不早朝。 ">>, | |||
<<"承欢侍宴无闲暇,春从春游夜专夜。 后宫佳丽三千人,三千宠爱在一身。 ">>, | |||
<<"金屋妆成娇侍夜,玉楼宴罢醉和春。姊妹弟兄皆列土,可怜光彩生门户③。">>, | |||
<<"遂令天下父母心,不重生男重生女。骊宫高处入青云,仙乐风飘处处闻。 ">>, | |||
<<"缓歌谩舞凝丝竹,尽日君王看不足。渔阳鼙鼓④动地来,惊破霓裳羽衣曲。">>, | |||
<<"九重城阙烟尘生,千乘万骑西南行。翠华摇摇行复止,西出都门百余里。 ">>, | |||
<<"六军不发无奈何,宛转蛾眉马前死。花钿委地无人收,翠翘金雀玉搔头。 ">>, | |||
<<"君王掩面救不得,回看血泪相和流。黄埃散漫风萧索,云栈萦纡登剑阁。 ">>, | |||
<<"峨嵋山下少人行,旌旗无光日色薄⑤。蜀江水碧蜀山青,圣主朝朝暮暮情。 ">>, | |||
<<"行宫见月伤心色,夜雨闻铃肠断声。 天旋地转回龙驭,到此踌躇不能去。 ">>, | |||
<<"马嵬坡下泥土中,不见玉颜空死处。君臣相顾尽沾衣,东望都门信⑥马归。">>, | |||
<<"归来池苑皆依旧,太液芙蓉未央柳。芙蓉如面柳如眉,对此如何不泪垂。 ">>, | |||
<<"春风桃李花开日,秋雨梧桐叶落时。 西宫南内多秋草,落叶满阶红不扫。 ">>, | |||
<<"梨园弟子白发新,椒房阿监青娥老。夕殿萤飞思悄然,孤灯挑尽未成眠。 ">>, | |||
<<"迟迟钟鼓初长夜,耿耿星河欲曙天。 鸳鸯瓦冷霜华重,翡翠衾寒谁与共。 ">>, | |||
<<"悠悠生死别经年,魂魄不曾来入梦。 临邛道士鸿都客,能以精诚致魂魄。 ">>, | |||
<<"为感君王辗转思,遂教方士殷勤觅。排空驭气奔如电,升天入地求之遍。 ">>, | |||
<<"上穷碧落⑦下黄泉,两处茫茫皆不见。忽闻海上有仙山,山在虚无缥渺间。 ">>, | |||
<<"楼阁玲珑五云起,其中绰约多仙子。中有一人字太真,雪肤花貌参差是。 ">>, | |||
<<"金阙西厢叩玉扃⑧,转教小玉报双成。闻道汉家天子使,九华帐里梦魂惊。 ">>, | |||
<<"揽衣推枕起徘徊,珠箔银屏迤逦开⑨。云鬓半偏新睡觉,花冠不整下堂来。 ">>, | |||
<<"风吹仙袂飘飘举,犹似霓裳羽衣舞。玉容寂寞泪阑干⑩,梨花一枝春带雨。">>, | |||
<<"含情凝睇谢君王,一别音容两渺茫。昭阳殿里恩爱绝,蓬莱宫中日月长。 ">>, | |||
<<"回头下望人寰处,不见长安见尘雾。惟将旧物表深情,钿合金钗寄将去。 ">>, | |||
<<"钗留一股合一扇,钗擘黄金合分钿。但教心似金钿坚,天上人间会相见。 ">>, | |||
<<"临别殷勤重寄词,词中有誓两心知。 七月七日长生殿,夜半无人私语时。 ">>, | |||
<<"在天愿作比翼鸟,在地愿为连理枝。 天长地久有时尽,此恨绵绵无绝期。">>, | |||
<<"明年上国富春光">>, | |||
<<"朝廷自昔选才良">>, | |||
<<"时平空山老壮士">>, | |||
<<"代言直似汉文章">>, | |||
<<"生来自秀培来秀">>, | |||
<<"日移花影上窗香">>, | |||
<<"快意一时荷叶雨">>, | |||
<<"乐来一顾遇孙阳">>, | |||
<<"メールアドレス">>, | |||
<<"バックアップファイルのパスを入力して下さい">>, | |||
<<"項目を埋めて Jabber User を検索して下さい">>, | |||
<<"ユーザー統計の取得">>, | |||
<<"は提携が変更されたためキックされました">>, | |||
<<"该点不可行走!">> | |||
]). | |||
handle(State) -> | |||
io:format("chat handle"), | |||
Cmds = [11001], | |||
Chat_List = ?AUTO_CHAT_LIST, | |||
Msg = tool:to_list(lists:nth(random:uniform(length(Chat_List)), Chat_List)), | |||
Cmd = lists:nth(random:uniform(length(Cmds)), Cmds), | |||
{ok, BinData} = ptr_11:write(Cmd, [1, Msg]), | |||
gen_tcp:send(State#robot.socket, BinData), | |||
State. | |||
do_parse_packet(_Socket, _Pid, Cmd, BinData) -> | |||
{ok, _Result} = ptr_11:read(Cmd, BinData), | |||
?TRACE("Cmd: ~p, Result: ~p~n", [Cmd, _Result]). | |||
@ -1,584 +0,0 @@ | |||
-module(robot_gateway). | |||
-behaviour(gen_server). | |||
-include("robot.hrl"). | |||
-compile(export_all). | |||
%% gen_server callbacks | |||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). | |||
%%% | |||
%%% API | |||
start() -> | |||
start(20000, 10000), | |||
ok. | |||
%%StartId 起始AccountID | |||
%%Num int 数量 | |||
%%Mod 跑步模式 1 ,2 | |||
start(StartId, Num) -> | |||
sleep(100), | |||
F = fun(N) -> | |||
io:format("start robot-~p~n", [N]), | |||
sleep(200), | |||
start_link(StartId + N) | |||
end, | |||
for(0, Num, F), | |||
ok. | |||
%%创建 一个ROBOT 进程 | |||
start_link(N) -> | |||
case gen_server:start(?MODULE, [N], []) of | |||
{ok, _Pid} -> | |||
io:format("--robot~p start finish!-~n", [N]); | |||
%gen_server:cast(Pid, {start_action}); | |||
_ -> | |||
fail | |||
end. | |||
%% -------------------------------------------------------------------- | |||
%% Function: init/1 | |||
%% Description: Initiates the server | |||
%% Returns: {ok, State} | | |||
%% {ok, State, Timeout} | | |||
%% ignore | | |||
%% {stop, Reason} | |||
%% -------------------------------------------------------------------- | |||
%%初始化玩家数据 | |||
init([N]) -> | |||
process_flag(trap_exit, true), | |||
Pid = self(), | |||
Robot = #robot{login = 0, | |||
acid = N, | |||
id = 0, | |||
pid = Pid | |||
}, | |||
erlang:send_after(10, self(), {'action'}), | |||
%%登陆成功后开始动作 | |||
{ok, Robot}. | |||
%% -------------------------------------------------------------------- | |||
%% 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({get_state}, _From, State) -> | |||
{reply, State, State}; | |||
%%处理模块发到某个模块的消息 | |||
handle_call({Mod, Msg}, _From, State) -> | |||
case lists:member(Mod, ?RANDOM_MODULE) of | |||
true -> | |||
Module = list_to_atom(lists:concat(["robot_", Mod])), | |||
case catch Module:handle_call(State, Msg) of | |||
{reply, Reply, NewState} when is_record(NewState, robot) -> | |||
{reply, Reply, NewState}; | |||
_ -> | |||
{reply, noreply, State} | |||
end; | |||
false -> | |||
io:format("Error cast call: Mod:~p Msg: ~p~n", [Mod, Msg]), | |||
{reply, error, State} | |||
end; | |||
handle_call(_Request, _From, State) -> | |||
Reply = ok, | |||
{reply, Reply, State}. | |||
%% -------------------------------------------------------------------- | |||
%% Function: handle_cast/2 | |||
%% Description: Handling cast messages | |||
%% Returns: {noreply, State} | | |||
%% {noreply, State, Timeout} | | |||
%% {stop, Reason, State} (terminate/2 is called) | |||
%% -------------------------------------------------------------------- | |||
handle_cast({gateway_fallback}, State) -> | |||
io:format("====gateway_fallback: ~p~n", [State#robot.pid]), | |||
gen_tcp:close(State#robot.socket), | |||
exit(State#robot.rpid, nomal), | |||
erlang:send_after(1000, self(), {'action'}), | |||
{noreply, State#robot{socket = []}}; | |||
handle_cast(login_failed, State) -> | |||
io:format("login failed~n"), | |||
{stop, normal, State}; | |||
handle_cast({playerid, Id}, State) -> | |||
NewState = State#robot{id = Id}, | |||
{noreply, NewState}; | |||
handle_cast(enter_ok, State) -> | |||
NewState = State#robot{act = run, status = standing}, | |||
gen_server:cast(self(), {start_action}), | |||
{noreply, NewState}; | |||
handle_cast({after_fight, Len, TargetBin}, State) -> | |||
DataList = get_robot_status(Len, TargetBin, []), | |||
case lists:keyfind(State#robot.id, 1, DataList) of | |||
{_, CurHp} -> | |||
case CurHp > 0 of | |||
true -> | |||
NewState = State; | |||
false -> | |||
NewState = State#robot{status = dead} | |||
end; | |||
_ -> | |||
NewState = State | |||
end, | |||
{noreply, NewState}; | |||
handle_cast({upgrade_state, NewState}, _State) -> | |||
{noreply, NewState}; | |||
handle_cast({get_state_13001}, State) -> | |||
handle(get_self_info, a, State#robot.socket), | |||
{noreply, State}; | |||
handle_cast({upgrade_state_13001, [Scene, X, Y]}, State) -> | |||
NewState = State#robot{x = X, y = Y, scene = Scene}, | |||
handle(enter_scene, [Scene], State#robot.socket), | |||
{noreply, NewState}; | |||
handle_cast({upgrade_state_revive, []}, State) -> | |||
NewState = State#robot{status = standing}, | |||
{noreply, NewState}; | |||
handle_cast({upgrade_state_13099, [IdLists]}, State) -> | |||
IdLists1 = [[State#robot.id] | IdLists], | |||
NewState = State#robot{frda = IdLists1}, | |||
{noreply, NewState}; | |||
handle_cast({run}, State) -> | |||
State2 = State#robot{act = run}, | |||
{noreply, State2}; | |||
handle_cast({stop}, State) -> | |||
State2 = State#robot{act = undefined}, | |||
{noreply, State2}; | |||
handle_cast({stop, _Reason}, State) -> | |||
io:format("~s_quit_2: /~p/~p/~p/,~n", [misc:time_format(now()), State#robot.acid, State#robot.id, _Reason]), | |||
{stop, normal, State}; | |||
%%处理模块发到某个模块的消息 | |||
handle_cast({Mod, Msg}, State) -> | |||
case lists:member(Mod, ?RANDOM_MODULE) of | |||
true -> | |||
Module = list_to_atom(lists:concat(["robot_", Mod])), | |||
case catch Module:handle_cast(Msg, State) of | |||
{noreply, NewState} when is_record(NewState, robot) -> | |||
NewState; | |||
_ -> | |||
State | |||
end; | |||
false -> | |||
io:format("Error cast call: Mod:~p Msg: ~p~n", [Mod, Msg]), | |||
State | |||
end, | |||
{noreply, State}; | |||
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({'action'}, State) -> | |||
case connect_server(?GATEWAY_ADD, ?GATEWAY_PORT) of | |||
{ok, Socket} -> | |||
Accid = State#robot.acid, | |||
AccName = "ROBOT" ++ integer_to_list(Accid), | |||
handle(login_gateway, {Accid, AccName}, Socket), | |||
RPid = spawn_link(fun() -> do_parse_packet(Socket, State#robot.pid) end), | |||
NewState = State#robot{socket = Socket, rpid = RPid}, | |||
{ok, Socket}; | |||
_Reason2 -> | |||
NewState = State, | |||
io:format("Connect to server failed: ~p~n", [_Reason2]), | |||
error | |||
end, | |||
{noreply, NewState}; | |||
handle_info({random}, State) -> | |||
NewState = handle_action_random(State), | |||
erlang:send_after(?RANDOM_INTERVAL, self(), {random}), | |||
{noreply, NewState}; | |||
handle_info({stop, _Reason}, State) -> | |||
io:format("~s ------ robot stop: /~p/~p/~p/,~n", [misc:time_format(now()), State#robot.acid, State#robot.id, _Reason]), | |||
{stop, normal, State}; | |||
handle_info(close, State) -> | |||
gen_tcp:close(State#robot.socket), | |||
{noreply, State}; | |||
%%处理模块发到某个模块的消息 | |||
handle_info({Mod, Msg}, State) -> | |||
case lists:member(Mod, ?RANDOM_MODULE) of | |||
true -> | |||
Module = list_to_atom(lists:concat(["robot_", Mod])), | |||
case catch Module:handle_info(Msg, State) of | |||
{noreply, NewState} when is_record(NewState, robot) -> | |||
NewState; | |||
_ -> | |||
State | |||
end; | |||
false -> | |||
io:format("Error msg call: Mod:~p Msg: ~p~n", [Mod, Msg]), | |||
State | |||
end, | |||
{noreply, State}; | |||
handle_info(_Info, State) -> | |||
{noreply, State}. | |||
%% -------------------------------------------------------------------- | |||
%% Function: terminate/2 | |||
%% Description: Shutdown the server | |||
%% Returns: any (ignored by gen_server) | |||
%% -------------------------------------------------------------------- | |||
terminate(_Reason, State) -> | |||
io:format(" ----------terminate-----------~s_quit_4: /~p/~p/~p/,~n", [misc:time_format(now()), State#robot.acid, State#robot.id, _Reason]), | |||
if is_port(State#robot.socket) -> | |||
gen_tcp:close(State#robot.socket); | |||
true -> no_socket | |||
end, | |||
ok. | |||
%% -------------------------------------------------------------------- | |||
%% Func: code_change/3 | |||
%% Purpose: Convert process state when code is changed | |||
%% Returns: {ok, NewState} | |||
%% -------------------------------------------------------------------- | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. | |||
%%========================================================================= | |||
%% 业务处理函数 | |||
%%========================================================================= | |||
%%登录游戏服务器 | |||
login(N, Pid) -> | |||
case connect_server(?GATEWAY_ADD, ?GATEWAY_PORT) of | |||
{ok, Socket} -> | |||
Accid = N, | |||
AccName = "ROBOT" ++ integer_to_list(Accid), | |||
handle(login_gateway, {Accid, AccName}, Socket), | |||
spawn_link(fun() -> do_parse_packet(Socket, Pid) end), | |||
{ok, Socket}; | |||
_Reason2 -> | |||
io:format("Connect to server failed: ~p~n", [_Reason2]), | |||
error | |||
end. | |||
%%连接服务端 | |||
connect_server(Ip, Port) -> | |||
gen_tcp:connect(Ip, Port, ?TCP_OPTS, 10000). | |||
%% 接受信息 | |||
async_recv(Sock, Length, Timeout) when is_port(Sock) -> | |||
case prim_inet:async_recv(Sock, Length, Timeout) of | |||
{error, Reason} -> throw({Reason}); | |||
{ok, Res} -> Res; | |||
Res -> Res | |||
end. | |||
%%接收来自服务器的数据 - 登陆后进入游戏逻辑 | |||
%%Socket:socket id | |||
%%Client: client记录 | |||
do_parse_packet(Socket, Pid) -> | |||
Ref = async_recv(Socket, ?HEADER_LENGTH, ?HEART_TIMEOUT), | |||
receive | |||
{inet_async, Socket, Ref, {ok, <<Len:16, Cmd:16>>}} -> | |||
BodyLen = Len - ?HEADER_LENGTH, | |||
RecvData = | |||
case BodyLen > 0 of | |||
true -> | |||
Ref1 = async_recv(Socket, BodyLen, ?TCP_TIMEOUT), | |||
receive | |||
{inet_async, Socket, Ref1, {ok, Binary}} -> | |||
{ok, Binary}; | |||
Other -> | |||
io:format("Data recv Error: ~p~n", [Other]), | |||
{fail, Other} | |||
end; | |||
false -> | |||
{ok, <<>>} | |||
end, | |||
case RecvData of | |||
{ok, _BinData} -> | |||
case Cmd of | |||
60000 -> | |||
gen_server:cast(Pid, {gateway_fallback}); | |||
_ -> | |||
io:format("do_parse_packet recv data failed:/~p/~p/~n~p~n", [Socket, Pid, RecvData]) | |||
end; | |||
{fail, _} -> | |||
io:format("do_parse_packet recv data failed:/~p/~p/~n~p~n", [Socket, Pid, RecvData]), | |||
gen_tcp:close(Socket), | |||
gen_server:cast(Pid, {stop, socket_error_1}) | |||
end; | |||
%%超时处理 | |||
{inet_async, Socket, Ref, {error, timeout}} -> | |||
io:format("do_parse_packet timeout:/~p/~p/~n", [Socket, Pid]), | |||
do_parse_packet(Socket, Pid); | |||
%%用户断开连接或出错 | |||
Reason -> | |||
io:format("do_parse_packet: Error Reason:/~p/~p/~n", [Socket, Reason]), | |||
gen_tcp:close(Socket), | |||
gen_server:cast(Pid, {stop, socket_error_3}) | |||
end. | |||
%% 随机事件处理 | |||
handle_action_random(State) -> | |||
Actions = ?RANDOM_MODULE, | |||
if Actions =/= [] -> | |||
Action = lists:nth(random:uniform(length(Actions)), Actions), | |||
Module = list_to_atom(lists:concat(["robot_", Action])), | |||
case catch Module:handle(State) of | |||
NewState when is_record(NewState, robot) -> | |||
NewState; | |||
_Error -> | |||
io:format("ERROR: ~p~n", [_Error]), | |||
State | |||
end; | |||
true -> | |||
State | |||
end. | |||
%%游戏相关操作%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%%心跳包 | |||
handle(heart, _, Socket) -> | |||
case gen_tcp:send(Socket, pack(10006, <<>>)) of | |||
ok -> | |||
sleep(24 * 1000), | |||
handle(heart, a, Socket); | |||
_ -> | |||
error | |||
end; | |||
%%连接网关 | |||
handle(login_gateway, {Accid, AccName}, Socket) -> | |||
io:format("======sending login_gateway : ~p ~p~n", [Accid, Socket]), | |||
StrBin = tool:to_binary(AccName), | |||
Len = byte_size(StrBin), | |||
Data = <<Accid:32, 123456789:32, Len:16, StrBin/binary>>, | |||
gen_tcp:send(Socket, pack(60000, Data)), | |||
ok; | |||
%%选择角色进入 | |||
handle(select_role, Accid, Socket) -> | |||
NickName = "GUEST" ++ integer_to_list(Accid), | |||
NameBin = list_to_binary(NickName), | |||
TLen = byte_size(NameBin), | |||
Gender = random:uniform(2), | |||
Career = random:uniform(3), | |||
gen_tcp:send(Socket, pack(10003, <<9999:16, Career:8, Gender:8, TLen:16, NameBin/binary>>)), | |||
ok; | |||
%%选择角色进入 | |||
handle(enter_player, {PlayerId}, Socket) -> | |||
%% Posx = random:uniform(30) , | |||
%% Posy = random:uniform(20) , | |||
gen_tcp:send(Socket, pack(10004, <<9999:16, PlayerId:64, 30:8, 20:8>>)), | |||
ok; | |||
%%跑步 | |||
handle(run, {DestX, DestY}, Socket) -> | |||
gen_tcp:send(Socket, pack(12011, <<DestX:8, DestY:8>>)); | |||
%%跑步 | |||
handle(broad_path, {DestX, DestY, Path}, Socket) -> | |||
Len = length(Path), | |||
Fun = fun({X, Y}) -> | |||
<<X:8, Y:8>> | |||
end, | |||
MoveBin = tool:to_binary([Fun(M) || M <- Path]), | |||
gen_tcp:send(Socket, pack(12010, <<DestX:8, DestY:8, Len:16, MoveBin/binary>>)); | |||
%%ai模式跑步 | |||
handle(run, {X, Y, SX, SY}, Socket) -> | |||
io:format("----running:[~p][~p]~n", [X, Y]), | |||
gen_tcp:send(Socket, pack(12001, <<X:8, Y:8, SX:8, SY:8>>)); | |||
%%进入场景 | |||
handle(enter_scene, [SceneId], Socket) -> | |||
Posx = random:uniform(30), | |||
Posy = random:uniform(20), | |||
gen_tcp:send(Socket, pack(12001, <<SceneId:16, Posx:8, Posy:8>>)); | |||
%%静止 | |||
handle(undefined, a, _Socket) -> | |||
ok; | |||
%%获取其他玩家信息 | |||
handle(get_player_info, Id, Socket) -> | |||
gen_tcp:send(Socket, pack(13004, <<Id:16>>)); | |||
%%获取自己信息 | |||
handle(get_self_info, _, Socket) -> | |||
io:format("get_self_info: sending 13001~n"), | |||
gen_tcp:send(Socket, pack(13001, <<>>)); | |||
%%原地复活 | |||
handle(revive, _, Socket) -> | |||
%% gen_tcp:send(Socket, pack(20004, <<3:8>>)), | |||
%% Action = tool:to_binary("-加血 100000"), | |||
%% ActionLen= byte_size(Action), | |||
%% Data = <<ActionLen:16, Action/binary>>, | |||
%% Packet = pack(11020, Data), | |||
%% gen_tcp:send(Socket, Packet); | |||
gen_tcp:send(Socket, pack(12020, <<>>)); | |||
handle(_Handle, _Data, Socket) -> | |||
io:format("handle error: /~p/~p/~n", [_Handle, _Data]), | |||
{reply, handle_no_match, Socket}. | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%%辅助函数 | |||
%%读取字符串 | |||
read_string(Bin) -> | |||
case Bin of | |||
<<Len:16, Bin1/binary>> -> | |||
case Bin1 of | |||
<<Str:Len/binary-unit:8, Rest/binary>> -> | |||
{binary_to_list(Str), Rest}; | |||
_R1 -> | |||
{[], <<>>} | |||
end; | |||
_R1 -> | |||
{[], <<>>} | |||
end. | |||
random_sleep(T) -> | |||
N = random:uniform(T), | |||
timer:sleep(N * 100). | |||
sleep(T) -> | |||
receive | |||
after T -> ok | |||
end. | |||
for(Max, Max, _F) -> | |||
[]; | |||
for(Min, Max, F) -> | |||
[F(Min) | for(Min + 1, Max, F)]. | |||
for(Max, Max, _F, X) -> | |||
X; | |||
for(Min, Max, F, X) -> | |||
F(X), | |||
for(Min + 1, Max, F, X). | |||
sleep_send({T, S}) -> | |||
receive | |||
after T -> handle(run, a, S) | |||
end. | |||
get_pid(Name) -> | |||
case whereis(Name) of | |||
undefined -> | |||
err; | |||
Pid -> Pid | |||
end. | |||
ping(Node) -> | |||
case net_adm:ping(Node) of | |||
pang -> | |||
io:format("ping ~p error.~n", [Node]); | |||
pong -> | |||
io:format("ping ~p success.~n", [Node]); | |||
_Error -> | |||
io:format("error: ~p ~n", [_Error]) | |||
end. | |||
get_robot_status(0, _TargetBin, DataList) -> | |||
DataList; | |||
get_robot_status(Len, TargetBin, DataList) -> | |||
<<_:8, UId:64, CurHp:32, _:32, _:32, _:32, _:8, OtherBin/binary>> = TargetBin, | |||
NewDataList = DataList ++ [{UId, CurHp}], | |||
get_robot_status(Len - 1, OtherBin, NewDataList). | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. | |||
rand(Same, Same) -> Same; | |||
rand(Min, Max) -> | |||
M = Min - 1, | |||
if | |||
Max - M =< 0 -> | |||
0; | |||
true -> | |||
random:uniform(Max - M) + M | |||
end. | |||
%%@spec 获取怪物追击路径 | |||
make_move_path(StartX, StartY, EndX, EndY, Path) -> | |||
if | |||
StartX =:= EndX andalso StartY =:= EndY -> | |||
Path; | |||
StartX =:= EndX -> | |||
NextX = StartX, | |||
NextY = make_next_step(StartY, EndY), | |||
NewPath = Path ++ [{NextX, NextY}], | |||
make_move_path(NextX, NextY, EndX, EndY, NewPath); | |||
StartY =:= EndY -> | |||
NextX = make_next_step(StartX, EndX), | |||
NextY = EndY, | |||
NewPath = Path ++ [{NextX, NextY}], | |||
make_move_path(NextX, NextY, EndX, EndY, NewPath); | |||
true -> | |||
NextX = make_next_step(StartX, EndX), | |||
NextY = make_next_step(StartY, EndY), | |||
NewPath = Path ++ [{NextX, NextY}], | |||
make_move_path(NextX, NextY, EndX, EndY, NewPath) | |||
end. | |||
make_next_step(Current, Target) -> | |||
if Current > Target -> | |||
if Current - Target > 1 -> | |||
Current - 1; | |||
true -> | |||
Target | |||
end; | |||
true -> | |||
if Target - Current > 1 -> | |||
Current + 1; | |||
true -> | |||
Target | |||
end | |||
end. | |||
rand(Min) when Min =< 0 -> | |||
0; | |||
rand(Max) -> | |||
case get("rand_seed") of | |||
undefined -> | |||
RandSeed = now(), | |||
random:seed(RandSeed), | |||
put("rand_seed", RandSeed); | |||
_ -> skip | |||
end, | |||
random:uniform(Max). | |||
@ -1,39 +0,0 @@ | |||
-module(robot_gm). | |||
-compile(export_all). | |||
-include("robot.hrl"). | |||
%%断言以及打印调试信息宏 | |||
%%不需要时启用 -undefine行 | |||
%%-define(gm_debug, 1). | |||
%-undefine(gm_debug). | |||
-ifdef(gm_debug). | |||
-define(MYTRACE(Str), io:format(Str)). | |||
-define(MYTRACE(Str, Args), io:format(Str, Args)). | |||
-else. | |||
-define(MYTRACE(Str), void). | |||
-define(MYTRACE(Str, Args), void). | |||
-endif. | |||
%%---------------------- 初始帐号 ---------------------- | |||
-define(AUTO_CHAT_LIST, [ | |||
<<"-level 10">>, | |||
%% <<"-coin 1000000">>, | |||
%% <<"-bcoin 1000000">>, | |||
%% <<"-gold 100000">>, | |||
%% <<"-bgold 100000">>, | |||
<<"-exp 10000000">> | |||
]). | |||
handle(State) -> | |||
F = fun(Msg) -> | |||
{ok, BinData} = ptr_11:write(11005, [1, Msg]), | |||
mysend(State#robot.socket, BinData) | |||
end, | |||
lists:foreach(F, ?AUTO_CHAT_LIST), | |||
State. | |||
mysend(Socket, BinData) -> | |||
<<_:16, _Cmd:16, _/binary>> = BinData, | |||
?MYTRACE("sending: cmd: ~p~n", [_Cmd]), | |||
gen_tcp:send(Socket, BinData). |
@ -1,60 +0,0 @@ | |||
%% Description: TODO: Add description to robot_goods | |||
-module(robot_goods). | |||
-behaviour(gen_server). | |||
%% | |||
%% Include files | |||
%% | |||
-include("robot.hrl"). | |||
%% | |||
%% Exported Functions | |||
%% | |||
-compile(export_all). | |||
%% -export([]). | |||
%% | |||
%% API Functions | |||
%% | |||
%% 开始机器人逻辑,RS = RobotStatus #robot | |||
start_robot_test(RS) -> | |||
io:format("**********20130916 robot_goods start_robot_test~n"), | |||
handle(15000, RS), | |||
handle(15002, RS#robot.socket), | |||
handle(15003, RS#robot.socket), | |||
handle(15004, RS), | |||
ok. | |||
%% 查询物品详细信息 | |||
handle(15000, RS) -> | |||
?TRACE("**********20130916 robot_goods 15000 handle~n"), | |||
Id = RS#robot.id, | |||
gen_tcp:send(RS#robot.socket, pack(15000, <<Id:64>>)), | |||
ok; | |||
%% 测试背包物品获取 | |||
handle(15002, Socket) -> | |||
?TRACE("**********20130916 robot_goods 15002 handle~n"), | |||
gen_tcp:send(Socket, pack(15002, <<0:8>>)), | |||
ok; | |||
%% 扩充背包 | |||
handle(15003, Socket) -> | |||
?TRACE("**********20130916 robot_goods 15003 handle~n"), | |||
gen_tcp:send(Socket, pack(15003, <<0:8, 1:8>>)), | |||
ok; | |||
%% 背包内拖动物品 | |||
handle(15004, RS) -> | |||
?TRACE("**********20130916 robot_goods 15004 handle~n"), | |||
gen_tcp:send(RS#robot.socket, pack(15004, <<123:64, 1:16, 2:16>>)), | |||
ok. | |||
%% | |||
%% Local Functions | |||
%% | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. |
@ -1,226 +0,0 @@ | |||
-module(robot_guild). | |||
-include("robot.hrl"). | |||
-compile(export_all). | |||
%%断言以及打印调试信息宏 | |||
%%不需要时启用 -undefine行 | |||
-define(guild_debug, 1). | |||
%-undefine(guild_debug). | |||
-ifdef(guild_debug). | |||
-define(MYTRACE(Str), io:format(Str)). | |||
-define(MYTRACE(Str, Args), io:format(Str, Args)). | |||
-else. | |||
-define(MYTRACE(Str), void). | |||
-define(MYTRACE(Str, Args), void). | |||
-endif. | |||
%%Robot进程调用 (外部调用) | |||
handle(State) -> | |||
?TRACE("guild handle : begin"), | |||
if State#robot.guild =:= 0 -> %%没有帮派情况 | |||
case robot:rand(100) of | |||
Int when Int =< 90 -> %%大部分都是加入,不建 | |||
Cmd = 40001; | |||
_ -> | |||
Cmd = 40002 | |||
end, | |||
case Cmd of | |||
40001 -> %查找帮派 -> | |||
IsNotFull = robot:rand(100) rem 2, | |||
IsSameGroup = robot:rand(100) rem 2, | |||
{ok, BinData} = ptr_40:write(Cmd, [1, IsNotFull, IsSameGroup]); | |||
40002 -> %建一个帮派 | |||
%%加建帮令 | |||
GoodsId = 390004205, | |||
Content = string:concat("-addgoods ", util:term_to_string(GoodsId)), | |||
Content2 = string:concat(Content, " 1"), | |||
ContentLen = length(Content2), | |||
NewContent = list_to_binary(Content2), | |||
gen_tcp:send(State#robot.socket, pack(11005, <<0:8, <<ContentLen:16, NewContent:ContentLen/binary-unit:8>>/binary>>)), | |||
%%升级 | |||
Level = 45, | |||
LevelContent = string:concat("-level ", util:term_to_string(Level)), | |||
LevelContentLen = length(LevelContent), | |||
NewLevelContent = list_to_binary(LevelContent), | |||
gen_tcp:send(State#robot.socket, pack(11005, <<0:8, <<LevelContentLen:16, NewLevelContent:LevelContentLen/binary-unit:8>>/binary>>)), | |||
GName = "Guild" ++ integer_to_list(State#robot.acid), | |||
{ok, BinData} = ptr_40:write(Cmd, [GName, <<" ">>]) | |||
end, | |||
mysend(State#robot.socket, BinData), | |||
State; | |||
true -> | |||
if State#robot.guild_post =:= 1 -> %帮主 | |||
%%{ok, Bin} = ptr_40:write(40031, [0]), %%先处理帮派申请 | |||
%% mysend(State#robot.socket, Bin), | |||
ActionCmds = [40005, 40010, 40031, 40034], | |||
Cmd = lists:nth(robot:rand(length(ActionCmds)), ActionCmds); | |||
true -> | |||
%ActionCmds = [40004, 40005, 40006], | |||
case robot:rand(100) < 20 of | |||
true -> | |||
Cmd = 40004; | |||
false -> | |||
ActionCmds = [40005], | |||
Cmd = lists:nth(robot:rand(length(ActionCmds)), ActionCmds) | |||
end | |||
end, | |||
case Cmd of | |||
40004 -> %退出帮派 | |||
{ok, BinData} = ptr_40:write(Cmd, [0]); | |||
40005 -> %查询帮派成员 | |||
{ok, BinData} = ptr_40:write(Cmd, [0, 0]); | |||
%% 40006 -> %弹劾帮主 | |||
%% {ok, BinData} = ptr_40:write(Cmd, [0]); | |||
40010 ->%请求帮派信息 | |||
{ok, BinData} = ptr_40:write(Cmd, [0, 0]); | |||
%% 40011 -> %查看帮派成员的属性值 | |||
%% | |||
%% skip; | |||
40031 -> %申请列表 | |||
{ok, BinData} = ptr_40:write(Cmd, [0]) | |||
%% 40034 -> %解散帮派 | |||
%% {ok, BinData} = ptr_40:write(Cmd, [0]) | |||
%% _ -> | |||
%% skip | |||
end, | |||
mysend(State#robot.socket, BinData), | |||
State | |||
end. | |||
%%Robot进程 | |||
handle_cast({guild_create_ok}, State) -> | |||
NewState = State#robot{guild = 1, guild_post = 1}, | |||
{noreply, NewState}; | |||
%%随便加入帮派 | |||
handle_cast({request_join, GuildId}, State) -> | |||
if State#robot.guild =:= 0 -> | |||
{ok, BinData} = ptr_40:write(40003, [GuildId]), | |||
mysend(State#robot.socket, BinData); | |||
true -> skip end, | |||
{noreply, State}; | |||
handle_cast({join_guild_approve}, State) -> | |||
NewState = State#robot{guild = 1, guild_post = 0}, | |||
{noreply, NewState}; | |||
handle_cast({refresh_robot_guild_state, [GuildId, Position]}, State) -> | |||
?TRACE("refresh_robot_guild_state:GuildId=~p,Position=~p ", [GuildId, Position]), | |||
NewState = State#robot{guild = GuildId, guild_post = Position}, | |||
{noreply, NewState}; | |||
handle_cast({quit_guild}, State) -> | |||
NewState = State#robot{guild = 0, guild_post = 0}, | |||
{noreply, NewState}; | |||
%%帮主对成员的操作 | |||
handle_cast({member, Uid}, State) -> | |||
if State#robot.guild =:= 1 andalso State#robot.guild_post =:= 1 -> %帮主 | |||
ActionCmds = [40033, 40035, 40037], | |||
Cmd = lists:nth(robot:rand(length(ActionCmds)), ActionCmds), | |||
case Cmd of | |||
40033 -> %任命副帮主(帮主操作) | |||
Position = robot:rand(2) + 1, %2妇帮 3长老 | |||
{ok, BinData} = ptr_40:write(Cmd, [Uid, Position]); | |||
40035 -> %踢出成员(帮主/副帮主) | |||
{ok, BinData} = ptr_40:write(Cmd, [Uid]); | |||
40037 -> %帮主让位 | |||
{ok, BinData} = ptr_40:write(Cmd, [Uid]) | |||
end, | |||
mysend(State#robot.socket, BinData); | |||
true -> skip end, | |||
{noreply, State}; | |||
%%成员的操作 | |||
handle_cast({new_guild_chief, Uid}, State) -> | |||
if State#robot.guild =:= 1 andalso State#robot.id =:= Uid -> %新帮主 | |||
{noreply, State#robot{guild_post = 1}}; | |||
true -> {noreply, State} | |||
end; | |||
handle_cast(_, State) -> | |||
{noreply, State}. | |||
%%在另一个进程中 | |||
do_parse_packet(Socket, Pid, Cmd, BinData) ->%%模拟客户端收包 | |||
%%?MYTRACE("do_parse_packet begin: Cmd: ~p, BinData: ~p~n", [Cmd, BinData]), | |||
{ok, DecodeMsg} = ptr_40:read(Cmd, BinData), | |||
%%?MYTRACE("Cmd: ~p, Result: ~p~n", [Cmd, DecodeMsg]), | |||
case Cmd of | |||
40001 -> %%帮派列表 | |||
[_, _, GuildList] = DecodeMsg, | |||
if GuildList =/= [] -> | |||
[[GuildId | _T1] | _T2] = GuildList, | |||
gen_server:cast(Pid, {guild, {request_join, GuildId}}); | |||
true -> skip end; | |||
40002 -> %%建帮派成功 | |||
[Result] = DecodeMsg, | |||
if Result =:= 1 -> gen_server:cast(Pid, {guild, {guild_create_ok}}); | |||
true -> skip end; | |||
40003 -> %%加入帮派成功 | |||
skip; | |||
40004 -> %%退出帮派成功 | |||
[Result] = DecodeMsg, | |||
if Result =:= 1 -> gen_server:cast(Pid, {guild, {quit_guild}}); | |||
true -> skip end; | |||
40005 -> %成员列表 | |||
[StCode | T] = DecodeMsg, | |||
if StCode =:= 1 andalso length(T) >= 1 -> | |||
[Uid | _] = lists:nth(robot:rand(length(T)), T), | |||
gen_server:cast(Pid, {guild, {member, Uid}}); | |||
true -> skip end; | |||
40006 -> %%弹劾操作返回 | |||
skip; | |||
40007 -> %弹劾投票返回 | |||
skip; | |||
40008 -> %弹劾结果 | |||
skip; | |||
40031 -> %%申请列表 | |||
F = fun(Apply) -> | |||
[Uid | _T] = Apply, | |||
Ops = robot:rand(2), | |||
{ok, BinData} = ptr_40:write(40032, [Uid, Ops]), | |||
mysend(Socket, BinData) | |||
end, | |||
lists:foreach(F, DecodeMsg); | |||
40034 -> %%解散帮派(帮主) | |||
[Result] = DecodeMsg, | |||
if Result =:= 1 -> gen_server:cast(Pid, {guild, {quit_guild}}); | |||
true -> skip end; | |||
40070 -> %新成员 | |||
[Uid | _] = DecodeMsg, | |||
gen_server:cast(Pid, {guild, {member, Uid}}); | |||
40071 -> %被踢了 | |||
gen_server:cast(Pid, {guild, {quit_guild}}); | |||
40074 -> %被让位了 | |||
[_, _, Uid, _] = DecodeMsg, | |||
gen_server:cast(Pid, {guild, {new_guild_chief, Uid}}); | |||
40078 -> %通过加入帮派的申请 | |||
gen_server:cast(Pid, {guild, {join_guild_approve}}); | |||
_ -> skip | |||
end. | |||
mysend(Socket, BinData) -> | |||
<<_:16, _Cmd:16, _/binary>> = BinData, | |||
?MYTRACE("sending: cmd: ~p~n", [_Cmd]), | |||
gen_tcp:send(Socket, BinData).%%最后会给玩家进程 | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. |
@ -1,17 +0,0 @@ | |||
-module(robot_mail). | |||
-include("robot.hrl"). | |||
-compile(export_all). | |||
handle(Status) -> | |||
%%因为发邮件不是用户请求协议触发的 所以要靠gm指令来压 | |||
Cmd = 11005, | |||
Type = 0, | |||
Content = "-mail 1", | |||
{ok, BinData} = ptr_11:write(Cmd, [0, Content]), | |||
gen_tcp:send(Status#robot.socket, BinData), | |||
Status. | |||
@ -1,158 +0,0 @@ | |||
-module(robot_market). | |||
-compile(export_all). | |||
%-include("common.hrl"). | |||
-include("robot.hrl"). | |||
-record(bag_list, { | |||
id, | |||
tid, | |||
cell, | |||
num, | |||
stren, | |||
strenPer, | |||
bind | |||
}). | |||
-record(sale_list, { | |||
saleId, | |||
goodsUid, | |||
goodsId, | |||
leftTime, | |||
num, | |||
price | |||
}). | |||
-define(ADD_GOODS, 1). | |||
-define(DO_SALE, 2). | |||
-define(DO_BUY, 3). | |||
-define(GOODS, [262035204]). | |||
-define(ACTIONS, [?ADD_GOODS, ?DO_SALE, ?DO_BUY]). | |||
handle(State) -> | |||
Cmds = [41001], | |||
%%Chat_List = ?AUTO_CHAT_LIST, | |||
%%Msg = tool:to_list(lists:nth(random:uniform(length(Chat_List)), Chat_List)), | |||
Rand = random:uniform(100), | |||
case Rand > 50 of | |||
true -> | |||
Act = ?DO_SALE; | |||
false -> | |||
case Rand > 20 of | |||
true -> | |||
Act = ?ADD_GOODS; | |||
false -> | |||
Act = ?ADD_GOODS | |||
end | |||
end, | |||
%%Act = lists:nth(random:uniform(length(?ACTIONS)), ?ACTIONS), | |||
do_action(State, Act), | |||
State. | |||
do_action(State, Act) -> | |||
case Act of | |||
?ADD_GOODS -> | |||
io:format("do_action:add_goods~n"), | |||
become_vip(State), | |||
sale_add_goods(State); | |||
?DO_SALE -> | |||
io:format("do_action:query_bag~n"), | |||
query_bag(State); | |||
?DO_BUY -> | |||
io:format("do_action:do_query~n"), | |||
do_buy(State); | |||
_ -> | |||
skip | |||
end. | |||
do_parse_packet(_Socket, _Pid, Cmd, BinData) -> | |||
{ok, _Result} = ptr_41:read(Cmd, BinData), | |||
case Cmd of | |||
41001 -> | |||
case _Result of | |||
<<Len:8, SellingBin/binary>> -> | |||
case parse_sale_list(SellingBin, []) of | |||
SaleList when length(SaleList) > 0 -> | |||
Sale = lists:nth(random:uniform(length(SaleList)), SaleList), | |||
io:format("SaleList~p~n", [Sale#sale_list.saleId]), | |||
{ok, BinData2} = ptr_41:write(41002, [Sale#sale_list.saleId]), | |||
gen_tcp:send(_Socket, BinData2); | |||
_ -> | |||
skip | |||
end; | |||
_ -> | |||
skip | |||
end; | |||
_ -> | |||
skip | |||
end, | |||
io:format("Cmd: ~p, Result: ~p~n", [Cmd, _Result]). | |||
do_buy(State) -> | |||
sale_add_gold(State), | |||
do_query(State). | |||
do_query(State) -> | |||
{ok, BinData} = ptr_41:write(41001, [0]), | |||
gen_tcp:send(State#robot.socket, BinData). | |||
do_sale(State, BagList) -> | |||
lists:map(fun(Data) -> | |||
case lists:member(Data#bag_list.tid, ?GOODS) of | |||
true -> | |||
io:format("do_sale::~p~n", [Data#bag_list.id]), | |||
{ok, BinData} = ptr_41:write(41003, [Data#bag_list.id, 1, 10]), | |||
gen_tcp:send(State#robot.socket, BinData); | |||
false -> | |||
skip | |||
end | |||
end, BagList). | |||
refresh_bag(State, BinData) -> | |||
<<Location:8, CellNum:16, ListNum:16, ListBin/binary>> = BinData, | |||
BagList = parse_bag_data(ListBin, []), | |||
do_sale(State, BagList). | |||
parse_sale_list(BinData, Result) -> | |||
case BinData of | |||
<<SaleId:64, GoodsUId:64, GoodsId:64, LeftTime:32, Num:32, Price:32, LeftData/binary>> -> | |||
Result2 = Result ++ [#sale_list{saleId = SaleId, goodsUid = GoodsUId, goodsId = GoodsId, leftTime = LeftTime, num = Num, price = Price}], | |||
parse_sale_list(LeftData, Result2); | |||
_ -> | |||
Result | |||
end. | |||
parse_bag_data(BinData, Result) -> | |||
case BinData of | |||
<<GoodsId:64, TypeId:32, Cell:16, GoodsNum:16, Stren:8, StrenPer:8, Bind:8, LeftData/binary>> -> | |||
Result2 = Result ++ [#bag_list{id = GoodsId, tid = TypeId, cell = Cell, num = GoodsNum, stren = Stren, strenPer = StrenPer, bind = Bind}], | |||
parse_bag_data(LeftData, Result2); | |||
_ -> | |||
io:format("market:parse_bag_data::~p~n", [Result]), | |||
Result | |||
end. | |||
become_vip(State) -> | |||
Content = "-gold 1000010", | |||
ContentLen = length(Content), | |||
NewContent = list_to_binary(Content), | |||
gen_tcp:send(State#robot.socket, pack(11005, <<0:8, <<ContentLen:16, NewContent:ContentLen/binary-unit:8>>/binary>>)). | |||
sale_add_goods(State) -> | |||
GoodsId = lists:nth(random:uniform(length(?GOODS)), ?GOODS), | |||
Content = string:concat("-addgoods ", util:term_to_string(GoodsId)), | |||
Content2 = string:concat(Content, " 1"), | |||
ContentLen = length(Content2), | |||
NewContent = list_to_binary(Content2), | |||
gen_tcp:send(State#robot.socket, pack(11005, <<0:8, <<ContentLen:16, NewContent:ContentLen/binary-unit:8>>/binary>>)). | |||
sale_add_gold(State) -> | |||
GoodsId = lists:nth(random:uniform(length(?GOODS)), ?GOODS), | |||
Content = "-addgold 11", | |||
ContentLen = length(Content), | |||
NewContent = list_to_binary(Content), | |||
gen_tcp:send(State#robot.socket, pack(11005, <<0:8, <<ContentLen:16, NewContent:ContentLen/binary-unit:8>>/binary>>)). | |||
query_bag(State) -> | |||
gen_tcp:send(State#robot.socket, pack(15002, <<0:8>>)). | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. |
@ -1,39 +0,0 @@ | |||
-module(robot_mount). | |||
-include("robot.hrl"). | |||
-compile(export_all). | |||
handle(Status) -> | |||
RandNum = util:rand(1, 3), | |||
if | |||
RandNum == 1 -> | |||
upgrade_mount_star(Status); | |||
RandNum == 2 -> | |||
upgrade_mount_level(Status); | |||
RandNum == 3 -> | |||
upgrade_mount_skill(Status); | |||
true -> | |||
skip | |||
end, | |||
Status. | |||
upgrade_mount_star(Status) -> | |||
Cmd = 44006, | |||
AutoBuy = 1, | |||
BatchUpgrade = 1, | |||
{ok, BinData} = ptr_44:write(Cmd, [AutoBuy, BatchUpgrade]), | |||
gen_tcp:send(Status#robot.socket, BinData), | |||
Status. | |||
upgrade_mount_level(Status) -> | |||
Cmd = 44007, | |||
AutoBuy = 1, | |||
{ok, BinData} = ptr_44:write(Cmd, [AutoBuy]), | |||
gen_tcp:send(Status#robot.socket, BinData), | |||
Status. | |||
upgrade_mount_skill(Status) -> | |||
Cmd = 44001, | |||
UpgradeType = 1, | |||
{ok, BinData} = ptr_44:write(Cmd, [UpgradeType]), | |||
gen_tcp:send(Status#robot.socket, BinData), | |||
Status. |
@ -1,12 +0,0 @@ | |||
-module(robot_newbie). | |||
-include("robot.hrl"). | |||
-compile(export_all). | |||
get_newbie_list(Status) -> | |||
Status. | |||
@ -1,17 +0,0 @@ | |||
-module(robot_openfunc). | |||
-include("robot.hrl"). | |||
-compile(export_all). | |||
handle(Status) -> | |||
%%通过-openfunc XX XX的gm指令来压 | |||
Cmd = 11005, | |||
Type = 0, | |||
Content = "-openfunc 1 20", | |||
{ok, BinData} = ptr_11:write(Cmd, [0, Content]), | |||
gen_tcp:send(Status#robot.socket, BinData), | |||
Status. | |||
@ -1,2 +0,0 @@ | |||
-module(robot_pet). | |||
@ -1,51 +0,0 @@ | |||
-module(robot_shop). | |||
-compile(export_all). | |||
%-include("common.hrl"). | |||
-include("robot.hrl"). | |||
-define(SHOP_REFRESH, 1). | |||
-define(SHOP_BUY, 2). | |||
-define(ADD_GOLD, 3). | |||
-define(ACTIONS, [?SHOP_REFRESH, ?SHOP_BUY]). | |||
handle(State) -> | |||
Rand = random:uniform(100), | |||
case Rand > 50 of | |||
true -> | |||
Act = ?SHOP_REFRESH; | |||
false -> | |||
Act = ?SHOP_BUY | |||
end, | |||
add_gold(State), | |||
do_action(State, Act), | |||
State. | |||
do_action(State, Act) -> | |||
case Act of | |||
?SHOP_REFRESH -> | |||
io:format("do_action:shop_refresh~n"), | |||
do_shop_refresh(State); | |||
?SHOP_BUY -> | |||
io:format("do_action:shop_buy~n"), | |||
do_shop_buy(State); | |||
_ -> | |||
skip | |||
end. | |||
do_shop_refresh(State) -> | |||
BinData = pt:pack(15042, <<>>), | |||
gen_tcp:send(State#robot.socket, BinData). | |||
do_shop_buy(State) -> | |||
BinData = pt:pack(15043, <<>>), | |||
gen_tcp:send(State#robot.socket, BinData). | |||
add_gold(State) -> | |||
Content = "-addgold 111", | |||
ContentLen = length(Content), | |||
NewContent = list_to_binary(Content), | |||
gen_tcp:send(State#robot.socket, pack(11005, <<0:8, <<ContentLen:16, NewContent:ContentLen/binary-unit:8>>/binary>>)). | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. |
@ -1,140 +0,0 @@ | |||
%%-------------------------------------------- | |||
%%---------任务机器人相关接口----------------- | |||
%%-----------------by CXF--------------------- | |||
%%-------------------------------------------- | |||
-module(robot_task). | |||
-include("robot.hrl"). | |||
%% gen_server callbacks | |||
-compile(export_all). | |||
accept_task(Socket, TaskProcessId) -> | |||
io:format("accept_task.....................~n"), | |||
gen_tcp:send(Socket, pack(30002, <<TaskProcessId:32>>)). | |||
finish_task(Socket, TaskId) -> | |||
io:format("finish_task.....................~n"), | |||
Content = string:concat("-taskgoto ", util:term_to_string(TaskId)), | |||
ContentLen = length(Content), | |||
NewContent = list_to_binary(Content), | |||
gen_tcp:send(Socket, pack(11005, <<0:8, <<ContentLen:16, NewContent:ContentLen/binary-unit:8>>/binary>>)). | |||
submit_task(Socket, TaskProcessId) -> | |||
io:format("submit_task.....................~n"), | |||
gen_tcp:send(Socket, pack(30004, <<TaskProcessId:32>>)). | |||
parse_task_data(BinData, Result) -> | |||
case BinData of | |||
<<Id:32, TaskId:16, State:8, Mark:32, Grade:8, LeftData/binary>> -> | |||
Result2 = Result ++ [#task_list{id = Id, taskId = TaskId, state = State, mark = Mark, grade = Grade}], | |||
parse_task_data(LeftData, Result2); | |||
_ -> | |||
Result | |||
end. | |||
%%随机获得玩家的某个任务 | |||
get_rand_taskPid(TaskList) -> | |||
io:format("VVVVVVVVVVVVVVVVVVVVV333::::::::~p~n", [length(TaskList)]), | |||
if | |||
is_list(TaskList) -> | |||
io:format("VVVVVVVVVVVVVVVVVVVVV666::::::::~p~n", [length(TaskList)]), | |||
RandNum = rand(length(TaskList)), | |||
io:format("VVVVVVVVVVVVVVVVVVVVV444::::::::~p~n", [RandNum]), | |||
TargetTask = lists:nth(RandNum, TaskList); | |||
true -> | |||
skip | |||
end. | |||
handle(get_task, {}, Socket) -> | |||
TotalNum = ?MAX_TASK_NUM, | |||
gen_tcp:send(Socket, pack(30006, <<TotalNum:8>>)). | |||
pack(Cmd, Data) -> | |||
L = byte_size(Data) + ?HEADER_LENGTH, | |||
<<L:16, Cmd:16, Data/binary>>. | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%%辅助函数 | |||
%%读取字符串 | |||
read_string(Bin) -> | |||
case Bin of | |||
<<Len:16, Bin1/binary>> -> | |||
case Bin1 of | |||
<<Str:Len/binary-unit:8, Rest/binary>> -> | |||
{binary_to_list(Str), Rest}; | |||
_R1 -> | |||
{[], <<>>} | |||
end; | |||
_R1 -> | |||
{[], <<>>} | |||
end. | |||
random_sleep(T) -> | |||
N = random:uniform(T), | |||
timer:sleep(N * 100). | |||
sleep(T) -> | |||
receive | |||
after T -> ok | |||
end. | |||
for(Max, Max, _F) -> | |||
[]; | |||
for(Min, Max, F) -> | |||
[F(Min) | for(Min + 1, Max, F)]. | |||
for(Max, Max, _F, X) -> | |||
X; | |||
for(Min, Max, F, X) -> | |||
F(X), | |||
for(Min + 1, Max, F, X). | |||
%%@spec 获取怪物追击路径 | |||
make_move_path(StartX, StartY, EndX, EndY, Path) -> | |||
if | |||
StartX =:= EndX andalso StartY =:= EndY -> | |||
Path; | |||
StartX =:= EndX -> | |||
NextX = StartX, | |||
NextY = make_next_step(StartY, EndY), | |||
NewPath = Path ++ [{NextX, NextY}], | |||
make_move_path(NextX, NextY, EndX, EndY, NewPath); | |||
StartY =:= EndY -> | |||
NextX = make_next_step(StartX, EndX), | |||
NextY = EndY, | |||
NewPath = Path ++ [{NextX, NextY}], | |||
make_move_path(NextX, NextY, EndX, EndY, NewPath); | |||
true -> | |||
NextX = make_next_step(StartX, EndX), | |||
NextY = make_next_step(StartY, EndY), | |||
NewPath = Path ++ [{NextX, NextY}], | |||
make_move_path(NextX, NextY, EndX, EndY, NewPath) | |||
end. | |||
make_next_step(Current, Target) -> | |||
if Current > Target -> | |||
if Current - Target > 1 -> | |||
Current - 1; | |||
true -> | |||
Target | |||
end; | |||
true -> | |||
if Target - Current > 1 -> | |||
Current + 1; | |||
true -> | |||
Target | |||
end | |||
end. | |||
rand(Min) when Min =< 0 -> | |||
0; | |||
rand(Max) -> | |||
case get("rand_seed") of | |||
undefined -> | |||
RandSeed = now(), | |||
random:seed(RandSeed), | |||
put("rand_seed", RandSeed); | |||
_ -> skip | |||
end, | |||
random:uniform(Max). | |||
@ -1,470 +0,0 @@ | |||
-module(record_to_code). | |||
%% | |||
%% Include files | |||
%% | |||
-include("common111.hrl"). | |||
-include("record111.hrl"). | |||
%% | |||
%% Exported Functions | |||
%% | |||
-compile(export_all). | |||
-define(CONFIG_FILE, "../config/gateway.config"). | |||
%% | |||
%% API Functions | |||
%% | |||
start() -> | |||
convert_player(), | |||
case get_db_config(?CONFIG_FILE) of | |||
[Host, Port, User, Password, DB, Encode] -> | |||
start_erlydb(Host, Port, User, Password, DB), | |||
mysql:start_link(?DB_SERVER, Host, Port, User, Password, DB, fun(_, _, _, _) -> ok end, Encode), | |||
mysql:connect(?DB_SERVER, Host, Port, User, Password, DB, Encode, true), | |||
table_fields_all(DB), | |||
get_all_tables(DB), | |||
ok; | |||
_ -> mysql_config_fail | |||
end, | |||
halt(), | |||
ok. | |||
get_db_config(Config_file) -> | |||
try | |||
{ok, [L]} = file:consult(Config_file), | |||
{_, C} = lists:keyfind(gateway, 1, L), | |||
{_, Mysql_config} = lists:keyfind(mysql_config, 1, C), | |||
{_, 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), | |||
[Host, Port, User, Password, DB, Encode] | |||
catch | |||
_:_ -> no_config | |||
end. | |||
%% | |||
%% Local Functions | |||
%% | |||
start_erlydb(IP, Port, User, Password, Db) -> | |||
erlydb:start(mysql, [{pool_id, erlydb_mysql}, | |||
{hostname, IP}, | |||
{port, Port}, | |||
{username, User}, | |||
{password, Password}, | |||
{database, Db}, | |||
{logfun, fun(_, _, _, _) -> ok end}, | |||
{encoding, utf8}, | |||
{pool_size, 10}]). | |||
convert_player() -> | |||
io:format("~n~n~n~n~n~nBegin create ../src/lib/lib_player_rw.erl!~n~n"), | |||
P_list = record_info(fields, player), | |||
O_list = record_info(fields, player_other), | |||
B_list = record_info(fields, battle_attr), | |||
File = "../src/lib/lib_player_rw.erl", | |||
file:write_file(File, ""), | |||
file:write_file(File, ""), | |||
file:write_file(File, "%%%------------------------------------------------\t\n", [append]), | |||
file:write_file(File, "%%% File : lib_player_rw.erl\t\n", [append]), | |||
file:write_file(File, "%%% Author : csj\t\n", [append]), | |||
Bytes0 = list_to_binary(io_lib:format("%%% Created : ~s\t\n", [time_format(now())])), | |||
file:write_file(File, Bytes0, [append]), | |||
file:write_file(File, "%%% Description: 从record生成的代码\t\n", [append]), | |||
file:write_file(File, "%%% Warning: 由程序自动生成,请不要随意修改!\t\n", [append]), | |||
file:write_file(File, "%%%------------------------------------------------ \t\n", [append]), | |||
file:write_file(File, " \t\n", [append]), | |||
file:write_file(File, "-module(lib_player_rw).\t\n", [append]), | |||
file:write_file(File, " \t\n", [append]), | |||
file:write_file(File, "%% \t\n", [append]), | |||
file:write_file(File, "%% Include files \t\n", [append]), | |||
file:write_file(File, "-include(\"common.hrl\"). \t\n", [append]), | |||
file:write_file(File, "-include(\"record.hrl\"). \t\n", [append]), | |||
file:write_file(File, " \t\n", [append]), | |||
file:write_file(File, "%% \t\n", [append]), | |||
file:write_file(File, "%% Exported Functions \t\n", [append]), | |||
file:write_file(File, "%% \t\n", [append]), | |||
file:write_file(File, "-compile(export_all). \t\n", [append]), | |||
file:write_file(File, " \t\n", [append]), | |||
file:write_file(File, "%%获取用户信息(按[字段1,字段2,...])\t\n", [append]), | |||
file:write_file(File, "%% handle_call({'PLAYER', [x ,y]}, _from, Status)\t\n", [append]), | |||
file:write_file(File, "get_player_info_fields(Player, List) ->\t\n", [append]), | |||
file:write_file(File, " lists:map(fun(T) ->\t\n", [append]), | |||
file:write_file(File, " case T of\t\n", [append]), | |||
lists:foreach(fun(Field_name) -> | |||
case lists:member(Field_name, B_list) of | |||
false -> | |||
Bytes00 = lists:concat([" ", Field_name, " -> Player#player.", Field_name, ";\t\n"]), | |||
file:write_file(File, Bytes00, [append]); | |||
true -> | |||
no_action | |||
end | |||
end, | |||
P_list), | |||
lists:foreach(fun(Field_name) -> | |||
Bytes00 = lists:concat([" ", Field_name, " -> Player#player.battle_attr#battle_attr.", Field_name, ";\t\n"]), | |||
file:write_file(File, Bytes00, [append]) | |||
end, | |||
B_list), | |||
lists:foreach(fun(Field_name) -> | |||
Bytes00 = lists:concat([" ", Field_name, " -> Player#player.other#player_other.", Field_name, ";\t\n"]), | |||
file:write_file(File, Bytes00, [append]) | |||
end, | |||
O_list), | |||
file:write_file(File, " _ -> undefined\t\n", [append]), | |||
file:write_file(File, " end\t\n", [append]), | |||
file:write_file(File, " end, List).\t\n", [append]), | |||
file:write_file(File, " \t\n", [append]), | |||
file:write_file(File, "%%设置用户信息(按[{字段1,值1},{字段2,值2, add},{字段3,值3, sub}...])\t\n", [append]), | |||
file:write_file(File, "%% handle_cast({'SET_PLAYER',[{x, 10} ,{y, 20, add}, ,{hp, 20, sub}]}, Status)\t\n", [append]), | |||
file:write_file(File, "set_player_info_fields(Player, []) ->\t\n", [append]), | |||
file:write_file(File, " Player;\t\n", [append]), | |||
file:write_file(File, "set_player_info_fields(Player, [H|T]) ->\t\n", [append]), | |||
file:write_file(File, " NewPlayer =\t\n", [append]), | |||
file:write_file(File, " case H of\t\n", [append]), | |||
lists:foreach(fun(Field_name) -> | |||
case Field_name =:= other orelse Field_name =:= battle_attr orelse lists:member(Field_name, B_list) of | |||
false -> | |||
Bytes1 = lists:concat([" {", Field_name, ", Val, add} -> Player#player{", Field_name, "=Player#player.", Field_name, " + Val};\t\n"]), | |||
file:write_file(File, Bytes1, [append]), | |||
Bytes2 = lists:concat([" {", Field_name, ", Val, sub} -> Player#player{", Field_name, "=Player#player.", Field_name, " - Val};\t\n"]), | |||
file:write_file(File, Bytes2, [append]), | |||
Bytes3 = lists:concat([" {", Field_name, ", Val, _} -> Player#player{", Field_name, "= Val};\t\n"]), | |||
file:write_file(File, Bytes3, [append]), | |||
Bytes4 = lists:concat([" {", Field_name, ", Val} -> Player#player{", Field_name, "= Val};\t\n"]), | |||
file:write_file(File, Bytes4, [append]); | |||
true -> no_action | |||
end | |||
end, | |||
P_list), | |||
lists:foreach(fun(Field_name) -> | |||
Bytes1 = lists:concat([" {", Field_name, | |||
", Val, add} -> Player#player{other=Player#player.other#player_other{", Field_name, | |||
" = Player#player.other#player_other.", Field_name, " + Val}};\t\n"]), | |||
file:write_file(File, Bytes1, [append]), | |||
Bytes2 = lists:concat([" {", Field_name, | |||
", Val, sub} -> Player#player{other=Player#player.other#player_other{", Field_name, | |||
" = Player#player.other#player_other.", Field_name, " - Val}};\t\n"]), | |||
file:write_file(File, Bytes2, [append]), | |||
Bytes3 = lists:concat([" {", Field_name, | |||
", Val, _} -> Player#player{other=Player#player.other#player_other{", Field_name, | |||
" = Val}};\t\n"]), | |||
file:write_file(File, Bytes3, [append]), | |||
Bytes4 = lists:concat([" {", Field_name, | |||
", Val} -> Player#player{other=Player#player.other#player_other{", Field_name, | |||
" = Val}};\t\n"]), | |||
file:write_file(File, Bytes4, [append]) | |||
end, | |||
O_list), | |||
lists:foreach(fun(Field_name) -> | |||
case lists:member(Field_name, P_list) of | |||
true -> %%同时字段也是player顶层成员, 两边都改 | |||
Bytes11 = lists:concat([" {", Field_name, | |||
", Val, add} -> Player1 = Player#player{battle_attr=Player#player.battle_attr#battle_attr{", Field_name, | |||
" = Player#player.battle_attr#battle_attr.", Field_name, " + Val}},\t\n"]), | |||
file:write_file(File, Bytes11, [append]), | |||
Bytes12 = lists:concat([" Player1#player{", Field_name, "=Player1#player.", Field_name, " + Val};\t\n"]), | |||
file:write_file(File, Bytes12, [append]), | |||
Bytes21 = lists:concat([" {", Field_name, | |||
", Val, sub} -> Player1 = Player#player{battle_attr=Player#player.battle_attr#battle_attr{", Field_name, | |||
" = Player#player.battle_attr#battle_attr.", Field_name, " - Val}},\t\n"]), | |||
file:write_file(File, Bytes21, [append]), | |||
Bytes22 = lists:concat([" Player1#player{", Field_name, "=Player1#player.", Field_name, " - Val};\t\n"]), | |||
file:write_file(File, Bytes22, [append]), | |||
Bytes31 = lists:concat([" {", Field_name, | |||
", Val, _} -> Player1 = Player#player{battle_attr=Player#player.battle_attr#battle_attr{", Field_name, | |||
" = Val}},\t\n"]), | |||
file:write_file(File, Bytes31, [append]), | |||
Bytes32 = lists:concat([" Player1#player{", Field_name, "= Val};\t\n"]), | |||
file:write_file(File, Bytes32, [append]), | |||
Bytes41 = lists:concat([" {", Field_name, | |||
", Val} -> Player1 = Player#player{battle_attr=Player#player.battle_attr#battle_attr{", Field_name, | |||
" = Val}},\t\n"]), | |||
file:write_file(File, Bytes41, [append]), | |||
Bytes42 = lists:concat([" Player1#player{", Field_name, "= Val};\t\n"]), | |||
file:write_file(File, Bytes42, [append]); | |||
false -> | |||
Bytes1 = lists:concat([" {", Field_name, | |||
", Val, add} -> Player#player{battle_attr=Player#player.battle_attr#battle_attr{", Field_name, | |||
" = Player#player.battle_attr#battle_attr.", Field_name, " + Val}};\t\n"]), | |||
file:write_file(File, Bytes1, [append]), | |||
Bytes2 = lists:concat([" {", Field_name, | |||
", Val, sub} -> Player#player{battle_attr=Player#player.battle_attr#battle_attr{", Field_name, | |||
" = Player#player.battle_attr#battle_attr.", Field_name, " - Val}};\t\n"]), | |||
file:write_file(File, Bytes2, [append]), | |||
Bytes3 = lists:concat([" {", Field_name, | |||
", Val, _} -> Player#player{battle_attr=Player#player.battle_attr#battle_attr{", Field_name, | |||
" = Val}};\t\n"]), | |||
file:write_file(File, Bytes3, [append]), | |||
Bytes4 = lists:concat([" {", Field_name, | |||
", Val} -> Player#player{battle_attr=Player#player.battle_attr#battle_attr{", Field_name, | |||
" = Val}};\t\n"]), | |||
file:write_file(File, Bytes4, [append]) | |||
end | |||
end, | |||
B_list), | |||
file:write_file(File, " _ -> Player\t\n", [append]), | |||
file:write_file(File, " end,\t\n", [append]), | |||
file:write_file(File, " set_player_info_fields(NewPlayer, T).\t\n", [append]), | |||
file:write_file(File, " \t\n", [append]), | |||
file:write_file(File, "%%设置宠物信息(按[{字段1,值1},{字段2,值2, add},{字段3,值3, sub}...])\t\n", [append]), | |||
file:write_file(File, "%% handle_cast({'SET_PET',[{x, 10} ,{y, 20, add}, ,{hp, 20, sub}]}, Status)\t\n", [append]), | |||
io:format("Create ../src/lib/lib_player_rw.erl finished!~n~n"), | |||
ok. | |||
%% 根据表名获取其完全字段 | |||
table_fields_all(DB_name) -> | |||
Filename = "../src/lib/lib_player_rw.erl", | |||
Sql = lists:concat(["SELECT table_name FROM information_schema.tables WHERE table_schema='", tool:to_list(DB_name), "' and table_type ='BASE TABLE'"]), | |||
try | |||
case db_esql:get_all(list_to_binary(Sql)) of | |||
[] -> error1; | |||
A -> | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format("\t\n\t\n%% 根据表名获取其完全字段\t\n", [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format("get_table_fields(Table_name) ->\t\n", [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(" Table_fileds = [ \t\n", [])), | |||
[append]), | |||
L = lists:flatten(A), | |||
F = fun(T) -> | |||
%% io:format("~p~n",[T]), | |||
Sql1 = lists:concat(["SELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_schema= '", tool:to_list(DB_name), "' AND table_name= '", tool:to_list(T), "'"]), | |||
case db_esql:get_all(list_to_binary(Sql1)) of | |||
[] -> error2; | |||
B -> | |||
%% D = lists:flatten(B), | |||
{DL, _} = | |||
lists:mapfoldl(fun([Field, Data_type0, Default0], Sum) -> | |||
Data_type = tool:to_atom(Data_type0), | |||
Default = | |||
case Default0 of | |||
undefined -> | |||
case erlydb_field:get_erl_type(Data_type) of | |||
binary -> | |||
""; | |||
integer -> | |||
0; | |||
_ -> 0 | |||
end; | |||
<<>> -> | |||
case erlydb_field:get_erl_type(Data_type) of | |||
binary -> | |||
""; | |||
integer -> | |||
0; | |||
_ -> "" | |||
end; | |||
<<"[]">> -> | |||
[]; | |||
Val -> | |||
case erlydb_field:get_erl_type(Data_type) of | |||
binary -> | |||
lists:concat(["", binary_to_list(Val), ""]); | |||
integer -> | |||
tool:to_integer(binary_to_list(Val)); | |||
decimal -> | |||
tool:to_float(binary_to_list(Val)); | |||
_ -> | |||
lists:concat([binary_to_list(Val)]) | |||
end | |||
end, | |||
S = if Sum == length(B) -> | |||
io_lib:format("{~s, ~p}", [tool:to_atom(Field), Default]); | |||
true -> | |||
io_lib:format("{~s, ~p},", [tool:to_atom(Field), Default]) | |||
end, | |||
{S, Sum + 1} | |||
end, | |||
1, B), | |||
E = io_lib:format('{~s,[~s]}', [tool:to_atom(T), lists:flatten(DL)]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(" ~s,\t\n", [E])), | |||
[append]), | |||
ok | |||
end | |||
end, | |||
[F(T) || T <- L], | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' {null,""}], \t\n', [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' case lists:keysearch(Table_name,1, Table_fileds) of \t\n', [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' {value,{_, Val}} -> Val; \t\n', [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' _ -> undefined \t\n', [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' end. \t\n', [])), | |||
[append]), | |||
ok | |||
end | |||
catch | |||
_:_ -> fail | |||
end. | |||
%%生成所有的表 | |||
get_all_tables(DB_name) -> | |||
Filename = "../src/lib/lib_player_rw.erl", | |||
Sql = lists:concat(["SELECT table_name FROM information_schema.tables WHERE table_schema='", tool:to_list(DB_name), "' and table_type ='BASE TABLE'"]), | |||
try | |||
case db_esql:get_all(list_to_binary(Sql)) of | |||
[] -> error1; | |||
A -> | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format("\t\n\t\n%% 获取所有表名\t\n", [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format("get_all_tables() ->\t\n", [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(" [ \t\n", [])), | |||
[append]), | |||
L = lists:flatten(A), | |||
F = fun(T) -> | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(" ~s,\t\n", [tool:to_atom(T)])), | |||
[append]) | |||
end, | |||
[F(T) || T <- L], | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' null \t\n', [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(" ]. \t\n", [])), | |||
[append]), | |||
ok | |||
end | |||
catch | |||
_:_ -> fail | |||
end. | |||
%% 根据表名获取其完全字段(前一版本) | |||
table_fields_all_bak(DB_name) -> | |||
Filename = "../src/lib/lib_player_rw.erl", | |||
Sql = lists:concat(["SELECT table_name FROM information_schema.tables WHERE table_schema='", tool:to_list(DB_name), "' and table_type ='BASE TABLE'"]), | |||
try | |||
case db_esql:get_all(list_to_binary(Sql)) of | |||
[] -> error1; | |||
A -> | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format("\t\n\t\n%% 根据表名获取其完全字段\t\n", [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format("get_table_fields(Table_name) ->\t\n", [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(" Table_fileds = [ \t\n", [])), | |||
[append]), | |||
L = lists:flatten(A), | |||
F = fun(T) -> | |||
%% io:format("~p~n",[T]), | |||
Sql1 = lists:concat(["SELECT column_name FROM information_schema.columns WHERE table_schema= '", tool:to_list(DB_name), "' AND table_name= '", tool:to_list(T), "'"]), | |||
case db_esql:get_all(list_to_binary(Sql1)) of | |||
[] -> error2; | |||
B -> | |||
D = lists:flatten(B), | |||
{DL, _} = | |||
lists:mapfoldl(fun(F, Sum) -> | |||
S = if Sum == length(D) -> | |||
io_lib:format("~s", [tool:to_atom(F)]); | |||
true -> | |||
io_lib:format("~s,", [tool:to_atom(F)]) | |||
end, | |||
{S, Sum + 1} | |||
end, | |||
1, D), | |||
E = io_lib:format('{~s,"~s"}', [tool:to_atom(T), lists:flatten(DL)]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(" ~s,\t\n", [E])), | |||
[append]), | |||
ok | |||
end | |||
end, | |||
[F(T) || T <- L], | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' {null,""}], \t\n', [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' case lists:keysearch(Table_name,1, Table_fileds) of \t\n', [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' {value,{_, Val}} -> Val; \t\n', [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' _ -> undefined \t\n', [])), | |||
[append]), | |||
file:write_file(Filename, | |||
list_to_binary(io_lib:format(' end. \t\n', [])), | |||
[append]), | |||
ok | |||
end | |||
catch | |||
_:_ -> fail | |||
end. | |||
%% -------------------------------------------------- | |||
%% time format | |||
one_to_two(One) -> io_lib:format("~2..0B", [One]). | |||
%% @doc get the time's seconds for integer type | |||
%% @spec get_seconds(Time) -> integer() | |||
get_seconds(Time) -> | |||
{_MegaSecs, Secs, _MicroSecs} = Time, | |||
Secs. | |||
time_format(Now) -> | |||
{{Y, M, D}, {H, MM, S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D), " ", | |||
one_to_two(H), ":", one_to_two(MM), ":", one_to_two(S)]). | |||
date_format(Now) -> | |||
{{Y, M, D}, {_H, _MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D)]). | |||
date_hour_format(Now) -> | |||
{{Y, M, D}, {H, _MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D), " ", one_to_two(H)]). | |||
date_hour_minute_format(Now) -> | |||
{{Y, M, D}, {H, MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D), " ", one_to_two(H), "-", one_to_two(MM)]). | |||
%% split by - | |||
minute_second_format(Now) -> | |||
{{_Y, _M, _D}, {H, MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([one_to_two(H), "-", one_to_two(MM)]). | |||
hour_minute_second_format(Now) -> | |||
{{_Y, _M, _D}, {H, MM, S}} = calendar:now_to_local_time(Now), | |||
lists:concat([one_to_two(H), ":", one_to_two(MM), ":", one_to_two(S)]). |
@ -1,485 +0,0 @@ | |||
-module(table_to_erlang). | |||
-compile(export_all). | |||
%% | |||
%% Include files | |||
%% | |||
-include("common111.hrl"). | |||
-define(CONFIG_FILE, "../config/gateway.config"). | |||
-define(TMP_TABLE_PATH, "./tmptable/"). | |||
-define(SRC_TABLE_PATH, "../src/table/"). | |||
-define(BEAM_PATH, "./"). | |||
-define(TABLES_TPLS, [ | |||
%数据库表名 Record名 %erlang文件名 %参数 | |||
{temp_combat_attr, temp_combat_attr, tpl_combat_attr, [1, 2]}, | |||
{temp_goods, temp_goods, tpl_goods, [1]}, | |||
{temp_goods_contain, temp_goods_contain, tpl_goods_contain, [1]}, | |||
{temp_goods_equipment, temp_goods_equipment, tpl_goods_equipment, [1]}, | |||
{temp_goods_gem, temp_goods_gem, tpl_goods_gem, [1]}, | |||
{temp_goods_suit, temp_goods_suit, tpl_goods_suit, [1, 2]}, | |||
%%{temp_mon_layout, temp_mon_layout, data_scene_mon, [1]} , | |||
%{temp_notice,temp_notice, []} , | |||
%{temp_npc,temp_npc, []} , | |||
{temp_npc_layout, temp_npc_layout, tpl_npc_layout, [2, 3]}, | |||
{temp_skill, temp_skill, tpl_skill, [1]}, | |||
{temp_skill_attr, temp_skill_attr, tpl_skill_attr, [2, 3]}, | |||
{temp_task, tpl_task, tpl_task, [1]}, | |||
%{temp_talk,temp_talk, temp_talk,[1]}, | |||
{temp_buff, temp_buff, tpl_buff, [1]}, | |||
{temp_drop_main, temp_drop_main, tpl_drop_main, [1]}, | |||
{temp_drop_sub, temp_drop_sub, tpl_drop_sub, [1]}, | |||
{temp_stren, temp_stren, tpl_stren, [1]}, | |||
{temp_polish, temp_polish, tpl_polish, [1]}, | |||
{temp_upgrade, temp_upgrade, tpl_upgrade, [1]}, | |||
{temp_task_detail, temp_task_detail, tpl_task_detail, [1]}, | |||
{temp_all_stren_reward, temp_all_stren_reward, tpl_all_stren_reward, [1]}, | |||
{temp_polish_goods, temp_polish_goods, tpl_polish_goods, [1]}, | |||
{temp_suit_reward, temp_suit_reward, tpl_suit_reward, [1, 2]}, | |||
{temp_all_gem_reward, temp_all_gem_reward, tpl_all_gem_reward, [1]}, | |||
{temp_gilding, temp_gilding, tpl_gilding, [1, 2]}, | |||
{temp_gold_bag, temp_gold_bag, tpl_gold_bag, [1]}, | |||
{temp_vip_bag, temp_vip_bag, tpl_vip_bag, [1]}, | |||
{temp_god_tried, temp_god_tried, tpl_god_tried, [1]}, | |||
{temp_compose, temp_compose, tpl_compose, [1]}, | |||
{temp_npc_shop, temp_npc_shop, tpl_npc_shop, [1, 2]}, | |||
{temp_meridian, tpl_meridian, tpl_meridian, [2, 3, 4]}, | |||
{temp_bones, tpl_bones, tpl_bones, [1]}, | |||
{temp_shop, temp_shop, tpl_shop, [1, 2]}, | |||
{temp_activity, temp_activity, tpl_activity, [1]}, | |||
{temp_activity_reward, temp_activity_reward, tpl_activity_reward, [1]}, | |||
{temp_mount_attr, temp_mount_attr, tpl_mount_attr, [2, 3]}, | |||
{temp_mount_medicine, temp_mount_medicine, tpl_mount_medicine, [1]}, | |||
{temp_mount_quality, temp_mount_quality, tpl_mount_quality, [1]}, | |||
{temp_mount_skill, temp_mount_skill, tpl_mount_skill, [2, 3]}, | |||
{temp_label, temp_label, tpl_label, [1]}, | |||
{temp_goods_buff, temp_goods_buff, tpl_goods_buff, [1]}, | |||
{temp_cultivation, tpl_cultivation, tpl_cultivation, [1]}, | |||
{temp_pet, temp_pet, tpl_pet, [1]}, | |||
{temp_pet_quality, temp_pet_quality, tpl_pet_quality, [1]}, | |||
{temp_pet_growth, temp_pet_growth, tpl_pet_growth, [1]}, | |||
{temp_pet_aptitude, temp_pet_aptitude, tpl_pet_aptitude, [1]}, | |||
{temp_pet_medicine, temp_pet_medicine, tpl_pet_medicine, [1]}, | |||
{temp_dungeon_group, temp_dungeon_group, tpl_dungeon_group, [1]}, | |||
{temp_dungeon, temp_dungeon, tpl_dungeon, [1]}, | |||
{temp_dungeon_trigger, temp_dungeon_trigger, tpl_dungeon_trigger, [2, 3]}, | |||
{temp_dungeon_obj, temp_dungeon_obj, tpl_dungeon_obj, [2, 3, 4]}, | |||
{temp_rand_shop, temp_rand_shop, tpl_rand_shop, [1]}, | |||
{temp_rand_shop_goods, temp_rand_shop_goods, tpl_rand_shop_goods, [1]}, | |||
{temp_goods_facade, temp_goods_facade, tpl_goods_facade_ex, [1, 2]}, | |||
{temp_goods_facade, temp_goods_facade, tpl_goods_facade, [1]}, | |||
{temp_pet_skill_book, temp_pet_skill_book, tpl_pet_skill_book, [4]}, | |||
{temp_mon_ai, temp_mon_ai, tpl_mon_ai, [1]}, | |||
{temp_tips, temp_tips, tpl_tips, [1]}, | |||
{temp_task_factor, temp_task_factor, tpl_task_factor, [1, 2]}, | |||
{temp_level_bag, temp_level_bag, tpl_level_bag, [1]}, | |||
{temp_energy, temp_energy, tpl_energy, [2, 3]}, | |||
{temp_download_gift, temp_download_gift, tpl_download_gift, [1]}, | |||
{temp_vip, temp_vip, tpl_vip, [1]}, | |||
{temp_vip, temp_vip, tpl_vip2, [9]}, | |||
{temp_guild_level, temp_guild_level, tpl_guild_level, [1]}, | |||
{temp_charge, temp_charge, tpl_charge, [1]}, | |||
{temp_guild_contribution, temp_guild_contribution, tpl_guild_contribution, [1]}, | |||
{temp_pet_skill_list, temp_pet_skill_list, tpl_pet_skill_list, [1]}, | |||
{temp_all_polish_reward, temp_all_polish_reward, tpl_all_polish_reward, [1]}, | |||
{temp_skill_point, temp_skill_point, tpl_skill_point, [1]}, | |||
{temp_task_daily, tpl_task_daily, tpl_task_daily, [2]}, | |||
{temp_cdkey_awards, temp_cdkey_awards, tpl_cdkey_awards, [1]} | |||
]). | |||
%%用于生成返回值为列表的函数 | |||
-define(TABLES_LIST, [ | |||
%数据库表名 %erlang文件名 %参数 %Id名 | |||
{temp_task, tpl_task, [type], [tid]}, | |||
{temp_task, tpl_task, [type, level], [tid]}, | |||
{temp_dungeon, tpl_dungeon, [grp], [sid]}, | |||
{temp_dungeon_trigger, tpl_dungeon_trigger, [sid], [sid, action]}, | |||
{temp_mount_skill, tpl_mount_skill, [mount_level], [sid, level]}, | |||
{temp_label, tpl_label, [type, condition_id], [leader_id]}, | |||
{temp_activity, tpl_activity, [btype, stype], [id]}, | |||
{temp_dungeon_obj, tpl_dungeon_obj, [dun_id], [dun_id, obj_id, action]}, | |||
{temp_rand_shop_goods, tpl_rand_shop_goods, [goods_lv], [goods_id]}, | |||
{temp_goods_facade, tpl_goods_facade_ex, [facade], [gtid, facade]}, | |||
{temp_goods_facade, tpl_goods_facade, [facade], [gtid]}, | |||
{temp_pet_skill_list, tpl_pet_skill_list, [type, condition_id], [list_id]}, | |||
{temp_pet_skill_book, tpl_pet_skill_book, [sid, skill_level], [skill_book_id]} | |||
%% {temp_task, tpl_task,[type,tid,ongoing_dialog],tid}, | |||
%% {temp_task, tpl_task,[type],tid} , | |||
%% {temp_skill_buff,tpl_skill_buff,[name],buff_id}, | |||
%% {temp_task, tpl_task,[start_npc],tid} | |||
]). | |||
%% | |||
%% Exported Functions | |||
%% | |||
%% | |||
%% API Functions | |||
%% | |||
start() -> | |||
case get_db_config(?CONFIG_FILE) of | |||
[Host, Port, User, Password, DB, Encode, _Conns] -> | |||
start_erlydb(Host, Port, User, Password, DB), | |||
mysql:start_link(?DB_SERVER, Host, Port, User, Password, DB, fun(_, _, _, _) -> ok end, Encode), | |||
mysql:connect(?DB_SERVER, Host, Port, User, Password, DB, Encode, true), | |||
tables_to_erlang(), | |||
tables_to_erlang_list(), | |||
ok; | |||
_ -> mysql_config_fail | |||
end, | |||
halt(), | |||
ok. | |||
get_db_config(Config_file) -> | |||
{ok, [L]} = file:consult(Config_file), | |||
{_, C} = lists:keyfind(gateway, 1, L), | |||
{_, Mysql_config} = lists:keyfind(mysql_config, 1, C), | |||
{_, 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]. | |||
%% | |||
%% Local Functions | |||
%% | |||
start_erlydb(IP, Port, User, Password, Db) -> | |||
erlydb:start(mysql, [{pool_id, erlydb_mysql}, | |||
{hostname, IP}, | |||
{port, Port}, | |||
{username, User}, | |||
{password, Password}, | |||
{database, Db}, | |||
{logfun, fun(_, _, _, _) -> ok end}, | |||
{encoding, utf8}, | |||
{pool_size, 10}]). | |||
%% @doc 生成指定的表名的beam文件 | |||
%% @spec code_gen/0 | |||
%% unilog_mysql_pool:code_gen() | |||
code_gen() -> | |||
code_gen(?TABLES_TPLS). | |||
code_gen(TableName) -> | |||
TableList = writeTempFile(TableName), | |||
erlydb:code_gen(TableList, {mysql, | |||
[{allow_unsafe_statements, true}, | |||
{skip_fk_checks, true}]}, | |||
[debug_info, {skip_fk_checks, true}, | |||
{outdir, "../ebin/"}]), | |||
clearTempFile(), | |||
ok. | |||
%% @doc 通过beam生成erl文件,方便开发查看模块方法 | |||
%% 调用该方法之前,必须先调用code_gen()方法,生成表对应的beam文件 | |||
%% @spec code_gen_src/0 | |||
code_gen_src() -> | |||
lists:foreach(fun(TableName) -> | |||
Beam = lists:concat([?BEAM_PATH, TableName, ".beam"]), | |||
case beam_lib:chunks(Beam, [abstract_code]) of | |||
{ok, {_, [{abstract_code, {_, AC}}]}} -> | |||
Code = erl_prettypr:format(erl_syntax:form_list(AC)), | |||
file:write_file(lists:concat([?SRC_TABLE_PATH, TableName, ".erl"]), list_to_binary(Code)), | |||
io:format("build beam:~p to erl:~p success.~n", [TableName, TableName]); | |||
{error, beam_lib, Reason} -> | |||
io:format("code_gen_erl_file error, reason:~p~n", [Reason]) | |||
end | |||
end, ?TABLES_TPLS). | |||
%% @doc 为指定的表名生成module文件,给code_gen/0 使用 | |||
%% @spec writeTempFile/0 ->[TableFilePath] | |||
%% eg: TableFilePath -> "./tmptable/tuser_friend_log.erl" | |||
writeTempFile(TableName) -> | |||
clearTempFile(), | |||
ok = file:make_dir(?TMP_TABLE_PATH), | |||
lists:map(fun(F) -> | |||
Filename = | |||
?TMP_TABLE_PATH ++ atom_to_list(F) ++ ".erl", | |||
Bytes = list_to_binary(io_lib:format("-module(~w).", [F])), | |||
file:write_file(Filename, Bytes), | |||
Filename | |||
end, TableName). | |||
clearTempFile() -> | |||
case file:list_dir(?TMP_TABLE_PATH) of | |||
{ok, Filenames} -> | |||
lists:foreach(fun(F) -> | |||
file:delete(?TMP_TABLE_PATH ++ F) end, Filenames); | |||
{error, _} -> ignore | |||
end, | |||
file:del_dir(?TMP_TABLE_PATH). | |||
%% time format | |||
one_to_two(One) -> io_lib:format("~2..0B", [One]). | |||
%% @doc get the time's seconds for integer type | |||
%% @spec get_seconds(Time) -> integer() | |||
get_seconds(Time) -> | |||
{_MegaSecs, Secs, _MicroSecs} = Time, | |||
Secs. | |||
time_format(Now) -> | |||
{{Y, M, D}, {H, MM, S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D), " ", | |||
one_to_two(H), ":", one_to_two(MM), ":", one_to_two(S)]). | |||
date_format(Now) -> | |||
{{Y, M, D}, {_H, _MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D)]). | |||
date_hour_format(Now) -> | |||
{{Y, M, D}, {H, _MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D), " ", one_to_two(H)]). | |||
date_hour_minute_format(Now) -> | |||
{{Y, M, D}, {H, MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D), " ", one_to_two(H), "-", one_to_two(MM)]). | |||
%% split by - | |||
minute_second_format(Now) -> | |||
{{_Y, _M, _D}, {H, MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([one_to_two(H), "-", one_to_two(MM)]). | |||
hour_minute_second_format(Now) -> | |||
{{_Y, _M, _D}, {H, MM, S}} = calendar:now_to_local_time(Now), | |||
lists:concat([one_to_two(H), ":", one_to_two(MM), ":", one_to_two(S)]). | |||
tables_to_erlang() -> | |||
io:format("~nstart converting table to erlang data TABLES_TPLS ~n", []), | |||
F = fun({TableName, RecordName, FileName, ParamList}) -> | |||
table_to_erlang(atom_to_list(TableName), atom_to_list(RecordName), atom_to_list(FileName), ParamList) | |||
end, | |||
lists:foreach(F, ?TABLES_TPLS). | |||
table_to_erlang(TableName, RecordName, FileName, ParamList) -> | |||
io:format("~s => ~s.erl, \tTable fields ~p as parametes~n", [TableName, FileName, ParamList]), | |||
DataFileName = lists:concat(["../src/data/", FileName, ".erl"]), | |||
%Bakfile = re:replace(lists:flatten(lists:concat([DataFileName , "_", time_format(now())])),"[ :]","_",[global,{return,list}]), | |||
%file:rename(DataFileName, Bakfile), | |||
file:write_file(DataFileName, ""), | |||
file:write_file(DataFileName, "%%%------------------------------------------------\t\n", [append]), | |||
FileBytes = list_to_binary(io_lib:format("%%% File : ~s.erl\t\n", [FileName])), | |||
file:write_file(DataFileName, FileBytes, [append]), | |||
file:write_file(DataFileName, "%%% Author : table_to_erlang\t\n", [append]), | |||
%Bytes = list_to_binary(io_lib:format("%%% Created : ~s\t\n", [time_format(now())])), | |||
Bytes = list_to_binary("%%% Created : \n"), | |||
file:write_file(DataFileName, Bytes, [append]), | |||
TableNameBytes = list_to_binary(io_lib:format("%%% Description:从数据库表~s生成\n", [TableName])), | |||
file:write_file(DataFileName, TableNameBytes, [append]), | |||
file:write_file(DataFileName, "%%% WARNING:程序生成,请不要增加手工代码!\n", [append]), | |||
file:write_file(DataFileName, "%%%------------------------------------------------ \t\n", [append]), | |||
file:write_file(DataFileName, " \t\n", [append]), | |||
ModuleName = lists:concat(["-module(", FileName, ")."]), | |||
file:write_file(DataFileName, ModuleName, [append]), | |||
file:write_file(DataFileName, " \t\n", [append]), | |||
file:write_file(DataFileName, "-compile(export_all).", [append]), | |||
file:write_file(DataFileName, " \t\n", [append]), | |||
%%从MYSQL查表所有内容 | |||
Sql = io_lib:format("select * from ~s;", [TableName]), | |||
Lists = db_esql:get_all(Sql), | |||
TableRecordAtom = list_to_atom(RecordName), | |||
F = fun(ValueList) -> | |||
list_to_tuple([TableRecordAtom | ValueList]) | |||
end, | |||
RecordList = lists:map(F, Lists), | |||
%[Key1|T] = ParamList, | |||
%SortedRecordList = lists:keysort(Key1+1, RecordList), | |||
SortedRecordList = lists:sort(RecordList), | |||
F2 = fun(Record) -> | |||
record_to_erlang(DataFileName, Record, ParamList) | |||
end, | |||
lists:foreach(F2, SortedRecordList), | |||
record_to_erlang_end(DataFileName, ParamList). | |||
%%转换表到Erlang文件, Record为数据库一条记录对应的Record | |||
%%DataFileName为文件名, ParamList为入口参数列表[] | |||
%%如get(Level, Career), ParamList应该指定 Level,Career在数据表位置 | |||
record_to_erlang(DataFileName, Record, ParamList) -> | |||
[RecordName | ValueList] = tuple_to_list(Record), | |||
F1 = fun(Index) -> | |||
Idx = lists:nth(Index, ParamList), | |||
Value = lists:nth(Idx, ValueList), | |||
if Index =:= length(ParamList) -> | |||
Bytes = lists:concat([integer_to_list(Value), ")->\n\t"]); | |||
true -> | |||
Bytes = lists:concat([integer_to_list(Value), ", "]) | |||
end, | |||
file:write_file(DataFileName, list_to_binary(Bytes), [append]) | |||
end, | |||
%%写get(xxx,xxx) -> | |||
file:write_file(DataFileName, "\t\n", [append]), | |||
file:write_file(DataFileName, "get(", [append]), | |||
lists:foreach(F1, lists:seq(1, length(ParamList))), | |||
%%写 {record_name, | |||
file:write_file(DataFileName, list_to_binary(io_lib:format("{~s, ", [RecordName])), [append]), | |||
F2 = fun(Index2) -> | |||
Value2 = lists:nth(Index2, ValueList), | |||
if is_integer(Value2) -> | |||
if Index2 =:= length(ValueList) -> | |||
file:write_file(DataFileName, list_to_binary(io_lib:format("~p};", [Value2])), [append]); | |||
true -> | |||
file:write_file(DataFileName, list_to_binary(io_lib:format("~p, ", [Value2])), [append]) | |||
end; | |||
%%列表类型(字符串) | |||
is_list(Value2) orelse is_binary(Value2) -> | |||
Value3 = case is_binary(Value2) of | |||
true -> binary_to_list(Value2); | |||
false -> Value2 | |||
end, | |||
if Index2 =:= length(ValueList) -> | |||
%%检查是否是[(91),{(123),"(34)开头,如果是, 不在前面加引号, 否则输出字符串时加引号 | |||
case length(Value3) >= 1 andalso | |||
(lists:nth(1, Value3) =:= 91 orelse lists:nth(1, Value3) =:= 123 orelse lists:nth(1, Value3) =:= 34) of | |||
true -> | |||
file:write_file(DataFileName, list_to_binary(io_lib:format("~s};", [Value3])), [append]); | |||
false -> | |||
file:write_file(DataFileName, list_to_binary(io_lib:format("<<\"~s\">>};", [Value3])), [append]) | |||
end; | |||
true -> | |||
%%检查是否是[(91),{(123),"(34)开头,如果是, 不在前面加引号, 否则输出字符串时加引号 | |||
case length(Value3) >= 1 andalso | |||
(lists:nth(1, Value3) =:= 91 orelse lists:nth(1, Value3) =:= 123 orelse lists:nth(1, Value3) =:= 34) of | |||
true -> | |||
file:write_file(DataFileName, list_to_binary(io_lib:format("~s,", [Value3])), [append]); | |||
false -> | |||
file:write_file(DataFileName, list_to_binary(io_lib:format("<<\"~s\">>, ", [Value3])), [append]) | |||
end | |||
end; | |||
true -> | |||
if Index2 =:= length(ValueList) -> | |||
file:write_file(DataFileName, list_to_binary(io_lib:format("~p};", [Value2])), [append]); | |||
true -> | |||
file:write_file(DataFileName, list_to_binary(io_lib:format("~p, ", [Value2])), [append]) | |||
end | |||
end | |||
end, | |||
lists:foreach(F2, lists:seq(1, length(ValueList))). | |||
%% %%写get(_,_, ...) -> []. | |||
record_to_erlang_end(DataFileName, ParamList) -> | |||
F = fun(Index) -> | |||
if Index =:= length(ParamList) -> | |||
Bytes = "_)->\t\n"; | |||
true -> | |||
Bytes = "_, " | |||
end, | |||
file:write_file(DataFileName, list_to_binary(Bytes), [append]) | |||
end, | |||
file:write_file(DataFileName, "\t\n", [append]), | |||
file:write_file(DataFileName, "get(", [append]), | |||
lists:foreach(F, lists:seq(1, length(ParamList))), | |||
file:write_file(DataFileName, "\t[].\t\n", [append]). | |||
%%============将数据库中的列表转换为erlang列表============= | |||
tables_to_erlang_list() -> | |||
io:format("~nstart converting table to erlang data list~n", []), | |||
F = fun({TableName, FileName, ParamList, IdList}) -> | |||
conver_start(atom_to_list(TableName), atom_to_list(FileName), ParamList, list_to_string(IdList)) | |||
end, | |||
lists:foreach(F, ?TABLES_LIST). | |||
%%获取列表宏的元素,逐一操作数据表 | |||
conver_start(TableName, FileName, ParamList, IdList) -> | |||
io:format("~s => ~s.erl, \tTable fields ~p as parametes~n", [TableName, FileName, ParamList]), | |||
DataFileName = lists:concat(["../src/data/", FileName, ".erl"]), | |||
get_filter_data(DataFileName, TableName, ParamList, IdList, FileName). | |||
get_filter_data(DataFileName, TableName, ParamList, IdList, FileName) -> | |||
F = fun(Param, Result) -> | |||
case Result of | |||
0 -> lists:concat([Param]); | |||
_ -> lists:concat([Result, ",", Param]) end end, | |||
Res = lists:foldl(F, 0, ParamList), | |||
Sql = io_lib:format("select distinct ~s from ~s;", [Res, TableName]), | |||
Lists = db_esql:get_all(Sql), | |||
lists:foreach(fun(Obj) -> | |||
construts_data(DataFileName, TableName, ParamList, Obj, IdList, FileName) end, Lists), | |||
EndRes = lists:foldl(fun(_Item, Sum) -> | |||
case Sum of | |||
0 -> ["_"]; | |||
_ -> Sum ++ ["_"] end end, 0, ParamList), | |||
make_fun_head(DataFileName, ParamList, EndRes), | |||
file:write_file(DataFileName, " [].\t\n", [append]). | |||
%%构造erlang函数 | |||
construts_data(DataFileName, TableName, ParamList, Res, IdList, FileName) -> | |||
Filter = for(ParamList, Res, 0, length(ParamList), []), | |||
Sql = io_lib:format("select ~s from ~s ~s;", [IdList, TableName, Filter]), | |||
Lists = db_esql:get_all(Sql), | |||
%%写get(xxx,xxx) -> | |||
make_fun_head(DataFileName, ParamList, Res), | |||
file:write_file(DataFileName, " lists:map(fun([", [append]), | |||
file:write_file(DataFileName, string:to_upper(IdList), [append]), | |||
file:write_file(DataFileName, "])->", [append]), | |||
file:write_file(DataFileName, FileName, [append]), | |||
file:write_file(DataFileName, ":get(", [append]), | |||
file:write_file(DataFileName, string:to_upper(IdList), [append]), | |||
file:write_file(DataFileName, ") end,\n\t", [append]), | |||
file:write_file(DataFileName, term_to_string(Lists), [append]), | |||
file:write_file(DataFileName, ");\t\n", [append]). | |||
%%构造函数头 | |||
make_fun_head(DataFileName, ParamList, Res) -> | |||
file:write_file(DataFileName, "\t\n", [append]), | |||
file:write_file(DataFileName, "get_by", [append]), | |||
lists:foreach(fun(Item) -> | |||
file:write_file(DataFileName, lists:concat(["_", Item]), [append]) end, ParamList), | |||
file:write_file(DataFileName, "(", [append]), | |||
lists:foldl(fun(Index, Sum) -> | |||
Value = lists:nth(Index, Res), | |||
R = case is_binary(Value) of | |||
true -> | |||
lists:concat(["\"", binary_to_list(Value), "\""]); | |||
false -> | |||
Value end, | |||
case Sum of | |||
0 -> | |||
file:write_file(DataFileName, lists:concat([R]), [append]); | |||
_ -> | |||
file:write_file(DataFileName, lists:concat([",", R]), [append]) end | |||
end, 0, lists:seq(1, length(ParamList))), | |||
file:write_file(DataFileName, ")->\t\n", [append]). | |||
%%构造where语句 | |||
for(_ParamList, _ResList, _Index, _Index, Out) -> | |||
Out; | |||
for(ParamList, ResList, I, Index, Out) -> | |||
[Pamram | PRest] = ParamList, | |||
[TempRes | RRest] = ResList, | |||
Res = convert_bin_4(TempRes), | |||
Sql = case I of | |||
0 -> | |||
lists:concat([" where ", Pamram, "=", Res]); | |||
_ -> | |||
lists:concat([Out, " and ", Pamram, "=", Res]) end, | |||
for(PRest, RRest, I + 1, Index, Sql). | |||
convert_bin_4(In) -> | |||
case is_binary(In) of | |||
true -> | |||
lists:concat(["'", binary_to_list(In), "'"]); | |||
false -> | |||
In end. | |||
%%将列表转换为string [a,b,c] -> "a,b,c" | |||
list_to_string(List) -> | |||
case List == [] orelse List == "" of | |||
true -> ""; | |||
false -> | |||
F = fun(E) -> | |||
atom_to_list(E) ++ "," | |||
end, | |||
L1 = [F(E) || E <- List], | |||
L2 = lists:concat(L1), | |||
string:substr(L2, 1, length(L2) - 1) | |||
end. | |||
%% term序列化,term转换为string格式,e.g., [{a},1] => "[{a},1]" | |||
term_to_string(Term) -> | |||
binary_to_list(list_to_binary(io_lib:format("~w", [Term]))). | |||
@ -1,545 +0,0 @@ | |||
-module(table_to_record). | |||
%% | |||
%% Include files | |||
%% | |||
-include("common111.hrl"). | |||
-define(CONFIG_FILE, "../config/gateway.config"). | |||
-define(TMP_TABLE_PATH, "./tmptable/"). | |||
-define(SRC_TABLE_PATH, "../src/table/"). | |||
-define(RECORD_FILENAME, "../include/table_to_record.hrl"). | |||
-define(BEAM_PATH, "./"). | |||
-define(TABLES, | |||
[ | |||
{server, server}, | |||
{config_server, config_server}, | |||
{server_player, server_player}, | |||
{player, player}, | |||
{goods, goods}, | |||
{goods_attribute, goods_attribute}, | |||
{skill, skill}, | |||
{system_config, system_config}, | |||
{feedback, feedback}, | |||
{temp_combat_attr, temp_combat_attr}, | |||
{temp_goods, temp_goods}, | |||
{temp_goods_contain, temp_goods_contain}, | |||
{temp_goods_equipment, temp_goods_equipment}, | |||
{temp_goods_gem, temp_goods_gem}, | |||
{temp_goods_suit, temp_goods_suit}, | |||
{temp_mon_layout, temp_mon_layout}, | |||
{temp_notice, temp_notice}, | |||
{temp_npc, temp_npc}, | |||
{temp_npc_layout, temp_npc_layout}, | |||
{temp_scene, temp_scene}, | |||
{temp_skill, temp_skill}, | |||
{temp_buff, temp_buff}, | |||
{temp_skill_attr, temp_skill_attr}, | |||
{mount, mount}, | |||
{leader, leader}, | |||
{activity, activity}, | |||
{bubble_msg, bubble_msg}, | |||
{business_announce, business_announce}, | |||
{contact, contact}, | |||
{relation, relation}, | |||
{temp_drop_main, temp_drop_main}, | |||
{temp_drop_sub, temp_drop_sub}, | |||
{temp_task, tpl_task}, %任务模板 | |||
{temp_task_detail, temp_task_detail},%任务模板子表 | |||
{task_finish, task_finish}, %已完成任务 | |||
{task_process, task_process},%任务进度 | |||
{temp_stren, temp_stren}, | |||
{temp_compose, temp_compose}, | |||
{temp_polish, temp_polish}, | |||
{temp_upgrade, temp_upgrade}, | |||
{temp_all_stren_reward, temp_all_stren_reward}, | |||
{casting_polish, casting_polish}, | |||
{temp_polish_goods, temp_polish_goods}, | |||
{temp_suit_reward, temp_suit_reward}, | |||
{temp_all_gem_reward, temp_all_gem_reward}, | |||
{temp_gilding, temp_gilding}, | |||
{temp_gold_bag, temp_gold_bag}, | |||
{temp_level_bag, temp_level_bag}, | |||
{temp_vip_bag, temp_vip_bag}, | |||
{temp_god_tried, temp_god_tried}, | |||
{guild, guild}, | |||
{guild_member, guild_member}, | |||
{guild_apply, guild_apply}, | |||
{buy_npc_shop_log, buy_npc_shop_log}, | |||
{temp_npc_shop, temp_npc_shop}, | |||
{goods_cd, goods_cd}, | |||
{temp_meridian, tpl_meridian}, | |||
{temp_bones, tpl_bones}, | |||
{meridian, meridian}, | |||
{bones, bones}, | |||
{temp_shop, temp_shop}, | |||
{buy_shop_log, buy_shop_log}, | |||
{pet, pet}, | |||
{temp_mount_attr, temp_mount_attr}, | |||
{temp_mount_skill, temp_mount_skill}, | |||
{temp_label, temp_label}, | |||
{temp_activity, temp_activity}, | |||
{temp_activity_reward, temp_activity_reward}, | |||
{buff, buff}, | |||
{temp_cultivation, tpl_cultivation}, | |||
{cultivation, cultivation}, | |||
{temp_goods_buff, temp_goods_buff}, | |||
{temp_pet, temp_pet}, | |||
{temp_pet_quality, temp_pet_quality}, | |||
{temp_pet_growth, temp_pet_growth}, | |||
{temp_pet_aptitude, temp_pet_aptitude}, | |||
{temp_pet_medicine, temp_pet_medicine}, | |||
{temp_goods_facade, temp_goods_facade}, | |||
{temp_dungeon_group, temp_dungeon_group}, | |||
{temp_dungeon, temp_dungeon}, | |||
{temp_dungeon_trigger, temp_dungeon_trigger}, | |||
{temp_dungeon_obj, temp_dungeon_obj}, | |||
{dungeon_daily, dungeon_daily}, | |||
{dungeon_finish, dungeon_finish}, | |||
{dungeon_master, dungeon_master}, | |||
{temp_pet_skill_book, temp_pet_skill_book}, | |||
{temp_mon_ai, temp_mon_ai}, | |||
{ban_account_list, ban_account_list}, | |||
{ban_ip_list, ban_ip_list}, | |||
{sys_announce, sys_announce}, | |||
{temp_tips, temp_tips}, | |||
{task_master, task_master}, | |||
{heaven, heaven}, | |||
{task_heaven, task_heaven}, | |||
{temp_task_factor, temp_task_factor}, | |||
{task_daily, task_daily}, | |||
{temp_energy, temp_energy}, | |||
{opera, opera}, | |||
{scene_gift, scene_gift}, | |||
{temp_download_gift, temp_download_gift}, | |||
{temp_vip, temp_vip}, | |||
{temp_rand_shop, temp_rand_shop}, | |||
{temp_rand_shop_goods, temp_rand_shop_goods}, | |||
{rand_shop, rand_shop}, | |||
{market_selling, market_selling}, | |||
{market_request, market_request}, | |||
{temp_guild_level, temp_guild_level}, | |||
{temp_charge, temp_charge}, | |||
{temp_guild_contribution, temp_guild_contribution}, | |||
{temp_pet_skill_list, temp_pet_skill_list}, | |||
{temp_all_polish_reward, temp_all_polish_reward}, | |||
{temp_skill_point, temp_skill_point}, | |||
{temp_task_daily, tpl_task_daily}, | |||
{temp_cdkey_awards, temp_cdkey_awards} | |||
]). | |||
-record(erlydb_field, | |||
{name, name_str, name_bin, type, modifier, erl_type, | |||
html_input_type, | |||
null, key, | |||
default, extra, attributes}). | |||
%% | |||
%% Exported Functions | |||
%% | |||
-compile(export_all). | |||
%% | |||
%% API Functions | |||
%% | |||
start() -> | |||
case get_db_config(?CONFIG_FILE) of | |||
[Host, Port, User, Password, DB, Encode, _Conns] -> | |||
start_erlydb(Host, Port, User, Password, DB), | |||
mysql:start_link(?DB_SERVER, Host, Port, User, Password, DB, fun(_, _, _, _) -> ok end, Encode), | |||
mysql:connect(?DB_SERVER, Host, Port, User, Password, DB, Encode, true), | |||
tables_to_record(), | |||
%% get_date_box(), | |||
%% make_data_box:get_data_box(), | |||
ok; | |||
_ -> | |||
mysql_config_fail | |||
end, | |||
halt(), | |||
ok. | |||
get_db_config(Config_file) -> | |||
{ok, [L]} = file:consult(Config_file), | |||
{_, C} = lists:keyfind(gateway, 1, L), | |||
{_, Mysql_config} = lists:keyfind(mysql_config, 1, C), | |||
{_, 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]. | |||
%% | |||
%% Local Functions | |||
%% | |||
start_erlydb(IP, Port, User, Password, Db) -> | |||
erlydb:start(mysql, [{pool_id, erlydb_mysql}, | |||
{hostname, IP}, | |||
{port, Port}, | |||
{username, User}, | |||
{password, Password}, | |||
{database, Db}, | |||
{logfun, fun(_, _, _, _) -> ok end}, | |||
{encoding, utf8}, | |||
{pool_size, 10}]). | |||
%% @doc 生成指定的表名的beam文件 | |||
%% @spec code_gen/0 | |||
%% unilog_mysql_pool:code_gen() | |||
code_gen() -> | |||
code_gen(?TABLES). | |||
code_gen(TableName) -> | |||
TableList = writeTempFile(TableName), | |||
%% io:format("TableList=~p~n~n",[TableList]), | |||
erlydb:code_gen(TableList, {mysql, | |||
[{allow_unsafe_statements, true}, | |||
{skip_fk_checks, true}]}, | |||
[debug_info, {skip_fk_checks, true}, | |||
{outdir, "../ebin/"}]), | |||
clearTempFile(), | |||
ok. | |||
%% @doc 通过beam生成erl文件,方便开发查看模块方法 | |||
%% 调用该方法之前,必须先调用code_gen()方法,生成表对应的beam文件 | |||
%% @spec code_gen_src/0 | |||
code_gen_src() -> | |||
lists:foreach(fun(TableName) -> | |||
Beam = lists:concat([?BEAM_PATH, TableName, ".beam"]), | |||
case beam_lib:chunks(Beam, [abstract_code]) of | |||
{ok, {_, [{abstract_code, {_, AC}}]}} -> | |||
Code = erl_prettypr:format(erl_syntax:form_list(AC)), | |||
file:write_file(lists:concat([?SRC_TABLE_PATH, TableName, ".erl"]), list_to_binary(Code)), | |||
io:format("build beam:~p to erl:~p success.~n", [TableName, TableName]); | |||
{error, beam_lib, Reason} -> | |||
io:format("code_gen_erl_file error, reason:~p~n", [Reason]) | |||
end | |||
end, ?TABLES). | |||
%% @doc 为指定的表名生成module文件,给code_gen/0 使用 | |||
%% @spec writeTempFile/0 ->[TableFilePath] | |||
%% eg: TableFilePath -> "./tmptable/tuser_friend_log.erl" | |||
writeTempFile(TableName) -> | |||
clearTempFile(), | |||
ok = file:make_dir(?TMP_TABLE_PATH), | |||
lists:map(fun(F) -> | |||
Filename = | |||
?TMP_TABLE_PATH ++ atom_to_list(F) ++ ".erl", | |||
Bytes = list_to_binary(io_lib:format("-module(~w).", [F])), | |||
file:write_file(Filename, Bytes), | |||
Filename | |||
end, TableName). | |||
clearTempFile() -> | |||
case file:list_dir(?TMP_TABLE_PATH) of | |||
{ok, Filenames} -> | |||
lists:foreach(fun(F) -> | |||
file:delete(?TMP_TABLE_PATH ++ F) end, Filenames); | |||
{error, _} -> ignore | |||
end, | |||
file:del_dir(?TMP_TABLE_PATH). | |||
tables_to_record() -> | |||
io:format("starting table to record ...~n"), | |||
Bakfile = re:replace( | |||
lists:flatten(lists:concat([?RECORD_FILENAME, "_", time_format(now())])), | |||
"[ :]", "_", [global, {return, list}]), | |||
lists:flatten(lists:concat([?RECORD_FILENAME, "_", time_format(now())])), | |||
file:rename(?RECORD_FILENAME, Bakfile), | |||
file:write_file(?RECORD_FILENAME, ""), | |||
file:write_file(?RECORD_FILENAME, "%%%------------------------------------------------\t\n", [append]), | |||
file:write_file(?RECORD_FILENAME, "%%% File : table_to_record.erl\t\n", [append]), | |||
file:write_file(?RECORD_FILENAME, "%%% Author : smxx\t\n", [append]), | |||
Bytes = list_to_binary(io_lib:format("%%% Created : ~s\t\n", [time_format(now())])), | |||
file:write_file(?RECORD_FILENAME, Bytes, [append]), | |||
file:write_file(?RECORD_FILENAME, "%%% Description: 从mysql表生成的record\t\n", [append]), | |||
file:write_file(?RECORD_FILENAME, "%%% Warning: 由程序自动生成,请不要随意修改!\t\n", [append]), | |||
file:write_file(?RECORD_FILENAME, "%%%------------------------------------------------ \t\n", [append]), | |||
file:write_file(?RECORD_FILENAME, " \t\n", [append]), | |||
io:format("~n~n"), | |||
lists:foreach(fun(Table) -> | |||
case Table of | |||
{Table_name, Record_name} -> table_to_record(Table_name, Record_name, ""); | |||
{Table_name, Record_name, TableComment} -> table_to_record(Table_name, Record_name, TableComment); | |||
_ -> no_action | |||
end | |||
end, | |||
?TABLES), | |||
io:format("finished!~n~n"), | |||
ok. | |||
%% table_to_record:table_to_record(user, 1). | |||
%% [A,B]=db_esql:get_row("show create table user;") | |||
%% db_esql:get_row("select * from base_goods_type;") | |||
table_to_record(Table_name, Record_name, TableComment) -> | |||
file:write_file(?RECORD_FILENAME, "\t\n", [append]), | |||
Sql = lists:concat(["show create table ", Table_name]), | |||
try | |||
case db_esql:get_row(Sql) of | |||
{db_error, _} -> | |||
error; | |||
[_, A | _] -> | |||
Create_table_list = re:split(A, "[\n]", [{return, binary}]), | |||
Table_comment = | |||
case TableComment of | |||
"" -> get_table_comment(Create_table_list, Table_name); | |||
_ -> TableComment | |||
end, | |||
file:write_file(?RECORD_FILENAME, | |||
list_to_binary(io_lib:format("%% ~s\t\n", [Table_comment])), | |||
[append]), | |||
file:write_file(?RECORD_FILENAME, | |||
list_to_binary(io_lib:format("%% ~s ==> ~s \t\n", [Table_name, Record_name])), | |||
[append]), | |||
file:write_file(?RECORD_FILENAME, | |||
list_to_binary(io_lib:format("-record(~s, {\t\n", [Record_name])), | |||
[append]), | |||
code_gen([Table_name]), | |||
Table_fields = erlang:apply(Table_name, db_fields, []), | |||
lists:mapfoldl(fun(Field, Sum) -> | |||
Field_comment = get_field_comment(Create_table_list, Sum), | |||
Default = | |||
case Field#erlydb_field.default of | |||
undefined -> ''; | |||
<<>> -> | |||
case erlydb_field:get_erl_type(Field#erlydb_field.type) of | |||
binary -> | |||
lists:concat([" = \"\""]); | |||
integer -> | |||
lists:concat([" = 0"]); | |||
_ -> '' | |||
end; | |||
<<"[]">> -> | |||
lists:concat([" = ", binary_to_list(Field#erlydb_field.default)]); | |||
Val -> | |||
case erlydb_field:get_erl_type(Field#erlydb_field.type) of | |||
binary -> | |||
lists:concat([" = <<\"", binary_to_list(Val), "\">>"]); | |||
_ -> | |||
lists:concat([" = ", binary_to_list(Val)]) | |||
end | |||
end, | |||
T1 = | |||
if Sum == length(Table_fields) -> | |||
''; | |||
true -> ',' | |||
end, | |||
T2 = io_lib:format("~s~s~s", | |||
[Field#erlydb_field.name, | |||
Default, | |||
T1]), | |||
T3 = lists:duplicate(40 - length(lists:flatten(T2)), " "), | |||
Bytes = list_to_binary(io_lib:format(" ~s~s%% ~s\t\n", | |||
[T2, | |||
T3, | |||
Field_comment])), | |||
file:write_file(?RECORD_FILENAME, | |||
Bytes, | |||
[append]), | |||
{ | |||
[], | |||
Sum + 1 | |||
} | |||
end, | |||
1, Table_fields), | |||
file:write_file(?RECORD_FILENAME, | |||
list_to_binary(io_lib:format(" }).\t\n", [])), | |||
[append]), | |||
io:format(" ~s ==> ~s ~n", [Table_name, Record_name]), | |||
ok | |||
end | |||
catch | |||
_:_ -> | |||
io:format("error when getting ~s ==> ~s ~n", [Table_name, Record_name]), | |||
error | |||
end. | |||
get_field_comment(Create_table_list, Loc) -> | |||
try | |||
%% L1 = re:split(lists:nth(Loc+1, Create_table_list),"[ ]",[{return, list}]), | |||
L1 = binary_to_list(lists:nth(Loc + 1, Create_table_list)), | |||
%% io:format("L1 = ~p ~n", [L1]), | |||
Loc1 = string:rstr(L1, "COMMENT "), | |||
%% io:format("Loc = ~p ~n", [Loc1]), | |||
case Loc1 > 0 of | |||
true -> | |||
L2 = string:substr(L1, Loc1 + 8), | |||
L3 = lists:subtract(L2, [39, 44]), | |||
lists:subtract(L3, [39]); | |||
_ -> "" | |||
end | |||
catch | |||
_:_ -> "" | |||
end. | |||
get_table_comment(Create_table_list, Table_name) -> | |||
try | |||
%% L1 = re:split(lists:nth(Loc+1, Create_table_list),"[ ]",[{return, list}]), | |||
Len = length(Create_table_list), | |||
L1 = binary_to_list(lists:nth(Len, Create_table_list)), | |||
%% io:format("L1 = ~p ~n", [L1]), | |||
Loc1 = string:rstr(L1, "COMMENT="), | |||
%% io:format("Loc = ~p ~n", [Loc1]), | |||
case Loc1 > 0 of | |||
true -> | |||
L2 = string:substr(L1, Loc1 + 8), | |||
L3 = lists:subtract(L2, [39, 44]), | |||
lists:subtract(L3, [39]); | |||
_ -> Table_name | |||
end | |||
catch | |||
_:_ -> Table_name | |||
end. | |||
%% time format | |||
one_to_two(One) -> io_lib:format("~2..0B", [One]). | |||
%% @doc get the time's seconds for integer type | |||
%% @spec get_seconds(Time) -> integer() | |||
get_seconds(Time) -> | |||
{_MegaSecs, Secs, _MicroSecs} = Time, | |||
Secs. | |||
time_format(Now) -> | |||
{{Y, M, D}, {H, MM, S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D), " ", | |||
one_to_two(H), ":", one_to_two(MM), ":", one_to_two(S)]). | |||
date_format(Now) -> | |||
{{Y, M, D}, {_H, _MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D)]). | |||
date_hour_format(Now) -> | |||
{{Y, M, D}, {H, _MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D), " ", one_to_two(H)]). | |||
date_hour_minute_format(Now) -> | |||
{{Y, M, D}, {H, MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([Y, "-", one_to_two(M), "-", one_to_two(D), " ", one_to_two(H), "-", one_to_two(MM)]). | |||
%% split by - | |||
minute_second_format(Now) -> | |||
{{_Y, _M, _D}, {H, MM, _S}} = calendar:now_to_local_time(Now), | |||
lists:concat([one_to_two(H), "-", one_to_two(MM)]). | |||
hour_minute_second_format(Now) -> | |||
{{_Y, _M, _D}, {H, MM, S}} = calendar:now_to_local_time(Now), | |||
lists:concat([one_to_two(H), ":", one_to_two(MM), ":", one_to_two(S)]). | |||
%% ------------------------------------------------------------------------------------------------------ | |||
%% *********************************从mysql表生成的诛邪系统物品概率碰撞表 start **************************** | |||
%% ------------------------------------------------------------------------------------------------------ | |||
get_date_box() -> | |||
Careers = lists:seq(1, 5), | |||
lists:map(fun get_data_box_career/1, Careers). | |||
get_data_box_career(Career) -> | |||
%% DataFileName = lists:concat(["../src/data/data_box_", Career, ".erl"]), | |||
%% Bakfile = re:replace( | |||
%% lists:flatten(lists:concat([DataFileName , "_", time_format(now())])), | |||
%% "[ :]","_",[global,{return,list}]), | |||
%% | |||
%% file:rename(DataFileName, Bakfile), | |||
%% | |||
%% file:write_file(DataFileName, ""), | |||
%% file:write_file(DataFileName, "%%%------------------------------------------------\t\n",[append]), | |||
%% file:write_file(DataFileName, "%%% File : data_box_X.erl\t\n",[append]), | |||
%% file:write_file(DataFileName, "%%% Author : xiaomai\t\n",[append]), | |||
%% Bytes = list_to_binary(io_lib:format("%%% Created : ~s\t\n", [time_format(now())])), | |||
%% file:write_file(DataFileName, Bytes,[append]), | |||
%% file:write_file(DataFileName, "%%% Description: 从mysql表生成的诛邪系统物品概率碰撞表\t\n",[append]), | |||
%% file:write_file(DataFileName, "%%% Warning: 由程序自动生成,请不要随意修改!\t\n",[append]), | |||
%% file:write_file(DataFileName, "%%%------------------------------------------------ \t\n",[append]), | |||
%% file:write_file(DataFileName, " \t\n",[append]), | |||
%% ModuleName = lists:concat(["-module(data_box_",Career,")."]), | |||
%% file:write_file(DataFileName, ModuleName,[append]), | |||
%% file:write_file(DataFileName, " \t\n\n",[append]), | |||
%% file:write_file(DataFileName, "-export([get_goods_one/3]).",[append]), | |||
%% file:write_file(DataFileName, " \t\n",[append]), | |||
%% file:write_file(DataFileName, " \t\n",[append]), | |||
%% file:write_file(DataFileName, "get_goods_one(HoleType, Career, RandomCount) ->\n\t",[append]), | |||
Counts = lists:seq(1, 3), | |||
lists:map(fun(Elem) -> handle_data_box_each(Career, Elem) end, Counts), | |||
%% ErCodeend = "GoodsInfo = lists:concat([\"Goods_info_\", HoleType, Elem, \"00\"]), | |||
%% %%注意这里的返回值 | |||
%% {BaseGoodsId} = lists:nth(RandomCount, GoodsInfo), | |||
%% BaseGoodsId.", | |||
%% file:write_file(DataFileName, ErCodeend,[append]), | |||
io:format("~n~n"). | |||
%% handle_data_box_each(Elem, Career) -> | |||
%% Sum = lists:seq(1,5), | |||
%% lists:foldl(fun handle_data_box_each_one/2,{Elem}, Sum). | |||
handle_data_box_each(Career, Elem) -> | |||
DataFileName = lists:concat(["../src/data/data_box_", Career, Elem, ".erl"]), | |||
Bakfile = re:replace( | |||
lists:flatten(lists:concat([DataFileName, "_", time_format(now())])), | |||
"[ :]", "_", [global, {return, list}]), | |||
file:rename(DataFileName, Bakfile), | |||
file:write_file(DataFileName, ""), | |||
file:write_file(DataFileName, "%%%------------------------------------------------\t\n", [append]), | |||
file:write_file(DataFileName, "%%% File : data_box_XX.erl\t\n", [append]), | |||
file:write_file(DataFileName, "%%% Author : xiaomai\t\n", [append]), | |||
Bytes = list_to_binary(io_lib:format("%%% Created : ~s\t\n", [time_format(now())])), | |||
file:write_file(DataFileName, Bytes, [append]), | |||
file:write_file(DataFileName, "%%% Description: 从mysql表生成的诛邪系统物品概率碰撞表\t\n", [append]), | |||
file:write_file(DataFileName, "%%% Warning: 由程序自动生成,请不要随意修改!\t\n", [append]), | |||
file:write_file(DataFileName, "%%%------------------------------------------------ \t\n", [append]), | |||
file:write_file(DataFileName, " \t\n", [append]), | |||
ModuleName = lists:concat(["-module(data_box_", Career, Elem, ")."]), | |||
file:write_file(DataFileName, ModuleName, [append]), | |||
file:write_file(DataFileName, " \t\n\n", [append]), | |||
file:write_file(DataFileName, "-export([get_goods_one/3]).", [append]), | |||
file:write_file(DataFileName, " \t\n", [append]), | |||
file:write_file(DataFileName, " \t\n", [append]), | |||
file:write_file(DataFileName, "get_goods_one(HoleType, Career, RandomCount) ->\n\t", [append]), | |||
Sql = | |||
io_lib:format("select a.pro, a.goods_id from `base_box_goods` a, `base_goods` b where hole_type = ~p and b.goods_id = a.goods_id and b.career in (0,~p) order by a.goods_id desc", | |||
[Elem, Career]), | |||
Lists = db_mysqlutil:get_all(Sql), | |||
ElemName = lists:concat(["Goods_info_", Career, Elem, "00 = ["]), | |||
file:write_file(DataFileName, ElemName, [append]), | |||
{_NewCount, _FileName} = lists:foldl(fun make_content_goods/2, {1, DataFileName}, Lists), | |||
%% io:format("the [~p]count is[~p]\n\n\n", [Career, NewCount]), | |||
%% String = lists:concat(Result), | |||
%% file:write_file(?FILENAME, string:substr(String,1, string:len(String)),[append]), | |||
file:write_file(DataFileName, "],\n\t", [append]), | |||
ErCodeEndOne = " | |||
%%注意这里的返回值 | |||
{BaseGoodsId} = lists:nth(RandomCount, ", | |||
ErCodeEndTwo = "), | |||
BaseGoodsId.", | |||
EndString = lists:concat([ErCodeEndOne, "Goods_info_", Career, Elem, "00", ErCodeEndTwo]), | |||
file:write_file(DataFileName, EndString, [append]). | |||
make_content_goods(List, AccIn) -> | |||
[Pro, GoodsId] = List, | |||
{Count, DataFileName} = AccIn, | |||
NewPro = Pro * 100000, | |||
NewProInt = tool:to_integer(NewPro), | |||
Sum = lists:seq(1, NewProInt), | |||
{NewCount, _GodosId, Result} = lists:foldl(fun get_content_array/2, {Count, GoodsId, []}, Sum), | |||
String = lists:concat(lists:reverse(Result)), | |||
file:write_file(DataFileName, String, [append]), | |||
%% file:write_file(DataFileName, "\n\t\t\t\t\t\t",[append]), | |||
%% io:format("the elem is {~p,~p,~p,~p}\t\t", [Pro,NewProInt,NewCount,length(Result)]), | |||
{NewCount, DataFileName}. | |||
get_content_array(_Elem, AccIn) -> | |||
{Count, GoodsId, ResultList} = AccIn, | |||
case tool:to_integer(Count) =:= 100000 of | |||
true -> | |||
ResultElem = lists:concat(["{", GoodsId, "}"]); | |||
false -> | |||
ResultElem = lists:concat(["{", GoodsId, "},"]) | |||
end, | |||
{Count + 1, GoodsId, [ResultElem | ResultList]}. | |||
%% ------------------------------------------------------------------------------------------------------ | |||
%% *********************************从mysql表生成的诛邪系统物品概率碰撞表 end **************************** | |||
%% ------------------------------------------------------------------------------------------------------ |
@ -1,863 +0,0 @@ | |||
-module(union_to_emongo). | |||
-compile([export_all]). | |||
-include("common111.hrl"). | |||
%%添加服号 | |||
-define(SN, config:get_server_number(gateway)). | |||
%%添加最大id数字 | |||
-define(Max_id, config:get_max_id(gateway)). | |||
%%添加服号数据表集合 | |||
-define(SN_List, [user, player, infant_ctrl_byuser]). | |||
%%删除数据等级限制 | |||
-define(DelLevel, 10). | |||
%%每次查询或更新记录条数 | |||
-define(PageSize, 100). | |||
%% monogo数据库连接初始化 | |||
init_mongo(App) -> | |||
try | |||
[PoolId, Host, Port, DB, EmongoSize] = config:get_mongo_config(App), | |||
emongo_sup:start_link(), | |||
emongo_app:initialize_pools([PoolId, Host, Port, DB, EmongoSize]), | |||
misc:write_system_info({self(), mongo}, mongo, {PoolId, Host, Port, DB, EmongoSize}), | |||
{ok, master_mongo} | |||
catch | |||
_:_ -> mongo_config_error | |||
end. | |||
%% monogo数据库连接初始化 | |||
init_slave_mongo(App) -> | |||
try | |||
[PoolId, Host, Port, DB, EmongoSize] = config:get_slave_mongo_config(App), | |||
emongo_sup:start_link(), | |||
emongo_app:initialize_pools([PoolId, Host, Port, DB, EmongoSize]), | |||
misc:write_system_info({self(), mongo_slave}, mongo_slave, {PoolId, Host, Port, DB, EmongoSize}), | |||
{ok, slave_mongo} | |||
catch | |||
_:_ -> slave_config_error %%没有配置从数据库 | |||
end. | |||
%% 启动合并程序 | |||
%%操作顺序 :1.部分表加列sn 2.删除角色数据(可选) 3.更新名字=服号+nickname 4.更新所有id,保证id唯一 5.批处理导入数据 6.最后更新audo_ids的对应的id 7.合服后根据条件删除数据 | |||
%%在player,user,infant_ctrl_byuser表中添加服号 | |||
start(1) -> | |||
io:format("?SN is ~p~n", [?SN]), | |||
case ?SN > 0 of | |||
false -> | |||
skip; | |||
true -> | |||
F = fun(Table_name) -> | |||
io:format("db.~p.update start...~n", [Table_name]), | |||
db_mongo:update(tool:to_list(Table_name), [{sn, ?SN}], []), | |||
io:format("db.~p.update ok...~n", [Table_name]) | |||
end, | |||
lists:foreach(F, ?SN_List) | |||
end, | |||
io:format("add server number finished!"); | |||
%%删除等级之下的所有角色 | |||
start(2) -> | |||
IdList = lists:flatten(db_mongo:select_all("player", "id", [{lv, "<=", ?DelLevel}])), | |||
case IdList of | |||
[] -> | |||
io:format("no data!"), | |||
skip; | |||
_ -> | |||
TableList = lib_player_rw:get_all_tables(), | |||
F = fun(Tablename) -> | |||
case Tablename of | |||
arena -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
arena_week -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
box_scene -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
cards -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
carry -> db_mongo:delete(Tablename, [{pid, "in", IdList}]); | |||
consign_player -> db_mongo:delete(Tablename, [{pid, "in", IdList}]); | |||
consign_task -> db_mongo:delete(Tablename, [{pid, "in", IdList}]); | |||
daily_bless -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
exc -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
feedback -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
fst_god -> db_mongo:delete(Tablename, [{uid, "in", IdList}]); | |||
goods -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
goods_attribute -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
goods_buff -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
goods_cd -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
%%帮派不能删除 | |||
%%guild -> db_mongo:delete(Tablename, [{player_id,"in",IdList}]); | |||
guild_apply -> db_mongo:delete(Tablename, [{uid, "in", IdList}]); | |||
guild_invite -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
guild_manor_cd -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
guild_member -> db_mongo:delete(Tablename, [{uid, "in", IdList}]); | |||
log_backout -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_box_open -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_box_player -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_box_throw -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_compose -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_consume -> db_mongo:delete(Tablename, [{pid, "in", IdList}]); | |||
log_dungeon -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_employ -> db_mongo:delete(Tablename, [{pid, "in", IdList}]); | |||
log_exc -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_exc_exp -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_free_pet -> db_mongo:delete(Tablename, [{uid, "in", IdList}]); | |||
log_fst -> db_mongo:delete(Tablename, [{uid, "in", IdList}]); | |||
log_fst_mail -> db_mongo:delete(Tablename, [{uid, "in", IdList}]); | |||
log_hole -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_icompose -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_idecompose -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_identify -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_inlay -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_linggen -> db_mongo:delete(Tablename, [{pid, "in", IdList}]); | |||
log_mail -> db_mongo:delete(Tablename, [{uid, "in", IdList}]); | |||
log_merge -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_meridian -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_pay -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_practise -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_quality_out -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_quality_up -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_refine -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_sale -> db_mongo:delete(Tablename, [{buyer_id, "in", IdList}]), | |||
db_mongo:delete(Tablename, [{sale_id, "in", IdList}]); | |||
log_sale_dir -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_shop -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_stren -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_suitmerge -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_throw -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_trade -> db_mongo:delete(Tablename, [{donor_id, "in", IdList}]), | |||
db_mongo:delete(Tablename, [{gainer_id, "in", IdList}]); | |||
log_uplevel -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_use -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_warehouse_flowdir -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
log_wash -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
mail -> db_mongo:delete(Tablename, [{uid, "in", IdList}]); | |||
master_apprentice -> db_mongo:delete(Tablename, [{apprentenice_id, "in", IdList}]), | |||
db_mongo:delete(Tablename, [{master_id, "in", IdList}]); | |||
master_charts -> db_mongo:delete(Tablename, [{master_id, "in", IdList}]); | |||
meridian -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
mon_drop_analytics -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
offline_award -> db_mongo:delete(Tablename, [{pid, "in", IdList}]); | |||
online_award -> db_mongo:delete(Tablename, [{pid, "in", IdList}]); | |||
online_gift -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
pet -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
player -> db_mongo:delete(Tablename, [{id, "in", IdList}]); | |||
player_buff -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
player_donttalk -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
player_hook_setting -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
player_sys_setting -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
relationship -> db_mongo:delete(Tablename, [{idA, "in", IdList}]), | |||
db_mongo:delete(Tablename, [{idB, "in", IdList}]); | |||
sale_goods -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
skill -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
target_gift -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
task_bag -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
task_consign -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
task_log -> db_mongo:delete(Tablename, [{player_id, "in", IdList}]); | |||
_ -> skip | |||
end | |||
end, | |||
[F(Tablename) || Tablename <- TableList], | |||
io:format("delete data finished!") | |||
end; | |||
%%更新角色名和帮派名,分别加上服号 | |||
start(3) -> | |||
TableList = lib_player_rw:get_all_tables(), | |||
F = fun(Tablename) -> | |||
case Tablename of | |||
arena -> update_name(Tablename, nickname, id); | |||
arena_week -> update_name(Tablename, nickname, id); | |||
feedback -> update_name(Tablename, player_name, id); | |||
fst_god -> update_name(Tablename, g_name, id), update_name(Tablename, nick, id); | |||
guild -> update_name(Tablename, name, id), update_name(Tablename, chief_name, id), | |||
update_name(Tablename, deputy_chief1_name, id), update_name(Tablename, deputy_chief2_name, id); | |||
guild_invite -> update_name(Tablename, recommander_name, id); | |||
guild_member -> update_name(Tablename, guild_name, id), update_name(Tablename, player_name, id); | |||
log_backout -> update_name(Tablename, nickname, id); | |||
log_box_open -> update_name(Tablename, player_name, id); | |||
log_compose -> update_name(Tablename, nickname, id); | |||
log_guild -> update_name(Tablename, guild_name, id); | |||
log_hole -> update_name(Tablename, nickname, id); | |||
log_icompose -> update_name(Tablename, nickname, id); | |||
log_idecompose -> update_name(Tablename, nickname, id); | |||
log_identify -> update_name(Tablename, nickname, id); | |||
log_inlay -> update_name(Tablename, nickname, id); | |||
log_mail -> update_name(Tablename, sname, id); | |||
log_merge -> update_name(Tablename, nickname, id); | |||
log_pay -> update_name(Tablename, nickname, id); | |||
log_practise -> update_name(Tablename, nickname, id); | |||
log_quality_out -> update_name(Tablename, nickname, id); | |||
log_quality_up -> update_name(Tablename, nickname, id); | |||
log_refine -> update_name(Tablename, nickname, id); | |||
log_sale -> update_name(Tablename, buyer_name, id), update_name(Tablename, saler_name, id); | |||
log_shop -> update_name(Tablename, nickname, id); | |||
log_stren -> update_name(Tablename, nickname, id); | |||
log_suitmerge -> update_name(Tablename, nickname, id); | |||
log_throw -> update_name(Tablename, nickname, id); | |||
log_trade -> update_name(Tablename, donor_name, id), update_name(Tablename, gainer_name, id); | |||
log_use -> update_name(Tablename, nickname, id); | |||
log_wash -> update_name(Tablename, nickname, id); | |||
mail -> update_name(Tablename, sname, id); | |||
master_apprentice -> update_name(Tablename, apprentenice_name, id); | |||
master_charts -> update_name(Tablename, master_name, id); | |||
mon_drop_analytics -> update_name(Tablename, player_name, id); | |||
player -> update_name(Tablename, nickname, id), update_name(Tablename, guild_name, id); | |||
sale_goods -> update_name(Tablename, player_name, id); | |||
_ -> skip | |||
end | |||
end, | |||
[F(Tablename) || Tablename <- TableList], | |||
io:format("change name finished!"); | |||
%%更新表所有id,保证id唯一 | |||
start(4) -> | |||
%%先查出另服的ID最大值 | |||
%%查询有记录的表及最大主键 | |||
L = search_another_max_id(), | |||
TableList = lib_player_rw:get_all_tables(), | |||
F1 = fun(Tablename) -> | |||
case Tablename of | |||
arena -> update_id(Tablename, [{arena, id}, {player, player_id}], 0, L); | |||
arena_week -> update_id(Tablename, [{arena_week, id}, {player, player_id}], 0, L); | |||
box_scene -> update_id(Tablename, [{box_scene, id}, {player, player_id}], 0, L); | |||
cards -> update_id(Tablename, [{cards, id}, {player, player_id}], 0, L); | |||
carry -> update_id(Tablename, [{carry, id}, {player, pid}], 0, L); | |||
consign_player -> update_id(Tablename, [{consign_player, id}, {player, pid}], 0, L); | |||
consign_task -> update_id(Tablename, [{consign_task, id}, {player, pid}], 0, L); | |||
daily_bless -> update_id(Tablename, [{daily_bless, id}, {player, player_id}], 0, L); | |||
exc -> update_id(Tablename, [{exc, id}, {player, player_id}], 0, L); | |||
feedback -> update_id(Tablename, [{feedback, id}, {player, player_id}], 0, L); | |||
fst_god -> update_id(Tablename, [{fst_god, id}, {player, uid}], 0, L); | |||
goods -> update_id(Tablename, [{goods, id}, {player, player_id}], 0, L); | |||
goods_attribute -> update_id(Tablename, [{goods_attribute, id}, {player, player_id}, {goods, gid}], 0, L); | |||
goods_buff -> update_id(Tablename, [{goods_buff, id}, {player, player_id}], 0, L); | |||
goods_cd -> update_id(Tablename, [{goods_cd, id}, {player, player_id}], 0, L); | |||
guild -> | |||
update_id(Tablename, [{guild, id}, {player, chief_id}, {player, deputy_chief1_id}, {player, deputy_chief2_id}], 1, L); | |||
guild_apply -> update_id(Tablename, [{guild_apply, id}, {guild, guild_id}, {player, player_id}], 0, L); | |||
guild_invite -> | |||
update_id(Tablename, [{guild_invite, id}, {guild, guild_id}, {player, player_id}, {player, recommander_id}], 1, L); | |||
guild_manor_cd -> update_id(Tablename, [{guild_manor_cd, id}, {player, player_id}], 0, L); | |||
guild_member -> update_id(Tablename, [{guild_member, id}, {guild, guild_id}, {player, player_id}], 0, L); | |||
guild_skills_attribute -> update_id(Tablename, [{guild_skills_attribute, id}, {guild, guild_id}], 0, L); | |||
infant_ctrl_byuser -> update_id(Tablename, [{infant_ctrl_byuser, id}], 0, L); | |||
log_backout -> update_id(Tablename, [{log_backout, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_box_open -> update_id(Tablename, [{log_box_open, id}, {player, player_id}, {goods, gid}], 1, L); | |||
log_box_player -> update_id(Tablename, [{log_box_player, id}, {player, player_id}], 0, L); | |||
log_box_throw -> update_id(Tablename, [{log_box_throw, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_compose -> update_id(Tablename, [{log_compose, id}, {player, player_id}], 0, L); | |||
log_consume -> update_id(Tablename, [{log_consume, id}, {player, pid}], 0, L); | |||
log_dungeon -> update_id(Tablename, [{log_dungeon, id}, {player, player_id}], 0, L); | |||
log_employ -> update_id(Tablename, [{log_employ, id}, {player, pid}], 0, L); | |||
log_exc -> update_id(Tablename, [{log_exc, id}, {player, player_id}], 0, L); | |||
log_exc_exp -> update_id(Tablename, [{log_exc_exp, id}, {player, player_id}], 0, L); | |||
log_free_pet -> update_id(Tablename, [{log_free_pet, id}, {player, uid}], 0, L); | |||
log_fst -> update_id(Tablename, [{log_fst, id}, {player, uid}], 0, L); | |||
log_fst_mail -> update_id(Tablename, [{log_fst_mail, id}, {player, uid}], 0, L); | |||
log_guild -> update_id(Tablename, [{log_guild, id}, {guild, guild_id}], 0, L); | |||
log_hole -> update_id(Tablename, [{log_hole, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_icompose -> update_id(Tablename, [{log_icompose, id}, {player, player_id}], 0, L); | |||
log_idecompose -> update_id(Tablename, [{log_idecompose, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_identify -> update_id(Tablename, [{log_identify, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_inlay -> update_id(Tablename, [{log_inlay, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_linggen -> update_id(Tablename, [{log_linggen, id}, {player, pid}], 0, L); | |||
log_mail -> update_id(Tablename, [{log_mail, id}, {player, uid}, {goods, gid}], 1, L); | |||
log_merge -> | |||
update_id(Tablename, [{log_merge, id}, {player, player_id}, {goods, gid_1}, {goods, gid_2}], 0, L); | |||
log_meridian -> update_id(Tablename, [{log_meridian, id}, {player, player_id}], 0, L); | |||
log_pay -> update_id(Tablename, [{log_pay, id}, {player, player_id}], 0, L); | |||
log_practise -> update_id(Tablename, [{log_practise, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_quality_out -> update_id(Tablename, [{log_quality_out, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_quality_up -> update_id(Tablename, [{log_quality_up, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_refine -> update_id(Tablename, [{log_refine, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_sale -> | |||
update_id(Tablename, [{log_sale, id}, {sale_goods, sale_id}, {player, player_id}, {goods, gid}], 1, L); | |||
log_sale_dir -> | |||
update_id(Tablename, [{log_sale_dir, id}, {sale_goods, sale_id}, {player, player_id}, {goods, gid}], 1, L); | |||
log_shop -> update_id(Tablename, [{log_shop, id}, {player, player_id}], 0, L); | |||
log_stren -> update_id(Tablename, [{log_stren, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_suitmerge -> | |||
update_id(Tablename, [{log_suitmerge, id}, {player, player_id}, {goods, gid1}, {goods, gid2}, {goods, gid3}], 0, L); | |||
log_throw -> update_id(Tablename, [{log_throw, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_trade -> | |||
update_id(Tablename, [{log_trade, id}, {player, donor_id}, {player, gainer_id}, {goods, gid}], 1, L); | |||
log_uplevel -> update_id(Tablename, [{log_uplevel, id}, {player, player_id}], 0, L); | |||
log_use -> update_id(Tablename, [{log_use, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_warehouse_flowdir -> | |||
update_id(Tablename, [{log_warehouse_flowdir, id}, {player, player_id}, {goods, gid}], 0, L); | |||
log_wash -> update_id(Tablename, [{log_wash, id}, {player, player_id}, {goods, gid}], 0, L); | |||
login_prize -> update_id(Tablename, [{login_prize, id}], 0, L); | |||
mail -> update_id(Tablename, [{mail, id}, {player, uid}, {goods, gid}], 1, L); | |||
master_apprentice -> | |||
update_id(Tablename, [{master_apprentice, id}, {player, apprentenice_id}, {player, master_id}], 1, L); | |||
master_charts -> update_id(Tablename, [{master_charts, id}, {player, master_id}], 0, L); | |||
meridian -> update_id(Tablename, [{meridian, id}, {player, player_id}], 0, L); | |||
mon_drop_analytics -> update_id(Tablename, [{mon_drop_analytics, id}, {player, player_id}], 0, L); | |||
offline_award -> update_id(Tablename, [{offline_award, id}, {player, pid}], 0, L); | |||
online_award -> update_id(Tablename, [{online_award, id}, {player, pid}], 0, L); | |||
online_gift -> update_id(Tablename, [{online_gift, id}, {player, player_id}], 0, L); | |||
pet -> update_id(Tablename, [{pet, id}, {player, player_id}], 0, L); | |||
player -> update_id(Tablename, [{player, id}, {guild, guild_id}], 1, L); | |||
player_buff -> update_id(Tablename, [{player_buff, id}, {player, player_id}], 0, L); | |||
player_donttalk -> update_id(Tablename, [{player, player_id}], 0, L); | |||
player_hook_setting -> update_id(Tablename, [{player_hook_setting, id}, {player, player_id}], 0, L); | |||
player_sys_setting -> update_id(Tablename, [{player_sys_setting, id}, {player, player_id}], 0, L); | |||
relationship -> update_id(Tablename, [{relationship, id}, {player, idA}, {player, idB}], 0, L); | |||
sale_goods -> update_id(Tablename, [{sale_goods, id}, {goods, gid}, {player, player_id}], 0, L); | |||
skill -> update_id(Tablename, [{skill, id}, {player, player_id}], 0, L); | |||
target_gift -> update_id(Tablename, [{target_gift, id}, {player, player_id}], 0, L); | |||
task_bag -> update_id(Tablename, [{task_bag, id}, {player, player_id}], 0, L); | |||
task_consign -> update_id(Tablename, [{task_consign, id}, {player, player_id}], 0, L); | |||
task_log -> update_id(Tablename, [{task_log, id}, {player, player_id}], 0, L); | |||
user -> update_id(Tablename, [{user, id}], 0, L); | |||
_ -> skip | |||
end | |||
end, | |||
[F1(Tablename) || Tablename <- TableList], | |||
io:format("change id finished!"); | |||
%%批处理导入数据 | |||
start(5) -> | |||
Master_mongo1 = | |||
case init_mongo(gateway) of | |||
{ok, Master_mongo} -> Master_mongo; | |||
_ -> [] | |||
end, | |||
Slave_mongo1 = | |||
case init_slave_mongo(gateway) of | |||
{ok, Slave_mongo} -> Slave_mongo; | |||
_ -> [] | |||
end, | |||
if (Master_mongo1 =/= [] andalso Slave_mongo1 =/= []) -> | |||
%% TableList = lib_player_rw:get_all_tables(), | |||
TableList = | |||
[ | |||
arena, | |||
arena_week, | |||
box_scene, | |||
cards, | |||
carry, | |||
consign_player, | |||
consign_task, | |||
daily_bless, | |||
exc, | |||
feedback, | |||
fst_god, | |||
goods, | |||
goods_attribute, | |||
goods_buff, | |||
goods_cd, | |||
guild, | |||
guild_apply, | |||
guild_invite, | |||
guild_manor_cd, | |||
guild_member, | |||
guild_skills_attribute, | |||
infant_ctrl_byuser, | |||
log_backout, | |||
log_box_open, | |||
log_box_player, | |||
log_box_throw, | |||
log_compose, | |||
log_consume, | |||
log_dungeon, | |||
log_employ, | |||
log_exc, | |||
log_exc_exp, | |||
log_free_pet, | |||
log_fst, | |||
log_fst_mail, | |||
log_guild, | |||
log_hole, | |||
log_icompose, | |||
log_idecompose, | |||
log_identify, | |||
log_inlay, | |||
log_linggen, | |||
log_mail, | |||
log_merge, | |||
log_meridian, | |||
log_pay, | |||
log_practise, | |||
log_quality_out, | |||
log_quality_up, | |||
log_refine, | |||
log_sale, | |||
log_sale_dir, | |||
log_shop, | |||
log_stren, | |||
log_suitmerge, | |||
log_throw, | |||
log_trade, | |||
log_uplevel, | |||
log_use, | |||
log_warehouse_flowdir, | |||
log_wash, | |||
login_prize, | |||
mail, | |||
master_apprentice, | |||
master_charts, | |||
meridian, | |||
mon_drop_analytics, | |||
offline_award, | |||
online_award, | |||
online_gift, | |||
pet, | |||
player, | |||
player_buff, | |||
player_donttalk, | |||
player_hook_setting, | |||
player_sys_setting, | |||
relationship, | |||
sale_goods, | |||
skill, | |||
target_gift, | |||
task_bag, | |||
task_consign, | |||
task_log, | |||
user | |||
], | |||
F = fun(Tablename) -> | |||
ResultList = emongo:find_all(tool:to_list(Slave_mongo1), tool:to_list(Tablename), [], []), | |||
F = fun(R) -> | |||
R1 = [({Key, Value}) || {Key, Value} <- R, Key =/= <<"_id">>], | |||
Opertion = db_mongoutil:make_insert_opertion(R1), | |||
emongo:insert(tool:to_list(Master_mongo1), tool:to_list(Tablename), Opertion) | |||
end, | |||
io:format("Tablename is ~p union data finish ~n", [Tablename]), | |||
[F(R) || R <- ResultList] | |||
end, | |||
[F(Tablename) || Tablename <- lists:reverse(TableList)], | |||
io:format("mongo and slave config ok"); | |||
true -> | |||
io:format("mongo and slave config error") | |||
end; | |||
%%最后更新audo_ids的对应的id | |||
start(6) -> | |||
update_ids(), | |||
io:format("change auto_ids finished!"); | |||
%%合服后根据条件删除数据 | |||
start(7) -> | |||
ok. | |||
update_name(Tablename, Field, WhereField) -> | |||
io:format("start update_name ~p~n ", [Tablename]), | |||
[Size] = db_mongo:select_count(Tablename, []), | |||
TotalPage = | |||
if (Size div ?PageSize == 0) -> | |||
Size div ?PageSize; | |||
true -> | |||
Size div ?PageSize + 1 | |||
end, | |||
io:format("Size is ~p~n", [Size]), | |||
if (TotalPage =< 1) -> | |||
NameList = db_mongo:select_all(Tablename, tool:to_list(WhereField) ++ "," ++ tool:to_list(Field)), | |||
F = fun(Name) -> | |||
Name1 = tool:to_list(lists:nth(2, Name)), | |||
case length(Name1) > 0 andalso Name1 =/= "[]" of | |||
false -> skip; | |||
true -> | |||
Id1 = lists:nth(1, Name), | |||
NewName = "【" ++ integer_to_list(?SN) ++ "】" ++ Name1, | |||
db_mongo:update(Tablename, [{Field, NewName}], [{WhereField, Id1}]) | |||
end | |||
end, | |||
io:format("end update_name ~p ", [Tablename]), | |||
[F(Name) || Name <- NameList]; | |||
true -> | |||
F = fun(Page) -> | |||
io:format("Page is ~p~n", [Page]), | |||
Result = db_mongo:select_all(Tablename, tool:to_list(WhereField) ++ "," ++ tool:to_list(Field), [], [{tool:to_list(WhereField), asc}, {tool:to_list(Field), asc}], [?PageSize, (Page - 1) * ?PageSize]), | |||
io:format("Result size is ~p~n", [length(Result)]), | |||
F = fun(Name) -> | |||
Name1 = tool:to_list(lists:nth(2, Name)), | |||
case length(Name1) > 0 andalso Name1 =/= "[]" of | |||
false -> skip; | |||
true -> | |||
Id1 = lists:nth(1, Name), | |||
NewName = "【" ++ integer_to_list(?SN) ++ "】" ++ Name1, | |||
db_mongo:update(Tablename, [{Field, NewName}], [{WhereField, Id1}]) | |||
end | |||
end, | |||
io:format("end update_name ~p ", [Tablename]), | |||
[F(Name) || Name <- Result] | |||
end, | |||
lists:foreach(F, lists:seq(1, TotalPage)) | |||
end. | |||
search_another_max_id() -> | |||
%%先查出另服的ID最大值 | |||
TableList = lib_player_rw:get_all_tables(), | |||
F = fun(Tablename) -> | |||
case Tablename of | |||
arena -> search_id(Tablename, [id]); | |||
arena_week -> search_id(Tablename, [id]); | |||
box_scene -> search_id(Tablename, [id]); | |||
cards -> search_id(Tablename, [id]); | |||
carry -> search_id(Tablename, [id]); | |||
consign_player -> search_id(Tablename, [id]); | |||
consign_task -> search_id(Tablename, [id]); | |||
daily_bless -> search_id(Tablename, [id]); | |||
exc -> search_id(Tablename, [id]); | |||
feedback -> search_id(Tablename, [id]); | |||
fst_god -> search_id(Tablename, [id]); | |||
goods -> search_id(Tablename, [id]); | |||
goods_attribute -> search_id(Tablename, [id]); | |||
goods_buff -> search_id(Tablename, [id]); | |||
goods_cd -> search_id(Tablename, [id]); | |||
guild -> search_id(Tablename, [id]); | |||
guild_apply -> search_id(Tablename, [id]); | |||
guild_invite -> search_id(Tablename, [id]); | |||
guild_manor_cd -> search_id(Tablename, [id]); | |||
guild_member -> search_id(Tablename, [id]); | |||
guild_skills_attribute -> search_id(Tablename, [id]); | |||
infant_ctrl_byuser -> search_id(Tablename, [id]); | |||
log_backout -> search_id(Tablename, [id]); | |||
log_box_open -> search_id(Tablename, [id]); | |||
log_box_player -> search_id(Tablename, [id]); | |||
log_box_throw -> search_id(Tablename, [id]); | |||
log_compose -> search_id(Tablename, [id]); | |||
log_consume -> search_id(Tablename, [id]); | |||
log_dungeon -> search_id(Tablename, [id]); | |||
log_employ -> search_id(Tablename, [id]); | |||
log_exc -> search_id(Tablename, [id]); | |||
log_exc_exp -> search_id(Tablename, [id]); | |||
log_free_pet -> search_id(Tablename, [id]); | |||
log_fst -> search_id(Tablename, [id]); | |||
log_fst_mail -> search_id(Tablename, [id]); | |||
log_guild -> search_id(Tablename, [id]); | |||
log_hole -> search_id(Tablename, [id]); | |||
log_icompose -> search_id(Tablename, [id]); | |||
log_idecompose -> search_id(Tablename, [id]); | |||
log_identify -> search_id(Tablename, [id]); | |||
log_inlay -> search_id(Tablename, [id]); | |||
log_linggen -> search_id(Tablename, [id]); | |||
log_mail -> search_id(Tablename, [id]); | |||
log_merge -> search_id(Tablename, [id]); | |||
log_meridian -> search_id(Tablename, [id]); | |||
log_pay -> search_id(Tablename, [id]); | |||
log_practise -> search_id(Tablename, [id]); | |||
log_quality_out -> search_id(Tablename, [id]); | |||
log_quality_up -> search_id(Tablename, [id]); | |||
log_refine -> search_id(Tablename, [id]); | |||
log_sale -> search_id(Tablename, [id]); | |||
log_sale_dir -> search_id(Tablename, [id]); | |||
log_shop -> search_id(Tablename, [id]); | |||
log_stren -> search_id(Tablename, [id]); | |||
log_suitmerge -> search_id(Tablename, [id]); | |||
log_throw -> search_id(Tablename, [id]); | |||
log_trade -> search_id(Tablename, [id]); | |||
log_uplevel -> search_id(Tablename, [id]); | |||
log_use -> search_id(Tablename, [id]); | |||
log_warehouse_flowdir -> search_id(Tablename, [id]); | |||
log_wash -> search_id(Tablename, [id]); | |||
login_prize -> search_id(Tablename, [id]); | |||
mail -> search_id(Tablename, [id]); | |||
master_apprentice -> search_id(Tablename, [id]); | |||
master_charts -> search_id(Tablename, [id]); | |||
meridian -> search_id(Tablename, [id]); | |||
mon_drop_analytics -> search_id(Tablename, [id]); | |||
offline_award -> search_id(Tablename, [id]); | |||
online_award -> search_id(Tablename, [id]); | |||
online_gift -> search_id(Tablename, [id]); | |||
pet -> search_id(Tablename, [id]); | |||
player -> search_id(Tablename, [id]); | |||
player_buff -> search_id(Tablename, [id]); | |||
player_donttalk -> search_id(Tablename, [player_id]); | |||
player_hook_setting -> search_id(Tablename, [id]); | |||
player_sys_setting -> search_id(Tablename, [id]); | |||
relationship -> search_id(Tablename, [id]); | |||
sale_goods -> search_id(Tablename, [id]); | |||
skill -> search_id(Tablename, [id]); | |||
target_gift -> search_id(Tablename, [id]); | |||
task_bag -> search_id(Tablename, [id]); | |||
task_consign -> search_id(Tablename, [id]); | |||
task_log -> search_id(Tablename, [id]); | |||
user -> search_id(Tablename, [id]); | |||
_ -> search_id([], []) | |||
end | |||
end, | |||
L = [F(Tablename) || Tablename <- TableList], | |||
%% 查询有记录的表及主键 | |||
[R || R <- L, R =/= {}]. | |||
%%查询表最大的主键 | |||
search_id(Tablename, FieldList) -> | |||
case Tablename =/= [] of | |||
false -> {}; | |||
_ -> | |||
io:format("search_id ~p~n", [Tablename]), | |||
FieldString = util:list_to_string(FieldList), | |||
MaxId = db_mongo:select_one_new(tool:to_list(?SLAVE_POOLID), Tablename, FieldString, [], [{FieldString, desc}], [1]), | |||
case MaxId of | |||
undefined -> {}; | |||
null -> {}; | |||
_ -> {Tablename, MaxId + 1} | |||
end | |||
end. | |||
update_id(Tablename, FieldList, CheckExist, TablesMaxIdList) -> | |||
io:format("update_id ~p~n", [Tablename]), | |||
%% FieldString = util:list_to_string(FieldList), | |||
case CheckExist of | |||
0 -> | |||
F = fun(AnotherTable, Field) -> | |||
case lists:keysearch(AnotherTable, 1, TablesMaxIdList) of | |||
false -> {}; | |||
{value, {AnotherTable, MaxId}} -> | |||
{Field, MaxId, add} | |||
end | |||
end, | |||
FieldList1 = [F(AnotherTable, Field) || {AnotherTable, Field} <- FieldList], | |||
FieldList2 = [FieldValue || FieldValue <- FieldList1, FieldValue =/= {}], | |||
%% emongo:update(tool:to_list(?MASTER_POOLID),tool:to_list(Tablename), [], [{"$inc",FieldList1}]); | |||
db_mongo:update(Tablename, FieldList2, []); | |||
1 -> | |||
FieldList1 = [(Field) || {_AnotherTable, Field} <- FieldList], | |||
FieldList2 = util:list_to_string(FieldList1), | |||
ResultList = db_mongo:select_all(Tablename, FieldList2), | |||
F = fun(Record) -> | |||
FieldTh = [N1 || N1 <- lists:seq(1, length(Record)), lists:nth(N1, Record) > 0, lists:nth(N1, Record) =/= undefined], | |||
F1 = fun(N2) -> | |||
OldValue1 = lists:nth(N2, Record), | |||
Field1 = lists:nth(N2, FieldList1), | |||
F2 = fun() -> | |||
[AnotherTableName2] = [AnotherTable2 || {AnotherTable2, Field2} <- FieldList, Field1 == Field2], | |||
case lists:keyfind(AnotherTableName2, 1, TablesMaxIdList) of | |||
false -> 0; | |||
{_, MaxId} -> MaxId | |||
end | |||
end, | |||
AnotherValue = F2, | |||
{Field1, AnotherValue + OldValue1} | |||
end, | |||
FieldString1 = [F1(N2) || N2 <- FieldTh], | |||
Where1 = [{lists:nth(1, FieldList1), lists:nth(1, Record)}], | |||
db_mongo:update(Tablename, FieldString1, Where1) | |||
end, | |||
[F(Record) || Record <- ResultList]; | |||
_ -> skip | |||
end. | |||
update_ids() -> | |||
AutoIdsList = emongo:find_all(tool:to_list(?MASTER_POOLID), tool:to_list(auto_ids), [], ["id,name,mid,counter,kid,gid,uid,num,level"]), | |||
io:format("AutoIdsList is ~p~n", [AutoIdsList]), | |||
F = fun(Result) -> | |||
{_E1, Value1} = lists:nth(1, Result), | |||
{_E2, Value2} = lists:nth(2, Result), | |||
case tool:to_atom(tool:to_list(Value1)) of | |||
master_apprentice -> update_ids(master_apprentice, [id]); | |||
mon_drop_analytics -> update_ids(mon_drop_analytics, [id]); | |||
online_gift -> update_ids(online_gift, [id]); | |||
player_buff -> update_ids(player_buff, [id]); | |||
player_hook_setting -> update_ids(player_hook_setting, [id]); | |||
relationship -> update_ids(relationship, [id]); | |||
sale_goods -> update_ids(sale_goods, [id]); | |||
stc_create_page -> update_ids(stc_create_page, [id]); | |||
system_config -> update_ids(system_config, [id]); | |||
target_gift -> update_ids(target_gift, [id]); | |||
user -> update_ids(user, [id]); | |||
task_bag -> update_ids(task_bag, [id]); | |||
task_log -> update_ids(task_log, [id]); | |||
skill -> update_ids(skill, [id]); | |||
player_sys_setting -> update_ids(player_sys_setting, [id]); | |||
realm_1 -> update_realm(player, [1]); | |||
realm_2 -> update_realm(player, [2]); | |||
realm_3 -> update_realm(player, [3]); | |||
log_box_player -> update_ids(log_box_player, [id]); | |||
dungeon_id -> update_dungeon_id(log_dungeon, [dungeon_id]); | |||
log_dungeon -> update_ids(log_dungeon, [id]); | |||
exc -> update_ids(exc, [id]); | |||
daily_bless -> update_ids(daily_bless, [id]); | |||
log_exc_exp -> update_ids(log_exc_exp, [id]); | |||
arena -> update_ids(arena, [id]); | |||
cards -> update_ids(cards, [id]); | |||
feedback -> update_ids(feedback, [id]); | |||
goods -> update_ids(goods, [id]); | |||
goods_attribute -> update_ids(goods_attribute, [id]); | |||
goods_buff -> update_ids(goods_buff, [id]); | |||
goods_cd -> update_ids(goods_cd, [id]); | |||
guild -> update_ids(guild, [id]); | |||
guild_apply -> update_ids(guild_apply, [id]); | |||
guild_invite -> update_ids(guild_invite, [id]); | |||
guild_member -> update_ids(guild_member, [id]); | |||
guild_skills_attribute -> update_ids(guild_skills_attribute, [id]); | |||
log_backout -> update_ids(log_backout, [id]); | |||
log_box_open -> update_ids(log_box_open, [id]); | |||
log_compose -> update_ids(log_compose, [id]); | |||
log_consume -> update_ids(log_consume, [id]); | |||
log_exc -> update_ids(log_exc, [id]); | |||
log_guild -> update_ids(log_guild, [id]); | |||
log_hole -> update_ids(log_hole, [id]); | |||
log_identify -> update_ids(log_identify, [id]); | |||
log_inlay -> update_ids(log_inlay, [id]); | |||
log_merge -> update_ids(log_merge, [id]); | |||
log_meridian -> update_ids(log_meridian, [id]); | |||
log_pay -> update_ids(log_pay, [id]); | |||
log_practise -> update_ids(log_practise, [id]); | |||
log_quality_out -> update_ids(log_quality_out, [id]); | |||
log_quality_up -> update_ids(log_quality_up, [id]); | |||
log_sale -> update_ids(log_sale, [id]); | |||
log_shop -> update_ids(log_shop, [id]); | |||
log_stren -> update_ids(log_stren, [id]); | |||
log_trade -> update_ids(log_trade, [id]); | |||
log_uplevel -> update_ids(log_uplevel, [id]); | |||
log_use -> update_ids(log_use, [id]); | |||
log_wash -> update_ids(log_wash, [id]); | |||
login_prize -> update_ids(login_prize, [id]); | |||
mail -> update_ids(mail, [id]); | |||
master_charts -> update_ids(master_charts, [id]); | |||
meridian -> update_ids(meridian, [id]); | |||
pet -> update_ids(pet, [id]); | |||
player -> update_ids(player, [id]); | |||
stc_min -> update_ids(stc_min, [id]); | |||
sys_acm -> update_ids(sys_acm, [id]); | |||
test -> update_ids(test, [id]); | |||
log_suitmerge -> update_ids(log_suitmerge, [id]); | |||
infant_ctrl_byuser -> update_ids(infant_ctrl_byuser, [id]); | |||
log_mail -> update_ids(log_mail, [id]); | |||
log_throw -> update_ids(log_throw, [id]); | |||
task_consign -> update_ids(task_consign, [id]); | |||
log_free_pet -> update_ids(log_free_pet, [id]); | |||
guild_manor_cd -> update_ids(guild_manor_cd, [id]); | |||
log_sale_dir -> update_ids(log_sale_dir, [id]); | |||
log_warehouse_flowdir -> update_ids(log_warehouse_flowdir, [id]); | |||
arena_week -> update_ids(arena_week, [id]); | |||
carry -> update_ids(carry, [id]); | |||
consign_player -> update_ids(consign_player, [id]); | |||
log_linggen -> update_ids(log_linggen, [id]); | |||
_ -> skip | |||
end, | |||
case tool:to_atom(tool:to_list(Value2)) of | |||
master_apprentice -> update_ids(master_apprentice, [id]); | |||
mon_drop_analytics -> update_ids(mon_drop_analytics, [id]); | |||
online_gift -> update_ids(online_gift, [id]); | |||
player_buff -> update_ids(player_buff, [id]); | |||
player_hook_setting -> update_ids(player_hook_setting, [id]); | |||
relationship -> update_ids(relationship, [id]); | |||
sale_goods -> update_ids(sale_goods, [id]); | |||
stc_create_page -> update_ids(stc_create_page, [id]); | |||
system_config -> update_ids(system_config, [id]); | |||
target_gift -> update_ids(target_gift, [id]); | |||
user -> update_ids(user, [id]); | |||
task_bag -> update_ids(task_bag, [id]); | |||
task_log -> update_ids(task_log, [id]); | |||
skill -> update_ids(skill, [id]); | |||
player_sys_setting -> update_ids(player_sys_setting, [id]); | |||
realm_1 -> update_realm(player, [1]); | |||
realm_2 -> update_realm(player, [2]); | |||
realm_3 -> update_realm(player, [3]); | |||
log_box_player -> update_ids(log_box_player, [id]); | |||
dungeon_id -> update_dungeon_id(log_dungeon, [dungeon_id]); | |||
log_dungeon -> update_ids(log_dungeon, [id]); | |||
exc -> update_ids(exc, [id]); | |||
daily_bless -> update_ids(daily_bless, [id]); | |||
log_exc_exp -> update_ids(log_exc_exp, [id]); | |||
arena -> update_ids(arena, [id]); | |||
cards -> update_ids(cards, [id]); | |||
feedback -> update_ids(feedback, [id]); | |||
goods -> update_ids(goods, [id]); | |||
goods_attribute -> update_ids(goods_attribute, [id]); | |||
goods_buff -> update_ids(goods_buff, [id]); | |||
goods_cd -> update_ids(goods_cd, [id]); | |||
guild -> update_ids(guild, [id]); | |||
guild_apply -> update_ids(guild_apply, [id]); | |||
guild_invite -> update_ids(guild_invite, [id]); | |||
guild_member -> update_ids(guild_member, [id]); | |||
guild_skills_attribute -> update_ids(guild_skills_attribute, [id]); | |||
log_backout -> update_ids(log_backout, [id]); | |||
log_box_open -> update_ids(log_box_open, [id]); | |||
log_compose -> update_ids(log_compose, [id]); | |||
log_consume -> update_ids(log_consume, [id]); | |||
log_exc -> update_ids(log_exc, [id]); | |||
log_guild -> update_ids(log_guild, [id]); | |||
log_hole -> update_ids(log_hole, [id]); | |||
log_identify -> update_ids(log_identify, [id]); | |||
log_inlay -> update_ids(log_inlay, [id]); | |||
log_merge -> update_ids(log_merge, [id]); | |||
log_meridian -> update_ids(log_meridian, [id]); | |||
log_pay -> update_ids(log_pay, [id]); | |||
log_practise -> update_ids(log_practise, [id]); | |||
log_quality_out -> update_ids(log_quality_out, [id]); | |||
log_quality_up -> update_ids(log_quality_up, [id]); | |||
log_sale -> update_ids(log_sale, [id]); | |||
log_shop -> update_ids(log_shop, [id]); | |||
log_stren -> update_ids(log_stren, [id]); | |||
log_trade -> update_ids(log_trade, [id]); | |||
log_uplevel -> update_ids(log_uplevel, [id]); | |||
log_use -> update_ids(log_use, [id]); | |||
log_wash -> update_ids(log_wash, [id]); | |||
login_prize -> update_ids(login_prize, [id]); | |||
mail -> update_ids(mail, [id]); | |||
master_charts -> update_ids(master_charts, [id]); | |||
meridian -> update_ids(meridian, [id]); | |||
pet -> update_ids(pet, [id]); | |||
player -> update_ids(player, [id]); | |||
stc_min -> update_ids(stc_min, [id]); | |||
sys_acm -> update_ids(sys_acm, [id]); | |||
test -> update_ids(test, [id]); | |||
log_suitmerge -> update_ids(log_suitmerge, [id]); | |||
infant_ctrl_byuser -> update_ids(infant_ctrl_byuser, [id]); | |||
log_mail -> update_ids(log_mail, [id]); | |||
log_throw -> update_ids(log_throw, [id]); | |||
task_consign -> update_ids(task_consign, [id]); | |||
log_free_pet -> update_ids(log_free_pet, [id]); | |||
guild_manor_cd -> update_ids(guild_manor_cd, [id]); | |||
log_sale_dir -> update_ids(log_sale_dir, [id]); | |||
log_warehouse_flowdir -> update_ids(log_warehouse_flowdir, [id]); | |||
arena_week -> update_ids(arena_week, [id]); | |||
carry -> update_ids(carry, [id]); | |||
consign_player -> update_ids(consign_player, [id]); | |||
log_linggen -> update_ids(log_linggen, [id]); | |||
_ -> skip | |||
end | |||
end, | |||
[F(lists:nthtail(1, AutoIds)) || AutoIds <- AutoIdsList]. | |||
update_ids(Tablename, FieldList) -> | |||
io:format("update_ids ~p~n", [Tablename]), | |||
FieldString = util:list_to_string(FieldList), | |||
MaxId = db_mongo:select_one(Tablename, FieldString, [], [{FieldString, desc}], [1]), | |||
MaxId1 = | |||
case MaxId of | |||
null -> 0; | |||
_ -> MaxId | |||
end, | |||
io:format("MaxId1 is ~p~n", [MaxId1]), | |||
MaxId2 = | |||
case Tablename of | |||
user -> | |||
[UserCount] = db_mongo:select_count(Tablename, []), | |||
if UserCount > MaxId1 -> | |||
UserCount; | |||
true -> | |||
MaxId1 | |||
end; | |||
_ -> | |||
MaxId1 | |||
end, | |||
io:format("MaxId2 is ~p~n", [MaxId2]), | |||
db_mongo:update("auto_ids", [{FieldString, MaxId2}], [{name, Tablename}]). | |||
update_realm(Tablename, NumList) -> | |||
Realm = lists:nth(1, NumList), | |||
[Total] = db_mongo:select_count(Tablename, [{realm, Realm}]), | |||
Realm_Num = lists:concat(["realm_", Realm]), | |||
db_mongo:update("auto_ids", [{num, Total}], [{name, Realm_Num}]). | |||
update_dungeon_id(Tablename, FieldList) -> | |||
io:format("update_dungeon_id ~p~n", [Tablename]), | |||
FieldString = util:list_to_string(FieldList), | |||
Total = db_agent:sum(log_dungeon, "dungeon_counter", []), | |||
db_mongo:update("auto_ids", [{counter, Total}], [{name, FieldString}]). | |||
@ -1,67 +0,0 @@ | |||
-module(memory_show). | |||
-compile(export_all). | |||
show(N) -> | |||
F = fun(P) -> | |||
case catch process_info(P, [memory, dictionary]) of | |||
[{_, Memory}, {_, Dict}] -> | |||
InitStart = util:prop_get_value('$initial_call', Dict, null), | |||
{InitStart, Memory}; | |||
_ -> {null, 0} | |||
end | |||
end, | |||
Infos1 = lists:map(F, processes()), | |||
Infos2 = [{Name, M} || {Name, M} <- Infos1, Name =/= null], | |||
SortFun = fun({_, M1}, {_, M2}) -> M1 > M2 end, | |||
Infos3 = lists:sort(SortFun, Infos2), | |||
Infos4 = lists:sublist(Infos3, N), | |||
[io:format("~p : ~p ~n", [Name, M]) || {Name, M} <- Infos4], | |||
ok. | |||
show(N, SkipNames) -> | |||
F = fun(P) -> | |||
case catch process_info(P, [memory, dictionary]) of | |||
[{_, Memory}, {_, Dict}] -> | |||
InitStart = util:prop_get_value('$initial_call', Dict, null), | |||
case catch tuple_to_list(InitStart) of | |||
[Name | _] -> | |||
case lists:member(Name, SkipNames) of | |||
true -> {null, 0}; | |||
false -> {InitStart, Memory} | |||
end; | |||
_ -> {null, 0} | |||
end; | |||
_ -> {null, 0} | |||
end | |||
end, | |||
Infos1 = lists:map(F, processes()), | |||
Infos2 = [{Name, M} || {Name, M} <- Infos1, Name =/= null], | |||
SortFun = fun({_, M1}, {_, M2}) -> M1 > M2 end, | |||
Infos3 = lists:sort(SortFun, Infos2), | |||
Infos4 = lists:sublist(Infos3, N), | |||
[io:format("~p : ~p ~n", [Name, M]) || {Name, M} <- Infos4], | |||
ok. | |||
show1(N) -> | |||
F = fun(P, Acc) -> | |||
case catch process_info(P, [memory, dictionary]) of | |||
[{_, Memory}, {_, Dict}] -> | |||
InitStart = util:prop_get_value('$initial_call', Dict, null), | |||
case lists:keyfind(InitStart, 1, Acc) of | |||
false -> [{InitStart, Memory, 1} | Acc]; | |||
{InitStart, Memory1, Num} -> lists:keystore(InitStart, 1, Acc, {InitStart, Memory + Memory1, Num + 1}) | |||
end; | |||
_ -> Acc | |||
end | |||
end, | |||
Infos1 = lists:foldl(F, [], processes()), | |||
Infos2 = [{Name, M, Num} || {Name, M, Num} <- Infos1, Name =/= null], | |||
SortFun = fun({_, M1, _}, {_, M2, _}) -> M1 > M2 end, | |||
Infos3 = lists:sort(SortFun, Infos2), | |||
Infos4 = lists:sublist(Infos3, N), | |||
[io:format("~p : per_memory=~p process_num=~p ~n", [Name, (M div Num), Num]) || {Name, M, Num} <- Infos4], | |||
ok. | |||
@ -1,704 +0,0 @@ | |||
%%% @author Fred Hebert <mononcqc@ferd.ca> | |||
%%% [http://ferd.ca/] | |||
%%% @doc Recon, as a module, provides access to the high-level functionality | |||
%%% contained in the Recon application. | |||
%%% | |||
%%% It has functions in five main categories: | |||
%%% | |||
%%% <dl> | |||
%%% <dt>1. State information</dt> | |||
%%% <dd>Process information is everything that has to do with the | |||
%%% general state of the node. Functions such as {@link info/1} | |||
%%% and {@link info/3} are wrappers to provide more details than | |||
%%% `erlang:process_info/1', while providing it in a production-safe | |||
%%% manner. They have equivalents to `erlang:process_info/2' in | |||
%%% the functions {@link info/2} and {@link info/4}, respectively.</dd> | |||
%%% <dd>{@link proc_count/2} and {@link proc_window/3} are to be used | |||
%%% when you require information about processes in a larger sense: | |||
%%% biggest consumers of given process information (say memory or | |||
%%% reductions), either absolutely or over a sliding time window, | |||
%%% respectively.</dd> | |||
%%% <dd>{@link bin_leak/1} is a function that can be used to try and | |||
%%% see if your Erlang node is leaking refc binaries. See the function | |||
%%% itself for more details.</dd> | |||
%%% <dd>Functions to access node statistics, in a manner somewhat similar | |||
%%% to what <a href="https://github.com/ferd/vmstats">vmstats</a> | |||
%%% provides as a library. There are 3 of them: | |||
%%% {@link node_stats_print/2}, which displays them, | |||
%%% {@link node_stats_list/2}, which returns them in a list, and | |||
%%% {@link node_stats/4}, which provides a fold-like interface | |||
%%% for stats gathering. For CPU usage specifically, see | |||
%%% {@link scheduler_usage/1}.</dd> | |||
%%% | |||
%%% <dt>2. OTP tools</dt> | |||
%%% <dd>This category provides tools to interact with pieces of OTP | |||
%%% more easily. At this point, the only function included is | |||
%%% {@link get_state/1}, which works as a wrapper around | |||
%%% {@link get_state/2}, which works as a wrapper around | |||
%%% `sys:get_state/1' in R16B01, and provides the required | |||
%%% functionality for older versions of Erlang.</dd> | |||
%%% | |||
%%% <dt>3. Code Handling</dt> | |||
%%% <dd>Specific functions are in `recon' for the sole purpose | |||
%%% of interacting with source and compiled code. | |||
%%% {@link remote_load/1} and {@link remote_load/2} will allow | |||
%%% to take a local module, and load it remotely (in a diskless | |||
%%% manner) on another Erlang node you're connected to.</dd> | |||
%%% <dd>{@link source/1} allows to print the source of a loaded module, | |||
%%% in case it's not available in the currently running node.</dd> | |||
%%% | |||
%%% <dt>4. Ports and Sockets</dt> | |||
%%% <dd>To make it simpler to debug some network-related issues, | |||
%%% recon contains functions to deal with Erlang ports (raw, file | |||
%%% handles, or inet). Functions {@link tcp/0}, {@link udp/0}, | |||
%%% {@link sctp/0}, {@link files/0}, and {@link port_types/0} will | |||
%%% list all the Erlang ports of a given type. The latter function | |||
%%% prints counts of all individual types.</dd> | |||
%%% <dd>Port state information can be useful to figure out why certain | |||
%%% parts of the system misbehave. Functions such as | |||
%%% {@link port_info/1} and {@link port_info/2} are wrappers to provide | |||
%%% more similar or more details than `erlang:port_info/1-2', and, for | |||
%%% inet ports, statistics and options for each socket.</dd> | |||
%%% <dd>Finally, the functions {@link inet_count/2} and {@link inet_window/3} | |||
%%% provide the absolute or sliding window functionality of | |||
%%% {@link proc_count/2} and {@link proc_count/3} to inet ports | |||
%%% and connections currently on the node.</dd> | |||
%%% | |||
%%% <dt>5. RPC</dt> | |||
%%% <dd>These are wrappers to make RPC work simpler with clusters of | |||
%%% Erlang nodes. Default RPC mechanisms (from the `rpc' module) | |||
%%% make it somewhat painful to call shell-defined funs over node | |||
%%% boundaries. The functions {@link rpc/1}, {@link rpc/2}, and | |||
%%% {@link rpc/3} will do it with a simpler interface.</dd> | |||
%%% <dd>Additionally, when you're running diagnostic code on remote | |||
%%% nodes and want to know which node evaluated what result, using | |||
%%% {@link named_rpc/1}, {@link named_rpc/2}, and {@link named_rpc/3} | |||
%%% will wrap the results in a tuple that tells you which node it's | |||
%%% coming from, making it easier to identify bad nodes.</dd> | |||
%%% </dl> | |||
%%% @end | |||
-module(recon). | |||
-export([info/1, info/2, info/3, info/4, | |||
proc_count/2, proc_window/3, | |||
bin_leak/1, | |||
node_stats_print/2, node_stats_list/2, node_stats/4, | |||
scheduler_usage/1]). | |||
-export([get_state/1, get_state/2]). | |||
-export([remote_load/1, remote_load/2, | |||
source/1]). | |||
-export([tcp/0, udp/0, sctp/0, files/0, port_types/0, | |||
inet_count/2, inet_window/3, | |||
port_info/1, port_info/2]). | |||
-export([rpc/1, rpc/2, rpc/3, | |||
named_rpc/1, named_rpc/2, named_rpc/3]). | |||
%%%%%%%%%%%%% | |||
%%% TYPES %%% | |||
%%%%%%%%%%%%% | |||
-type proc_attrs() :: {pid(), | |||
Attr :: _, | |||
[Name :: atom() | |||
|{current_function, mfa()} | |||
|{initial_call, mfa()}, ...]}. | |||
-type inet_attrs() :: {port(), | |||
Attr :: _, | |||
[{atom(), term()}]}. | |||
-type pid_term() :: pid() | atom() | string() | |||
| {global, term()} | {via, module(), term()} | |||
| {non_neg_integer(), non_neg_integer(), non_neg_integer()}. | |||
-type info_type() :: meta | signals | location | memory_used | work. | |||
-type info_meta_key() :: registered_name | dictionary | group_leader | status. | |||
-type info_signals_key() :: links | monitors | monitored_by | trap_exit. | |||
-type info_location_key() :: initial_call | current_stacktrace. | |||
-type info_memory_key() :: memory | message_queue_len | heap_size | |||
| total_heap_size | garbage_collection. | |||
-type info_work_key() :: reductions. | |||
-type info_key() :: info_meta_key() | info_signals_key() | info_location_key() | |||
| info_memory_key() | info_work_key(). | |||
-type port_term() :: port() | string() | atom() | pos_integer(). | |||
-type port_info_type() :: meta | signals | io | memory_used | specific. | |||
-type port_info_meta_key() :: registered_name | id | name | os_pid. | |||
-type port_info_signals_key() :: connected | links | monitors. | |||
-type port_info_io_key() :: input | output. | |||
-type port_info_memory_key() :: memory | queue_size. | |||
-type port_info_specific_key() :: atom(). | |||
-type port_info_key() :: port_info_meta_key() | port_info_signals_key() | |||
| port_info_io_key() | port_info_memory_key() | |||
| port_info_specific_key(). | |||
-export_type([proc_attrs/0, inet_attrs/0, pid_term/0, port_term/0]). | |||
-export_type([info_type/0, info_key/0, | |||
info_meta_key/0, info_signals_key/0, info_location_key/0, | |||
info_memory_key/0, info_work_key/0]). | |||
-export_type([port_info_type/0, port_info_key/0, | |||
port_info_meta_key/0, port_info_signals_key/0, port_info_io_key/0, | |||
port_info_memory_key/0, port_info_specific_key/0]). | |||
%%%%%%%%%%%%%%%%%% | |||
%%% PUBLIC API %%% | |||
%%%%%%%%%%%%%%%%%% | |||
%%% Process Info %%% | |||
%% @doc Equivalent to `info(<A.B.C>)' where `A', `B', and `C' are integers part | |||
%% of a pid | |||
-spec info(N, N, N) -> [{info_type(), [{info_key(), term()}]}, ...] when | |||
N :: non_neg_integer(). | |||
info(A, B, C) -> info(recon_lib:triple_to_pid(A, B, C)). | |||
%% @doc Equivalent to `info(<A.B.C>, Key)' where `A', `B', and `C' are integers part | |||
%% of a pid | |||
-spec info(N, N, N, Key) -> term() when | |||
N :: non_neg_integer(), | |||
Key :: info_type() | [atom()] | atom(). | |||
info(A, B, C, Key) -> info(recon_lib:triple_to_pid(A, B, C), Key). | |||
%% @doc Allows to be similar to `erlang:process_info/1', but excludes fields | |||
%% such as the mailbox, which have a tendency to grow and be unsafe when called | |||
%% in production systems. Also includes a few more fields than what is usually | |||
%% given (`monitors', `monitored_by', etc.), and separates the fields in a more | |||
%% readable format based on the type of information contained. | |||
%% | |||
%% Moreover, it will fetch and read information on local processes that were | |||
%% registered locally (an atom), globally (`{global, Name}'), or through | |||
%% another registry supported in the `{via, Module, Name}' syntax (must have a | |||
%% `Module:whereis_name/1' function). Pids can also be passed in as a string | |||
%% (`"<0.39.0>"') or a triple (`{0,39,0}') and will be converted to be used. | |||
-spec info(pid_term()) -> [{info_type(), [{info_key(), Value}]}, ...] when | |||
Value :: term(). | |||
info(PidTerm) -> | |||
Pid = recon_lib:term_to_pid(PidTerm), | |||
[info(Pid, Type) || Type <- [meta, signals, location, memory_used, work]]. | |||
%% @doc Allows to be similar to `erlang:process_info/2', but allows to | |||
%% sort fields by safe categories and pre-selections, avoiding items such | |||
%% as the mailbox, which may have a tendency to grow and be unsafe when | |||
%% called in production systems. | |||
%% | |||
%% Moreover, it will fetch and read information on local processes that were | |||
%% registered locally (an atom), globally (`{global, Name}'), or through | |||
%% another registry supported in the `{via, Module, Name}' syntax (must have a | |||
%% `Module:whereis_name/1' function). Pids can also be passed in as a string | |||
%% (`"<0.39.0>"') or a triple (`{0,39,0}') and will be converted to be used. | |||
%% | |||
%% Although the type signature doesn't show it in generated documentation, | |||
%% a list of arguments or individual arguments accepted by | |||
%% `erlang:process_info/2' and return them as that function would. | |||
%% | |||
%% A fake attribute `binary_memory' is also available to return the | |||
%% amount of memory used by refc binaries for a process. | |||
-spec info(pid_term(), info_type()) -> {info_type(), [{info_key(), term()}]} | |||
; (pid_term(), [atom()]) -> [{atom(), term()}] | |||
; (pid_term(), atom()) -> {atom(), term()}. | |||
info(PidTerm, meta) -> | |||
info_type(PidTerm, meta, [registered_name, dictionary, group_leader, | |||
status]); | |||
info(PidTerm, signals) -> | |||
info_type(PidTerm, signals, [links, monitors, monitored_by, trap_exit]); | |||
info(PidTerm, location) -> | |||
info_type(PidTerm, location, [initial_call, current_stacktrace]); | |||
info(PidTerm, memory_used) -> | |||
info_type(PidTerm, memory_used, [memory, message_queue_len, heap_size, | |||
total_heap_size, garbage_collection]); | |||
info(PidTerm, work) -> | |||
info_type(PidTerm, work, [reductions]); | |||
info(PidTerm, Keys) -> | |||
proc_info(recon_lib:term_to_pid(PidTerm), Keys). | |||
%% @private makes access to `info_type()' calls simpler. | |||
-spec info_type(pid_term(), info_type(), [info_key()]) -> | |||
{info_type(), [{info_key(), term()}]}. | |||
info_type(PidTerm, Type, Keys) -> | |||
Pid = recon_lib:term_to_pid(PidTerm), | |||
{Type, proc_info(Pid, Keys)}. | |||
%% @private wrapper around `erlang:process_info/2' that allows special | |||
%% attribute handling for items like `binary_memory'. | |||
proc_info(Pid, binary_memory) -> | |||
{binary, Bins} = erlang:process_info(Pid, binary), | |||
{binary_memory, recon_lib:binary_memory(Bins)}; | |||
proc_info(Pid, Term) when is_atom(Term) -> | |||
erlang:process_info(Pid, Term); | |||
proc_info(Pid, List) when is_list(List) -> | |||
case lists:member(binary_memory, List) of | |||
false -> | |||
erlang:process_info(Pid, List); | |||
true -> | |||
Res = erlang:process_info(Pid, replace(binary_memory, binary, List)), | |||
proc_fake(List, Res) | |||
end. | |||
%% @private Replace keys around | |||
replace(_, _, []) -> []; | |||
replace(H, Val, [H | T]) -> [Val | replace(H, Val, T)]; | |||
replace(R, Val, [H | T]) -> [H | replace(R, Val, T)]. | |||
proc_fake([], []) -> | |||
[]; | |||
proc_fake([binary_memory | T1], [{binary, Bins} | T2]) -> | |||
[{binary_memory, recon_lib:binary_memory(Bins)} | |||
| proc_fake(T1, T2)]; | |||
proc_fake([_ | T1], [H | T2]) -> | |||
[H | proc_fake(T1, T2)]. | |||
%% @doc Fetches a given attribute from all processes (except the | |||
%% caller) and returns the biggest `Num' consumers. | |||
%% @todo Implement this function so it only stores `Num' entries in | |||
%% memory at any given time, instead of as many as there are | |||
%% processes. | |||
-spec proc_count(AttributeName, Num) -> [proc_attrs()] when | |||
AttributeName :: atom(), | |||
Num :: non_neg_integer(). | |||
proc_count(AttrName, Num) -> | |||
recon_lib:sublist_top_n_attrs(recon_lib:proc_attrs(AttrName), Num). | |||
%% @doc Fetches a given attribute from all processes (except the | |||
%% caller) and returns the biggest entries, over a sliding time window. | |||
%% | |||
%% This function is particularly useful when processes on the node | |||
%% are mostly short-lived, usually too short to inspect through other | |||
%% tools, in order to figure out what kind of processes are eating | |||
%% through a lot resources on a given node. | |||
%% | |||
%% It is important to see this function as a snapshot over a sliding | |||
%% window. A program's timeline during sampling might look like this: | |||
%% | |||
%% `--w---- [Sample1] ---x-------------y----- [Sample2] ---z--->' | |||
%% | |||
%% Some processes will live between `w' and die at `x', some between `y' and | |||
%% `z', and some between `x' and `y'. These samples will not be too significant | |||
%% as they're incomplete. If the majority of your processes run between a time | |||
%% interval `x'...`y' (in absolute terms), you should make sure that your | |||
%% sampling time is smaller than this so that for many processes, their | |||
%% lifetime spans the equivalent of `w' and `z'. Not doing this can skew the | |||
%% results: long-lived processes, that have 10 times the time to accumulate | |||
%% data (say reductions) will look like bottlenecks when they're not one. | |||
%% | |||
%% Warning: this function depends on data gathered at two snapshots, and then | |||
%% building a dictionary with entries to differentiate them. This can take a | |||
%% heavy toll on memory when you have many dozens of thousands of processes. | |||
-spec proc_window(AttributeName, Num, Milliseconds) -> [proc_attrs()] when | |||
AttributeName :: atom(), | |||
Num :: non_neg_integer(), | |||
Milliseconds :: pos_integer(). | |||
proc_window(AttrName, Num, Time) -> | |||
Sample = fun() -> recon_lib:proc_attrs(AttrName) end, | |||
{First, Last} = recon_lib:sample(Time, Sample), | |||
recon_lib:sublist_top_n_attrs(recon_lib:sliding_window(First, Last), Num). | |||
%% @doc Refc binaries can be leaking when barely-busy processes route them | |||
%% around and do little else, or when extremely busy processes reach a stable | |||
%% amount of memory allocated and do the vast majority of their work with refc | |||
%% binaries. When this happens, it may take a very long while before references | |||
%% get deallocated and refc binaries get to be garbage collected, leading to | |||
%% Out Of Memory crashes. | |||
%% This function fetches the number of refc binary references in each process | |||
%% of the node, garbage collects them, and compares the resulting number of | |||
%% references in each of them. The function then returns the `N' processes | |||
%% that freed the biggest amount of binaries, potentially highlighting leaks. | |||
%% | |||
%% See <a href="http://www.erlang.org/doc/efficiency_guide/binaryhandling.html#id65722">The efficiency guide</a> | |||
%% for more details on refc binaries | |||
-spec bin_leak(pos_integer()) -> [proc_attrs()]. | |||
bin_leak(N) -> | |||
Procs = recon_lib:sublist_top_n_attrs([ | |||
try | |||
{ok, {_, Pre, Id}} = recon_lib:proc_attrs(binary, Pid), | |||
erlang:garbage_collect(Pid), | |||
{ok, {_, Post, _}} = recon_lib:proc_attrs(binary, Pid), | |||
{Pid, length(Pre) - length(Post), Id} | |||
catch | |||
_:_ -> {Pid, 0, []} | |||
end || Pid <- processes() | |||
], N), | |||
[{Pid, -Val, Id} || {Pid, Val, Id} <- Procs]. | |||
%% @doc Shorthand for `node_stats(N, Interval, fun(X,_) -> io:format("~p~n",[X]) end, nostate)'. | |||
-spec node_stats_print(Repeat, Interval) -> term() when | |||
Repeat :: non_neg_integer(), | |||
Interval :: pos_integer(). | |||
node_stats_print(N, Interval) -> | |||
node_stats(N, Interval, fun(X, _) -> io:format("~p~n", [X]) end, ok). | |||
%% @doc Because Erlang CPU usage as reported from `top' isn't the most | |||
%% reliable value (due to schedulers doing idle spinning to avoid going | |||
%% to sleep and impacting latency), a metric exists that is based on | |||
%% scheduler wall time. | |||
%% | |||
%% For any time interval, Scheduler wall time can be used as a measure | |||
%% of how 'busy' a scheduler is. A scheduler is busy when: | |||
%% | |||
%% <ul> | |||
%% <li>executing process code</li> | |||
%% <li>executing driver code</li> | |||
%% <li>executing NIF code</li> | |||
%% <li>executing BIFs</li> | |||
%% <li>garbage collecting</li> | |||
%% <li>doing memory management</li> | |||
%% </ul> | |||
%% | |||
%% A scheduler isn't busy when doing anything else. | |||
-spec scheduler_usage(Millisecs) -> [{SchedulerId, Usage}] when | |||
Millisecs :: non_neg_integer(), | |||
SchedulerId :: pos_integer(), | |||
Usage :: number(). | |||
scheduler_usage(Interval) when is_integer(Interval) -> | |||
%% We start and stop the scheduler_wall_time system flag if | |||
%% it wasn't in place already. Usually setting the flag should | |||
%% have a CPU impact (making it higher) only when under low usage. | |||
FormerFlag = erlang:system_flag(scheduler_wall_time, true), | |||
First = erlang:statistics(scheduler_wall_time), | |||
timer:sleep(Interval), | |||
Last = erlang:statistics(scheduler_wall_time), | |||
erlang:system_flag(scheduler_wall_time, FormerFlag), | |||
recon_lib:scheduler_usage_diff(First, Last). | |||
%% @doc Shorthand for `node_stats(N, Interval, fun(X,Acc) -> [X|Acc] end, [])' | |||
%% with the results reversed to be in the right temporal order. | |||
-spec node_stats_list(Repeat, Interval) -> [Stats] when | |||
Repeat :: non_neg_integer(), | |||
Interval :: pos_integer(), | |||
Stats :: {[Absolutes :: {atom(), term()}], | |||
[Increments :: {atom(), term()}]}. | |||
node_stats_list(N, Interval) -> | |||
lists:reverse(node_stats(N, Interval, fun(X, Acc) -> [X | Acc] end, [])). | |||
%% @doc Gathers statistics `N' time, waiting `Interval' milliseconds between | |||
%% each run, and accumulates results using a folding function `FoldFun'. | |||
%% The function will gather statistics in two forms: Absolutes and Increments. | |||
%% | |||
%% Absolutes are values that keep changing with time, and are useful to know | |||
%% about as a datapoint: process count, size of the run queue, error_logger | |||
%% queue length, and the memory of the node (total, processes, atoms, binaries, | |||
%% and ets tables). | |||
%% | |||
%% Increments are values that are mostly useful when compared to a previous | |||
%% one to have an idea what they're doing, because otherwise they'd never | |||
%% stop increasing: bytes in and out of the node, number of garbage colelctor | |||
%% runs, words of memory that were garbage collected, and the global reductions | |||
%% count for the node. | |||
-spec node_stats(N, Interval, FoldFun, Acc) -> Acc when | |||
N :: non_neg_integer(), | |||
Interval :: pos_integer(), | |||
FoldFun :: fun((Stats, Acc) -> Acc), | |||
Acc :: term(), | |||
Stats :: {[Absolutes :: {atom(), term()}], | |||
[Increments :: {atom(), term()}]}. | |||
node_stats(N, Interval, FoldFun, Init) -> | |||
%% Turn on scheduler wall time if it wasn't there already | |||
FormerFlag = erlang:system_flag(scheduler_wall_time, true), | |||
%% Stats is an ugly fun, but it does its thing. | |||
Stats = fun({{OldIn, OldOut}, {OldGCs, OldWords, _}, SchedWall}) -> | |||
%% Absolutes | |||
ProcC = erlang:system_info(process_count), | |||
RunQ = erlang:statistics(run_queue), | |||
{_, LogQ} = process_info(whereis(error_logger), message_queue_len), | |||
%% Mem (Absolutes) | |||
Mem = erlang:memory(), | |||
Tot = proplists:get_value(total, Mem), | |||
ProcM = proplists:get_value(processes_used, Mem), | |||
Atom = proplists:get_value(atom_used, Mem), | |||
Bin = proplists:get_value(binary, Mem), | |||
Ets = proplists:get_value(ets, Mem), | |||
%% Incremental | |||
{{input, In}, {output, Out}} = erlang:statistics(io), | |||
GC = {GCs, Words, _} = erlang:statistics(garbage_collection), | |||
BytesIn = In - OldIn, | |||
BytesOut = Out - OldOut, | |||
GCCount = GCs - OldGCs, | |||
GCWords = Words - OldWords, | |||
{_, Reds} = erlang:statistics(reductions), | |||
SchedWallNew = erlang:statistics(scheduler_wall_time), | |||
SchedUsage = recon_lib:scheduler_usage_diff(SchedWall, SchedWallNew), | |||
%% Stats Results | |||
{{[{process_count, ProcC}, {run_queue, RunQ}, | |||
{error_logger_queue_len, LogQ}, {memory_total, Tot}, | |||
{memory_procs, ProcM}, {memory_atoms, Atom}, | |||
{memory_bin, Bin}, {memory_ets, Ets}], | |||
[{bytes_in, BytesIn}, {bytes_out, BytesOut}, | |||
{gc_count, GCCount}, {gc_words_reclaimed, GCWords}, | |||
{reductions, Reds}, {scheduler_usage, SchedUsage}]}, | |||
%% New State | |||
{{In, Out}, GC, SchedWallNew}} | |||
end, | |||
{{input, In}, {output, Out}} = erlang:statistics(io), | |||
Gc = erlang:statistics(garbage_collection), | |||
SchedWall = erlang:statistics(scheduler_wall_time), | |||
Result = recon_lib:time_fold( | |||
N, Interval, Stats, | |||
{{In, Out}, Gc, SchedWall}, | |||
FoldFun, Init), | |||
%% Set scheduler wall time back to what it was | |||
erlang:system_flag(scheduler_wall_time, FormerFlag), | |||
Result. | |||
%%% OTP & Manipulations %%% | |||
%% @doc Shorthand call to `recon:get_state(PidTerm, 5000)' | |||
-spec get_state(pid_term()) -> term(). | |||
get_state(PidTerm) -> get_state(PidTerm, 5000). | |||
%% @doc Fetch the internal state of an OTP process. | |||
%% Calls `sys:get_state/2' directly in R16B01+, and fetches | |||
%% it dynamically on older versions of OTP. | |||
-spec get_state(pid_term(), Ms :: non_neg_integer() | 'infinity') -> term(). | |||
get_state(PidTerm, Timeout) -> | |||
Proc = recon_lib:term_to_pid(PidTerm), | |||
try | |||
sys:get_state(Proc, Timeout) | |||
catch | |||
error:undef -> | |||
case sys:get_status(Proc, Timeout) of | |||
{status, _Pid, {module, gen_server}, Data} -> | |||
{data, Props} = lists:last(lists:nth(5, Data)), | |||
proplists:get_value("State", Props); | |||
{status, _Pod, {module, gen_fsm}, Data} -> | |||
{data, Props} = lists:last(lists:nth(5, Data)), | |||
proplists:get_value("StateData", Props) | |||
end | |||
end. | |||
%%% Code & Stuff %%% | |||
%% @equiv remote_load(nodes(), Mod) | |||
-spec remote_load(module()) -> term(). | |||
remote_load(Mod) -> remote_load(nodes(), Mod). | |||
%% @doc Loads one or more modules remotely, in a diskless manner. Allows to | |||
%% share code loaded locally with a remote node that doesn't have it | |||
-spec remote_load(Nodes, module()) -> term() when | |||
Nodes :: [node(), ...] | node(). | |||
remote_load(Nodes = [_ | _], Mod) when is_atom(Mod) -> | |||
{Mod, Bin, File} = code:get_object_code(Mod), | |||
rpc:multicall(Nodes, code, load_binary, [Mod, File, Bin]); | |||
remote_load(Nodes = [_ | _], Modules) when is_list(Modules) -> | |||
[remote_load(Nodes, Mod) || Mod <- Modules]; | |||
remote_load(Node, Mod) -> | |||
remote_load([Node], Mod). | |||
%% @doc Obtain the source code of a module compiled with `debug_info'. | |||
%% The returned list sadly does not allow to format the types and typed | |||
%% records the way they look in the original module, but instead goes to | |||
%% an intermediary form used in the AST. They will still be placed | |||
%% in the right module attributes, however. | |||
%% @todo Figure out a way to pretty-print typespecs and records. | |||
-spec source(module()) -> iolist(). | |||
source(Module) -> | |||
Path = code:which(Module), | |||
{ok, {_, [{abstract_code, {_, AC}}]}} = beam_lib:chunks(Path, [abstract_code]), | |||
erl_prettypr:format(erl_syntax:form_list(AC)). | |||
%%% Ports Info %%% | |||
%% @doc returns a list of all TCP ports (the data type) open on the node. | |||
-spec tcp() -> [port()]. | |||
tcp() -> recon_lib:port_list(name, "tcp_inet"). | |||
%% @doc returns a list of all UDP ports (the data type) open on the node. | |||
-spec udp() -> [port()]. | |||
udp() -> recon_lib:port_list(name, "udp_inet"). | |||
%% @doc returns a list of all SCTP ports (the data type) open on the node. | |||
-spec sctp() -> [port()]. | |||
sctp() -> recon_lib:port_list(name, "sctp_inet"). | |||
%% @doc returns a list of all file handles open on the node. | |||
-spec files() -> [port()]. | |||
files() -> recon_lib:port_list(name, "efile"). | |||
%% @doc Shows a list of all different ports on the node with their respective | |||
%% types. | |||
-spec port_types() -> [{Type :: string(), Count :: pos_integer()}]. | |||
port_types() -> | |||
lists:usort( | |||
%% sorts by biggest count, smallest type | |||
fun({KA, VA}, {KB, VB}) -> {VA, KB} > {VB, KA} end, | |||
recon_lib:count([Name || {_, Name} <- recon_lib:port_list(name)]) | |||
). | |||
%% @doc Fetches a given attribute from all inet ports (TCP, UDP, SCTP) | |||
%% and returns the biggest `Num' consumers. | |||
%% | |||
%% The values to be used can be the number of octets (bytes) sent, received, | |||
%% or both (`send_oct', `recv_oct', `oct', respectively), or the number | |||
%% of packets sent, received, or both (`send_cnt', `recv_cnt', `cnt', | |||
%% respectively). Individual absolute values for each metric will be returned | |||
%% in the 3rd position of the resulting tuple. | |||
%% | |||
%% @todo Implement this function so it only stores `Num' entries in | |||
%% memory at any given time, instead of as many as there are | |||
%% processes. | |||
-spec inet_count(AttributeName, Num) -> [inet_attrs()] when | |||
AttributeName :: 'recv_cnt' | 'recv_oct' | 'send_cnt' | 'send_oct' | |||
| 'cnt' | 'oct', | |||
Num :: non_neg_integer(). | |||
inet_count(Attr, Num) -> | |||
recon_lib:sublist_top_n_attrs(recon_lib:inet_attrs(Attr), Num). | |||
%% @doc Fetches a given attribute from all inet ports (TCP, UDP, SCTP) | |||
%% and returns the biggest entries, over a sliding time window. | |||
%% | |||
%% Warning: this function depends on data gathered at two snapshots, and then | |||
%% building a dictionary with entries to differentiate them. This can take a | |||
%% heavy toll on memory when you have many dozens of thousands of ports open. | |||
%% | |||
%% The values to be used can be the number of octets (bytes) sent, received, | |||
%% or both (`send_oct', `recv_oct', `oct', respectively), or the number | |||
%% of packets sent, received, or both (`send_cnt', `recv_cnt', `cnt', | |||
%% respectively). Individual absolute values for each metric will be returned | |||
%% in the 3rd position of the resulting tuple. | |||
-spec inet_window(AttributeName, Num, Milliseconds) -> [inet_attrs()] when | |||
AttributeName :: 'recv_cnt' | 'recv_oct' | 'send_cnt' | 'send_oct' | |||
| 'cnt' | 'oct', | |||
Num :: non_neg_integer(), | |||
Milliseconds :: pos_integer(). | |||
inet_window(Attr, Num, Time) when is_atom(Attr) -> | |||
Sample = fun() -> recon_lib:inet_attrs(Attr) end, | |||
{First, Last} = recon_lib:sample(Time, Sample), | |||
recon_lib:sublist_top_n_attrs(recon_lib:sliding_window(First, Last), Num). | |||
%% @doc Allows to be similar to `erlang:port_info/1', but allows | |||
%% more flexible port usage: usual ports, ports that were registered | |||
%% locally (an atom), ports represented as strings (`"#Port<0.2013>"'), | |||
%% or through an index lookup (`2013', for the same result as | |||
%% `"#Port<0.2013>"'). | |||
%% | |||
%% Moreover, the function will try to fetch implementation-specific | |||
%% details based on the port type (only inet ports have this feature | |||
%% so far). For example, TCP ports will include information about the | |||
%% remote peer, transfer statistics, and socket options being used. | |||
%% | |||
%% The information-specific and the basic port info are sorted and | |||
%% categorized in broader categories ({@link port_info_type()}). | |||
-spec port_info(port_term()) -> [{port_info_type(), | |||
[{port_info_key(), term()}]}, ...]. | |||
port_info(PortTerm) -> | |||
Port = recon_lib:term_to_port(PortTerm), | |||
[port_info(Port, Type) || Type <- [meta, signals, io, memory_used, | |||
specific]]. | |||
%% @doc Allows to be similar to `erlang:port_info/2', but allows | |||
%% more flexible port usage: usual ports, ports that were registered | |||
%% locally (an atom), ports represented as strings (`"#Port<0.2013>"'), | |||
%% or through an index lookup (`2013', for the same result as | |||
%% `"#Port<0.2013>"'). | |||
%% | |||
%% Moreover, the function allows to to fetch information by category | |||
%% as defined in {@link port_info_type()}, and although the type signature | |||
%% doesn't show it in the generated documentation, individual items | |||
%% accepted by `erlang:port_info/2' are accepted, and lists of them too. | |||
-spec port_info(port_term(), port_info_type()) -> {port_info_type(), | |||
[{port_info_key(), _}]} | |||
; (port_term(), [atom()]) -> [{atom(), term()}] | |||
; (port_term(), atom()) -> {atom(), term()}. | |||
port_info(PortTerm, meta) -> | |||
{meta, List} = port_info_type(PortTerm, meta, [id, name, os_pid]), | |||
case port_info(PortTerm, registered_name) of | |||
[] -> {meta, List}; | |||
Name -> {meta, [Name | List]} | |||
end; | |||
port_info(PortTerm, signals) -> | |||
port_info_type(PortTerm, signals, [connected, links, monitors]); | |||
port_info(PortTerm, io) -> | |||
port_info_type(PortTerm, io, [input, output]); | |||
port_info(PortTerm, memory_used) -> | |||
port_info_type(PortTerm, memory_used, [memory, queue_size]); | |||
port_info(PortTerm, specific) -> | |||
Port = recon_lib:term_to_port(PortTerm), | |||
Props = case erlang:port_info(Port, name) of | |||
{_, Type} when Type =:= "udp_inet"; | |||
Type =:= "tcp_inet"; | |||
Type =:= "sctp_inet" -> | |||
case inet:getstat(Port) of | |||
{ok, Stats} -> [{statistics, Stats}]; | |||
_ -> [] | |||
end ++ | |||
case inet:peername(Port) of | |||
{ok, Peer} -> [{peername, Peer}]; | |||
{error, _} -> [] | |||
end ++ | |||
case inet:sockname(Port) of | |||
{ok, Local} -> [{sockname, Local}]; | |||
{error, _} -> [] | |||
end ++ | |||
case inet:getopts(Port, [active, broadcast, buffer, delay_send, | |||
dontroute, exit_on_close, header, | |||
high_watermark, ipv6_v6only, keepalive, | |||
linger, low_watermark, mode, nodelay, | |||
packet, packet_size, priority, | |||
read_packets, recbuf, reuseaddr, | |||
send_timeout, sndbuf]) of | |||
{ok, Opts} -> [{options, Opts}]; | |||
{error, _} -> [] | |||
end; | |||
{_, "efile"} -> | |||
%% would be nice to support file-specific info, but things | |||
%% are too vague with the file_server and how it works in | |||
%% order to make this work efficiently | |||
[]; | |||
_ -> | |||
[] | |||
end, | |||
{type, Props}; | |||
port_info(PortTerm, Keys) when is_list(Keys) -> | |||
Port = recon_lib:term_to_port(PortTerm), | |||
[erlang:port_info(Port, Key) || Key <- Keys]; | |||
port_info(PortTerm, Key) when is_atom(Key) -> | |||
erlang:port_info(recon_lib:term_to_port(PortTerm), Key). | |||
%% @private makes access to `port_info_type()' calls simpler. | |||
%-spec port_info_type(pid_term(), port_info_type(), [port_info_key()]) -> | |||
% {port_info_type(), [{port_info_key(), term()}]}. | |||
port_info_type(PortTerm, Type, Keys) -> | |||
Port = recon_lib:term_to_port(PortTerm), | |||
{Type, [erlang:port_info(Port, Key) || Key <- Keys]}. | |||
%%% RPC Utils %%% | |||
%% @doc Shorthand for `rpc([node()|nodes()], Fun)'. | |||
-spec rpc(fun(() -> term())) -> {[Success :: _], [Fail :: _]}. | |||
rpc(Fun) -> | |||
rpc([node() | nodes()], Fun). | |||
%% @doc Shorthand for `rpc(Nodes, Fun, infinity)'. | |||
-spec rpc(node()|[node(), ...], fun(() -> term())) -> {[Success :: _], [Fail :: _]}. | |||
rpc(Nodes, Fun) -> | |||
rpc(Nodes, Fun, infinity). | |||
%% @doc Runs an arbitrary fun (of arity 0) over one or more nodes. | |||
-spec rpc(node()|[node(), ...], fun(() -> term()), timeout()) -> {[Success :: _], [Fail :: _]}. | |||
rpc(Nodes = [_ | _], Fun, Timeout) when is_function(Fun, 0) -> | |||
rpc:multicall(Nodes, erlang, apply, [Fun, []], Timeout); | |||
rpc(Node, Fun, Timeout) when is_atom(Node) -> | |||
rpc([Node], Fun, Timeout). | |||
%% @doc Shorthand for `named_rpc([node()|nodes()], Fun)'. | |||
-spec named_rpc(fun(() -> term())) -> {[Success :: _], [Fail :: _]}. | |||
named_rpc(Fun) -> | |||
named_rpc([node() | nodes()], Fun). | |||
%% @doc Shorthand for `named_rpc(Nodes, Fun, infinity)'. | |||
-spec named_rpc(node()|[node(), ...], fun(() -> term())) -> {[Success :: _], [Fail :: _]}. | |||
named_rpc(Nodes, Fun) -> | |||
named_rpc(Nodes, Fun, infinity). | |||
%% @doc Runs an arbitrary fun (of arity 0) over one or more nodes, and returns the | |||
%% name of the node that computed a given result along with it, in a tuple. | |||
-spec named_rpc(node()|[node(), ...], fun(() -> term()), timeout()) -> {[Success :: _], [Fail :: _]}. | |||
named_rpc(Nodes = [_ | _], Fun, Timeout) when is_function(Fun, 0) -> | |||
rpc:multicall(Nodes, erlang, apply, [fun() -> {node(), Fun()} end, []], Timeout); | |||
named_rpc(Node, Fun, Timeout) when is_atom(Node) -> | |||
named_rpc([Node], Fun, Timeout). | |||
@ -1,726 +0,0 @@ | |||
%%% @author Fred Hebert <mononcqc@ferd.ca> | |||
%%% [http://ferd.ca/] | |||
%%% @author Lukas Larsson <lukas@erlang.org> | |||
%%% @doc Functions to deal with | |||
%%% <a href="http://www.erlang.org/doc/man/erts_alloc.html">Erlang's memory | |||
%%% allocators</a>, or particularly, to try to present the allocator data | |||
%%% in a way that makes it simpler to discover possible problems. | |||
%%% | |||
%%% Tweaking Erlang memory allocators and their behaviour is a very tricky | |||
%%% ordeal whenever you have to give up the default settings. This module | |||
%%% (and its documentation) will try and provide helpful pointers to help | |||
%%% in this task. | |||
%%% | |||
%%% This module should mostly be helpful to figure out <em>if</em> there is | |||
%%% a problem, but will offer little help to figure out <em>what</em> is wrong. | |||
%%% | |||
%%% To figure this out, you need to dig deeper into the allocator data | |||
%%% (obtainable with {@link allocators/0}), and/or have some precise knowledge | |||
%%% about the type of load and work done by the VM to be able to assess what | |||
%%% each reaction to individual tweak should be. | |||
%%% | |||
%%% A lot of trial and error might be required to figure out if tweaks have | |||
%%% helped or not, ultimately. | |||
%%% | |||
%%% In order to help do offline debugging of memory allocator problems | |||
%%% recon_alloc also has a few functions that store snapshots of the | |||
%%% memory statistics. | |||
%%% These snapshots can be used to freeze the current allocation values so that | |||
%%% they do not change during analysis while using the regular functionality of | |||
%%% this module, so that the allocator values can be saved, or that | |||
%%% they can be shared, dumped, and reloaded for further analysis using files. | |||
%%% See {@link snapshot_load/1} for a simple use-case. | |||
%%% | |||
%%% Glossary: | |||
%%% <dl> | |||
%%% <dt>sys_alloc</dt> | |||
%%% <dd>System allocator, usually just malloc</dd> | |||
%%% | |||
%%% <dt>mseg_alloc</dt> | |||
%%% <dd>Used by other allocators, can do mmap. Caches allocations</dd> | |||
%%% | |||
%%% <dt>temp_alloc</dt> | |||
%%% <dd>Used for temporary allocations</dd> | |||
%%% | |||
%%% <dt>eheap_alloc</dt> | |||
%%% <dd>Heap data (i.e. process heaps) allocator</dd> | |||
%%% | |||
%%% <dt>binary_alloc</dt> | |||
%%% <dd>Global binary heap allocator</dd> | |||
%%% | |||
%%% <dt>ets_alloc</dt> | |||
%%% <dd>ETS data allocator</dd> | |||
%%% | |||
%%% <dt>driver_alloc</dt> | |||
%%% <dd>Driver data allocator</dd> | |||
%%% | |||
%%% <dt>sl_alloc</dt> | |||
%%% <dd>Short-lived memory blocks allocator</dd> | |||
%%% | |||
%%% <dt>ll_alloc</dt> | |||
%%% <dd>Long-lived data (i.e. Erlang code itself) allocator</dd> | |||
%%% | |||
%%% <dt>fix_alloc</dt> | |||
%%% <dd>Frequently used fixed-size data allocator</dd> | |||
%%% | |||
%%% <dt>std_alloc</dt> | |||
%%% <dd>Allocator for other memory blocks</dd> | |||
%%% | |||
%%% <dt>carrier</dt> | |||
%%% <dd>When a given area of memory is allocated by the OS to the | |||
%%% VM (through sys_alloc or mseg_alloc), it is put into a 'carrier'. There | |||
%%% are two kinds of carriers: multiblock and single block. The default | |||
%%% carriers data is sent to are multiblock carriers, owned by a specific | |||
%%% allocator (ets_alloc, binary_alloc, etc.). The specific allocator can | |||
%%% thus do allocation for specific Erlang requirements within bits of | |||
%%% memory that has been preallocated before. This allows more reuse, | |||
%%% and we can even measure the cache hit rates {@link cache_hit_rates/0}. | |||
%%% | |||
%%% There is however a threshold above which an item in memory won't fit | |||
%%% a multiblock carrier. When that happens, the specific allocator does | |||
%%% a special allocation to a single block carrier. This is done by the | |||
%%% allocator basically asking for space directly from sys_alloc or | |||
%%% mseg_alloc rather than a previously multiblock area already obtained | |||
%%% before. | |||
%%% | |||
%%% This leads to various allocation strategies where you decide to | |||
%%% choose: | |||
%%% <ol> | |||
%%% <li>which multiblock carrier you're going to (if at all)</li> | |||
%%% <li>which block in that carrier you're going to</li> | |||
%%% </ol> | |||
%%% | |||
%%% See <a href="http://www.erlang.org/doc/man/erts_alloc.html">the official | |||
%%% documentation on erts_alloc</a> for more details. | |||
%%% </dd> | |||
%%% | |||
%%% <dt>mbcs</dt> | |||
%%% <dd>Multiblock carriers.</dd> | |||
%%% | |||
%%% <dt>sbcs</dt> | |||
%%% <dd>Single block carriers.</dd> | |||
%%% | |||
%%% <dt>lmbcs</dt> | |||
%%% <dd>Largest multiblock carrier size</dd> | |||
%%% | |||
%%% <dt>smbcs</dt> | |||
%%% <dd>Smallest multiblock carrier size</dd> | |||
%%% | |||
%%% <dt>sbct</dt> | |||
%%% <dd>Single block carrier threshold</dd> | |||
%%% </dl> | |||
%%% | |||
%%% By default all sizes returned by this module are in bytes. You can change | |||
%%% this by calling {@link set_unit/1}. | |||
%%% | |||
-module(recon_alloc). | |||
-define(UTIL_ALLOCATORS, [temp_alloc, | |||
eheap_alloc, | |||
binary_alloc, | |||
ets_alloc, | |||
driver_alloc, | |||
sl_alloc, | |||
ll_alloc, | |||
fix_alloc, | |||
std_alloc | |||
]). | |||
-type allocator() :: temp_alloc | eheap_alloc | binary_alloc | ets_alloc | |||
| driver_alloc | sl_alloc | ll_alloc | fix_alloc | |||
| std_alloc. | |||
-type instance() :: non_neg_integer(). | |||
-type allocdata(T) :: {{allocator(), instance()}, T}. | |||
-type allocdata_types(T) :: {{allocator(), [instance()]}, T}. | |||
-export_type([allocator/0, instance/0, allocdata/1]). | |||
-define(CURRENT_POS, 2). % pos in sizes tuples for current value | |||
-define(MAX_POS, 4). % pos in sizes tuples for max value | |||
-export([memory/1, memory/2, fragmentation/1, cache_hit_rates/0, | |||
average_block_sizes/1, sbcs_to_mbcs/1, allocators/0, | |||
allocators/1]). | |||
%% Snapshot handling | |||
-type memory() :: [{atom(), atom()}]. | |||
-type snapshot() :: {memory(), [allocdata(term())]}. | |||
-export_type([memory/0, snapshot/0]). | |||
-export([snapshot/0, snapshot_clear/0, | |||
snapshot_print/0, snapshot_get/0, | |||
snapshot_save/1, snapshot_load/1]). | |||
%% Unit handling | |||
-export([set_unit/1]). | |||
%%%%%%%%%%%%%% | |||
%%% Public %%% | |||
%%%%%%%%%%%%%% | |||
%% @doc Equivalent to `memory(Key, current)'. | |||
-spec memory(used | allocated | unused) -> pos_integer() | |||
; (usage) -> number() | |||
; (allocated_types | allocated_instances) -> | |||
[{allocator(), pos_integer()}]. | |||
memory(Key) -> memory(Key, current). | |||
%% @doc reports one of multiple possible memory values for the entire | |||
%% node depending on what is to be reported: | |||
%% | |||
%% <ul> | |||
%% <li>`used' reports the memory that is actively used for allocated | |||
%% Erlang data;</li> | |||
%% <li>`allocated' reports the memory that is reserved by the VM. It | |||
%% includes the memory used, but also the memory yet-to-be-used but still | |||
%% given by the OS. This is the amount you want if you're dealing with | |||
%% ulimit and OS-reported values. </li> | |||
%% <li>`allocated_types' report the memory that is reserved by the | |||
%% VM grouped into the different util allocators.</li> | |||
%% <li>`allocated_instances' report the memory that is reserved | |||
%% by the VM grouped into the different schedulers. Note that | |||
%% instance id 0 is the global allocator used to allocate data from | |||
%% non-managed threads, i.e. async and driver threads.</li> | |||
%% <li>`unused' reports the amount of memory reserved by the VM that | |||
%% is not being allocated. | |||
%% Equivalent to `allocated - used'.</li> | |||
%% <li>`usage' returns a percentage (0.0 .. 1.0) of `used/allocated' | |||
%% memory ratios.</li> | |||
%% </ul> | |||
%% | |||
%% The memory reported by `allocated' should roughly | |||
%% match what the OS reports. If this amount is different by a large margin, | |||
%% it may be the sign that someone is allocating memory in C directly, outside | |||
%% of Erlang's own allocator -- a big warning sign. There are currently | |||
%% three sources of memory alloction that are not counted towards this value: | |||
%% The cached segments in the mseg allocator, any memory allocated as a | |||
%% super carrier, and small pieces of memory allocated during startup | |||
%% before the memory allocators are initialized. | |||
%% | |||
%% Also note that low memory usages can be the sign of fragmentation in | |||
%% memory, in which case exploring which specific allocator is at fault | |||
%% is recommended (see {@link fragmentation/1}) | |||
-spec memory(used | allocated | unused, current | max) -> pos_integer() | |||
; (usage, current | max) -> number() | |||
; (allocated_types|allocated_instances, current | max) -> | |||
[{allocator(), pos_integer()}]. | |||
memory(used, Keyword) -> | |||
lists:sum(lists:map(fun({_, Prop}) -> | |||
container_size(Prop, Keyword, blocks_size) | |||
end, util_alloc())); | |||
memory(allocated, Keyword) -> | |||
lists:sum(lists:map(fun({_, Prop}) -> | |||
container_size(Prop, Keyword, carriers_size) | |||
end, util_alloc())); | |||
memory(allocated_types, Keyword) -> | |||
lists:foldl(fun({{Alloc, _N}, Props}, Acc) -> | |||
CZ = container_size(Props, Keyword, carriers_size), | |||
orddict:update_counter(Alloc, CZ, Acc) | |||
end, orddict:new(), util_alloc()); | |||
memory(allocated_instances, Keyword) -> | |||
lists:foldl(fun({{_Alloc, N}, Props}, Acc) -> | |||
CZ = container_size(Props, Keyword, carriers_size), | |||
orddict:update_counter(N, CZ, Acc) | |||
end, orddict:new(), util_alloc()); | |||
memory(unused, Keyword) -> | |||
memory(allocated, Keyword) - memory(used, Keyword); | |||
memory(usage, Keyword) -> | |||
memory(used, Keyword) / memory(allocated, Keyword). | |||
%% @doc Compares the block sizes to the carrier sizes, both for | |||
%% single block (`sbcs') and multiblock (`mbcs') carriers. | |||
%% | |||
%% The returned results are sorted by a weight system that is | |||
%% somewhat likely to return the most fragmented allocators first, | |||
%% based on their percentage of use and the total size of the carriers, | |||
%% for both `sbcs' and `mbcs'. | |||
%% | |||
%% The values can both be returned for `current' allocator values, and | |||
%% for `max' allocator values. The current values hold the present allocation | |||
%% numbers, and max values, the values at the peak. Comparing both together | |||
%% can give an idea of whether the node is currently being at its memory peak | |||
%% when possibly leaky, or if it isn't. This information can in turn | |||
%% influence the tuning of allocators to better fit sizes of blocks and/or | |||
%% carriers. | |||
-spec fragmentation(current | max) -> [allocdata([{atom(), term()}])]. | |||
fragmentation(Keyword) -> | |||
WeighedData = [begin | |||
BlockSbcs = container_value(Props, Keyword, sbcs, blocks_size), | |||
CarSbcs = container_value(Props, Keyword, sbcs, carriers_size), | |||
BlockMbcs = container_value(Props, Keyword, mbcs, blocks_size), | |||
CarMbcs = container_value(Props, Keyword, mbcs, carriers_size), | |||
{Weight, Vals} = weighed_values({BlockSbcs, CarSbcs}, | |||
{BlockMbcs, CarMbcs}), | |||
{Weight, {Allocator, N}, Vals} | |||
end || {{Allocator, N}, Props} <- util_alloc()], | |||
[{Key, Val} || {_W, Key, Val} <- lists:reverse(lists:sort(WeighedData))]. | |||
%% @doc looks at the `mseg_alloc' allocator (allocator used by all the | |||
%% allocators in {@link allocator()}) and returns information relative to | |||
%% the cache hit rates. Unless memory has expected spiky behaviour, it should | |||
%% usually be above 0.80 (80%). | |||
%% | |||
%% Cache can be tweaked using three VM flags: `+MMmcs', `+MMrmcbf', and | |||
%% `+MMamcbf'. | |||
%% | |||
%% `+MMmcs' stands for the maximum amount of cached memory segments. Its | |||
%% default value is '10' and can be anything from 0 to 30. Increasing | |||
%% it first and verifying if cache hits get better should be the first | |||
%% step taken. | |||
%% | |||
%% The two other options specify what are the maximal values of a segment | |||
%% to cache, in relative (in percent) and absolute terms (in kilobytes), | |||
%% respectively. Increasing these may allow more segments to be cached, but | |||
%% should also add overheads to memory allocation. An Erlang node that has | |||
%% limited memory and increases these values may make things worse on | |||
%% that point. | |||
%% | |||
%% The values returned by this function are sorted by a weight combining | |||
%% the lower cache hit joined to the largest memory values allocated. | |||
-spec cache_hit_rates() -> [{{instance, instance()}, [{Key, Val}]}] when | |||
Key :: hit_rate | hits | calls, | |||
Val :: term(). | |||
cache_hit_rates() -> | |||
WeighedData = [begin | |||
Mem = proplists:get_value(memkind, Props), | |||
{_, Hits} = lists:keyfind(cache_hits, 1, proplists:get_value(status, Mem)), | |||
{_, Giga, Ones} = lists:keyfind(mseg_alloc, 1, proplists:get_value(calls, Mem)), | |||
Calls = 1000000000 * Giga + Ones, | |||
HitRate = usage(Hits, Calls), | |||
Weight = (1.00 - HitRate) * Calls, | |||
{Weight, {instance, N}, [{hit_rate, HitRate}, {hits, Hits}, {calls, Calls}]} | |||
end || {{_, N}, Props} <- alloc([mseg_alloc])], | |||
[{Key, Val} || {_W, Key, Val} <- lists:reverse(lists:sort(WeighedData))]. | |||
%% @doc Checks all allocators in {@link allocator()} and returns the average | |||
%% block sizes being used for `mbcs' and `sbcs'. This value is interesting | |||
%% to use because it will tell us how large most blocks are. | |||
%% This can be related to the VM's largest multiblock carrier size | |||
%% (`lmbcs') and smallest multiblock carrier size (`smbcs') to specify | |||
%% allocation strategies regarding the carrier sizes to be used. | |||
%% | |||
%% This function isn't exceptionally useful unless you know you have some | |||
%% specific problem, say with sbcs/mbcs ratios (see {@link sbcs_to_mbcs/0}) | |||
%% or fragmentation for a specific allocator, and want to figure out what | |||
%% values to pick to increase or decrease sizes compared to the currently | |||
%% configured value. | |||
%% | |||
%% Do note that values for `lmbcs' and `smbcs' are going to be rounded up | |||
%% to the next power of two when configuring them. | |||
-spec average_block_sizes(current | max) -> [{allocator(), [{Key, Val}]}] when | |||
Key :: mbcs | sbcs, | |||
Val :: number(). | |||
average_block_sizes(Keyword) -> | |||
Dict = lists:foldl(fun({{Instance, _}, Props}, Dict0) -> | |||
CarSbcs = container_value(Props, Keyword, sbcs, blocks), | |||
SizeSbcs = container_value(Props, Keyword, sbcs, blocks_size), | |||
CarMbcs = container_value(Props, Keyword, mbcs, blocks), | |||
SizeMbcs = container_value(Props, Keyword, mbcs, blocks_size), | |||
Dict1 = dict:update_counter({Instance, sbcs, count}, CarSbcs, Dict0), | |||
Dict2 = dict:update_counter({Instance, sbcs, size}, SizeSbcs, Dict1), | |||
Dict3 = dict:update_counter({Instance, mbcs, count}, CarMbcs, Dict2), | |||
Dict4 = dict:update_counter({Instance, mbcs, size}, SizeMbcs, Dict3), | |||
Dict4 | |||
end, | |||
dict:new(), | |||
util_alloc()), | |||
average_group(average_calc(lists:sort(dict:to_list(Dict)))). | |||
%% @doc compares the amount of single block carriers (`sbcs') vs the | |||
%% number of multiblock carriers (`mbcs') for each individual allocator in | |||
%% {@link allocator()}. | |||
%% | |||
%% When a specific piece of data is allocated, it is compared to a threshold, | |||
%% called the 'single block carrier threshold' (`sbct'). When the data is | |||
%% larger than the `sbct', it gets sent to a single block carrier. When the | |||
%% data is smaller than the `sbct', it gets placed into a multiblock carrier. | |||
%% | |||
%% mbcs are to be prefered to sbcs because they basically represent pre- | |||
%% allocated memory, whereas sbcs will map to one call to sys_alloc | |||
%% or mseg_alloc, which is more expensive than redistributing | |||
%% data that was obtained for multiblock carriers. Moreover, the VM is able to | |||
%% do specific work with mbcs that should help reduce fragmentation in ways | |||
%% sys_alloc or mmap usually won't. | |||
%% | |||
%% Ideally, most of the data should fit inside multiblock carriers. If | |||
%% most of the data ends up in `sbcs', you may need to adjust the multiblock | |||
%% carrier sizes, specifically the maximal value (`lmbcs') and the threshold | |||
%% (`sbct'). On 32 bit VMs, `sbct' is limited to 8MBs, but 64 bit VMs can go | |||
%% to pretty much any practical size. | |||
%% | |||
%% Given the value returned is a ratio of sbcs/mbcs, the higher the value, | |||
%% the worst the condition. The list is sorted accordingly. | |||
-spec sbcs_to_mbcs(max | current) -> [allocdata(term())]. | |||
sbcs_to_mbcs(Keyword) -> | |||
WeightedList = [begin | |||
Sbcs = container_value(Props, Keyword, sbcs, blocks), | |||
Mbcs = container_value(Props, Keyword, mbcs, blocks), | |||
Ratio = case {Sbcs, Mbcs} of | |||
{0, 0} -> 0; | |||
{_, 0} -> infinity; % that is bad! | |||
{_, _} -> Sbcs / Mbcs | |||
end, | |||
{Ratio, {Allocator, N}} | |||
end || {{Allocator, N}, Props} <- util_alloc()], | |||
[{Alloc, Ratio} || {Ratio, Alloc} <- lists:reverse(lists:sort(WeightedList))]. | |||
%% @doc returns a dump of all allocator settings and values | |||
-spec allocators() -> [allocdata(term())]. | |||
allocators() -> | |||
UtilAllocators = erlang:system_info(alloc_util_allocators), | |||
Allocators = [sys_alloc, mseg_alloc | UtilAllocators], | |||
%% versions is deleted in order to allow the use of the orddict api, | |||
%% and never really having come across a case where it was useful to know. | |||
[{{A, N}, lists:sort(proplists:delete(versions, Props))} || | |||
A <- Allocators, | |||
Allocs <- [erlang:system_info({allocator, A})], | |||
Allocs =/= false, | |||
{_, N, Props} <- Allocs]. | |||
%% @doc returns a dump of all allocator settings and values modified | |||
%% depending on the argument. | |||
%% <ul> | |||
%% <li>`types` report the settings and accumulated values for each | |||
%% allocator type. This is useful when looking for anomalies | |||
%% in the system as a whole and not specific instances.</li> | |||
%% </ul> | |||
-spec allocators(types) -> [allocdata_types(term())]. | |||
allocators(types) -> | |||
allocators_types(alloc(), []). | |||
allocators_types([{{Type, No}, Vs} | T], As) -> | |||
case lists:keytake(Type, 1, As) of | |||
false -> | |||
allocators_types(T, [{Type, [No], sort_values(Type, Vs)} | As]); | |||
{value, {Type, Nos, OVs}, NAs} -> | |||
MergedValues = merge_values(sort_values(Type, Vs), OVs), | |||
allocators_types(T, [{Type, [No | Nos], MergedValues} | NAs]) | |||
end; | |||
allocators_types([], As) -> | |||
[{{Type, Nos}, Vs} || {Type, Nos, Vs} <- As]. | |||
merge_values([{Key, Vs} | T1], [{Key, OVs} | T2]) when Key =:= memkind -> | |||
[{Key, merge_values(Vs, OVs)} | merge_values(T1, T2)]; | |||
merge_values([{Key, Vs} | T1], [{Key, OVs} | T2]) when Key =:= calls; | |||
Key =:= fix_types; | |||
Key =:= sbmbcs; | |||
Key =:= mbcs; | |||
Key =:= mbcs_pool; | |||
Key =:= sbcs; | |||
Key =:= status -> | |||
[{Key, lists:map( | |||
fun({{K, MV1, V1}, {K, MV2, V2}}) -> | |||
%% Merge the MegaVs + Vs into one | |||
V = MV1 * 1000000 + V1 + MV2 * 1000000 + V2, | |||
{K, V div 1000000, V rem 1000000}; | |||
({{K, V1}, {K, V2}}) when K =:= segments_watermark -> | |||
%% We take the maximum watermark as that is | |||
%% a value that we can use somewhat. Ideally | |||
%% maybe the average should be used, but the | |||
%% value is very rarely important so leave it | |||
%% like this for now. | |||
{K, lists:max([V1, V2])}; | |||
({{K, V1}, {K, V2}}) -> | |||
{K, V1 + V2}; | |||
({{K, C1, L1, M1}, {K, C2, L2, M2}}) -> | |||
%% Merge the Curr, Last, Max into one | |||
{K, C1 + C2, L1 + L2, M1 + M2} | |||
end, lists:zip(Vs, OVs))} | merge_values(T1, T2)]; | |||
merge_values([{Type, _Vs} = E | T1], T2) when Type =:= mbcs_pool -> | |||
%% For values never showing up in instance 0 but in all other | |||
[E | merge_values(T1, T2)]; | |||
merge_values(T1, [{Type, _Vs} = E | T2]) when Type =:= fix_types -> | |||
%% For values only showing up in instance 0 | |||
[E | merge_values(T1, T2)]; | |||
merge_values([E | T1], [E | T2]) -> | |||
%% For values that are constant | |||
[E | merge_values(T1, T2)]; | |||
merge_values([{options, _Vs1} | T1], [{options, _Vs2} = E | T2]) -> | |||
%% Options change a but in between instance 0 and the other, | |||
%% We show the others as they are the most interesting. | |||
[E | merge_values(T1, T2)]; | |||
merge_values([], []) -> | |||
[]. | |||
sort_values(mseg_alloc, Vs) -> | |||
{value, {memkind, MemKindVs}, OVs} = lists:keytake(memkind, 1, Vs), | |||
lists:sort([{memkind, lists:sort(MemKindVs)} | OVs]); | |||
sort_values(_Type, Vs) -> | |||
lists:sort(Vs). | |||
%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%%% Snapshot handling %%% | |||
%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%% @doc Take a new snapshot of the current memory allocator statistics. | |||
%% The snapshot is stored in the process dictionary of the calling process, | |||
%% with all the limitations that it implies (i.e. no garbage-collection). | |||
%% To unsert the snapshot, see {@link snapshot_clear/1}. | |||
-spec snapshot() -> snapshot() | undefined. | |||
snapshot() -> | |||
put(recon_alloc_snapshot, snapshot_int()). | |||
%% @doc clear the current snapshot in the process dictionary, if present, | |||
%% and return the value it had before being unset. | |||
%% @end | |||
%% Maybe we should use erlang:delete(Key) here instead? | |||
-spec snapshot_clear() -> snapshot() | undefined. | |||
snapshot_clear() -> | |||
put(recon_alloc_snapshot, undefined). | |||
%% @doc print a dump of the current snapshot stored by {@link snapshot/0} | |||
%% Prints `undefined' if no snapshot has been taken. | |||
-spec snapshot_print() -> ok. | |||
snapshot_print() -> | |||
io:format("~p.~n", [snapshot_get()]). | |||
%% @doc returns the current snapshot stored by {@link snapshot/0}. | |||
%% Returns `undefined' if no snapshot has been taken. | |||
-spec snapshot_get() -> snapshot() | undefined. | |||
snapshot_get() -> | |||
get(recon_alloc_snapshot). | |||
%% @doc save the current snapshot taken by {@link snapshot/0} to a file. | |||
%% If there is no current snapshot, a snaphot of the current allocator | |||
%% statistics will be written to the file. | |||
-spec snapshot_save(Filename) -> ok when | |||
Filename :: file:name(). | |||
snapshot_save(Filename) -> | |||
Snapshot = case snapshot_get() of | |||
undefined -> | |||
snapshot_int(); | |||
Snap -> | |||
Snap | |||
end, | |||
case file:write_file(Filename, io_lib:format("~p.~n", [Snapshot])) of | |||
ok -> ok; | |||
{error, Reason} -> | |||
erlang:error(Reason, [Filename]) | |||
end. | |||
%% @doc load a snapshot from a given file. The format of the data in the | |||
%% file can be either the same as output by {@link snapshot_save()}, | |||
%% or the output obtained by calling | |||
%% `{erlang:memory(),[{A,erlang:system_info({allocator,A})} || A <- erlang:system_info(alloc_util_allocators)++[sys_alloc,mseg_alloc]]}.' | |||
%% and storing it in a file. | |||
%% If the latter option is taken, please remember to add a full stop at the end | |||
%% of the resulting Erlang term, as this function uses `file:consult/1' to load | |||
%% the file. | |||
%% | |||
%% Example usage: | |||
%% | |||
%%```On target machine: | |||
%% 1> recon_alloc:snapshot(). | |||
%% undefined | |||
%% 2> recon_alloc:memory(used). | |||
%% 18411064 | |||
%% 3> recon_alloc:snapshot_save("recon_snapshot.terms"). | |||
%% ok | |||
%% | |||
%% On other machine: | |||
%% 1> recon_alloc:snapshot_load("recon_snapshot.terms"). | |||
%% undefined | |||
%% 2> recon_alloc:memory(used). | |||
%% 18411064''' | |||
%% | |||
-spec snapshot_load(Filename) -> snapshot() | undefined when | |||
Filename :: file:name(). | |||
snapshot_load(Filename) -> | |||
{ok, [Terms]} = file:consult(Filename), | |||
Snapshot = | |||
case Terms of | |||
%% We handle someone using | |||
%% {erlang:memory(), | |||
%% [{A,erlang:system_info({allocator,A})} || | |||
%% A <- erlang:system_info(alloc_util_allocators)++[sys_alloc,mseg_alloc]]} | |||
%% to dump data. | |||
{M, [{Alloc, _D} | _] = Allocs} when is_atom(Alloc) -> | |||
{M, [{{A, N}, lists:sort(proplists:delete(versions, Props))} || | |||
{A, Instances = [_ | _]} <- Allocs, | |||
{_, N, Props} <- Instances]}; | |||
%% We assume someone used recon_alloc:snapshot() to store this one | |||
{M, Allocs} -> | |||
{M, [{AN, lists:sort(proplists:delete(versions, Props))} || | |||
{AN, Props} <- Allocs]} | |||
end, | |||
put(recon_alloc_snapshot, Snapshot). | |||
%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%%% Handling of units %%% | |||
%%%%%%%%%%%%%%%%%%%%%%%%% | |||
%% @doc set the current unit to be used by recon_alloc. This effects all | |||
%% functions that return bytes. | |||
%% | |||
%% Eg. | |||
%% ```1> recon_alloc:memory(used,current). | |||
%% 17548752 | |||
%% 2> recon_alloc:set_unit(kilobyte). | |||
%% undefined | |||
%% 3> recon_alloc:memory(used,current). | |||
%% 17576.90625''' | |||
%% | |||
-spec set_unit(byte | kilobyte | megabyte | gigabyte) -> ok. | |||
set_unit(byte) -> | |||
put(recon_alloc_unit, undefined); | |||
set_unit(kilobyte) -> | |||
put(recon_alloc_unit, 1024); | |||
set_unit(megabyte) -> | |||
put(recon_alloc_unit, 1024 * 1024); | |||
set_unit(gigabyte) -> | |||
put(recon_alloc_unit, 1024 * 1024 * 1024). | |||
conv({Mem, Allocs} = D) -> | |||
case get(recon_alloc_unit) of | |||
undefined -> | |||
D; | |||
Factor -> | |||
{conv_mem(Mem, Factor), conv_alloc(Allocs, Factor)} | |||
end. | |||
conv_mem(Mem, Factor) -> | |||
[{T, M / Factor} || {T, M} <- Mem]. | |||
conv_alloc([{{sys_alloc, _I}, _Props} = Alloc | R], Factor) -> | |||
[Alloc | conv_alloc(R, Factor)]; | |||
conv_alloc([{{mseg_alloc, _I} = AI, Props} | R], Factor) -> | |||
MemKind = orddict:fetch(memkind, Props), | |||
Status = orddict:fetch(status, MemKind), | |||
{segments_size, Curr, Last, Max} = lists:keyfind(segments_size, 1, Status), | |||
NewSegSize = {segments_size, Curr / Factor, Last / Factor, Max / Factor}, | |||
NewStatus = lists:keyreplace(segments_size, 1, Status, NewSegSize), | |||
NewProps = orddict:store(memkind, orddict:store(status, NewStatus, MemKind), | |||
Props), | |||
[{AI, NewProps} | conv_alloc(R, Factor)]; | |||
conv_alloc([{AI, Props} | R], Factor) -> | |||
FactorFun = fun({T, Curr}) when | |||
T =:= blocks_size; T =:= carriers_size -> | |||
{T, Curr / Factor}; | |||
({T, Curr, Last, Max}) when | |||
T =:= blocks_size; T =:= carriers_size; | |||
T =:= mseg_alloc_carriers_size; | |||
T =:= sys_alloc_carriers_size -> | |||
{T, Curr / Factor, Last / Factor, Max / Factor}; | |||
(T) -> | |||
T | |||
end, | |||
NewMbcsProp = [FactorFun(Prop) || Prop <- orddict:fetch(mbcs, Props)], | |||
NewSbcsProp = [FactorFun(Prop) || Prop <- orddict:fetch(sbcs, Props)], | |||
NewProps = orddict:store(sbcs, NewSbcsProp, | |||
orddict:store(mbcs, NewMbcsProp, Props)), | |||
case orddict:find(mbcs_pool, Props) of | |||
error -> | |||
[{AI, NewProps} | conv_alloc(R, Factor)]; | |||
{ok, MbcsPoolProps} -> | |||
NewMbcsPoolProp = [FactorFun(Prop) || Prop <- MbcsPoolProps], | |||
NewPoolProps = orddict:store(mbcs_pool, NewMbcsPoolProp, NewProps), | |||
[{AI, NewPoolProps} | conv_alloc(R, Factor)] | |||
end; | |||
conv_alloc([], _Factor) -> | |||
[]. | |||
%%%%%%%%%%%%%%% | |||
%%% Private %%% | |||
%%%%%%%%%%%%%%% | |||
%% Sort on small usage vs large size. | |||
%% The weight cares about both the sbcs and mbcs values, and also | |||
%% returns a proplist of possibly interesting values. | |||
weighed_values({SbcsBlockSize, SbcsCarrierSize}, | |||
{MbcsBlockSize, MbcsCarrierSize}) -> | |||
SbcsUsage = usage(SbcsBlockSize, SbcsCarrierSize), | |||
MbcsUsage = usage(MbcsBlockSize, MbcsCarrierSize), | |||
SbcsWeight = (1.00 - SbcsUsage) * SbcsCarrierSize, | |||
MbcsWeight = (1.00 - MbcsUsage) * MbcsCarrierSize, | |||
Weight = SbcsWeight + MbcsWeight, | |||
{Weight, [{sbcs_usage, SbcsUsage}, | |||
{mbcs_usage, MbcsUsage}, | |||
{sbcs_block_size, SbcsBlockSize}, | |||
{sbcs_carriers_size, SbcsCarrierSize}, | |||
{mbcs_block_size, MbcsBlockSize}, | |||
{mbcs_carriers_size, MbcsCarrierSize}]}. | |||
%% Returns the `BlockSize/CarrierSize' as a 0.0 -> 1.0 percentage, | |||
%% but also takes 0/0 to be 100% to make working with sorting and | |||
%% weights simpler. | |||
usage(0, 0) -> 1.00; | |||
usage(0.0, 0.0) -> 1.00; | |||
%usage(N,0) -> ???; | |||
usage(Block, Carrier) -> Block / Carrier. | |||
%% Calculation for the average of blocks being used. | |||
average_calc([]) -> | |||
[]; | |||
average_calc([{{Instance, Type, count}, Ct}, {{Instance, Type, size}, Size} | Rest]) -> | |||
case {Size, Ct} of | |||
{_, 0} when Size == 0 -> [{Instance, Type, 0} | average_calc(Rest)]; | |||
_ -> [{Instance, Type, Size / Ct} | average_calc(Rest)] | |||
end. | |||
%% Regrouping/merging values together in proplists | |||
average_group([]) -> []; | |||
average_group([{Instance, Type1, N}, {Instance, Type2, M} | Rest]) -> | |||
[{Instance, [{Type1, N}, {Type2, M}]} | average_group(Rest)]. | |||
%% Get the total carrier size | |||
container_size(Props, Keyword, Container) -> | |||
Sbcs = container_value(Props, Keyword, sbcs, Container), | |||
Mbcs = container_value(Props, Keyword, mbcs, Container), | |||
Sbcs + Mbcs. | |||
container_value(Props, Keyword, Type, Container) | |||
when is_atom(Keyword) -> | |||
container_value(Props, key2pos(Keyword), Type, Container); | |||
container_value(Props, Pos, mbcs = Type, Container) | |||
when Pos == ?CURRENT_POS, | |||
((Container =:= blocks) or (Container =:= blocks_size) | |||
or (Container =:= carriers) or (Container =:= carriers_size)) -> | |||
%% We include the mbcs_pool into the value for mbcs. | |||
%% The mbcs_pool contains carriers that have been abandoned | |||
%% by the specific allocator instance and can therefore be | |||
%% grabbed by another instance of the same type. | |||
%% The pool was added in R16B02 and enabled by default in 17.0. | |||
%% See erts/emulator/internal_docs/CarrierMigration.md in | |||
%% Erlang/OTP repo for more details. | |||
Pool = case proplists:get_value(mbcs_pool, Props) of | |||
PoolProps when PoolProps =/= undefined -> | |||
element(Pos, lists:keyfind(Container, 1, PoolProps)); | |||
_ -> 0 | |||
end, | |||
TypeProps = proplists:get_value(Type, Props), | |||
Pool + element(Pos, lists:keyfind(Container, 1, TypeProps)); | |||
container_value(Props, Pos, Type, Container) | |||
when Type =:= sbcs; Type =:= mbcs -> | |||
TypeProps = proplists:get_value(Type, Props), | |||
element(Pos, lists:keyfind(Container, 1, TypeProps)). | |||
%% Create a new snapshot | |||
snapshot_int() -> | |||
{erlang:memory(), allocators()}. | |||
%% If no snapshot has been taken/loaded then we use current values | |||
snapshot_get_int() -> | |||
case snapshot_get() of | |||
undefined -> | |||
conv(snapshot_int()); | |||
Snapshot -> | |||
conv(Snapshot) | |||
end. | |||
%% Get the alloc part of a snapshot | |||
alloc() -> | |||
{_Mem, Allocs} = snapshot_get_int(), | |||
Allocs. | |||
alloc(Type) -> | |||
[{{T, Instance}, Props} || {{T, Instance}, Props} <- alloc(), | |||
lists:member(T, Type)]. | |||
%% Get only alloc_util allocs | |||
util_alloc() -> | |||
alloc(?UTIL_ALLOCATORS). | |||
key2pos(current) -> | |||
?CURRENT_POS; | |||
key2pos(max) -> | |||
?MAX_POS. |
@ -1,278 +0,0 @@ | |||
%%% @author Fred Hebert <mononcqc@ferd.ca> | |||
%%% [http://ferd.ca/] | |||
%%% @doc Regroups useful functionality used by recon when dealing with data | |||
%%% from the node. The functions in this module allow quick runtime access | |||
%%% to fancier behaviour than what would be done using recon module itself. | |||
%%% @end | |||
-module(recon_lib). | |||
-export([sliding_window/2, sample/2, count/1, | |||
port_list/1, port_list/2, | |||
proc_attrs/1, proc_attrs/2, | |||
inet_attrs/1, inet_attrs/2, | |||
triple_to_pid/3, term_to_pid/1, | |||
term_to_port/1, | |||
time_map/5, time_fold/6, | |||
scheduler_usage_diff/2, | |||
sublist_top_n_attrs/2]). | |||
%% private exports | |||
-export([binary_memory/1]). | |||
-type diff() :: [recon:proc_attrs() | recon:inet_attrs()]. | |||
%% @doc Compare two samples and return a list based on some key. The type mentioned | |||
%% for the structure is `diff()' (`{Key,Val,Other}'), which is compatible with | |||
%% the {@link recon:proc_attrs()} type. | |||
-spec sliding_window(First :: diff(), Last :: diff()) -> diff(). | |||
sliding_window(First, Last) -> | |||
Dict = lists:foldl( | |||
fun({Key, {Current, Other}}, Acc) -> | |||
dict:update(Key, | |||
fun({Old, _Other}) -> {Current - Old, Other} end, | |||
{Current, Other}, | |||
Acc) | |||
end, | |||
dict:from_list([{K, {V, O}} || {K, V, O} <- First]), | |||
[{K, {V, O}} || {K, V, O} <- Last] | |||
), | |||
[{K, V, O} || {K, {V, O}} <- dict:to_list(Dict)]. | |||
%% @doc Runs a fun once, waits `Ms', runs the fun again, | |||
%% and returns both results. | |||
-spec sample(Ms :: non_neg_integer(), fun(() -> term())) -> | |||
{First :: term(), Second :: term()}. | |||
sample(Delay, Fun) -> | |||
First = Fun(), | |||
timer:sleep(Delay), | |||
Second = Fun(), | |||
{First, Second}. | |||
%% @doc Takes a list of terms, and counts how often each of | |||
%% them appears in the list. The list returned is in no | |||
%% particular order. | |||
-spec count([term()]) -> [{term(), Count :: integer()}]. | |||
count(Terms) -> | |||
Dict = lists:foldl( | |||
fun(Val, Acc) -> dict:update_counter(Val, 1, Acc) end, | |||
dict:new(), | |||
Terms | |||
), | |||
dict:to_list(Dict). | |||
%% @doc Returns a list of all the open ports in the VM, coupled with | |||
%% one of the properties desired from `erlang:port_info/1-2'. | |||
-spec port_list(Attr :: atom()) -> [{port(), term()}]. | |||
port_list(Attr) -> | |||
[{Port, Val} || Port <- erlang:ports(), | |||
{_, Val} <- [erlang:port_info(Port, Attr)]]. | |||
%% @doc Returns a list of all the open ports in the VM, but only | |||
%% if the `Attr''s resulting value matches `Val'. `Attr' must be | |||
%% a property accepted by `erlang:port_info/2'. | |||
-spec port_list(Attr :: atom(), term()) -> [port()]. | |||
port_list(Attr, Val) -> | |||
[Port || Port <- erlang:ports(), | |||
{Attr, Val} =:= erlang:port_info(Port, Attr)]. | |||
%% @doc Returns the attributes ({@link recon:proc_attrs()}) of | |||
%% all processes of the node, except the caller. | |||
-spec proc_attrs(term()) -> [recon:proc_attrs()]. | |||
proc_attrs(AttrName) -> | |||
[Attrs || Pid <- processes() -- [self()], | |||
{ok, Attrs} <- [proc_attrs(AttrName, Pid)]]. | |||
%% @doc Returns the attributes of a given process. This form of attributes | |||
%% is standard for most comparison functions for processes in recon. | |||
%% | |||
%% A special attribute is `binary_memory', which will reduce the memory used | |||
%% by the process for binary data on the global heap. | |||
-spec proc_attrs(term(), pid()) -> {ok, recon:proc_attrs()} | {error, term()}. | |||
proc_attrs(binary_memory, Pid) -> | |||
case process_info(Pid, [binary, registered_name, | |||
current_function, initial_call]) of | |||
[{_, Bins}, {registered_name, Name}, Init, Cur] -> | |||
{ok, {Pid, binary_memory(Bins), [Name || is_atom(Name)] ++ [Init, Cur]}}; | |||
undefined -> | |||
{error, undefined} | |||
end; | |||
proc_attrs(AttrName, Pid) -> | |||
case process_info(Pid, [AttrName, registered_name, | |||
current_function, initial_call]) of | |||
[{_, Attr}, {registered_name, Name}, Init, Cur] -> | |||
{ok, {Pid, Attr, [Name || is_atom(Name)] ++ [Init, Cur]}}; | |||
undefined -> | |||
{error, undefined} | |||
end. | |||
%% @doc Returns the attributes ({@link recon:inet_attrs()}) of | |||
%% all inet ports (UDP, SCTP, TCP) of the node. | |||
-spec inet_attrs(term()) -> [recon:inet_attrs()]. | |||
inet_attrs(AttrName) -> | |||
Ports = [Port || Port <- erlang:ports(), | |||
{_, Name} <- [erlang:port_info(Port, name)], | |||
Name =:= "tcp_inet" orelse | |||
Name =:= "udp_inet" orelse | |||
Name =:= "sctp_inet"], | |||
[Attrs || Port <- Ports, | |||
{ok, Attrs} <- [inet_attrs(AttrName, Port)]]. | |||
%% @doc Returns the attributes required for a given inet port (UDP, | |||
%% SCTP, TCP). This form of attributes is standard for most comparison | |||
%% functions for processes in recon. | |||
-spec inet_attrs(AttributeName, port()) -> {ok, recon:inet_attrs()} | |||
| {error, term()} when | |||
AttributeName :: 'recv_cnt' | 'recv_oct' | 'send_cnt' | 'send_oct' | |||
| 'cnt' | 'oct'. | |||
inet_attrs(Attr, Port) -> | |||
Attrs = case Attr of | |||
cnt -> [recv_cnt, send_cnt]; | |||
oct -> [recv_oct, send_oct]; | |||
_ -> [Attr] | |||
end, | |||
case inet:getstat(Port, Attrs) of | |||
{ok, Props} -> | |||
ValSum = lists:foldl(fun({_, X}, Y) -> X + Y end, 0, Props), | |||
{ok, {Port, ValSum, Props}}; | |||
{error, Reason} -> | |||
{error, Reason} | |||
end. | |||
%% @doc Equivalent of `pid(X,Y,Z)' in the Erlang shell. | |||
-spec triple_to_pid(N, N, N) -> pid() when | |||
N :: non_neg_integer(). | |||
triple_to_pid(X, Y, Z) -> | |||
list_to_pid("<" ++ integer_to_list(X) ++ "." ++ | |||
integer_to_list(Y) ++ "." ++ | |||
integer_to_list(Z) ++ ">"). | |||
%% @doc Transforms a given term to a pid. | |||
-spec term_to_pid(recon:pid_term()) -> pid(). | |||
term_to_pid(Pid) when is_pid(Pid) -> Pid; | |||
term_to_pid(Name) when is_atom(Name) -> whereis(Name); | |||
term_to_pid(List = "<0." ++ _) -> list_to_pid(List); | |||
term_to_pid(Binary = <<"<0.", _/binary>>) -> list_to_pid(binary_to_list(Binary)); | |||
term_to_pid({global, Name}) -> global:whereis_name(Name); | |||
term_to_pid({via, Module, Name}) -> Module:whereis_name(Name); | |||
term_to_pid({X, Y, Z}) when is_integer(X), is_integer(Y), is_integer(Z) -> | |||
triple_to_pid(X, Y, Z). | |||
%% @doc Transforms a given term to a port | |||
-spec term_to_port(recon:port_term()) -> port(). | |||
term_to_port(Port) when is_port(Port) -> Port; | |||
term_to_port(Name) when is_atom(Name) -> whereis(Name); | |||
term_to_port("#Port<0." ++ Id) -> | |||
N = list_to_integer(lists:sublist(Id, length(Id) - 1)), % drop trailing '>' | |||
term_to_port(N); | |||
term_to_port(N) when is_integer(N) -> | |||
%% We rebuild the term from the int received: | |||
%% http://www.erlang.org/doc/apps/erts/erl_ext_dist.html#id86892 | |||
Name = iolist_to_binary(atom_to_list(node())), | |||
NameLen = iolist_size(Name), | |||
Vsn = binary:last(term_to_binary(self())), | |||
Bin = <<131, % term encoding value | |||
102, % port tag | |||
100, % atom ext tag, used for node name | |||
NameLen:2/unit:8, | |||
Name:NameLen/binary, | |||
N:4/unit:8, % actual counter value | |||
Vsn:8>>, % version | |||
binary_to_term(Bin). | |||
%% @doc Calls a given function every `Interval' milliseconds and supports | |||
%% a map-like interface (each result is modified and returned) | |||
-spec time_map(N, Interval, Fun, State, MapFun) -> [term()] when | |||
N :: non_neg_integer(), | |||
Interval :: pos_integer(), | |||
Fun :: fun((State) -> {term(), State}), | |||
State :: term(), | |||
MapFun :: fun((_) -> term()). | |||
time_map(0, _, _, _, _) -> | |||
[]; | |||
time_map(N, Interval, Fun, State, MapFun) -> | |||
{Res, NewState} = Fun(State), | |||
timer:sleep(Interval), | |||
[MapFun(Res) | time_map(N - 1, Interval, Fun, NewState, MapFun)]. | |||
%% @doc Calls a given function every `Interval' milliseconds and supports | |||
%% a fold-like interface (each result is modified and accumulated) | |||
-spec time_fold(N, Interval, Fun, State, FoldFun, Init) -> [term()] when | |||
N :: non_neg_integer(), | |||
Interval :: pos_integer(), | |||
Fun :: fun((State) -> {term(), State}), | |||
State :: term(), | |||
FoldFun :: fun((term(), Init) -> Init), | |||
Init :: term(). | |||
time_fold(0, _, _, _, _, Acc) -> | |||
Acc; | |||
time_fold(N, Interval, Fun, State, FoldFun, Init) -> | |||
{Res, NewState} = Fun(State), | |||
timer:sleep(Interval), | |||
Acc = FoldFun(Res, Init), | |||
time_fold(N - 1, Interval, Fun, NewState, FoldFun, Acc). | |||
%% @doc Diffs two runs of erlang:statistics(scheduler_wall_time) and | |||
%% returns usage metrics in terms of cores and 0..1 percentages. | |||
-spec scheduler_usage_diff(SchedTime, SchedTime) -> [{SchedulerId, Usage}] when | |||
SchedTime :: [{SchedulerId, ActiveTime, TotalTime}], | |||
SchedulerId :: pos_integer(), | |||
Usage :: number(), | |||
ActiveTime :: non_neg_integer(), | |||
TotalTime :: non_neg_integer(). | |||
scheduler_usage_diff(First, Last) -> | |||
lists:map( | |||
fun({{I, _A0, T}, {I, _A1, T}}) -> {I, 0.0}; % Avoid divide by zero | |||
({{I, A0, T0}, {I, A1, T1}}) -> {I, (A1 - A0) / (T1 - T0)} | |||
end, | |||
lists:zip(lists:sort(First), lists:sort(Last)) | |||
). | |||
%% @doc Returns the top n element of a list of process or inet attributes | |||
-spec sublist_top_n_attrs([Attrs], pos_integer()) -> [Attrs] | |||
when Attrs :: recon:proc_attrs() | recon:inet_attrs(). | |||
sublist_top_n_attrs(_, 0) -> | |||
%% matching lists:sublist/2 behaviour | |||
[]; | |||
sublist_top_n_attrs(List, Len) -> | |||
pheap_fill(List, Len, []). | |||
%% @private crush binaries from process_info into their amount of place | |||
%% taken in memory. | |||
binary_memory(Bins) -> | |||
lists:foldl(fun({_, Mem, _}, Tot) -> Mem + Tot end, 0, Bins). | |||
%%%%%%%%%%%%%%% | |||
%%% PRIVATE %%% | |||
%%%%%%%%%%%%%%% | |||
pheap_fill(List, 0, Heap) -> | |||
pheap_full(List, Heap); | |||
pheap_fill([], _, Heap) -> | |||
pheap_to_list(Heap, []); | |||
pheap_fill([{Y, X, _} = H | T], N, Heap) -> | |||
pheap_fill(T, N - 1, insert({{X, Y}, H}, Heap)). | |||
pheap_full([], Heap) -> | |||
pheap_to_list(Heap, []); | |||
pheap_full([{Y, X, _} = H | T], [{K, _} | HeapT] = Heap) -> | |||
case {X, Y} of | |||
N when N > K -> | |||
pheap_full(T, insert({N, H}, merge_pairs(HeapT))); | |||
_ -> | |||
pheap_full(T, Heap) | |||
end. | |||
pheap_to_list([], Acc) -> Acc; | |||
pheap_to_list([{_, H} | T], Acc) -> | |||
pheap_to_list(merge_pairs(T), [H | Acc]). | |||
-compile({inline, [insert/2, merge/2]}). | |||
insert(E, []) -> [E]; %% merge([E], H) | |||
insert(E, [E2 | _] = H) when E =< E2 -> [E, H]; | |||
insert(E, [E2 | H]) -> [E2, [E] | H]. | |||
merge(H1, []) -> H1; | |||
merge([E1 | H1], [E2 | _] = H2) when E1 =< E2 -> [E1, H2 | H1]; | |||
merge(H1, [E2 | H2]) -> [E2, H1 | H2]. | |||
merge_pairs([]) -> []; | |||
merge_pairs([H]) -> H; | |||
merge_pairs([A, B | T]) -> merge(merge(A, B), merge_pairs(T)). |
@ -1,644 +0,0 @@ | |||
%%% @author Fred Hebert <mononcqc@ferd.ca> | |||
%%% [http://ferd.ca/] | |||
%%% @doc | |||
%%% `recon_trace' is a module that handles tracing in a safe manner for single | |||
%%% Erlang nodes, currently for function calls only. Functionality includes: | |||
%%% | |||
%%% <ul> | |||
%%% <li>Nicer to use interface (arguably) than `dbg' or trace BIFs.</li> | |||
%%% <li>Protection against dumb decisions (matching all calls on a node | |||
%%% being traced, for example)</li> | |||
%%% <li>Adding safe guards in terms of absolute trace count or | |||
%%% rate-limitting</li> | |||
%%% <li>Nicer formatting than default traces</li> | |||
%%% </ul> | |||
%%% | |||
%%% == Tracing Erlang Code == | |||
%%% | |||
%%% The Erlang Trace BIFs allow to trace any Erlang code at all. They work in | |||
%%% two parts: pid specifications, and trace patterns. | |||
%%% | |||
%%% Pid specifications let you decide which processes to target. They can be | |||
%%% specific pids, `all' pids, `existing' pids, or `new' pids (those not | |||
%%% spawned at the time of the function call). | |||
%%% | |||
%%% The trace patterns represent functions. Functions can be specified in two | |||
%%% parts: specifying the modules, functions, and arguments, and then with | |||
%%% Erlang match specifications to add constraints to arguments (see | |||
%%% {@link calls/3} for details). | |||
%%% | |||
%%% What defines whether you get traced or not is the intersection of both: | |||
%%% | |||
%%% ``` | |||
%%% _,--------,_ _,--------,_ | |||
%%% ,-' `-,,-' `-, | |||
%%% ,-' ,-' '-, `-, | |||
%%% | Matching -' '- Matching | | |||
%%% | Pids | Getting | Trace | | |||
%%% | | Traced | Patterns | | |||
%%% | -, ,- | | |||
%%% '-, '-, ,-' ,-' | |||
%%% '-,_ _,-''-,_ _,-' | |||
%%% '--------' '--------' | |||
%%% ''' | |||
%%% | |||
%%% If either the pid specification excludes a process or a trace pattern | |||
%%% excludes a given call, no trace will be received. | |||
%%% | |||
%%% == Example Session == | |||
%%% | |||
%%% First let's trace the `queue:new' functions in any process: | |||
%%% | |||
%%% ``` | |||
%%% 1> recon_trace:calls({queue, new, '_'}, 1). | |||
%%% 1 | |||
%%% 13:14:34.086078 <0.44.0> queue:new() | |||
%%% Recon tracer rate limit tripped. | |||
%%% ''' | |||
%%% | |||
%%% The limit was set to `1' trace message at most, and `recon' let us | |||
%%% know when that limit was reached. | |||
%%% | |||
%%% Let's instead look for all the `queue:in/2' calls, to see what it is | |||
%%% we're inserting in queues: | |||
%%% | |||
%%% ``` | |||
%%% 2> recon_trace:calls({queue, in, 2}, 1). | |||
%%% 1 | |||
%%% 13:14:55.365157 <0.44.0> queue:in(a, {[],[]}) | |||
%%% Recon tracer rate limit tripped. | |||
%%% ''' | |||
%%% | |||
%%% In order to see the content we want, we should change the trace patterns | |||
%%% to use a `fun' that matches on all arguments in a list (`_') and returns | |||
%%% `return_trace()'. This last part will generate a second trace for each | |||
%%% call that includes the return value: | |||
%%% | |||
%%% ``` | |||
%%% 3> recon_trace:calls({queue, in, fun(_) -> return_trace() end}, 3). | |||
%%% 1 | |||
%%% | |||
%%% 13:15:27.655132 <0.44.0> queue:in(a, {[],[]}) | |||
%%% | |||
%%% 13:15:27.655467 <0.44.0> queue:in/2 --> {[a],[]} | |||
%%% | |||
%%% 13:15:27.757921 <0.44.0> queue:in(a, {[],[]}) | |||
%%% Recon tracer rate limit tripped. | |||
%%% ''' | |||
%%% | |||
%%% Matching on argument lists can be done in a more complex manner: | |||
%%% | |||
%%% ``` | |||
%%% 4> recon_trace:calls( | |||
%%% 4> {queue, '_', fun([A,_]) when is_list(A); is_integer(A) andalso A > 1 -> return_trace() end}, | |||
%%% 4> {10,100} | |||
%%% 4> ). | |||
%%% 32 | |||
%%% | |||
%%% 13:24:21.324309 <0.38.0> queue:in(3, {[],[]}) | |||
%%% | |||
%%% 13:24:21.371473 <0.38.0> queue:in/2 --> {[3],[]} | |||
%%% | |||
%%% 13:25:14.694865 <0.53.0> queue:split(4, {[10,9,8,7],[1,2,3,4,5,6]}) | |||
%%% | |||
%%% 13:25:14.695194 <0.53.0> queue:split/2 --> {{[4,3,2],[1]},{[10,9,8,7],[5,6]}} | |||
%%% | |||
%%% 5> recon_trace:clear(). | |||
%%% ok | |||
%%% ''' | |||
%%% | |||
%%% Note that in the pattern above, no specific function (<code>'_'</code>) was | |||
%%% matched against. Instead, the `fun' used restricted functions to those | |||
%%% having two arguments, the first of which is either a list or an integer | |||
%%% greater than `1'. | |||
%%% | |||
%%% The limit was also set using `{10,100}' instead of an integer, making the | |||
%%% rate-limitting at 10 messages per 100 milliseconds, instead of an absolute | |||
%%% value. | |||
%%% | |||
%%% Any tracing can be manually interrupted by calling `recon_trace:clear()', | |||
%%% or killing the shell process. | |||
%%% | |||
%%% Be aware that extremely broad patterns with lax rate-limitting (or very | |||
%%% high absolute limits) may impact your node's stability in ways | |||
%%% `recon_trace' cannot easily help you with. | |||
%%% | |||
%%% In doubt, start with the most restrictive tracing possible, with low | |||
%%% limits, and progressively increase your scope. | |||
%%% | |||
%%% See {@link calls/3} for more details and tracing possibilities. | |||
%%% | |||
%%% == Structure == | |||
%%% | |||
%%% This library is production-safe due to taking the following structure for | |||
%%% tracing: | |||
%%% | |||
%%% ``` | |||
%%% [IO/Group leader] <---------------------, | |||
%%% | | | |||
%%% [shell] ---> [tracer process] ----> [formatter] | |||
%%% ''' | |||
%%% | |||
%%% The tracer process receives trace messages from the node, and enforces | |||
%%% limits in absolute terms or trace rates, before forwarding the messages | |||
%%% to the formatter. This is done so the tracer can do as little work as | |||
%%% possible and never block while building up a large mailbox. | |||
%%% | |||
%%% The tracer process is linked to the shell, and the formatter to the | |||
%%% tracer process. The formatter also traps exits to be able to handle | |||
%%% all received trace messages until the tracer termination, but will then | |||
%%% shut down as soon as possible. | |||
%%% | |||
%%% In case the operator is tracing from a remote shell which gets | |||
%%% disconnected, the links between the shell and the tracer should make it | |||
%%% so tracing is automatically turned off once you disconnect. | |||
%%% | |||
%%% If sending output to the Group Leader is not desired, you may specify | |||
%%% a different pid() via the option `io_server' in the {@link calls/3} function. | |||
%%% For instance to write the traces to a file you can do something like | |||
%%% | |||
%%% ``` | |||
%%% 1> {ok, Dev} = file:open("/tmp/trace",[write]). | |||
%%% 2> recon_trace:calls({queue, in, fun(_) -> return_trace() end}, 3, [{io_server, Dev}]). | |||
%%% 1 | |||
%%% 3> | |||
%%% Recon tracer rate limit tripped. | |||
%%% 4> file:close(Dev). | |||
%%% ''' | |||
%%% | |||
%%% The only output still sent to the Group Leader is the rate limit being | |||
%%% tripped, and any errors. The rest will be sent to the other IO | |||
%%% server (see [http://erlang.org/doc/apps/stdlib/io_protocol.html]). | |||
%%% @end | |||
-module(recon_trace). | |||
%% API | |||
-export([clear/0, calls/2, calls/3]). | |||
-export([format/1]). | |||
%% Internal exports | |||
-export([count_tracer/1, rate_tracer/2, formatter/5]). | |||
-type matchspec() :: [{[term()], [term()], [term()]}]. | |||
-type shellfun() :: fun((_) -> term()). | |||
-type formatterfun() :: fun((_) -> iodata()). | |||
-type millisecs() :: non_neg_integer(). | |||
-type pidspec() :: all | existing | new | recon:pid_term(). | |||
-type max_traces() :: non_neg_integer(). | |||
-type max_rate() :: {max_traces(), millisecs()}. | |||
%% trace options | |||
-type options() :: [{pid, pidspec() | [pidspec(), ...]} % default: all | |||
| {timestamp, formatter | trace} % default: formatter | |||
| {args, args | arity} % default: args | |||
| {io_server, pid()} % default: group_leader() | |||
| {formatter, formatterfun()} % default: internal formatter | |||
| return_to | {return_to, boolean()} % default: false | |||
%% match pattern options | |||
| {scope, global | local} % default: global | |||
]. | |||
-type mod() :: '_' | module(). | |||
-type fn() :: '_' | atom(). | |||
-type args() :: '_' | 0..255 | return_trace | matchspec() | shellfun(). | |||
-type tspec() :: {mod(), fn(), args()}. | |||
-type max() :: max_traces() | max_rate(). | |||
-type num_matches() :: non_neg_integer(). | |||
-export_type([mod/0, fn/0, args/0, tspec/0, num_matches/0, options/0, | |||
max_traces/0, max_rate/0]). | |||
%%%%%%%%%%%%%% | |||
%%% PUBLIC %%% | |||
%%%%%%%%%%%%%% | |||
%% @doc Stops all tracing at once. | |||
-spec clear() -> ok. | |||
clear() -> | |||
erlang:trace(all, false, [all]), | |||
erlang:trace_pattern({'_', '_', '_'}, false, [local, meta, call_count, call_time]), | |||
erlang:trace_pattern({'_', '_', '_'}, false, []), % unsets global | |||
maybe_kill(recon_trace_tracer), | |||
maybe_kill(recon_trace_formatter), | |||
ok. | |||
%% @equiv calls({Mod, Fun, Args}, Max, []) | |||
-spec calls(tspec() | [tspec(), ...], max()) -> num_matches(). | |||
calls({Mod, Fun, Args}, Max) -> | |||
calls([{Mod, Fun, Args}], Max, []); | |||
calls(TSpecs = [_ | _], Max) -> | |||
calls(TSpecs, Max, []). | |||
%% @doc Allows to set trace patterns and pid specifications to trace | |||
%% function calls. | |||
%% | |||
%% The basic calls take the trace patterns as tuples of the form | |||
%% `{Module, Function, Args}' where: | |||
%% | |||
%% <ul> | |||
%% <li>`Module' is any atom representing a module</li> | |||
%% <li>`Function' is any atom representing a function, or the wildcard | |||
%% <code>'_'</code></li> | |||
%% <li>`Args' is either the arity of a function (`0..255'), a wildcard | |||
%% pattern (<code>'_'</code>), a | |||
%% <a href="http://learnyousomeerlang.com/ets#you-have-been-selected">match specification</a>, | |||
%% or a function from a shell session that can be transformed into | |||
%% a match specification</li> | |||
%% </ul> | |||
%% | |||
%% There is also an argument specifying either a maximal count (a number) | |||
%% of trace messages to be received, or a maximal frequency (`{Num, Millisecs}'). | |||
%% | |||
%% Here are examples of things to trace: | |||
%% | |||
%% <ul> | |||
%% <li>All calls from the `queue' module, with 10 calls printed at most: | |||
%% ``recon_trace:calls({queue, '_', '_'}, 10)''</li> | |||
%% <li>All calls to `lists:seq(A,B)', with 100 calls printed at most: | |||
%% `recon_trace:calls({lists, seq, 2}, 100)'</li> | |||
%% <li>All calls to `lists:seq(A,B)', with 100 calls per second at most: | |||
%% `recon_trace:calls({lists, seq, 2}, {100, 1000})'</li> | |||
%% <li>All calls to `lists:seq(A,B,2)' (all sequences increasing by two) | |||
%% with 100 calls at most: | |||
%% `recon_trace:calls({lists, seq, fun([_,_,2]) -> ok end}, 100)'</li> | |||
%% <li>All calls to `iolist_to_binary/1' made with a binary as an argument | |||
%% already (kind of useless conversion!): | |||
%% `recon_trace:calls({erlang, iolist_to_binary, fun([X]) when is_binary(X) -> ok end}, 10)'</li> | |||
%% <li>Calls to the queue module only in a given process `Pid', at a rate | |||
%% of 50 per second at most: | |||
%% ``recon_trace:calls({queue, '_', '_'}, {50,1000}, [{pid, Pid}])''</li> | |||
%% <li>Print the traces with the function arity instead of literal arguments: | |||
%% `recon_trace:calls(TSpec, Max, [{args, arity}])'</li> | |||
%% <li>Matching the `filter/2' functions of both `dict' and `lists' modules, | |||
%% across new processes only: | |||
%% `recon_trace:calls([{dict,filter,2},{lists,filter,2}], 10, [{pid, new}])'</li> | |||
%% <li>Tracing the `handle_call/3' functions of a given module for all new processes, | |||
%% and those of an existing one registered with `gproc': | |||
%% `recon_trace:calls({Mod,handle_call,3}, {10,100}, [{pid, [{via, gproc, Name}, new]}'</li> | |||
%% <li>Show the result of a given function call: | |||
%% `recon_trace:calls({Mod,Fun,fun(_) -> return_trace() end}, Max, Opts)' | |||
%% or | |||
%% ``recon_trace:calls({Mod,Fun,[{'_', [], [{return_trace}]}]}, Max, Opts)'', | |||
%% the important bit being the `return_trace()' call or the | |||
%% `{return_trace}' match spec value. | |||
%% A short-hand version for this pattern of 'match anything, trace everything' | |||
%% for a function is `recon_trace:calls({Mod, Fun, return_trace})'. </li> | |||
%% </ul> | |||
%% | |||
%% There's a few more combination possible, with multiple trace patterns per call, and more | |||
%% options: | |||
%% | |||
%% <ul> | |||
%% <li>`{pid, PidSpec}': which processes to trace. Valid options is any of | |||
%% `all', `new', `existing', or a process descriptor (`{A,B,C}', | |||
%% `"<A.B.C>"', an atom representing a name, `{global, Name}', | |||
%% `{via, Registrar, Name}', or a pid). It's also possible to specify | |||
%% more than one by putting them in a list.</li> | |||
%% <li>`{timestamp, formatter | trace}': by default, the formatter process | |||
%% adds timestamps to messages received. If accurate timestamps are | |||
%% required, it's possible to force the usage of timestamps within | |||
%% trace messages by adding the option `{timestamp, trace}'.</li> | |||
%% <li>`{args, arity | args}': whether to print arity in function calls | |||
%% or their (by default) literal representation.</li> | |||
%% <li>`{scope, global | local}': by default, only 'global' (fully qualified | |||
%% function calls) are traced, not calls made internally. To force tracing | |||
%% of local calls, pass in `{scope, local}'. This is useful whenever | |||
%% you want to track the changes of code in a process that isn't called | |||
%% with `Module:Fun(Args)', but just `Fun(Args)'.</li> | |||
%% <li>`{formatter, fun(Term) -> io_data() end}': override the default | |||
%% formatting functionality provided by recon.</li> | |||
%% <li>`{io_server, pid() | atom()}': by default, recon logs to the current | |||
%% group leader, usually the shell. This option allows to redirect | |||
%% trace output to a different IO server (such as a file handle).</li> | |||
%% <li>`return_to': If this option is set (in conjunction with the match | |||
%% option `{scope, local}'), the function to which the value is returned | |||
%% is output in a trace. Note that this is distinct from giving the | |||
%% *caller* since exception handling or calls in tail position may | |||
%% hide the original caller.</li> | |||
%% </ul> | |||
%% | |||
%% Also note that putting extremely large `Max' values (i.e. `99999999' or | |||
%% `{10000,1}') will probably negate most of the safe-guarding this library | |||
%% does and be dangerous to your node. Similarly, tracing extremely large | |||
%% amounts of function calls (all of them, or all of `io' for example) | |||
%% can be risky if more trace messages are generated than any process on | |||
%% the node could ever handle, despite the precautions taken by this library. | |||
%% @end | |||
-spec calls(tspec() | [tspec(), ...], max(), options()) -> num_matches(). | |||
calls({Mod, Fun, Args}, Max, Opts) -> | |||
calls([{Mod, Fun, Args}], Max, Opts); | |||
calls(TSpecs = [_ | _], {Max, Time}, Opts) -> | |||
Pid = setup(rate_tracer, [Max, Time], | |||
validate_formatter(Opts), validate_io_server(Opts)), | |||
trace_calls(TSpecs, Pid, Opts); | |||
calls(TSpecs = [_ | _], Max, Opts) -> | |||
Pid = setup(count_tracer, [Max], | |||
validate_formatter(Opts), validate_io_server(Opts)), | |||
trace_calls(TSpecs, Pid, Opts). | |||
%%%%%%%%%%%%%%%%%%%%%%% | |||
%%% PRIVATE EXPORTS %%% | |||
%%%%%%%%%%%%%%%%%%%%%%% | |||
%% @private Stops when N trace messages have been received | |||
count_tracer(0) -> | |||
exit(normal); | |||
count_tracer(N) -> | |||
receive | |||
Msg -> | |||
recon_trace_formatter ! Msg, | |||
count_tracer(N - 1) | |||
end. | |||
%% @private Stops whenever the trace message rates goes higher than | |||
%% `Max' messages in `Time' milliseconds. Note that if the rate | |||
%% proposed is higher than what the IO system of the formatter | |||
%% can handle, this can still put a node at risk. | |||
%% | |||
%% It is recommended to try stricter rates to begin with. | |||
rate_tracer(Max, Time) -> rate_tracer(Max, Time, 0, os:timestamp()). | |||
rate_tracer(Max, Time, Count, Start) -> | |||
receive | |||
Msg -> | |||
recon_trace_formatter ! Msg, | |||
Now = os:timestamp(), | |||
Delay = timer:now_diff(Now, Start) div 1000, | |||
if Delay > Time -> rate_tracer(Max, Time, 0, Now) | |||
; Max > Count -> rate_tracer(Max, Time, Count + 1, Start) | |||
; Max =:= Count -> exit(normal) | |||
end | |||
end. | |||
%% @private Formats traces to be output | |||
formatter(Tracer, Parent, Ref, FormatterFun, IOServer) -> | |||
process_flag(trap_exit, true), | |||
link(Tracer), | |||
Parent ! {Ref, linked}, | |||
formatter(Tracer, IOServer, FormatterFun). | |||
formatter(Tracer, IOServer, FormatterFun) -> | |||
receive | |||
{'EXIT', Tracer, normal} -> | |||
io:format("Recon tracer rate limit tripped.~n"), | |||
exit(normal); | |||
{'EXIT', Tracer, Reason} -> | |||
exit(Reason); | |||
TraceMsg -> | |||
io:format(IOServer, FormatterFun(TraceMsg), []), | |||
formatter(Tracer, IOServer, FormatterFun) | |||
end. | |||
%%%%%%%%%%%%%%%%%%%%%%% | |||
%%% SETUP FUNCTIONS %%% | |||
%%%%%%%%%%%%%%%%%%%%%%% | |||
%% starts the tracer and formatter processes, and | |||
%% cleans them up before each call. | |||
setup(TracerFun, TracerArgs, FormatterFun, IOServer) -> | |||
clear(), | |||
Ref = make_ref(), | |||
Tracer = spawn_link(?MODULE, TracerFun, TracerArgs), | |||
register(recon_trace_tracer, Tracer), | |||
Format = spawn(?MODULE, formatter, [Tracer, self(), Ref, FormatterFun, IOServer]), | |||
register(recon_trace_formatter, Format), | |||
receive | |||
{Ref, linked} -> Tracer | |||
after 5000 -> | |||
error(setup_failed) | |||
end. | |||
%% Sets the traces in action | |||
trace_calls(TSpecs, Pid, Opts) -> | |||
{PidSpecs, TraceOpts, MatchOpts} = validate_opts(Opts), | |||
Matches = [begin | |||
{Arity, Spec} = validate_tspec(Mod, Fun, Args), | |||
erlang:trace_pattern({Mod, Fun, Arity}, Spec, MatchOpts) | |||
end || {Mod, Fun, Args} <- TSpecs], | |||
[erlang:trace(PidSpec, true, [call, {tracer, Pid} | TraceOpts]) | |||
|| PidSpec <- PidSpecs], | |||
lists:sum(Matches). | |||
%%%%%%%%%%%%%%%%%% | |||
%%% VALIDATION %%% | |||
%%%%%%%%%%%%%%%%%% | |||
validate_opts(Opts) -> | |||
PidSpecs = validate_pid_specs(proplists:get_value(pid, Opts, all)), | |||
Scope = proplists:get_value(scope, Opts, global), | |||
TraceOpts = case proplists:get_value(timestamp, Opts, formatter) of | |||
formatter -> []; | |||
trace -> [timestamp] | |||
end ++ | |||
case proplists:get_value(args, Opts, args) of | |||
args -> []; | |||
arity -> [arity] | |||
end ++ | |||
case proplists:get_value(return_to, Opts, undefined) of | |||
true when Scope =:= local -> | |||
[return_to]; | |||
true when Scope =:= global -> | |||
io:format("Option return_to only works with option {scope, local}~n"), | |||
%% Set it anyway | |||
[return_to]; | |||
_ -> | |||
[] | |||
end, | |||
MatchOpts = [Scope], | |||
{PidSpecs, TraceOpts, MatchOpts}. | |||
%% Support the regular specs, but also allow `recon:pid_term()' and lists | |||
%% of further pid specs. | |||
-spec validate_pid_specs(pidspec() | [pidspec(), ...]) -> | |||
[all | new | existing | pid(), ...]. | |||
validate_pid_specs(all) -> [all]; | |||
validate_pid_specs(existing) -> [existing]; | |||
validate_pid_specs(new) -> [new]; | |||
validate_pid_specs([Spec]) -> validate_pid_specs(Spec); | |||
validate_pid_specs(PidTerm = [Spec | Rest]) -> | |||
%% can be "<a.b.c>" or [pidspec()] | |||
try | |||
[recon_lib:term_to_pid(PidTerm)] | |||
catch | |||
error:function_clause -> | |||
validate_pid_specs(Spec) ++ validate_pid_specs(Rest) | |||
end; | |||
validate_pid_specs(PidTerm) -> | |||
%% has to be `recon:pid_term()'. | |||
[recon_lib:term_to_pid(PidTerm)]. | |||
validate_tspec(Mod, Fun, Args) when is_function(Args) -> | |||
validate_tspec(Mod, Fun, fun_to_ms(Args)); | |||
%% helper to save typing for common actions | |||
validate_tspec(Mod, Fun, return_trace) -> | |||
validate_tspec(Mod, Fun, [{'_', [], [{return_trace}]}]); | |||
validate_tspec(Mod, Fun, Args) -> | |||
BannedMods = ['_', ?MODULE, io, lists], | |||
%% The banned mod check can be bypassed by using | |||
%% match specs if you really feel like being dumb. | |||
case {lists:member(Mod, BannedMods), Args} of | |||
{true, '_'} -> error({dangerous_combo, {Mod, Fun, Args}}); | |||
{true, []} -> error({dangerous_combo, {Mod, Fun, Args}}); | |||
_ -> ok | |||
end, | |||
case Args of | |||
'_' -> {'_', true}; | |||
_ when is_list(Args) -> {'_', Args}; | |||
_ when Args >= 0, Args =< 255 -> {Args, true} | |||
end. | |||
validate_formatter(Opts) -> | |||
case proplists:get_value(formatter, Opts) of | |||
F when is_function(F, 1) -> F; | |||
_ -> fun format/1 | |||
end. | |||
validate_io_server(Opts) -> | |||
proplists:get_value(io_server, Opts, group_leader()). | |||
%%%%%%%%%%%%%%%%%%%%%%%% | |||
%%% TRACE FORMATTING %%% | |||
%%%%%%%%%%%%%%%%%%%%%%%% | |||
%% Thanks Geoff Cant for the foundations for this. | |||
format(TraceMsg) -> | |||
{Type, Pid, {Hour, Min, Sec}, TraceInfo} = extract_info(TraceMsg), | |||
{FormatStr, FormatArgs} = case {Type, TraceInfo} of | |||
%% {trace, Pid, 'receive', Msg} | |||
{'receive', [Msg]} -> | |||
{"< ~p", [Msg]}; | |||
%% {trace, Pid, send, Msg, To} | |||
{send, [Msg, To]} -> | |||
{" > ~p: ~p", [To, Msg]}; | |||
%% {trace, Pid, send_to_non_existing_process, Msg, To} | |||
{send_to_non_existing_process, [Msg, To]} -> | |||
{" > (non_existent) ~p: ~p", [To, Msg]}; | |||
%% {trace, Pid, call, {M, F, Args}} | |||
{call, [{M, F, Args}]} -> | |||
{"~p:~p~s", [M, F, format_args(Args)]}; | |||
%% {trace, Pid, return_to, {M, F, Arity}} | |||
{return_to, [{M, F, Arity}]} -> | |||
{" '--> ~p:~p/~p", [M, F, Arity]}; | |||
%% {trace, Pid, return_from, {M, F, Arity}, ReturnValue} | |||
{return_from, [{M, F, Arity}, Return]} -> | |||
{"~p:~p/~p --> ~p", [M, F, Arity, Return]}; | |||
%% {trace, Pid, exception_from, {M, F, Arity}, {Class, Value}} | |||
{exception_from, [{M, F, Arity}, {Class, Val}]} -> | |||
{"~p:~p/~p ~p ~p", [M, F, Arity, Class, Val]}; | |||
%% {trace, Pid, spawn, Spawned, {M, F, Args}} | |||
{spawn, [Spawned, {M, F, Args}]} -> | |||
{"spawned ~p as ~p:~p~s", [Spawned, M, F, format_args(Args)]}; | |||
%% {trace, Pid, exit, Reason} | |||
{exit, [Reason]} -> | |||
{"EXIT ~p", [Reason]}; | |||
%% {trace, Pid, link, Pid2} | |||
{link, [Linked]} -> | |||
{"link(~p)", [Linked]}; | |||
%% {trace, Pid, unlink, Pid2} | |||
{unlink, [Linked]} -> | |||
{"unlink(~p)", [Linked]}; | |||
%% {trace, Pid, getting_linked, Pid2} | |||
{getting_linked, [Linker]} -> | |||
{"getting linked by ~p", [Linker]}; | |||
%% {trace, Pid, getting_unlinked, Pid2} | |||
{getting_unlinked, [Unlinker]} -> | |||
{"getting unlinked by ~p", [Unlinker]}; | |||
%% {trace, Pid, register, RegName} | |||
{register, [Name]} -> | |||
{"registered as ~p", [Name]}; | |||
%% {trace, Pid, unregister, RegName} | |||
{unregister, [Name]} -> | |||
{"no longer registered as ~p", [Name]}; | |||
%% {trace, Pid, in, {M, F, Arity} | 0} | |||
{in, [{M, F, Arity}]} -> | |||
{"scheduled in for ~p:~p/~p", [M, F, Arity]}; | |||
{in, [0]} -> | |||
{"scheduled in", []}; | |||
%% {trace, Pid, out, {M, F, Arity} | 0} | |||
{out, [{M, F, Arity}]} -> | |||
{"scheduled out from ~p:~p/~p", [M, F, Arity]}; | |||
{out, [0]} -> | |||
{"scheduled out", []}; | |||
%% {trace, Pid, gc_start, Info} | |||
{gc_start, [Info]} -> | |||
HeapSize = proplists:get_value(heap_size, Info), | |||
OldHeapSize = proplists:get_value(old_heap_size, Info), | |||
MbufSize = proplists:get_value(mbuf_size, Info), | |||
{"gc beginning -- heap ~p bytes", | |||
[HeapSize + OldHeapSize + MbufSize]}; | |||
%% {trace, Pid, gc_end, Info} | |||
{gc_end, [Info]} -> | |||
HeapSize = proplists:get_value(heap_size, Info), | |||
OldHeapSize = proplists:get_value(old_heap_size, Info), | |||
MbufSize = proplists:get_value(mbuf_size, Info), | |||
{"gc finished -- heap ~p bytes", | |||
[HeapSize + OldHeapSize + MbufSize]}; | |||
_ -> | |||
{"unknown trace type ~p -- ~p", [Type, TraceInfo]} | |||
end, | |||
io_lib:format("~n~p:~p:~9.6.0f ~p " ++ FormatStr ++ "~n", | |||
[Hour, Min, Sec, Pid] ++ FormatArgs). | |||
extract_info(TraceMsg) -> | |||
case tuple_to_list(TraceMsg) of | |||
[trace_ts, Pid, Type | Info] -> | |||
{TraceInfo, [Timestamp]} = lists:split(length(Info) - 1, Info), | |||
{Type, Pid, to_hms(Timestamp), TraceInfo}; | |||
[trace, Pid, Type | TraceInfo] -> | |||
{Type, Pid, to_hms(os:timestamp()), TraceInfo} | |||
end. | |||
to_hms(Stamp = {_, _, Micro}) -> | |||
{_, {H, M, Secs}} = calendar:now_to_local_time(Stamp), | |||
Seconds = Secs rem 60 + (Micro / 1000000), | |||
{H, M, Seconds}; | |||
to_hms(_) -> | |||
{0, 0, 0}. | |||
format_args(Arity) when is_integer(Arity) -> | |||
"/" ++ integer_to_list(Arity); | |||
format_args(Args) when is_list(Args) -> | |||
"(" ++ string:join([io_lib:format("~p", [Arg]) || Arg <- Args], ", ") ++ ")". | |||
%%%%%%%%%%%%%%% | |||
%%% HELPERS %%% | |||
%%%%%%%%%%%%%%% | |||
maybe_kill(Name) -> | |||
case whereis(Name) of | |||
undefined -> | |||
ok; | |||
Pid -> | |||
unlink(Pid), | |||
exit(Pid, kill), | |||
wait_for_death(Pid, Name) | |||
end. | |||
wait_for_death(Pid, Name) -> | |||
case is_process_alive(Pid) orelse whereis(Name) =:= Pid of | |||
true -> | |||
timer:sleep(10), | |||
wait_for_death(Pid, Name); | |||
false -> | |||
ok | |||
end. | |||
%% Borrowed from dbg | |||
fun_to_ms(ShellFun) when is_function(ShellFun) -> | |||
case erl_eval:fun_data(ShellFun) of | |||
{fun_data, ImportList, Clauses} -> | |||
case ms_transform:transform_from_shell( | |||
dbg, Clauses, ImportList) of | |||
{error, [{_, [{_, _, Code} | _]} | _], _} -> | |||
io:format("Error: ~s~n", | |||
[ms_transform:format_error(Code)]), | |||
{error, transform_error}; | |||
Else -> | |||
Else | |||
end; | |||
false -> | |||
exit(shell_funs_only) | |||
end. |
@ -1,73 +0,0 @@ | |||
-module(tester). | |||
-export([ | |||
test_online_num/1, | |||
test_scene_sign/0, | |||
test_lists_and_proplists/0, | |||
test_lists_and_proplists/3 | |||
]). | |||
%% Apis ------------------------------ | |||
test_online_num(Repeat) -> | |||
F1 = fun(_) -> lib_world_boss:online_num() end, | |||
F2 = fun(_) -> lib_world_boss:online_num2() end, | |||
Tester = [ | |||
{"lib_world_boss:online_num", F1}, | |||
{"lib_world_boss:online_num2", F2} | |||
], | |||
test:time_compare(Repeat, Tester). | |||
test_lists_and_proplists() -> | |||
test_lists_and_proplists(10000, 10, 50). | |||
test_lists_and_proplists(Repeat, ListSize, FindSize) -> | |||
{ToFindKeys, MaterialList} = gen_list_material(ListSize, FindSize), | |||
F1 = fun(_) -> [lists:keyfind(K, 1, MaterialList) || K <- ToFindKeys] end, | |||
F2 = fun(_) -> [proplists:get_value(K, MaterialList) || K <- ToFindKeys] end, | |||
Tester = [ | |||
{"lists:keyfind/3", F1}, | |||
{"proplists:get_value/2", F2} | |||
], | |||
test:time_compare(Repeat, Tester). | |||
%% @doc 测试地图区域 | |||
test_scene_sign() -> | |||
case conf_scene_list:get_id_list() of | |||
SceneList when SceneList =/= [] -> | |||
SceneID = util:list_rand(SceneList), | |||
test_scene_sign(10000, SceneID, 100); | |||
_ -> | |||
ignore | |||
end. | |||
test_scene_sign(Repeat, SceneID, FindSize) -> | |||
{MaxX, MaxY} = lib_scene_sign:get_max_xy(SceneID), | |||
PosList = [{util:rand(1, MaxX), util:rand(1, MaxY)} || _ <- lists:seq(1, FindSize)], | |||
%% 从s3取svr_scene_sign模块,并将svr_scene_sign:load_sign/1改为call | |||
svr_scene_sign:ensure_scene_sign(SceneID), | |||
F1 = fun(_) -> | |||
[svr_scene_sign:get_scene_poses({SceneID, X, Y}) || {X, Y} <- PosList] | |||
end, | |||
Ets = conf_scene:get_ets(SceneID), | |||
F2 = fun(_) -> | |||
[lib_scene_sign:is_pos_blocked(Ets, {SceneID, X, Y}) || {X, Y} <- PosList] | |||
end, | |||
F3 = fun(_) -> | |||
[lib_scene_sign:is_pos_blocked({SceneID, X, Y}) || {X, Y} <- PosList] | |||
end, | |||
Tester = [ | |||
{"scene_sign+gen_server", F1}, | |||
{"scene_sign+ets", F2}, | |||
{"scene_sign+ets", F3} | |||
], | |||
test:time_compare(Repeat, Tester). | |||
%% Privates -------------------------- | |||
gen_list_material(ListSize, FindSize) -> | |||
Keys = [list_to_atom(lists:concat([k, I])) || I <- lists:seq(1, ListSize)], | |||
Values = [list_to_atom(lists:concat([v, I])) || I <- lists:seq(1, ListSize)], | |||
List = lists:zip(Keys, Values), | |||
ToFind = util:gen_n(FindSize, Keys), | |||
{ToFind, List}. |