@ -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}. |