large rewrite and code cleanup

This commit is contained in:
Magnus Auvinen 2007-07-13 13:40:04 +00:00
parent 7be0ae1b29
commit 125d04e51f
31 changed files with 4218 additions and 3817 deletions

80
datasrc/client.dts Normal file
View file

@ -0,0 +1,80 @@
struct image {
int id = 0
string filename = filename@1
}
struct spriteset {
ptr:image img = @1
int gridx = @2
int gridy = @3
}
struct sprite {
ptr:spriteset set = parent
int x = @1
int y = @2
int w = @3
int h = @4
}
struct sound {
int id = 0
string filename = @0
}
struct soundset {
int last = 0
array:sound sounds = *
}
struct particleinfo {
ptr:sprite spr = sprite@1
float color_r = color@1
float color_g = color@2
float color_b = color@3
float color_a = color@4
int lifemod = life@1
}
struct weapon {
ptr:sprite sprite_body = sprite_body@1
ptr:sprite sprite_cursor = sprite_cursor@1
ptr:sprite sprite_proj = sprite_proj@1
int recoil = recoil@1
int visual_size = visual_size@1
}
struct keyframe {
float time = @0
float x = @1
float y = @2
float angle = @3
}
struct sequence {
array:keyframe frames = *
}
struct animation {
instance:sequence body = body
instance:sequence back_foot = back_foot
instance:sequence front_foot = front_foot
instance:sequence attach = attach
}
struct data_container {
array:image images = images.*
array:spriteset spritesets = sprites.*
array:sprite sprites = sprites.*.*
array:weapon weapons = weapons.*
array:particleinfo particles = particles.*
array:soundset sounds = sounds.*
array:animation animations = animations.*
}
const array:int weapon = weapons.*
const array:int sound = sounds.*
const array:int image = images.*
const array:int sprite = sprites.*.*
const array:int anim = animations.*

5
datasrc/server.dts Normal file
View file

@ -0,0 +1,5 @@
struct data_container {
}
const array:int sound = sounds.*
const array:int weapon = weapons.*

415
datasrc/teewars.ds Normal file
View file

@ -0,0 +1,415 @@
sounds {
gun_fire {
"data/audio/wp_gun_fire-01.wav"
"data/audio/wp_gun_fire-02.wav"
"data/audio/wp_gun_fire-03.wav"
}
shotgun_fire {
"data/audio/wp_shotty_fire-01.wav"
"data/audio/wp_shotty_fire-02.wav"
"data/audio/wp_shotty_fire-03.wav"
}
rocket_fire {
"data/audio/wp_flump_launch-01.wav"
"data/audio/wp_flump_launch-02.wav"
"data/audio/wp_flump_launch-03.wav"
}
hammer_fire {
"data/audio/wp_hammer_swing-01.wav"
"data/audio/wp_hammer_swing-02.wav"
"data/audio/wp_hammer_swing-03.wav"
}
ninja_fire {
"data/audio/wp_ninja_attack-01.wav"
"data/audio/wp_ninja_attack-02.wav"
"data/audio/wp_ninja_attack-03.wav"
}
rocket_explode {
"data/audio/wp_flump_explo-01.wav"
"data/audio/wp_flump_explo-02.wav"
"data/audio/wp_flump_explo-03.wav"
}
ninja_hit {
"data/audio/wp_ninja_hit-01.wav"
"data/audio/wp_ninja_hit-02.wav"
"data/audio/wp_ninja_hit-03.wav"
}
weapon_switch {
"data/audio/wp_switch-01.wav"
"data/audio/wp_switch-02.wav"
"data/audio/wp_switch-03.wav"
}
player_pain_short {
"data/audio/vo_teefault_pain_short-01.wav"
"data/audio/vo_teefault_pain_short-02.wav"
"data/audio/vo_teefault_pain_short-03.wav"
"data/audio/vo_teefault_pain_short-04.wav"
"data/audio/vo_teefault_pain_short-05.wav"
"data/audio/vo_teefault_pain_short-06.wav"
"data/audio/vo_teefault_pain_short-07.wav"
"data/audio/vo_teefault_pain_short-08.wav"
"data/audio/vo_teefault_pain_short-09.wav"
"data/audio/vo_teefault_pain_short-10.wav"
"data/audio/vo_teefault_pain_short-11.wav"
"data/audio/vo_teefault_pain_short-12.wav"
}
player_pain_long {
"data/audio/vo_teefault_pain_long-01.wav"
"data/audio/vo_teefault_pain_long-02.wav"
}
body_land {
"data/audio/foley_land-01.wav"
"data/audio/foley_land-02.wav"
"data/audio/foley_land-03.wav"
"data/audio/foley_land-04.wav"
}
player_jump {
"data/audio/foley_foot_left-01.wav"
"data/audio/foley_foot_left-02.wav"
"data/audio/foley_foot_left-03.wav"
"data/audio/foley_foot_left-04.wav"
"data/audio/foley_foot_right-01.wav"
"data/audio/foley_foot_right-02.wav"
"data/audio/foley_foot_right-03.wav"
"data/audio/foley_foot_right-04.wav"
}
player_die {
"data/audio/foley_body_splat-02.wav"
"data/audio/foley_body_splat-03.wav"
"data/audio/foley_body_splat-04.wav"
}
player_spawn {
"data/audio/vo_teefault_spawn-01.wav"
"data/audio/vo_teefault_spawn-02.wav"
"data/audio/vo_teefault_spawn-03.wav"
"data/audio/vo_teefault_spawn-04.wav"
"data/audio/vo_teefault_spawn-05.wav"
"data/audio/vo_teefault_spawn-06.wav"
"data/audio/vo_teefault_spawn-07.wav"
}
tee_cry {
"data/audio/vo_teefault_cry-01.wav"
"data/audio/vo_teefault_cry-02.wav"
}
hook_loop {
"data/audio/hook_loop-01.wav"
"data/audio/hook_loop-02.wav"
}
hook_attach {
"data/audio/hook_attach-01.wav"
"data/audio/hook_attach-02.wav"
"data/audio/hook_attach-03.wav"
}
}
images {
weapons {
filename "data/tileset_weapons.png"
}
game {
filename "data/game_main.png"
}
particles {
filename "data/tileset_particles.png"
}
sun {
filename "data/sun.png"
}
char_default {
filename "data/char_teefault.png"
}
}
particles {
part1 {
sprite sprites.particles.part1
color 0.7 0.7 0.7 1.0
life 50
}
part2 {
sprite sprites.particles.part2
color 1.0 1.0 1.0 1.0
life 50
}
part3 {
sprite sprites.particles.part3
color 0.8 0.8 0.8 1.0
life 50
}
part4 {
sprite sprites.particles.part4
color 0.98 0.1 0.16 1.0
life 70
}
part5 {
sprite sprites.particles.part5
color 1.0 1.0 1.0 1.0
life 70
}
part6 {
sprite sprites.particles.part6
color 0.6 0.6 0.6 1.0
life 100
}
part7 {
sprite sprites.particles.part7
color 1.0 1.0 1.0 1.0
life 100
}
part8 {
sprite sprites.particles.part8
color 0.7 0.7 0.7 1.0
life 150
}
part9 {
sprite sprites.particles.part9
color 1.0 1.0 1.0 1.0
life 40
}
}
weapons {
gun {
sprite_body sprites.weapons.weapon_gun_body
sprite_cursor sprites.weapons.weapon_gun_cursor
sprite_proj sprites.weapons.weapon_gun_proj
recoil 10
reloadtime 100
visual_size 64
}
rocket {
sprite_body sprites.weapons.weapon_rocket_body
sprite_cursor sprites.weapons.weapon_rocket_cursor
sprite_proj sprites.weapons.weapon_rocket_proj
recoil 10
reloadtime 600
visual_size 96
}
shotgun {
sprite_body sprites.weapons.weapon_shotgun_body
sprite_cursor sprites.weapons.weapon_shotgun_cursor
sprite_proj sprites.weapons.weapon_shotgun_proj
recoil 10
reloadtime 800
visual_size 96
}
hammer {
sprite_body sprites.weapons.weapon_hammer_body
sprite_cursor sprites.weapons.weapon_hammer_cursor
sprite_proj sprites.weapons.weapon_hammer_proj
recoil 10
reloadtime 100
visual_size 96
}
}
sprites {
particles images.particles 16 16 {
part1 2 0 2 2
part2 4 0 2 2
part3 6 0 2 2
part4 8 0 2 2
part5 10 0 2 2
part6 2 2 2 2
part7 4 2 2 2
part8 6 2 2 2
part9 8 2 2 2
star1 0 0 2 2
star2 0 2 2 2
}
hud images.game 32 16 {
health_full 0 0 4 4
health_empty 5 0 4 4
armor_full 0 5 4 4
armor_empty 5 5 4 4
}
weapons images.weapons 32 32 {
weapon_gun_body 2 4 4 2
weapon_gun_cursor 0 4 2 2
weapon_gun_proj 6 4 2 2
weapon_gun_muzzle1 8 4 3 2
weapon_gun_muzzle2 12 4 3 2
weapon_gun_muzzle3 16 4 3 2
weapon_shotgun_body 2 6 8 2
weapon_shotgun_cursor 0 6 2 2
weapon_shotgun_proj 10 6 2 2
weapon_shotgun_muzzle1 12 6 3 2
weapon_shotgun_muzzle2 16 6 3 2
weapon_shotgun_muzzle3 20 6 3 2
weapon_rocket_body 2 8 7 2
weapon_rocket_cursor 0 8 2 2
weapon_rocket_proj 10 8 2 2
weapon_hammer_body 2 1 4 3
weapon_hammer_cursor 0 0 2 2
weapon_hammer_proj 0 0 0 0
weapon_ninja_body 2 10 7 2
weapon_ninja_cursor 0 10 2 2
weapon_ninja_proj 0 0 0 0
hook_chain 2 0 1 1
hook_head 3 0 2 1
hadoken1 1 12 7 4
hadoken2 8 12 8 4
hadoken3 17 12 7 4
}
powerups images.weapons 32 32 {
powerup_health 10 2 2 2
powerup_armor 12 2 2 2
powerup_weapon 3 0 6 2
powerup_ninja 3 10 7 2
powerup_timefield 3 0 6 2
}
tees images.char_default 16 64 {
tee_body 0 0 4 4
tee_body_outline 4 0 4 4
tee_foot 8 3 2 1
tee_foot_outline 8 2 2 1
tee_eye_normal 10 2 1 1
}
}
animations {
base {
body {
0.0 0 -4 0
}
back_foot {
0.0 0 10 0
}
front_foot {
0.0 0 10 0
}
attach {
}
}
idle {
body {
}
back_foot {
0.0 -7 0 0
}
front_foot {
0.0 7 0 0
}
attach {
0.0 0 0 0
}
}
inair {
body {
}
back_foot {
0.0 -3 0 -0.1
}
front_foot {
0.0 3 0 -0.1
}
attach {
}
}
walk {
body {
}
front_foot {
0.0 8 0 0
0.2 -8 0 0
0.4 -10 -4 0.2
0.6 -8 -8 0.3
0.8 4 -4 -0.2
1.0 8 0 0
}
back_foot {
0.0 -10 -4 0.2
0.2 -8 -8 0.3
0.4 -4 -4 -0.2
0.6 0 0 0
0.8 -8 0 0
1.0 -10 -4 0.2
}
attach {
}
}
hammer_swing {
body {
}
front_foot {
}
back_foot {
}
attach {
0.0 0 0 -0.10
0.3 0 0 0.25
0.4 0 0 0.30
0.5 0 0 0.25
1.0 0 0 -0.10
}
}
}

24
datasrc/teewars.dsd Normal file
View file

@ -0,0 +1,24 @@
tag:images {
ident:name * {
tag:filename string:filename
}
}
tag:sounds {
ident:name * {
tag:filename string:path
}
}
tag:weapons {
ident:name * {
tag:sprite_gun ptr:sprite
tag:sprite_cursor ptr:sprite
}
}
tag:sprites {
ident:name ptr:image int:gridx int:gridy * {
ident:name int:x int:y int:w int:h *
}
}

View file

@ -31,7 +31,60 @@ function Copy(outputdir, ...)
return outputs
end
--
function dc_header(output, data, script)
print("dc_header " .. PathFilename(output) .. " = " .. PathFilename(data) .. " ~ " .. PathFilename(script))
return os.execute("scripts/compiler.py " .. data .. " " .. script .. " -h " .. output)
end
function dc_source(output, data, script)
print("dc_source " .. PathFilename(output) .. " = " .. PathFilename(data) .. " ~ " .. PathFilename(script))
return os.execute("scripts/compiler.py " .. data .. " " .. script .. " -s " .. output)
end
function dc_data(output, data, script)
print("dc_data " .. PathFilename(output) .. " = " .. PathFilename(data) .. " ~ " .. PathFilename(script))
return os.execute("scripts/compiler.py " .. data .. " " .. script .. " -d " .. output)
end
function DataCompile_Header(datafile, scriptfile, outputfile)
datafile = Path(datafile)
scriptfile = Path(scriptfile)
outputfile = Path(outputfile)
bam_add_job("dc_header", outputfile, datafile, scriptfile)
bam_add_dependency(outputfile, datafile)
bam_add_dependency(outputfile, scriptfile)
return outputfile
end
function DataCompile_Source(datafile, scriptfile, outputfile)
datafile = Path(datafile)
scriptfile = Path(scriptfile)
outputfile = Path(outputfile)
bam_add_job("dc_source", outputfile, datafile, scriptfile)
bam_add_dependency(outputfile, datafile)
bam_add_dependency(outputfile, scriptfile)
return outputfile
end
function DataCompile(datafile, scriptfile, headerfile, sourcefile, outputdatafile)
datafile = Path(datafile)
scriptfile = Path(scriptfile)
headerfile = Path(headerfile)
sourcefile = Path(sourcefile)
outputdatafile = Path(outputdatafile)
bam_add_job("dc_source", sourcefile, datafile, scriptfile)
bam_add_job("dc_header", headerfile, datafile, scriptfile)
bam_add_job("dc_data", outputdatafile, datafile, scriptfile)
bam_add_dependency(sourcefile, datafile)
bam_add_dependency(sourcefile, scriptfile)
bam_add_dependency(sourcefile, headerfile)
bam_add_dependency(headerfile, datafile)
bam_add_dependency(headerfile, scriptfile)
bam_add_dependency(outputdatafile, datafile)
bam_add_dependency(outputdatafile, scriptfile)
return {data = outputdatafile, header=headerfile, source=sourcefile}
end
--baselib = Import("src/baselib/baselib.bam")
baselib = Import("../baselib/baselib.bam")
@ -47,18 +100,25 @@ settings.cc.flags = "-Wall"
settings.cc.includes:add("src")
settings.cc.includes:add("../baselib/src/external/zlib")
serverdata = DataCompile("datasrc/teewars.ds", "datasrc/server.dts", "src/game/server/data.h", "src/game/server/data/data.cpp", "data/server.dat")
clientdata = DataCompile("datasrc/teewars.ds", "datasrc/client.dts", "src/game/client/data.h", "src/game/client/data/data.cpp", "data/client.dat")
engine = Compile(settings, Collect("src/engine/*.cpp"))
client = Compile(settings, Collect("src/engine/client/*.cpp", "src/engine/client/pnglite/*.c"))
server = Compile(settings, Collect("src/engine/server/*.cpp"))
game_shared = Compile(settings, Collect("src/game/*.cpp"))
game_client = Compile(settings, Collect("src/game/client/*.cpp"))
game_server = Compile(settings, Collect("src/game/server/*.cpp"))
game_client = Compile(settings, Collect("src/game/client/*.cpp"), clientdata.source)
game_server = Compile(settings, Collect("src/game/server/*.cpp"), serverdata.source)
editor = Compile(settings, Collect("src/editor/*.cpp"))
crapnet = Compile(settings, Collect("src/crapnet/*.cpp"))
client_exe = Link(settings, "teewars", engine, client, game_shared, game_client)
server_exe = Link(server_settings, "teewars_srv", engine, server, game_shared, game_server)
-- editor_exe = Link(settings, "editor", engine, game_shared, editor)
crapnet_exe = Link(server_settings, "crapnet", crapnet)
Target(PseudoTarget("client", client_exe))
Target(PseudoTarget("server", server_exe))
Target(PseudoTarget("client", client_exe, clientdata.data))
Target(PseudoTarget("server", server_exe, serverdata.data))
Target(PseudoTarget("tools", crapnet_exe))
-- Target(PseudoTarget("editor", editor_exe))

598
scripts/compiler.py Executable file
View file

@ -0,0 +1,598 @@
#!/usr/bin/python
import sys
import struct
option_ptrsize = struct.calcsize("P")
option_intsize = struct.calcsize("l")
option_floatsize = struct.calcsize("f")
option_inttype = "long"
option_floattype = "float"
class node:
def __init__(self):
self.values = []
self.children = []
self.parent = 0
def name(self):
if len(self.values):
return self.values[0]
return ""
def debug_print(self, level):
print (" "*level) + " ".join(self.values),
if len(self.children):
print "{"
for c in self.children:
c.debug_print(level+1)
print (" "*level)+"}"
else:
print ""
def debug_root(self):
for c in self.children:
c.debug_print(0)
# TODO: should return list of items in the tree,
def gather(self, str):
def recurse(parts, path, node):
if not len(parts):
r = {}
path = path + "." + node.values[0]
r = [node]
#print "found", path
return r
l = []
for c in node.children:
if parts[0] == "*" or c.values[0] == parts[0]:
if len(node.values):
if len(path):
l += recurse(parts[1:], path+"."+node.values[0], c)
else:
l += recurse(parts[1:], node.values[0], c)
else:
l += recurse(parts[1:], path, c)
return l
parts = str.split(".")
return recurse(parts, "", self)
def find_node(self, str):
parts = str.split(".")
node = self
for part in parts:
if len(part) == 0:
continue
if part == "parent":
node = node.parent
else:
found = 0
for c in node.children:
if part == c.values[0]:
node = c
found = 1
break
if node == self:
return
return node
def get_single(self, str):
parts = str.split("@")
index = -1
if len(parts) == 2:
index = int(parts[1])
node = self
if len(parts[0]):
node = self.find_node(parts[0])
if not node:
print "failed to get", str
return Null
if index == -1:
return node.get_path()[1:]
return node.values[index]
def get_path(self):
if self.parent == 0:
return ""
return self.parent.get_path() + "." + self.values[0]
def get_single_name(self, str):
return self.get_path()[1:] + "." + str
class parser:
lines = []
def parse_node(self, this_node):
while len(self.lines):
line = self.lines.pop(0) # grab line
fields = line.strip().split() # TODO: improve this to handle strings with spaces
if not len(fields):
continue
if fields[-1] == '{':
new_node = node()
new_node.parent = this_node
new_node.values = fields[:-1]
this_node.children += [new_node]
self.parse_node(new_node)
elif fields[-1] == '}':
break
else:
new_node = node()
new_node.parent = this_node
new_node.values = fields
this_node.children += [new_node]
def parse_file(self, filename):
self.lines = file(filename).readlines()
n = node()
self.parse_node(n)
return n
def parse_file(filename):
return parser().parse_file(filename)
class pointer:
def __init__(self, index, target):
self.index = index
self.target = target
class data_constructor:
def __init__(self):
self.data = ""
self.trans = 0
self.pointers = []
self.targets = {}
def get_type(self, s):
return self.trans.types[s]
def allocate(self, size):
index = len(self.data)
self.data += "\0"*size
return index
def add_pointer(self, index, target):
self.pointers += [pointer(index, target)]
def add_target(self, target, index):
# TODO: warn about duplicates
#print "add_target(target='%s' index=%d)" % (target, index)
self.targets[target] = index
def write(self, index, size, data):
try:
self.data = self.data[:index] + data + self.data[index+size:]
except:
print "write error:"
print "\tself.data =", self.data
print "\tdata =", data
def patch_pointers(self):
for p in self.pointers:
if p.target in self.targets:
i = self.targets[p.target]
#print "ptr @ %d -> %s -> %d" % (p.index, p.target, i)
self.write(p.index, 8, struct.pack("P", i)) # TODO: fix me
else:
print "ERROR: couldn't find target '%s' for pointer at %d" % (p.target, p.index)
class type:
def __init__(self):
self.name = ""
def size(self):
pass
class structure:
def __init__(self):
self.name = ""
self.members = []
def size(self):
s = 0
for m in self.members:
s += m.size()
return s
def emit_header_code(self, out):
print >>out, "struct", self.name
print >>out, "{"
for m in self.members:
for l in m.get_code():
print >>out, "\t" + l
print >>out, "};"
print >>out, ""
def emit_source_code(self, out):
print >>out, "static void patch_ptr_%s(%s *self, char *base)" % (self.name, self.name)
print >>out, "{"
for m in self.members:
for l in m.get_patch_code("self", "base"):
print >>out, "\t" + l
print >>out, "}"
print >>out, ""
def emit_data(self, cons, index, src_data):
#print self.name+":"
member_index = index
for m in self.members:
#print "\t" + m.name
m.emit_data(cons, member_index, src_data)
member_index += m.size()
class variable:
def __init__(self):
self.expr = ""
self.type = ""
self.subtype = ""
def get_code(self):
return []
def get_patch_code(self, ptrname, basename):
return []
def emit_data(self, cons, index, src_data):
pass
class variable_int(variable):
def get_code(self):
return ["%s %s;" % (option_inttype, self.name)]
def size(self):
return option_intsize
def emit_data(self, cons, index, src_data):
try:
value = int(self.expr)
except:
value = int(src_data.get_single(self.expr))
#print "int", self.name, "=", value, "@", index
data = struct.pack("l", value)
cons.write(index, len(data), data)
class variable_float(variable):
def get_code(self):
return ["%s %s;" % (option_floattype, self.name)]
def size(self):
return option_floatsize
def emit_data(self, cons, index, src_data):
try:
value = float(self.expr)
except:
value = float(src_data.get_single(self.expr))
#print "int", self.name, "=", value, "@", index
data = struct.pack("f", value)
cons.write(index, len(data), data)
class variable_string(variable):
def get_code(self):
return ["char *%s;" % (self.name)]
def get_patch_code(self, ptrname, basename):
return ["patch_ptr((char **)&(%s->%s), %s);" % (ptrname, self.name, basename)]
def size(self):
return option_ptrsize
def emit_data(self, cons, index, src_data):
string = src_data.get_single(self.expr)
string = string.strip()[1:-1] # skip " and "
string_index = cons.allocate(len(string)+1)
cons.write(string_index, len(string), string)
data = struct.pack("P", string_index) # TODO: solve this
cons.write(index, len(data), data)
class variable_ptr(variable):
def get_code(self):
return ["%s *%s;" % (self.subtype, self.name)]
def get_patch_code(self, ptrname, basename):
return ["patch_ptr((char**)&(%s->%s), %s);" % (ptrname, self.name, basename)]
def size(self):
return option_ptrsize
def emit_data(self, cons, index, src_data):
target = src_data.get_single(self.expr)
cons.add_pointer(index, target)
class variable_instance(variable):
def get_code(self):
return ["%s %s;" % (self.subtype, self.name)]
def get_patch_code(self, ptrname, basename):
return ["patch_ptr_%s(&(%s->%s), %s);" % (self.subtype, ptrname, self.name, basename)]
def size(self):
return translator.types[self.subtype].size()
def emit_data(self, cons, index, src_data):
print self.expr
target = src_data.find_node(self.expr)
print target
translator.types[self.subtype].emit_data(cons, index, target)
#target =
#cons.add_pointer(index, target)
class variable_array(variable):
def get_code(self):
return ["long num_%s;" % self.name,
"%s *%s;" % (self.subtype, self.name)]
def get_patch_code(self, ptrname, baseptr):
code = []
code += ["patch_ptr((char**)&(%s->%s), %s);" % (ptrname, self.name, baseptr)]
code += ["for(int i = 0; i < %s->num_%s; i++)" % (ptrname, self.name)]
code += ["\tpatch_ptr_%s(%s->%s+i, %s);" % (self.subtype, ptrname, self.name, baseptr)]
return code
def emit_data(self, cons, index, src_data):
array_data = src_data.gather(self.expr)
array_type = cons.get_type(self.subtype)
size = array_type.size()*len(array_data)
#print "packing array", self.name
#print "\ttype =", array_type.name
#print "\tsize =", array_type.size()
array_index = cons.allocate(size)
data = struct.pack("lP", len(array_data), array_index) # TODO: solve this
cons.write(index, len(data), data)
member_index = array_index
for node in array_data:
cons.add_target(node.get_path()[1:], member_index)
array_type.emit_data(cons, member_index, node)
member_index += array_type.size()
#print "array", member_index
def size(self):
return option_ptrsize+option_intsize
class const_arrayint:
def __init__(self):
self.name = ""
self.values = []
def emit_header_code(self, out):
print >>out, "enum"
print >>out, "{"
for i in xrange(0, len(self.values)):
print >>out, "\t%s_%s = %d," % (self.name.upper(), self.values[i].upper(), i)
print >>out, "\tNUM_%sS = %d" % (self.name.upper(), len(self.values))
print >>out, "};"
print >>out, ""
class translator:
def __init__(self):
self.types = {}
self.structs = []
self.constants = []
self.data = 0
self.srcdata = 0
self.types["int"] = variable_int()
self.types["float"] = variable_float()
self.types["string"] = variable_string()
self.types["ptr"] = variable_ptr()
self.types["array"] = variable_array()
def parse_variable(self, node):
if len(node.values) != 4:
print node.values
raise "error parsing variable"
type = node.values[0]
subtype = ""
if type == "int":
v = variable_int()
elif type == "float":
v = variable_float()
elif type == "string":
v = variable_string()
else:
# complex type
parts = type.split(":")
if len(parts) != 2:
raise "can't emit code for variable %s of type %s" % (self.name, self.type)
elif parts[0] == "ptr":
subtype = parts[1]
v = variable_ptr()
elif parts[0] == "instance":
subtype = parts[1]
v = variable_instance()
elif parts[0] == "array":
subtype = parts[1]
v = variable_array()
else:
raise "can't emit code for variable %s of type %s" % (self.name, self.type)
v.translator = self
v.type = node.values[0]
v.subtype = subtype
v.name = node.values[1]
assignment = node.values[2]
v.expr = node.values[3]
if assignment != "=":
raise "error parsing variable. expected ="
return v
def parse_struct(self, node):
if len(node.values) != 2:
raise "error parsing struct"
s = structure()
s.name = node.values[1]
for statement in node.children:
s.members += [self.parse_variable(statement)]
return s
def parse_constant(self, node):
if len(node.values) != 5:
print node.values
raise "error parsing constant"
type = node.values[1]
name = node.values[2]
assignment = node.values[3]
expression = node.values[4]
if assignment != "=":
print node.values
raise "error parsing constant"
ints = const_arrayint()
ints.name = name
items = self.srcdata.gather(expression)
for c in items:
ints.values += [c.name()]
self.constants += [ints]
def parse(self, script, srcdata):
self.srcdata = srcdata
for statement in script.children:
if statement.values[0] == "struct":
s = self.parse_struct(statement)
self.structs += [s]
self.types[s.name] = s
elif statement.values[0] == "const":
self.parse_constant(statement)
else:
raise "unknown statement:" + statement
def emit_header_code(self, out):
for c in self.constants:
c.emit_header_code(out)
for s in self.structs:
s.emit_header_code(out)
print >>out, ""
print >>out, "data_container *load_data_container(const char *filename);"
print >>out, ""
def emit_source_code(self, out):
print >>out, '''
#include "../data.h"
#include <stdio.h>
#include <stdlib.h>
void patch_ptr(char **ptr, char *base)
{
*ptr = base+(size_t)(*ptr);
}
'''
for s in self.structs:
s.emit_source_code(out)
print >>out, '''
data_container *load_data_container(const char *filename)
{
data_container *con = 0;
int size;
/* open file */
FILE *f = fopen(filename, "rb");
/* get size */
fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, 0, SEEK_SET);
/* allocate, read data and close file */
con = (data_container*)malloc(size);
fread(con, 1, size, f);
fclose(f);
/* patch all pointers */
patch_ptr_data_container(con, (char *)con);
return con;
}
'''
def emit_data(self):
for s in self.structs:
if s.name == "data_container":
#print "found data_container"
cons = data_constructor()
cons.trans = self
i = cons.allocate(s.size())
s.emit_data(cons, i, self.srcdata)
cons.patch_pointers()
return cons.data
def create_translator(script, srcdata):
t = translator()
t.parse(script, srcdata)
return t
def validate(script, validator):
def validate_values(values, check):
if not len(check) or check[0] == "*":
print "too many values"
return
p = check[0].split(":")
type = p[0]
name = p[1]
# TODO: check type and stuff
# recurse
if len(values) > 1:
if not len(check):
print "unexpected value"
validate_values(values[1:], check[1:])
else:
if len(check) > 1 and check[1] != "*":
print "to few values"
if len(script.values):
validate_values(script.values, validator.values)
for child in script.children:
tag = child.values[0]
n = validator.find_node("tag:"+tag)
if not n:
found = 0
for vc in validator.children:
if "ident:" in vc.values[0]:
validate(child, vc)
print vc.values[0]
found = 1
break
if not found:
print "error:", tag, "not found"
else:
print "tag:"+tag
validate(child, n)
input_filename = sys.argv[1]
script_filename = sys.argv[2]
output_filename = 0
header_filename = 0
source_filename = 0
if sys.argv[3] == '-h':
header_filename = sys.argv[4]
elif sys.argv[3] == '-s':
source_filename = sys.argv[4]
elif sys.argv[3] == '-d':
output_filename = sys.argv[4]
srcdata = parse_file(input_filename)
script = parse_file(script_filename)
translator = create_translator(script, srcdata)
if header_filename:
translator.emit_header_code(file(header_filename, "w"))
if source_filename:
translator.emit_source_code(file(source_filename, "w"))
if output_filename:
rawdata = translator.emit_data()
file(output_filename, "wb").write(rawdata)
#print "filesize:", len(rawdata)

126
src/crapnet/crapnet.cpp Normal file
View file

@ -0,0 +1,126 @@
#include <baselib/system.h>
#include <baselib/network.h>
#include <cstdlib>
using namespace baselib;
struct packet
{
packet *prev;
packet *next;
netaddr4 send_to;
int64 timestamp;
int id;
int data_size;
char data[1];
};
static packet *first = (packet *)0;
static packet *last = (packet *)0;
static int current_latency = 0;
static int debug = 0;
int run(int port, netaddr4 dest)
{
netaddr4 src(0,0,0,0,0);
socket_udp4 socket;
socket.open(port);
char buffer[1024*2];
int id = 0;
while(1)
{
// handle incomming packets
while(1)
{
// fetch data
netaddr4 from;
int bytes = socket.recv(&from, buffer, 1024*2);
if(bytes <= 0)
break;
// create new packet
packet *p = (packet *)mem_alloc(sizeof(packet)+bytes, 1);
if(from == dest)
p->send_to = src;
else
{
src = from;
p->send_to = dest;
}
// queue packet
p->prev = last;
p->next = 0;
if(last)
last->next = p;
else
{
first = p;
last = p;
}
last = p;
// set data in packet
p->timestamp = time_get();
p->data_size = bytes;
p->id = id++;
mem_copy(p->data, buffer, bytes);
if(debug)
dbg_msg("crapnet", "<< %08d %d.%d.%d.%d:%5d (%d)", p->id, from.ip[0], from.ip[1], from.ip[2], from.ip[3], from.port, p->data_size);
}
//
while(1)
{
//dbg_msg("crapnet", "%p", first);
if(first && (time_get()-first->timestamp) > current_latency)
{
packet *p = first;
first = first->next;
if(first)
first->prev = 0;
else
last = 0;
if(debug)
{
dbg_msg("crapnet", ">> %08d %d.%d.%d.%d:%5d (%d)", p->id,
p->send_to.ip[0], p->send_to.ip[1],
p->send_to.ip[2], p->send_to.ip[3],
p->send_to.port, p->data_size);
}
// send and remove packet
if((rand()%10) != 0) // heavy packetloss
socket.send(&p->send_to, p->data, p->data_size);
// update lag
double flux = rand()/(double)RAND_MAX;
int ms_spike = 250;
int ms_flux = 100;
int ms_ping = 50;
current_latency = ((time_freq()*ms_ping)/1000) + (int64)(((time_freq()*ms_flux)/1000)*flux); // 50ms
if((p->id%100) == 0)
current_latency += (time_freq()*ms_spike)/1000;
mem_free(p);
}
else
break;
}
thread_sleep(1);
}
}
int main(int argc, char **argv)
{
run(8302, netaddr4(127,0,0,1,8303));
return 0;
}

View file

@ -6,6 +6,7 @@
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <engine/interface.h>
@ -13,59 +14,14 @@
#include <engine/snapshot.h>
#include "ui.h"
#include <engine/lzw.h>
#include <engine/compression.h>
#include <engine/versions.h>
#include <engine/config.h>
#include <engine/network.h>
using namespace baselib;
// --- string handling (MOVE THESE!!) ---
void snap_encode_string(const char *src, int *dst, int length, int max_length)
{
const unsigned char *p = (const unsigned char *)src;
// handle whole int
for(int i = 0; i < length/4; i++)
{
*dst = (p[0]<<24|p[1]<<16|p[2]<<8|p[3]);
p += 4;
dst++;
}
// take care of the left overs
int left = length%4;
if(left)
{
unsigned last = 0;
switch(left)
{
case 3: last |= p[2]<<8;
case 2: last |= p[1]<<16;
case 1: last |= p[0]<<24;
}
*dst = last;
}
}
void snap_decode_string(const int *src, char *dst, int max_length)
{
dbg_assert((max_length%4) == 0, "length must be power of 4");
for(int i = 0; i < max_length; i++)
dst[0] = 0;
for(int i = 0; i < max_length/4; i++)
{
dst[0] = (*src>>24)&0xff;
dst[1] = (*src>>16)&0xff;
dst[2] = (*src>>8)&0xff;
dst[3] = (*src)&0xff;
src++;
dst+=4;
}
dst[-1] = 0; // make sure to zero terminate
}
// --- input wrappers ---
static int keyboard_state[2][input::last];
static int keyboard_current = 0;
@ -92,7 +48,7 @@ void inp_update()
}
// --- input snapping ---
static int input_data[MAX_INPUT_SIZE];
static int input_data[MAX_INPUT_SIZE] = {0};
static int input_data_size;
static int input_is_changed = 1;
void snap_input(void *data, int size)
@ -110,6 +66,8 @@ enum
NUM_SNAPSHOT_TYPES=3,
};
static snapshot_storage snapshots_new;
static int current_tick;
static snapshot *snapshots[NUM_SNAPSHOT_TYPES];
static char snapshot_data[NUM_SNAPSHOT_TYPES][MAX_SNAPSHOT_SIZE];
static int recived_snapshots;
@ -127,7 +85,7 @@ void *snap_get_item(int snapid, int index, snap_item *item)
snapshot::item *i = snapshots[snapid]->get_item(index);
item->type = i->type();
item->id = i->id();
return (void *)i->data;
return (void *)i->data();
}
int snap_num_items(int snapid)
@ -145,11 +103,16 @@ static void snap_init()
recived_snapshots = 0;
}
float snap_intratick()
float client_intratick()
{
return (time_get() - snapshot_start_time)/(float)(time_freq()/SERVER_TICK_SPEED);
}
int client_tick()
{
return current_tick;
}
void *snap_find_item(int snapid, int type, int id)
{
// TODO: linear search. should be fixed.
@ -157,123 +120,45 @@ void *snap_find_item(int snapid, int type, int id)
{
snapshot::item *itm = snapshots[snapid]->get_item(i);
if(itm->type() == type && itm->id() == id)
return (void *)itm->data;
return (void *)itm->data();
}
return 0x0;
}
int menu_loop();
float frametime = 0.0001f;
int menu_loop(); // TODO: what is this?
static float frametime = 0.0001f;
float client_frametime()
{
return frametime;
}
void unpack(const char *src, const char *fmt, ...)
static net_client net;
int client_send_msg()
{
const msg_info *info = msg_get_info();
NETPACKET packet;
packet.client_id = 0;
packet.data = info->data;
packet.data_size = info->size;
if(info->flags&MSGFLAG_VITAL)
packet.flags = PACKETFLAG_VITAL;
net.send(&packet);
return 0;
}
/*int modc_onmsg(int msg)
{
msg_get("iis")
}*/
/*
i = int (int i)
s = string (const char *str)
r = raw data (int size, void *data)
*/
/*
class packet2
{
private:
// packet data
struct header
{
unsigned msg;
unsigned ack;
unsigned seq;
};
unsigned char packet_data[MAX_PACKET_SIZE];
unsigned char *current;
enum
{
MAX_PACKET_SIZE = 1024,
};
public:
packet2()
{
current = packet_data;
current += sizeof(header);
}
int pack(char *dst, const char *fmt, ...)
{
va_list arg_list;
va_start(arg_list, fmt);
while(*fmt)
{
if(*fmt == 's')
{
// pack string
const char *s = va_arg(arg_list, const char*);
*dst++ = 2;
while(*s)
{
*dst = *s;
dst++;
s++;
}
*dst = 0; // null terminate
dst++;
fmt++;
}
else if(*fmt == 'i')
{
// pack int
int i = va_arg(arg_list, int);
*dst++ = 1;
*dst++ = (i>>24)&0xff;
*dst++ = (i>>16)&0xff;
*dst++ = (i>>8)&0xff;
*dst++ = i&0xff;
fmt++;
}
else
{
dbg_break(); // error
break;
}
}
va_end(arg_list);
}
};
*/
/*
int msg_get(const char *fmt)
{
}
int client_msg_send(int msg, const char *fmt, ...)
int server_msg_send(int msg, const char *fmt, ...)
{
}*/
// --- client ---
// TODO: remove this class
class client
{
public:
socket_udp4 socket;
connection conn;
//socket_udp4 socket;
//connection conn;
int64 reconnect_timer;
int snapshot_part;
@ -305,37 +190,24 @@ public:
void set_fullscreen(bool flag) { fullscreen = flag; }
void send_packet(packet *p)
{
conn.send(p);
}
void send_connect()
void send_info()
{
recived_snapshots = 0;
/*
pack(NETMSG_CLIENT_CONNECT, "sssss",
TEEWARS_NETVERSION,
name,
"no clan",
"password",
"myskin");
*/
packet p(NETMSG_CLIENT_CONNECT);
p.write_str(TEEWARS_VERSION); // payload
p.write_str(config.player_name);
p.write_str("no clan");
p.write_str("password");
p.write_str("myskin");
send_packet(&p);
msg_pack_start(NETMSG_INFO, MSGFLAG_VITAL);
msg_pack_string(config.player_name, 128);
msg_pack_string(config.clan_name, 128);
msg_pack_string(config.password, 128);
msg_pack_string("myskin", 128);
msg_pack_end();
client_send_msg();
}
void send_done()
void send_entergame()
{
packet p(NETMSG_CLIENT_DONE);
send_packet(&p);
msg_pack_start(NETMSG_ENTERGAME, MSGFLAG_VITAL);
msg_pack_end();
client_send_msg();
}
void send_error(const char *error)
@ -343,40 +215,38 @@ public:
/*
pack(NETMSG_CLIENT_ERROR, "s", error);
*/
/*
packet p(NETMSG_CLIENT_ERROR);
p.write_str(error);
send_packet(&p);
//send_packet(&p);
//send_packet(&p);
*/
}
void send_input()
{
/*
pack(NETMSG_CLIENT_ERROR, "s", error);
*/
packet p(NETMSG_CLIENT_INPUT);
p.write_int(input_data_size);
msg_pack_start(NETMSG_INPUT, 0);
msg_pack_int(input_data_size);
for(int i = 0; i < input_data_size/4; i++)
p.write_int(input_data[i]);
send_packet(&p);
msg_pack_int(input_data[i]);
msg_pack_end();
client_send_msg();
}
void disconnect()
{
/*
send_error("disconnected");
set_state(STATE_OFFLINE);
map_unload();
*/
}
void connect(netaddr4 *server_address)
{
conn.init(&socket, server_address);
// start by sending connect
send_connect();
net.connect(server_address);
set_state(STATE_CONNECTING);
reconnect_timer = time_get()+time_freq();
}
bool load_data()
@ -385,14 +255,42 @@ public:
return true;
}
void debug_render()
{
gfx_blend_normal();
gfx_texture_set(debug_font);
gfx_mapscreen(0,0,gfx_screenwidth(),gfx_screenheight());
static NETSTATS prev, current;
static int64 last_snap = 0;
if(time_get()-last_snap > time_freq()/10)
{
last_snap = time_get();
prev = current;
net.stats(&current);
}
char buffer[512];
sprintf(buffer, "send: %8d recv: %8d",
(current.send_bytes-prev.send_bytes)*10,
(current.recv_bytes-prev.recv_bytes)*10);
gfx_quads_text(10, 10, 16, buffer);
}
void render()
{
gfx_clear(0.0f,0.0f,0.0f);
// this should be moved around abit
// TODO: clean this shit up!
if(get_state() == STATE_ONLINE)
{
modc_render();
// debug render stuff
debug_render();
}
else if (get_state() != STATE_CONNECTING && get_state() != STATE_LOADING)
{
@ -404,7 +302,7 @@ public:
else if (status)
connect(&server_address);
}
else if (get_state() == STATE_CONNECTING)
else if (get_state() == STATE_CONNECTING || get_state() == STATE_LOADING)
{
static int64 start = time_get();
static int tee_texture;
@ -458,15 +356,18 @@ public:
// init menu
modmenu_init();
net.open(0);
// open socket
/*
if(!socket.open(0))
{
dbg_msg("network/client", "failed to open socket");
return;
}
}*/
// connect to the server if wanted
if (server_address)
if(server_address)
connect(server_address);
//int64 inputs_per_second = 50;
@ -504,6 +405,9 @@ public:
input::set_mouse_mode(input::mode_absolute);
if(input::pressed(input::f2))
input::set_mouse_mode(input::mode_relative);
if(input::pressed(input::lctrl) && input::pressed('Q'))
break;
// pump the network
pump_network();
@ -523,13 +427,13 @@ public:
if(reporttime < time_get())
{
unsigned sent, recved;
conn.counter_get(&sent, &recved);
dbg_msg("client/report", "fps=%.02f",
frames/(float)(reportinterval/time_freq()));
//unsigned sent, recved;
//conn.counter_get(&sent, &recved);
dbg_msg("client/report", "fps=%.02f netstate=%d",
frames/(float)(reportinterval/time_freq()), net.state());
frames = 0;
reporttime += reportinterval;
conn.counter_reset();
//conn.counter_reset();
}
if (input::pressed(input::esc))
@ -556,117 +460,171 @@ public:
set_state(STATE_BROKEN);
}
void process_packet(packet *p)
void process_packet(NETPACKET *packet)
{
if(p->version() != TEEWARS_NETVERSION)
int msg = msg_unpack_start(packet->data, packet->data_size);
if(msg == NETMSG_MAP)
{
error("wrong version");
}
else if(p->msg() == NETMSG_SERVER_ACCEPT)
{
const char *map;
map = p->read_str();
const char *map = msg_unpack_string();
dbg_msg("client/network", "connection accepted, map=%s", map);
set_state(STATE_LOADING);
if(p->is_good())
if(map_load(map))
{
dbg_msg("client/network", "connection accepted, map=%s", map);
set_state(STATE_LOADING);
if(map_load(map))
{
modc_entergame();
send_done();
dbg_msg("client/network", "loading done");
// now we will wait for two snapshots
// to finish the connection
}
else
{
error("failure to load map");
}
modc_entergame();
send_entergame();
dbg_msg("client/network", "loading done");
// now we will wait for two snapshots
// to finish the connection
}
else
{
error("failure to load map");
}
}
else if(p->msg() == NETMSG_SERVER_SNAP)
else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSMALL || msg == NETMSG_SNAPEMPTY)
{
//dbg_msg("client/network", "got snapshot");
int num_parts = p->read_int();
int part = p->read_int();
int part_size = p->read_int();
int game_tick = msg_unpack_int();
int delta_tick = game_tick-msg_unpack_int();
int num_parts = 1;
int part = 0;
int part_size = 0;
if(p->is_good())
if(msg == NETMSG_SNAP)
{
if(snapshot_part == part)
num_parts = msg_unpack_int();
part = msg_unpack_int();
}
if(msg != NETMSG_SNAPEMPTY)
part_size = msg_unpack_int();
if(snapshot_part == part)
{
// TODO: clean this up abit
const char *d = (const char *)msg_unpack_raw(part_size);
mem_copy((char*)snapshots[SNAP_INCOMMING] + part*MAX_SNAPSHOT_PACKSIZE, d, part_size);
snapshot_part++;
if(snapshot_part == num_parts)
{
const char *d = p->read_raw(part_size);
mem_copy((char*)snapshots[SNAP_INCOMMING] + part*MAX_SNAPSHOT_PACKSIZE, d, part_size);
snapshot_part++;
if(snapshot_part == num_parts)
{
snapshot *tmp = snapshots[SNAP_PREV];
snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
snapshots[SNAP_CURRENT] = tmp;
snapshot *tmp = snapshots[SNAP_PREV];
snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
snapshots[SNAP_CURRENT] = tmp;
current_tick = game_tick;
// decompress snapshot
lzw_decompress(snapshots[SNAP_INCOMMING], snapshots[SNAP_CURRENT]);
// apply snapshot, cycle pointers
recived_snapshots++;
snapshot_start_time = time_get();
// we got two snapshots until we see us self as connected
if(recived_snapshots == 2)
{
local_start_time = time_get();
set_state(STATE_ONLINE);
}
if(recived_snapshots > 2)
modc_newsnapshot();
snapshot_part = 0;
// decompress snapshot
void *deltadata = snapshot_empty_delta();
int deltasize = sizeof(int)*3;
unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE];
unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE];
if(part_size)
{
//int snapsize = lzw_decompress(snapshots[SNAP_INCOMMING], snapshots[SNAP_CURRENT]);
int compsize = zerobit_decompress(snapshots[SNAP_INCOMMING], part_size, tmpbuffer);
//int compsize = lzw_decompress(snapshots[SNAP_INCOMMING],tmpbuffer);
int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2);
deltadata = tmpbuffer2;
deltasize = intsize;
}
}
else
{
dbg_msg("client", "snapshot reset!");
// find snapshot that we should use as delta
static snapshot emptysnap;
emptysnap.data_size = 0;
emptysnap.num_items = 0;
snapshot *deltashot = &emptysnap;
int deltashot_size;
if(delta_tick >= 0)
{
void *delta_data;
deltashot_size = snapshots_new.get(delta_tick, &delta_data);
if(deltashot_size >= 0)
{
deltashot = (snapshot *)delta_data;
}
else
{
// TODO: handle this
dbg_msg("client", "error, couldn't find the delta snapshot");
}
}
int snapsize = snapshot_unpack_delta(deltashot, (snapshot*)snapshots[SNAP_CURRENT], deltadata, deltasize);
//snapshot *shot = (snapshot *)snapshots[SNAP_CURRENT];
// purge old snapshots
snapshots_new.purge_until(delta_tick);
snapshots_new.purge_until(game_tick-50); // TODO: change this to server tickrate
// add new
snapshots_new.add(game_tick, snapsize, snapshots[SNAP_CURRENT]);
// apply snapshot, cycle pointers
recived_snapshots++;
snapshot_start_time = time_get();
// we got two snapshots until we see us self as connected
if(recived_snapshots == 2)
{
local_start_time = time_get();
set_state(STATE_ONLINE);
}
if(recived_snapshots > 2)
modc_newsnapshot();
snapshot_part = 0;
// ack snapshot
msg_pack_start(NETMSG_SNAPACK, 0);
msg_pack_int(game_tick);
msg_pack_end();
client_send_msg();
}
}
}
else
{
dbg_msg("server/client", "unknown packet %x", p->msg());
else
{
dbg_msg("client", "snapshot reset!");
snapshot_part = 0;
}
}
}
void pump_network()
{
while(1)
net.update();
// check for errors
if(get_state() != STATE_OFFLINE && net.state() == NETSTATE_OFFLINE)
{
packet p;
netaddr4 from;
int bytes = socket.recv(&from, p.data(), p.max_size());
if(bytes <= 0)
break;
process_packet(&p);
// TODO: add message to the user there
set_state(STATE_OFFLINE);
}
//
if(get_state() == STATE_CONNECTING && net.state() == NETSTATE_ONLINE)
{
// we switched to online
dbg_msg("client", "connected, sending info");
set_state(STATE_LOADING);
send_info();
}
//
if(get_state() == STATE_CONNECTING && time_get() > reconnect_timer)
{
send_connect();
reconnect_timer = time_get() + time_freq();
}
// process packets
NETPACKET packet;
while(net.recv(&packet))
process_packet(&packet);
}
};
int main(int argc, char **argv)
{
dbg_msg("client", "starting...");
config_reset();
config_load("teewars.cfg");
@ -683,9 +641,23 @@ int main(int argc, char **argv)
{
if(argv[i][0] == '-' && argv[i][1] == 'c' && argv[i][2] == 0 && argc - i > 1)
{
// -c SERVER
// -c SERVER:PORT
i++;
if(net_host_lookup(argv[i], 8303, &server_address) != 0)
const char *port_str = 0;
for(int k = 0; argv[i][k]; k++)
{
if(argv[i][k] == ':')
{
port_str = &(argv[i][k+1]);
argv[i][k] = 0;
break;
}
}
int port = 8303;
if(port_str)
port = atoi(port_str);
if(net_host_lookup(argv[i], port, &server_address) != 0)
dbg_msg("main", "could not find the address of %s, connecting to localhost", argv[i]);
else
connect_at_once = true;

View file

@ -1,3 +1,4 @@
#include <baselib/system.h>
#include <string.h>
// LZW Compressor
@ -221,3 +222,149 @@ long lzw_decompress(const void *src_, void *dst_)
return 0;
}
// Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign
unsigned char *vint_pack(unsigned char *dst, int i)
{
*dst = (i>>25)&0x40; // set sign bit if i<0
i = i^(i>>31); // if(i<0) i = ~i
*dst |= i&0x3F; // pack 6bit into dst
i >>= 6; // discard 6 bits
if(i)
{
*dst |= 0x80; // set extend bit
while(1)
{
dst++;
*dst = i&(0x7F); // pack 7bit
i >>= 7; // discard 7 bits
*dst |= (i!=0)<<7; // set extend bit (may branch)
if(!i)
break;
}
}
dst++;
return dst;
}
const unsigned char *vint_unpack(const unsigned char *src, int *i)
{
int sign = (*src>>6)&1;
*i = *src&0x3F;
while(1)
{
if(!(*src&0x80)) break;
src++;
*i |= (*src&(0x7F))<<(6);
if(!(*src&0x80)) break;
src++;
*i |= (*src&(0x7F))<<(6+7);
if(!(*src&0x80)) break;
src++;
*i |= (*src&(0x7F))<<(6+7+7);
if(!(*src&0x80)) break;
src++;
*i |= (*src&(0x7F))<<(6+7+7+7);
}
src++;
*i ^= -sign; // if(sign) *i = ~(*i)
return src;
}
long intpack_decompress(const void *src_, int size, void *dst_)
{
const unsigned char *src = (unsigned char *)src_;
const unsigned char *end = src + size;
int *dst = (int *)dst_;
while(src < end)
{
src = vint_unpack(src, dst);
dst++;
}
return (long)((unsigned char *)dst-(unsigned char *)dst_);
}
long intpack_compress(const void *src_, int size, void *dst_)
{
int *src = (int *)src_;
unsigned char *dst = (unsigned char *)dst_;
size /= 4;
while(size)
{
dst = vint_pack(dst, *src);
size--;
src++;
}
return (long)(dst-(unsigned char *)dst_);
}
long zerobit_compress(const void *src_, int size, void *dst_)
{
unsigned char *src = (unsigned char *)src_;
unsigned char *dst = (unsigned char *)dst_;
//int zerocount = 0 ;
while(size)
{
unsigned char bit = 0x80;
unsigned char mask = 0;
int dst_move = 1;
int chunk = size < 8 ? size : 8;
size -= chunk;
for(int b = 0; b < chunk; b++, bit>>=1)
{
if(*src)
{
dst[dst_move] = *src;
mask |= bit;
dst_move++;
}
src++;
}
*dst = mask;
dst += dst_move;
}
long l = (long)(dst-(unsigned char *)dst_);
//dbg_msg("zerobit", "%d", (int)l);
return l;
}
long zerobit_decompress(const void *src_, int size, void *dst_)
{
unsigned char *src = (unsigned char *)src_;
unsigned char *dst = (unsigned char *)dst_;
unsigned char *end = src + size;
//int zerocount = 0 ;
while(src != end)
{
unsigned char bit = 0x80;
unsigned char mask = *src++;
for(int b = 0; b < 8; b++, bit>>=1)
{
if(mask&bit)
*dst++ = *src++;
else
*dst++ = 0;
}
}
long l = (long)(dst-(unsigned char *)dst_);
//dbg_msg("zerobit", "%d", (int)l);
return l;
}

12
src/engine/compression.h Normal file
View file

@ -0,0 +1,12 @@
// lzw is no longer in use
long lzw_compress(const void *src, int size, void *dst);
long lzw_decompress(const void *src, void *dst);
unsigned char *vint_pack(unsigned char *dst, int i);
const unsigned char *vint_unpack(const unsigned char *src, int *inout);
long intpack_compress(const void *src, int size, void *dst);
long intpack_decompress(const void *src, int size, void *dst);
long zerobit_compress(const void *src, int size, void *dst);
long zerobit_decompress(const void *src, int size, void *dst);

View file

@ -29,7 +29,7 @@ void config_set(const char *line)
const char *val_str = strchr(line, '=');
if (val_str)
{
memcpy(var_str, line, val_str - line);
mem_copy(var_str, line, val_str - line);
var_str[val_str - line] = 0;
++val_str;

View file

@ -1,2 +1,9 @@
#include "../game/game_variables.h"
MACRO_CONFIG_INT(screen_width, 800, 0, 0)
MACRO_CONFIG_INT(screen_height, 600, 0, 0)
MACRO_CONFIG_STR(player_name, 32, "nameless tee")
MACRO_CONFIG_STR(clan_name, 32, "")
MACRO_CONFIG_STR(password, 32, "")
MACRO_CONFIG_INT(sv_port, 8303, 0, 0)

View file

@ -520,7 +520,7 @@ void snap_input(void *data, int size);
Remarks:
DOCTODO: Explain how to use it.
*/
float snap_intratick();
//float snap_intratick();
/*
Group: Server Callbacks
@ -681,8 +681,13 @@ void modmenu_shutdown();
*/
int modmenu_render(void *server_address);
void snap_encode_string(const char *src, int *dst, int length, int max_length);
void snap_decode_string(const int *src, char *dst, int length);
//void snap_encode_string(const char *src, int *dst, int length, int max_length);
//void snap_decode_string(const int *src, char *dst, int length);
int server_getclientinfo(int client_id, client_info *info);
int server_tick();
@ -694,6 +699,44 @@ void inp_update();
float client_frametime();
float client_localtime();
// message packing
enum
{
MSGFLAG_VITAL=1,
};
void msg_pack_start(int msg, int flags);
void msg_pack_int(int i);
void msg_pack_string(const char *p, int limit);
void msg_pack_raw(const void *data, int size);
void msg_pack_end();
struct msg_info
{
int msg;
int flags;
const unsigned char *data;
int size;
};
const msg_info *msg_get_info();
// message unpacking
int msg_unpack_start(const void *data, int data_size);
int msg_unpack_int();
const char *msg_unpack_string();
const unsigned char *msg_unpack_raw(int size);
// message sending
int server_send_msg(int client_id);
int client_send_msg();
int client_tick();
float client_intratick();
int modc_message();
#define MASTER_SERVER_ADDRESS "master.teewars.com"
#define MASTER_SERVER_PORT 8300

View file

@ -1,2 +0,0 @@
long lzw_compress(const void *src, int size, void *dst);
long lzw_decompress(const void *src, void *dst);

43
src/engine/msg.cpp Normal file
View file

@ -0,0 +1,43 @@
#include "interface.h"
#include "packet.h"
// message packing
static data_packer packer;
static msg_info pack_info;
void msg_pack_int(int i) { packer.add_int(i); }
void msg_pack_string(const char *p, int limit) { packer.add_string(p, limit); }
void msg_pack_raw(const void *data, int size) { packer.add_raw((const unsigned char *)data, size); }
void msg_pack_start(int msg, int flags)
{
packer.reset();
pack_info.msg = msg;
pack_info.flags = flags;
msg_pack_int(msg);
}
void msg_pack_end()
{
pack_info.size = packer.size();
pack_info.data = packer.data();
}
const msg_info *msg_get_info()
{
return &pack_info;
}
// message unpacking
static data_unpacker unpacker;
int msg_unpack_start(const void *data, int data_size)
{
unpacker.reset((const unsigned char *)data, data_size);
return msg_unpack_int();
}
int msg_unpack_int() { return unpacker.get_int(); }
const char *msg_unpack_string() { return unpacker.get_string(); }
const unsigned char *msg_unpack_raw(int size) { return unpacker.get_raw(size); }

686
src/engine/network.cpp Normal file
View file

@ -0,0 +1,686 @@
#include <baselib/system.h>
#include "network.h"
#include "ringbuffer.h"
/*
header:
unsigned char ID[2]; 2 'T' 'W'
unsigned char version; 3
unsigned char flags; 4
unsigned short seq; 6
unsigned short ack; 8
unsigned crc; 12 bytes
header v2:
unsigned char flags; 1
unsigned char seq_ack[3]; 4
unsigned char crc[2]; 6
*/
#define NETWORK_HEADER_V2
// move
static int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b)
{
if( a->ip[0] != b->ip[0] ||
a->ip[1] != b->ip[1] ||
a->ip[2] != b->ip[2] ||
a->ip[3] != b->ip[3] ||
a->port != b->port
)
return 1;
return 0;
}
enum
{
NETWORK_VERSION = 1,
#ifdef NETWORK_HEADER_V2
NETWORK_HEADER_SIZE = 6,
#else
NETWORK_HEADER_SIZE = 12,
#endif
NETWORK_MAX_PACKET_SIZE = 1024,
NETWORK_MAX_CLIENTS = 16,
NETWORK_CONNSTATE_OFFLINE=0,
NETWORK_CONNSTATE_CONNECT=1,
NETWORK_CONNSTATE_CONNECTACCEPTED=2,
NETWORK_CONNSTATE_ONLINE=3,
NETWORK_CONNSTATE_ERROR=4,
NETWORK_PACKETFLAG_CONNECT=0x01,
NETWORK_PACKETFLAG_ACCEPT=0x02,
NETWORK_PACKETFLAG_CLOSE=0x04,
NETWORK_PACKETFLAG_VITAL=0x08,
NETWORK_PACKETFLAG_RESEND=0x10,
//NETWORK_PACKETFLAG_STATELESS=0x20,
};
struct NETPACKETDATA
{
unsigned char ID[2];
unsigned char version;
unsigned char flags;
unsigned short seq;
unsigned short ack;
unsigned crc;
unsigned data_size;
int64 first_send_time;
unsigned char *data;
};
static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETDATA *packet)
{
unsigned char buffer[NETWORK_MAX_PACKET_SIZE];
#ifdef NETWORK_HEADER_V2
buffer[0] = packet->flags;
buffer[1] = ((packet->seq>>4)&0xf0) | ((packet->ack>>8)&0x0f);
buffer[2] = packet->seq;
buffer[3] = packet->ack;
buffer[4] = packet->crc>>8;
buffer[5] = packet->crc&0xff;
#else
buffer[0] = packet->ID[0];
buffer[1] = packet->ID[1];
buffer[2] = packet->version;
buffer[3] = packet->flags;
buffer[4] = packet->seq>>8;
buffer[5] = packet->seq&0xff;
buffer[6] = packet->ack>>8;
buffer[7] = packet->ack&0xff;
buffer[8] = (packet->crc>>24)&0xff;
buffer[9] = (packet->crc>>16)&0xff;
buffer[10] = (packet->crc>>8)&0xff;
buffer[11] = packet->crc&0xff;
#endif
mem_copy(buffer+NETWORK_HEADER_SIZE, packet->data, packet->data_size);
int send_size = NETWORK_HEADER_SIZE+packet->data_size;
//dbg_msg("network", "sending packet, size=%d (%d + %d)", send_size, NETWORK_HEADER_SIZE, packet->data_size);
net_udp4_send(socket, addr, buffer, send_size);
}
struct NETCONNECTION
{
unsigned seq;
unsigned ack;
unsigned state;
ring_buffer buffer;
int64 last_recv_time;
int64 last_send_time;
const char *error_string;
NETADDR4 peeraddr;
NETSOCKET socket;
NETSTATS stats;
};
struct NETSLOT
{
int online;
NETCONNECTION conn;
};
struct NETSERVER
{
NETSOCKET socket;
NETSLOT slots[NETWORK_MAX_CLIENTS];
unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE];
};
struct NETCLIENT
{
NETADDR4 server_addr;
NETSOCKET socket;
unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE];
NETCONNECTION conn;
};
static void conn_reset_stats(NETCONNECTION *conn)
{
mem_zero(&conn->stats, sizeof(conn->stats));
}
static void conn_reset(NETCONNECTION *conn)
{
conn->seq = 0;
conn->ack = 0;
conn->state = NETWORK_CONNSTATE_OFFLINE;
conn->error_string = 0;
conn->last_send_time = 0;
conn->last_recv_time = 0;
conn->buffer.reset();
}
static const char *conn_error(NETCONNECTION *conn)
{
return conn->error_string;
}
/*
static int conn_state(NETCONNECTION *conn)
{
return conn->state;
}*/
static void conn_init(NETCONNECTION *conn, NETSOCKET socket)
{
conn_reset(conn);
conn_reset_stats(conn);
conn->socket = socket;
}
static void conn_ack(NETCONNECTION *conn, int ack)
{
while(1)
{
ring_buffer::item *i = conn->buffer.first();
if(!i)
break;
NETPACKETDATA *resend = (NETPACKETDATA *)i->data();
if(resend->seq <= ack)
conn->buffer.pop_first();
else
break;
}
}
static void conn_send_raw(NETCONNECTION *conn, NETPACKETDATA *data)
{
conn->last_send_time = time_get();
conn->stats.send_packets++;
conn->stats.send_bytes += data->data_size + NETWORK_HEADER_SIZE;
send_packet(conn->socket, &conn->peeraddr, data);
}
static void conn_resend(NETCONNECTION *conn)
{
ring_buffer::item *i = conn->buffer.first();
while(i)
{
NETPACKETDATA *resend = (NETPACKETDATA *)i->data();
conn->stats.resend_packets++;
conn->stats.resend_bytes += resend->data_size + NETWORK_HEADER_SIZE;
conn_send_raw(conn, resend);
i = i->next;
}
}
static void conn_send(NETCONNECTION *conn, int flags, int data_size, const void *data)
{
if(flags&NETWORK_PACKETFLAG_VITAL)
conn->seq++;
NETPACKETDATA p;
p.ID[0] = 'T';
p.ID[1] = 'W';
p.version = NETWORK_VERSION;
p.flags = flags;
p.seq = conn->seq;
p.ack = conn->ack;
p.crc = 0;
p.data_size = data_size;
p.data = (unsigned char *)data;
p.first_send_time = time_get();
if(flags&NETWORK_PACKETFLAG_VITAL)
{
// save packet if we need to resend
NETPACKETDATA *resend = (NETPACKETDATA *)conn->buffer.alloc(sizeof(NETPACKETDATA)+p.data_size);
*resend = p;
resend->data = (unsigned char *)(resend+1);
mem_copy(resend->data, p.data, p.data_size);
}
// TODO: calc crc
conn_send_raw(conn, &p);
}
static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr)
{
if(conn->state != NETWORK_CONNSTATE_OFFLINE)
return -1;
// init connection
conn_reset(conn);
conn->peeraddr = *addr;
conn->state = NETWORK_CONNSTATE_CONNECT;
conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0);
return 0;
}
static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr)
{
conn->last_recv_time = time_get();
conn->stats.recv_packets++;
conn->stats.recv_bytes += p->data_size + NETWORK_HEADER_SIZE;
if(conn->state == NETWORK_CONNSTATE_OFFLINE)
{
if(p->flags == NETWORK_PACKETFLAG_CONNECT)
{
// send response and init connection
conn->state = NETWORK_CONNSTATE_ONLINE;
conn->peeraddr = *addr;
conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0);
dbg_msg("connection", "got connection, sending connect+accept");
}
}
else if(net_addr4_cmp(&conn->peeraddr, addr) == 0)
{
if(conn->state == NETWORK_CONNSTATE_ONLINE)
{
// remove packages that are acked
conn_ack(conn, p->ack);
//
if(p->flags&NETWORK_PACKETFLAG_RESEND)
conn_resend(conn);
if(p->flags&NETWORK_PACKETFLAG_VITAL)
{
if(p->seq == conn->ack+1)
{
// in sequence
conn->ack++;
}
else
{
// out of sequence, request resend
dbg_msg("conn", "asking for resend");
conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0);
return 0;
}
}
return 1;
}
else if(conn->state == NETWORK_CONNSTATE_CONNECT)
{
// connection made
if(p->flags == NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT)
{
conn_send(conn, NETWORK_PACKETFLAG_ACCEPT, 0, 0);
conn->state = NETWORK_CONNSTATE_ONLINE;
dbg_msg("connection", "got connect+accept, sending accept. connection online");
}
}
/*
else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED)
{
// connection made
if(p->flags == NETWORK_PACKETFLAG_ACCEPT)
{
conn->state = NETWORK_CONNSTATE_ONLINE;
dbg_msg("connection", "got accept. connection online");
}
}*/
else
{
// strange packet, wrong state
}
}
else
{
// strange packet, not ment for me
}
return 0;
}
static void conn_update(NETCONNECTION *conn)
{
if(conn->state == NETWORK_CONNSTATE_ERROR)
return;
// check for timeout
if(conn->state != NETWORK_CONNSTATE_OFFLINE &&
conn->state != NETWORK_CONNSTATE_CONNECT &&
(time_get()-conn->last_recv_time) > time_freq()*3)
{
conn->state = NETWORK_CONNSTATE_ERROR;
conn->error_string = "timeout";
}
// check for large buffer errors
if(conn->buffer.size() > 1024*64)
{
conn->state = NETWORK_CONNSTATE_ERROR;
conn->error_string = "too weak connection (out of buffer)";
}
if(conn->buffer.first())
{
NETPACKETDATA *resend = (NETPACKETDATA *)conn->buffer.first()->data();
if(time_get()-resend->first_send_time > time_freq()*3)
{
conn->state = NETWORK_CONNSTATE_ERROR;
conn->error_string = "too weak connection (not acked for 3 seconds)";
}
}
// send keep alives if nothing has happend for 250ms
if(conn->state == NETWORK_CONNSTATE_ONLINE)
{
if(time_get()-conn->last_send_time> time_freq()/4)
conn_send(conn, NETWORK_PACKETFLAG_VITAL, 0, 0);
}
else if(conn->state == NETWORK_CONNSTATE_CONNECT)
{
if(time_get()-conn->last_send_time > time_freq()/2) // send a new connect every 500ms
conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0);
}
else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED)
{
if(time_get()-conn->last_send_time > time_freq()/2) // send a new connect/accept every 500ms
conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0);
}
}
static int check_packet(unsigned char *buffer, int size, NETPACKETDATA *packet)
{
// check the size
if(size < NETWORK_HEADER_SIZE || size > NETWORK_MAX_PACKET_SIZE)
return -1;
// read the packet
#ifdef NETWORK_HEADER_V2
packet->ID[0] = 'T';
packet->ID[1] = 'W';
packet->version = NETWORK_VERSION;
packet->flags = buffer[0];
packet->seq = ((buffer[1]&0xf0)<<4)|buffer[2];
packet->ack = ((buffer[1]&0x0f)<<8)|buffer[3];
packet->crc = (buffer[8]<<24)|(buffer[9]<<16)|(buffer[10]<<8)|buffer[11];
#else
packet->ID[0] = buffer[0];
packet->ID[1] = buffer[1];
packet->version = buffer[2];
packet->flags = buffer[3];
packet->seq = (buffer[4]<<8)|buffer[5];
packet->ack = (buffer[6]<<8)|buffer[7];
packet->crc = (buffer[8]<<24)|(buffer[9]<<16)|(buffer[10]<<8)|buffer[11];
#endif
packet->data_size = size - NETWORK_HEADER_SIZE;
packet->data = buffer+NETWORK_HEADER_SIZE;
// check the packet
if(packet->ID[0] != 'T' || packet->ID[1] != 'W')
return 1;
if(packet->version != NETWORK_VERSION)
return 1;
// TODO: perform crc check
// return success
return 0;
}
NETSERVER *net_server_open(int port, int max_clients, int flags)
{
NETSERVER *server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1);
mem_zero(server, sizeof(NETSERVER));
server->socket = net_udp4_create(port);
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
conn_init(&server->slots[i].conn, server->socket);
return server;
}
int net_server_close(NETSERVER *s)
{
// TODO: implement me
return 0;
}
int net_server_newclient(NETSERVER *s)
{
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
{
if(!s->slots[i].online && s->slots[i].conn.state == NETWORK_CONNSTATE_ONLINE)
{
s->slots[i].online = 1;
return i;
}
}
return -1;
}
int net_server_delclient(NETSERVER *s)
{
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
{
if(s->slots[i].online && s->slots[i].conn.state != NETWORK_CONNSTATE_ONLINE)
{
s->slots[i].online = 0;
return i;
}
}
return -1;
}
int net_server_drop(NETSERVER *s, int client_id, const char *reason)
{
// TODO: insert lots of checks here
dbg_msg("net_server", "client dropped. cid=%d reason=\"%s\"", client_id, reason);
conn_reset(&s->slots[client_id].conn);
return 0;
}
int net_server_update(NETSERVER *s)
{
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
{
conn_update(&s->slots[i].conn);
if(conn_error(&s->slots[i].conn))
net_server_drop(s, i, conn_error(&s->slots[i].conn));
}
return 0;
}
int net_server_recv(NETSERVER *s, NETPACKET *packet)
{
while(1)
{
NETADDR4 addr;
int bytes = net_udp4_recv(s->socket, &addr, s->recv_buffer, NETWORK_MAX_PACKET_SIZE);
// no more packets for now
if(bytes <= 0)
break;
NETPACKETDATA data;
int r = check_packet(s->recv_buffer, bytes, &data);
if(r == 0)
{
// ok packet, process it
if(data.flags == NETWORK_PACKETFLAG_CONNECT)
{
// client that wants to connect
int found = 0;
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
{
if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE)
{
conn_feed(&s->slots[i].conn, &data, &addr);
found = 1;
break;
}
}
if(!found)
{
// TODO: send error
}
}
else
{
// find matching slot
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
{
if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
{
if(conn_feed(&s->slots[i].conn, &data, &addr))
{
if(data.data_size)
{
packet->client_id = i;
packet->address = addr;
packet->flags = 0;
packet->data_size = data.data_size;
packet->data = data.data;
return 1;
}
}
}
}
}
}
else
{
// errornous packet, drop it
}
// read header
// do checksum
}
return 0;
}
int net_server_send(NETSERVER *s, NETPACKET *packet)
{
// TODO: insert stuff for stateless stuff
dbg_assert(packet->client_id >= 0, "errornous client id");
dbg_assert(packet->client_id < NETWORK_MAX_CLIENTS, "errornous client id");
conn_send(&s->slots[packet->client_id].conn, 0, packet->data_size, packet->data);
return 0;
}
void net_server_stats(NETSERVER *s, NETSTATS *stats)
{
mem_zero(stats, sizeof(NETSTATS));
int num_stats = sizeof(NETSTATS)/sizeof(int);
int *istats = (int *)stats;
for(int c = 0; c < NETWORK_MAX_CLIENTS; c++)
{
int *sstats = (int *)(&(s->slots[c].conn.stats));
for(int i = 0; i < num_stats; i++)
istats[i] += sstats[i];
}
}
//
NETCLIENT *net_client_open(int flags)
{
NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1);
mem_zero(client, sizeof(NETCLIENT));
client->socket = net_udp4_create(0);
conn_init(&client->conn, client->socket);
return client;
}
int net_client_close(NETCLIENT *c)
{
// TODO: implement me
return 0;
}
int net_client_update(NETCLIENT *c)
{
// TODO: implement me
conn_update(&c->conn);
if(conn_error(&c->conn))
net_client_disconnect(c, conn_error(&c->conn));
return 0;
}
int net_client_disconnect(NETCLIENT *c, const char *reason)
{
// TODO: do this more graceful
dbg_msg("net_client", "disconnected. reason=\"%s\"", reason);
conn_reset(&c->conn);
return 0;
}
int net_client_connect(NETCLIENT *c, NETADDR4 *addr)
{
//net_client_disconnect(c);
conn_connect(&c->conn, addr);
return 0;
}
int net_client_recv(NETCLIENT *c, NETPACKET *packet)
{
while(1)
{
NETADDR4 addr;
int bytes = net_udp4_recv(c->socket, &addr, c->recv_buffer, NETWORK_MAX_PACKET_SIZE);
// no more packets for now
if(bytes <= 0)
break;
NETPACKETDATA data;
int r = check_packet(c->recv_buffer, bytes, &data);
if(r == 0)
{
// ok packet, process it
conn_feed(&c->conn, &data, &addr);
// fill in packet
packet->client_id = 0;
packet->address = addr;
packet->flags = 0;
packet->data_size = data.data_size;
packet->data = data.data;
return 1;
}
else
{
// errornous packet, drop it
}
// read header
// do checksum
}
return 0;
}
int net_client_send(NETCLIENT *c, NETPACKET *packet)
{
// TODO: insert stuff for stateless stuff
dbg_assert(packet->client_id == 0, "errornous client id");
conn_send(&c->conn, 0, packet->data_size, packet->data);
return 0;
}
int net_client_state(NETCLIENT *c)
{
if(c->conn.state == NETWORK_CONNSTATE_ONLINE)
return NETSTATE_ONLINE;
if(c->conn.state == NETWORK_CONNSTATE_OFFLINE)
return NETSTATE_OFFLINE;
return NETSTATE_CONNECTING;
}
void net_client_stats(NETCLIENT *c, NETSTATS *stats)
{
*stats = c->conn.stats;
}

104
src/engine/network.h Normal file
View file

@ -0,0 +1,104 @@
struct NETPACKET
{
// -1 means that it's a stateless packet
// 0 on the client means the server
int client_id;
NETADDR4 address; // only used when client_id == -1
int flags;
int data_size;
const void *data;
};
struct NETSTATS
{
int send_bytes;
int recv_bytes;
int send_packets;
int recv_packets;
int resend_packets;
int resend_bytes;
};
struct NETSERVER;
struct NETCLIENT;
enum
{
NETFLAG_ALLOWSTATELESS=1,
PACKETFLAG_VITAL=1,
NETSTATE_OFFLINE=0,
NETSTATE_CONNECTING,
NETSTATE_ONLINE,
};
// server side
NETSERVER *net_server_open(int port, int max_clients, int flags);
int net_server_recv(NETSERVER *s, NETPACKET *packet);
int net_server_send(NETSERVER *s, NETPACKET *packet);
int net_server_close(NETSERVER *s);
int net_server_update(NETSERVER *s);
int net_server_drop(NETSERVER *s, int client_id, const char *reason);
int net_server_newclient(NETSERVER *s); // -1 when no more, else, client id
int net_server_delclient(NETSERVER *s); // -1 when no more, else, client id
void net_server_stats(NETSERVER *s, NETSTATS *stats);
// client side
NETCLIENT *net_client_open(int flags);
int net_client_disconnect(NETCLIENT *c, const char *reason);
int net_client_connect(NETCLIENT *c, NETADDR4 *addr);
int net_client_recv(NETCLIENT *c, NETPACKET *packet);
int net_client_send(NETCLIENT *c, NETPACKET *packet);
int net_client_close(NETCLIENT *c);
int net_client_update(NETCLIENT *c);
int net_client_state(NETCLIENT *c);
void net_client_stats(NETCLIENT *c, NETSTATS *stats);
// wrapper classes for c++
#ifdef __cplusplus
class net_server
{
NETSERVER *ptr;
public:
net_server() : ptr(0) {}
~net_server() { close(); }
int open(int port, int max, int flags) { ptr = net_server_open(port, max, flags); return ptr != 0; }
int close() { int r = net_server_close(ptr); ptr = 0; return r; }
int recv(NETPACKET *packet) { return net_server_recv(ptr, packet); }
int send(NETPACKET *packet) { return net_server_send(ptr, packet); }
int update() { return net_server_update(ptr); }
int drop(int client_id, const char *reason) { return net_server_drop(ptr, client_id, reason); }
int newclient() { return net_server_newclient(ptr); }
int delclient() { return net_server_delclient(ptr); }
void stats(NETSTATS *stats) { net_server_stats(ptr, stats); }
};
class net_client
{
NETCLIENT *ptr;
public:
net_client() : ptr(0) {}
~net_client() { close(); }
int open(int flags) { ptr = net_client_open(flags); return ptr != 0; }
int close() { int r = net_client_close(ptr); ptr = 0; return r; }
int connect(NETADDR4 *addr) { return net_client_connect(ptr, addr); }
int disconnect(const char *reason) { return net_client_disconnect(ptr, reason); }
int recv(NETPACKET *packet) { return net_client_recv(ptr, packet); }
int send(NETPACKET *packet) { return net_client_send(ptr, packet); }
int update() { return net_client_update(ptr); }
int state() { return net_client_state(ptr); }
void stats(NETSTATS *stats) { net_client_stats(ptr, stats); }
};
#endif

View file

@ -1,437 +1,34 @@
#include <stdarg.h>
#include <baselib/stream/file.h>
#include <baselib/network.h>
#include "versions.h"
#define MACRO_MAKEINT(a,b,c,d) ((a<<24)|(b<<16)|(c<<8)|d)
// TODO: this is not KISS
class packet
{
friend class connection;
protected:
enum
{
MAX_PACKET_SIZE = 1024,
};
// packet data
struct header
{
unsigned id;
unsigned version;
unsigned size_and_flags;
unsigned crc;
unsigned msg;
unsigned ack;
unsigned seq;
};
unsigned char packet_data[MAX_PACKET_SIZE];
unsigned char *current;
// these are used to prepend data in the packet
// used for debugging so we have checks that we
// pack and unpack the same way
enum
{
DEBUG_TYPE_INT=0x1,
DEBUG_TYPE_STR=0x2,
DEBUG_TYPE_RAW=0x3,
};
// writes an int to the packet
void write_int_raw(int i)
{
// TODO: check for overflow
*(int*)current = i;
current += sizeof(int);
}
// reads an int from the packet
int read_int_raw()
{
// TODO: check for overflow
int i = *(int*)current;
current += sizeof(int);
return i;
}
void debug_insert_mark(int type, int size)
{
write_int_raw((type<<16)|size);
}
void debug_verify_mark(int type, int size)
{
if(read_int_raw() != ((type<<16) | size))
dbg_assert(0, "error during packet disassembly");
}
public:
enum
{
FLAG_VITAL=1,
FLAG_RESEND=2
};
packet(unsigned msg=0)
{
current = packet_data;
current += sizeof(header);
((header*)packet_data)->id = MACRO_MAKEINT('K','M','A',1);
((header*)packet_data)->version = TEEWARS_NETVERSION;
((header*)packet_data)->msg = msg;
}
void set_header(unsigned ack, unsigned seq)
{
((header*)packet_data)->ack = ack;
((header*)packet_data)->seq = seq;
}
// writes an int to the packet
void write_int(int i)
{
debug_insert_mark(DEBUG_TYPE_INT, 4);
write_int_raw(i);
}
void write_raw(const char *raw, int size)
{
debug_insert_mark(DEBUG_TYPE_RAW, size);
while(size--)
*current++ = *raw++;
}
// writes a string to the packet
void write_str(const char *str)
{
debug_insert_mark(DEBUG_TYPE_STR, 0);
int s = strlen(str)+1;
write_int_raw(s);
for(;*str; current++, str++)
*current = *str;
*current = 0;
current++;
}
// reads an int from the packet
int read_int()
{
debug_verify_mark(DEBUG_TYPE_INT, 4);
return read_int_raw();
}
// reads a string from the packet
const char *read_str()
{
debug_verify_mark(DEBUG_TYPE_STR, 0);
int size = read_int_raw();
const char *s = (const char *)current;
//dbg_msg("packet", "reading string '%s' (%d)", s, size);
current += size;
return s;
}
const char *read_raw(int size)
{
debug_verify_mark(DEBUG_TYPE_RAW, size);
const char *d = (const char *)current;
current += size;
return d;
}
// TODO: impelement this
bool is_good() const { return true; }
unsigned version() const { return ((header*)packet_data)->version; }
unsigned msg() const { return ((header*)packet_data)->msg; }
unsigned seq() const { return ((header*)packet_data)->seq; }
unsigned ack() const { return ((header*)packet_data)->ack; }
unsigned flags() const { return (((header*)packet_data)->size_and_flags) & 0xffff; }
// access functions to get the size and data
int size() const { return (int)(current-(unsigned char*)packet_data); }
int max_size() const { return MAX_PACKET_SIZE; }
void *data() { return packet_data; }
};
// TODO: remove all the allocations from this class
class ring_buffer
{
struct item
{
item *next;
item *prev;
int size;
};
item *first;
item *last;
unsigned buffer_size;
public:
ring_buffer()
{
first = 0;
last = 0;
buffer_size = 0;
}
~ring_buffer()
{
reset();
}
void reset()
{
// clear all
while(peek_data())
next();
}
void *alloc(int size)
{
item *i = (item*)mem_alloc(sizeof(item)+size, 1);
i->size = size;
i->prev = last;
i->next = 0;
if(last)
last->next = i;
else
first = i;
last = i;
buffer_size += size;
return (void*)(i+1);
}
unsigned peek_size()
{
if(!first)
return 0;
return first->size;
}
void *peek_data()
{
if(!first)
return 0;
return (void*)(first+1);
}
void next()
{
if(first)
{
item *next = first->next;
buffer_size += first->size;
mem_free(first);
first = next;
if(first)
first->prev = 0;
else
last = 0;
}
}
unsigned size() { return buffer_size; }
};
//
class connection
{
baselib::socket_udp4 *socket;
baselib::netaddr4 addr;
unsigned seq;
unsigned ack;
unsigned counter_sent_bytes;
unsigned counter_recv_bytes;
int needs_resend;
/*
struct resend_packet
{
resend_packet *next;
unsigned seq;
unsigned msg;
unsigned size;
char data[1];
};
resend_packet *first_resend;
resend_packet *last_resend;
*/
ring_buffer resend_buffer;
void save_for_resend(packet *p)
{
/*
packet *n = (packet *)resend_buffer.alloc(p->size());
mem_copy(n->data(), p->data(), p->size());
n->current = (unsigned char*)n->data() + p->size();
*/
}
void remove_resends(unsigned ack)
{
/*
while(1)
{
packet *p = (packet *)resend_buffer.peek_data();
if(!p)
break;
if(p->seq() > ack)
break;
resend_buffer.next();
}*/
}
public:
void counter_reset()
{
counter_sent_bytes = 0;
counter_recv_bytes = 0;
}
void counter_get(unsigned *sent, unsigned *recved)
{
*sent = counter_sent_bytes;
*recved = counter_recv_bytes;
}
void init(baselib::socket_udp4 *socket, const baselib::netaddr4 *addr)
{
resend_buffer.reset();
this->addr = *addr;
this->socket = socket;
ack = 0;
seq = 0;
needs_resend = 0;
counter_reset();
}
void send(packet *p)
{
if(p->flags()&packet::FLAG_VITAL)
seq++;
p->set_header(ack, seq);
if(p->flags()&packet::FLAG_VITAL)
save_for_resend(p);
// TODO: request resend if needed, use needs_resend variable
socket->send(&address(), p->data(), p->size());
counter_sent_bytes += p->size();
}
packet *feed(packet *p)
{
counter_recv_bytes += p->size();
if(p->flags()&packet::FLAG_VITAL)
{
if(p->seq() == ack+1)
{
// packet in seqence, ack it
ack++;
//dbg_msg("network/connection", "packet in sequence. seq/ack=%x", ack);
return p;
}
else if(p->seq() > ack)
{
// packet loss
needs_resend = 1;
dbg_msg("network/connection", "packet loss! seq=%x ack=%x+1", p->seq(), ack);
return p;
}
else
{
// we already got this packet
return 0x0;
}
}
// remove resends
remove_resends(p->ack());
// handle resends
if(p->flags()&packet::FLAG_RESEND)
{
// peer as requested a resend of all non acked packages.
}
return p;
}
const baselib::netaddr4 &address() const { return addr; }
void update()
{
}
};
//const char *NETWORK_VERSION = "development";
#include "ringbuffer.h"
#include "compression.h"
#include "snapshot.h"
enum
{
NETMSG_CONTEXT_CONNECT=0x00010000,
NETMSG_CONTEXT_GAME=0x00020000,
NETMSG_CONTEXT_GLOBAL=0x00040000,
NETMSG_NULL=0,
// connection phase
NETMSG_CLIENT_CONNECT=NETMSG_CONTEXT_CONNECT|1,
// str32 name
// str32 clan
// str32 password
// str32 skin
// sent by server
NETMSG_MAP,
NETMSG_SNAP,
NETMSG_SNAPEMPTY,
NETMSG_SNAPSMALL,
// TODO: These should be implemented to make the server send the map to the client on connect
// NETMSG_CLIENT_FETCH,
// NETMSG_SERVER_MAPDATA,
// sent by client
NETMSG_INFO,
NETMSG_ENTERGAME,
NETMSG_INPUT,
NETMSG_SNAPACK,
NETMSG_SERVER_ACCEPT=NETMSG_CONTEXT_CONNECT|2,
// str32 mapname
NETMSG_CLIENT_DONE=NETMSG_CONTEXT_CONNECT|3,
// nothing
// game phase
NETMSG_SERVER_SNAP = NETMSG_CONTEXT_GAME|1, // server will spam these
// int num_parts
// int part
// int size
// data *
NETMSG_CLIENT_INPUT = NETMSG_CONTEXT_GAME|1, // client will spam these
// int input[MAX_INPUTS]
NETMSG_SERVER_EVENT = NETMSG_CONTEXT_GAME|2,
NETMSG_CLIENT_EVENT = NETMSG_CONTEXT_GAME|2,
NETMSG_CLIENT_CHECKALIVE = NETMSG_CONTEXT_GAME|3, // check if client is alive
NETMSG_CLIENT_ERROR=0x0fffffff,
// str128 reason
NETMSG_SERVER_ERROR=0x0fffffff,
// str128 reason
// sent by both
NETMSG_ERROR,
};
// this should be revised
enum
{
MAX_NAME_LENGTH=32,
@ -440,3 +37,320 @@ enum
MAX_SNAPSHOT_SIZE=64*1024,
MAX_SNAPSHOT_PACKSIZE=768
};
class snapshot_storage
{
struct holder
{
int tick;
int data_size;
int *data() { return (int *)(this+1); }
};
ring_buffer buffer;
public:
void purge_until(int tick)
{
while(1)
{
ring_buffer::item *i = buffer.first();
if(!i)
break;
holder *h = (holder *)i->data();
if(h->tick < tick)
buffer.pop_first();
else
break;
}
}
void purge_all()
{
buffer.reset();
}
void add(int tick, int data_size, void *data)
{
holder *h = (holder *)buffer.alloc(sizeof(holder)+data_size);
h->tick = tick;
h->data_size = data_size;
mem_copy(h->data(), data, data_size);
}
int get(int tick, void **data)
{
ring_buffer::item *i = buffer.first();
while(i)
{
holder *h = (holder *)i->data();
if(h->tick == tick)
{
*data = h->data();
return h->data_size;
}
i = i->next;
}
return -1;
}
};
/*
class snapshot_delta_builder
{
public:
static const int MAX_ITEMS = 512;
char data[MAX_SNAPSHOT_SIZE];
int data_size;
int offsets[MAX_ITEMS];
int num_items;
int top_size;
int top_items;
int snapnum;
snapshot_delta_builder()
{
top_size = 0;
top_items = 0;
snapnum = 0;
}
void start()
{
data_size = 0;
num_items = 0;
}
int finish(void *snapdata)
{
snapnum++;
// flattern and make the snapshot
snapshot *snap = (snapshot *)snapdata;
snap->data_size = data_size;
snap->num_items = num_items;
int offset_size = sizeof(int)*num_items;
mem_copy(snap->offsets, offsets, offset_size);
mem_copy(snap->data_start(), data, data_size);
return sizeof(int) + offset_size + data_size;
}
void *new_item(int type, int id, int size)
{
snapshot::item *obj = (snapshot::item *)(data+data_size);
obj->type_and_id = (type<<16)|id;
offsets[num_items] = data_size;
data_size += sizeof(int) + size;
num_items++;
dbg_assert(data_size < MAX_SNAPSHOT_SIZE, "too much data");
dbg_assert(num_items < MAX_ITEMS, "too many items");
return &obj->data;
}
};
*/
class snapshot_builder
{
public:
static const int MAX_ITEMS = 512;
char data[MAX_SNAPSHOT_SIZE];
int data_size;
int offsets[MAX_ITEMS];
int num_items;
int top_size;
int top_items;
int snapnum;
snapshot_builder()
{
top_size = 0;
top_items = 0;
snapnum = 0;
}
void start()
{
data_size = 0;
num_items = 0;
}
snapshot::item *get_item(int index)
{
return (snapshot::item *)&(data[offsets[index]]);
}
int *get_item_data(int key)
{
for(int i = 0; i < num_items; i++)
{
if(get_item(i)->key() == key)
return (int *)get_item(i)->data();
}
return 0;
}
int finish(void *snapdata)
{
snapnum++;
// flattern and make the snapshot
snapshot *snap = (snapshot *)snapdata;
snap->data_size = data_size;
snap->num_items = num_items;
int offset_size = sizeof(int)*num_items;
mem_copy(snap->offsets(), offsets, offset_size);
mem_copy(snap->data_start(), data, data_size);
return sizeof(snapshot) + offset_size + data_size;
}
void *new_item(int type, int id, int size)
{
snapshot::item *obj = (snapshot::item *)(data+data_size);
obj->type_and_id = (type<<16)|id;
offsets[num_items] = data_size;
data_size += sizeof(snapshot::item) + size;
num_items++;
dbg_assert(data_size < MAX_SNAPSHOT_SIZE, "too much data");
dbg_assert(num_items < MAX_ITEMS, "too many items");
return obj->data();
}
};
class data_packer
{
enum
{
BUFFER_SIZE=1024*2
};
unsigned char buffer[BUFFER_SIZE];
unsigned char *current;
unsigned char *end;
int error;
public:
void reset()
{
error = 0;
current = buffer;
end = current + BUFFER_SIZE;
}
void add_int(int i)
{
// TODO: add space check
// TODO: variable length encoding perhaps
// TODO: add debug marker
current = vint_pack(current, i);
//*current++ = (i>>24)&0xff;
//*current++ = (i>>16)&0xff;
//*current++ = (i>>8)&0xff;
//*current++ = i&0xff;
}
void add_string(const char *p, int limit)
{
// TODO: add space check
// TODO: add debug marker
if(limit > 0)
{
while(*p && limit != 0)
{
*current++ = *p++;
limit--;
}
*current++ = 0;
}
else
{
while(*p)
*current++ = *p++;
*current++ = 0;
}
}
void add_raw(const unsigned char *data, int size)
{
// TODO: add space check
// TODO: add debug marker
//add_int(size);
while(size)
{
*current++ = *data++;
size--;
}
}
int size() const
{
return (const unsigned char *)current-(const unsigned char *)buffer;
}
const unsigned char *data()
{
return (const unsigned char *)buffer;
}
};
class data_unpacker
{
const unsigned char *current;
const unsigned char *start;
const unsigned char *end;
int error;
public:
void reset(const unsigned char *data, int size)
{
error = 0;
start = data;
end = start + size;
current = start;
}
int get_int()
{
int i;
current = vint_unpack(current, &i);
// TODO: might be changed into variable width
// TODO: add range check
// TODO: add debug marker
//i = (current[0]<<24) | (current[1]<<16) | (current[2]<<8) | (current[3]);
//current += 4;
return i;
}
const char *get_string()
{
// TODO: add range check
// TODO: add debug marker
const char *ptr = (const char *)current;
while(*current) // skip the string
current++;
current++;
return ptr;
}
const unsigned char *get_raw(int size)
{
// TODO: add range check
// TODO: add debug marker
//int s = get_int();
//if(size)
//*size = s;
const unsigned char *ptr = current;
current += size;
return ptr;
}
};

84
src/engine/ringbuffer.h Normal file
View file

@ -0,0 +1,84 @@
// TODO: remove all the allocations from this class
class ring_buffer
{
public:
struct item
{
item *next;
item *prev;
int size;
unsigned char *data() { return (unsigned char *)(this+1); }
};
item *first_item;
item *last_item;
unsigned buffer_size;
ring_buffer()
{
first_item = 0;
last_item = 0;
buffer_size = 0;
}
~ring_buffer()
{
reset();
}
void reset()
{
// clear all
while(first())
pop_first();
}
void *alloc(int size)
{
item *i = (item*)mem_alloc(sizeof(item)+size, 1);
i->size = size;
i->prev = last_item;
i->next = 0;
if(last_item)
last_item->next = i;
else
first_item = i;
last_item = i;
buffer_size += size;
return i->data();
}
item *first()
{
return first_item;
}
/*
void *peek_data()
{
if(!first)
return 0;
return (void*)(first+1);
}*/
void pop_first()
{
if(first_item)
{
item *next = first_item->next;
buffer_size -= first_item->size;
mem_free(first_item);
first_item = next;
if(first_item)
first_item->prev = 0;
else
last_item = 0;
}
}
unsigned size() { return buffer_size; }
};

View file

@ -1,139 +1,24 @@
#include <stdio.h>
#include <string.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <baselib/system.h>
#include <engine/interface.h>
//#include "socket.h"
#include <engine/packet.h>
#include <engine/snapshot.h>
#include <engine/lzw.h>
#include <engine/compression.h>
#include <engine/versions.h>
#include <engine/network.h>
#include <engine/config.h>
namespace baselib {}
using namespace baselib;
int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b)
{
if(
a->ip[0] != b->ip[0] ||
a->ip[1] != b->ip[1] ||
a->ip[2] != b->ip[2] ||
a->ip[3] != b->ip[3] ||
a->port != b->port
)
return 1;
return 0;
}
// --- string handling (MOVE THESE!!) ---
void snap_encode_string(const char *src, int *dst, int length, int max_length)
{
const unsigned char *p = (const unsigned char *)src;
// handle whole int
for(int i = 0; i < length/4; i++)
{
*dst = (p[0]<<24|p[1]<<16|p[2]<<8|p[3]);
p += 4;
dst++;
}
// take care of the left overs
int left = length%4;
if(left)
{
unsigned last = 0;
switch(left)
{
case 3: last |= p[2]<<8;
case 2: last |= p[1]<<16;
case 1: last |= p[0]<<24;
}
*dst = last;
}
}
class snapshot_builder
{
public:
static const int MAX_ITEMS = 512;
//static const int MAX_DATA_SIZE=1*1024;
char data[MAX_SNAPSHOT_SIZE];
int data_size;
int offsets[MAX_ITEMS];
int num_items;
int top_size;
int top_items;
int snapnum;
snapshot_builder()
{
top_size = 0;
top_items = 0;
snapnum = 0;
}
void start()
{
data_size = 0;
num_items = 0;
}
int finish(void *snapdata)
{
snapnum++;
// collect some data
/*
int change = 0;
if(data_size > top_size)
{
change++;
top_size = data_size;
}
if(num_items > top_items)
{
change++;
top_items = num_items;
}
if(change)
{
dbg_msg("snapshot", "new top, items=%d size=%d", top_items, top_size);
}*/
// flattern and make the snapshot
snapshot *snap = (snapshot *)snapdata;
snap->num_items = num_items;
int offset_size = sizeof(int)*num_items;
mem_copy(snap->offsets, offsets, offset_size);
mem_copy(snap->data_start(), data, data_size);
return sizeof(int) + offset_size + data_size;
}
void *new_item(int type, int id, int size)
{
snapshot::item *obj = (snapshot::item *)(data+data_size);
obj->type_and_id = (type<<16)|id;
offsets[num_items] = data_size;
data_size += sizeof(int) + size;
num_items++;
dbg_assert(data_size < MAX_SNAPSHOT_SIZE, "too much data");
dbg_assert(num_items < MAX_ITEMS, "too many items");
return &obj->data;
}
};
static snapshot_builder builder;
void *snap_new_item(int type, int id, int size)
@ -143,7 +28,6 @@ void *snap_new_item(int type, int id, int size)
return builder.new_item(type, id, size);
}
//
class client
{
@ -157,34 +41,21 @@ public:
// connection state info
int state;
// (ticks) if lastactivity > 5 seconds kick him
int64 lastactivity;
connection conn;
int last_acked_snapshot;
snapshot_storage snapshots;
char name[MAX_NAME_LENGTH];
char clan[MAX_CLANNAME_LENGTH];
/*
client()
{
state = STATE_EMPTY;
name[0] = 0;
clan[0] = 0;
}
~client()
{
dbg_assert(state == STATE_EMPTY, "client destoyed while in use");
}*/
bool is_empty() const { return state == STATE_EMPTY; }
bool is_ingame() const { return state == STATE_INGAME; }
const netaddr4 &address() const { return conn.address(); }
};
static client clients[MAX_CLIENTS];
static int current_tick = 0;
static int send_heartbeats = 1;
static net_server net;
int server_tick()
{
@ -193,7 +64,7 @@ int server_tick()
int server_tickspeed()
{
return 50;
return SERVER_TICK_SPEED;
}
int server_init()
@ -203,7 +74,7 @@ int server_init()
clients[i].state = client::STATE_EMPTY;
clients[i].name[0] = 0;
clients[i].clan[0] = 0;
clients[i].lastactivity = 0;
//clients[i].lastactivity = 0;
}
current_tick = 0;
@ -225,12 +96,27 @@ int server_getclientinfo(int client_id, client_info *info)
return 0;
}
//
int server_send_msg(int client_id)
{
const msg_info *info = msg_get_info();
NETPACKET packet;
packet.client_id = client_id;
packet.data = info->data;
packet.data_size = info->size;
if(info->flags&MSGFLAG_VITAL)
packet.flags = PACKETFLAG_VITAL;
net.send(&packet);
return 0;
}
// TODO: remove this class
class server
{
public:
socket_udp4 game_socket;
//socket_udp4 game_socket;
const char *map_name;
const char *server_name;
@ -256,14 +142,14 @@ public:
}
// start server
if(!game_socket.open(8303))
if(!net.open(8303, 0, 0))
{
dbg_msg("network/server", "couldn't open socket");
return false;
}
for(int i = 0; i < MAX_CLIENTS; i++)
dbg_msg("network/server", "\t%d: %d", i, clients[i].state);
//for(int i = 0; i < MAX_CLIENTS; i++)
//dbg_msg("network/server", "\t%d: %d", i, clients[i].state);
if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &master_server) != 0)
{
@ -304,17 +190,6 @@ public:
snaptime += time_get()-start;
}
// Check for client timeouts
for (int i = 0; i < MAX_CLIENTS; i++)
{
if (clients[i].state != client::STATE_EMPTY)
{
// check last activity time
if (((lasttick - clients[i].lastactivity) / time_freq()) > SERVER_CLIENT_TIMEOUT)
client_timeout(i);
}
}
lasttick += time_per_tick;
}
@ -332,8 +207,7 @@ public:
// TODO: fix me
netaddr4 me(127, 0, 0, 0, 8303);
send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname);
//send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname);
}
lastheartbeat = t+time_per_heartbeat;
@ -357,6 +231,7 @@ public:
(simulationtime+snaptime+networktime)/(float)reportinterval*100.0f);
unsigned sent_total=0, recv_total=0;
/*
for (int i = 0; i < MAX_CLIENTS; i++)
if (!clients[i].is_empty())
{
@ -366,7 +241,7 @@ public:
sent_total += s;
recv_total += r;
}
*/
dbg_msg("server/report", "biggestsnap=%d send=%d recv=%d",
biggest_snapshot, sent_total/3, recv_total/3);
@ -393,6 +268,8 @@ public:
void snap()
{
//if(current_tick&1)
// return;
mods_presnap();
for(int i = 0; i < MAX_CLIENTS; i++)
@ -400,33 +277,86 @@ public:
if(clients[i].is_ingame())
{
char data[MAX_SNAPSHOT_SIZE];
char deltadata[MAX_SNAPSHOT_SIZE];
char compdata[MAX_SNAPSHOT_SIZE];
//char intdata[MAX_SNAPSHOT_SIZE];
builder.start();
mods_snap(i);
// finish snapshot
int snapshot_size = builder.finish(data);
// compress it
int compsize = lzw_compress(data, snapshot_size, compdata);
snapshot_size = compsize;
if(snapshot_size > biggest_snapshot)
biggest_snapshot = snapshot_size;
const int max_size = MAX_SNAPSHOT_PACKSIZE;
int numpackets = (snapshot_size+max_size-1)/max_size;
for(int n = 0, left = snapshot_size; left; n++)
// remove old snapshos
// keep 1 seconds worth of snapshots
clients[i].snapshots.purge_until(current_tick-SERVER_TICK_SPEED);
// save it the snapshot
clients[i].snapshots.add(current_tick, snapshot_size, data);
// find snapshot that we can preform delta against
static snapshot emptysnap;
emptysnap.data_size = 0;
emptysnap.num_items = 0;
snapshot *deltashot = &emptysnap;
int deltashot_size;
int delta_tick = -1;
{
int chunk = left < max_size ? left : max_size;
left -= chunk;
void *delta_data;
deltashot_size = clients[i].snapshots.get(clients[i].last_acked_snapshot, (void **)&delta_data);
if(deltashot_size >= 0)
{
delta_tick = clients[i].last_acked_snapshot;
deltashot = (snapshot *)delta_data;
}
}
// create delta
int deltasize = snapshot_create_delta(deltashot, (snapshot*)data, deltadata);
if(deltasize)
{
// compress it
//int intsize = -1;
unsigned char intdata[MAX_SNAPSHOT_SIZE];
int intsize = intpack_compress(deltadata, deltasize, intdata);
int compsize = zerobit_compress(intdata, intsize, compdata);
//dbg_msg("compress", "%5d --delta-> %5d --int-> %5d --zero-> %5d %5d",
//snapshot_size, deltasize, intsize, compsize, intsize-compsize);
snapshot_size = compsize;
packet p(NETMSG_SERVER_SNAP);
p.write_int(numpackets);
p.write_int(n);
p.write_int(chunk);
p.write_raw(&compdata[n*max_size], chunk);
clients[i].conn.send(&p);
if(snapshot_size > biggest_snapshot)
biggest_snapshot = snapshot_size;
const int max_size = MAX_SNAPSHOT_PACKSIZE;
int numpackets = (snapshot_size+max_size-1)/max_size;
for(int n = 0, left = snapshot_size; left; n++)
{
int chunk = left < max_size ? left : max_size;
left -= chunk;
if(numpackets == 1)
msg_pack_start(NETMSG_SNAPSMALL, 0);
else
msg_pack_start(NETMSG_SNAP, 0);
msg_pack_int(current_tick);
msg_pack_int(current_tick-delta_tick); // compressed with
msg_pack_int(chunk);
msg_pack_raw(&compdata[n*max_size], chunk);
msg_pack_end();
//const msg_info *info = msg_get_info();
//dbg_msg("server", "size=%d", info->size);
server_send_msg(i);
}
}
else
{
msg_pack_start(NETMSG_SNAPEMPTY, 0);
msg_pack_int(current_tick);
msg_pack_int(current_tick-delta_tick); // compressed with
msg_pack_end();
server_send_msg(i);
}
}
}
@ -434,11 +364,12 @@ public:
mods_postsnap();
}
void send_accept(client *client, const char *map)
void send_map(int cid)
{
packet p(NETMSG_SERVER_ACCEPT);
p.write_str(map);
client->conn.send(&p);
msg_pack_start(NETMSG_MAP, MSGFLAG_VITAL);
msg_pack_string(map_name, 0);
msg_pack_end();
server_send_msg(cid);
}
void drop(int cid, const char *reason)
@ -451,161 +382,47 @@ public:
dbg_msg("game", "player dropped. reason='%s' cid=%x name='%s'", reason, cid, clients[cid].name);
}
int find_client(const netaddr4 *addr)
void process_client_packet(NETPACKET *packet)
{
// fetch client
for(int i = 0; i < MAX_CLIENTS; i++)
int cid = packet->client_id;
int msg = msg_unpack_start(packet->data, packet->data_size);
if(msg == NETMSG_INFO)
{
if(!clients[i].is_empty() && clients[i].address() == *addr)
return i;
strncpy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH);
strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH);
const char *password = msg_unpack_string();
const char *skin = msg_unpack_string();
(void)password; // ignore these variables
(void)skin;
send_map(cid);
}
return -1;
}
void client_process_packet(int cid, packet *p)
{
clients[cid].lastactivity = lasttick;
if(p->msg() == NETMSG_CLIENT_DONE)
else if(msg == NETMSG_ENTERGAME)
{
dbg_msg("game", "player as entered the game. cid=%x", cid);
clients[cid].state = client::STATE_INGAME;
mods_client_enter(cid);
}
else if(p->msg() == NETMSG_CLIENT_INPUT)
else if(msg == NETMSG_INPUT)
{
int input[MAX_INPUT_SIZE];
int size = p->read_int();
int size = msg_unpack_int();
for(int i = 0; i < size/4; i++)
input[i] = p->read_int();
if(p->is_good())
{
//dbg_msg("network/server", "applying input %d %d %d", input[0], input[1], input[2]);
mods_client_input(cid, input);
}
input[i] = msg_unpack_int();
mods_client_input(cid, input);
}
else if(p->msg() == NETMSG_CLIENT_ERROR)
else if(msg == NETMSG_SNAPACK)
{
const char *reason = p->read_str();
if(p->is_good())
dbg_msg("network/server", "client error. cid=%x reason='%s'", cid, reason);
else
dbg_msg("network/server", "client error. cid=%x", cid);
drop(cid, "client error");
clients[cid].last_acked_snapshot = msg_unpack_int();
}
else
{
dbg_msg("network/server", "invalid message. cid=%x msg=%x", cid, p->msg());
drop(cid, "invalid message");
dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size);
}
}
void process_packet(packet *p, netaddr4 *from)
void process_packet(NETPACKET *packet)
{
// do version check
if(p->version() != TEEWARS_NETVERSION)
{
// send an empty packet back.
// this will allow the client to check the version
packet p;
game_socket.send(from, p.data(), p.size());
return;
}
if(p->msg() == NETMSG_CLIENT_CONNECT)
{
// we got no state for this client yet
const char *version;
const char *name;
const char *clan;
const char *password;
const char *skin;
version = p->read_str();
name = p->read_str();
clan = p->read_str();
password = p->read_str();
skin = p->read_str();
if(p->is_good())
{
/*
// check version
if(strcmp(version, TEEWARS_NETVERSION) != 0)
{
dbg_msg("network/server", "wrong version connecting '%s'", version);
// TODO: send error
return;
}*/
// look for empty slot, linear search
int id = -1;
for(int i = 0; i < MAX_CLIENTS; i++)
if(clients[i].is_empty())
{
id = i;
break;
}
if(id != -1)
{
// slot found
// TODO: perform correct copy here
mem_copy(clients[id].name, name, MAX_NAME_LENGTH);
mem_copy(clients[id].clan, clan, MAX_CLANNAME_LENGTH);
clients[id].state = client::STATE_CONNECTING;
clients[id].conn.init(&game_socket, from);
clients[id].lastactivity = lasttick;
clients[id].name[MAX_NAME_LENGTH-1] = 0;
clients[id].clan[MAX_CLANNAME_LENGTH-1] = 0;
dbg_msg("network/server", "client connected. '%s' on slot %d", name, id);
// TODO: return success
send_accept(&clients[id], map_name);
}
else
{
// no slot found
// TODO: send error
dbg_msg("network/server", "client connected but server is full");
for(int i = 0; i < MAX_CLIENTS; i++)
dbg_msg("network/server", "\t%d: %d", i, clients[i].state);
}
}
}
else
{
int cid = find_client(from);
if(cid >= 0)
{
if(clients[cid].conn.feed(p))
{
// packet is ok
unsigned msg = p->msg();
// client found, check state
if(((msg>>16)&0xff)&clients[cid].state)
{
// state is ok
client_process_packet(cid, p);
}
else
{
// invalid state, disconnect the client
drop(cid, "invalid message at this state");
}
}
else
{
drop(cid, "connection error");
}
}
else
dbg_msg("network/server", "packet from strange address.");
}
}
void client_timeout(int clientId)
@ -615,67 +432,53 @@ public:
void pump_network()
{
net.update();
// process packets
NETPACKET packet;
while(net.recv(&packet))
{
if(packet.client_id == -1)
{
// stateless
}
else
process_client_packet(&packet);
}
// check for removed clients
while(1)
{
packet p;
netaddr4 from;
//int bytes = net_udp4_recv(
int bytes = game_socket.recv(&from, p.data(), p.max_size());
//int bytes = game_socket.recv(&from, p.data(), p.max_size());
if(bytes <= 0)
int cid = net.delclient();
if(cid == -1)
break;
process_packet(&p, &from);
clients[cid].state = client::STATE_EMPTY;
clients[cid].name[0] = 0;
clients[cid].clan[0] = 0;
clients[cid].snapshots.purge_all();
mods_client_drop(cid);
dbg_msg("server", "del client %d", cid);
}
// check for new clients
while(1)
{
int cid = net.newclient();
if(cid == -1)
break;
clients[cid].state = client::STATE_CONNECTING;
clients[cid].name[0] = 0;
clients[cid].clan[0] = 0;
clients[cid].snapshots.purge_all();
clients[cid].last_acked_snapshot = -1;
dbg_msg("server", "new client %d", cid);
}
// TODO: check for client timeouts
}
char *write_int(char *buffer, int integer)
{
*buffer++ = integer >> 24;
*buffer++ = integer >> 16;
*buffer++ = integer >> 8;
*buffer++ = integer;
return buffer;
}
char *write_netaddr4(char *buffer, NETADDR4 *address)
{
*buffer++ = address->ip[0];
*buffer++ = address->ip[1];
*buffer++ = address->ip[2];
*buffer++ = address->ip[3];
return write_int(buffer, address->port);
}
void send_heartbeat(int version, netaddr4 *address, int players, int max_players, const char *name, const char *map_name)
{
char buffer[216] = {0};
char *d = buffer;
d = write_int(d, 'TWHB');
d = write_int(d, version);
d = write_netaddr4(d, address);
d = write_int(d,players);
d = write_int(d, max_players);
int len = strlen(name);
if (len > 128)
len = 128;
memcpy(d, name, len);
d += 128;
len = strlen(map_name);
if (len > 64)
len = 64;
memcpy(d, map_name, len);
d += 64;
game_socket.send(&master_server, buffer, sizeof(buffer));
}
};
@ -683,8 +486,14 @@ int main(int argc, char **argv)
{
dbg_msg("server", "starting...");
dbg_msg("server", "%d %d", sizeof(snapshot), sizeof(snapshot::item));
config_reset();
config_load("server.cfg");
const char *mapname = "data/demo.map";
const char *servername = 0;
// parse arguments
for(int i = 1; i < argc; i++)
{
@ -705,6 +514,12 @@ int main(int argc, char **argv)
// -p (private server)
send_heartbeats = 0;
}
else if(argv[i][0] == '-' && argv[i][1] == 'o' && argv[i][2] == 0)
{
// -o port
i++;
config_set_sv_port(&config, atol(argv[i]));
}
}
if(!mapname)

235
src/engine/snapshot.cpp Normal file
View file

@ -0,0 +1,235 @@
#include "packet.h"
#include "snapshot.h"
struct snapshot_delta
{
int num_deleted_items;
int num_update_items;
int num_temp_items; // needed?
int data[1];
/*
char *data_start() { return (char *)&offsets[num_deleted_items+num_update_items+num_temp_items]; }
int deleted_item(int index) { return offsets[index]; }
item *update_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+index]); }
item *temp_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+num_update_items+index]); }
* */
};
static const int MAX_ITEMS = 512;
static snapshot_delta empty = {0,0,0,{0}};
void *snapshot_empty_delta()
{
return &empty;
}
static int diff_item(int *past, int *current, int *out, int size)
{
/*
int needed = 0;
while(size)
{
*out = *current-*past;
if(*out)
needed = 1;
out++;
current++;
past++;
size--;
}*/
int needed = 0;
while(size)
{
*out = *current-*past;
if(*out)
needed = 1;
out++;
past++;
current++;
size--;
}
return needed;
}
// 1 = 4-3
// d = n-p
// n(4) = p(3)+d(1)
static void undiff_item(int *past, int *diff, int *out, int size)
{
while(size)
{
*out = *past+*diff;
out++;
past++;
diff++;
size--;
}
}
int snapshot_create_delta(snapshot *from, snapshot *to, void *dstdata)
{
//int deleted[MAX_ITEMS];
//int update[MAX_ITEMS];
//int mark[MAX_ITEMS];
//char data[MAX_SNAPSHOT_SIZE];
snapshot_delta *delta = (snapshot_delta *)dstdata;
int *data = (int *)delta->data;
delta->num_deleted_items = 0;
delta->num_update_items = 0;
delta->num_temp_items = 0;
// pack deleted stuff
for(int i = 0; i < from->num_items; i++)
{
snapshot::item *fromitem = from->get_item(i);
if(to->get_item_index(fromitem->key()) == -1)
{
// deleted
delta->num_deleted_items++;
*data = fromitem->key();
data++;
}
}
// pack updated stuff
int count = 0, size_count = 0;
for(int i = 0; i < to->num_items; i++)
{
// do delta
int itemsize = to->get_item_datasize(i);
snapshot::item *curitem = to->get_item(i);
int pastindex = from->get_item_index(curitem->key());
if(pastindex != -1)
{
snapshot::item *pastitem = from->get_item(pastindex);
if(diff_item((int*)pastitem->data(), (int*)curitem->data(), data+3, itemsize/4))
{
*data++ = itemsize;
*data++ = curitem->type();
*data++ = curitem->id();
//*data++ = curitem->key();
data += itemsize/4;
delta->num_update_items++;
}
}
else
{
*data++ = itemsize;
*data++ = curitem->type();
*data++ = curitem->id();
//*data++ = curitem->key();
mem_copy(data, curitem->data(), itemsize);
size_count += itemsize;
data += itemsize/4;
delta->num_update_items++;
count++;
}
}
if(0)
{
dbg_msg("snapshot", "%d %d %d",
delta->num_deleted_items,
delta->num_update_items,
delta->num_temp_items);
}
// TODO: pack temp stuff
// finish
//mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int));
//mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int));
//mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int));
//mem_copy(delta->data_start(), data, data_size);
//delta->data_size = data_size;
if(!delta->num_deleted_items && !delta->num_update_items && !delta->num_temp_items)
return 0;
return (int)((char*)data-(char*)dstdata);
}
int snapshot_unpack_delta(snapshot *from, snapshot *to, void *srcdata, int data_size)
{
snapshot_builder builder;
snapshot_delta *delta = (snapshot_delta *)srcdata;
int *data = (int *)delta->data;
builder.start();
// unpack deleted stuff
int *deleted = data;
data += delta->num_deleted_items;
// copy all non deleted stuff
for(int i = 0; i < from->num_items; i++)
{
//dbg_assert(0, "fail!");
snapshot::item *fromitem = from->get_item(i);
int itemsize = from->get_item_datasize(i);
int keep = 1;
for(int d = 0; d < delta->num_deleted_items; d++)
{
if(deleted[d] == fromitem->key())
{
keep = 0;
break;
}
}
if(keep)
{
// keep it
int *newdata = (int *)(snapshot::item *)builder.new_item(fromitem->type(), fromitem->id(), itemsize);
mem_copy(newdata, fromitem->data(), itemsize);
}
}
// unpack updated stuff
for(int i = 0; i < delta->num_update_items; i++)
{
int itemsize, id, type, key;
itemsize = *data++;
//key = *data++;
type = *data++;
id = *data++;
key = (type<<16)|id;
// create the item if needed
int *newdata = builder.get_item_data(key);
if(!newdata)
newdata = (int *)builder.new_item(key>>16, key&0xffff, itemsize);
int fromindex = from->get_item_index(key);
if(fromindex != -1)
{
// we got an update so we need to apply the diff
int *pastdata = (int *)from->get_item(fromindex)->data();
undiff_item(pastdata, data, newdata, itemsize/4);
}
else // no previous, just copy the data
mem_copy(newdata, data, itemsize);
data += itemsize/4;
}
// TODO: unpack temp stuff
// finish up
return builder.finish(to);
}

View file

@ -1,19 +1,47 @@
#ifndef FILE_SNAPSHOT_H
#define FILE_SNAPSHOT_H
struct snapshot
{
int data_size;
int num_items;
int offsets[1];
struct item
{
int type_and_id;
char data[1];
int *data() { return (int *)(this+1); }
int type() { return type_and_id>>16; }
int id() { return type_and_id&(0xffff); }
int key() { return type_and_id; }
};
int *offsets() { return (int *)(this+1); }
char *data_start() { return (char *)(offsets() + num_items); }
item *get_item(int index) { return (item *)(data_start() + offsets()[index]); };
char *data_start() { return (char *)&offsets[num_items]; }
item *get_item(int index) { return (item *)(data_start() + offsets[index]); };
// returns the number of ints in the item data
int get_item_datasize(int index)
{
if(index == num_items-1)
return (data_size - offsets()[index]) - sizeof(item);
return (offsets()[index+1] - offsets()[index]) - sizeof(item);
}
int get_item_index(int key)
{
// TODO: this should not be a linear search. very bad
for(int i = 0; i < num_items; i++)
{
if(get_item(i)->key() == key)
return i;
}
return -1;
}
};
void *snapshot_empty_delta();
int snapshot_create_delta(snapshot *from, snapshot *to, void *data);
int snapshot_unpack_delta(snapshot *from, snapshot *to, void *data, int data_size);
#endif // FILE_SNAPSHOT_H

View file

@ -1,3 +1,3 @@
#define TEEWARS_NETVERSION 0xffffffff
//#define TEEWARS_NETVERSION_STRING "dev v2"
#define TEEWARS_VERSION "0.2.1-dev"
#define TEEWARS_VERSION "0.2.0-dev"

File diff suppressed because it is too large Load diff

View file

@ -33,7 +33,8 @@ void tilemap_render(float scale, int fg)
gfx_quads_begin();
int c = 0;
float frac = (1.0f/1024.0f); //2.0f;
float frac = (1.0f/1024.0f);//2.0f; //2.0f;
const float s = 1.0f;
for(int y = 0; y < tmap->height; y++)
for(int x = 0; x < tmap->width; x++, c++)
{
@ -41,13 +42,14 @@ void tilemap_render(float scale, int fg)
if(d)
{
gfx_quads_setsubset(
(d%16)/16.0f+frac,
(d/16)/16.0f+frac,
(d%16)/16.0f+1.0f/16.0f-frac,
(d/16)/16.0f+1.0f/16.0f-frac);
(d%16)/16.0f*s+frac,
(d/16)/16.0f*s+frac,
((d%16)/16.0f+1.0f/16.0f)*s-frac,
((d/16)/16.0f+1.0f/16.0f)*s-frac);
gfx_quads_drawTL(x*scale, y*scale, scale, scale);
}
}
gfx_quads_end();
}
}

View file

@ -374,13 +374,14 @@ void draw_single_part_button(void *id, const char *text, int checked, float x, f
else
tileset = tileset_regular;
draw_part((gui_parts_enum)(int)(int *)extra, tileset, x, y, w, h);
draw_part((gui_parts_enum)((char*)extra-(char*)0), tileset, x, y, w, h);
}
void draw_menu_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra)
{
gui_composite_box_enum box_style;
if ((int)extra)
if ((int)((char*)extra-(char*)0))
box_style = screen_info_box;
else
box_style = screen_list_box;
@ -401,7 +402,7 @@ void draw_teewars_button(void *id, const char *text, int checked, float x, float
else
tileset = tileset_regular;
if ((int)(int *)extra == 1)
if ((int)((char*)extra-(char*)0) == 1)
tileset = tileset_inactive;
draw_box(button_big_box, tileset, x, y, w, h);
@ -486,7 +487,7 @@ int ui_do_combo_box(void *id, float x, float y, float w, char *lines, int line_c
float line_height = 36.0f;
int inside = (ui_active_item() == id) ? ui_mouse_inside(x, y, w, line_count * line_height) : ui_mouse_inside(x, y, w, line_height);
int hover_index = (ui_mouse_y() - y) / line_height;
int hover_index = (int)((ui_mouse_y() - y) / line_height);
if (ui_active_item() == id)
{

View file

@ -1,16 +1,9 @@
#include <baselib/system.h>
#include <baselib/vmath.h>
#include <baselib/math.h>
#include <math.h>
#include "../engine/interface.h"
#include "mapres_col.h"
// Don't tweak :)
const float pi = 3.14159265358979f;
#define LERP(a,b,t) (a + (b-a) * t)
#define min(a, b) ( a > b ? b : a)
#define max(a, b) ( a > b ? a : b)
inline baselib::vec2 get_direction(int angle)
{
float a = angle/256.0f;
@ -21,7 +14,7 @@ inline float get_angle(baselib::vec2 dir)
{
float a = atan(dir.y/dir.x);
if(dir.x < 0)
a = a+pi;
a = a+baselib::pi;
return a;
}
@ -76,7 +69,8 @@ struct ev_sound
struct ev_healthmod
{
int x, y;
int x, y; // could perhaps be an client id
int vx, vy; // should be an angle instead
int amount;
};
@ -84,20 +78,19 @@ struct obj_projectile
{
int type;
int x, y;
int vx, vy;
int vx, vy; // should be an angle instead
};
struct obj_powerup
{
int type;
int subtype;
int x, y;
int vx, vy;
int type; // why do we need two types?
int subtype;
};
struct obj_player
{
int name[8];
//int name[8];
int local;
int clientid;
@ -113,12 +106,10 @@ struct obj_player
// current active weapon
int weapon;
// current active modifier
int modifier;
//int modifier;
// num attack ticks left of current attck
int attackticks;
int attacklen;
int visualtimeattack;
// num attack ticks left of current attack
int attacktick;
int score;
int emote;
@ -183,49 +174,3 @@ enum
ITEM_ARMOR_10=0x00030010,
ITEM_NINJA=0x00040001,
};
// sound categories and stuff
enum
{
SOUND_FIRE_GUN = 0,
SOUND_FIRE_SHOTGUN,
SOUND_FIRE_ROCKET,
SOUND_FIRE_MELEE,
SOUND_FIRE_NINJA,
// impacts with world
SOUND_IMPACT_PROJECTILE_GUN,
SOUND_IMPACT_PROJECTILE_SHOTGUN,
SOUND_IMPACT_PROJECTILE_ROCKET,
// chain ?
// Player movement
SOUND_PLAYER_JUMP,
SOUND_PLAYER_HURT_SHORT,
SOUND_PLAYER_HURT_LONG,
SOUND_PLAYER_SPAWN,
SOUND_PLAYER_CHAIN_LOOP,
SOUND_PLAYER_CHAIN_IMPACT,
SOUND_PLAYER_IMPACT,
SOUND_PLAYER_IMPACT_NINJA,
SOUND_PLAYER_DIE,
SOUND_PLAYER_SWITCHWEAPON,
SOUND_PLAYER_EQUIP,
SOUND_PLAYER_LAND,
SOUND_NUMSOUNDS,
// extra defs (for future?)
SOUND_EQUIP_GUN = SOUND_PLAYER_EQUIP,
SOUND_EQUIP_ROCKET = SOUND_PLAYER_EQUIP,
SOUND_EQUIP_SHOTGUN = SOUND_PLAYER_EQUIP,
SOUND_EQUIP_MELEE = SOUND_PLAYER_EQUIP,
SOUND_LOOPFLAG_STARTLOOP = 0x80000000,
SOUND_LOOPFLAG_STOPLOOP = 0x40000000,
SOUND_MASK = ~(SOUND_LOOPFLAG_STARTLOOP | SOUND_LOOPFLAG_STOPLOOP),
};

View file

@ -1,8 +1,7 @@
MACRO_CONFIG_INT(screen_width, 800, 0, 0)
MACRO_CONFIG_INT(screen_height, 600, 0, 0)
MACRO_CONFIG_STR(player_name, 32, "nameless tee")
MACRO_CONFIG_INT(key_move_left, 65, 32, 512)
MACRO_CONFIG_INT(key_move_right, 68, 32, 512)
MACRO_CONFIG_INT(key_jump, 32, 32, 512)
MACRO_CONFIG_INT(key_fire, 384, 32, 512)
MACRO_CONFIG_INT(key_hook, 385, 32, 512)
//MACRO_CONFIG_INT(key_fire, 'E', 32, 512)
//MACRO_CONFIG_INT(key_hook, 'Q', 32, 512)

View file

@ -1,8 +1,12 @@
#include <baselib/system.h>
#include <baselib/vmath.h>
#include <baselib/math.h>
#include "../engine/interface.h"
#include "mapres_col.h"
#include "mapres.h"
using namespace baselib;
/*
Simple collision rutines!
*/
@ -42,3 +46,24 @@ int col_check_point(int x, int y)
return col.data[ny*col.w+nx];
}
// TODO: rewrite this smarter!
bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out)
{
float d = distance(pos0, pos1);
for(float f = 0; f < d; f++)
{
float a = f/d;
vec2 pos = mix(pos0, pos1, a);
if(col_check_point((int)pos.x, (int)pos.y))
{
if(out)
*out = pos;
return true;
}
}
if(out)
*out = pos1;
return false;
}

View file

@ -1,3 +1,4 @@
#include <baselib/vmath.h>
struct mapres_collision
{
@ -8,3 +9,4 @@ struct mapres_collision
int col_init(int dividor);
int col_check_point(int x, int y);
bool col_intersect_line(baselib::vec2 pos0, baselib::vec2 pos1, baselib::vec2 *out);

File diff suppressed because it is too large Load diff