[0.1.1-4] server.port defined. also, heartbeat server starting to take shape, though you can't actually READ the list from another comp yet.
This commit is contained in:
parent
44766398ff
commit
a8119ad35a
@ -55,6 +55,11 @@ common.base_dir @
|
|||||||
writing to this string will *not* change the base dir,
|
writing to this string will *not* change the base dir,
|
||||||
so *don't write to it* or else you'll just screw up your own code!
|
so *don't write to it* or else you'll just screw up your own code!
|
||||||
|
|
||||||
|
server.port @
|
||||||
|
port we are bound to
|
||||||
|
|
||||||
|
there is NO alias to common here! if you want it, do it manually.
|
||||||
|
|
||||||
common.version = {cmp={w,x,y,a,z},num,str="w.x.ya-z"} @
|
common.version = {cmp={w,x,y,a,z},num,str="w.x.ya-z"} @
|
||||||
current engine version
|
current engine version
|
||||||
|
|
||||||
|
@ -4,74 +4,74 @@ Main heartbeat server:
|
|||||||
play.iceballga.me port 27790
|
play.iceballga.me port 27790
|
||||||
(port is for UDP *and* TCP)
|
(port is for UDP *and* TCP)
|
||||||
|
|
||||||
|
Iceball clients should connect to:
|
||||||
|
play.iceballga.me port 27795
|
||||||
|
[ NOT IMPLEMENTED ]
|
||||||
|
|
||||||
Iceball only supports IPv4 UDP ports at the time of writing, sorry.
|
Iceball only supports IPv4 UDP ports at the time of writing, sorry.
|
||||||
|
|
||||||
Feel free to host your own heartbeat server, though!
|
Feel free to host your own heartbeat server, though!
|
||||||
|
|
||||||
Note, S = heartbeat server, C = heartbeat client (AKA iceball server).
|
Note, S = heartbeat server, C = heartbeat client (AKA iceball server).
|
||||||
|
|
||||||
You should always confirm you have received a packet from the heartbeat server.
|
|
||||||
To do this correctly, send packets every 3 seconds until you get a response.
|
|
||||||
If you get no response after 60 seconds, give up.
|
|
||||||
You might want to retry after 180 seconds.
|
|
||||||
|
|
||||||
The heartbeat server will terminate your session after 120 seconds of inactivity.
|
The heartbeat server will terminate your session after 120 seconds of inactivity.
|
||||||
|
|
||||||
|
ALL PROTOCOLS ARE LITTLE ENDIAN WHERE APPROPRIATE, GUYS!
|
||||||
|
|
||||||
Iceball servers interface the heartbeat server using this UDP protocol...
|
Iceball servers interface the heartbeat server using this UDP protocol...
|
||||||
|
|
||||||
0x01 hbversion.u16 port.u16 addr.z:
|
"1CEB" hbversion.u16 ibversion.u32 port.u16 players_current.u16 players_max.u16 name.z[30] mode.z[10] map.z[30]
|
||||||
C->S Request ID (conf 0x02)
|
I->H
|
||||||
|
Announce a server.
|
||||||
|
|
||||||
This should be the first thing you send to the heartbeat server.
|
Note, remember to send a burst of 5 of these
|
||||||
|
with a second in between each packet,
|
||||||
|
and do this every 40 seconds
|
||||||
|
|
||||||
At the time of writing, hbversion should be 1.
|
(that is, a gap of 36 seconds between the last of a burst
|
||||||
|
and the first of the next).
|
||||||
|
|
||||||
0x02 id.u32 port.u16 addr.z:
|
Note, the strings are terminated with a NUL (char 0)
|
||||||
S->C Here Is Your ID
|
unless they hit their string limit,
|
||||||
|
in which case there is no NUL. BE WARY OF THIS.
|
||||||
|
|
||||||
Every time you receive this packet, CHANGE YOUR ID.
|
"MSOK"
|
||||||
|
H->I
|
||||||
|
|
||||||
0x03 id.u32 field.u8 name.z:
|
Your packet was accepted.
|
||||||
C->S Set String Field (conf 0x03)
|
|
||||||
S->C This Is Your String Field
|
|
||||||
|
|
||||||
Valid string fields are:
|
If your server gets this message, you can stop sending your burst.
|
||||||
0x01: server name
|
|
||||||
0x02: server mode
|
|
||||||
|
|
||||||
0x04 id.u32 field.u8 data.s32:
|
"BADV" hbversion.u16 ibversion.u32
|
||||||
C->S Set Integer Field (conf 0x04)
|
H->I
|
||||||
S->C This Is Your Integer Field
|
|
||||||
|
|
||||||
Valid integer fields are:
|
Your version of Iceball and/or the heartbeat protocol are too old and/or new.
|
||||||
0x01: player count
|
Here's the version we expect.
|
||||||
0x02: max players
|
|
||||||
|
|
||||||
0x05 id.u32:
|
If your server gets this message, give up.
|
||||||
C->S Hello Me Not Dead
|
|
||||||
|
|
||||||
Every 30 seconds, send 5 of these at 1 second intervals.
|
Alternatively, if you support the given heartbeat version, try falling back to it.
|
||||||
|
|
||||||
0x06:
|
"BADF"
|
||||||
S->C I'll Need To See Your ID
|
H->I
|
||||||
|
|
||||||
If you send any packets and you don't have an active session
|
The packet you sent was complete horseshit and you should feel bad for writing such bad code.
|
||||||
(either you didn't initiate one or the server terminated your session),
|
|
||||||
you'll get this. Apply for a new ID.
|
|
||||||
|
|
||||||
0x07 version.u16:
|
If your server gets this message, crash in shame, because your code is seriously broken.
|
||||||
S->C Version Mismatch
|
Or alternatively just don't send any more data to the server.
|
||||||
|
|
||||||
You are using the wrong version of the heartbeat protocol.
|
But crashing with an error is a much better idea.
|
||||||
This should give you the correct version.
|
|
||||||
|
|
||||||
OK, so that's the UDP side of the master server.
|
OK, so that's the UDP side of the master server.
|
||||||
The TCP side is a minimal HTTP server with two interfaces:
|
|
||||||
|
The TCP side is a minimal HTTP server at the same port with these interfaces:
|
||||||
|
[ INCOMPLETE IMPLEMENTATION THAT DOESN'T ACTUALLY WORK ]
|
||||||
|
|
||||||
/:
|
/:
|
||||||
|
/index.html:
|
||||||
An HTML page following a template.
|
An HTML page following a template.
|
||||||
/style.css:
|
/style.css:
|
||||||
|
A stylesheet you can provide.
|
||||||
/master.json:
|
/master.json:
|
||||||
The raw serverlist info in JSON format.
|
The raw serverlist info in JSON format.
|
||||||
Here's the expected format!
|
Here's the expected format!
|
||||||
@ -80,12 +80,14 @@ The TCP side is a minimal HTTP server with two interfaces:
|
|||||||
"version": int,
|
"version": int,
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
"name": str,
|
|
||||||
"address": str,
|
"address": str,
|
||||||
"port": int,
|
"port": int,
|
||||||
"basedir": str,
|
|
||||||
"players_current": int,
|
"players_current": int,
|
||||||
"players_max": int,
|
"players_max": int,
|
||||||
|
"name": str,
|
||||||
|
"mode": str,
|
||||||
|
"map": str,
|
||||||
|
"version": str
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,249 @@
|
|||||||
# along with Iceball. If not, see <http://www.gnu.org/licenses/>.
|
# along with Iceball. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
import sys
|
import heapq, socket, struct, sys, time
|
||||||
import socket
|
|
||||||
|
CONN_PORT = int(sys.argv[1])
|
||||||
|
|
||||||
|
def ib_version_str(n):
|
||||||
|
z = n & ((1<<10)-1); n >>= 10
|
||||||
|
a = n & ((1<<5)-1); n >>= 5
|
||||||
|
y = n & ((1<<7)-1); n >>= 7
|
||||||
|
x = n & ((1<<5)-1); n >>= 5
|
||||||
|
w = n
|
||||||
|
|
||||||
|
s = "%i.%i" % (w, x)
|
||||||
|
if y > 0:
|
||||||
|
s += ".%i" % y
|
||||||
|
if a > 0:
|
||||||
|
s += "." + chr(ord('a')+a-1)
|
||||||
|
if z > 0:
|
||||||
|
s += "-%i" % z
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
def calc_ib_version(w,x,y,a,z):
|
||||||
|
return (((((((w<<5) + x
|
||||||
|
)<<7) + y
|
||||||
|
)<<5) + a
|
||||||
|
)<<10) + z
|
||||||
|
|
||||||
|
HB_LIFETIME = 120
|
||||||
|
HB_VERSION = 1
|
||||||
|
IB_VERSION_CMP = (0,1,1,0,0)
|
||||||
|
IB_VERSION = calc_ib_version(*IB_VERSION_CMP)
|
||||||
|
|
||||||
|
# ignore "Z" version
|
||||||
|
IB_VERSION_MASK = ~((1<<10)-1)
|
||||||
|
# if you wish to ignore "A" version as well, use this instead:
|
||||||
|
# IB_VERSION_MASK = ~((1<<(10+5))-1)
|
||||||
|
|
||||||
|
def stripnul(s):
|
||||||
|
idx = s.find("\x00")
|
||||||
|
return (s if idx == -1 else s[:idx])
|
||||||
|
|
||||||
|
class HTTPClient:
|
||||||
|
def __init__(self, reactor, server, sockfd):
|
||||||
|
self.reactor = reactor
|
||||||
|
self.server = server
|
||||||
|
self.sockfd = sockfd
|
||||||
|
self.buf = ""
|
||||||
|
|
||||||
|
def is_dead(self, ct):
|
||||||
|
return self.sockfd == None
|
||||||
|
|
||||||
|
def update(self, ct):
|
||||||
|
self.get_msgs(ct)
|
||||||
|
|
||||||
|
def collect(self, ct):
|
||||||
|
# TODO!
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_msgs(self, ct):
|
||||||
|
if not self.sockfd:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
msg = self.sockfd.recv(2048)
|
||||||
|
if msg == None:
|
||||||
|
self.sockfd.close()
|
||||||
|
self.sockfd = None
|
||||||
|
|
||||||
|
self.buf += msg
|
||||||
|
self.collect(ct)
|
||||||
|
except socket.timeout:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class HReactor:
|
||||||
|
def __init__(self):
|
||||||
|
self.evq = []
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while len(self.evq) > 0:
|
||||||
|
ct = self.get_time()
|
||||||
|
|
||||||
|
dt = self.evq[0][0] - ct
|
||||||
|
if dt > 0:
|
||||||
|
time.sleep(dt)
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
# we've run out of tasks, so exit.
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
ct = self.get_time()
|
||||||
|
while self.evq and ct >= self.evq[0][0]:
|
||||||
|
et, fn = heapq.heappop(self.evq)
|
||||||
|
fn(et, ct)
|
||||||
|
|
||||||
|
def get_time(self):
|
||||||
|
return time.time()
|
||||||
|
|
||||||
|
def push(self, et, fn):
|
||||||
|
heapq.heappush(self.evq, (et, fn))
|
||||||
|
|
||||||
|
class HServer:
|
||||||
|
def __init__(self, ct, reactor, af, port):
|
||||||
|
self.af = af
|
||||||
|
self.port = port
|
||||||
|
self.reactor = reactor
|
||||||
|
self.clients = {}
|
||||||
|
self.http_clients = {}
|
||||||
|
self.bans = set([]) # TODO: use a proper banlist
|
||||||
|
|
||||||
|
self.http_sockfd = socket.socket(af, socket.SOCK_STREAM)
|
||||||
|
self.http_sockfd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
self.http_sockfd.bind(("", self.port))
|
||||||
|
self.http_sockfd.settimeout(0)
|
||||||
|
|
||||||
|
self.sockfd = socket.socket(af, socket.SOCK_DGRAM)
|
||||||
|
self.sockfd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
self.sockfd.bind(("", self.port))
|
||||||
|
self.sockfd.settimeout(0.05)
|
||||||
|
self.reactor.push(ct, self.update)
|
||||||
|
|
||||||
|
def update(self, et, ct):
|
||||||
|
self.get_hb_packets(ct)
|
||||||
|
self.update_http_clients(ct)
|
||||||
|
self.kill_old_clients(ct)
|
||||||
|
self.reactor.push(ct+0.05, self.update)
|
||||||
|
|
||||||
|
def get_hb_packets(self, ct):
|
||||||
|
try:
|
||||||
|
(msg, adtup) = self.sockfd.recvfrom(2048)
|
||||||
|
|
||||||
|
if msg and adtup:
|
||||||
|
(addr, port) = adtup
|
||||||
|
self.on_msg(ct, self.af, addr, port, msg)
|
||||||
|
except socket.timeout:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update_http_clients(self, ct):
|
||||||
|
for v in self.http_clients:
|
||||||
|
v.update(ct)
|
||||||
|
|
||||||
|
def kill_old_clients(self, ct):
|
||||||
|
# TODO: use a priority queue for the clients
|
||||||
|
# that'd mean we'd get this running in O(1) time instead of O(n)
|
||||||
|
|
||||||
|
kill = []
|
||||||
|
|
||||||
|
for k,v in self.clients.iteritems():
|
||||||
|
if v.is_dead(ct):
|
||||||
|
kill.append(k)
|
||||||
|
|
||||||
|
for k in kill:
|
||||||
|
self.clients.pop(k)
|
||||||
|
|
||||||
|
def on_msg(self, ct, af, addr, port, msg):
|
||||||
|
client = self.get_client(ct, af, addr, port)
|
||||||
|
if client:
|
||||||
|
client.on_msg(ct, msg)
|
||||||
|
|
||||||
|
def get_client(self, ct, af, addr, port):
|
||||||
|
tup = (af, addr, port)
|
||||||
|
|
||||||
|
if tup in self.bans:
|
||||||
|
return None
|
||||||
|
if tup not in self.clients:
|
||||||
|
self.clients[tup] = HClient(ct, self.reactor, self, af, addr, port)
|
||||||
|
|
||||||
|
return self.clients[tup]
|
||||||
|
|
||||||
|
def get_ib_fields(self):
|
||||||
|
l = []
|
||||||
|
|
||||||
|
for k, v in self.clients.iteritems():
|
||||||
|
d = v.get_fields()
|
||||||
|
if d:
|
||||||
|
l.append(d)
|
||||||
|
|
||||||
|
return l
|
||||||
|
|
||||||
|
class HClient:
|
||||||
|
def __init__(self, ct, reactor, server, af, addr, port):
|
||||||
|
self.reactor = reactor
|
||||||
|
self.server = server
|
||||||
|
self.af = af
|
||||||
|
self.addr = addr
|
||||||
|
self.port = port
|
||||||
|
self.ibdata = None
|
||||||
|
self.not_dead(ct)
|
||||||
|
|
||||||
|
def get_fields(self):
|
||||||
|
return self.ibdata
|
||||||
|
|
||||||
|
def is_dead(self, ct):
|
||||||
|
return ct > self.last_msg + HB_LIFETIME
|
||||||
|
|
||||||
|
def not_dead(self, ct):
|
||||||
|
self.last_msg = ct
|
||||||
|
|
||||||
|
def send_delayed(self, t, msg):
|
||||||
|
self.reactor.push(lambda et, ct : self.send(msg))
|
||||||
|
|
||||||
|
def send(self, msg):
|
||||||
|
self.server.sockfd.sendto(msg, (self.addr, self.port))
|
||||||
|
|
||||||
|
def on_msg(self, ct, msg):
|
||||||
|
if len(msg) < 4:
|
||||||
|
# empty message - do nothing
|
||||||
|
return
|
||||||
|
|
||||||
|
typ = msg[:4]
|
||||||
|
msg = msg[4:]
|
||||||
|
|
||||||
|
if typ == "1CEB" and len(msg) >= 6:
|
||||||
|
hbver, ibver = struct.unpack("<HI", msg[:6])
|
||||||
|
msg = msg[6:]
|
||||||
|
|
||||||
|
if hbver != HB_VERSION or ((ibver^IB_VERSION)&IB_VERSION_MASK) != 0:
|
||||||
|
# version is incorrect
|
||||||
|
self.send("BADV" + struct.pack("<HI", HB_VERSION, IB_VERSION))
|
||||||
|
elif len(msg) != (2+2+2+30+10+30):
|
||||||
|
# bad format
|
||||||
|
print "BADF", len(msg)
|
||||||
|
self.send("BADF")
|
||||||
|
else:
|
||||||
|
# parse this!
|
||||||
|
d = {}
|
||||||
|
d["address"] = str(self.addr)
|
||||||
|
d["port"], d["players_current"], d["players_max"] = struct.unpack("<HHH", msg[:6])
|
||||||
|
msg = msg[6:]
|
||||||
|
d["name"] = stripnul(msg[:30])
|
||||||
|
msg = msg[30:]
|
||||||
|
d["mode"] = stripnul(msg[:10])
|
||||||
|
msg = msg[10:]
|
||||||
|
d["map"] = stripnul(msg[:30])
|
||||||
|
|
||||||
|
d["version"] = ib_version_str(ibver)
|
||||||
|
|
||||||
|
self.ibdata = d
|
||||||
|
self.not_dead(ct)
|
||||||
|
self.send("MSOK")
|
||||||
|
print "MSOK", d
|
||||||
|
|
||||||
|
hb_reactor = HReactor()
|
||||||
|
hb_server = HServer(hb_reactor.get_time(), hb_reactor, socket.AF_INET, CONN_PORT)
|
||||||
|
hb_reactor.run()
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#define VERSION_X 1
|
#define VERSION_X 1
|
||||||
#define VERSION_Y 1
|
#define VERSION_Y 1
|
||||||
#define VERSION_A 0
|
#define VERSION_A 0
|
||||||
#define VERSION_Z 3
|
#define VERSION_Z 4
|
||||||
// Remember to bump "Z" basically every time you change the engine!
|
// Remember to bump "Z" basically every time you change the engine!
|
||||||
// Remember to bump the version in Lua too!
|
// Remember to bump the version in Lua too!
|
||||||
// Remember to document API changes in a new version!
|
// Remember to document API changes in a new version!
|
||||||
|
@ -137,6 +137,6 @@ do
|
|||||||
|
|
||||||
common.map_set(lmap)
|
common.map_set(lmap)
|
||||||
print("gen finished")
|
print("gen finished")
|
||||||
return ret
|
return ret, "classic("..mw..","..mh..")"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -36,6 +36,6 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
print("gen finished")
|
print("gen finished")
|
||||||
return ret
|
return ret, "flat("..mx..","..mz..","..my..")"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
131
pkg/base/lib_heartbeat.lua
Normal file
131
pkg/base/lib_heartbeat.lua
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
--[[
|
||||||
|
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/>.
|
||||||
|
]]
|
||||||
|
|
||||||
|
HB_VERSION = 1
|
||||||
|
|
||||||
|
heartbeat_sockfd = nil
|
||||||
|
heartbeat_t_nextmsg = nil
|
||||||
|
heartbeat_t_nextburst = nil
|
||||||
|
heartbeat_burstsleft = nil
|
||||||
|
heartbeat_cooloff = true
|
||||||
|
|
||||||
|
local function pad_nul(n, s)
|
||||||
|
while s:len() < n do
|
||||||
|
s = s .. "\0"
|
||||||
|
end
|
||||||
|
|
||||||
|
if s:len() > n then
|
||||||
|
s = s:sub(1, n)
|
||||||
|
end
|
||||||
|
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
function heartbeat_init()
|
||||||
|
-- check if we have this actually enabled
|
||||||
|
if not server_config.heartbeat_send then return end
|
||||||
|
|
||||||
|
-- open the socket
|
||||||
|
heartbeat_sockfd = common.udp_open()
|
||||||
|
end
|
||||||
|
|
||||||
|
function heartbeat_update(sec_current, sec_delta)
|
||||||
|
-- we need to let the timer "cool off" as the first few values are just plain wrong.
|
||||||
|
if heartbeat_cooloff then
|
||||||
|
if sec_current < 60 and sec_current >= 2 then
|
||||||
|
heartbeat_cooloff = nil
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we're using the wrong heartbeat and/or iceball version,
|
||||||
|
-- heartbeat_sockfd will be nil,
|
||||||
|
-- because we have given up.
|
||||||
|
-- (it'll also be nil if we haven't enabled the heartbeat client.)
|
||||||
|
if not heartbeat_sockfd then return end
|
||||||
|
|
||||||
|
-- versions before 0.1.1-4 don't have server.port,
|
||||||
|
-- so we need to rip the port from server.hook_connect.
|
||||||
|
if not server.port then return end
|
||||||
|
|
||||||
|
-- check if we received any messages
|
||||||
|
while true do
|
||||||
|
local msg, host, port = common.udp_recvfrom(heartbeat_sockfd)
|
||||||
|
if msg == "" then
|
||||||
|
break
|
||||||
|
elseif msg == false then
|
||||||
|
error("UDP socket used to connect to master servers broke horribly. What the hell?!")
|
||||||
|
elseif msg == "MSOK" then
|
||||||
|
-- we're ignoring MSOK messages for now,
|
||||||
|
-- until we can track ALL master/heartbeat servers properly
|
||||||
|
elseif msg == "BADF" then
|
||||||
|
error("heartbeat server \""..host.."\" port "..port.." reports bad packet format - FIX ME OR REMOVE THIS SERVER")
|
||||||
|
elseif msg:len() >= 4 and msg:sub(1,4) == "BADV" then
|
||||||
|
error("heartbeat server \""..host.."\" port "..port.." reports bad version - UPGRADE OR REMOVE THIS SERVER")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if we need to send a new burst
|
||||||
|
heartbeat_t_nextburst = heartbeat_t_nextburst or sec_current
|
||||||
|
|
||||||
|
if sec_current >= heartbeat_t_nextburst then
|
||||||
|
heartbeat_t_burstsleft = 5
|
||||||
|
heartbeat_t_nextmsg = heartbeat_t_nextburst
|
||||||
|
heartbeat_t_nextburst = heartbeat_t_nextburst + 40
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if we need to send a new message
|
||||||
|
if heartbeat_t_burstsleft and heartbeat_t_nextmsg and sec_current >= heartbeat_t_nextmsg then
|
||||||
|
-- get player count
|
||||||
|
local players_max = players.max
|
||||||
|
local players_current = 0
|
||||||
|
local i
|
||||||
|
|
||||||
|
for i=1,players_max do
|
||||||
|
if players[i] then
|
||||||
|
players_current = players_current + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- assemble message
|
||||||
|
local msg = "1CEB" .. common.net_pack("HI", HB_VERSION, common.version.num)
|
||||||
|
msg = msg .. common.net_pack("HHH", server.port, players_current, players_max)
|
||||||
|
msg = msg .. pad_nul(30, server_config.name)
|
||||||
|
msg = msg .. pad_nul(10, game_hb_mode)
|
||||||
|
msg = msg .. pad_nul(30, map_name)
|
||||||
|
|
||||||
|
--print("HEARTBEAT MESSAGE")
|
||||||
|
|
||||||
|
-- send message
|
||||||
|
local i
|
||||||
|
local hbl = server_config.heartbeat
|
||||||
|
for i=1,#hbl do
|
||||||
|
local host, port = hbl[i][1], hbl[i][2]
|
||||||
|
common.udp_sendto(heartbeat_sockfd, msg, host, port)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- give time for next message if necessary
|
||||||
|
heartbeat_t_burstsleft = heartbeat_t_burstsleft - 1
|
||||||
|
if heartbeat_t_burstsleft <= 0 then
|
||||||
|
heartbeat_t_burstsleft = nil
|
||||||
|
heartbeat_t_nextmsg = nil
|
||||||
|
else
|
||||||
|
heartbeat_t_nextmsg = heartbeat_t_nextmsg + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -66,6 +66,7 @@ load_mod_list(getfenv(), mod_data.mods, {"preload", "preload_server"}, server_co
|
|||||||
|
|
||||||
dofile("pkg/base/common.lua")
|
dofile("pkg/base/common.lua")
|
||||||
dofile("pkg/base/commands.lua")
|
dofile("pkg/base/commands.lua")
|
||||||
|
dofile("pkg/base/lib_heartbeat.lua")
|
||||||
|
|
||||||
client_list = {fdlist={}, banned={}}
|
client_list = {fdlist={}, banned={}}
|
||||||
server_tick_accum = 0
|
server_tick_accum = 0
|
||||||
@ -161,6 +162,9 @@ function server.hook_connect(neth, addrinfo)
|
|||||||
addrinfo.addr and addrinfo.addr.ip,
|
addrinfo.addr and addrinfo.addr.ip,
|
||||||
addrinfo.addr and addrinfo.addr.cport)
|
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 source = false
|
||||||
if addrinfo.proto == "enet/ip6" or addrinfo.proto == "tcp/ip6" then
|
if addrinfo.proto == "enet/ip6" or addrinfo.proto == "tcp/ip6" then
|
||||||
-- There are two variants:
|
-- There are two variants:
|
||||||
@ -228,6 +232,8 @@ end
|
|||||||
|
|
||||||
lflush = nil
|
lflush = nil
|
||||||
function server.hook_tick(sec_current, sec_delta)
|
function server.hook_tick(sec_current, sec_delta)
|
||||||
|
heartbeat_update(sec_current, sec_delta)
|
||||||
|
|
||||||
--print("tick",sec_current,sec_delta)
|
--print("tick",sec_current,sec_delta)
|
||||||
--[[
|
--[[
|
||||||
local xlen,ylen,zlen
|
local xlen,ylen,zlen
|
||||||
@ -416,17 +422,38 @@ end
|
|||||||
|
|
||||||
-- load map
|
-- load map
|
||||||
if server_settings.gen then
|
if server_settings.gen then
|
||||||
map_loaded = loadfile(server_settings.gen)(loose, server_toggles, server_settings)
|
map_loaded, map_name = loadfile(server_settings.gen)(loose, server_toggles, server_settings)
|
||||||
elseif map_fname then
|
elseif map_fname then
|
||||||
map_loaded = common.map_load(map_fname, "auto")
|
map_loaded = common.map_load(map_fname, "auto")
|
||||||
else
|
map_name = map_fname
|
||||||
map_loaded = loadfile("pkg/base/gen_classic.lua")(loose, server_toggles, server_settings)
|
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
|
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
|
||||||
|
|
||||||
common.map_set(map_loaded)
|
common.map_set(map_loaded)
|
||||||
|
|
||||||
mode_create_server()
|
mode_create_server()
|
||||||
|
|
||||||
print("pkg/base/main_server.lua: Loading mods...")
|
print("pkg/base/main_server.lua: Loading mods...")
|
||||||
load_mod_list(getfenv(), mod_data.mods, {"load", "load_server"}, server_config, mod_data)
|
load_mod_list(getfenv(), mod_data.mods, {"load", "load_server"}, server_config, mod_data)
|
||||||
|
|
||||||
|
print("Starting heartbeat server...")
|
||||||
|
heartbeat_init()
|
||||||
print("pkg/base/main_server.lua loaded.")
|
print("pkg/base/main_server.lua loaded.")
|
||||||
|
|
||||||
|
@ -583,6 +583,11 @@ int icelua_init(void)
|
|||||||
|
|
||||||
icelua_pushversion(lstate_server, "common");
|
icelua_pushversion(lstate_server, "common");
|
||||||
icelua_pushversion(lstate_server, "server");
|
icelua_pushversion(lstate_server, "server");
|
||||||
|
|
||||||
|
lua_getglobal(lstate_server, "server");
|
||||||
|
lua_pushinteger(lstate_server, net_port);
|
||||||
|
lua_setfield(lstate_server, -2, "port");
|
||||||
|
lua_pop(lstate_server, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(lstate_client != NULL)
|
if(lstate_client != NULL)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user