mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
large rewrite and code cleanup
This commit is contained in:
parent
7be0ae1b29
commit
125d04e51f
80
datasrc/client.dts
Normal file
80
datasrc/client.dts
Normal 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
5
datasrc/server.dts
Normal file
|
@ -0,0 +1,5 @@
|
|||
struct data_container {
|
||||
}
|
||||
|
||||
const array:int sound = sounds.*
|
||||
const array:int weapon = weapons.*
|
415
datasrc/teewars.ds
Normal file
415
datasrc/teewars.ds
Normal 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
24
datasrc/teewars.dsd
Normal 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 *
|
||||
}
|
||||
}
|
70
default.bam
70
default.bam
|
@ -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
598
scripts/compiler.py
Executable 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
126
src/crapnet/crapnet.cpp
Normal 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;
|
||||
}
|
|
@ -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(¤t);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -505,6 +406,9 @@ public:
|
|||
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++;
|
||||
snapshot *tmp = snapshots[SNAP_PREV];
|
||||
snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
|
||||
snapshots[SNAP_CURRENT] = tmp;
|
||||
current_tick = game_tick;
|
||||
|
||||
if(snapshot_part == num_parts)
|
||||
// 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)
|
||||
{
|
||||
snapshot *tmp = snapshots[SNAP_PREV];
|
||||
snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
|
||||
snapshots[SNAP_CURRENT] = tmp;
|
||||
|
||||
// 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;
|
||||
//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 && time_get() > reconnect_timer)
|
||||
if(get_state() == STATE_CONNECTING && net.state() == NETSTATE_ONLINE)
|
||||
{
|
||||
send_connect();
|
||||
reconnect_timer = time_get() + time_freq();
|
||||
// we switched to online
|
||||
dbg_msg("client", "connected, sending info");
|
||||
set_state(STATE_LOADING);
|
||||
send_info();
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
|
|
@ -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
12
src/engine/compression.h
Normal 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);
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
43
src/engine/msg.cpp
Normal 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
686
src/engine/network.cpp
Normal 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
104
src/engine/network.h
Normal 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
|
|
@ -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
84
src/engine/ringbuffer.h
Normal 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; }
|
||||
};
|
|
@ -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
|
||||
{
|
||||
|
@ -158,33 +42,20 @@ 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;
|
||||
// remove old snapshos
|
||||
// keep 1 seconds worth of snapshots
|
||||
clients[i].snapshots.purge_until(current_tick-SERVER_TICK_SPEED);
|
||||
|
||||
if(snapshot_size > biggest_snapshot)
|
||||
biggest_snapshot = snapshot_size;
|
||||
// save it the snapshot
|
||||
clients[i].snapshots.add(current_tick, snapshot_size, data);
|
||||
|
||||
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++)
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
// 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;
|
||||
|
||||
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);
|
||||
}
|
||||
// TODO: check for client timeouts
|
||||
}
|
||||
|
||||
char *write_int(char *buffer, int integer)
|
||||
{
|
||||
*buffer++ = integer >> 24;
|
||||
*buffer++ = integer >> 16;
|
||||
*buffer++ = integer >> 8;
|
||||
*buffer++ = integer;
|
||||
// check for new clients
|
||||
while(1)
|
||||
{
|
||||
int cid = net.newclient();
|
||||
if(cid == -1)
|
||||
break;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
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;
|
||||
|
||||
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));
|
||||
dbg_msg("server", "new client %d", cid);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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
235
src/engine/snapshot.cpp
Normal 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 ∅
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
|
@ -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; }
|
||||
};
|
||||
|
||||
char *data_start() { return (char *)&offsets[num_items]; }
|
||||
item *get_item(int index) { return (item *)(data_start() + offsets[index]); };
|
||||
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]); };
|
||||
|
||||
// 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
|
||||
|
|
|
@ -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
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue