fix implementation for ghost commands, now it mostly tested and work https://github.com/HarpyWar/pvpgn/issues/35
This commit is contained in:
parent
9b2e7fda4e
commit
0c6738fe89
15 changed files with 408 additions and 220 deletions
|
@ -19,7 +19,7 @@ ah_mh_value = 139
|
|||
|
||||
function ah_init()
|
||||
timer_add("ah_timer", config.ah_interval, ah_timer_tick)
|
||||
TRACE("Antihack activated")
|
||||
INFO("Starcraft Antihack activated")
|
||||
end
|
||||
|
||||
-- send memory check request to all players in games
|
||||
|
@ -50,7 +50,7 @@ function ah_handle_client(account, request_id, data)
|
|||
if (request_id == ah_mh_request_id) then
|
||||
-- read value from the memory
|
||||
local value = bytes_to_int(data, 0, 2)
|
||||
DEBUG(account.name .. " memory value: " .. value)
|
||||
--TRACE(account.name .. " memory value: " .. value)
|
||||
|
||||
if not (value == ah_mh_value) then
|
||||
is_cheater = true
|
||||
|
|
16
lua/command/ping.lua
Normal file
16
lua/command/ping.lua
Normal file
|
@ -0,0 +1,16 @@
|
|||
--[[
|
||||
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
|
||||
|
||||
This file is a part of the PvPGN Project http://pvpgn.pro
|
||||
Licensed under the same terms as Lua itself.
|
||||
]]--
|
||||
|
||||
|
||||
function command_ping(account, text)
|
||||
-- allow warcraft 3 client only
|
||||
if (account.clienttag == CLIENTTAG_WAR3XP) and (config.ghost) then
|
||||
return gh_command_ping(account, text)
|
||||
end
|
||||
|
||||
return 1
|
||||
end
|
15
lua/command/stats.lua
Normal file
15
lua/command/stats.lua
Normal file
|
@ -0,0 +1,15 @@
|
|||
--[[
|
||||
Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
|
||||
|
||||
This file is a part of the PvPGN Project http://pvpgn.pro
|
||||
Licensed under the same terms as Lua itself.
|
||||
]]--
|
||||
|
||||
|
||||
function command_stats(account, text)
|
||||
if config.ghost_dota_server and config.ghost and (account.clienttag == CLIENTTAG_WAR3XP) then
|
||||
return gh_command_stats(account, text)
|
||||
end
|
||||
|
||||
return 1
|
||||
end
|
|
@ -9,6 +9,12 @@
|
|||
-- Config table can be extended here with your own variables
|
||||
-- values are preloaded from bnetd.conf
|
||||
config = {
|
||||
-- Path to "var" directory (with slash at the end)
|
||||
-- Usage: config.vardir()
|
||||
vardir = function()
|
||||
return string.replace(config.statusdir, "status", "")
|
||||
end,
|
||||
|
||||
flood_immunity_users = { "admin", "" }, -- ignore flood protection for these users
|
||||
|
||||
-- Quiz settings
|
||||
|
@ -27,7 +33,7 @@ config = {
|
|||
|
||||
-- GHost++ (https://github.com/OHSystem/ohsystem)
|
||||
ghost = false, -- enable GHost commands
|
||||
ghost_bots = { hostbot1, hostbot2 }, -- list of authorized bots
|
||||
ghost_bots = { "hostbot1", "hostbot2" }, -- list of authorized bots
|
||||
ghost_dota_server = true, -- replace normal Warcraft 3 stats with DotA
|
||||
ghost_ping_expire = 90, -- interval when outdated botpings should be removed (bot ping updates for each user when he join a game hosted by ghost); game list shows to user depending on the best ping to host bot
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ function users_count()
|
|||
return count
|
||||
end
|
||||
|
||||
-- Get count of all server account
|
||||
-- Get count of all server account that were used by the server
|
||||
function accounts_count()
|
||||
local count = 0
|
||||
for i,account in pairs(api.server_get_users(true)) do
|
||||
|
|
|
@ -91,10 +91,10 @@ function account_set_auth_lock(username, value)
|
|||
end
|
||||
|
||||
function account_get_auth_locktime(username)
|
||||
return api.account_get_attr(username, "BNET\\auth\\locktime", attr_type_int)
|
||||
return api.account_get_attr(username, "BNET\\auth\\locktime", attr_type_num)
|
||||
end
|
||||
function account_set_auth_locktime(username, value)
|
||||
return api.account_set_attr(username, "BNET\\auth\\locktime", attr_type_int, value)
|
||||
return api.account_set_attr(username, "BNET\\auth\\locktime", attr_type_num, value)
|
||||
end
|
||||
|
||||
function account_get_auth_lockreason(username)
|
||||
|
@ -119,10 +119,10 @@ function account_set_auth_mute(username, value)
|
|||
end
|
||||
|
||||
function account_get_auth_mutetime(username)
|
||||
return api.account_get_attr(username, "BNET\\auth\\mutetime", attr_type_int)
|
||||
return api.account_get_attr(username, "BNET\\auth\\mutetime", attr_type_num)
|
||||
end
|
||||
function account_set_auth_mutetime(username, value)
|
||||
return api.account_set_attr(username, "BNET\\auth\\mutetime", attr_type_int, value)
|
||||
return api.account_set_attr(username, "BNET\\auth\\mutetime", attr_type_num, value)
|
||||
end
|
||||
|
||||
function account_get_auth_mutereason(username)
|
||||
|
@ -140,17 +140,17 @@ function account_set_auth_muteby(username, value)
|
|||
end
|
||||
|
||||
function account_get_auth_command_groups(username)
|
||||
return api.account_get_attr(username, "BNET\\auth\\command_groups", attr_type_int)
|
||||
return api.account_get_attr(username, "BNET\\auth\\command_groups", attr_type_num)
|
||||
end
|
||||
function account_set_auth_command_groups(username, value)
|
||||
return api.account_set_attr(username, "BNET\\auth\\command_groups", attr_type_int, value)
|
||||
return api.account_set_attr(username, "BNET\\auth\\command_groups", attr_type_num, value)
|
||||
end
|
||||
|
||||
function account_get_acct_lastlogin_time(username)
|
||||
return api.account_get_attr(username, "BNET\\acct\\lastlogin_time", attr_type_int)
|
||||
return api.account_get_attr(username, "BNET\\acct\\lastlogin_time", attr_type_num)
|
||||
end
|
||||
function account_set_acct_lastlogin_time(username, value)
|
||||
return api.account_set_attr(username, "BNET\\acct\\lastlogin_time", attr_type_int, value)
|
||||
return api.account_set_attr(username, "BNET\\acct\\lastlogin_time", attr_type_num, value)
|
||||
end
|
||||
|
||||
function account_get_acct_lastlogin_owner(username)
|
||||
|
@ -161,10 +161,10 @@ function account_set_acct_lastlogin_owner(username, value)
|
|||
end
|
||||
|
||||
function account_get_acct_createtime(username)
|
||||
return api.account_get_attr(username, "BNET\\acct\\ctime", attr_type_int)
|
||||
return api.account_get_attr(username, "BNET\\acct\\ctime", attr_type_num)
|
||||
end
|
||||
function account_set_acct_createtime(username, value)
|
||||
return api.account_set_attr(username, "BNET\\acct\\ctime", attr_type_int, value)
|
||||
return api.account_set_attr(username, "BNET\\acct\\ctime", attr_type_num, value)
|
||||
end
|
||||
|
||||
function account_get_acct_lastlogin_clienttag(username)
|
||||
|
@ -529,7 +529,9 @@ end
|
|||
--
|
||||
|
||||
function account_get_dotarating_3x3(username)
|
||||
return api.account_get_attr(username, "Record\\W3XP\\dota_3_rating", attr_type_num)
|
||||
value = api.account_get_attr(username, "Record\\W3XP\\dota_3_rating", attr_type_num)
|
||||
if (value == 0) then value = 1000 end
|
||||
return value
|
||||
end
|
||||
function account_set_dotarating_3x3(username, value)
|
||||
return api.account_set_attr(username, "Record\\W3XP\\dota_3_rating", attr_type_num, value)
|
||||
|
@ -565,7 +567,9 @@ end
|
|||
|
||||
|
||||
function account_get_dotarating_5x5(username)
|
||||
return api.account_get_attr(username, "Record\\W3XP\\dota_5_rating", attr_type_num)
|
||||
value = api.account_get_attr(username, "Record\\W3XP\\dota_5_rating", attr_type_num)
|
||||
if (value == 0) then value = 1000 end
|
||||
return value
|
||||
end
|
||||
function account_set_dotarating_5x5(username, value)
|
||||
return api.account_set_attr(username, "Record\\W3XP\\dota_5_rating", attr_type_num, value)
|
||||
|
@ -602,6 +606,8 @@ end
|
|||
function account_get_botping(username)
|
||||
value = api.account_get_attr(username, "BNET\\acct\\botping", attr_type_str)
|
||||
local pings = {}
|
||||
-- if pings were not set yet then return empty table
|
||||
if string.empty(value) then return pings end
|
||||
|
||||
-- deserialize and return table
|
||||
-- data format: "unixtime,botname,ping;..."
|
||||
|
@ -630,7 +636,7 @@ function account_set_botping(username, pings)
|
|||
-- serialize table
|
||||
for k,v in pairs(pings) do
|
||||
-- ignore expired pings
|
||||
if (os.time() - v.date) < 60*60*24*ghost_ping_expire then
|
||||
if (os.time() - tonumber(v.date)) < 60*60*24*config.ghost_ping_expire then
|
||||
value = value .. string.format("%s,%s,%s;", v.date, v.bot, v.ping);
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
Licensed under the same terms as Lua itself.
|
||||
]]--
|
||||
|
||||
-- Strings used here - not numbers, because these values will be
|
||||
-- compare with string values returned from API.
|
||||
-- For example "0" != 0 in Lua.
|
||||
|
||||
game_type_none,
|
||||
game_type_all,
|
||||
|
@ -27,7 +30,7 @@ game_type_diablo,
|
|||
game_type_diablo2open,
|
||||
game_type_diablo2closed,
|
||||
game_type_anongame
|
||||
= 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
|
||||
= "0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20"
|
||||
|
||||
|
||||
game_status_started,
|
||||
|
@ -35,7 +38,7 @@ game_status_full,
|
|||
game_status_open,
|
||||
game_status_loaded,
|
||||
game_status_done
|
||||
= 0,1,2,3,4
|
||||
= "0","1","2","3","4"
|
||||
|
||||
|
||||
game_result_none,
|
||||
|
@ -45,7 +48,7 @@ game_result_draw,
|
|||
game_result_disconnect,
|
||||
game_result_observer,
|
||||
game_result_playing
|
||||
= 0,1,2,3,4,5,6
|
||||
= "0","1","2","3","4","5","6"
|
||||
|
||||
|
||||
game_option_none,
|
||||
|
@ -81,7 +84,7 @@ game_option_topvbot_4,
|
|||
game_option_topvbot_3,
|
||||
game_option_topvbot_2,
|
||||
game_option_topvbot_1
|
||||
= 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32
|
||||
= "0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32"
|
||||
|
||||
|
||||
game_maptype_none,
|
||||
|
@ -91,7 +94,7 @@ game_maptype_ladder,
|
|||
game_maptype_pgl,
|
||||
game_maptype_kbk,
|
||||
game_maptype_compusa
|
||||
= 0,1,2,3,4,5,6
|
||||
= "0","1","2","3","4","5","6"
|
||||
|
||||
|
||||
game_tileset_none,
|
||||
|
@ -103,7 +106,7 @@ game_tileset_jungle,
|
|||
game_tileset_desert,
|
||||
game_tileset_ice,
|
||||
game_tileset_twilight
|
||||
= 0,1,2,3,4,5,6,7,8,9
|
||||
= "0","1","2","3","4","5","6","7","8","9"
|
||||
|
||||
|
||||
game_speed_none,
|
||||
|
@ -114,7 +117,7 @@ game_speed_normal,
|
|||
game_speed_fast,
|
||||
game_speed_faster,
|
||||
game_speed_fastest
|
||||
= 0,1,2,3,4,5,6,7
|
||||
= "0","1","2","3","4","5","6","7"
|
||||
|
||||
|
||||
game_difficulty_none,
|
||||
|
@ -124,10 +127,10 @@ game_difficulty_hell,
|
|||
game_difficulty_hardcore_normal,
|
||||
game_difficulty_hardcore_nightmare,
|
||||
game_difficulty_hardcore_hell
|
||||
= 0,1,2,3,4,5,6
|
||||
= "0","1","2","3","4","5","6"
|
||||
|
||||
|
||||
game_flag_none,
|
||||
game_flag_private
|
||||
= 0,1
|
||||
= "0","1"
|
||||
|
||||
|
|
|
@ -25,11 +25,12 @@ function command_host(account, text)
|
|||
return -1
|
||||
end
|
||||
|
||||
if gh_get_userbot(account.name) then
|
||||
local gamename = gh_get_usergame(account.name)
|
||||
local game = game_get_by_name(gamename, account.clienttag, game_type_all)
|
||||
-- if user already host a game
|
||||
if gh_get_userbot_name(account.name) then
|
||||
local gamename = gh_get_userbot_game(account.name)
|
||||
local game = api.game_get_by_name(gamename, account.clienttag, game_type_all)
|
||||
if next(game) then
|
||||
api.message_send_text(account.name, message_type_info, localize("You already host a game \"{}\". Use /unhost to destroy it.", gamename))
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "You already host a game \"{}\". Use /unhost to destroy it.", gamename))
|
||||
return -1
|
||||
else
|
||||
-- if game doesn't exist then remove mapped bot for user
|
||||
|
@ -37,30 +38,23 @@ function command_host(account, text)
|
|||
end
|
||||
end
|
||||
|
||||
-- get bot by ping
|
||||
-- get available bot by ping
|
||||
local botname = gh_select_bot(account.name)
|
||||
if not botname then
|
||||
api.message_send_text(account.name, message_type_error, nil, localize(account.name, "Enable to create game. HostBots are temporary offline."))
|
||||
return -1
|
||||
end
|
||||
|
||||
-- redirect message to bot
|
||||
message_send_ghost(botname, string.format("/pvpgn host %s %s %s %s", account.name, args[1], args[2], args[3]) )
|
||||
gh_message_send(botname, string.format("/pvpgn host %s %s %s %s", account.name, args[1], args[2], args[3]) )
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
gh_maplist = {}
|
||||
|
||||
-- /chost [code] [gamename]
|
||||
function command_chost(account, text)
|
||||
if not config.ghost or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end
|
||||
|
||||
local filename = gh_directory() .. "/maplist.txt"
|
||||
-- load maps from the file
|
||||
file_load(filename, file_load_dictionary_callback,
|
||||
function(a,b)
|
||||
-- split second value to get name and filename
|
||||
local mapname,mapfile = string.split(b,"|")
|
||||
table.insert(gh_maplist, { code = a, name = mapname, filename = mapfile })
|
||||
end)
|
||||
|
||||
local args = split_command(text, 2)
|
||||
|
||||
if not args[1] or not args[2] then
|
||||
|
@ -68,7 +62,7 @@ function command_chost(account, text)
|
|||
|
||||
-- send user each map on a new line
|
||||
for i,map in pairs(gh_maplist) do
|
||||
api.message_send_text(account.name, message_type_info, string.format("%s = %s", map.code, map.name) )
|
||||
api.message_send_text(account.name, message_type_info, nil, string.format("%s = %s", map.code, map.name) )
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
@ -76,20 +70,20 @@ function command_chost(account, text)
|
|||
-- find map by code
|
||||
local mapfile = nil
|
||||
for i,map in pairs(gh_maplist) do
|
||||
if (args[1] == map.code) then mapfile = map.filename end
|
||||
if string.lower(args[1]) == string.lower(map.code) then mapfile = map.filename end
|
||||
end
|
||||
|
||||
if not mapfile then
|
||||
api.message_send_text(account.name, message_type_info, localize(account.name, "Invalid map code.") )
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Invalid map code.") )
|
||||
return -1
|
||||
end
|
||||
|
||||
|
||||
if gh_get_userbot(account.name) then
|
||||
local gamename = gh_get_usergame(account.name)
|
||||
local game = game_get_by_name(gamename, account.clienttag, game_type_all)
|
||||
-- if user already host a game
|
||||
if gh_get_userbot_name(account.name) then
|
||||
local gamename = gh_get_userbot_game(account.name)
|
||||
local game = api.game_get_by_name(gamename, account.clienttag, game_type_all)
|
||||
if next(game) then
|
||||
api.message_send_text(account.name, message_type_info, localize("You already host a game \"{}\". Use /unhost to destroy it.", gamename))
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "You already host a game \"{}\". Use /unhost to destroy it.", gamename))
|
||||
return -1
|
||||
else
|
||||
-- if game doesn't exist then remove mapped bot for user
|
||||
|
@ -97,11 +91,15 @@ function command_chost(account, text)
|
|||
end
|
||||
end
|
||||
|
||||
-- get bot by ping
|
||||
-- get available bot by ping
|
||||
local botname = gh_select_bot(account.name)
|
||||
if not botname then
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Enable to create game. HostBots are temporary offline."))
|
||||
return -1
|
||||
end
|
||||
|
||||
-- redirect message to bot
|
||||
message_send_ghost(botname, string.format("/pvpgn chost %s %s %s", account.name, mapfile, args[2]) )
|
||||
gh_message_send(botname, string.format("/pvpgn chost %s %s %s", account.name, mapfile, args[2]) )
|
||||
return 0
|
||||
end
|
||||
|
||||
|
@ -109,46 +107,36 @@ end
|
|||
function command_unhost(account, text)
|
||||
if not config.ghost or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end
|
||||
|
||||
-- check if user has a mapped bot
|
||||
if gh_get_userbot(account.name) then
|
||||
api.message_send_text(account.name, message_type_info, localize("You don't host a game."))
|
||||
local botname = gh_get_userbot_name(account.name)
|
||||
-- check if user hasn't a mapped bot
|
||||
if not botname then
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "You don't host a game."))
|
||||
return -1
|
||||
end
|
||||
|
||||
-- do not allow unhost if the game is started
|
||||
local game = game_get_by_id(account.game_id)
|
||||
if next(game) and (game.status == game_status_started) then
|
||||
-- do not allow unhost if the game is started (and owner is a mapped user bot -
|
||||
-- it is necessary because we can get duplicate name of another's game,
|
||||
-- due to save/restore table state when Lua rehash)
|
||||
local game = api.game_get_by_id(account.game_id)
|
||||
if next(game) and (game.status == game_status_started) and (game.owner == botname) then
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "You can't unhost a started game."))
|
||||
return -1
|
||||
end
|
||||
|
||||
-- redirect message to bot
|
||||
local botname = gh_get_userbot(account.name)
|
||||
message_send_ghost(botname, string.format("/pvpgn unhost %s %s %s %s", account.name, args[1], args[2], args[3]) )
|
||||
local botname = gh_get_userbot_name(account.name)
|
||||
gh_message_send(botname, string.format("/pvpgn unhost %s", account.name) )
|
||||
|
||||
-- remove mapped bot anyway to make sure that it was removed
|
||||
-- (even if bot casual shutdown before send callback)
|
||||
gh_del_userbot(account.name)
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
-- /ping
|
||||
function command_ping(account, text)
|
||||
if not config.ghost or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end
|
||||
|
||||
local game = game_get_by_id(account.game_id)
|
||||
-- if user not in game
|
||||
if not next(game) then return 1 end
|
||||
|
||||
-- check if game owner is ghost bot
|
||||
if not gh_is_bot(game.owner) then return 1 end
|
||||
|
||||
-- redirect message to bot
|
||||
message_send_ghost(game.owner, string.format("/pvpgn ping %s", account.name))
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Your game was destroyed."))
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
-- /swap [slot1] [slot2]
|
||||
function command_swap(account, text)
|
||||
if not config.ghost or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end
|
||||
|
@ -161,8 +149,8 @@ function command_swap(account, text)
|
|||
end
|
||||
|
||||
-- redirect message to bot
|
||||
local botname = gh_get_userbot(account.name)
|
||||
message_send_ghost(botname, string.format("/pvpgn swap %s %s %s %s", account.name, args[1], args[2]) )
|
||||
local botname = gh_get_userbot_name(account.name)
|
||||
gh_message_send(botname, string.format("/pvpgn swap %s %s %s", account.name, args[1], args[2]) )
|
||||
return 0
|
||||
end
|
||||
|
||||
|
@ -178,8 +166,8 @@ function command_open_close(account, text)
|
|||
end
|
||||
|
||||
-- redirect message to bot
|
||||
local botname = gh_get_userbot(account.name)
|
||||
message_send_ghost(botname, string.format("/pvpgn %s %s %s", args[0], account.name, args[1]) )
|
||||
local botname = gh_get_userbot_name(account.name)
|
||||
gh_message_send(botname, string.format("/pvpgn %s %s %s", args[0], account.name, args[1]) )
|
||||
return 0
|
||||
end
|
||||
|
||||
|
@ -191,90 +179,136 @@ function command_start_abort_pub_priv(account, text)
|
|||
local args = split_command(text, 0)
|
||||
|
||||
-- redirect message to bot
|
||||
local botname = gh_get_userbot(account.name)
|
||||
message_send_ghost(botname, string.format("/pvpgn %s %s %s", args[0], account.name) )
|
||||
local botname = gh_get_userbot_name(account.name)
|
||||
gh_message_send(botname, string.format("/pvpgn %s %s", args[0], account.name) )
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
-- /p
|
||||
function gh_command_ping(account, text)
|
||||
-- if user not in game then do not override command
|
||||
if not account.game_id then return false end
|
||||
local game = api.game_get_by_id(account.game_id)
|
||||
|
||||
-- check if game owner is ghost bot
|
||||
if not gh_is_bot(game.owner) then return 1 end
|
||||
|
||||
-- redirect message to bot
|
||||
gh_message_send(game.owner, string.format("/pvpgn ping %s", account.name))
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
-- /stats
|
||||
function command_stats(account, text)
|
||||
if not config.ghost or not config.ghost_dota_server or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end
|
||||
function gh_command_stats(account, text)
|
||||
|
||||
local useracc = account
|
||||
|
||||
local args = split_command(text, 1)
|
||||
if args[1] then
|
||||
useracc = account_get_by_name(args[1])
|
||||
useracc = api.account_get_by_name(args[1])
|
||||
-- if user not found
|
||||
if not next(useracc) then
|
||||
api.message_send_text(account.name, localize(account, "Invalid user."))
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Invalid user."))
|
||||
return -1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local stats = gh_get_dotastats(useracc)
|
||||
|
||||
-- localized strings
|
||||
local win, loss = localize(account.name, "win"), localize(account.name, "loss")
|
||||
local pts = localize(account.name, "pts")
|
||||
|
||||
local rating5x5 = account_get_dotarating_5x5(useracc.name)
|
||||
local rating3x3 = account_get_dotarating_3x3(useracc.name)
|
||||
local wins5x5 = account_get_dotawins_5x5(useracc.name)
|
||||
local wins3x3 = account_get_dotawins_3x3(useracc.name)
|
||||
local loses5x5 = account_get_dotaloses_5x5(useracc.name)
|
||||
local loses3x3 = account_get_dotaloses_3x3(useracc.name)
|
||||
local streaks5x5 = account_get_dotastreaks_5x5(useracc.name)
|
||||
local streaks3x3 = account_get_dotastreaks_3x3(useracc.name)
|
||||
local leaves5x5 = account_get_dotaleaves_5x5(useracc.name)
|
||||
local leaves3x3 = account_get_dotaleaves_3x3(useracc.name)
|
||||
local game = api.game_get_by_id(account.game_id)
|
||||
|
||||
local rank5x5 = icon_get_rank(rating5x5, CLIENTTAG_WAR3XP)
|
||||
local rank3x3 = icon_get_rank(rating3x3, CLIENTTAG_WAR3XP)
|
||||
-- user who is owner of the hostbot in the current game
|
||||
local owner = gh_find_userbot_by_game(game.name)
|
||||
local gametype = "5x5"--gh_get_userbot_gametype(owner)
|
||||
|
||||
local leaves = leaves5x5 + leaves3x3
|
||||
local leaves_percent = math.round(leaves / ((wins5x5+wins3x3+loses5x5+loses3x3)/100), 1)
|
||||
-- user given in args or (user in game and game is ladder)
|
||||
if not args[1] and next(game) and not string:empty(gametype) then
|
||||
-- iterate all users in the game
|
||||
for u in string.split("harpywar,admin",",") do
|
||||
-- get new stats for the player
|
||||
stats = gh_get_dotastats(api.account_get_by_name(u))
|
||||
|
||||
local country = useracc.country
|
||||
if not country then country = "-" end
|
||||
|
||||
local game = game_get_by_id(account.game_id)
|
||||
-- user in game
|
||||
if next(game) then
|
||||
|
||||
local gametype = "5x5"
|
||||
local rank = rank5x5
|
||||
local rating = rating5x5
|
||||
local rank = stats.rank5x5
|
||||
local rating = stats.rating5x5
|
||||
local leaves = stats.leaves5x5
|
||||
local leaves_percent = stats.leaves5x5_percent
|
||||
|
||||
-- switch gametype if game name has substr "3x3"
|
||||
if string.find(game.name, "3x3") then
|
||||
gametype = "3x3"
|
||||
rank = rank3x3
|
||||
rating = rating3x3
|
||||
if gametype == "3x3" then
|
||||
rank = stats.rank3x3
|
||||
rating = stats.rating3x3
|
||||
leaves = stats.leaves3x3
|
||||
leaves_percent = stats.leaves3x3_percent
|
||||
end
|
||||
|
||||
-- bnproxy stats output format
|
||||
api.message_send_text(account.name, message_type_info, string.format("[%s] %s DotA (%s): [%s] %d pts. Leave count: %d (%f%%)",
|
||||
country, useracc.name, gametype,
|
||||
-- bnproxy stats output format (DO NOT MODIFY!!!)
|
||||
api.message_send_text(account.name, message_type_info, nil, string.format("[%s] %s DotA (%s): [%s] %d pts. Leave count: %d (%f%%)",
|
||||
stats.country, u, gametype,
|
||||
rank, rating,
|
||||
leaves, leaves_percent))
|
||||
end
|
||||
|
||||
else -- in chat
|
||||
api.message_send_text(account.name, message_type_info, localize(account.name, "[{}] {}'s record:", country, useracc.name))
|
||||
api.message_send_text(account.name, message_type_info, localize(account.name, "DotA games ({}): {}-{} [{}] {} pts", "5x5", wins5x5, loses5x5, rank5x5, rating5x5))
|
||||
api.message_send_text(account.name, message_type_info, localize(account.name, "DotA games ({}): {}-{} [{}] {} pts", "3x3", wins3x3, loses3x3, rank3x3, rating3x3))
|
||||
api.message_send_text(account.name, message_type_info, nil, string.format("[%s] ", stats.country) .. localize(account.name, "{}'s record:", useracc.name))
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "DotA games") .. string.format(" (%s): %d-%d [%s] %d %s", "5x5", stats.wins5x5, stats.losses5x5, stats.rank5x5, stats.rating5x5, pts))
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "DotA games") .. string.format(" (%s): %d-%d [%s] %d %s", "3x3", stats.wins3x3, stats.losses3x3, stats.rank3x3, stats.rating3x3, pts))
|
||||
|
||||
-- display streaks in self user stats
|
||||
if not args[1] then
|
||||
local streaks5x5_result, streaks3x3_result = win, win
|
||||
if (streaks5x5 < 0) then streaks5x5_result = loss end
|
||||
if (streaks3x3 < 0) then streaks3x3_result = loss end
|
||||
if (stats.streaks5x5 < 0) then streaks5x5_result = loss end
|
||||
if (stats.streaks3x3 < 0) then streaks3x3_result = loss end
|
||||
|
||||
api.message_send_text(account.name, message_type_info, localize(account.name, "Current {} streak ({}): {}", "5x5", streaks5x5_result, streaks5x5))
|
||||
api.message_send_text(account.name, message_type_info, localize(account.name, "Current {} streak ({}): {}", "3x3", streaks3x3_result, streaks3x3))
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Current {} streak", "5x5") .. string.format(" (%s): %s", streaks5x5_result, stats.streaks5x5))
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Current {} streak", "3x3") .. string.format(" (%s): %s", streaks3x3_result, stats.streaks3x3))
|
||||
end
|
||||
api.message_send_text(account.name, message_type_info, localize(account.name, "Current leave count: {} ({}%)", leaves, leaves_percent))
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Current leave count:") .. string.format(" %d (%s%%)", stats.leaves, stats.leaves_percent))
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
-- return table with all needed dota stats fields
|
||||
function gh_get_dotastats(account)
|
||||
stats = {
|
||||
rating5x5 = account_get_dotarating_5x5(account.name),
|
||||
rating3x3 = account_get_dotarating_3x3(account.name),
|
||||
wins5x5 = account_get_dotawins_5x5(account.name),
|
||||
wins3x3 = account_get_dotawins_3x3(account.name),
|
||||
losses5x5 = account_get_dotalosses_5x5(account.name),
|
||||
losses3x3 = account_get_dotalosses_3x3(account.name),
|
||||
streaks5x5 = account_get_dotastreaks_5x5(account.name),
|
||||
streaks3x3 = account_get_dotastreaks_3x3(account.name),
|
||||
leaves5x5 = account_get_dotaleaves_5x5(account.name),
|
||||
leaves3x3 = account_get_dotaleaves_3x3(account.name),
|
||||
country = account.country
|
||||
}
|
||||
if not stats.country then stats.country = "-" end
|
||||
stats.country = stats.country:sub(1,2) -- two first symbols
|
||||
|
||||
|
||||
stats.leaves5x5_percent = math.round(stats.leaves5x5 / ((stats.wins5x5+stats.losses5x5)/100), 1)
|
||||
stats.leaves3x3_percent = math.round(stats.leaves3x3 / ((stats.wins3x3+stats.losses3x3)/100), 1)
|
||||
if math.isnan(stats.leaves5x5_percent) then stats.leaves5x5_percent = 0 end
|
||||
if math.isnan(stats.leaves3x3_percent) then stats.leaves3x3_percent = 0 end
|
||||
|
||||
stats.leaves = stats.leaves5x5 + stats.leaves3x3
|
||||
stats.leaves_percent = math.round(stats.leaves5x5_percent + stats.leaves3x3_percent, 1)
|
||||
|
||||
|
||||
stats.rank5x5 = api.icon_get_rank(stats.rating5x5, CLIENTTAG_WAR3XP)
|
||||
stats.rank3x3 = api.icon_get_rank(stats.rating3x3, CLIENTTAG_WAR3XP)
|
||||
|
||||
return stats
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ function command_ghost(account, text)
|
|||
if not config.ghost then return 1 end
|
||||
|
||||
-- restrict commands for authorized bots only
|
||||
if not gh_is_bot_authorized(account.name) then return 1 end
|
||||
if not gh_is_bot(account.name) then return 1 end
|
||||
|
||||
local args = split_command(text, 5)
|
||||
local cmd = args[1]
|
||||
|
@ -29,62 +29,88 @@ function command_ghost(account, text)
|
|||
if code ~= "ok" then
|
||||
if code == "err" then
|
||||
local err_message = args[3]
|
||||
-- HINT: handle error here
|
||||
-- HINT: you can handle error here
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
-- /ghost init
|
||||
if (cmd == "init") then
|
||||
DEBUG("(callback) pvpgn mode is activated on bot " .. account.name)
|
||||
elseif (cmd == "gameresult") then
|
||||
gh_callback_gameresult(args[2], args[3], args[4])
|
||||
INFO("GHost bot is connected (" .. account.name .. ")")
|
||||
|
||||
elseif (cmd == "host" or cmd == "chost") then
|
||||
-- /ghost gameresult [players] [ratings] [results]
|
||||
elseif (cmd == "gameresult") then
|
||||
gh_callback_gameresult(args[3], args[4], args[5])
|
||||
|
||||
-- /ghost host [code] [user] [gamename]
|
||||
elseif (cmd == "host") then
|
||||
args = split_command(text, 4) -- support game name with spaces
|
||||
gh_callback_host(args[3], account.name, args[4])
|
||||
-- /ghost chost [code] [user] [gamename]
|
||||
elseif (cmd == "chost") then
|
||||
args = split_command(text, 4) -- support game name with spaces
|
||||
gh_callback_chost(args[3], account.name, args[4])
|
||||
|
||||
-- /ghost unhost [code] [user] [gamename]
|
||||
elseif (cmd == "unhost") then
|
||||
gh_callback_unhost(args[3], args[4])
|
||||
|
||||
-- /ghost ping [code] [user] [players] [pings]
|
||||
elseif (cmd == "ping") then
|
||||
gh_callback_ping(args[3], args[4], args[5])
|
||||
|
||||
-- /ghost swap [code] [user] [username1] [username2]
|
||||
elseif (cmd == "swap") then
|
||||
-- HINT: notify user about success usage
|
||||
-- HINT: you can notify user about success usage
|
||||
|
||||
-- /ghost open [code] [user] [slot]
|
||||
elseif (cmd == "open") or (cmd == "close") then
|
||||
-- HINT: notify user about success usage
|
||||
-- HINT: you can notify user about success usage
|
||||
|
||||
-- /ghost [cmd] [code] [user] [message]
|
||||
elseif (cmd == "start") or (cmd == "abort")
|
||||
or (cmd == "pub") or (cmd == "priv") then
|
||||
-- HINT: notify user about success usage
|
||||
-- HINT: you can notify user about success usage
|
||||
end
|
||||
|
||||
-- FIXME: may be do not log bot commands? (return -1)
|
||||
-- FIXME: may be do not log commands from bot? (return -1)
|
||||
return 0
|
||||
end
|
||||
|
||||
function gh_callback_host(username, botname, gamename)
|
||||
gh_set_userbot(username, botname, gamename)
|
||||
api.message_send_text(username, message_type_info, "Game " .. gamename .. " created")
|
||||
local gametype = "5x5"
|
||||
if string.find(game.name, "3x3") then gametype = "3x3" end
|
||||
|
||||
gh_set_userbot(username, botname, gamename, gametype)
|
||||
api.message_send_text(username, message_type_info, nil, localize(username, "Game \"{}\" is created by {}. You are an owner.", gamename, botname))
|
||||
end
|
||||
function gh_callback_chost(username, botname, gamename)
|
||||
gh_set_userbot(username, botname, gamename, "")
|
||||
api.message_send_text(username, message_type_info, nil, localize(username, "Game \"{}\" is created by {}. You are an owner.", gamename, botname))
|
||||
end
|
||||
|
||||
|
||||
function gh_callback_unhost(username, gamename)
|
||||
gh_del_userbot(username)
|
||||
api.message_send_text(username, message_type_info, "Game " .. gamename .. " destroyed")
|
||||
api.message_send_text(username, message_type_info, nil, localize(username, "The game \"{}\" was destroyed.", gamename))
|
||||
end
|
||||
|
||||
|
||||
function gh_callback_ping(username, players, pings)
|
||||
-- make sure that user is in a game
|
||||
-- make sure that user is still in a game
|
||||
local account = api.account_get_by_name(username)
|
||||
local game = api.game_get_by_id(account.game_id)
|
||||
if not next(game) then return end
|
||||
-- make sure game owner is a bot
|
||||
if not gh_is_bot(game.owner) then return end
|
||||
|
||||
-- silent flag (do not send result to user, just update in db)
|
||||
local silent = gh_read_silentping(username)
|
||||
|
||||
-- get silent flag (do not send result to user)
|
||||
-- it's needed to do not show ping when user join a game, but we want to save the ping into database
|
||||
local silent = gh_get_silentflag(username)
|
||||
|
||||
local users = {}
|
||||
|
||||
-- fill table users
|
||||
i = 1
|
||||
-- fill usernames
|
||||
for v in string.split(players, ",") do
|
||||
-- init each user table
|
||||
users[i] = {}
|
||||
|
@ -92,9 +118,8 @@ function gh_callback_ping(username, players, pings)
|
|||
i = i + 1
|
||||
end
|
||||
i = 1
|
||||
-- fill pings
|
||||
for v in string.split(pings, ",") do
|
||||
-- init each user table
|
||||
users[i] = {}
|
||||
users[i]["ping"] = v
|
||||
i = i + 1
|
||||
end
|
||||
|
@ -107,13 +132,13 @@ function gh_callback_ping(username, players, pings)
|
|||
local pings = account_get_botping(u["name"])
|
||||
local is_found = false
|
||||
for k,v in pairs(pings) do
|
||||
-- update user pings for current bot
|
||||
-- update user ping for current bot
|
||||
if (v.bot == game.owner) then
|
||||
pings[k].ping = u["ping"]
|
||||
is_found = true
|
||||
end
|
||||
end
|
||||
-- if row not found for the botname then add a new one
|
||||
-- if bot not found in database pings then add a new row
|
||||
if not is_found then
|
||||
local item = {
|
||||
date = os.time(),
|
||||
|
@ -124,20 +149,19 @@ function gh_callback_ping(username, players, pings)
|
|||
end
|
||||
|
||||
-- save updated pings
|
||||
account_set_ping2bot(u["name"], pings)
|
||||
account_set_botping(u["name"], pings)
|
||||
|
||||
latency_all = latency_all .. string.format("{}: [{} {}]; ", u["name"], u["ping"], ms)
|
||||
latency_all = latency_all .. string.format("%s: [%s %s]; ", u["name"], u["ping"], ms)
|
||||
|
||||
-- if game started send ping of each players on a new line
|
||||
if (game.status == game_status_started) and not (silent) then
|
||||
api.message_send_text(username, message_type_info, localize(username, "{}'s latency to {}: {} {}", u["name"], game.owner, u["ping"], ms))
|
||||
api.message_send_text(username, message_type_info, nil, localize(username, "{}'s latency to {}: {} {}", u["name"], game.owner, u["ping"], ms))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- if game not started send pings of all players in a single line
|
||||
if not (game.status == game_status_started) and not (silent) then
|
||||
api.message_send_text(u["name"], message_type_info, localize(username, "Latency: {}", latency_all))
|
||||
api.message_send_text(username, message_type_info, nil, localize(username, "Latency: {}", latency_all))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -155,12 +179,12 @@ function gh_callback_gameresult(players, ratings, results)
|
|||
end
|
||||
i = 1
|
||||
for v in string.split(ratings, ",") do
|
||||
users[i]["rating"] = v
|
||||
users[i]["rating"] = tonumber(v)
|
||||
i = i + 1
|
||||
end
|
||||
i = 1
|
||||
for v in string.split(results, ",") do
|
||||
users[i]["result"] = v
|
||||
users[i]["result"] = tonumber(v)
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
|
@ -203,18 +227,21 @@ function gh_callback_gameresult(players, ratings, results)
|
|||
losses = losses + 1
|
||||
end
|
||||
|
||||
if streaks >= 0 then -- win
|
||||
local result_str = localize(u["name"], "won")
|
||||
|
||||
if streaks >= 0 then -- win streak
|
||||
if u["result"] > 0 then
|
||||
streaks = streaks + 1
|
||||
else
|
||||
streaks = 1
|
||||
streaks = -1
|
||||
end
|
||||
else -- loss
|
||||
else -- loss streak
|
||||
if u["result"] <= 0 then
|
||||
streaks = streaks - 1
|
||||
else
|
||||
streaks = -1
|
||||
streaks = 1
|
||||
end
|
||||
result_str = localize(u["name"], "lose")
|
||||
end
|
||||
|
||||
-- update stats
|
||||
|
@ -233,12 +260,11 @@ function gh_callback_gameresult(players, ratings, results)
|
|||
end
|
||||
|
||||
-- diff between old and new rating
|
||||
local points = rating-u["rating"]
|
||||
local result_str = localize("won")
|
||||
if pts < 0 then result_str = localize("lose") end
|
||||
local points = u["rating"]-rating
|
||||
|
||||
-- inform user about new stats
|
||||
api.message_send_text(u["name"], message_type_info, localize(u["name"], "You {} {} points. New DotA ({}) rating: {} pts. Current {} streak: {}", result_str, points, rating, gametype, result_str, streaks))
|
||||
api.message_send_text(u["name"], message_type_info, nil, localize(u["name"], "You {} {} points.", result_str, points))
|
||||
api.message_send_text(u["name"], message_type_info, nil, localize(u["name"], "New DotA ({}) rating: {} pts. Current {} streak: {}", gametype, rating, result_str, streaks))
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -5,25 +5,53 @@
|
|||
Licensed under the same terms as Lua itself.
|
||||
]]--
|
||||
|
||||
-- custom map list
|
||||
gh_maplist = {}
|
||||
|
||||
function gh_load()
|
||||
local filename = gh_directory() .. "/users.dmp"
|
||||
|
||||
if not file_exists(filename) then
|
||||
return
|
||||
-- load ghost state
|
||||
local filename = config.vardir() .. "ghost_users.dmp"
|
||||
if file_exists(filename) then
|
||||
gh_load_userbots(filename)
|
||||
DEBUG("Loaded GHost state from " .. filename)
|
||||
end
|
||||
|
||||
-- load ghost state
|
||||
gh_load_userbot(filename)
|
||||
-- preload custom map list
|
||||
local mapfile = gh_directory() .. "/maplist.txt"
|
||||
if file_exists(mapfile) then
|
||||
gh_load_maps(mapfile)
|
||||
DEBUG("Loaded " .. #gh_maplist .. " GHost custom maps")
|
||||
else
|
||||
WARN("File with GHost custom map list is not found: " .. mapfile)
|
||||
end
|
||||
|
||||
DEBUG("Loaded GHost state from " .. filename)
|
||||
end
|
||||
|
||||
|
||||
function gh_unload()
|
||||
-- save ghost state
|
||||
local filename = gh_directory() .. "/users.dmp"
|
||||
gh_save_userbot(filename)
|
||||
local filename = config.vardir() .. "ghost_users.dmp"
|
||||
gh_save_userbots(filename)
|
||||
|
||||
DEBUG("Saved GHost state to " .. filename)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
function gh_load_maps(filename)
|
||||
-- load maps from the file
|
||||
file_load(filename, file_load_dictionary_callback,
|
||||
function(a,b)
|
||||
-- split second part of the line to get mapname and filename
|
||||
local idx = 0
|
||||
for v in string.split(b, "|") do
|
||||
if idx == 0 then
|
||||
mapname = string:trim(v)
|
||||
else
|
||||
mapfile = string:trim(v)
|
||||
end
|
||||
idx = idx + 1
|
||||
end
|
||||
table.insert(gh_maplist, { code = a, name = mapname, filename = mapfile })
|
||||
end)
|
||||
end
|
|
@ -11,26 +11,35 @@ function gh_handle_game_userjoin(game, account)
|
|||
if not gh_is_bot(game.owner) then return end
|
||||
|
||||
-- send ping silently
|
||||
gh_write_silentping(account.name)
|
||||
gh_set_silentflag(account.name)
|
||||
|
||||
local botaccount = account_get_by_name(game.owner)
|
||||
local botaccount = api.account_get_by_name(game.owner)
|
||||
-- send ping to bot (to save result)
|
||||
command_ping(botaccount, "/ping")
|
||||
|
||||
if (config.ghost_dota_server) then
|
||||
-- show stats of each player in the game
|
||||
-- user who is owner of the hostbot in the current game
|
||||
local owner = gh_find_userbot_by_game(game.name)
|
||||
-- if game is not ladder
|
||||
if string:empty(gh_get_userbot_gametype(owner)) then return end
|
||||
|
||||
|
||||
for u in string.split(game.players, ",") do
|
||||
-- (except current player)
|
||||
if (account.name ~= u) then
|
||||
command_stats(botaccount, "/stats " .. u)
|
||||
end
|
||||
-- show stats of the player who joined the game for each other player
|
||||
local useracc = api.account_get_by_name(u)
|
||||
command_stats(useracc, "/stats " .. account.name)
|
||||
|
||||
-- show stats of each player in the game to a player who joined the game
|
||||
command_stats(account, "/stats " .. u)
|
||||
end
|
||||
api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Joined ladder game. Game owner: {}", owner))
|
||||
end
|
||||
end
|
||||
|
||||
function gh_handle_user_login(account)
|
||||
if gh_is_bot(account.name) then
|
||||
-- activate pvpgn mode on ghost side
|
||||
gh_message_send(botname, "/pvpgn init");
|
||||
gh_message_send(account.name, "/pvpgn init");
|
||||
DEBUG("init bot " .. account.name)
|
||||
end
|
||||
end
|
|
@ -13,43 +13,65 @@ gh_user2bot = {}
|
|||
-- array with users who will not receive a response from /ping
|
||||
silentpings = {}
|
||||
|
||||
-- return a user mapped bot
|
||||
-- return a user mapped bot name
|
||||
-- (nil if bot not mapped)
|
||||
function gh_get_userbot(username)
|
||||
if not next(user2bot) or not user2bot[username] then return nil end
|
||||
return user2bot[username].bot
|
||||
function gh_get_userbot_name(username)
|
||||
if not next(gh_user2bot) or not gh_user2bot[username] then return nil end
|
||||
return gh_user2bot[username].bot
|
||||
end
|
||||
function gh_get_usergame(username)
|
||||
if not next(user2bot) or not user2bot[username] then return nil end
|
||||
return user2bot[username].game
|
||||
-- return a user mapped game name that created by bot
|
||||
function gh_get_userbot_game(username)
|
||||
if not next(gh_user2bot) or not gh_user2bot[username] then return nil end
|
||||
return gh_user2bot[username].game
|
||||
end
|
||||
function gh_set_userbot(username, botname, gamename)
|
||||
user2bot[username] = { bot = botname, game = gamename }
|
||||
-- return a user mapped game type that created by bot
|
||||
function gh_get_userbot_gametype(username)
|
||||
if not next(gh_user2bot) or not gh_user2bot[username] then return nil end
|
||||
return gh_user2bot[username].gametype
|
||||
end
|
||||
-- find gamename in games and return owner of the game (username)
|
||||
-- return false if not found
|
||||
function gh_find_userbot_by_game(gamename)
|
||||
for u,bot in pairs(gh_user2bot) do
|
||||
if (bot.game == gamename) then return u end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function gh_set_userbot(username, botname, gamename, gametype)
|
||||
gh_user2bot[username] = { bot = botname, game = gamename, type = gametype, time = os.time() }
|
||||
end
|
||||
function gh_del_userbot(username)
|
||||
user2bot[username] = nil
|
||||
gh_user2bot[username] = nil
|
||||
end
|
||||
-- save table to file
|
||||
function gh_save_userbot(filename)
|
||||
-- do not save empty table
|
||||
if not next(gh_user2bot) then return end
|
||||
|
||||
-- save table to file
|
||||
function gh_save_userbots(filename)
|
||||
table.save(gh_user2bot, filename)
|
||||
return true
|
||||
end
|
||||
-- load table from file
|
||||
function gh_load_userbot(filename)
|
||||
function gh_load_userbots(filename)
|
||||
gh_user2bot = table.load(filename)
|
||||
|
||||
-- debug info
|
||||
if next(gh_user2bot) then
|
||||
TRACE("Users assigned to bots:")
|
||||
for u,bot in pairs(gh_user2bot) do
|
||||
TRACE(u..": "..bot.bot.." ("..bot.game..", "..bot.time..")")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
-- add user into a silenttable
|
||||
function gh_write_silentping(username)
|
||||
function gh_set_silentflag(username)
|
||||
table.insert(silentpings, account.name)
|
||||
end
|
||||
-- return true if user in a silenttable, and remove it
|
||||
function gh_read_silentping(username)
|
||||
function gh_get_silentflag(username)
|
||||
for k,v in pairs(silentpings) do
|
||||
if v == username then
|
||||
table.remove(silentpings, k)
|
||||
|
@ -74,7 +96,7 @@ function gh_is_owner(account)
|
|||
|
||||
local game = game_get_by_id(account.game_id)
|
||||
-- 2) user owner of the bot in current game
|
||||
if not game.owner == gh_get_userbot(account.name) then return false end
|
||||
if not game.owner == gh_get_userbot_name(account.name) then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
@ -85,7 +107,7 @@ end
|
|||
-- is bot in authorized config list?
|
||||
function gh_is_bot(username)
|
||||
for i,bot in pairs(config.ghost_bots) do
|
||||
if (bot == username) then return true end
|
||||
if string.lower(bot) == string.lower(username) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
@ -93,14 +115,14 @@ end
|
|||
|
||||
-- redirect command from user to bot through PvPGN
|
||||
function gh_message_send(botname, text)
|
||||
|
||||
-- send message from the server to ghost
|
||||
api.message_send_text(nil, message_type_whisper, botname, text)
|
||||
-- send message to ghost from the server
|
||||
api.message_send_text(botname, message_type_whisper, nil, text)
|
||||
end
|
||||
|
||||
-- return bot name (the best for user by ping)
|
||||
-- return nil if no available bots
|
||||
function gh_select_bot(username)
|
||||
local pings = account_get_ping2bot(username)
|
||||
local pings = account_get_botping(username)
|
||||
local botname = nil
|
||||
|
||||
-- iterate all bots in config
|
||||
|
@ -121,6 +143,17 @@ function gh_select_bot(username)
|
|||
botname = pings[1].bot
|
||||
end
|
||||
|
||||
local botacc = api.account_get_by_name(botname)
|
||||
-- if bot is offline then use first available
|
||||
-- FIXME: use next available by the best ping?
|
||||
if botacc.online == "false" then
|
||||
botname = nil
|
||||
for i,bot in pairs(config.ghost_bots) do
|
||||
botacc = api.account_get_by_name(bot)
|
||||
if botacc.online == "true" then return bot end
|
||||
end
|
||||
end
|
||||
|
||||
return botname
|
||||
end
|
||||
|
||||
|
|
|
@ -11,3 +11,6 @@ function math.round(num, idp)
|
|||
local mult = 10^(idp or 0)
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
end
|
||||
|
||||
function math.isnan(x) return x ~= x end
|
||||
function math.isinf(x) return (x+1)==x end
|
|
@ -20,6 +20,7 @@ end
|
|||
|
||||
-- Check string is nil or empty
|
||||
-- bool
|
||||
-- Usage example: string:empty("") -> true, string:empty(nil) -> true
|
||||
function string:empty(str)
|
||||
return str == nil or str == ''
|
||||
end
|
||||
|
@ -37,6 +38,7 @@ function string.ends(str, ends)
|
|||
end
|
||||
|
||||
-- Replace string
|
||||
-- Usage example: string.replace("hello world","world","Joe") -> "hello Joe"
|
||||
function string.replace(str, pattern, replacement)
|
||||
if string:empty(str) then return str end
|
||||
local s, n = string.gsub(str, pattern, replacement)
|
||||
|
|
|
@ -60,6 +60,13 @@ function q_command_start(account, filename)
|
|||
end
|
||||
|
||||
quiz:start(channel.name, filename)
|
||||
|
||||
|
||||
i =0
|
||||
for t=0,1000000 do
|
||||
file_save(t, "test.txt")
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue