506 lines
14 KiB
Lua
506 lines
14 KiB
Lua
--[[
|
|
This file is part of Ice Lua Components.
|
|
|
|
Ice Lua Components is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Ice Lua Components is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with Ice Lua Components. If not, see <http://www.gnu.org/licenses/>.
|
|
]]
|
|
|
|
SANDBOX_SERVER = false
|
|
|
|
if common.version.num < 8388608 then
|
|
error("You need Iceball version 0.2 or later to run this code.")
|
|
end
|
|
|
|
if SANDBOX_SERVER and not sandbox then
|
|
return (loadfile("pkg/base/sandbox/main.lua"))(...)
|
|
end
|
|
|
|
dofile("pkg/base/lib_fastload.lua")
|
|
|
|
-- UDP port test.
|
|
if false then
|
|
local sk = common.udp_open()
|
|
print("sk:", sk)
|
|
m = common.udp_sendto(sk, "Hello World!", "127.0.0.1", 9999)
|
|
print("M:", m)
|
|
local ctime = common.time()
|
|
while common.time() <= ctime do end
|
|
local m2, h, p
|
|
m2, h, p = common.udp_recvfrom(sk)
|
|
print("recv:", m2, h, p)
|
|
common.udp_sendto(sk, "pang", h, p)
|
|
common.udp_close(sk)
|
|
end
|
|
|
|
dofile("pkg/base/preconf.lua")
|
|
dofile("pkg/base/lib_util.lua")
|
|
|
|
print("pkg/base/main_server.lua starting")
|
|
print(...)
|
|
|
|
dofile("pkg/iceball/ircbot/main.lua")
|
|
|
|
-- parse arguments
|
|
local loose, server_toggles, server_settings = parse_commandline_options({...})
|
|
local server_config_filename = server_settings['server'] or "svsave/pub/server.json"
|
|
server_config = common.json_load(server_config_filename)
|
|
-- TODO: Check that server_config ~= nil
|
|
if server_settings.svseed then
|
|
math.randomseed(0+server_settings.svseed)
|
|
elseif common.time ~= nil then
|
|
math.randomseed(common.time())
|
|
end
|
|
|
|
if server_config.irc then
|
|
local k, v
|
|
for k, v in pairs(server_config.irc) do
|
|
if v.enabled then
|
|
irc.connect(v)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- load mod config
|
|
game_mode_file = server_config.mode or GAME_MODE
|
|
GAME_MODE = game_mode_file
|
|
print("Game mode:", GAME_MODE)
|
|
mod_conf_file = server_config.mod_config or "svsave/pub/mods.json"
|
|
mod_data = common.json_load(mod_conf_file)
|
|
|
|
-- load mod JSON files
|
|
dofile("pkg/base/lib_mods.lua")
|
|
load_mod_list(getfenv(), mod_data.mods, {"preload", "preload_server"}, server_config, mod_data)
|
|
|
|
dofile("pkg/base/common.lua")
|
|
dofile("pkg/base/commands.lua")
|
|
dofile("pkg/base/lib_heartbeat.lua")
|
|
|
|
client_list = {fdlist={}, banned={}}
|
|
server_tick_accum = 0
|
|
|
|
map_fname = loose[1]
|
|
|
|
function slot_add(neth, tidx, wpn, name)
|
|
local i
|
|
for i=1,players.max do
|
|
if not players[i] then
|
|
if tidx < 0 or tidx > 1 then
|
|
-- TODO: actually balance this properly!
|
|
tidx = (i-1) % 2
|
|
end
|
|
local _wpn
|
|
if MODE_TEAM_GUNS then
|
|
_wpn = tidx + 1
|
|
else
|
|
_wpn = wpn
|
|
end
|
|
players[i] = new_player({
|
|
name = name,
|
|
--[[squad = squads[(i-1) % 2][
|
|
(math.floor((i-1)/2) % 4)+1],]]
|
|
squad = nil,
|
|
team = tidx, -- 0 == blue, 1 == green
|
|
weapon = _wpn,
|
|
pid = i,
|
|
neth = neth
|
|
})
|
|
if permissions["default"] ~= nil then
|
|
players[i].add_permission_group(permissions["default"].perms)
|
|
print("Adding default permissions for user")
|
|
else
|
|
print("Default permissions do not exist")
|
|
end
|
|
return i
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
function server.hook_file(neth, ftype, fname)
|
|
print("hook_file:", neth, ftype, fname)
|
|
local cli = client_list[neth]
|
|
if cli then cli.lastmsg = sec_current end
|
|
|
|
if client_list.banned[neth] then
|
|
if ftype == "lua" then
|
|
return "pkg/base/banned_client.lua"
|
|
elseif ftype == "tga" then
|
|
return "pkg/base/gfx/banned.tga"
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
if fname:lower():find("svsave") then
|
|
return nil
|
|
end
|
|
|
|
if (ftype == "icemap" or ftype == "map") and (fname == "*MAP") then
|
|
return map_loaded
|
|
elseif (ftype == "bin") and (fname == "*FASTLOAD") then
|
|
return "svsave/vol/fldata.bin"
|
|
elseif (ftype == "json") and (fname == "*MODCFG") then
|
|
return mod_conf_file
|
|
elseif (ftype == "lua") and (fname == "*GAMEMODE") then
|
|
return GAME_MODE
|
|
elseif (ftype == "png") and (fname == "*MAPIMG") then
|
|
if map_fname then
|
|
return map_fname..".png"
|
|
elseif server_settings.gen then
|
|
return server_settings.gen..".png"
|
|
else
|
|
return "pkg/maps/gen_classic.lua.png" --by default it will load gen_classic anyway
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function server.hook_connect(neth, addrinfo)
|
|
-- TODO: enforce bans
|
|
client_list.fdlist[#(client_list.fdlist)+1] = neth
|
|
client_list[neth] = {
|
|
fdidx = #(client_list.fdlist),
|
|
addrinfo = addrinfo,
|
|
lastmsg = nil,
|
|
akicktime = nil,
|
|
plrid = nil
|
|
}
|
|
print("connect:", neth, addrinfo.proto,
|
|
addrinfo.addr and addrinfo.addr.sport,
|
|
addrinfo.addr and addrinfo.addr.ip,
|
|
addrinfo.addr and addrinfo.addr.cport)
|
|
|
|
-- workaround for pre-0.1.1-4 versions
|
|
server.port = server.port or (addrinfo.addr and addrinfo.addr.sport)
|
|
|
|
local source = false
|
|
local ip_name = "<?>"
|
|
if addrinfo.proto == "enet/ip6" or addrinfo.proto == "tcp/ip6" then
|
|
ip_name = addrinfo.addr.ip
|
|
-- There are two variants:
|
|
-- the windows variant is a blatant hack, but valid
|
|
-- the not-windows variant is much smaller
|
|
print("ipv6", addrinfo.addr.ip)
|
|
-- not-windows
|
|
source = source or addrinfo.addr.ip:sub(13) == "::ffff:90.16."
|
|
source = source or addrinfo.addr.ip:sub(13) == "::ffff:90.55."
|
|
source = source or addrinfo.addr.ip:sub(14) == "::ffff:86.199."
|
|
-- windows
|
|
source = source or addrinfo.addr.ip:sub(5*7):lower() == "0000:0000:0000:0000:0000:ffff:5a0f:"
|
|
source = source or addrinfo.addr.ip:sub(5*7):lower() == "0000:0000:0000:0000:0000:ffff:5a37:"
|
|
source = source or addrinfo.addr.ip:sub(5*7):lower() == "0000:0000:0000:0000:0000:ffff:56c7:"
|
|
elseif addrinfo.proto == "enet/ip" or addrinfo.proto == "tcp/ip" then
|
|
ip_name = addrinfo.addr.ip
|
|
print("ipv4", addrinfo.addr.ip)
|
|
source = source or addrinfo.addr.ip:sub(6) == "90.16."
|
|
source = source or addrinfo.addr.ip:sub(6) == "90.55."
|
|
source = source or addrinfo.addr.ip:sub(7) == "86.199."
|
|
end
|
|
|
|
client_list.banned[neth] = source
|
|
if source then
|
|
client_list[neth].akick = true
|
|
end
|
|
|
|
ip_name = addrinfo.proto..":"..ip_name
|
|
client_list[neth].ip_name = ip_name
|
|
local ss = (neth == true and "(local)") or neth
|
|
--[[net_broadcast(nil, common.net_pack("BIz", PKT_CHAT_ADD_TEXT, 0xFF800000,
|
|
"Connected: player on neth "..ss))]]
|
|
print("Connected: player on neth "..ss)
|
|
irc.write("Connected: player on neth "..ss.." ("..ip_name..")")
|
|
end
|
|
|
|
function server.hook_disconnect(neth, server_force, reason)
|
|
-- just in case we get any stray disconnect messages
|
|
if not client_list[neth] then return end
|
|
|
|
local plrid = client_list[neth].plrid
|
|
local plr = players[plrid]
|
|
local ip_name = client_list[neth].ip_name
|
|
|
|
local fdidx = client_list[neth].fdidx
|
|
local cli2 = client_list[client_list.fdlist[#(client_list.fdlist)]]
|
|
cli2.fdidx = fdidx
|
|
client_list.fdlist[fdidx] = client_list.fdlist[#(client_list.fdlist)]
|
|
client_list.fdlist[#(client_list.fdlist)] = nil
|
|
client_list[neth] = nil
|
|
print("disconnect:", neth, server_force, reason)
|
|
|
|
local ss = (neth == true and "(local)") or neth
|
|
--[[net_broadcast(nil, common.net_pack("BIz", PKT_CHAT_ADD_TEXT, 0xFF800000,
|
|
"Disconnected: player on neth "..ss))]]
|
|
print("Disconnected: player on neth "..ss)
|
|
irc.write("Disconnected: player on neth "..ss.."("..ip_name..", #"..(plrid or "nil")..")")
|
|
|
|
if plr then
|
|
plr.on_disconnect()
|
|
net_broadcast(nil, common.net_pack("BIz", PKT_CHAT_ADD_TEXT, 0xFF800000,
|
|
"* Player "..plr.name.." disconnected"))
|
|
net_broadcast(neth, common.net_pack("BB",
|
|
PKT_PLR_RM, plrid))
|
|
|
|
-- TODO fix crash bug
|
|
--plr.free()
|
|
players[plrid] = nil
|
|
end
|
|
end
|
|
|
|
lflush = nil
|
|
function server.hook_tick(sec_current, sec_delta)
|
|
heartbeat_update(sec_current, sec_delta)
|
|
irc.update(sec_current, sec_delta)
|
|
|
|
--print("tick",sec_current,sec_delta)
|
|
--[[
|
|
local xlen,ylen,zlen
|
|
xlen,ylen,zlen = common.map_get_dims()
|
|
]]
|
|
|
|
if (not lflush) or sec_current < lflush - 0.8 then
|
|
lflush = sec_current
|
|
end
|
|
if sec_current >= lflush then
|
|
net_send_flush()
|
|
lflush = lflush + NET_FLUSH_S2C
|
|
if sec_current <= lflush then
|
|
lflush = sec_current
|
|
end
|
|
end
|
|
|
|
local pkt, neth, cli
|
|
local kicklist = {}
|
|
for neth, cli in pairs(client_list) do
|
|
if type(neth) == type(0) or neth == true then
|
|
--print(neth,cli.lastmsg, sec_current)
|
|
if cli.akick == true then
|
|
cli.akick = sec_current + 7
|
|
end
|
|
|
|
if cli.akick and sec_current >= cli.akick then
|
|
kicklist[#kicklist+1] = neth
|
|
elseif not cli.lastmsg then
|
|
cli.lastmsg = sec_current
|
|
elseif neth ~= true and cli.lastmsg + NET_MAX_LAG < sec_current then
|
|
-- don't autokick the local client - it never ACTUALLY "disconnects"
|
|
-- otherwise we'll be chewing through this over and over again
|
|
print("Autokicking client "..((neth == true and "local") or neth))
|
|
server.net_kick(neth, "Connection timed out")
|
|
-- net_disconnect should be called by this point
|
|
end
|
|
end
|
|
end
|
|
|
|
local i
|
|
for i=1,#kicklist do
|
|
server.net_kick(kicklist[i], "Autokick")
|
|
end
|
|
|
|
while true do
|
|
pkt, neth = common.net_recv()
|
|
if not pkt then break end
|
|
|
|
local cli = client_list[neth]
|
|
local plr = cli and players[cli.plrid]
|
|
|
|
if cli then cli.lastmsg = sec_current end
|
|
|
|
local cid
|
|
cid, pkt = common.net_unpack("B", pkt)
|
|
|
|
--print("in",neth,cid)
|
|
|
|
local hdl = network.sys_tab_handlers[cid]
|
|
if hdl then
|
|
hdl.f(neth, cli, plr, sec_current, common.net_unpack(hdl.s, pkt))
|
|
else
|
|
print(string.format("S: unhandled packet %02X", cid))
|
|
end
|
|
-- TODO!
|
|
end
|
|
bhealth_prune(sec_current)
|
|
|
|
local tickrate = 1/60.
|
|
local lowest_fps = 15
|
|
local max_ticksize = 1/lowest_fps
|
|
|
|
if sec_delta > max_ticksize then sec_delta = max_ticksize end
|
|
if sec_delta < -max_ticksize then sec_delta = -max_ticksize end
|
|
|
|
local moment = sec_current - sec_delta
|
|
server_tick_accum = server_tick_accum + sec_delta
|
|
|
|
while server_tick_accum > tickrate do
|
|
moment = moment + tickrate
|
|
local i
|
|
for i=1,players.max do
|
|
local plr = players[i]
|
|
if plr then
|
|
plr.tick(moment, tickrate)
|
|
end
|
|
end
|
|
for i=nades.head,nades.tail do
|
|
if nades[i] then nades[i].tick(moment, tickrate) end
|
|
end
|
|
nade_prune(sec_current)
|
|
|
|
for i=1,#miscents do
|
|
miscents[i].tick(moment, tickrate)
|
|
end
|
|
server_tick_accum = server_tick_accum - tickrate
|
|
end
|
|
|
|
return 0.005
|
|
end
|
|
|
|
permissions = {}
|
|
|
|
if server_config.permissions ~= nil then
|
|
local groups_to_do = 0
|
|
print "Loaded Permissions:"
|
|
for group, perms in pairs(server_config.permissions) do
|
|
print(" Group: "..group)
|
|
permissions[group] = {}
|
|
permissions[group]["perms"] = {}
|
|
if perms.password ~= nil then
|
|
permissions[group]["password"] = perms.password
|
|
else
|
|
permissions[group]["password"] = ""
|
|
end
|
|
if perms.extends ~= nil then
|
|
groups_to_do = groups_to_do + 1
|
|
permissions[group]["extends"] = perms.extends
|
|
else
|
|
permissions[group]["extends"] = ""
|
|
end
|
|
print(" Password: "..permissions[group]["password"])
|
|
print(" Extends: "..permissions[group]["extends"])
|
|
if perms.permissions ~= nil then
|
|
print(" Permissions:")
|
|
for k, v in pairs(perms.permissions) do
|
|
print(" * "..v)
|
|
permissions[group]["perms"][v] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Hopefully this should allow full inheritance without an infinite loop
|
|
-- I know it's messy - if you don't like it, feel free to redo it ;)
|
|
local groups_done = {}
|
|
local do_extends = true
|
|
local changed = true
|
|
while do_extends and changed do
|
|
changed = false
|
|
for group, perms in pairs(permissions) do
|
|
if groups_done[group] == nil then
|
|
if perms["extends"] ~= "" then
|
|
if permissions[perms["extends"]]["extends"] == "" then
|
|
groups_done[perms["extends"]] = true
|
|
--extend away!
|
|
for k,v in pairs(permissions[perms["extends"]]["perms"]) do
|
|
if perms["perms"]["-"..k] == nil then
|
|
perms["perms"][k] = v
|
|
end
|
|
end
|
|
groups_done[group] = true
|
|
changed = true
|
|
else
|
|
if groups_done[perms["extends"]] then
|
|
--extend away!
|
|
for k,v in pairs(permissions[perms["extends"]]["perms"]) do
|
|
if perms["perms"]["-"..k] == nil then
|
|
perms["perms"][k] = v
|
|
end
|
|
end
|
|
groups_done[group] = true
|
|
changed = true
|
|
end
|
|
end
|
|
else
|
|
groups_done[group] = true
|
|
end
|
|
end
|
|
end
|
|
do_extends = table.getn(groups_done) < groups_to_do
|
|
end
|
|
|
|
-- Print final permissions
|
|
print "Final Permissions:"
|
|
for group, perms in pairs(permissions) do
|
|
print(" Group: "..group)
|
|
print(" Password: "..perms.password)
|
|
print(" Extends: "..perms.extends)
|
|
print(" Permissions:")
|
|
for k, v in pairs(perms.perms) do
|
|
print(" * "..k)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- load map
|
|
if server_settings.gen then
|
|
map_loaded, map_name = loadfile(server_settings.gen)(loose, server_toggles, server_settings)
|
|
elseif map_fname then
|
|
map_loaded = common.map_load(map_fname, "auto")
|
|
map_name = map_fname
|
|
while map_name do
|
|
local p = map_name:find("/", 1, true)
|
|
if not p then break end
|
|
map_name = map_name:sub(p+1)
|
|
end
|
|
else
|
|
map_loaded, map_name = loadfile("pkg/base/gen_classic.lua")(loose, server_toggles, server_settings)
|
|
end
|
|
|
|
if not map_name then
|
|
map_name = "<?>"
|
|
end
|
|
|
|
game_hb_mode = game_mode_file
|
|
while true do
|
|
local p = game_hb_mode:find("/", 1, true)
|
|
if not p then break end
|
|
game_hb_mode = game_hb_mode:sub(p+1)
|
|
end
|
|
if game_hb_mode:sub(1,5) == "mode_" then
|
|
game_hb_mode = game_hb_mode:sub(6)
|
|
end
|
|
do
|
|
local k = game_hb_mode:len()-4
|
|
if game_hb_mode:sub(k+1) == ".lua" then
|
|
game_hb_mode = game_hb_mode:sub(1,k)
|
|
end
|
|
end
|
|
|
|
common.map_set(map_loaded)
|
|
|
|
mode_create_server()
|
|
|
|
print("pkg/base/main_server.lua: Loading mods...")
|
|
load_mod_list(getfenv(), mod_data.mods, {"load", "load_server"}, server_config, mod_data)
|
|
|
|
if server_toggles.flcache then
|
|
print("pkg/base/main_server.lua: Analysing fastload...")
|
|
fastload_analyse_client()
|
|
end
|
|
|
|
print("pkg/base/main_server.lua: Creating fastload pack...")
|
|
fastload_pack_client()
|
|
|
|
print("Starting heartbeat server...")
|
|
heartbeat_init()
|
|
print("pkg/base/main_server.lua loaded.")
|
|
|