From 878ede3080ab2cfb627aca505c397d9765052996 Mon Sep 17 00:00:00 2001 From: Magnus Auvinen Date: Tue, 27 Oct 2009 14:38:53 +0000 Subject: major update with stuff --- bam.lua | 303 ++++ default.bam | 311 ---- other/sdl/sdl.bam | 77 - other/sdl/sdl.lua | 77 + src/base/system.h | 3 +- src/base/tl/array.hpp | 6 +- src/engine/client/ec_client.c | 2061 ------------------------ src/engine/client/ec_client.cpp | 2087 +++++++++++++++++++++++++ src/engine/client/ec_gfx.c | 1020 ------------ src/engine/client/ec_gfx.cpp | 992 ++++++++++++ src/engine/client/ec_gfx_text.c | 668 -------- src/engine/client/ec_gfx_text.cpp | 669 ++++++++ src/engine/client/ec_inp.c | 230 --- src/engine/client/ec_inp.cpp | 234 +++ src/engine/client/ec_snd.c | 465 ------ src/engine/client/ec_snd.cpp | 471 ++++++ src/engine/client/ec_srvbrowse.c | 737 --------- src/engine/client/ec_srvbrowse.cpp | 736 +++++++++ src/engine/e_client_interface.h | 8 - src/engine/e_common_interface.h | 8 - src/engine/e_compression.c | 148 -- src/engine/e_compression.cpp | 86 + src/engine/e_compression.h | 4 - src/engine/e_config.c | 49 - src/engine/e_config.cpp | 49 + src/engine/e_config.h | 8 - src/engine/e_console.c | 464 ------ src/engine/e_console.cpp | 464 ++++++ src/engine/e_console.h | 14 +- src/engine/e_datafile.c | 677 -------- src/engine/e_datafile.cpp | 677 ++++++++ src/engine/e_demorec.c | 639 -------- src/engine/e_demorec.cpp | 640 ++++++++ src/engine/e_demorec.h | 8 - src/engine/e_engine.c | 596 ------- src/engine/e_engine.cpp | 596 +++++++ src/engine/e_huffman.c | 276 ---- src/engine/e_huffman.cpp | 276 ++++ src/engine/e_huffman.h | 8 - src/engine/e_if_other.h | 3 +- src/engine/e_jobs.c | 76 - src/engine/e_jobs.cpp | 76 + src/engine/e_keynames.c | 524 ------- src/engine/e_keynames.cpp | 524 +++++++ src/engine/e_linereader.c | 62 - src/engine/e_linereader.cpp | 62 + src/engine/e_map.c | 66 - src/engine/e_map.cpp | 66 + src/engine/e_memheap.c | 102 -- src/engine/e_memheap.cpp | 102 ++ src/engine/e_memheap.h | 2 +- src/engine/e_msg.c | 70 - src/engine/e_msg.cpp | 70 + src/engine/e_network.c | 351 ----- src/engine/e_network.cpp | 347 ++++ src/engine/e_network.h | 395 +++-- src/engine/e_network_client.c | 154 -- src/engine/e_network_client.cpp | 139 ++ src/engine/e_network_conn.c | 366 ----- src/engine/e_network_conn.cpp | 349 +++++ src/engine/e_network_internal.h | 156 -- src/engine/e_network_server.c | 484 ------ src/engine/e_network_server.cpp | 408 +++++ src/engine/e_packer.c | 213 --- src/engine/e_packer.cpp | 155 ++ src/engine/e_packer.h | 58 +- src/engine/e_ringbuffer.c | 362 ----- src/engine/e_ringbuffer.cpp | 194 +++ src/engine/e_ringbuffer.h | 67 +- src/engine/e_server_interface.h | 8 - src/engine/e_snapshot.c | 604 ------- src/engine/e_snapshot.cpp | 571 +++++++ src/engine/e_snapshot.h | 160 +- src/engine/server/es_register.c | 272 ---- src/engine/server/es_register.cpp | 271 ++++ src/engine/server/es_server.c | 1380 ---------------- src/engine/server/es_server.cpp | 1969 +++++++++++++++++++++++ src/game/client/clienthooks.cpp | 31 +- src/game/client/component.hpp | 8 + src/game/client/components/broadcast.cpp | 5 +- src/game/client/components/chat.cpp | 3 +- src/game/client/components/console.cpp | 97 +- src/game/client/components/console.hpp | 8 +- src/game/client/components/damageind.cpp | 15 +- src/game/client/components/debughud.cpp | 17 +- src/game/client/components/emoticon.cpp | 37 +- src/game/client/components/flow.cpp | 9 +- src/game/client/components/hud.cpp | 109 +- src/game/client/components/hud.hpp | 2 + src/game/client/components/items.cpp | 87 +- src/game/client/components/killmessages.cpp | 47 +- src/game/client/components/mapimages.cpp | 7 +- src/game/client/components/maplayers.cpp | 56 +- src/game/client/components/maplayers.hpp | 3 + src/game/client/components/menus.cpp | 724 ++++----- src/game/client/components/menus.hpp | 104 +- src/game/client/components/menus_browser.cpp | 338 ++-- src/game/client/components/menus_demo.cpp | 221 +-- src/game/client/components/menus_ingame.cpp | 194 +-- src/game/client/components/menus_settings.cpp | 460 +++--- src/game/client/components/motd.cpp | 17 +- src/game/client/components/particles.cpp | 20 +- src/game/client/components/players.cpp | 101 +- src/game/client/components/scoreboard.cpp | 67 +- src/game/client/components/skins.cpp | 12 +- src/game/client/components/voting.cpp | 24 +- src/game/client/components/voting.hpp | 8 +- src/game/client/gameclient.cpp | 41 +- src/game/client/gameclient.hpp | 20 +- src/game/client/render.cpp | 93 +- src/game/client/render.hpp | 51 +- src/game/client/render_map.cpp | 47 +- src/game/client/ui.cpp | 225 +-- src/game/client/ui.hpp | 117 +- src/game/editor/ed_editor.cpp | 1708 +++++++++++--------- src/game/editor/ed_editor.hpp | 155 +- src/game/editor/ed_io.cpp | 23 +- src/game/editor/ed_layer_game.cpp | 2 +- src/game/editor/ed_layer_quads.cpp | 45 +- src/game/editor/ed_layer_tiles.cpp | 60 +- src/game/editor/ed_popups.cpp | 235 +-- src/game/localization.cpp | 2 - src/game/server/hooks.cpp | 10 +- 123 files changed, 17245 insertions(+), 16500 deletions(-) create mode 100644 bam.lua delete mode 100644 default.bam delete mode 100644 other/sdl/sdl.bam create mode 100644 other/sdl/sdl.lua delete mode 100644 src/engine/client/ec_client.c create mode 100644 src/engine/client/ec_client.cpp delete mode 100644 src/engine/client/ec_gfx.c create mode 100644 src/engine/client/ec_gfx.cpp delete mode 100644 src/engine/client/ec_gfx_text.c create mode 100644 src/engine/client/ec_gfx_text.cpp delete mode 100644 src/engine/client/ec_inp.c create mode 100644 src/engine/client/ec_inp.cpp delete mode 100644 src/engine/client/ec_snd.c create mode 100644 src/engine/client/ec_snd.cpp delete mode 100644 src/engine/client/ec_srvbrowse.c create mode 100644 src/engine/client/ec_srvbrowse.cpp delete mode 100644 src/engine/e_compression.c create mode 100644 src/engine/e_compression.cpp delete mode 100644 src/engine/e_config.c create mode 100644 src/engine/e_config.cpp delete mode 100644 src/engine/e_console.c create mode 100644 src/engine/e_console.cpp delete mode 100644 src/engine/e_datafile.c create mode 100644 src/engine/e_datafile.cpp delete mode 100644 src/engine/e_demorec.c create mode 100644 src/engine/e_demorec.cpp delete mode 100644 src/engine/e_engine.c create mode 100644 src/engine/e_engine.cpp delete mode 100644 src/engine/e_huffman.c create mode 100644 src/engine/e_huffman.cpp delete mode 100644 src/engine/e_jobs.c create mode 100644 src/engine/e_jobs.cpp delete mode 100644 src/engine/e_keynames.c create mode 100644 src/engine/e_keynames.cpp delete mode 100644 src/engine/e_linereader.c create mode 100644 src/engine/e_linereader.cpp delete mode 100644 src/engine/e_map.c create mode 100644 src/engine/e_map.cpp delete mode 100644 src/engine/e_memheap.c create mode 100644 src/engine/e_memheap.cpp delete mode 100644 src/engine/e_msg.c create mode 100644 src/engine/e_msg.cpp delete mode 100644 src/engine/e_network.c create mode 100644 src/engine/e_network.cpp delete mode 100644 src/engine/e_network_client.c create mode 100644 src/engine/e_network_client.cpp delete mode 100644 src/engine/e_network_conn.c create mode 100644 src/engine/e_network_conn.cpp delete mode 100644 src/engine/e_network_internal.h delete mode 100644 src/engine/e_network_server.c create mode 100644 src/engine/e_network_server.cpp delete mode 100644 src/engine/e_packer.c create mode 100644 src/engine/e_packer.cpp delete mode 100644 src/engine/e_ringbuffer.c create mode 100644 src/engine/e_ringbuffer.cpp delete mode 100644 src/engine/e_snapshot.c create mode 100644 src/engine/e_snapshot.cpp delete mode 100644 src/engine/server/es_register.c create mode 100644 src/engine/server/es_register.cpp delete mode 100644 src/engine/server/es_server.c create mode 100644 src/engine/server/es_server.cpp diff --git a/bam.lua b/bam.lua new file mode 100644 index 00000000..c83fdab0 --- /dev/null +++ b/bam.lua @@ -0,0 +1,303 @@ +CheckVersion("0.3") + +Import("other/sdl/sdl.lua") + +--- Setup Config -------- +config = NewConfig() +config:Add(OptCCompiler("compiler")) +config:Add(OptTestCompileC("stackprotector", "int main(){return 0;}", "-fstack-protector -fstack-protector-all")) +config:Add(OptLibrary("zlib", "zlib.h", false)) +config:Add(SDL.OptFind("sdl", true)) +config:Finalize("config.bam") + +-- data compiler +function Script(name) + if family == "windows" then + return str_replace(name, "/", "\\") + end + return "python " .. name +end + +function CHash(output, ...) + local inputs = FlattenTable({...}) + + output = Path(output) + + -- compile all the files + local cmd = Script("scripts/cmd5.py") .. " " + for index, inname in ipairs(inputs) do + cmd = cmd .. Path(inname) .. " " + end + + cmd = cmd .. " > " .. output + + AddJob(output, "cmd5 " .. output, cmd) + for index, inname in ipairs(inputs) do + AddDependency(output, inname) + end + return output +end + +function ResCompile(scriptfile) + scriptfile = Path(scriptfile) + output = PathBase(scriptfile) .. ".res" + AddJob(output, "rc " .. scriptfile, "rc /fo " .. output .. " " .. scriptfile) + AddDependency(output, scriptfile) + return output +end + +function Dat2c(datafile, sourcefile, arrayname) + datafile = Path(datafile) + sourcefile = Path(sourcefile) + + AddJob( + sourcefile, + "dat2c " .. PathFilename(sourcefile) .. " = " .. PathFilename(datafile), + Script("scripts/dat2c.py") .. " " .. datafile .. " " .. arrayname .. " > " .. sourcefile + ) + AddDependency(sourcefile, datafile) + return sourcefile +end + +function ContentCompile(action, output) + output = Path(output) + AddJob( + output, + action .. " > " .. output, + Script("datasrc/compile.py") .. " " .. action .. " > " .. Path(output) + ) + AddDependency(output, Path("datasrc/content.py")) -- do this more proper + AddDependency(output, Path("datasrc/network.py")) + AddDependency(output, Path("datasrc/compile.py")) + AddDependency(output, Path("datasrc/datatypes.py")) + return output +end + +-- Content Compile +network_source = ContentCompile("network_source", "src/game/generated/g_protocol.cpp") +network_header = ContentCompile("network_header", "src/game/generated/g_protocol.hpp") +client_content_source = ContentCompile("client_content_source", "src/game/generated/gc_data.cpp") +client_content_header = ContentCompile("client_content_header", "src/game/generated/gc_data.hpp") +server_content_source = ContentCompile("server_content_source", "src/game/generated/gs_data.cpp") +server_content_header = ContentCompile("server_content_header", "src/game/generated/gs_data.hpp") + +AddDependency(network_source, network_header) +AddDependency(client_content_source, client_content_header) +AddDependency(server_content_source, server_content_header) + +nethash = CHash("src/game/generated/nethash.c", "src/engine/e_protocol.h", "src/game/generated/g_protocol.hpp", "src/game/tuning.hpp", "src/game/gamecore.cpp", network_header) + +client_link_other = {} +client_depends = {} + +if family == "windows" then + table.insert(client_depends, Copy(".", "other\\sdl\\vc2005libs\\SDL.dll")) +end + + +if config.compiler.value == "cl" then + client_link_other = {ResCompile("other/icons/teeworlds.rc")} +end + +function Intermediate_Output(settings, input) + return Path("objs/" .. PathBase(PathFilename(input)) .. settings.config_ext) +end + +function build(settings) + --settings.objdir = Path("objs") + settings.cc.Output = Intermediate_Output + + if config.compiler.value == "cl" then + settings.cc.flags:Add("/wd4244") + else + settings.cc.flags:Add("-Wall", "-fno-exceptions") + if platform == "macosx" then + settings.cc.flags:Add("-mmacosx-version-min=10.4", "-isysroot /Developer/SDKs/MacOSX10.4u.sdk") + settings.link.flags:Add("-mmacosx-version-min=10.4", "-isysroot /Developer/SDKs/MacOSX10.4u.sdk") + elseif config.stackprotector.value == 1 then + settings.cc.flags:Add("-fstack-protector", "-fstack-protector-all") + settings.link.flags:Add("-fstack-protector", "-fstack-protector-all") + end + end + + -- set some platform specific settings + settings.cc.includes:Add("src") + + if family == "unix" then + if platform == "macosx" then + settings.link.frameworks:Add("Carbon") + settings.link.frameworks:Add("AppKit") + else + settings.link.libs:Add("pthread") + end + elseif family == "windows" then + settings.link.libs:Add("gdi32") + settings.link.libs:Add("user32") + settings.link.libs:Add("ws2_32") + settings.link.libs:Add("ole32") + settings.link.libs:Add("shell32") + end + + -- compile zlib if needed + if config.zlib.value == 1 then + settings.link.libs:Add("z") + if config.zlib.include_path then + settings.cc.includes:Add(config.zlib.include_path) + end + zlib = {} + else + zlib = Compile(settings, Collect("src/engine/external/zlib/*.c")) + settings.cc.includes:Add("src/engine/external/zlib") + end + + -- build the small libraries + wavpack = Compile(settings, Collect("src/engine/external/wavpack/*.c")) + pnglite = Compile(settings, Collect("src/engine/external/pnglite/*.c")) + + -- build game components + engine_settings = settings:Copy() + server_settings = engine_settings:Copy() + client_settings = engine_settings:Copy() + launcher_settings = engine_settings:Copy() + + if family == "unix" then + if platform == "macosx" then + client_settings.link.frameworks:Add("OpenGL") + client_settings.link.frameworks:Add("AGL") + client_settings.link.frameworks:Add("Carbon") + client_settings.link.frameworks:Add("Cocoa") + launcher_settings.link.frameworks:Add("Cocoa") + else + client_settings.link.libs:Add("X11") + client_settings.link.libs:Add("GL") + client_settings.link.libs:Add("GLU") + end + + client_settings.cc.flags:Add("`freetype-config --cflags`") + client_settings.link.flags:Add("`freetype-config --libs`") + elseif family == "windows" then + client_settings.link.libs:Add("opengl32") + client_settings.link.libs:Add("glu32") + client_settings.link.libs:Add("winmm") + end + + -- apply sdl settings + config.sdl:Apply(client_settings) + + engine = Compile(engine_settings, Collect("src/engine/*.cpp", "src/base/*.c")) + client = Compile(client_settings, Collect("src/engine/client/*.cpp")) + server = Compile(server_settings, Collect("src/engine/server/*.cpp")) + + versionserver = Compile(settings, Collect("src/versionsrv/*.cpp")) + masterserver = Compile(settings, Collect("src/mastersrv/*.cpp")) + game_shared = Compile(settings, Collect("src/game/*.cpp"), nethash, network_source) + game_client = Compile(settings, CollectRecursive("src/game/client/*.cpp"), client_content_source) + game_server = Compile(settings, CollectRecursive("src/game/server/*.cpp"), server_content_source) + game_editor = Compile(settings, Collect("src/game/editor/*.cpp")) + + -- build tools (TODO: fix this so we don't get double _d_d stuff) + tools_src = Collect("src/tools/*.cpp", "src/tools/*.c") + + client_osxlaunch = {} + server_osxlaunch = {} + if platform == "macosx" then + client_osxlaunch = Compile(client_settings, "src/osxlaunch/client.m") + server_osxlaunch = Compile(launcher_settings, "src/osxlaunch/server.m") + end + + tools = {} + for i,v in ipairs(tools_src) do + toolname = PathFilename(PathBase(v)) + tools[i] = Link(settings, toolname, Compile(settings, v), engine, zlib) + end + + -- build client, server, version server and master server + client_exe = Link(client_settings, "teeworlds", game_shared, game_client, + engine, client, game_editor, zlib, pnglite, wavpack, + client_link_other, client_osxlaunch) + + server_exe = Link(server_settings, "teeworlds_srv", engine, server, + game_shared, game_server, zlib) + + serverlaunch = {} + if platform == "macosx" then + serverlaunch = Link(launcher_settings, "serverlaunch", server_osxlaunch) + end + + versionserver_exe = Link(server_settings, "versionsrv", versionserver, + engine, zlib) + + masterserver_exe = Link(server_settings, "mastersrv", masterserver, + engine, zlib) + + -- make targets + c = PseudoTarget("client".."_"..settings.config_name, client_exe, client_depends) + s = PseudoTarget("server".."_"..settings.config_name, server_exe, serverlaunch) + g = PseudoTarget("game".."_"..settings.config_name, client_exe, server_exe) + + v = PseudoTarget("versionserver".."_"..settings.config_name, versionserver_exe) + m = PseudoTarget("masterserver".."_"..settings.config_name, masterserver_exe) + t = PseudoTarget("tools".."_"..settings.config_name, tools) + + all = PseudoTarget(settings.config_name, c, s, v, m, t) + return all +end + + +debug_settings = NewSettings() +debug_settings.config_name = "debug" +debug_settings.config_ext = "_d" +debug_settings.debug = 1 +debug_settings.cc.optimize = 0 +debug_settings.cc.defines:Add("CONF_DEBUG") + +release_settings = NewSettings() +release_settings.config_name = "release" +release_settings.config_ext = "" +release_settings.debug = 0 +release_settings.cc.optimize = 1 +release_settings.cc.defines:Add("CONF_RELEASE") + +if platform == "macosx" and arch == "ia32" then + debug_settings_ppc = debug_settings:Copy() + debug_settings_ppc.config_name = "debug_ppc" + debug_settings_ppc.config_ext = "_ppc_d" + debug_settings_ppc.cc.flags:Add("-arch ppc") + debug_settings_ppc.link.flags:Add("-arch ppc") + debug_settings_ppc.cc.defines:Add("CONF_DEBUG") + + release_settings_ppc = release_settings:Copy() + release_settings_ppc.config_name = "release_ppc" + release_settings_ppc.config_ext = "_ppc" + release_settings_ppc.cc.flags:Add("-arch ppc") + release_settings_ppc.link.flags:Add("-arch ppc") + release_settings_ppc.cc.defines:Add("CONF_RELEASE") + + debug_settings_x86 = debug_settings:Copy() + debug_settings_x86.config_name = "debug_x86" + debug_settings_x86.config_ext = "_x86_d" + debug_settings_x86.cc.defines:Add("CONF_DEBUG") + + release_settings_x86 = release_settings:Copy() + release_settings_x86.config_name = "release_x86" + release_settings_x86.config_ext = "_x86" + release_settings_x86.cc.defines:Add("CONF_RELEASE") + + ppc_d = build(debug_settings_ppc) + x86_d = build(debug_settings_x86) + ppc_r = build(release_settings_ppc) + x86_r = build(release_settings_x86) + DefaultTarget("game_debug_x86") + PseudoTarget("release", ppc_r, x86_r) + PseudoTarget("debug", ppc_d, x86_d) + + PseudoTarget("server_release", "server_release_x86", "server_release_ppc") + PseudoTarget("server_debug", "server_debug_x86", "server_debug_ppc") + PseudoTarget("client_release", "client_release_x86", "client_release_ppc") + PseudoTarget("client_debug", "client_debug_x86", "client_debug_ppc") +else + build(debug_settings) + build(release_settings) + DefaultTarget("game_debug") +end + diff --git a/default.bam b/default.bam deleted file mode 100644 index 8f735dff..00000000 --- a/default.bam +++ /dev/null @@ -1,311 +0,0 @@ -CheckVersion("0.2") - -Import("other/sdl/sdl.bam") - ---- Setup Config -------- -config = NewConfig() -config:Add(OptFindCompiler()) -config:Add(OptTestCompileC("stackprotector", "int main(){return 0;}", "-fstack-protector -fstack-protector-all")) -config:Add(OptFindLibrary("zlib", "zlib.h", false)) -config:Add(SDL.OptFind("sdl", true)) -config:Finalize("config.bam") - --- data compiler -function Script(name) - if family == "windows" then - return str_replace(name, "/", "\\") - end - return "python " .. name -end - -function CHash(output, ...) - local inputs = FlatternTable({...}) - - output = Path(output) - - -- compile all the files - local cmd = Script("scripts/cmd5.py") .. " " - for index, inname in ipairs(inputs) do - cmd = cmd .. Path(inname) .. " " - end - - cmd = cmd .. " > " .. output - - AddJob(output, "cmd5 " .. output, cmd) - for index, inname in ipairs(inputs) do - AddDependency(output, inname) - end - return output -end - -function ResCompile(scriptfile) - scriptfile = Path(scriptfile) - output = PathBase(scriptfile) .. ".res" - AddJob(output, "rc " .. scriptfile, "rc /fo " .. output .. " " .. scriptfile) - AddDependency(output, scriptfile) - return output -end - -function Dat2c(datafile, sourcefile, arrayname) - datafile = Path(datafile) - sourcefile = Path(sourcefile) - - AddJob( - sourcefile, - "dat2c " .. PathFilename(sourcefile) .. " = " .. PathFilename(datafile), - Script("scripts/dat2c.py") .. " " .. datafile .. " " .. arrayname .. " > " .. sourcefile - ) - AddDependency(sourcefile, datafile) - return sourcefile -end - -function ContentCompile(action, output) - output = Path(output) - AddJob( - output, - action .. " > " .. output, - Script("datasrc/compile.py") .. " " .. action .. " > " .. Path(output) - ) - AddDependency(output, Path("datasrc/content.py")) -- do this more proper - AddDependency(output, Path("datasrc/network.py")) - AddDependency(output, Path("datasrc/compile.py")) - AddDependency(output, Path("datasrc/datatypes.py")) - return output -end - --- Content Compile -network_source = ContentCompile("network_source", "src/game/generated/g_protocol.cpp") -network_header = ContentCompile("network_header", "src/game/generated/g_protocol.hpp") -client_content_source = ContentCompile("client_content_source", "src/game/generated/gc_data.cpp") -client_content_header = ContentCompile("client_content_header", "src/game/generated/gc_data.hpp") -server_content_source = ContentCompile("server_content_source", "src/game/generated/gs_data.cpp") -server_content_header = ContentCompile("server_content_header", "src/game/generated/gs_data.hpp") - -AddDependency(network_source, network_header) -AddDependency(client_content_source, client_content_header) -AddDependency(server_content_source, server_content_header) - -nethash = CHash("src/game/generated/nethash.c", "src/engine/e_protocol.h", "src/game/generated/g_protocol.hpp", "src/game/tuning.hpp", "src/game/gamecore.cpp", network_header) - -client_link_other = {} -client_depends = {} - -if family == "windows" then - table.insert(client_depends, Copy(".", "other\\sdl\\vc2005libs\\SDL.dll")) -end - - -if config.compiler.value == "cl" then - client_link_other = {ResCompile("other/icons/teeworlds.rc")} -end - -function Intermediate_Output(settings, input) - return Path("objs/" .. PathBase(PathFilename(input)) .. settings.config_ext) -end - -function build(settings) - --settings.objdir = Path("objs") - settings.cc.Output = Intermediate_Output - - if config.compiler.value == "cl" then - settings.cc.flags:Add("/wd4244") - else - settings.cc.flags:Add("-Wall", "-fno-exceptions") - if platform == "macosx" then - settings.cc.flags:Add("-mmacosx-version-min=10.4", "-isysroot /Developer/SDKs/MacOSX10.4u.sdk") - settings.link.flags:Add("-mmacosx-version-min=10.4", "-isysroot /Developer/SDKs/MacOSX10.4u.sdk") - elseif config.stackprotector.value == 1 then - settings.cc.flags:Add("-fstack-protector", "-fstack-protector-all") - settings.link.flags:Add("-fstack-protector", "-fstack-protector-all") - end - end - - -- set some platform specific settings - settings.cc.includes:Add("src") - - if family == "unix" then - if platform == "macosx" then - settings.link.frameworks:Add("Carbon") - settings.link.frameworks:Add("AppKit") - else - settings.link.libs:Add("pthread") - end - elseif family == "windows" then - settings.link.libs:Add("gdi32") - settings.link.libs:Add("user32") - settings.link.libs:Add("ws2_32") - settings.link.libs:Add("ole32") - settings.link.libs:Add("shell32") - end - - -- compile zlib if needed - if config.zlib.value == 1 then - settings.link.libs:Add("z") - if config.zlib.include_path then - settings.cc.includes:Add(config.zlib.include_path) - end - zlib = {} - else - zlib = Compile(settings, Collect("src/engine/external/zlib/*.c")) - settings.cc.includes:Add("src/engine/external/zlib") - end - - -- build the small libraries - wavpack = Compile(settings, Collect("src/engine/external/wavpack/*.c")) - pnglite = Compile(settings, Collect("src/engine/external/pnglite/*.c")) - - -- build game components - engine_settings = settings:Copy() - server_settings = engine_settings:Copy() - client_settings = engine_settings:Copy() - launcher_settings = engine_settings:Copy() - - if family == "unix" then - if platform == "macosx" then - client_settings.link.frameworks:Add("OpenGL") - client_settings.link.frameworks:Add("AGL") - client_settings.link.frameworks:Add("Carbon") - client_settings.link.frameworks:Add("Cocoa") - launcher_settings.link.frameworks:Add("Cocoa") - else - client_settings.link.libs:Add("X11") - client_settings.link.libs:Add("GL") - client_settings.link.libs:Add("GLU") - end - - client_settings.cc.flags:Add("`freetype-config --cflags`") - client_settings.link.flags:Add("`freetype-config --libs`") - elseif family == "windows" then - client_settings.link.libs:Add("opengl32") - client_settings.link.libs:Add("glu32") - client_settings.link.libs:Add("winmm") - end - - -- apply sdl settings - config.sdl:Apply(client_settings) - - engine = Compile(engine_settings, Collect("src/engine/*.c", "src/base/*.c")) - client = Compile(client_settings, Collect("src/engine/client/*.c")) - server = Compile(server_settings, Collect("src/engine/server/*.c")) - - versionserver = Compile(settings, Collect("src/versionsrv/*.cpp")) - masterserver = Compile(settings, Collect("src/mastersrv/*.cpp")) - game_shared = Compile(settings, Collect("src/game/*.cpp"), nethash, network_source) - game_client = Compile(settings, CollectRecursive("src/game/client/*.cpp"), client_content_source) - game_server = Compile(settings, CollectRecursive("src/game/server/*.cpp"), server_content_source) - game_editor = Compile(settings, Collect("src/game/editor/*.cpp")) - - -- build tools (TODO: fix this so we don't get double _d_d stuff) - tools_src = Collect("src/tools/*.cpp", "src/tools/*.c") - - client_osxlaunch = {} - server_osxlaunch = {} - if platform == "macosx" then - client_osxlaunch = Compile(client_settings, "src/osxlaunch/client.m") - server_osxlaunch = Compile(launcher_settings, "src/osxlaunch/server.m") - end - - tools = {} - for i,v in ipairs(tools_src) do - toolname = PathFilename(PathBase(v)) - tools[i] = Link(settings, toolname, Compile(settings, v), engine, zlib) - end - - -- build client, server, version server and master server - client_exe = Link(client_settings, "teeworlds", game_shared, game_client, - engine, client, game_editor, zlib, pnglite, wavpack, - client_link_other, client_osxlaunch) - - server_exe = Link(server_settings, "teeworlds_srv", engine, server, - game_shared, game_server, zlib) - - serverlaunch = {} - if platform == "macosx" then - serverlaunch = Link(launcher_settings, "serverlaunch", server_osxlaunch) - end - - versionserver_exe = Link(server_settings, "versionsrv", versionserver, - engine, zlib) - - masterserver_exe = Link(server_settings, "mastersrv", masterserver, - engine, zlib) - - -- make targets - c = PseudoTarget("client".."_"..settings.config_name, client_exe, client_depends) - s = PseudoTarget("server".."_"..settings.config_name, server_exe, serverlaunch) - g = PseudoTarget("game".."_"..settings.config_name, client_exe, server_exe) - - v = PseudoTarget("versionserver".."_"..settings.config_name, versionserver_exe) - m = PseudoTarget("masterserver".."_"..settings.config_name, masterserver_exe) - t = PseudoTarget("tools".."_"..settings.config_name, tools) - - Target(c) - Target(s) - Target(v) - Target(m) - Target(t) - - all = PseudoTarget(settings.config_name, c, s, v, m, t) - - Target(all) - return all -end - - -debug_settings = NewSettings() -debug_settings.config_name = "debug" -debug_settings.config_ext = "_d" -debug_settings.debug = 1 -debug_settings.cc.optimize = 0 -debug_settings.cc.defines:Add("CONF_DEBUG") - -release_settings = NewSettings() -release_settings.config_name = "release" -release_settings.config_ext = "" -release_settings.debug = 0 -release_settings.cc.optimize = 1 -release_settings.cc.defines:Add("CONF_RELEASE") - -if platform == "macosx" and arch == "ia32" then - debug_settings_ppc = debug_settings:Copy() - debug_settings_ppc.config_name = "debug_ppc" - debug_settings_ppc.config_ext = "_ppc_d" - debug_settings_ppc.cc.flags:Add("-arch ppc") - debug_settings_ppc.link.flags:Add("-arch ppc") - debug_settings_ppc.cc.defines:Add("CONF_DEBUG") - - release_settings_ppc = release_settings:Copy() - release_settings_ppc.config_name = "release_ppc" - release_settings_ppc.config_ext = "_ppc" - release_settings_ppc.cc.flags:Add("-arch ppc") - release_settings_ppc.link.flags:Add("-arch ppc") - release_settings_ppc.cc.defines:Add("CONF_RELEASE") - - debug_settings_x86 = debug_settings:Copy() - debug_settings_x86.config_name = "debug_x86" - debug_settings_x86.config_ext = "_x86_d" - debug_settings_x86.cc.defines:Add("CONF_DEBUG") - - release_settings_x86 = release_settings:Copy() - release_settings_x86.config_name = "release_x86" - release_settings_x86.config_ext = "_x86" - release_settings_x86.cc.defines:Add("CONF_RELEASE") - - ppc_d = build(debug_settings_ppc) - x86_d = build(debug_settings_x86) - ppc_r = build(release_settings_ppc) - x86_r = build(release_settings_x86) - DefaultTarget("game_debug_x86") - PseudoTarget("release", ppc_r, x86_r) - PseudoTarget("debug", ppc_d, x86_d) - - PseudoTarget("server_release", "server_release_x86", "server_release_ppc") - PseudoTarget("server_debug", "server_debug_x86", "server_debug_ppc") - PseudoTarget("client_release", "client_release_x86", "client_release_ppc") - PseudoTarget("client_debug", "client_debug_x86", "client_debug_ppc") -else - build(debug_settings) - build(release_settings) - DefaultTarget("game_debug") -end - diff --git a/other/sdl/sdl.bam b/other/sdl/sdl.bam deleted file mode 100644 index be94209d..00000000 --- a/other/sdl/sdl.bam +++ /dev/null @@ -1,77 +0,0 @@ -SDL = { - basepath = PathPath(ModuleFilename()), - - OptFind = function (name, required) - local check = function(option, settings) - option.value = nil - option.use_sdlconfig = nil - option.use_win32sdl = nil - option.use_osxframework = nil - option.lib_path = nil - - if ExecuteSilent("sdl-config") > 0 and ExecuteSilent("sdl-config --cflags") == 0 then - option.value = 1 - option.use_sdlconfig = 1 - end - - if platform == "win32" then - option.value = 1 - option.use_win32sdl = 1 - end - - if platform == "macosx" then - option.value = 1 - option.use_osxframework = 1 - option.use_sdlconfig = nil - end - end - - local apply = function(option, settings) - if option.use_sdlconfig then - settings.cc.flags:Add("`sdl-config --cflags`") - settings.link.flags:Add("`sdl-config --libs`") - end - - if option.use_osxframework then - client_settings.link.frameworks:Add("SDL") - client_settings.cc.includes:Add("/Library/Frameworks/SDL.framework/Headers") - end - - if option.use_win32sdl then - settings.cc.includes:Add(SDL.basepath .. "/include") - settings.link.libpath:Add(SDL.basepath .. "/vc2005libs") - settings.link.libs:Add("SDL") - settings.link.libs:Add("SDLmain") - end - end - - local save = function(option, output) - output:option(option, "value") - output:option(option, "use_sdlconfig") - output:option(option, "use_win32sdl") - output:option(option, "use_osxframework") - end - - local display = function(option) - if option.value then - if option.use_sdlconfig then return "using sdl-config" end - if option.use_win32sdl then return "using supplied win32 libraries" end - if option.use_osxframework then return "using osx framework" end - return "using unknown method" - else - if option.required then - return "not found (required)" - else - return "not found (optional)" - end - end - end - - local o = MakeOption(name, 0, check, save, display) - o.Apply = apply - o.include_path = nil - o.lib_path = nil - o.required = required - return o - end -} diff --git a/other/sdl/sdl.lua b/other/sdl/sdl.lua new file mode 100644 index 00000000..be94209d --- /dev/null +++ b/other/sdl/sdl.lua @@ -0,0 +1,77 @@ +SDL = { + basepath = PathPath(ModuleFilename()), + + OptFind = function (name, required) + local check = function(option, settings) + option.value = nil + option.use_sdlconfig = nil + option.use_win32sdl = nil + option.use_osxframework = nil + option.lib_path = nil + + if ExecuteSilent("sdl-config") > 0 and ExecuteSilent("sdl-config --cflags") == 0 then + option.value = 1 + option.use_sdlconfig = 1 + end + + if platform == "win32" then + option.value = 1 + option.use_win32sdl = 1 + end + + if platform == "macosx" then + option.value = 1 + option.use_osxframework = 1 + option.use_sdlconfig = nil + end + end + + local apply = function(option, settings) + if option.use_sdlconfig then + settings.cc.flags:Add("`sdl-config --cflags`") + settings.link.flags:Add("`sdl-config --libs`") + end + + if option.use_osxframework then + client_settings.link.frameworks:Add("SDL") + client_settings.cc.includes:Add("/Library/Frameworks/SDL.framework/Headers") + end + + if option.use_win32sdl then + settings.cc.includes:Add(SDL.basepath .. "/include") + settings.link.libpath:Add(SDL.basepath .. "/vc2005libs") + settings.link.libs:Add("SDL") + settings.link.libs:Add("SDLmain") + end + end + + local save = function(option, output) + output:option(option, "value") + output:option(option, "use_sdlconfig") + output:option(option, "use_win32sdl") + output:option(option, "use_osxframework") + end + + local display = function(option) + if option.value then + if option.use_sdlconfig then return "using sdl-config" end + if option.use_win32sdl then return "using supplied win32 libraries" end + if option.use_osxframework then return "using osx framework" end + return "using unknown method" + else + if option.required then + return "not found (required)" + else + return "not found (optional)" + end + end + end + + local o = MakeOption(name, 0, check, save, display) + o.Apply = apply + o.include_path = nil + o.lib_path = nil + o.required = required + return o + end +} diff --git a/src/base/system.h b/src/base/system.h index cdbfe294..3c9974d8 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -46,8 +46,7 @@ void dbg_break(); /* Function: dbg_msg - - Prints a debug message. + Prints a debug message. Parameters: sys - A string that describes what system the message belongs to diff --git a/src/base/tl/array.hpp b/src/base/tl/array.hpp index 7fa4ab1b..7b8698d4 100644 --- a/src/base/tl/array.hpp +++ b/src/base/tl/array.hpp @@ -165,7 +165,7 @@ public: int add(const T& item) { incsize(); - set_size(size()+1); + num_elements = size()+1; list[num_elements-1] = item; return num_elements-1; } @@ -189,7 +189,7 @@ public: int index = (int)(&r.front()-list); incsize(); - set_size(size()+1); + num_elements = size()+1; for(int i = num_elements-1; i > index; i--) list[i] = list[i-1]; @@ -283,7 +283,7 @@ public: */ int memusage() { - return sizeof(array) + sizeof(T)*size; + return sizeof(*this) + sizeof(T)*list_size; } /* diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c deleted file mode 100644 index 0c707ccf..00000000 --- a/src/engine/client/ec_client.c +++ /dev/null @@ -1,2061 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include - -const int prediction_margin = 1000/50/2; /* magic network prediction value */ - -/* - Server Time - Client Mirror Time - Client Predicted Time - - Snapshot Latency - Downstream latency - - Prediction Latency - Upstream latency -*/ - -/* network client, must be accessible from other parts like the server browser */ -NETCLIENT *net; - -/* TODO: ugly, fix me */ -extern void client_serverbrowse_set(NETADDR *addr, int request, int token, SERVER_INFO *info); -extern void client_serverbrowse_save(); - -static unsigned snapshot_parts; -static int64 local_start_time; - -static int debug_font; -static float frametime = 0.0001f; -static float frametime_low = 1.0f; -static float frametime_high = 0.0f; -static int frames = 0; -static NETADDR server_address; -static int window_must_refocus = 0; -static int snapcrcerrors = 0; - -static int ack_game_tick = -1; -static int current_recv_tick = 0; -static int rcon_authed = 0; - -/* version-checking */ -static char versionstr[10] = "0"; - -/* pinging */ -static int64 ping_start_time = 0; - -/* */ -static char current_map[256] = {0}; -static int current_map_crc = 0; - -/* */ -static char cmd_connect[256] = {0}; - -/* map download */ -static char mapdownload_filename[256] = {0}; -static char mapdownload_name[256] = {0}; -static IOHANDLE mapdownload_file = 0; -static int mapdownload_chunk = 0; -static int mapdownload_crc = 0; -static int mapdownload_amount = -1; -static int mapdownload_totalsize = -1; - -/* */ -static SERVER_INFO current_server_info = {0}; -static int64 current_server_info_requesttime = -1; /* >= 0 should request, == -1 got info */ - -/* current time */ -static int current_tick = 0; -static float intratick = 0; -static float ticktime = 0; -static int prev_tick = 0; - -/* */ -/*static int predictiontime_pingspikecounter = 0; -static int gametime_pingspikecounter = 0;*/ - -/* predicted time */ -static int current_predtick = 0; -static float predintratick = 0; -static int last_input_timeleft = 0; - -static struct /* TODO: handle input better */ -{ - int data[MAX_INPUT_SIZE]; /* the input data */ - int tick; /* the tick that the input is for */ - int64 predicted_time; /* prediction latency when we sent this input */ - int64 time; -} inputs[200]; -static int current_input = 0; - -enum -{ - GRAPH_MAX=128 -}; - -typedef struct -{ - float min, max; - float values[GRAPH_MAX]; - float colors[GRAPH_MAX][3]; - int index; -} GRAPH; - -static void graph_init(GRAPH *g, float min, float max) -{ - g->min = min; - g->max = max; - g->index = 0; -} - -static void graph_scale_max(GRAPH *g) -{ - int i = 0; - g->max = 0; - for(i = 0; i < GRAPH_MAX; i++) - { - if(g->values[i] > g->max) - g->max = g->values[i]; - } -} - -static void graph_scale_min(GRAPH *g) -{ - int i = 0; - g->min = g->max; - for(i = 0; i < GRAPH_MAX; i++) - { - if(g->values[i] < g->min) - g->min = g->values[i]; - } -} - -static void graph_add(GRAPH *graph, float v, float r, float g, float b) -{ - graph->index = (graph->index+1)&(GRAPH_MAX-1); - graph->values[graph->index] = v; - graph->colors[graph->index][0] = r; - graph->colors[graph->index][1] = g; - graph->colors[graph->index][2] = b; -} - -static void graph_render(GRAPH *g, float x, float y, float w, float h, const char *description) -{ - char buf[32]; - int i; - - gfx_blend_normal(); - - - gfx_texture_set(-1); - - gfx_quads_begin(); - gfx_setcolor(0, 0, 0, 0.75f); - gfx_quads_drawTL(x, y, w, h); - gfx_quads_end(); - - gfx_lines_begin(); - gfx_setcolor(0.95f, 0.95f, 0.95f, 1.00f); - gfx_lines_draw(x, y+h/2, x+w, y+h/2); - gfx_setcolor(0.5f, 0.5f, 0.5f, 0.75f); - gfx_lines_draw(x, y+(h*3)/4, x+w, y+(h*3)/4); - gfx_lines_draw(x, y+h/4, x+w, y+h/4); - for(i = 1; i < GRAPH_MAX; i++) - { - float a0 = (i-1)/(float)GRAPH_MAX; - float a1 = i/(float)GRAPH_MAX; - int i0 = (g->index+i-1)&(GRAPH_MAX-1); - int i1 = (g->index+i)&(GRAPH_MAX-1); - - float v0 = (g->values[i0]-g->min) / (g->max-g->min); - float v1 = (g->values[i1]-g->min) / (g->max-g->min); - - gfx_setcolorvertex(0, g->colors[i0][0], g->colors[i0][1], g->colors[i0][2], 0.75f); - gfx_setcolorvertex(1, g->colors[i1][0], g->colors[i1][1], g->colors[i1][2], 0.75f); - gfx_lines_draw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h); - - } - gfx_lines_end(); - - - gfx_texture_set(debug_font); - gfx_quads_text(x+2, y+h-16, 16, 1,1,1,1, description); - - str_format(buf, sizeof(buf), "%.2f", g->max); - gfx_quads_text(x+w-8*strlen(buf)-8, y+2, 16, 1,1,1,1, buf); - - str_format(buf, sizeof(buf), "%.2f", g->min); - gfx_quads_text(x+w-8*strlen(buf)-8, y+h-16, 16, 1,1,1,1, buf); - -} - -typedef struct -{ - int64 snap; - int64 current; - int64 target; - - int64 rlast; - int64 tlast; - GRAPH graph; - - int spikecounter; - - float adjustspeed[2]; /* 0 = down, 1 = up */ -} SMOOTHTIME; - -static void st_init(SMOOTHTIME *st, int64 target) -{ - st->snap = time_get(); - st->current = target; - st->target = target; - st->adjustspeed[0] = 0.3f; - st->adjustspeed[1] = 0.3f; - graph_init(&st->graph, 0.0f, 0.5f); -} - -static int64 st_get(SMOOTHTIME *st, int64 now) -{ - float adjust_speed, a; - int64 c = st->current + (now - st->snap); - int64 t = st->target + (now - st->snap); - int64 r; - - /* it's faster to adjust upward instead of downward */ - /* we might need to adjust these abit */ - - adjust_speed = st->adjustspeed[0]; - if(t > c) - adjust_speed = st->adjustspeed[1]; - - a = ((now-st->snap)/(float)time_freq()) * adjust_speed; - if(a > 1.0f) - a = 1.0f; - - r = c + (int64)((t-c)*a); - - graph_add(&st->graph, a+0.5f,1,1,1); - - return r; -} - -static void st_update_int(SMOOTHTIME *st, int64 target) -{ - int64 now = time_get(); - st->current = st_get(st, now); - st->snap = now; - st->target = target; -} - -static void st_update(SMOOTHTIME *st, GRAPH *graph, int64 target, int time_left, int adjust_direction) -{ - int update_timer = 1; - - if(time_left < 0) - { - int is_spike = 0; - if(time_left < -50) - { - is_spike = 1; - - st->spikecounter += 5; - if(st->spikecounter > 50) - st->spikecounter = 50; - } - - if(is_spike && st->spikecounter < 15) - { - /* ignore this ping spike */ - update_timer = 0; - graph_add(graph, time_left, 1,1,0); - } - else - { - graph_add(graph, time_left, 1,0,0); - if(st->adjustspeed[adjust_direction] < 30.0f) - st->adjustspeed[adjust_direction] *= 2.0f; - } - } - else - { - if(st->spikecounter) - st->spikecounter--; - - graph_add(graph, time_left, 0,1,0); - - st->adjustspeed[adjust_direction] *= 0.95f; - if(st->adjustspeed[adjust_direction] < 2.0f) - st->adjustspeed[adjust_direction] = 2.0f; - } - - last_input_timeleft = time_left; - - if(update_timer) - st_update_int(st, target); -} - -static SMOOTHTIME game_time; -static SMOOTHTIME predicted_time; - -/* graphs */ -static GRAPH inputtime_margin_graph; -static GRAPH gametime_margin_graph; -static GRAPH fps_graph; - -/* -- snapshot handling --- */ -enum -{ - NUM_SNAPSHOT_TYPES=2 -}; - -/* the game snapshots are modifiable by the game */ -SNAPSTORAGE snapshot_storage; -static SNAPSTORAGE_HOLDER *snapshots[NUM_SNAPSHOT_TYPES] = {0, 0}; - -static int recived_snapshots = 0; -static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE]; - -static SNAPSTORAGE_HOLDER demorec_snapshotholders[NUM_SNAPSHOT_TYPES]; -static char *demorec_snapshotdata[NUM_SNAPSHOT_TYPES][2][MAX_SNAPSHOT_SIZE]; - -/* --- */ - -void *snap_get_item(int snapid, int index, SNAP_ITEM *item) -{ - SNAPSHOT_ITEM *i; - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - i = snapshot_get_item(snapshots[snapid]->alt_snap, index); - item->datasize = snapshot_get_item_datasize(snapshots[snapid]->alt_snap, index); - item->type = snapitem_type(i); - item->id = snapitem_id(i); - return (void *)snapitem_data(i); -} - -void snap_invalidate_item(int snapid, int index) -{ - SNAPSHOT_ITEM *i; - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - i = snapshot_get_item(snapshots[snapid]->alt_snap, index); - if(i) - { - if((char *)i < (char *)snapshots[snapid]->alt_snap || (char *)i > (char *)snapshots[snapid]->alt_snap + snapshots[snapid]->snap_size) - dbg_msg("ASDFASDFASdf", "ASDFASDFASDF"); - if((char *)i >= (char *)snapshots[snapid]->snap && (char *)i < (char *)snapshots[snapid]->snap + snapshots[snapid]->snap_size) - dbg_msg("ASDFASDFASdf", "ASDFASDFASDF"); - i->type_and_id = -1; - } -} - -void *snap_find_item(int snapid, int type, int id) -{ - /* TODO: linear search. should be fixed. */ - int i; - - if(!snapshots[snapid]) - return 0x0; - - for(i = 0; i < snapshots[snapid]->snap->num_items; i++) - { - SNAPSHOT_ITEM *itm = snapshot_get_item(snapshots[snapid]->alt_snap, i); - if(snapitem_type(itm) == type && snapitem_id(itm) == id) - return (void *)snapitem_data(itm); - } - return 0x0; -} - -int snap_num_items(int snapid) -{ - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - if(!snapshots[snapid]) - return 0; - return snapshots[snapid]->snap->num_items; -} - -/* ------ time functions ------ */ -float client_intratick() { return intratick; } -float client_predintratick() { return predintratick; } -float client_ticktime() { return ticktime; } -int client_tick() { return current_tick; } -int client_prevtick() { return prev_tick; } -int client_predtick() { return current_predtick; } -int client_tickspeed() { return SERVER_TICK_SPEED; } -float client_frametime() { return frametime; } -float client_localtime() { return (time_get()-local_start_time)/(float)(time_freq()); } - -/* ----- send functions ----- */ -int client_send_msg() -{ - const MSG_INFO *info = msg_get_info(); - NETCHUNK packet; - - if(!info) - return -1; - - if(client_state() == CLIENTSTATE_OFFLINE) - return 0; - - - mem_zero(&packet, sizeof(NETCHUNK)); - - packet.client_id = 0; - packet.data = info->data; - packet.data_size = info->size; - - if(info->flags&MSGFLAG_VITAL) - packet.flags |= NETSENDFLAG_VITAL; - if(info->flags&MSGFLAG_FLUSH) - packet.flags |= NETSENDFLAG_FLUSH; - - if(info->flags&MSGFLAG_RECORD) - { - if(demorec_isrecording()) - demorec_record_message(packet.data, packet.data_size); - } - - if(!(info->flags&MSGFLAG_NOSEND)) - netclient_send(net, &packet); - return 0; -} - -static void client_send_info() -{ - msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_string(modc_net_version(), 128); - msg_pack_string(config.player_name, 128); - msg_pack_string(config.clan_name, 128); - msg_pack_string(config.password, 128); - msg_pack_end(); - client_send_msg(); -} - - -static void client_send_entergame() -{ - msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_end(); - client_send_msg(); -} - -static void client_send_ready() -{ - msg_pack_start_system(NETMSG_READY, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_end(); - client_send_msg(); -} - -int client_rcon_authed() -{ - return rcon_authed; -} - -void client_rcon_auth(const char *name, const char *password) -{ - msg_pack_start_system(NETMSG_RCON_AUTH, MSGFLAG_VITAL); - msg_pack_string(name, 32); - msg_pack_string(password, 32); - msg_pack_end(); - client_send_msg(); -} - -void client_rcon(const char *cmd) -{ - msg_pack_start_system(NETMSG_RCON_CMD, MSGFLAG_VITAL); - msg_pack_string(cmd, 256); - msg_pack_end(); - client_send_msg(); -} - -int client_connection_problems() -{ - return netclient_gotproblems(net); -} - -void client_direct_input(int *input, int size) -{ - int i; - msg_pack_start_system(NETMSG_INPUT, 0); - msg_pack_int(ack_game_tick); - msg_pack_int(current_predtick); - msg_pack_int(size); - - for(i = 0; i < size/4; i++) - msg_pack_int(input[i]); - - msg_pack_end(); - client_send_msg(); -} - - -static void client_send_input() -{ - int64 now = time_get(); - int i, size; - - if(current_predtick <= 0) - return; - - /* fetch input */ - size = modc_snap_input(inputs[current_input].data); - - if(!size) - return; - - /* pack input */ - msg_pack_start_system(NETMSG_INPUT, MSGFLAG_FLUSH); - msg_pack_int(ack_game_tick); - msg_pack_int(current_predtick); - msg_pack_int(size); - - inputs[current_input].tick = current_predtick; - inputs[current_input].predicted_time = st_get(&predicted_time, now); - inputs[current_input].time = now; - - /* pack it */ - for(i = 0; i < size/4; i++) - msg_pack_int(inputs[current_input].data[i]); - - current_input++; - current_input%=200; - - msg_pack_end(); - client_send_msg(); -} - -const char *client_latestversion() -{ - return versionstr; -} - -/* TODO: OPT: do this alot smarter! */ -int *client_get_input(int tick) -{ - int i; - int best = -1; - for(i = 0; i < 200; i++) - { - if(inputs[i].tick <= tick && (best == -1 || inputs[best].tick < inputs[i].tick)) - best = i; - } - - if(best != -1) - return (int *)inputs[best].data; - return 0; -} - -/* ------ state handling ----- */ -static int state = CLIENTSTATE_OFFLINE; -int client_state() { return state; } -static void client_set_state(int s) -{ - int old = state; - if(config.debug) - dbg_msg("client", "state change. last=%d current=%d", state, s); - state = s; - if(old != s) - modc_statechange(state, old); -} - -/* called when the map is loaded and we should init for a new round */ -static void client_on_enter_game() -{ - /* reset input */ - int i; - for(i = 0; i < 200; i++) - inputs[i].tick = -1; - current_input = 0; - - /* reset snapshots */ - snapshots[SNAP_CURRENT] = 0; - snapshots[SNAP_PREV] = 0; - snapstorage_purge_all(&snapshot_storage); - recived_snapshots = 0; - snapshot_parts = 0; - current_predtick = 0; - current_recv_tick = 0; -} - -void client_entergame() -{ - if(state == CLIENTSTATE_DEMOPLAYBACK) - return; - - /* now we will wait for two snapshots */ - /* to finish the connection */ - client_send_entergame(); - client_on_enter_game(); -} - -void client_connect(const char *server_address_str) -{ - char buf[512]; - const char *port_str = 0; - int k; - int port = 8303; - - client_disconnect(); - - dbg_msg("client", "connecting to '%s'", server_address_str); - - //client_serverinfo_request(); - str_copy(buf, server_address_str, sizeof(buf)); - - for(k = 0; buf[k]; k++) - { - if(buf[k] == ':') - { - port_str = &(buf[k+1]); - buf[k] = 0; - break; - } - } - - if(port_str) - port = atoi(port_str); - - /* TODO: IPv6 support */ - if(net_host_lookup(buf, &server_address, NETTYPE_IPV4) != 0) - dbg_msg("client", "could not find the address of %s, connecting to localhost", buf); - - rcon_authed = 0; - server_address.port = port; - netclient_connect(net, &server_address); - client_set_state(CLIENTSTATE_CONNECTING); - - graph_init(&inputtime_margin_graph, -150.0f, 150.0f); - graph_init(&gametime_margin_graph, -150.0f, 150.0f); -} - -void client_disconnect_with_reason(const char *reason) -{ - /* stop demo playback */ - demorec_playback_stop(); - - /* */ - rcon_authed = 0; - netclient_disconnect(net, reason); - client_set_state(CLIENTSTATE_OFFLINE); - map_unload(); - - /* disable all downloads */ - mapdownload_chunk = 0; - if(mapdownload_file) - io_close(mapdownload_file); - mapdownload_file = 0; - mapdownload_crc = 0; - mapdownload_totalsize = -1; - mapdownload_amount = 0; - - /* clear the current server info */ - mem_zero(¤t_server_info, sizeof(current_server_info)); - mem_zero(&server_address, sizeof(server_address)); - - /* clear snapshots */ - snapshots[SNAP_CURRENT] = 0; - snapshots[SNAP_PREV] = 0; - recived_snapshots = 0; -} - -void client_disconnect() -{ - client_disconnect_with_reason(0); -} - - -void client_serverinfo(SERVER_INFO *serverinfo) -{ - mem_copy(serverinfo, ¤t_server_info, sizeof(current_server_info)); -} - -void client_serverinfo_request() -{ - mem_zero(¤t_server_info, sizeof(current_server_info)); - current_server_info_requesttime = 0; -} - -static int client_load_data() -{ - debug_font = gfx_load_texture("debug_font.png", IMG_AUTO, TEXLOAD_NORESAMPLE); - return 1; -} - -extern int snapshot_data_rate[0xffff]; -extern int snapshot_data_updates[0xffff]; - -const char *modc_getitemname(int type); - -static void client_debug_render() -{ - static NETSTATS prev, current; - static int64 last_snap = 0; - static float frametime_avg = 0; - int64 now = time_get(); - char buffer[512]; - - if(!config.debug) - return; - - gfx_blend_normal(); - gfx_texture_set(debug_font); - gfx_mapscreen(0,0,gfx_screenwidth(),gfx_screenheight()); - - if(time_get()-last_snap > time_freq()) - { - last_snap = time_get(); - prev = current; - net_stats(¤t); - } - - /* - eth = 14 - ip = 20 - udp = 8 - total = 42 - */ - frametime_avg = frametime_avg*0.9f + frametime*0.1f; - str_format(buffer, sizeof(buffer), "ticks: %8d %8d mem %dk %d gfxmem: %dk fps: %3d", - current_tick, current_predtick, - mem_stats()->allocated/1024, - mem_stats()->total_allocations, - gfx_memory_usage()/1024, - (int)(1.0f/frametime_avg)); - gfx_quads_text(2, 2, 16, 1,1,1,1, buffer); - - - { - int send_packets = (current.sent_packets-prev.sent_packets); - int send_bytes = (current.sent_bytes-prev.sent_bytes); - int send_total = send_bytes + send_packets*42; - int recv_packets = (current.recv_packets-prev.recv_packets); - int recv_bytes = (current.recv_bytes-prev.recv_bytes); - int recv_total = recv_bytes + recv_packets*42; - - if(!send_packets) send_packets++; - if(!recv_packets) recv_packets++; - str_format(buffer, sizeof(buffer), "send: %3d %5d+%4d=%5d (%3d kbps) avg: %5d\nrecv: %3d %5d+%4d=%5d (%3d kbps) avg: %5d", - send_packets, send_bytes, send_packets*42, send_total, (send_total*8)/1024, send_bytes/send_packets, - recv_packets, recv_bytes, recv_packets*42, recv_total, (recv_total*8)/1024, recv_bytes/recv_packets); - gfx_quads_text(2, 14, 16, 1,1,1,1, buffer); - } - - /* render rates */ - { - int y = 0; - int i; - for(i = 0; i < 256; i++) - { - if(snapshot_data_rate[i]) - { - str_format(buffer, sizeof(buffer), "%4d %20s: %8d %8d %8d", i, modc_getitemname(i), snapshot_data_rate[i]/8, snapshot_data_updates[i], - (snapshot_data_rate[i]/snapshot_data_updates[i])/8); - gfx_quads_text(2, 100+y*12, 16, 1,1,1,1, buffer); - y++; - } - } - } - - str_format(buffer, sizeof(buffer), "pred: %d ms %3.2f", - (int)((st_get(&predicted_time, now)-st_get(&game_time, now))*1000/(float)time_freq()), - predicted_time.adjustspeed[1]); - gfx_quads_text(2, 70, 16, 1,1,1,1, buffer); - - /* render graphs */ - if(config.dbg_graphs) - { - //gfx_mapscreen(0,0,400.0f,300.0f); - float w = gfx_screenwidth()/4.0f; - float h = gfx_screenheight()/6.0f; - float sp = gfx_screenwidth()/100.0f; - float x = gfx_screenwidth()-w-sp; - - graph_scale_max(&fps_graph); - graph_scale_min(&fps_graph); - graph_render(&fps_graph, x, sp*5, w, h, "FPS"); - graph_render(&inputtime_margin_graph, x, sp*5+h+sp, w, h, "Prediction Margin"); - graph_render(&gametime_margin_graph, x, sp*5+h+sp+h+sp, w, h, "Gametime Margin"); - } -} - -void client_quit() -{ - client_set_state(CLIENTSTATE_QUITING); -} - -const char *client_error_string() -{ - return netclient_error_string(net); -} - -static void client_render() -{ - if(config.gfx_clear) - gfx_clear(1,1,0); - - modc_render(); - client_debug_render(); -} - -static const char *client_load_map(const char *name, const char *filename, int wanted_crc) -{ - static char errormsg[128]; - DATAFILE *df; - int crc; - - client_set_state(CLIENTSTATE_LOADING); - - df = datafile_load(filename); - if(!df) - { - str_format(errormsg, sizeof(errormsg), "map '%s' not found", filename); - return errormsg; - } - - /* get the crc of the map */ - crc = datafile_crc(filename); - if(crc != wanted_crc) - { - datafile_unload(df); - str_format(errormsg, sizeof(errormsg), "map differs from the server. %08x != %08x", crc, wanted_crc); - return errormsg; - } - - // stop demo recording if we loaded a new map - demorec_record_stop(); - - dbg_msg("client", "loaded map '%s'", filename); - recived_snapshots = 0; - map_set(df); - - str_copy(current_map, name, sizeof(current_map)); - current_map_crc = crc; - - return NULL; -} - -static const char *client_load_map_search(const char *mapname, int wanted_crc) -{ - const char *error = 0; - char buf[512]; - dbg_msg("client", "loading map, map=%s wanted crc=%08x", mapname, wanted_crc); - client_set_state(CLIENTSTATE_LOADING); - - /* try the normal maps folder */ - str_format(buf, sizeof(buf), "maps/%s.map", mapname); - error = client_load_map(mapname, buf, wanted_crc); - if(!error) - return error; - - /* try the downloaded maps */ - str_format(buf, sizeof(buf), "downloadedmaps/%s_%8x.map", mapname, wanted_crc); - error = client_load_map(mapname, buf, wanted_crc); - return error; -} - -static int player_score_comp(const void *a, const void *b) -{ - SERVER_INFO_PLAYER *p0 = (SERVER_INFO_PLAYER *)a; - SERVER_INFO_PLAYER *p1 = (SERVER_INFO_PLAYER *)b; - if(p0->score == p1->score) - return 0; - if(p0->score < p1->score) - return 1; - return -1; -} - -static void client_process_packet(NETCHUNK *packet) -{ - if(packet->client_id == -1) - { - /* connectionlesss */ - if(packet->data_size == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) && - memcmp(packet->data, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0) - { - unsigned char *versiondata = (unsigned char*) packet->data + sizeof(VERSIONSRV_VERSION); - int version_match = !memcmp(versiondata, VERSION_DATA, sizeof(VERSION_DATA)); - - dbg_msg("client/version", "version does %s (%d.%d.%d)", - version_match ? "match" : "NOT match", - versiondata[1], versiondata[2], versiondata[3]); - - /* assume version is out of date when version-data doesn't match */ - if (!version_match) - { - sprintf(versionstr, "%d.%d.%d", versiondata[1], versiondata[2], versiondata[3]); - } - } - - if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) && - memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) - { - int size = packet->data_size-sizeof(SERVERBROWSE_LIST); - int num = size/sizeof(MASTERSRV_ADDR); - MASTERSRV_ADDR *addrs = (MASTERSRV_ADDR *)((char*)packet->data+sizeof(SERVERBROWSE_LIST)); - int i; - - for(i = 0; i < num; i++) - { - NETADDR addr; - - /* convert address */ - mem_zero(&addr, sizeof(addr)); - addr.type = NETTYPE_IPV4; - addr.ip[0] = addrs[i].ip[0]; - addr.ip[1] = addrs[i].ip[1]; - addr.ip[2] = addrs[i].ip[2]; - addr.ip[3] = addrs[i].ip[3]; - addr.port = (addrs[i].port[1]<<8) | addrs[i].port[0]; - - client_serverbrowse_set(&addr, BROWSESET_MASTER_ADD, -1, NULL); - } - } - - { - int packet_type = 0; - if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) && memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) - packet_type = 2; - - if(packet->data_size >= (int)sizeof(SERVERBROWSE_OLD_INFO) && memcmp(packet->data, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)) == 0) - packet_type = 1; - - if(packet_type) - { - /* we got ze info */ - UNPACKER up; - SERVER_INFO info = {0}; - int i; - int token = -1; - - unpacker_reset(&up, (unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO)); - if(packet_type >= 2) - token = atol(unpacker_get_string(&up)); - str_copy(info.version, unpacker_get_string(&up), sizeof(info.version)); - str_copy(info.name, unpacker_get_string(&up), sizeof(info.name)); - str_copy(info.map, unpacker_get_string(&up), sizeof(info.map)); - str_copy(info.gametype, unpacker_get_string(&up), sizeof(info.gametype)); - info.flags = atol(unpacker_get_string(&up)); - info.progression = atol(unpacker_get_string(&up)); - info.num_players = atol(unpacker_get_string(&up)); - info.max_players = atol(unpacker_get_string(&up)); - str_format(info.address, sizeof(info.address), "%d.%d.%d.%d:%d", - packet->address.ip[0], packet->address.ip[1], packet->address.ip[2], - packet->address.ip[3], packet->address.port); - - for(i = 0; i < info.num_players; i++) - { - str_copy(info.players[i].name, unpacker_get_string(&up), sizeof(info.players[i].name)); - info.players[i].score = atol(unpacker_get_string(&up)); - } - - if(!up.error) - { - /* sort players */ - qsort(info.players, info.num_players, sizeof(*info.players), player_score_comp); - - if(net_addr_comp(&server_address, &packet->address) == 0) - { - mem_copy(¤t_server_info, &info, sizeof(current_server_info)); - current_server_info.netaddr = server_address; - current_server_info_requesttime = -1; - } - else - { - if(packet_type == 2) - client_serverbrowse_set(&packet->address, BROWSESET_TOKEN, token, &info); - else - client_serverbrowse_set(&packet->address, BROWSESET_OLD_INTERNET, -1, &info); - } - } - } - } - } - else - { - int sys; - int msg = msg_unpack_start(packet->data, packet->data_size, &sys); - - if(sys) - { - /* system message */ - if(msg == NETMSG_MAP_CHANGE) - { - const char *map = msg_unpack_string(); - int map_crc = msg_unpack_int(); - const char *error = 0; - int i; - - if(msg_unpack_error()) - return; - - for(i = 0; map[i]; i++) /* protect the player from nasty map names */ - { - if(map[i] == '/' || map[i] == '\\') - error = "strange character in map name"; - } - - if(error) - client_disconnect_with_reason(error); - else - { - error = client_load_map_search(map, map_crc); - - if(!error) - { - dbg_msg("client/network", "loading done"); - client_send_ready(); - modc_connected(); - } - else - { - str_format(mapdownload_filename, sizeof(mapdownload_filename), "downloadedmaps/%s_%08x.map", map, map_crc); - - dbg_msg("client/network", "starting to download map to '%s'", mapdownload_filename); - - mapdownload_chunk = 0; - str_copy(mapdownload_name, map, sizeof(mapdownload_name)); - mapdownload_file = engine_openfile(mapdownload_filename, IOFLAG_WRITE); - mapdownload_crc = map_crc; - mapdownload_totalsize = -1; - mapdownload_amount = 0; - - msg_pack_start_system(NETMSG_REQUEST_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_int(mapdownload_chunk); - msg_pack_end(); - client_send_msg(); - - if(config.debug) - dbg_msg("client/network", "requested chunk %d", mapdownload_chunk); - } - } - } - else if(msg == NETMSG_MAP_DATA) - { - int last = msg_unpack_int(); - int total_size = msg_unpack_int(); - int size = msg_unpack_int(); - const unsigned char *data = msg_unpack_raw(size); - - /* check fior errors */ - if(msg_unpack_error() || size <= 0 || total_size <= 0 || !mapdownload_file) - return; - - io_write(mapdownload_file, data, size); - - mapdownload_totalsize = total_size; - mapdownload_amount += size; - - if(last) - { - const char *error; - dbg_msg("client/network", "download complete, loading map"); - - io_close(mapdownload_file); - mapdownload_file = 0; - mapdownload_amount = 0; - mapdownload_totalsize = -1; - - /* load map */ - error = client_load_map(mapdownload_name, mapdownload_filename, mapdownload_crc); - if(!error) - { - dbg_msg("client/network", "loading done"); - client_send_ready(); - modc_connected(); - } - else - client_disconnect_with_reason(error); - } - else - { - /* request new chunk */ - mapdownload_chunk++; - msg_pack_start_system(NETMSG_REQUEST_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_int(mapdownload_chunk); - msg_pack_end(); - client_send_msg(); - - if(config.debug) - dbg_msg("client/network", "requested chunk %d", mapdownload_chunk); - } - } - else if(msg == NETMSG_PING) - { - msg_pack_start_system(NETMSG_PING_REPLY, 0); - msg_pack_end(); - client_send_msg(); - } - else if(msg == NETMSG_RCON_AUTH_STATUS) - { - int result = msg_unpack_int(); - if(msg_unpack_error() == 0) - rcon_authed = result; - } - else if(msg == NETMSG_RCON_LINE) - { - const char *line = msg_unpack_string(); - if(msg_unpack_error() == 0) - { - /*dbg_msg("remote", "%s", line);*/ - modc_rcon_line(line); - } - } - else if(msg == NETMSG_PING_REPLY) - dbg_msg("client/network", "latency %.2f", (time_get() - ping_start_time)*1000 / (float)time_freq()); - else if(msg == NETMSG_INPUTTIMING) - { - int input_predtick = msg_unpack_int(); - int time_left = msg_unpack_int(); - - /* adjust our prediction time */ - int k; - int64 target = 0; - for(k = 0; k < 200; k++) - { - if(inputs[k].tick == input_predtick) - { - target = inputs[k].predicted_time + (time_get() - inputs[k].time); - target = target - (int64)(((time_left-prediction_margin)/1000.0f)*time_freq()); - //st_update(&predicted_time, ); - break; - } - } - - if(target) - st_update(&predicted_time, &inputtime_margin_graph, target, time_left, 1); - } - else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSINGLE || msg == NETMSG_SNAPEMPTY) - { - /*dbg_msg("client/network", "got snapshot"); */ - int num_parts = 1; - int part = 0; - int game_tick = msg_unpack_int(); - int delta_tick = game_tick-msg_unpack_int(); - int part_size = 0; - int crc = 0; - int complete_size = 0; - const char *data = 0; - - /* we are not allowed to process snapshot yet */ - if(client_state() < CLIENTSTATE_LOADING) - return; - - if(msg == NETMSG_SNAP) - { - num_parts = msg_unpack_int(); - part = msg_unpack_int(); - } - - if(msg != NETMSG_SNAPEMPTY) - { - crc = msg_unpack_int(); - part_size = msg_unpack_int(); - } - - data = (const char *)msg_unpack_raw(part_size); - - if(msg_unpack_error()) - return; - - if(game_tick >= current_recv_tick) - { - if(game_tick != current_recv_tick) - { - snapshot_parts = 0; - current_recv_tick = game_tick; - } - - /* TODO: clean this up abit */ - mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, data, part_size); - snapshot_parts |= 1<= 0) - { - int deltashot_size = snapstorage_get(&snapshot_storage, delta_tick, 0, &deltashot, 0); - - if(deltashot_size < 0) - { - /* couldn't find the delta snapshots that the server used */ - /* to compress this snapshot. force the server to resync */ - if(config.debug) - dbg_msg("client", "error, couldn't find the delta snapshot"); - - /* ack snapshot */ - /* TODO: combine this with the input message */ - ack_game_tick = -1; - return; - } - } - - /* decompress snapshot */ - deltadata = snapshot_empty_delta(); - deltasize = sizeof(int)*3; - - if(complete_size) - { - int intsize = intpack_decompress(snapshot_incomming_data, complete_size, tmpbuffer2); - - if(intsize < 0) /* failure during decompression, bail */ - return; - - deltadata = tmpbuffer2; - deltasize = intsize; - } - - /* unpack delta */ - purgetick = delta_tick; - snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize); - if(snapsize < 0) - { - dbg_msg("client", "delta unpack failed!"); - return; - } - - if(msg != NETMSG_SNAPEMPTY && snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc) - { - if(config.debug) - { - dbg_msg("client", "snapshot crc error #%d - tick=%d wantedcrc=%d gotcrc=%d compressed_size=%d delta_tick=%d", - snapcrcerrors, game_tick, crc, snapshot_crc((SNAPSHOT*)tmpbuffer3), complete_size, delta_tick); - } - - snapcrcerrors++; - if(snapcrcerrors > 10) - { - /* to many errors, send reset */ - ack_game_tick = -1; - client_send_input(); - snapcrcerrors = 0; - } - return; - } - else - { - if(snapcrcerrors) - snapcrcerrors--; - } - - /* purge old snapshots */ - purgetick = delta_tick; - if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->tick < purgetick) - purgetick = snapshots[SNAP_PREV]->tick; - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->tick < purgetick) - purgetick = snapshots[SNAP_PREV]->tick; - snapstorage_purge_until(&snapshot_storage, purgetick); - - /* add new */ - snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3, 1); - - /* add snapshot to demo */ - if(demorec_isrecording()) - { - - /* write tick marker */ - /* - DEMOREC_TICKMARKER marker; - marker.tick = game_tick; - swap_endian(&marker, sizeof(int), sizeof(marker)/sizeof(int)); - demorec_record_write("TICK", sizeof(marker), &marker); - demorec_record_write("SNAP", snapsize, tmpbuffer3); - */ - - /* write snapshot */ - demorec_record_snapshot(game_tick, tmpbuffer3, snapsize); - } - - /* apply snapshot, cycle pointers */ - recived_snapshots++; - - current_recv_tick = game_tick; - - /* we got two snapshots until we see us self as connected */ - if(recived_snapshots == 2) - { - /* start at 200ms and work from there */ - st_init(&predicted_time, game_tick*time_freq()/50); - predicted_time.adjustspeed[1] = 1000.0f; - st_init(&game_time, (game_tick-1)*time_freq()/50); - snapshots[SNAP_PREV] = snapshot_storage.first; - snapshots[SNAP_CURRENT] = snapshot_storage.last; - local_start_time = time_get(); - client_set_state(CLIENTSTATE_ONLINE); - } - - /* adjust game time */ - { - int64 now = st_get(&game_time, time_get()); - int64 tickstart = game_tick*time_freq()/50; - int64 time_left = (tickstart-now)*1000 / time_freq(); - /*st_update(&game_time, (game_tick-1)*time_freq()/50);*/ - st_update(&game_time, &gametime_margin_graph, (game_tick-1)*time_freq()/50, time_left, 0); - } - - /* ack snapshot */ - ack_game_tick = game_tick; - } - } - } - } - else - { - /* game message */ - if(demorec_isrecording()) - demorec_record_message(packet->data, packet->data_size); - /* demorec_record_write("MESG", packet->data_size, ); */ - - modc_message(msg); - } - } -} - -int client_mapdownload_amount() { return mapdownload_amount; } -int client_mapdownload_totalsize() { return mapdownload_totalsize; } - -static void client_pump_network() -{ - NETCHUNK packet; - - netclient_update(net); - - if(client_state() != CLIENTSTATE_DEMOPLAYBACK) - { - /* check for errors */ - if(client_state() != CLIENTSTATE_OFFLINE && netclient_state(net) == NETSTATE_OFFLINE) - { - client_set_state(CLIENTSTATE_OFFLINE); - client_disconnect(); - dbg_msg("client", "offline error='%s'", netclient_error_string(net)); - } - - /* */ - if(client_state() == CLIENTSTATE_CONNECTING && netclient_state(net) == NETSTATE_ONLINE) - { - /* we switched to online */ - dbg_msg("client", "connected, sending info"); - client_set_state(CLIENTSTATE_LOADING); - client_send_info(); - } - } - - /* process packets */ - while(netclient_recv(net, &packet)) - client_process_packet(&packet); -} - -static void client_democallback_snapshot(void *data, int size) -{ - /* update ticks, they could have changed */ - const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); - SNAPSTORAGE_HOLDER *temp; - current_tick = info->current_tick; - prev_tick = info->previous_tick; - - /* handle snapshots */ - temp = snapshots[SNAP_PREV]; - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = temp; - - mem_copy(snapshots[SNAP_CURRENT]->snap, data, size); - mem_copy(snapshots[SNAP_CURRENT]->alt_snap, data, size); - - modc_newsnapshot(); - /*modc_predict();*/ -} - -static void client_democallback_message(void *data, int size) -{ - int sys = 0; - int msg = msg_unpack_start(data, size, &sys); - if(!sys) - modc_message(msg); -} - - -const DEMOPLAYBACK_INFO *client_demoplayer_getinfo() -{ - static DEMOPLAYBACK_INFO ret; - const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); - ret.first_tick = info->first_tick; - ret.last_tick = info->last_tick; - ret.current_tick = info->current_tick; - ret.paused = info->paused; - ret.speed = info->speed; - return &ret; -} - -void client_demoplayer_setpos(float percent) -{ - demorec_playback_set(percent); -} - -void client_demoplayer_setspeed(float speed) -{ - demorec_playback_setspeed(speed); -} - -void client_demoplayer_setpause(int paused) -{ - if(paused) - demorec_playback_pause(); - else - demorec_playback_unpause(); -} - -static void client_update() -{ - if(client_state() == CLIENTSTATE_DEMOPLAYBACK) - { - demorec_playback_update(); - if(demorec_isplaying()) - { - /* update timers */ - const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); - current_tick = info->current_tick; - prev_tick = info->previous_tick; - intratick = info->intratick; - ticktime = info->ticktime; - } - else - { - /* disconnect on error */ - client_disconnect(); - } - } - else if(client_state() != CLIENTSTATE_OFFLINE && recived_snapshots >= 3) - { - /* switch snapshot */ - int repredict = 0; - int64 freq = time_freq(); - int64 now = st_get(&game_time, time_get()); - int64 pred_now = st_get(&predicted_time, time_get()); - - while(1) - { - SNAPSTORAGE_HOLDER *cur = snapshots[SNAP_CURRENT]; - int64 tickstart = (cur->tick)*time_freq()/50; - - if(tickstart < now) - { - SNAPSTORAGE_HOLDER *next = snapshots[SNAP_CURRENT]->next; - if(next) - { - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = next; - - /* set ticks */ - current_tick = snapshots[SNAP_CURRENT]->tick; - prev_tick = snapshots[SNAP_PREV]->tick; - - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) - { - modc_newsnapshot(); - repredict = 1; - } - } - else - break; - } - else - break; - } - - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) - { - int64 curtick_start = (snapshots[SNAP_CURRENT]->tick)*time_freq()/50; - int64 prevtick_start = (snapshots[SNAP_PREV]->tick)*time_freq()/50; - /*tg_add(&predicted_time_graph, pred_now, 0); */ - int prev_pred_tick = (int)(pred_now*50/time_freq()); - int new_pred_tick = prev_pred_tick+1; - static float last_predintra = 0; - - intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start); - ticktime = (now - prevtick_start) / (float)freq; /*(float)SERVER_TICK_SPEED);*/ - - curtick_start = new_pred_tick*time_freq()/50; - prevtick_start = prev_pred_tick*time_freq()/50; - predintratick = (pred_now - prevtick_start) / (float)(curtick_start-prevtick_start); - - if(new_pred_tick < snapshots[SNAP_PREV]->tick-SERVER_TICK_SPEED || new_pred_tick > snapshots[SNAP_PREV]->tick+SERVER_TICK_SPEED) - { - dbg_msg("client", "prediction time reset!"); - st_init(&predicted_time, snapshots[SNAP_CURRENT]->tick*time_freq()/50); - } - - if(new_pred_tick > current_predtick) - { - last_predintra = predintratick; - current_predtick = new_pred_tick; - repredict = 1; - - /* send input */ - client_send_input(); - } - - last_predintra = predintratick; - } - - /* only do sane predictions */ - if(repredict) - { - if(current_predtick > current_tick && current_predtick < current_tick+50) - modc_predict(); - } - - /* fetch server info if we don't have it */ - if(client_state() >= CLIENTSTATE_LOADING && - current_server_info_requesttime >= 0 && - time_get() > current_server_info_requesttime) - { - client_serverbrowse_request(&server_address); - current_server_info_requesttime = time_get()+time_freq()*2; - } - } - - /* STRESS TEST: join the server again */ - if(config.dbg_stress) - { - static int64 action_taken = 0; - int64 now = time_get(); - if(client_state() == CLIENTSTATE_OFFLINE) - { - if(now > action_taken+time_freq()*2) - { - dbg_msg("stress", "reconnecting!"); - client_connect(config.dbg_stress_server); - action_taken = now; - } - } - else - { - /*if(now > action_taken+time_freq()*(10+config.dbg_stress)) - { - dbg_msg("stress", "disconnecting!"); - client_disconnect(); - action_taken = now; - }*/ - } - } - - /* pump the network */ - client_pump_network(); - - /* update the maser server registry */ - mastersrv_update(); - - /* update the server browser */ - client_serverbrowse_update(); -} - - -static void client_versionupdate() -{ - static int state = 0; - static HOSTLOOKUP version_serveraddr; - - if(state == 0) - { - engine_hostlookup(&version_serveraddr, config.cl_version_server); - state++; - } - else if(state == 1) - { - if(jobs_status(&version_serveraddr.job) == JOBSTATUS_DONE) - { - NETCHUNK packet; - - mem_zero(&packet, sizeof(NETCHUNK)); - - version_serveraddr.addr.port = VERSIONSRV_PORT; - - packet.client_id = -1; - packet.address = version_serveraddr.addr; - packet.data = VERSIONSRV_GETVERSION; - packet.data_size = sizeof(VERSIONSRV_GETVERSION); - packet.flags = NETSENDFLAG_CONNLESS; - - netclient_send(net, &packet); - state++; - } - } -} - -extern int editor_update_and_render(); -extern void editor_init(); - -static void client_run() -{ - NETADDR bindaddr; - int64 reporttime = time_get(); - int64 reportinterval = time_freq()*1; - - static PERFORMACE_INFO rootscope = {"root", 0}; - perf_start(&rootscope); - - local_start_time = time_get(); - snapshot_parts = 0; - - /* init graphics and sound */ - if(gfx_init() != 0) - return; - - /* start refreshing addresses while we load */ - mastersrv_refresh_addresses(); - - /* init the editor */ - editor_init(); - - /* sound is allowed to fail */ - snd_init(); - - /* load data */ - if(!client_load_data()) - return; - - /* init the mod */ - modc_init(); - dbg_msg("client", "version %s", modc_net_version()); - - /* open socket */ - mem_zero(&bindaddr, sizeof(bindaddr)); - net = netclient_open(bindaddr, 0); - - /* connect to the server if wanted */ - /* - if(config.cl_connect[0] != 0) - client_connect(config.cl_connect); - config.cl_connect[0] = 0; - */ - - /* */ - graph_init(&fps_graph, 0.0f, 200.0f); - - /* never start with the editor */ - config.cl_editor = 0; - - inp_mouse_mode_relative(); - - while (1) - { - static PERFORMACE_INFO rootscope = {"root", 0}; - int64 frame_start_time = time_get(); - frames++; - - perf_start(&rootscope); - - /* */ - client_versionupdate(); - - /* handle pending connects */ - if(cmd_connect[0]) - { - client_connect(cmd_connect); - cmd_connect[0] = 0; - } - - /* update input */ - { - static PERFORMACE_INFO scope = {"inp_update", 0}; - perf_start(&scope); - inp_update(); - perf_end(); - } - - /* update sound */ - { - static PERFORMACE_INFO scope = {"snd_update", 0}; - perf_start(&scope); - snd_update(); - perf_end(); - } - - /* release focus */ - if(!gfx_window_active()) - { - if(window_must_refocus == 0) - inp_mouse_mode_absolute(); - window_must_refocus = 1; - } - else if (config.dbg_focus && inp_key_pressed(KEY_ESCAPE)) - { - inp_mouse_mode_absolute(); - window_must_refocus = 1; - } - - /* refocus */ - if(window_must_refocus && gfx_window_active()) - { - if(window_must_refocus < 3) - { - inp_mouse_mode_absolute(); - window_must_refocus++; - } - - if(inp_key_pressed(KEY_MOUSE_1)) - { - inp_mouse_mode_relative(); - window_must_refocus = 0; - } - } - - /* panic quit button */ - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_pressed('q')) - break; - - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('d')) - config.debug ^= 1; - - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('g')) - config.dbg_graphs ^= 1; - - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('e')) - { - config.cl_editor = config.cl_editor^1; - inp_mouse_mode_relative(); - } - - /* - if(!gfx_window_open()) - break; - */ - - /* render */ - if(config.cl_editor) - { - client_update(); - editor_update_and_render(); - gfx_swap(); - } - else - { - { - static PERFORMACE_INFO scope = {"client_update", 0}; - perf_start(&scope); - client_update(); - perf_end(); - } - - if(config.dbg_stress) - { - if((frames%10) == 0) - { - client_render(); - gfx_swap(); - } - } - else - { - { - static PERFORMACE_INFO scope = {"client_render", 0}; - perf_start(&scope); - client_render(); - perf_end(); - } - - { - static PERFORMACE_INFO scope = {"gfx_swap", 0}; - perf_start(&scope); - gfx_swap(); - perf_end(); - } - } - } - - perf_end(); - - - /* check conditions */ - if(client_state() == CLIENTSTATE_QUITING) - break; - - /* be nice */ - if(config.dbg_stress) - thread_sleep(5); - else if(config.cl_cpu_throttle || !gfx_window_active()) - thread_sleep(1); - - if(config.dbg_hitch) - { - thread_sleep(config.dbg_hitch); - config.dbg_hitch = 0; - } - - if(reporttime < time_get()) - { - if(0 && config.debug) - { - dbg_msg("client/report", "fps=%.02f (%.02f %.02f) netstate=%d", - frames/(float)(reportinterval/time_freq()), - 1.0f/frametime_high, - 1.0f/frametime_low, - netclient_state(net)); - } - frametime_low = 1; - frametime_high = 0; - frames = 0; - reporttime += reportinterval; - perf_next(); - - if(config.dbg_pref) - perf_dump(&rootscope); - } - - /* update frametime */ - frametime = (time_get()-frame_start_time)/(float)time_freq(); - if(frametime < frametime_low) - frametime_low = frametime; - if(frametime > frametime_high) - frametime_high = frametime; - - graph_add(&fps_graph, 1.0f/frametime, 1,1,1); - } - - modc_shutdown(); - client_disconnect(); - - gfx_shutdown(); - snd_shutdown(); -} - -static void con_connect(void *result, void *user_data) -{ - str_copy(cmd_connect, console_arg_string(result, 0), sizeof(cmd_connect)); -} - -static void con_disconnect(void *result, void *user_data) -{ - client_disconnect(); -} - -static void con_quit(void *result, void *user_data) -{ - client_quit(); -} - -static void con_ping(void *result, void *user_data) -{ - msg_pack_start_system(NETMSG_PING, 0); - msg_pack_end(); - client_send_msg(); - ping_start_time = time_get(); -} - -static void con_screenshot(void *result, void *user_data) -{ - gfx_screenshot(); -} - -static void con_rcon(void *result, void *user_data) -{ - client_rcon(console_arg_string(result, 0)); -} - -static void con_rcon_auth(void *result, void *user_data) -{ - client_rcon_auth("", console_arg_string(result, 0)); -} - -static void con_addfavorite(void *result, void *user_data) -{ - NETADDR addr; - if(net_addr_from_str(&addr, console_arg_string(result, 0)) == 0) - client_serverbrowse_addfavorite(addr); -} - -const char *client_demoplayer_play(const char *filename) -{ - int crc; - const char *error; - client_disconnect(); - netclient_error_string_reset(net); - - /* try to start playback */ - demorec_playback_registercallbacks(client_democallback_snapshot, client_democallback_message); - - if(demorec_playback_load(filename)) - return "error loading demo"; - - /* load map */ - crc = (demorec_playback_info()->header.crc[0]<<24)| - (demorec_playback_info()->header.crc[1]<<16)| - (demorec_playback_info()->header.crc[2]<<8)| - (demorec_playback_info()->header.crc[3]); - error = client_load_map_search(demorec_playback_info()->header.map, crc); - if(error) - { - client_disconnect_with_reason(error); - return error; - } - - modc_connected(); - - /* setup buffers */ - mem_zero(demorec_snapshotdata, sizeof(demorec_snapshotdata)); - - snapshots[SNAP_CURRENT] = &demorec_snapshotholders[SNAP_CURRENT]; - snapshots[SNAP_PREV] = &demorec_snapshotholders[SNAP_PREV]; - - snapshots[SNAP_CURRENT]->snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_CURRENT][0]; - snapshots[SNAP_CURRENT]->alt_snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_CURRENT][1]; - snapshots[SNAP_CURRENT]->snap_size = 0; - snapshots[SNAP_CURRENT]->tick = -1; - - snapshots[SNAP_PREV]->snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_PREV][0]; - snapshots[SNAP_PREV]->alt_snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_PREV][1]; - snapshots[SNAP_PREV]->snap_size = 0; - snapshots[SNAP_PREV]->tick = -1; - - /* enter demo playback state */ - client_set_state(CLIENTSTATE_DEMOPLAYBACK); - - demorec_playback_play(); - modc_entergame(); - - return 0; -} - -static void con_play(void *result, void *user_data) -{ - client_demoplayer_play(console_arg_string(result, 0)); -} - -static void con_record(void *result, void *user_data) -{ - if(state != CLIENTSTATE_ONLINE) - dbg_msg("demorec/record", "client is not online"); - else - { - char filename[512]; - str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(result, 0)); - demorec_record_start(filename, modc_net_version(), current_map, current_map_crc, "client"); - } -} - -static void con_stoprecord(void *result, void *user_data) -{ - demorec_record_stop(); -} - -static void con_serverdummy(void *result, void *user_data) -{ - dbg_msg("client", "this command is not available on the client"); -} - -static void client_register_commands() -{ - MACRO_REGISTER_COMMAND("quit", "", CFGFLAG_CLIENT, con_quit, 0x0, "Quit Teeworlds"); - MACRO_REGISTER_COMMAND("exit", "", CFGFLAG_CLIENT, con_quit, 0x0, "Quit Teeworlds"); - MACRO_REGISTER_COMMAND("connect", "s", CFGFLAG_CLIENT, con_connect, 0x0, "Connect to the specified host/ip"); - MACRO_REGISTER_COMMAND("disconnect", "", CFGFLAG_CLIENT, con_disconnect, 0x0, "Disconnect from the server"); - MACRO_REGISTER_COMMAND("ping", "", CFGFLAG_CLIENT, con_ping, 0x0, "Ping the current server"); - MACRO_REGISTER_COMMAND("screenshot", "", CFGFLAG_CLIENT, con_screenshot, 0x0, "Take a screenshot"); - MACRO_REGISTER_COMMAND("rcon", "r", CFGFLAG_CLIENT, con_rcon, 0x0, "Send specified command to rcon"); - MACRO_REGISTER_COMMAND("rcon_auth", "s", CFGFLAG_CLIENT, con_rcon_auth, 0x0, "Authenticate to rcon"); - - MACRO_REGISTER_COMMAND("play", "r", CFGFLAG_CLIENT, con_play, 0x0, "Play the file specified"); - MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_CLIENT, con_record, 0, "Record to the file"); - MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_CLIENT, con_stoprecord, 0, "Stop recording"); - - MACRO_REGISTER_COMMAND("add_favorite", "s", CFGFLAG_CLIENT, con_addfavorite, 0x0, "Add a server as a favorite"); - - /* register server dummy commands for tab completion */ - MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, con_serverdummy, 0, "Kick player with specified id"); - MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, con_serverdummy, 0, "Ban player with ip/id for x minutes"); - MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, con_serverdummy, 0, "Unban ip"); - MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, con_serverdummy, 0, "Show banlist"); - MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, con_serverdummy, 0, "List players"); - MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, con_serverdummy, 0, "Shut down"); - /*MACRO_REGISTER_COMMAND("record", "", CFGFLAG_SERVER, con_serverdummy, 0); - MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, con_serverdummy, 0);*/ -} - -void client_save_line(const char *line) -{ - engine_config_write_line(line); -} - -const char *client_user_directory() -{ - static char path[1024] = {0}; - fs_storage_path("Teeworlds", path, sizeof(path)); - return path; -} - -#if defined(CONF_PLATFORM_MACOSX) -int SDL_main(int argc, char **argv) -#else -int main(int argc, char **argv) -#endif -{ - /* init the engine */ - dbg_msg("client", "starting..."); - engine_init("Teeworlds"); - - /* register all console commands */ - client_register_commands(); - modc_console_init(); - - /* parse the command line arguments */ - engine_parse_arguments(argc, argv); - - /* execute config file */ - console_execute_file("settings.cfg"); - - /* run the client*/ - client_run(); - - /* write down the config and quit */ - if(engine_config_write_start() == 0) - { - config_save(); - client_serverbrowse_save(); - modc_save_config(); - engine_config_write_stop(); - } - - return 0; -} diff --git a/src/engine/client/ec_client.cpp b/src/engine/client/ec_client.cpp new file mode 100644 index 00000000..7ba0a2bb --- /dev/null +++ b/src/engine/client/ec_client.cpp @@ -0,0 +1,2087 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "editor.h" +#include "graphics.h" +#include "client.h" + +static IEditor *m_pEditor = 0; +static IEngineGraphics *m_pGraphics = 0; +IEngineGraphics *Graphics() { return m_pGraphics; } + +static IGameClient *m_pGameClient = 0; + + +class CClient : public IEngine +{ +public: + virtual class IGraphics *Graphics() + { + return m_pGraphics; + } +}; + +static CClient m_Client; + +const int prediction_margin = 1000/50/2; /* magic network prediction value */ + +/* + Server Time + Client Mirror Time + Client Predicted Time + + Snapshot Latency + Downstream latency + + Prediction Latency + Upstream latency +*/ + +/* network client, must be accessible from other parts like the server browser */ +CNetClient m_NetClient; + +/* TODO: ugly, fix me */ +extern void client_serverbrowse_set(NETADDR *addr, int request, int token, SERVER_INFO *info); +extern void client_serverbrowse_save(); + +static unsigned snapshot_parts; +static int64 local_start_time; + +static int debug_font; +static float frametime = 0.0001f; +static float frametime_low = 1.0f; +static float frametime_high = 0.0f; +static int frames = 0; +static NETADDR server_address; +static int window_must_refocus = 0; +static int snapcrcerrors = 0; + +static int ack_game_tick = -1; +static int current_recv_tick = 0; +static int rcon_authed = 0; + +/* version-checking */ +static char versionstr[10] = "0"; + +/* pinging */ +static int64 ping_start_time = 0; + +/* */ +static char current_map[256] = {0}; +static int current_map_crc = 0; + +/* */ +static char cmd_connect[256] = {0}; + +/* map download */ +static char mapdownload_filename[256] = {0}; +static char mapdownload_name[256] = {0}; +static IOHANDLE mapdownload_file = 0; +static int mapdownload_chunk = 0; +static int mapdownload_crc = 0; +static int mapdownload_amount = -1; +static int mapdownload_totalsize = -1; + +/* */ +static SERVER_INFO current_server_info = {0}; +static int64 current_server_info_requesttime = -1; /* >= 0 should request, == -1 got info */ + +/* current time */ +static int current_tick = 0; +static float intratick = 0; +static float ticktime = 0; +static int prev_tick = 0; + +/* */ +/*static int predictiontime_pingspikecounter = 0; +static int gametime_pingspikecounter = 0;*/ + +/* predicted time */ +static int current_predtick = 0; +static float predintratick = 0; +static int last_input_timeleft = 0; + +static struct /* TODO: handle input better */ +{ + int data[MAX_INPUT_SIZE]; /* the input data */ + int tick; /* the tick that the input is for */ + int64 predicted_time; /* prediction latency when we sent this input */ + int64 time; +} inputs[200]; +static int current_input = 0; + +enum +{ + GRAPH_MAX=128 +}; + +typedef struct +{ + float min, max; + float values[GRAPH_MAX]; + float colors[GRAPH_MAX][3]; + int index; +} GRAPH; + +static void graph_init(GRAPH *g, float min, float max) +{ + g->min = min; + g->max = max; + g->index = 0; +} + +static void graph_scale_max(GRAPH *g) +{ + int i = 0; + g->max = 0; + for(i = 0; i < GRAPH_MAX; i++) + { + if(g->values[i] > g->max) + g->max = g->values[i]; + } +} + +static void graph_scale_min(GRAPH *g) +{ + int i = 0; + g->min = g->max; + for(i = 0; i < GRAPH_MAX; i++) + { + if(g->values[i] < g->min) + g->min = g->values[i]; + } +} + +static void graph_add(GRAPH *graph, float v, float r, float g, float b) +{ + graph->index = (graph->index+1)&(GRAPH_MAX-1); + graph->values[graph->index] = v; + graph->colors[graph->index][0] = r; + graph->colors[graph->index][1] = g; + graph->colors[graph->index][2] = b; +} + +static void graph_render(GRAPH *g, float x, float y, float w, float h, const char *description) +{ + char buf[32]; + int i; + + //m_pGraphics->BlendNormal(); + + + Graphics()->TextureSet(-1); + + m_pGraphics->QuadsBegin(); + Graphics()->SetColor(0, 0, 0, 0.75f); + Graphics()->QuadsDrawTL(x, y, w, h); + m_pGraphics->QuadsEnd(); + + Graphics()->LinesBegin(); + Graphics()->SetColor(0.95f, 0.95f, 0.95f, 1.00f); + Graphics()->LinesDraw(x, y+h/2, x+w, y+h/2); + Graphics()->SetColor(0.5f, 0.5f, 0.5f, 0.75f); + Graphics()->LinesDraw(x, y+(h*3)/4, x+w, y+(h*3)/4); + Graphics()->LinesDraw(x, y+h/4, x+w, y+h/4); + for(i = 1; i < GRAPH_MAX; i++) + { + float a0 = (i-1)/(float)GRAPH_MAX; + float a1 = i/(float)GRAPH_MAX; + int i0 = (g->index+i-1)&(GRAPH_MAX-1); + int i1 = (g->index+i)&(GRAPH_MAX-1); + + float v0 = (g->values[i0]-g->min) / (g->max-g->min); + float v1 = (g->values[i1]-g->min) / (g->max-g->min); + + Graphics()->SetColorVertex(0, g->colors[i0][0], g->colors[i0][1], g->colors[i0][2], 0.75f); + Graphics()->SetColorVertex(1, g->colors[i1][0], g->colors[i1][1], g->colors[i1][2], 0.75f); + Graphics()->LinesDraw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h); + + } + Graphics()->LinesEnd(); + + + Graphics()->TextureSet(debug_font); + Graphics()->QuadsText(x+2, y+h-16, 16, 1,1,1,1, description); + + str_format(buf, sizeof(buf), "%.2f", g->max); + Graphics()->QuadsText(x+w-8*strlen(buf)-8, y+2, 16, 1,1,1,1, buf); + + str_format(buf, sizeof(buf), "%.2f", g->min); + Graphics()->QuadsText(x+w-8*strlen(buf)-8, y+h-16, 16, 1,1,1,1, buf); + +} + +typedef struct +{ + int64 snap; + int64 current; + int64 target; + + int64 rlast; + int64 tlast; + GRAPH graph; + + int spikecounter; + + float adjustspeed[2]; /* 0 = down, 1 = up */ +} SMOOTHTIME; + +static void st_init(SMOOTHTIME *st, int64 target) +{ + st->snap = time_get(); + st->current = target; + st->target = target; + st->adjustspeed[0] = 0.3f; + st->adjustspeed[1] = 0.3f; + graph_init(&st->graph, 0.0f, 0.5f); +} + +static int64 st_get(SMOOTHTIME *st, int64 now) +{ + float adjust_speed, a; + int64 c = st->current + (now - st->snap); + int64 t = st->target + (now - st->snap); + int64 r; + + /* it's faster to adjust upward instead of downward */ + /* we might need to adjust these abit */ + + adjust_speed = st->adjustspeed[0]; + if(t > c) + adjust_speed = st->adjustspeed[1]; + + a = ((now-st->snap)/(float)time_freq()) * adjust_speed; + if(a > 1.0f) + a = 1.0f; + + r = c + (int64)((t-c)*a); + + graph_add(&st->graph, a+0.5f,1,1,1); + + return r; +} + +static void st_update_int(SMOOTHTIME *st, int64 target) +{ + int64 now = time_get(); + st->current = st_get(st, now); + st->snap = now; + st->target = target; +} + +static void st_update(SMOOTHTIME *st, GRAPH *graph, int64 target, int time_left, int adjust_direction) +{ + int update_timer = 1; + + if(time_left < 0) + { + int is_spike = 0; + if(time_left < -50) + { + is_spike = 1; + + st->spikecounter += 5; + if(st->spikecounter > 50) + st->spikecounter = 50; + } + + if(is_spike && st->spikecounter < 15) + { + /* ignore this ping spike */ + update_timer = 0; + graph_add(graph, time_left, 1,1,0); + } + else + { + graph_add(graph, time_left, 1,0,0); + if(st->adjustspeed[adjust_direction] < 30.0f) + st->adjustspeed[adjust_direction] *= 2.0f; + } + } + else + { + if(st->spikecounter) + st->spikecounter--; + + graph_add(graph, time_left, 0,1,0); + + st->adjustspeed[adjust_direction] *= 0.95f; + if(st->adjustspeed[adjust_direction] < 2.0f) + st->adjustspeed[adjust_direction] = 2.0f; + } + + last_input_timeleft = time_left; + + if(update_timer) + st_update_int(st, target); +} + +static SMOOTHTIME game_time; +static SMOOTHTIME predicted_time; + +/* graphs */ +static GRAPH inputtime_margin_graph; +static GRAPH gametime_margin_graph; +static GRAPH fps_graph; + +/* -- snapshot handling --- */ +enum +{ + NUM_SNAPSHOT_TYPES=2 +}; + +/* the game snapshots are modifiable by the game */ +CSnapshotStorage snapshot_storage; +static CSnapshotStorage::CHolder *snapshots[NUM_SNAPSHOT_TYPES] = {0, 0}; + +static int recived_snapshots = 0; +static char snapshot_incomming_data[CSnapshot::MAX_SIZE]; + +static CSnapshotStorage::CHolder demorec_snapshotholders[NUM_SNAPSHOT_TYPES]; +static char *demorec_snapshotdata[NUM_SNAPSHOT_TYPES][2][CSnapshot::MAX_SIZE]; + +/* --- */ + +void *snap_get_item(int snapid, int index, SNAP_ITEM *item) +{ + CSnapshotItem *i; + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + i = snapshots[snapid]->m_pAltSnap->GetItem(index); + item->datasize = snapshots[snapid]->m_pAltSnap->GetItemSize(index); + item->type = i->Type(); + item->id = i->ID(); + return (void *)i->Data(); +} + +void snap_invalidate_item(int snapid, int index) +{ + CSnapshotItem *i; + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + i = snapshots[snapid]->m_pAltSnap->GetItem(index); + if(i) + { + if((char *)i < (char *)snapshots[snapid]->m_pAltSnap || (char *)i > (char *)snapshots[snapid]->m_pAltSnap + snapshots[snapid]->m_SnapSize) + dbg_msg("ASDFASDFASdf", "ASDFASDFASDF"); + if((char *)i >= (char *)snapshots[snapid]->m_pSnap && (char *)i < (char *)snapshots[snapid]->m_pSnap + snapshots[snapid]->m_SnapSize) + dbg_msg("ASDFASDFASdf", "ASDFASDFASDF"); + i->m_TypeAndID = -1; + } +} + +void *snap_find_item(int snapid, int type, int id) +{ + /* TODO: linear search. should be fixed. */ + int i; + + if(!snapshots[snapid]) + return 0x0; + + for(i = 0; i < snapshots[snapid]->m_pSnap->m_NumItems; i++) + { + CSnapshotItem *itm = snapshots[snapid]->m_pAltSnap->GetItem(i); + if(itm->Type() == type && itm->ID() == id) + return (void *)itm->Data(); + } + return 0x0; +} + +int snap_num_items(int snapid) +{ + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + if(!snapshots[snapid]) + return 0; + return snapshots[snapid]->m_pSnap->m_NumItems; +} + +/* ------ time functions ------ */ +float client_intratick() { return intratick; } +float client_predintratick() { return predintratick; } +float client_ticktime() { return ticktime; } +int client_tick() { return current_tick; } +int client_prevtick() { return prev_tick; } +int client_predtick() { return current_predtick; } +int client_tickspeed() { return SERVER_TICK_SPEED; } +float client_frametime() { return frametime; } +float client_localtime() { return (time_get()-local_start_time)/(float)(time_freq()); } + +/* ----- send functions ----- */ +int client_send_msg() +{ + const MSG_INFO *pInfo = msg_get_info(); + CNetChunk Packet; + + if(!pInfo) + return -1; + + if(client_state() == CLIENTSTATE_OFFLINE) + return 0; + + mem_zero(&Packet, sizeof(CNetChunk)); + + Packet.m_ClientID = 0; + Packet.m_pData = pInfo->data; + Packet.m_DataSize = pInfo->size; + + if(pInfo->flags&MSGFLAG_VITAL) + Packet.m_Flags |= NETSENDFLAG_VITAL; + if(pInfo->flags&MSGFLAG_FLUSH) + Packet.m_Flags |= NETSENDFLAG_FLUSH; + + if(pInfo->flags&MSGFLAG_RECORD) + { + if(demorec_isrecording()) + demorec_record_message(Packet.m_pData, Packet.m_DataSize); + } + + if(!(pInfo->flags&MSGFLAG_NOSEND)) + m_NetClient.Send(&Packet); + return 0; +} + +static void client_send_info() +{ + msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_string(modc_net_version(), 128); + msg_pack_string(config.player_name, 128); + msg_pack_string(config.clan_name, 128); + msg_pack_string(config.password, 128); + msg_pack_end(); + client_send_msg(); +} + + +static void client_send_entergame() +{ + msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_end(); + client_send_msg(); +} + +static void client_send_ready() +{ + msg_pack_start_system(NETMSG_READY, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_end(); + client_send_msg(); +} + +int client_rcon_authed() +{ + return rcon_authed; +} + +void client_rcon_auth(const char *name, const char *password) +{ + msg_pack_start_system(NETMSG_RCON_AUTH, MSGFLAG_VITAL); + msg_pack_string(name, 32); + msg_pack_string(password, 32); + msg_pack_end(); + client_send_msg(); +} + +void client_rcon(const char *cmd) +{ + msg_pack_start_system(NETMSG_RCON_CMD, MSGFLAG_VITAL); + msg_pack_string(cmd, 256); + msg_pack_end(); + client_send_msg(); +} + +int client_connection_problems() +{ + return m_NetClient.GotProblems(); +} + +void client_direct_input(int *input, int size) +{ + int i; + msg_pack_start_system(NETMSG_INPUT, 0); + msg_pack_int(ack_game_tick); + msg_pack_int(current_predtick); + msg_pack_int(size); + + for(i = 0; i < size/4; i++) + msg_pack_int(input[i]); + + msg_pack_end(); + client_send_msg(); +} + + +static void client_send_input() +{ + int64 now = time_get(); + int i, size; + + if(current_predtick <= 0) + return; + + /* fetch input */ + size = modc_snap_input(inputs[current_input].data); + + if(!size) + return; + + /* pack input */ + msg_pack_start_system(NETMSG_INPUT, MSGFLAG_FLUSH); + msg_pack_int(ack_game_tick); + msg_pack_int(current_predtick); + msg_pack_int(size); + + inputs[current_input].tick = current_predtick; + inputs[current_input].predicted_time = st_get(&predicted_time, now); + inputs[current_input].time = now; + + /* pack it */ + for(i = 0; i < size/4; i++) + msg_pack_int(inputs[current_input].data[i]); + + current_input++; + current_input%=200; + + msg_pack_end(); + client_send_msg(); +} + +const char *client_latestversion() +{ + return versionstr; +} + +/* TODO: OPT: do this alot smarter! */ +int *client_get_input(int tick) +{ + int i; + int best = -1; + for(i = 0; i < 200; i++) + { + if(inputs[i].tick <= tick && (best == -1 || inputs[best].tick < inputs[i].tick)) + best = i; + } + + if(best != -1) + return (int *)inputs[best].data; + return 0; +} + +/* ------ state handling ----- */ +static int state = CLIENTSTATE_OFFLINE; +int client_state() { return state; } +static void client_set_state(int s) +{ + int old = state; + if(config.debug) + dbg_msg("client", "state change. last=%d current=%d", state, s); + state = s; + if(old != s) + modc_statechange(state, old); +} + +/* called when the map is loaded and we should init for a new round */ +static void client_on_enter_game() +{ + /* reset input */ + int i; + for(i = 0; i < 200; i++) + inputs[i].tick = -1; + current_input = 0; + + /* reset snapshots */ + snapshots[SNAP_CURRENT] = 0; + snapshots[SNAP_PREV] = 0; + snapshot_storage.PurgeAll(); + recived_snapshots = 0; + snapshot_parts = 0; + current_predtick = 0; + current_recv_tick = 0; +} + +void client_entergame() +{ + if(state == CLIENTSTATE_DEMOPLAYBACK) + return; + + /* now we will wait for two snapshots */ + /* to finish the connection */ + client_send_entergame(); + client_on_enter_game(); +} + +void client_connect(const char *server_address_str) +{ + char buf[512]; + const char *port_str = 0; + int k; + int port = 8303; + + client_disconnect(); + + dbg_msg("client", "connecting to '%s'", server_address_str); + + //client_serverinfo_request(); + str_copy(buf, server_address_str, sizeof(buf)); + + for(k = 0; buf[k]; k++) + { + if(buf[k] == ':') + { + port_str = &(buf[k+1]); + buf[k] = 0; + break; + } + } + + if(port_str) + port = atoi(port_str); + + /* TODO: IPv6 support */ + if(net_host_lookup(buf, &server_address, NETTYPE_IPV4) != 0) + dbg_msg("client", "could not find the address of %s, connecting to localhost", buf); + + rcon_authed = 0; + server_address.port = port; + m_NetClient.Connect(&server_address); + client_set_state(CLIENTSTATE_CONNECTING); + + graph_init(&inputtime_margin_graph, -150.0f, 150.0f); + graph_init(&gametime_margin_graph, -150.0f, 150.0f); +} + +void client_disconnect_with_reason(const char *reason) +{ + /* stop demo playback */ + demorec_playback_stop(); + + /* */ + rcon_authed = 0; + m_NetClient.Disconnect(reason); + client_set_state(CLIENTSTATE_OFFLINE); + map_unload(); + + /* disable all downloads */ + mapdownload_chunk = 0; + if(mapdownload_file) + io_close(mapdownload_file); + mapdownload_file = 0; + mapdownload_crc = 0; + mapdownload_totalsize = -1; + mapdownload_amount = 0; + + /* clear the current server info */ + mem_zero(¤t_server_info, sizeof(current_server_info)); + mem_zero(&server_address, sizeof(server_address)); + + /* clear snapshots */ + snapshots[SNAP_CURRENT] = 0; + snapshots[SNAP_PREV] = 0; + recived_snapshots = 0; +} + +void client_disconnect() +{ + client_disconnect_with_reason(0); +} + + +void client_serverinfo(SERVER_INFO *serverinfo) +{ + mem_copy(serverinfo, ¤t_server_info, sizeof(current_server_info)); +} + +void client_serverinfo_request() +{ + mem_zero(¤t_server_info, sizeof(current_server_info)); + current_server_info_requesttime = 0; +} + +static int client_load_data() +{ + debug_font = Graphics()->LoadTexture("debug_font.png", IMG_AUTO, TEXLOAD_NORESAMPLE); + return 1; +} + +extern int snapshot_data_rate[0xffff]; +extern int snapshot_data_updates[0xffff]; + +const char *modc_getitemname(int type); + +static void client_debug_render() +{ + static NETSTATS prev, current; + static int64 last_snap = 0; + static float frametime_avg = 0; + int64 now = time_get(); + char buffer[512]; + + if(!config.debug) + return; + + //m_pGraphics->BlendNormal(); + Graphics()->TextureSet(debug_font); + Graphics()->MapScreen(0,0,Graphics()->ScreenWidth(),Graphics()->ScreenHeight()); + + if(time_get()-last_snap > time_freq()) + { + last_snap = time_get(); + prev = current; + net_stats(¤t); + } + + /* + eth = 14 + ip = 20 + udp = 8 + total = 42 + */ + frametime_avg = frametime_avg*0.9f + frametime*0.1f; + str_format(buffer, sizeof(buffer), "ticks: %8d %8d mem %dk %d gfxmem: N/A fps: %3d", + current_tick, current_predtick, + mem_stats()->allocated/1024, + mem_stats()->total_allocations, + /*gfx_memory_usage()/1024, */ // TODO: Refactor: Reenable this + (int)(1.0f/frametime_avg)); + Graphics()->QuadsText(2, 2, 16, 1,1,1,1, buffer); + + + { + int send_packets = (current.sent_packets-prev.sent_packets); + int send_bytes = (current.sent_bytes-prev.sent_bytes); + int send_total = send_bytes + send_packets*42; + int recv_packets = (current.recv_packets-prev.recv_packets); + int recv_bytes = (current.recv_bytes-prev.recv_bytes); + int recv_total = recv_bytes + recv_packets*42; + + if(!send_packets) send_packets++; + if(!recv_packets) recv_packets++; + str_format(buffer, sizeof(buffer), "send: %3d %5d+%4d=%5d (%3d kbps) avg: %5d\nrecv: %3d %5d+%4d=%5d (%3d kbps) avg: %5d", + send_packets, send_bytes, send_packets*42, send_total, (send_total*8)/1024, send_bytes/send_packets, + recv_packets, recv_bytes, recv_packets*42, recv_total, (recv_total*8)/1024, recv_bytes/recv_packets); + Graphics()->QuadsText(2, 14, 16, 1,1,1,1, buffer); + } + + /* render rates */ + { + int y = 0; + int i; + for(i = 0; i < 256; i++) + { + if(snapshot_data_rate[i]) + { + str_format(buffer, sizeof(buffer), "%4d %20s: %8d %8d %8d", i, modc_getitemname(i), snapshot_data_rate[i]/8, snapshot_data_updates[i], + (snapshot_data_rate[i]/snapshot_data_updates[i])/8); + Graphics()->QuadsText(2, 100+y*12, 16, 1,1,1,1, buffer); + y++; + } + } + } + + str_format(buffer, sizeof(buffer), "pred: %d ms %3.2f", + (int)((st_get(&predicted_time, now)-st_get(&game_time, now))*1000/(float)time_freq()), + predicted_time.adjustspeed[1]); + Graphics()->QuadsText(2, 70, 16, 1,1,1,1, buffer); + + /* render graphs */ + if(config.dbg_graphs) + { + //Graphics()->MapScreen(0,0,400.0f,300.0f); + float w = Graphics()->ScreenWidth()/4.0f; + float h = Graphics()->ScreenHeight()/6.0f; + float sp = Graphics()->ScreenWidth()/100.0f; + float x = Graphics()->ScreenWidth()-w-sp; + + graph_scale_max(&fps_graph); + graph_scale_min(&fps_graph); + graph_render(&fps_graph, x, sp*5, w, h, "FPS"); + graph_render(&inputtime_margin_graph, x, sp*5+h+sp, w, h, "Prediction Margin"); + graph_render(&gametime_margin_graph, x, sp*5+h+sp+h+sp, w, h, "Gametime Margin"); + } +} + +void client_quit() +{ + client_set_state(CLIENTSTATE_QUITING); +} + +const char *client_error_string() +{ + return m_NetClient.ErrorString(); +} + +static void client_render() +{ + if(config.gfx_clear) + Graphics()->Clear(1,1,0); + + modc_render(); + client_debug_render(); +} + +static const char *client_load_map(const char *name, const char *filename, int wanted_crc) +{ + static char errormsg[128]; + DATAFILE *df; + int crc; + + client_set_state(CLIENTSTATE_LOADING); + + df = datafile_load(filename); + if(!df) + { + str_format(errormsg, sizeof(errormsg), "map '%s' not found", filename); + return errormsg; + } + + /* get the crc of the map */ + crc = datafile_crc(filename); + if(crc != wanted_crc) + { + datafile_unload(df); + str_format(errormsg, sizeof(errormsg), "map differs from the server. %08x != %08x", crc, wanted_crc); + return errormsg; + } + + // stop demo recording if we loaded a new map + demorec_record_stop(); + + dbg_msg("client", "loaded map '%s'", filename); + recived_snapshots = 0; + map_set(df); + + str_copy(current_map, name, sizeof(current_map)); + current_map_crc = crc; + + return NULL; +} + +static const char *client_load_map_search(const char *mapname, int wanted_crc) +{ + const char *error = 0; + char buf[512]; + dbg_msg("client", "loading map, map=%s wanted crc=%08x", mapname, wanted_crc); + client_set_state(CLIENTSTATE_LOADING); + + /* try the normal maps folder */ + str_format(buf, sizeof(buf), "maps/%s.map", mapname); + error = client_load_map(mapname, buf, wanted_crc); + if(!error) + return error; + + /* try the downloaded maps */ + str_format(buf, sizeof(buf), "downloadedmaps/%s_%8x.map", mapname, wanted_crc); + error = client_load_map(mapname, buf, wanted_crc); + return error; +} + +static int player_score_comp(const void *a, const void *b) +{ + SERVER_INFO_PLAYER *p0 = (SERVER_INFO_PLAYER *)a; + SERVER_INFO_PLAYER *p1 = (SERVER_INFO_PLAYER *)b; + if(p0->score == p1->score) + return 0; + if(p0->score < p1->score) + return 1; + return -1; +} + +static void client_process_packet(CNetChunk *pPacket) +{ + if(pPacket->m_ClientID == -1) + { + /* connectionlesss */ + if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) && + memcmp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0) + { + unsigned char *versiondata = (unsigned char*)pPacket->m_pData + sizeof(VERSIONSRV_VERSION); + int version_match = !memcmp(versiondata, VERSION_DATA, sizeof(VERSION_DATA)); + + dbg_msg("client/version", "version does %s (%d.%d.%d)", + version_match ? "match" : "NOT match", + versiondata[1], versiondata[2], versiondata[3]); + + /* assume version is out of date when version-data doesn't match */ + if (!version_match) + { + sprintf(versionstr, "%d.%d.%d", versiondata[1], versiondata[2], versiondata[3]); + } + } + + if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_LIST) && + memcmp(pPacket->m_pData, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) + { + int size = pPacket->m_DataSize-sizeof(SERVERBROWSE_LIST); + int num = size/sizeof(MASTERSRV_ADDR); + MASTERSRV_ADDR *addrs = (MASTERSRV_ADDR *)((char*)pPacket->m_pData+sizeof(SERVERBROWSE_LIST)); + int i; + + for(i = 0; i < num; i++) + { + NETADDR addr; + + /* convert address */ + mem_zero(&addr, sizeof(addr)); + addr.type = NETTYPE_IPV4; + addr.ip[0] = addrs[i].ip[0]; + addr.ip[1] = addrs[i].ip[1]; + addr.ip[2] = addrs[i].ip[2]; + addr.ip[3] = addrs[i].ip[3]; + addr.port = (addrs[i].port[1]<<8) | addrs[i].port[0]; + + client_serverbrowse_set(&addr, BROWSESET_MASTER_ADD, -1, NULL); + } + } + + { + int packet_type = 0; + if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_INFO) && memcmp(pPacket->m_pData, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) + packet_type = 2; + + if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_OLD_INFO) && memcmp(pPacket->m_pData, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)) == 0) + packet_type = 1; + + if(packet_type) + { + /* we got ze info */ + CUnpacker up; + SERVER_INFO info = {0}; + int i; + int token = -1; + + up.Reset((unsigned char*)pPacket->m_pData+sizeof(SERVERBROWSE_INFO), pPacket->m_DataSize-sizeof(SERVERBROWSE_INFO)); + if(packet_type >= 2) + token = atol(up.GetString()); + str_copy(info.version, up.GetString(), sizeof(info.version)); + str_copy(info.name, up.GetString(), sizeof(info.name)); + str_copy(info.map, up.GetString(), sizeof(info.map)); + str_copy(info.gametype, up.GetString(), sizeof(info.gametype)); + info.flags = atol(up.GetString()); + info.progression = atol(up.GetString()); + info.num_players = atol(up.GetString()); + info.max_players = atol(up.GetString()); + str_format(info.address, sizeof(info.address), "%d.%d.%d.%d:%d", + pPacket->m_Address.ip[0], pPacket->m_Address.ip[1], pPacket->m_Address.ip[2], + pPacket->m_Address.ip[3], pPacket->m_Address.port); + + for(i = 0; i < info.num_players; i++) + { + str_copy(info.players[i].name, up.GetString(), sizeof(info.players[i].name)); + info.players[i].score = atol(up.GetString()); + } + + if(!up.Error()) + { + /* sort players */ + qsort(info.players, info.num_players, sizeof(*info.players), player_score_comp); + + if(net_addr_comp(&server_address, &pPacket->m_Address) == 0) + { + mem_copy(¤t_server_info, &info, sizeof(current_server_info)); + current_server_info.netaddr = server_address; + current_server_info_requesttime = -1; + } + else + { + if(packet_type == 2) + client_serverbrowse_set(&pPacket->m_Address, BROWSESET_TOKEN, token, &info); + else + client_serverbrowse_set(&pPacket->m_Address, BROWSESET_OLD_INTERNET, -1, &info); + } + } + } + } + } + else + { + int sys; + int msg = msg_unpack_start(pPacket->m_pData, pPacket->m_DataSize, &sys); + + if(sys) + { + /* system message */ + if(msg == NETMSG_MAP_CHANGE) + { + const char *map = msg_unpack_string(); + int map_crc = msg_unpack_int(); + const char *error = 0; + int i; + + if(msg_unpack_error()) + return; + + for(i = 0; map[i]; i++) /* protect the player from nasty map names */ + { + if(map[i] == '/' || map[i] == '\\') + error = "strange character in map name"; + } + + if(error) + client_disconnect_with_reason(error); + else + { + error = client_load_map_search(map, map_crc); + + if(!error) + { + dbg_msg("client/network", "loading done"); + client_send_ready(); + modc_connected(); + } + else + { + str_format(mapdownload_filename, sizeof(mapdownload_filename), "downloadedmaps/%s_%08x.map", map, map_crc); + + dbg_msg("client/network", "starting to download map to '%s'", mapdownload_filename); + + mapdownload_chunk = 0; + str_copy(mapdownload_name, map, sizeof(mapdownload_name)); + mapdownload_file = engine_openfile(mapdownload_filename, IOFLAG_WRITE); + mapdownload_crc = map_crc; + mapdownload_totalsize = -1; + mapdownload_amount = 0; + + msg_pack_start_system(NETMSG_REQUEST_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_int(mapdownload_chunk); + msg_pack_end(); + client_send_msg(); + + if(config.debug) + dbg_msg("client/network", "requested chunk %d", mapdownload_chunk); + } + } + } + else if(msg == NETMSG_MAP_DATA) + { + int last = msg_unpack_int(); + int total_size = msg_unpack_int(); + int size = msg_unpack_int(); + const unsigned char *data = msg_unpack_raw(size); + + /* check fior errors */ + if(msg_unpack_error() || size <= 0 || total_size <= 0 || !mapdownload_file) + return; + + io_write(mapdownload_file, data, size); + + mapdownload_totalsize = total_size; + mapdownload_amount += size; + + if(last) + { + const char *error; + dbg_msg("client/network", "download complete, loading map"); + + io_close(mapdownload_file); + mapdownload_file = 0; + mapdownload_amount = 0; + mapdownload_totalsize = -1; + + /* load map */ + error = client_load_map(mapdownload_name, mapdownload_filename, mapdownload_crc); + if(!error) + { + dbg_msg("client/network", "loading done"); + client_send_ready(); + modc_connected(); + } + else + client_disconnect_with_reason(error); + } + else + { + /* request new chunk */ + mapdownload_chunk++; + msg_pack_start_system(NETMSG_REQUEST_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_int(mapdownload_chunk); + msg_pack_end(); + client_send_msg(); + + if(config.debug) + dbg_msg("client/network", "requested chunk %d", mapdownload_chunk); + } + } + else if(msg == NETMSG_PING) + { + msg_pack_start_system(NETMSG_PING_REPLY, 0); + msg_pack_end(); + client_send_msg(); + } + else if(msg == NETMSG_RCON_AUTH_STATUS) + { + int result = msg_unpack_int(); + if(msg_unpack_error() == 0) + rcon_authed = result; + } + else if(msg == NETMSG_RCON_LINE) + { + const char *line = msg_unpack_string(); + if(msg_unpack_error() == 0) + { + /*dbg_msg("remote", "%s", line);*/ + modc_rcon_line(line); + } + } + else if(msg == NETMSG_PING_REPLY) + dbg_msg("client/network", "latency %.2f", (time_get() - ping_start_time)*1000 / (float)time_freq()); + else if(msg == NETMSG_INPUTTIMING) + { + int input_predtick = msg_unpack_int(); + int time_left = msg_unpack_int(); + + /* adjust our prediction time */ + int k; + int64 target = 0; + for(k = 0; k < 200; k++) + { + if(inputs[k].tick == input_predtick) + { + target = inputs[k].predicted_time + (time_get() - inputs[k].time); + target = target - (int64)(((time_left-prediction_margin)/1000.0f)*time_freq()); + //st_update(&predicted_time, ); + break; + } + } + + if(target) + st_update(&predicted_time, &inputtime_margin_graph, target, time_left, 1); + } + else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSINGLE || msg == NETMSG_SNAPEMPTY) + { + /*dbg_msg("client/network", "got snapshot"); */ + int num_parts = 1; + int part = 0; + int game_tick = msg_unpack_int(); + int delta_tick = game_tick-msg_unpack_int(); + int part_size = 0; + int crc = 0; + int complete_size = 0; + const char *data = 0; + + /* we are not allowed to process snapshot yet */ + if(client_state() < CLIENTSTATE_LOADING) + return; + + if(msg == NETMSG_SNAP) + { + num_parts = msg_unpack_int(); + part = msg_unpack_int(); + } + + if(msg != NETMSG_SNAPEMPTY) + { + crc = msg_unpack_int(); + part_size = msg_unpack_int(); + } + + data = (const char *)msg_unpack_raw(part_size); + + if(msg_unpack_error()) + return; + + if(game_tick >= current_recv_tick) + { + if(game_tick != current_recv_tick) + { + snapshot_parts = 0; + current_recv_tick = game_tick; + } + + /* TODO: clean this up abit */ + mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, data, part_size); + snapshot_parts |= 1<= 0) + { + int deltashot_size = snapshot_storage.Get(delta_tick, 0, &deltashot, 0); + + if(deltashot_size < 0) + { + /* couldn't find the delta snapshots that the server used */ + /* to compress this snapshot. force the server to resync */ + if(config.debug) + dbg_msg("client", "error, couldn't find the delta snapshot"); + + /* ack snapshot */ + /* TODO: combine this with the input message */ + ack_game_tick = -1; + return; + } + } + + /* decompress snapshot */ + deltadata = CSnapshot::EmptyDelta(); + deltasize = sizeof(int)*3; + + if(complete_size) + { + int intsize = intpack_decompress(snapshot_incomming_data, complete_size, tmpbuffer2); + + if(intsize < 0) /* failure during decompression, bail */ + return; + + deltadata = tmpbuffer2; + deltasize = intsize; + } + + /* unpack delta */ + purgetick = delta_tick; + snapsize = CSnapshot::UnpackDelta(deltashot, (CSnapshot*)tmpbuffer3, deltadata, deltasize); + if(snapsize < 0) + { + dbg_msg("client", "delta unpack failed!"); + return; + } + + if(msg != NETMSG_SNAPEMPTY && ((CSnapshot*)tmpbuffer3)->Crc() != crc) + { + if(config.debug) + { + dbg_msg("client", "snapshot crc error #%d - tick=%d wantedcrc=%d gotcrc=%d compressed_size=%d delta_tick=%d", + snapcrcerrors, game_tick, crc, ((CSnapshot*)tmpbuffer3)->Crc(), complete_size, delta_tick); + } + + snapcrcerrors++; + if(snapcrcerrors > 10) + { + /* to many errors, send reset */ + ack_game_tick = -1; + client_send_input(); + snapcrcerrors = 0; + } + return; + } + else + { + if(snapcrcerrors) + snapcrcerrors--; + } + + /* purge old snapshots */ + purgetick = delta_tick; + if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->m_Tick < purgetick) + purgetick = snapshots[SNAP_PREV]->m_Tick; + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->m_Tick < purgetick) + purgetick = snapshots[SNAP_PREV]->m_Tick; + snapshot_storage.PurgeUntil(purgetick); + + /* add new */ + snapshot_storage.Add(game_tick, time_get(), snapsize, (CSnapshot*)tmpbuffer3, 1); + + /* add snapshot to demo */ + if(demorec_isrecording()) + { + + /* write tick marker */ + /* + DEMOREC_TICKMARKER marker; + marker.tick = game_tick; + swap_endian(&marker, sizeof(int), sizeof(marker)/sizeof(int)); + demorec_record_write("TICK", sizeof(marker), &marker); + demorec_record_write("SNAP", snapsize, tmpbuffer3); + */ + + /* write snapshot */ + demorec_record_snapshot(game_tick, tmpbuffer3, snapsize); + } + + /* apply snapshot, cycle pointers */ + recived_snapshots++; + + current_recv_tick = game_tick; + + /* we got two snapshots until we see us self as connected */ + if(recived_snapshots == 2) + { + /* start at 200ms and work from there */ + st_init(&predicted_time, game_tick*time_freq()/50); + predicted_time.adjustspeed[1] = 1000.0f; + st_init(&game_time, (game_tick-1)*time_freq()/50); + snapshots[SNAP_PREV] = snapshot_storage.m_pFirst; + snapshots[SNAP_CURRENT] = snapshot_storage.m_pLast; + local_start_time = time_get(); + client_set_state(CLIENTSTATE_ONLINE); + } + + /* adjust game time */ + { + int64 now = st_get(&game_time, time_get()); + int64 tickstart = game_tick*time_freq()/50; + int64 time_left = (tickstart-now)*1000 / time_freq(); + /*st_update(&game_time, (game_tick-1)*time_freq()/50);*/ + st_update(&game_time, &gametime_margin_graph, (game_tick-1)*time_freq()/50, time_left, 0); + } + + /* ack snapshot */ + ack_game_tick = game_tick; + } + } + } + } + else + { + /* game message */ + if(demorec_isrecording()) + demorec_record_message(pPacket->m_pData, pPacket->m_DataSize); + /* demorec_record_write("MESG", pPacket->data_size, ); */ + + modc_message(msg); + } + } +} + +int client_mapdownload_amount() { return mapdownload_amount; } +int client_mapdownload_totalsize() { return mapdownload_totalsize; } + +static void client_pump_network() +{ + + m_NetClient.Update(); + + if(client_state() != CLIENTSTATE_DEMOPLAYBACK) + { + /* check for errors */ + if(client_state() != CLIENTSTATE_OFFLINE && m_NetClient.State() == NETSTATE_OFFLINE) + { + client_set_state(CLIENTSTATE_OFFLINE); + client_disconnect(); + dbg_msg("client", "offline error='%s'", m_NetClient.ErrorString()); + } + + /* */ + if(client_state() == CLIENTSTATE_CONNECTING && m_NetClient.State() == NETSTATE_ONLINE) + { + /* we switched to online */ + dbg_msg("client", "connected, sending info"); + client_set_state(CLIENTSTATE_LOADING); + client_send_info(); + } + } + + /* process packets */ + CNetChunk Packet; + while(m_NetClient.Recv(&Packet)) + client_process_packet(&Packet); +} + +static void client_democallback_snapshot(void *pData, int Size) +{ + /* update ticks, they could have changed */ + const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); + CSnapshotStorage::CHolder *temp; + current_tick = info->current_tick; + prev_tick = info->previous_tick; + + /* handle snapshots */ + temp = snapshots[SNAP_PREV]; + snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; + snapshots[SNAP_CURRENT] = temp; + + mem_copy(snapshots[SNAP_CURRENT]->m_pSnap, pData, Size); + mem_copy(snapshots[SNAP_CURRENT]->m_pAltSnap, pData, Size); + + modc_newsnapshot(); + /*modc_predict();*/ +} + +static void client_democallback_message(void *data, int size) +{ + int sys = 0; + int msg = msg_unpack_start(data, size, &sys); + if(!sys) + modc_message(msg); +} + + +const DEMOPLAYBACK_INFO *client_demoplayer_getinfo() +{ + static DEMOPLAYBACK_INFO ret; + const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); + ret.first_tick = info->first_tick; + ret.last_tick = info->last_tick; + ret.current_tick = info->current_tick; + ret.paused = info->paused; + ret.speed = info->speed; + return &ret; +} + +void client_demoplayer_setpos(float percent) +{ + demorec_playback_set(percent); +} + +void client_demoplayer_setspeed(float speed) +{ + demorec_playback_setspeed(speed); +} + +void client_demoplayer_setpause(int paused) +{ + if(paused) + demorec_playback_pause(); + else + demorec_playback_unpause(); +} + +static void client_update() +{ + if(client_state() == CLIENTSTATE_DEMOPLAYBACK) + { + demorec_playback_update(); + if(demorec_isplaying()) + { + /* update timers */ + const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); + current_tick = info->current_tick; + prev_tick = info->previous_tick; + intratick = info->intratick; + ticktime = info->ticktime; + } + else + { + /* disconnect on error */ + client_disconnect(); + } + } + else if(client_state() != CLIENTSTATE_OFFLINE && recived_snapshots >= 3) + { + /* switch snapshot */ + int repredict = 0; + int64 freq = time_freq(); + int64 now = st_get(&game_time, time_get()); + int64 pred_now = st_get(&predicted_time, time_get()); + + while(1) + { + CSnapshotStorage::CHolder *cur = snapshots[SNAP_CURRENT]; + int64 tickstart = (cur->m_Tick)*time_freq()/50; + + if(tickstart < now) + { + CSnapshotStorage::CHolder *next = snapshots[SNAP_CURRENT]->m_pNext; + if(next) + { + snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; + snapshots[SNAP_CURRENT] = next; + + /* set ticks */ + current_tick = snapshots[SNAP_CURRENT]->m_Tick; + prev_tick = snapshots[SNAP_PREV]->m_Tick; + + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) + { + modc_newsnapshot(); + repredict = 1; + } + } + else + break; + } + else + break; + } + + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) + { + int64 curtick_start = (snapshots[SNAP_CURRENT]->m_Tick)*time_freq()/50; + int64 prevtick_start = (snapshots[SNAP_PREV]->m_Tick)*time_freq()/50; + /*tg_add(&predicted_time_graph, pred_now, 0); */ + int prev_pred_tick = (int)(pred_now*50/time_freq()); + int new_pred_tick = prev_pred_tick+1; + static float last_predintra = 0; + + intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start); + ticktime = (now - prevtick_start) / (float)freq; /*(float)SERVER_TICK_SPEED);*/ + + curtick_start = new_pred_tick*time_freq()/50; + prevtick_start = prev_pred_tick*time_freq()/50; + predintratick = (pred_now - prevtick_start) / (float)(curtick_start-prevtick_start); + + if(new_pred_tick < snapshots[SNAP_PREV]->m_Tick-SERVER_TICK_SPEED || new_pred_tick > snapshots[SNAP_PREV]->m_Tick+SERVER_TICK_SPEED) + { + dbg_msg("client", "prediction time reset!"); + st_init(&predicted_time, snapshots[SNAP_CURRENT]->m_Tick*time_freq()/50); + } + + if(new_pred_tick > current_predtick) + { + last_predintra = predintratick; + current_predtick = new_pred_tick; + repredict = 1; + + /* send input */ + client_send_input(); + } + + last_predintra = predintratick; + } + + /* only do sane predictions */ + if(repredict) + { + if(current_predtick > current_tick && current_predtick < current_tick+50) + modc_predict(); + } + + /* fetch server info if we don't have it */ + if(client_state() >= CLIENTSTATE_LOADING && + current_server_info_requesttime >= 0 && + time_get() > current_server_info_requesttime) + { + client_serverbrowse_request(&server_address); + current_server_info_requesttime = time_get()+time_freq()*2; + } + } + + /* STRESS TEST: join the server again */ + if(config.dbg_stress) + { + static int64 action_taken = 0; + int64 now = time_get(); + if(client_state() == CLIENTSTATE_OFFLINE) + { + if(now > action_taken+time_freq()*2) + { + dbg_msg("stress", "reconnecting!"); + client_connect(config.dbg_stress_server); + action_taken = now; + } + } + else + { + /*if(now > action_taken+time_freq()*(10+config.dbg_stress)) + { + dbg_msg("stress", "disconnecting!"); + client_disconnect(); + action_taken = now; + }*/ + } + } + + /* pump the network */ + client_pump_network(); + + /* update the maser server registry */ + mastersrv_update(); + + /* update the server browser */ + client_serverbrowse_update(); +} + + +static void client_versionupdate() +{ + static int state = 0; + static HOSTLOOKUP version_serveraddr; + + if(state == 0) + { + engine_hostlookup(&version_serveraddr, config.cl_version_server); + state++; + } + else if(state == 1) + { + if(jobs_status(&version_serveraddr.job) == JOBSTATUS_DONE) + { + CNetChunk Packet; + + mem_zero(&Packet, sizeof(Packet)); + + version_serveraddr.addr.port = VERSIONSRV_PORT; + + Packet.m_ClientID = -1; + Packet.m_Address = version_serveraddr.addr; + Packet.m_pData = VERSIONSRV_GETVERSION; + Packet.m_DataSize = sizeof(VERSIONSRV_GETVERSION); + Packet.m_Flags = NETSENDFLAG_CONNLESS; + + m_NetClient.Send(&Packet); + state++; + } + } +} + +static void client_run() +{ + NETADDR bindaddr; + int64 reporttime = time_get(); + int64 reportinterval = time_freq()*1; + + static PERFORMACE_INFO rootscope = {"root", 0}; + perf_start(&rootscope); + + local_start_time = time_get(); + snapshot_parts = 0; + + /* init graphics and sound */ + m_pGraphics = CreateEngineGraphics(); + if(m_pGraphics->Init() != 0) + return; + + /* start refreshing addresses while we load */ + mastersrv_refresh_addresses(); + + /* init the editor */ + m_pEditor = CreateEditor(); + m_pEditor->Init(m_pGraphics); + + /* sound is allowed to fail */ + snd_init(); + + /* load data */ + if(!client_load_data()) + return; + + /* init the mod */ + m_pGameClient = CreateGameClient(&m_Client); + modc_init(); + dbg_msg("client", "version %s", modc_net_version()); + + /* open socket */ + mem_zero(&bindaddr, sizeof(bindaddr)); + m_NetClient.Open(bindaddr, 0); + + /* connect to the server if wanted */ + /* + if(config.cl_connect[0] != 0) + client_connect(config.cl_connect); + config.cl_connect[0] = 0; + */ + + /* */ + graph_init(&fps_graph, 0.0f, 200.0f); + + /* never start with the editor */ + config.cl_editor = 0; + + inp_mouse_mode_relative(); + + while (1) + { + static PERFORMACE_INFO rootscope = {"root", 0}; + int64 frame_start_time = time_get(); + frames++; + + perf_start(&rootscope); + + /* */ + client_versionupdate(); + + /* handle pending connects */ + if(cmd_connect[0]) + { + client_connect(cmd_connect); + cmd_connect[0] = 0; + } + + /* update input */ + { + static PERFORMACE_INFO scope = {"inp_update", 0}; + perf_start(&scope); + inp_update(); + perf_end(); + } + + /* update sound */ + { + static PERFORMACE_INFO scope = {"snd_update", 0}; + perf_start(&scope); + snd_update(); + perf_end(); + } + + /* release focus */ + if(!Graphics()->WindowActive()) + { + if(window_must_refocus == 0) + inp_mouse_mode_absolute(); + window_must_refocus = 1; + } + else if (config.dbg_focus && inp_key_pressed(KEY_ESCAPE)) + { + inp_mouse_mode_absolute(); + window_must_refocus = 1; + } + + /* refocus */ + if(window_must_refocus && Graphics()->WindowActive()) + { + if(window_must_refocus < 3) + { + inp_mouse_mode_absolute(); + window_must_refocus++; + } + + if(inp_key_pressed(KEY_MOUSE_1)) + { + inp_mouse_mode_relative(); + window_must_refocus = 0; + } + } + + /* panic quit button */ + if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_pressed('q')) + break; + + if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('d')) + config.debug ^= 1; + + if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('g')) + config.dbg_graphs ^= 1; + + if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('e')) + { + config.cl_editor = config.cl_editor^1; + inp_mouse_mode_relative(); + } + + /* + if(!gfx_window_open()) + break; + */ + + /* render */ + if(config.cl_editor) + { + client_update(); + m_pEditor->UpdateAndRender(); + m_pGraphics->Swap(); + } + else + { + { + static PERFORMACE_INFO scope = {"client_update", 0}; + perf_start(&scope); + client_update(); + perf_end(); + } + + if(config.dbg_stress) + { + if((frames%10) == 0) + { + client_render(); + m_pGraphics->Swap(); + } + } + else + { + { + static PERFORMACE_INFO scope = {"client_render", 0}; + perf_start(&scope); + client_render(); + perf_end(); + } + + { + static PERFORMACE_INFO scope = {"gfx_swap", 0}; + perf_start(&scope); + m_pGraphics->Swap(); + perf_end(); + } + } + } + + perf_end(); + + + /* check conditions */ + if(client_state() == CLIENTSTATE_QUITING) + break; + + /* be nice */ + if(config.dbg_stress) + thread_sleep(5); + else if(config.cl_cpu_throttle || !Graphics()->WindowActive()) + thread_sleep(1); + + if(config.dbg_hitch) + { + thread_sleep(config.dbg_hitch); + config.dbg_hitch = 0; + } + + if(reporttime < time_get()) + { + if(0 && config.debug) + { + dbg_msg("client/report", "fps=%.02f (%.02f %.02f) netstate=%d", + frames/(float)(reportinterval/time_freq()), + 1.0f/frametime_high, + 1.0f/frametime_low, + m_NetClient.State()); + } + frametime_low = 1; + frametime_high = 0; + frames = 0; + reporttime += reportinterval; + perf_next(); + + if(config.dbg_pref) + perf_dump(&rootscope); + } + + /* update frametime */ + frametime = (time_get()-frame_start_time)/(float)time_freq(); + if(frametime < frametime_low) + frametime_low = frametime; + if(frametime > frametime_high) + frametime_high = frametime; + + graph_add(&fps_graph, 1.0f/frametime, 1,1,1); + } + + modc_shutdown(); + client_disconnect(); + + m_pGraphics->Shutdown(); + snd_shutdown(); +} + +void gfx_swap() +{ + m_pGraphics->Swap(); +} + +static void con_connect(void *result, void *user_data) +{ + str_copy(cmd_connect, console_arg_string(result, 0), sizeof(cmd_connect)); +} + +static void con_disconnect(void *result, void *user_data) +{ + client_disconnect(); +} + +static void con_quit(void *result, void *user_data) +{ + client_quit(); +} + +static void con_ping(void *result, void *user_data) +{ + msg_pack_start_system(NETMSG_PING, 0); + msg_pack_end(); + client_send_msg(); + ping_start_time = time_get(); +} + +static void con_screenshot(void *result, void *user_data) +{ + Graphics()->TakeScreenshot(); +} + +static void con_rcon(void *result, void *user_data) +{ + client_rcon(console_arg_string(result, 0)); +} + +static void con_rcon_auth(void *result, void *user_data) +{ + client_rcon_auth("", console_arg_string(result, 0)); +} + +static void con_addfavorite(void *result, void *user_data) +{ + NETADDR addr; + if(net_addr_from_str(&addr, console_arg_string(result, 0)) == 0) + client_serverbrowse_addfavorite(addr); +} + +const char *client_demoplayer_play(const char *filename) +{ + int crc; + const char *error; + client_disconnect(); + m_NetClient.ResetErrorString(); + + /* try to start playback */ + demorec_playback_registercallbacks(client_democallback_snapshot, client_democallback_message); + + if(demorec_playback_load(filename)) + return "error loading demo"; + + /* load map */ + crc = (demorec_playback_info()->header.crc[0]<<24)| + (demorec_playback_info()->header.crc[1]<<16)| + (demorec_playback_info()->header.crc[2]<<8)| + (demorec_playback_info()->header.crc[3]); + error = client_load_map_search(demorec_playback_info()->header.map, crc); + if(error) + { + client_disconnect_with_reason(error); + return error; + } + + modc_connected(); + + /* setup buffers */ + mem_zero(demorec_snapshotdata, sizeof(demorec_snapshotdata)); + + snapshots[SNAP_CURRENT] = &demorec_snapshotholders[SNAP_CURRENT]; + snapshots[SNAP_PREV] = &demorec_snapshotholders[SNAP_PREV]; + + snapshots[SNAP_CURRENT]->m_pSnap = (CSnapshot *)demorec_snapshotdata[SNAP_CURRENT][0]; + snapshots[SNAP_CURRENT]->m_pAltSnap = (CSnapshot *)demorec_snapshotdata[SNAP_CURRENT][1]; + snapshots[SNAP_CURRENT]->m_SnapSize = 0; + snapshots[SNAP_CURRENT]->m_Tick = -1; + + snapshots[SNAP_PREV]->m_pSnap = (CSnapshot *)demorec_snapshotdata[SNAP_PREV][0]; + snapshots[SNAP_PREV]->m_pAltSnap = (CSnapshot *)demorec_snapshotdata[SNAP_PREV][1]; + snapshots[SNAP_PREV]->m_SnapSize = 0; + snapshots[SNAP_PREV]->m_Tick = -1; + + /* enter demo playback state */ + client_set_state(CLIENTSTATE_DEMOPLAYBACK); + + demorec_playback_play(); + modc_entergame(); + + return 0; +} + +static void con_play(void *result, void *user_data) +{ + client_demoplayer_play(console_arg_string(result, 0)); +} + +static void con_record(void *result, void *user_data) +{ + if(state != CLIENTSTATE_ONLINE) + dbg_msg("demorec/record", "client is not online"); + else + { + char filename[512]; + str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(result, 0)); + demorec_record_start(filename, modc_net_version(), current_map, current_map_crc, "client"); + } +} + +static void con_stoprecord(void *result, void *user_data) +{ + demorec_record_stop(); +} + +static void con_serverdummy(void *result, void *user_data) +{ + dbg_msg("client", "this command is not available on the client"); +} + +static void client_register_commands() +{ + MACRO_REGISTER_COMMAND("quit", "", CFGFLAG_CLIENT, con_quit, 0x0, "Quit Teeworlds"); + MACRO_REGISTER_COMMAND("exit", "", CFGFLAG_CLIENT, con_quit, 0x0, "Quit Teeworlds"); + MACRO_REGISTER_COMMAND("connect", "s", CFGFLAG_CLIENT, con_connect, 0x0, "Connect to the specified host/ip"); + MACRO_REGISTER_COMMAND("disconnect", "", CFGFLAG_CLIENT, con_disconnect, 0x0, "Disconnect from the server"); + MACRO_REGISTER_COMMAND("ping", "", CFGFLAG_CLIENT, con_ping, 0x0, "Ping the current server"); + MACRO_REGISTER_COMMAND("screenshot", "", CFGFLAG_CLIENT, con_screenshot, 0x0, "Take a screenshot"); + MACRO_REGISTER_COMMAND("rcon", "r", CFGFLAG_CLIENT, con_rcon, 0x0, "Send specified command to rcon"); + MACRO_REGISTER_COMMAND("rcon_auth", "s", CFGFLAG_CLIENT, con_rcon_auth, 0x0, "Authenticate to rcon"); + + MACRO_REGISTER_COMMAND("play", "r", CFGFLAG_CLIENT, con_play, 0x0, "Play the file specified"); + MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_CLIENT, con_record, 0, "Record to the file"); + MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_CLIENT, con_stoprecord, 0, "Stop recording"); + + MACRO_REGISTER_COMMAND("add_favorite", "s", CFGFLAG_CLIENT, con_addfavorite, 0x0, "Add a server as a favorite"); + + /* register server dummy commands for tab completion */ + MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, con_serverdummy, 0, "Kick player with specified id"); + MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, con_serverdummy, 0, "Ban player with ip/id for x minutes"); + MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, con_serverdummy, 0, "Unban ip"); + MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, con_serverdummy, 0, "Show banlist"); + MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, con_serverdummy, 0, "List players"); + MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, con_serverdummy, 0, "Shut down"); + /*MACRO_REGISTER_COMMAND("record", "", CFGFLAG_SERVER, con_serverdummy, 0); + MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, con_serverdummy, 0);*/ +} + +void client_save_line(const char *line) +{ + engine_config_write_line(line); +} + +const char *client_user_directory() +{ + static char path[1024] = {0}; + fs_storage_path("Teeworlds", path, sizeof(path)); + return path; +} + +#if defined(CONF_PLATFORM_MACOSX) +int SDL_main(int argc, char **argv) +#else +int main(int argc, char **argv) +#endif +{ + /* init the engine */ + dbg_msg("client", "starting..."); + engine_init("Teeworlds"); + + /* register all console commands */ + client_register_commands(); + modc_console_init(); + + /* parse the command line arguments */ + engine_parse_arguments(argc, argv); + + /* execute config file */ + console_execute_file("settings.cfg"); + + /* run the client*/ + client_run(); + + /* write down the config and quit */ + if(engine_config_write_start() == 0) + { + config_save(); + client_serverbrowse_save(); + modc_save_config(); + engine_config_write_stop(); + } + + return 0; +} diff --git a/src/engine/client/ec_gfx.c b/src/engine/client/ec_gfx.c deleted file mode 100644 index 1ea9f407..00000000 --- a/src/engine/client/ec_gfx.c +++ /dev/null @@ -1,1020 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include - -#include "SDL.h" - -#ifdef CONF_FAMILY_WINDOWS - #define WIN32_LEAN_AND_MEAN - #include -#endif - -#ifdef CONF_PLATFORM_MACOSX - #include - #include -#else - #include - #include -#endif - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -/* compressed textures */ -#define GL_COMPRESSED_RGB_ARB 0x84ED -#define GL_COMPRESSED_RGBA_ARB 0x84EE -#define GL_COMPRESSED_ALPHA_ARB 0x84E9 - -#define TEXTURE_MAX_ANISOTROPY_EXT 0x84FE - -enum -{ - DRAWING_QUADS=1, - DRAWING_LINES=2 -}; - -/* */ -typedef struct { float x, y, z; } VEC3; -typedef struct { float u, v; } TEXCOORD; -typedef struct { float r, g, b, a; } COLOR; - -typedef struct -{ - VEC3 pos; - TEXCOORD tex; - COLOR color; -} VERTEX; - -const int vertex_buffer_size = 32*1024; -static VERTEX *vertices = 0; -static int num_vertices = 0; - -static int no_gfx = 0; - -static COLOR color[4]; -static TEXCOORD texture[4]; - -static int do_screenshot = 0; -static int render_enable = 1; - -static int screen_width = -1; -static int screen_height = -1; -static float rotation = 0; -static int drawing = 0; - -static float screen_x0 = 0; -static float screen_y0 = 0; -static float screen_x1 = 0; -static float screen_y1 = 0; - -static int invalid_texture = 0; - -typedef struct -{ - GLuint tex; - int memsize; - int flags; - int next; -} TEXTURE; - -enum -{ - MAX_TEXTURES = 1024*4 -}; - -static TEXTURE textures[MAX_TEXTURES]; -static int first_free_texture; -static int memory_usage = 0; - -static SDL_Surface *screen_surface; - -static const unsigned char null_texture_data[] = { - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, -}; - -static void flush() -{ - if(num_vertices == 0) - return; - - if(no_gfx) - { - num_vertices = 0; - return; - } - - - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glVertexPointer(3, GL_FLOAT, - sizeof(VERTEX), - (char*)vertices); - glTexCoordPointer(2, GL_FLOAT, - sizeof(VERTEX), - (char*)vertices + sizeof(float)*3); - glColorPointer(4, GL_FLOAT, - sizeof(VERTEX), - (char*)vertices + sizeof(float)*5); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - if(render_enable) - { - if(drawing == DRAWING_QUADS) - glDrawArrays(GL_QUADS, 0, num_vertices); - else if(drawing == DRAWING_LINES) - glDrawArrays(GL_LINES, 0, num_vertices); - } - - /* Reset pointer */ - num_vertices = 0; -} - -static void add_vertices(int count) -{ - num_vertices += count; - if((num_vertices + count) >= vertex_buffer_size) - flush(); -} - -static int try_init() -{ - const SDL_VideoInfo *info; - int flags = SDL_OPENGL; - - screen_width = config.gfx_screen_width; - screen_height = config.gfx_screen_height; - - info = SDL_GetVideoInfo(); - - /* set flags */ - flags = SDL_OPENGL; - flags |= SDL_GL_DOUBLEBUFFER; - flags |= SDL_HWPALETTE; - if(config.dbg_resizable) - flags |= SDL_RESIZABLE; - - if(info->hw_available) - flags |= SDL_HWSURFACE; - else - flags |= SDL_SWSURFACE; - - if(info->blit_hw) - flags |= SDL_HWACCEL; - - if(config.gfx_fullscreen) - flags |= SDL_FULLSCREEN; - - /* set gl attributes */ - if(config.gfx_fsaa_samples) - { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config.gfx_fsaa_samples); - } - else - { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); - } - - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, config.gfx_vsync); - - /* set caption */ - SDL_WM_SetCaption("Teeworlds", "Teeworlds"); - - /* create window */ - screen_surface = SDL_SetVideoMode(screen_width, screen_height, 0, flags); - if(screen_surface == NULL) - { - dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError()); - return -1; - } - - return 0; -} - -void gfx_font_init(); - -static int gfx_init_window() -{ - if(try_init() == 0) - return 0; - - /* try disabling fsaa */ - while(config.gfx_fsaa_samples) - { - config.gfx_fsaa_samples--; - - if(config.gfx_fsaa_samples) - dbg_msg("gfx", "lowering FSAA to %d and trying again", config.gfx_fsaa_samples); - else - dbg_msg("gfx", "disabling FSAA and trying again"); - - if(try_init() == 0) - return 0; - } - - /* try lowering the resolution */ - if(config.gfx_screen_width != 640 || config.gfx_screen_height != 480) - { - dbg_msg("gfx", "setting resolution to 640x480 and trying again"); - config.gfx_screen_width = 640; - config.gfx_screen_height = 480; - - if(try_init() == 0) - return 0; - } - - dbg_msg("gfx", "out of ideas. failed to init graphics"); - - return -1; -} - -int gfx_init() -{ - int i; - - if(config.dbg_stress) - no_gfx = 1; - - { - int systems = 0; - - if(!no_gfx) - systems |= SDL_INIT_VIDEO; - - if(config.snd_enable) - systems |= SDL_INIT_AUDIO; - - if(config.cl_eventthread) - systems |= SDL_INIT_EVENTTHREAD; - - if(SDL_Init(systems) < 0) - { - dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError()); - return -1; - } - } - - atexit(SDL_Quit); - - if(!no_gfx) - { - #ifdef CONF_FAMILY_WINDOWS - if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) - putenv("SDL_VIDEO_WINDOW_POS=8,27"); - #endif - - if(gfx_init_window() != 0) - return -1; - } - - /* Init vertices */ - if (vertices) - mem_free(vertices); - vertices = (VERTEX*)mem_alloc(sizeof(VERTEX) * vertex_buffer_size, 1); - num_vertices = 0; - - - /* - dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), - context.version_minor(), - context.version_rev());*/ - - - /* Set all z to -5.0f */ - for (i = 0; i < vertex_buffer_size; i++) - vertices[i].pos.z = -5.0f; - - /* init textures */ - first_free_texture = 0; - for(i = 0; i < MAX_TEXTURES; i++) - textures[i].next = i+1; - textures[MAX_TEXTURES-1].next = -1; - - if(!no_gfx) - { - SDL_ShowCursor(0); - gfx_mapscreen(0,0,config.gfx_screen_width, config.gfx_screen_height); - - /* set some default settings */ - glEnable(GL_BLEND); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glAlphaFunc(GL_GREATER, 0); - glEnable(GL_ALPHA_TEST); - glDepthMask(0); - - - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - - /* init input */ - inp_init(); - - /* create null texture, will get id=0 */ - invalid_texture = gfx_load_texture_raw(4,4,IMG_RGBA,null_texture_data,IMG_RGBA,TEXLOAD_NORESAMPLE); - dbg_msg("", "invalid texture id: %d %d", invalid_texture, textures[invalid_texture].tex); - - - /* font init */ - gfx_font_init(); - /* perform some tests */ - /* pixeltest_dotesting(); */ - - /*if(config.dbg_stress) - gfx_minimize();*/ - - return 0; -} - -float gfx_screenaspect() -{ - return gfx_screenwidth()/(float)gfx_screenheight(); -} - -int gfx_window_active() -{ - return SDL_GetAppState()&SDL_APPINPUTFOCUS; -} - -int gfx_window_open() -{ - return SDL_GetAppState()&SDL_APPACTIVE; -} - -VIDEO_MODE fakemodes[] = { - {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, - {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, - {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, - {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, - {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, - {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, - {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, - {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, - {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, - {2048,1536,8,8,8}, - - {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, - {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, - {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, - {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, - {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, - {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, - {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, - {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, - {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, - {2048,1536,5,6,5} -}; - -int gfx_get_video_modes(VIDEO_MODE *list, int maxcount) -{ - int num_modes = sizeof(fakemodes)/sizeof(VIDEO_MODE); - SDL_Rect **modes; - - if(config.gfx_display_all_modes) - { - int count = sizeof(fakemodes)/sizeof(VIDEO_MODE); - mem_copy(list, fakemodes, sizeof(fakemodes)); - if(maxcount < count) - count = maxcount; - return count; - } - - /* TODO: fix this code on osx or windows */ - - modes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN); - if(modes == NULL) - { - /* no modes */ - num_modes = 0; - } - else if(modes == (SDL_Rect**)-1) - { - /* all modes */ - } - else - { - int i; - num_modes = 0; - for(i = 0; modes[i]; ++i) - { - if(num_modes == maxcount) - break; - list[num_modes].width = modes[i]->w; - list[num_modes].height = modes[i]->h; - list[num_modes].red = 8; - list[num_modes].green = 8; - list[num_modes].blue = 8; - num_modes++; - } - } - - return num_modes; -} - -int gfx_unload_texture(int index) -{ - if(index == invalid_texture) - return 0; - - if(index < 0) - return 0; - - glDeleteTextures(1, &textures[index].tex); - textures[index].next = first_free_texture; - memory_usage -= textures[index].memsize; - first_free_texture = index; - return 0; -} - -void gfx_blend_none() -{ - if(no_gfx) return; - glDisable(GL_BLEND); -} - -void gfx_blend_normal() -{ - if(no_gfx) return; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -} - -void gfx_blend_additive() -{ - if(no_gfx) return; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); -} - -int gfx_memory_usage() { return memory_usage; } - -static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset) -{ - return (data[(v*w+u)*4+offset]+ - data[(v*w+u+1)*4+offset]+ - data[((v+1)*w+u)*4+offset]+ - data[((v+1)*w+u+1)*4+offset])/4; -} - -int gfx_load_texture_raw(int w, int h, int format, const void *data, int store_format, int flags) -{ - int mipmap = 1; - unsigned char *texdata = (unsigned char *)data; - unsigned char *tmpdata = 0; - int oglformat = 0; - int store_oglformat = 0; - int tex = 0; - - /* don't waste memory on texture if we are stress testing */ - if(config.dbg_stress || no_gfx) - return invalid_texture; - - /* grab texture */ - tex = first_free_texture; - first_free_texture = textures[tex].next; - textures[tex].next = -1; - - /* resample if needed */ - if(!(flags&TEXLOAD_NORESAMPLE) && config.gfx_texture_quality==0) - { - if(w > 16 && h > 16 && format == IMG_RGBA) - { - unsigned char *tmpdata; - int c = 0; - int x, y; - - tmpdata = (unsigned char *)mem_alloc(w*h*4, 1); - - w/=2; - h/=2; - - for(y = 0; y < h; y++) - for(x = 0; x < w; x++, c++) - { - tmpdata[c*4] = sample(w*2, h*2, texdata, x*2,y*2, 0); - tmpdata[c*4+1] = sample(w*2, h*2, texdata, x*2,y*2, 1); - tmpdata[c*4+2] = sample(w*2, h*2, texdata, x*2,y*2, 2); - tmpdata[c*4+3] = sample(w*2, h*2, texdata, x*2,y*2, 3); - } - texdata = tmpdata; - } - } - - oglformat = GL_RGBA; - if(format == IMG_RGB) - oglformat = GL_RGB; - else if(format == IMG_ALPHA) - oglformat = GL_ALPHA; - - /* upload texture */ - if(config.gfx_texture_compression) - { - store_oglformat = GL_COMPRESSED_RGBA_ARB; - if(store_format == IMG_RGB) - store_oglformat = GL_COMPRESSED_RGB_ARB; - else if(store_format == IMG_ALPHA) - store_oglformat = GL_COMPRESSED_ALPHA_ARB; - } - else - { - store_oglformat = GL_RGBA; - if(store_format == IMG_RGB) - store_oglformat = GL_RGB; - else if(store_format == IMG_ALPHA) - store_oglformat = GL_ALPHA; - } - - glGenTextures(1, &textures[tex].tex); - glBindTexture(GL_TEXTURE_2D, textures[tex].tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - gluBuild2DMipmaps(GL_TEXTURE_2D, store_oglformat, w, h, oglformat, GL_UNSIGNED_BYTE, texdata); - - /* calculate memory usage */ - { - int pixel_size = 4; - if(store_format == IMG_RGB) - pixel_size = 3; - else if(store_format == IMG_ALPHA) - pixel_size = 1; - - textures[tex].memsize = w*h*pixel_size; - if(mipmap) - { - while(w > 2 && h > 2) - { - w>>=1; - h>>=1; - textures[tex].memsize += w*h*pixel_size; - } - } - } - - memory_usage += textures[tex].memsize; - mem_free(tmpdata); - return tex; -} - -/* simple uncompressed RGBA loaders */ -int gfx_load_texture(const char *filename, int store_format, int flags) -{ - int l = strlen(filename); - int id; - IMAGE_INFO img; - - if(l < 3) - return -1; - if(gfx_load_png(&img, filename)) - { - if (store_format == IMG_AUTO) - store_format = img.format; - - id = gfx_load_texture_raw(img.width, img.height, img.format, img.data, store_format, flags); - mem_free(img.data); - return id; - } - - return invalid_texture; -} - -int gfx_load_png(IMAGE_INFO *img, const char *filename) -{ - char completefilename[512]; - unsigned char *buffer; - png_t png; - - /* open file for reading */ - png_init(0,0); - - engine_getpath(completefilename, sizeof(completefilename), filename, IOFLAG_READ); - - if(png_open_file(&png, completefilename) != PNG_NO_ERROR) - { - dbg_msg("game/png", "failed to open file. filename='%s'", completefilename); - return 0; - } - - if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA)) - { - dbg_msg("game/png", "invalid format. filename='%s'", completefilename); - png_close_file(&png); - return 0; - } - - buffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); - png_get_data(&png, buffer); - png_close_file(&png); - - img->width = png.width; - img->height = png.height; - if(png.color_type == PNG_TRUECOLOR) - img->format = IMG_RGB; - else if(png.color_type == PNG_TRUECOLOR_ALPHA) - img->format = IMG_RGBA; - img->data = buffer; - return 1; -} - -void gfx_shutdown() -{ - if (vertices) - mem_free(vertices); - - /* TODO: SDL, is this correct? */ - SDL_Quit(); -} - -void gfx_screenshot() -{ - do_screenshot = 1; -} - - -extern int text_render_codepaths_usage[5]; - -void gfx_swap() -{ - /*dbg_msg("", "%d %d %d %d %d", - text_render_codepaths_usage[0], - text_render_codepaths_usage[1], - text_render_codepaths_usage[2], - text_render_codepaths_usage[3], - text_render_codepaths_usage[4]); - - text_render_codepaths_usage[0] = 0; - text_render_codepaths_usage[1] = 0; - text_render_codepaths_usage[2] = 0; - text_render_codepaths_usage[3] = 0; - text_render_codepaths_usage[4] = 0;*/ - - if(do_screenshot) - { - /* find filename */ - char filename[128]; - static int index = 1; - - for(; index < 1000; index++) - { - IOHANDLE io; - str_format(filename, sizeof(filename), "screenshots/screenshot%04d.png", index); - io = engine_openfile(filename, IOFLAG_READ); - if(io) - io_close(io); - else - break; - } - - gfx_screenshot_direct(filename); - - do_screenshot = 0; - } - - { - static PERFORMACE_INFO pscope = {"glfwSwapBuffers", 0}; - perf_start(&pscope); - SDL_GL_SwapBuffers(); - perf_end(); - } - - if(render_enable && config.gfx_finish) - glFinish(); -} - -void gfx_screenshot_direct(const char *filename) -{ - /* fetch image data */ - int y; - int w = screen_width; - int h = screen_height; - unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*4, 1); - unsigned char *temp_row = pixel_data+w*h*4; - glReadPixels(0,0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixel_data); - - /* flip the pixel because opengl works from bottom left corner */ - for(y = 0; y < h/2; y++) - { - mem_copy(temp_row, pixel_data+y*w*4, w*4); - mem_copy(pixel_data+y*w*4, pixel_data+(h-y-1)*w*4, w*4); - mem_copy(pixel_data+(h-y-1)*w*4, temp_row,w*4); - } - - /* find filename */ - { - char wholepath[1024]; - png_t png; - - engine_savepath(filename, wholepath, sizeof(wholepath)); - - /* save png */ - dbg_msg("client", "saved screenshot to '%s'", wholepath); - png_open_file_write(&png, wholepath); - png_set_data(&png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pixel_data); - png_close_file(&png); - } - - /* clean up */ - mem_free(pixel_data); -} - -int gfx_screenwidth() -{ - return screen_width; -} - -int gfx_screenheight() -{ - return screen_height; -} - -void gfx_texture_set(int slot) -{ - dbg_assert(drawing == 0, "called gfx_texture_set within begin"); - if(no_gfx) return; - if(slot == -1) - { - glDisable(GL_TEXTURE_2D); - } - else - { - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, textures[slot].tex); - } -} - -void gfx_clear(float r, float g, float b) -{ - if(no_gfx) return; - glClearColor(r,g,b,0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y) -{ - screen_x0 = tl_x; - screen_y0 = tl_y; - screen_x1 = br_x; - screen_y1 = br_y; - if(no_gfx) return; - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); -} - -void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y) -{ - *tl_x = screen_x0; - *tl_y = screen_y0; - *br_x = screen_x1; - *br_y = screen_y1; -} - -void gfx_quads_begin() -{ - dbg_assert(drawing == 0, "called quads_begin twice"); - drawing = DRAWING_QUADS; - - gfx_quads_setsubset(0,0,1,1); - gfx_quads_setrotation(0); - gfx_setcolor(1,1,1,1); -} - -void gfx_quads_end() -{ - dbg_assert(drawing == DRAWING_QUADS, "called quads_end without begin"); - flush(); - drawing = 0; -} - - -void gfx_quads_setrotation(float angle) -{ - dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setrotation without begin"); - rotation = angle; -} - -void gfx_setcolorvertex(int i, float r, float g, float b, float a) -{ - dbg_assert(drawing != 0, "called gfx_quads_setcolorvertex without begin"); - color[i].r = r; - color[i].g = g; - color[i].b = b; - color[i].a = a; -} - -void gfx_setcolor(float r, float g, float b, float a) -{ - dbg_assert(drawing != 0, "called gfx_quads_setcolor without begin"); - gfx_setcolorvertex(0, r, g, b, a); - gfx_setcolorvertex(1, r, g, b, a); - gfx_setcolorvertex(2, r, g, b, a); - gfx_setcolorvertex(3, r, g, b, a); -} - -void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v) -{ - dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setsubset without begin"); - - texture[0].u = tl_u; texture[1].u = br_u; - texture[0].v = tl_v; texture[1].v = tl_v; - - texture[3].u = tl_u; texture[2].u = br_u; - texture[3].v = br_v; texture[2].v = br_v; -} - -void gfx_quads_setsubset_free( - float x0, float y0, float x1, float y1, - float x2, float y2, float x3, float y3) -{ - texture[0].u = x0; texture[0].v = y0; - texture[1].u = x1; texture[1].v = y1; - texture[2].u = x2; texture[2].v = y2; - texture[3].u = x3; texture[3].v = y3; -} - - -static void rotate(VEC3 *center, VEC3 *point) -{ - float x = point->x - center->x; - float y = point->y - center->y; - point->x = x * cosf(rotation) - y * sinf(rotation) + center->x; - point->y = x * sinf(rotation) + y * cosf(rotation) + center->y; -} - -void gfx_quads_draw(float x, float y, float w, float h) -{ - gfx_quads_drawTL(x-w/2, y-h/2,w,h); -} - -void gfx_quads_drawTL(float x, float y, float width, float height) -{ - VEC3 center; - - dbg_assert(drawing == DRAWING_QUADS, "called quads_draw without begin"); - - center.x = x + width/2; - center.y = y + height/2; - center.z = 0; - - vertices[num_vertices].pos.x = x; - vertices[num_vertices].pos.y = y; - vertices[num_vertices].tex = texture[0]; - vertices[num_vertices].color = color[0]; - rotate(¢er, &vertices[num_vertices].pos); - - vertices[num_vertices + 1].pos.x = x+width; - vertices[num_vertices + 1].pos.y = y; - vertices[num_vertices + 1].tex = texture[1]; - vertices[num_vertices + 1].color = color[1]; - rotate(¢er, &vertices[num_vertices + 1].pos); - - vertices[num_vertices + 2].pos.x = x + width; - vertices[num_vertices + 2].pos.y = y+height; - vertices[num_vertices + 2].tex = texture[2]; - vertices[num_vertices + 2].color = color[2]; - rotate(¢er, &vertices[num_vertices + 2].pos); - - vertices[num_vertices + 3].pos.x = x; - vertices[num_vertices + 3].pos.y = y+height; - vertices[num_vertices + 3].tex = texture[3]; - vertices[num_vertices + 3].color = color[3]; - rotate(¢er, &vertices[num_vertices + 3].pos); - - add_vertices(4); -} - -void gfx_quads_draw_freeform( - float x0, float y0, float x1, float y1, - float x2, float y2, float x3, float y3) -{ - dbg_assert(drawing == DRAWING_QUADS, "called quads_draw_freeform without begin"); - - vertices[num_vertices].pos.x = x0; - vertices[num_vertices].pos.y = y0; - vertices[num_vertices].tex = texture[0]; - vertices[num_vertices].color = color[0]; - - vertices[num_vertices + 1].pos.x = x1; - vertices[num_vertices + 1].pos.y = y1; - vertices[num_vertices + 1].tex = texture[1]; - vertices[num_vertices + 1].color = color[1]; - - vertices[num_vertices + 2].pos.x = x3; - vertices[num_vertices + 2].pos.y = y3; - vertices[num_vertices + 2].tex = texture[3]; - vertices[num_vertices + 2].color = color[3]; - - vertices[num_vertices + 3].pos.x = x2; - vertices[num_vertices + 3].pos.y = y2; - vertices[num_vertices + 3].tex = texture[2]; - vertices[num_vertices + 3].color = color[2]; - - add_vertices(4); -} - -void gfx_quads_text(float x, float y, float size, float r, float g, float b, float a, const char *text) -{ - float startx = x; - - gfx_quads_begin(); - gfx_setcolor(r,g,b,a); - - while(*text) - { - char c = *text; - text++; - - if(c == '\n') - { - x = startx; - y += size; - } - else - { - gfx_quads_setsubset( - (c%16)/16.0f, - (c/16)/16.0f, - (c%16)/16.0f+1.0f/16.0f, - (c/16)/16.0f+1.0f/16.0f); - - gfx_quads_drawTL(x,y,size,size); - x += size/2; - } - } - - gfx_quads_end(); -} - -void gfx_lines_begin() -{ - dbg_assert(drawing == 0, "called begin twice"); - drawing = DRAWING_LINES; - gfx_setcolor(1,1,1,1); -} - -void gfx_lines_end() -{ - dbg_assert(drawing == DRAWING_LINES, "called end without begin"); - flush(); - drawing = 0; -} - -void gfx_lines_draw(float x0, float y0, float x1, float y1) -{ - dbg_assert(drawing == DRAWING_LINES, "called draw without begin"); - - vertices[num_vertices].pos.x = x0; - vertices[num_vertices].pos.y = y0; - vertices[num_vertices].tex = texture[0]; - vertices[num_vertices].color = color[0]; - - vertices[num_vertices + 1].pos.x = x1; - vertices[num_vertices + 1].pos.y = y1; - vertices[num_vertices + 1].tex = texture[1]; - vertices[num_vertices + 1].color = color[1]; - - add_vertices(2); -} - -void gfx_clip_enable(int x, int y, int w, int h) -{ - if(no_gfx) return; - glScissor(x, gfx_screenheight()-(y+h), w, h); - glEnable(GL_SCISSOR_TEST); -} - -void gfx_clip_disable() -{ - if(no_gfx) return; - glDisable(GL_SCISSOR_TEST); -} - -void gfx_minimize() -{ - SDL_WM_IconifyWindow(); -} - -void gfx_maximize() -{ - /* TODO: SDL */ -} diff --git a/src/engine/client/ec_gfx.cpp b/src/engine/client/ec_gfx.cpp new file mode 100644 index 00000000..5632581a --- /dev/null +++ b/src/engine/client/ec_gfx.cpp @@ -0,0 +1,992 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include + +#include "SDL.h" + +#ifdef CONF_FAMILY_WINDOWS + #define WIN32_LEAN_AND_MEAN + #include +#endif + +#ifdef CONF_PLATFORM_MACOSX + #include + #include +#else + #include + #include +#endif + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +/* compressed textures */ +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 + +#define TEXTURE_MAX_ANISOTROPY_EXT 0x84FE + + +void gfx_font_init(); + +VIDEO_MODE fakemodes[] = { + {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, + {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, + {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, + {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, + {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, + {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, + {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, + {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, + {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, + {2048,1536,8,8,8}, + + {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, + {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, + {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, + {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, + {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, + {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, + {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, + {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, + {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, + {2048,1536,5,6,5} +}; + +int gfx_get_video_modes(VIDEO_MODE *list, int maxcount) +{ + int num_modes = sizeof(fakemodes)/sizeof(VIDEO_MODE); + SDL_Rect **modes; + + if(config.gfx_display_all_modes) + { + int count = sizeof(fakemodes)/sizeof(VIDEO_MODE); + mem_copy(list, fakemodes, sizeof(fakemodes)); + if(maxcount < count) + count = maxcount; + return count; + } + + /* TODO: fix this code on osx or windows */ + + modes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN); + if(modes == NULL) + { + /* no modes */ + num_modes = 0; + } + else if(modes == (SDL_Rect**)-1) + { + /* all modes */ + } + else + { + int i; + num_modes = 0; + for(i = 0; modes[i]; ++i) + { + if(num_modes == maxcount) + break; + list[num_modes].width = modes[i]->w; + list[num_modes].height = modes[i]->h; + list[num_modes].red = 8; + list[num_modes].green = 8; + list[num_modes].blue = 8; + num_modes++; + } + } + + return num_modes; +} + + +#include "graphics.h" + +class CGraphics_OpenGL : public IEngineGraphics +{ +protected: + /* */ + typedef struct { float x, y, z; } CPoint; + typedef struct { float u, v; } CTexCoord; + typedef struct { float r, g, b, a; } CColor; + + typedef struct + { + CPoint m_Pos; + CTexCoord m_Tex; + CColor m_Color; + } CVertex; + + enum + { + MAX_VERTICES = 32*1024, + MAX_TEXTURES = 1024*4, + + DRAWING_QUADS=1, + DRAWING_LINES=2 + }; + + CVertex m_aVertices[MAX_VERTICES]; + int m_NumVertices; + + CColor m_aColor[4]; + CTexCoord m_aTexture[4]; + + bool m_RenderEnable; + + float m_Rotation; + int m_Drawing; + bool m_DoScreenshot; + + float m_ScreenX0; + float m_ScreenY0; + float m_ScreenX1; + float m_ScreenY1; + + int m_InvalidTexture; + + struct CTexture + { + GLuint tex; + int memsize; + int flags; + int next; + }; + + enum + { + + }; + + CTexture m_aTextures[MAX_TEXTURES]; + int m_FirstFreeTexture; + int m_TextureMemoryUsage; + + + void Flush() + { + if(m_NumVertices == 0) + return; + + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glVertexPointer(3, GL_FLOAT, + sizeof(CVertex), + (char*)m_aVertices); + glTexCoordPointer(2, GL_FLOAT, + sizeof(CVertex), + (char*)m_aVertices + sizeof(float)*3); + glColorPointer(4, GL_FLOAT, + sizeof(CVertex), + (char*)m_aVertices + sizeof(float)*5); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + if(m_RenderEnable) + { + if(m_Drawing == DRAWING_QUADS) + glDrawArrays(GL_QUADS, 0, m_NumVertices); + else if(m_Drawing == DRAWING_LINES) + glDrawArrays(GL_LINES, 0, m_NumVertices); + } + + /* Reset pointer */ + m_NumVertices = 0; + } + + void AddVertices(int count) + { + m_NumVertices += count; + if((m_NumVertices + count) >= MAX_VERTICES) + Flush(); + } + + void Rotate(CPoint *pCenter, CPoint *pPoint) + { + float x = pPoint->x - pCenter->x; + float y = pPoint->y - pCenter->y; + pPoint->x = x * cosf(m_Rotation) - y * sinf(m_Rotation) + pCenter->x; + pPoint->y = x * sinf(m_Rotation) + y * cosf(m_Rotation) + pCenter->y; + } + + + + + static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset) + { + return (data[(v*w+u)*4+offset]+ + data[(v*w+u+1)*4+offset]+ + data[((v+1)*w+u)*4+offset]+ + data[((v+1)*w+u+1)*4+offset])/4; + } +public: + CGraphics_OpenGL() + { + m_NumVertices = 0; + + m_ScreenX0 = 0; + m_ScreenY0 = 0; + m_ScreenX1 = 0; + m_ScreenY1 = 0; + + m_ScreenWidth = -1; + m_ScreenHeight = -1; + + m_Rotation = 0; + m_Drawing = 0; + m_InvalidTexture = 0; + + m_TextureMemoryUsage = 0; + + m_RenderEnable = true; + m_DoScreenshot = false; + } + + + virtual void ClipEnable(int x, int y, int w, int h) + { + //if(no_gfx) return; + glScissor(x, ScreenHeight()-(y+h), w, h); + glEnable(GL_SCISSOR_TEST); + } + + virtual void ClipDisable() + { + //if(no_gfx) return; + glDisable(GL_SCISSOR_TEST); + } + + + virtual void BlendNone() + { + glDisable(GL_BLEND); + } + + virtual void BlendNormal() + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + virtual void BlendAdditive() + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } + + //int gfx_memory_usage() { return m_MemoryUsage; } + + virtual void MapScreen(float tl_x, float tl_y, float br_x, float br_y) + { + m_ScreenX0 = tl_x; + m_ScreenY0 = tl_y; + m_ScreenX1 = br_x; + m_ScreenY1 = br_y; + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); + } + + virtual void GetScreen(float *tl_x, float *tl_y, float *br_x, float *br_y) + { + *tl_x = m_ScreenX0; + *tl_y = m_ScreenY0; + *br_x = m_ScreenX1; + *br_y = m_ScreenY1; + } + + virtual void LinesBegin() + { + dbg_assert(m_Drawing == 0, "called begin twice"); + m_Drawing = DRAWING_LINES; + SetColor(1,1,1,1); + } + + virtual void LinesEnd() + { + dbg_assert(m_Drawing == DRAWING_LINES, "called end without begin"); + Flush(); + m_Drawing = 0; + } + + virtual void LinesDraw(float x0, float y0, float x1, float y1) + { + dbg_assert(m_Drawing == DRAWING_LINES, "called draw without begin"); + + m_aVertices[m_NumVertices].m_Pos.x = x0; + m_aVertices[m_NumVertices].m_Pos.y = y0; + m_aVertices[m_NumVertices].m_Tex = m_aTexture[0]; + m_aVertices[m_NumVertices].m_Color = m_aColor[0]; + + m_aVertices[m_NumVertices + 1].m_Pos.x = x1; + m_aVertices[m_NumVertices + 1].m_Pos.y = y1; + m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1]; + m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1]; + + AddVertices(2); + } + + + + virtual int UnloadTexture(int Index) + { + if(Index == m_InvalidTexture) + return 0; + + if(Index < 0) + return 0; + + glDeleteTextures(1, &m_aTextures[Index].tex); + m_aTextures[Index].next = m_FirstFreeTexture; + m_TextureMemoryUsage -= m_aTextures[Index].memsize; + m_FirstFreeTexture = Index; + return 0; + } + + + virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) + { + int mipmap = 1; + unsigned char *texdata = (unsigned char *)pData; + unsigned char *tmpdata = 0; + int oglformat = 0; + int store_oglformat = 0; + int tex = 0; + + /* don't waste memory on texture if we are stress testing */ + if(config.dbg_stress) + return m_InvalidTexture; + + /* grab texture */ + tex = m_FirstFreeTexture; + m_FirstFreeTexture = m_aTextures[tex].next; + m_aTextures[tex].next = -1; + + /* resample if needed */ + if(!(Flags&TEXLOAD_NORESAMPLE) && config.gfx_texture_quality==0) + { + if(Width > 16 && Height > 16 && Format == IMG_RGBA) + { + unsigned char *tmpdata; + int c = 0; + int x, y; + + tmpdata = (unsigned char *)mem_alloc(Width*Height*4, 1); + + Width/=2; + Height/=2; + + for(y = 0; y < Height; y++) + for(x = 0; x < Width; x++, c++) + { + tmpdata[c*4] = sample(Width*2, Height*2, texdata, x*2,y*2, 0); + tmpdata[c*4+1] = sample(Width*2, Height*2, texdata, x*2,y*2, 1); + tmpdata[c*4+2] = sample(Width*2, Height*2, texdata, x*2,y*2, 2); + tmpdata[c*4+3] = sample(Width*2, Height*2, texdata, x*2,y*2, 3); + } + texdata = tmpdata; + } + } + + oglformat = GL_RGBA; + if(Format == IMG_RGB) + oglformat = GL_RGB; + else if(Format == IMG_ALPHA) + oglformat = GL_ALPHA; + + /* upload texture */ + if(config.gfx_texture_compression) + { + store_oglformat = GL_COMPRESSED_RGBA_ARB; + if(StoreFormat == IMG_RGB) + store_oglformat = GL_COMPRESSED_RGB_ARB; + else if(StoreFormat == IMG_ALPHA) + store_oglformat = GL_COMPRESSED_ALPHA_ARB; + } + else + { + store_oglformat = GL_RGBA; + if(StoreFormat == IMG_RGB) + store_oglformat = GL_RGB; + else if(StoreFormat == IMG_ALPHA) + store_oglformat = GL_ALPHA; + } + + glGenTextures(1, &m_aTextures[tex].tex); + glBindTexture(GL_TEXTURE_2D, m_aTextures[tex].tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + gluBuild2DMipmaps(GL_TEXTURE_2D, store_oglformat, Width, Height, oglformat, GL_UNSIGNED_BYTE, texdata); + + /* calculate memory usage */ + { + int pixel_size = 4; + if(StoreFormat == IMG_RGB) + pixel_size = 3; + else if(StoreFormat == IMG_ALPHA) + pixel_size = 1; + + m_aTextures[tex].memsize = Width*Height*pixel_size; + if(mipmap) + { + while(Width > 2 && Height > 2) + { + Width>>=1; + Height>>=1; + m_aTextures[tex].memsize += Width*Height*pixel_size; + } + } + } + + m_TextureMemoryUsage += m_aTextures[tex].memsize; + mem_free(tmpdata); + return tex; + } + + /* simple uncompressed RGBA loaders */ + virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags) + { + int l = strlen(pFilename); + int id; + IMAGE_INFO img; + + if(l < 3) + return -1; + if(LoadPNG(&img, pFilename)) + { + if (StoreFormat == IMG_AUTO) + StoreFormat = img.format; + + id = LoadTextureRaw(img.width, img.height, img.format, img.data, StoreFormat, Flags); + mem_free(img.data); + return id; + } + + return m_InvalidTexture; + } + + virtual int LoadPNG(IMAGE_INFO *pImg, const char *pFilename) + { + char aCompleteFilename[512]; + unsigned char *pBuffer; + png_t png; + + /* open file for reading */ + png_init(0,0); + + engine_getpath(aCompleteFilename, sizeof(aCompleteFilename), pFilename, IOFLAG_READ); + + if(png_open_file(&png, aCompleteFilename) != PNG_NO_ERROR) + { + dbg_msg("game/png", "failed to open file. filename='%s'", aCompleteFilename); + return 0; + } + + if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA)) + { + dbg_msg("game/png", "invalid format. filename='%s'", aCompleteFilename); + png_close_file(&png); + return 0; + } + + pBuffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); + png_get_data(&png, pBuffer); + png_close_file(&png); + + pImg->width = png.width; + pImg->height = png.height; + if(png.color_type == PNG_TRUECOLOR) + pImg->format = IMG_RGB; + else if(png.color_type == PNG_TRUECOLOR_ALPHA) + pImg->format = IMG_RGBA; + pImg->data = pBuffer; + return 1; + } + + void ScreenshotDirect(const char *filename) + { + /* fetch image data */ + int y; + int w = m_ScreenWidth; + int h = m_ScreenHeight; + unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*4, 1); + unsigned char *temp_row = pixel_data+w*h*4; + glReadPixels(0,0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixel_data); + + /* flip the pixel because opengl works from bottom left corner */ + for(y = 0; y < h/2; y++) + { + mem_copy(temp_row, pixel_data+y*w*4, w*4); + mem_copy(pixel_data+y*w*4, pixel_data+(h-y-1)*w*4, w*4); + mem_copy(pixel_data+(h-y-1)*w*4, temp_row,w*4); + } + + /* find filename */ + { + char wholepath[1024]; + png_t png; + + engine_savepath(filename, wholepath, sizeof(wholepath)); + + /* save png */ + dbg_msg("client", "saved screenshot to '%s'", wholepath); + png_open_file_write(&png, wholepath); + png_set_data(&png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pixel_data); + png_close_file(&png); + } + + /* clean up */ + mem_free(pixel_data); + } + + virtual void TextureSet(int TextureID) + { + dbg_assert(m_Drawing == 0, "called Graphics()->TextureSet within begin"); + if(TextureID == -1) + { + glDisable(GL_TEXTURE_2D); + } + else + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, m_aTextures[TextureID].tex); + } + } + + virtual void Clear(float r, float g, float b) + { + glClearColor(r,g,b,0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + virtual void QuadsBegin() + { + dbg_assert(m_Drawing == 0, "called quads_begin twice"); + m_Drawing = DRAWING_QUADS; + + QuadsSetSubset(0,0,1,1); + QuadsSetRotation(0); + SetColor(1,1,1,1); + } + + virtual void QuadsEnd() + { + dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_end without begin"); + Flush(); + m_Drawing = 0; + } + + virtual void QuadsSetRotation(float Angle) + { + dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetRotation without begin"); + m_Rotation = Angle; + } + + virtual void SetColorVertex(int i, float r, float g, float b, float a) + { + dbg_assert(m_Drawing != 0, "called gfx_quads_setcolorvertex without begin"); + m_aColor[i].r = r; + m_aColor[i].g = g; + m_aColor[i].b = b; + m_aColor[i].a = a; + } + + virtual void SetColor(float r, float g, float b, float a) + { + dbg_assert(m_Drawing != 0, "called gfx_quads_setcolor without begin"); + SetColorVertex(0, r, g, b, a); + SetColorVertex(1, r, g, b, a); + SetColorVertex(2, r, g, b, a); + SetColorVertex(3, r, g, b, a); + } + + virtual void QuadsSetSubset(float tl_u, float tl_v, float br_u, float br_v) + { + dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetSubset without begin"); + + m_aTexture[0].u = tl_u; m_aTexture[1].u = br_u; + m_aTexture[0].v = tl_v; m_aTexture[1].v = tl_v; + + m_aTexture[3].u = tl_u; m_aTexture[2].u = br_u; + m_aTexture[3].v = br_v; m_aTexture[2].v = br_v; + } + + virtual void QuadsSetSubsetFree( + float x0, float y0, float x1, float y1, + float x2, float y2, float x3, float y3) + { + m_aTexture[0].u = x0; m_aTexture[0].v = y0; + m_aTexture[1].u = x1; m_aTexture[1].v = y1; + m_aTexture[2].u = x2; m_aTexture[2].v = y2; + m_aTexture[3].u = x3; m_aTexture[3].v = y3; + } + + virtual void QuadsDraw(float x, float y, float w, float h) + { + QuadsDrawTL(x-w/2, y-h/2,w,h); + } + + virtual void QuadsDrawTL(float x, float y, float w, float h) + { + CPoint Center; + + dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw without begin"); + + Center.x = x + w/2; + Center.y = y + h/2; + Center.z = 0; + + m_aVertices[m_NumVertices].m_Pos.x = x; + m_aVertices[m_NumVertices].m_Pos.y = y; + m_aVertices[m_NumVertices].m_Tex = m_aTexture[0]; + m_aVertices[m_NumVertices].m_Color = m_aColor[0]; + Rotate(&Center, &m_aVertices[m_NumVertices].m_Pos); + + m_aVertices[m_NumVertices + 1].m_Pos.x = x+w; + m_aVertices[m_NumVertices + 1].m_Pos.y = y; + m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1]; + m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1]; + Rotate(&Center, &m_aVertices[m_NumVertices + 1].m_Pos); + + m_aVertices[m_NumVertices + 2].m_Pos.x = x + w; + m_aVertices[m_NumVertices + 2].m_Pos.y = y+h; + m_aVertices[m_NumVertices + 2].m_Tex = m_aTexture[2]; + m_aVertices[m_NumVertices + 2].m_Color = m_aColor[2]; + Rotate(&Center, &m_aVertices[m_NumVertices + 2].m_Pos); + + m_aVertices[m_NumVertices + 3].m_Pos.x = x; + m_aVertices[m_NumVertices + 3].m_Pos.y = y+h; + m_aVertices[m_NumVertices + 3].m_Tex = m_aTexture[3]; + m_aVertices[m_NumVertices + 3].m_Color = m_aColor[3]; + Rotate(&Center, &m_aVertices[m_NumVertices + 3].m_Pos); + + AddVertices(4); + } + + void QuadsDrawFreeform( + float x0, float y0, float x1, float y1, + float x2, float y2, float x3, float y3) + { + dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw_freeform without begin"); + + m_aVertices[m_NumVertices].m_Pos.x = x0; + m_aVertices[m_NumVertices].m_Pos.y = y0; + m_aVertices[m_NumVertices].m_Tex = m_aTexture[0]; + m_aVertices[m_NumVertices].m_Color = m_aColor[0]; + + m_aVertices[m_NumVertices + 1].m_Pos.x = x1; + m_aVertices[m_NumVertices + 1].m_Pos.y = y1; + m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1]; + m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1]; + + m_aVertices[m_NumVertices + 2].m_Pos.x = x3; + m_aVertices[m_NumVertices + 2].m_Pos.y = y3; + m_aVertices[m_NumVertices + 2].m_Tex = m_aTexture[3]; + m_aVertices[m_NumVertices + 2].m_Color = m_aColor[3]; + + m_aVertices[m_NumVertices + 3].m_Pos.x = x2; + m_aVertices[m_NumVertices + 3].m_Pos.y = y2; + m_aVertices[m_NumVertices + 3].m_Tex = m_aTexture[2]; + m_aVertices[m_NumVertices + 3].m_Color = m_aColor[2]; + + AddVertices(4); + } + + virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) + { + float startx = x; + + QuadsBegin(); + SetColor(r,g,b,a); + + while(*pText) + { + char c = *pText; + pText++; + + if(c == '\n') + { + x = startx; + y += Size; + } + else + { + QuadsSetSubset( + (c%16)/16.0f, + (c/16)/16.0f, + (c%16)/16.0f+1.0f/16.0f, + (c/16)/16.0f+1.0f/16.0f); + + QuadsDrawTL(x,y,Size,Size); + x += Size/2; + } + } + + QuadsEnd(); + } + + virtual bool Init() + { + /* Set all z to -5.0f */ + for(int i = 0; i < MAX_VERTICES; i++) + m_aVertices[i].m_Pos.z = -5.0f; + + /* init textures */ + m_FirstFreeTexture = 0; + for(int i = 0; i < MAX_TEXTURES; i++) + m_aTextures[i].next = i+1; + m_aTextures[MAX_TEXTURES-1].next = -1; + + /* set some default settings */ + glEnable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glAlphaFunc(GL_GREATER, 0); + glEnable(GL_ALPHA_TEST); + glDepthMask(0); + + /* create null texture, will get id=0 */ + static const unsigned char aNullTextureData[] = { + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, + }; + + m_InvalidTexture = LoadTextureRaw(4,4,IMG_RGBA,aNullTextureData,IMG_RGBA,TEXLOAD_NORESAMPLE); + dbg_msg("", "invalid texture id: %d %d", m_InvalidTexture, m_aTextures[m_InvalidTexture].tex); + + return true; + } +}; + +class CGraphics_SDL : public CGraphics_OpenGL +{ + SDL_Surface *m_pScreenSurface; + + int TryInit() + { + const SDL_VideoInfo *pInfo; + int Flags = SDL_OPENGL; + + m_ScreenWidth = config.gfx_screen_width; + m_ScreenHeight = config.gfx_screen_height; + + pInfo = SDL_GetVideoInfo(); + + /* set flags */ + Flags = SDL_OPENGL; + Flags |= SDL_GL_DOUBLEBUFFER; + Flags |= SDL_HWPALETTE; + if(config.dbg_resizable) + Flags |= SDL_RESIZABLE; + + if(pInfo->hw_available) + Flags |= SDL_HWSURFACE; + else + Flags |= SDL_SWSURFACE; + + if(pInfo->blit_hw) + Flags |= SDL_HWACCEL; + + if(config.gfx_fullscreen) + Flags |= SDL_FULLSCREEN; + + /* set gl attributes */ + if(config.gfx_fsaa_samples) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config.gfx_fsaa_samples); + } + else + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); + } + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, config.gfx_vsync); + + /* set caption */ + SDL_WM_SetCaption("Teeworlds", "Teeworlds"); + + /* create window */ + m_pScreenSurface = SDL_SetVideoMode(m_ScreenWidth, m_ScreenHeight, 0, Flags); + if(m_pScreenSurface == NULL) + { + dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError()); + return -1; + } + + return 0; + } + + + int InitWindow() + { + if(TryInit() == 0) + return 0; + + /* try disabling fsaa */ + while(config.gfx_fsaa_samples) + { + config.gfx_fsaa_samples--; + + if(config.gfx_fsaa_samples) + dbg_msg("gfx", "lowering FSAA to %d and trying again", config.gfx_fsaa_samples); + else + dbg_msg("gfx", "disabling FSAA and trying again"); + + if(TryInit() == 0) + return 0; + } + + /* try lowering the resolution */ + if(config.gfx_screen_width != 640 || config.gfx_screen_height != 480) + { + dbg_msg("gfx", "setting resolution to 640x480 and trying again"); + config.gfx_screen_width = 640; + config.gfx_screen_height = 480; + + if(TryInit() == 0) + return 0; + } + + dbg_msg("gfx", "out of ideas. failed to init graphics"); + + return -1; + } + + +public: + CGraphics_SDL() + { + m_pScreenSurface = 0; + } + + virtual bool Init() + { + { + int Systems = SDL_INIT_VIDEO; + + if(config.snd_enable) + Systems |= SDL_INIT_AUDIO; + + if(config.cl_eventthread) + Systems |= SDL_INIT_EVENTTHREAD; + + if(SDL_Init(Systems) < 0) + { + dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError()); + return -1; + } + } + + atexit(SDL_Quit); + + #ifdef CONF_FAMILY_WINDOWS + if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) + putenv("SDL_VIDEO_WINDOW_POS=8,27"); + #endif + + if(InitWindow() != 0) + return -1; + + SDL_ShowCursor(0); + + CGraphics_OpenGL::Init(); + + MapScreen(0,0,config.gfx_screen_width, config.gfx_screen_height); + + /* init input */ + inp_init(); + + /* font init */ + gfx_font_init(); + + return 0; + } + + virtual void Shutdown() + { + /* TODO: SDL, is this correct? */ + SDL_Quit(); + } + + virtual void Minimize() + { + SDL_WM_IconifyWindow(); + } + + virtual void Maximize() + { + /* TODO: SDL */ + } + + virtual int WindowActive() + { + return SDL_GetAppState()&SDL_APPINPUTFOCUS; + } + + virtual int WindowOpen() + { + return SDL_GetAppState()&SDL_APPACTIVE; + + } + + virtual void TakeScreenshot() + { + m_DoScreenshot = true; + } + + virtual void Swap() + { + if(m_DoScreenshot) + { + /* find filename */ + char filename[128]; + static int index = 1; + + for(; index < 1000; index++) + { + IOHANDLE io; + str_format(filename, sizeof(filename), "screenshots/screenshot%04d.png", index); + io = engine_openfile(filename, IOFLAG_READ); + if(io) + io_close(io); + else + break; + } + + ScreenshotDirect(filename); + m_DoScreenshot = false; + } + + { + static PERFORMACE_INFO pscope = {"glfwSwapBuffers", 0}; + perf_start(&pscope); + SDL_GL_SwapBuffers(); + perf_end(); + } + + if(config.gfx_finish) + glFinish(); + } +}; + +extern IEngineGraphics *CreateEngineGraphics() { return new CGraphics_SDL(); } diff --git a/src/engine/client/ec_gfx_text.c b/src/engine/client/ec_gfx_text.c deleted file mode 100644 index de40e391..00000000 --- a/src/engine/client/ec_gfx_text.c +++ /dev/null @@ -1,668 +0,0 @@ -#include -#include -#include - - -#ifdef CONF_PLATFORM_MACOSX - #include - #include -#else - #include - #include -#endif - -static int word_length(const char *text) -{ - int s = 1; - while(1) - { - if(*text == 0) - return s-1; - if(*text == '\n' || *text == '\t' || *text == ' ') - return s; - text++; - s++; - } -} - -static float text_r=1; -static float text_g=1; -static float text_b=1; -static float text_a=1; - -static struct FONT *default_font = 0; -void gfx_text_set_default_font(struct FONT *font) -{ - default_font = font; -} - - -void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags) -{ - mem_zero(cursor, sizeof(*cursor)); - cursor->font_size = font_size; - cursor->start_x = x; - cursor->start_y = y; - cursor->x = x; - cursor->y = y; - cursor->line_count = 1; - cursor->line_width = -1; - cursor->flags = flags; - cursor->charcount = 0; -} - - -void gfx_text(void *font_set_v, float x, float y, float size, const char *text, int max_width) -{ - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, x, y, size, TEXTFLAG_RENDER); - cursor.line_width = max_width; - gfx_text_ex(&cursor, text, -1); -} - -float gfx_text_width(void *font_set_v, float size, const char *text, int length) -{ - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, 0, 0, size, 0); - gfx_text_ex(&cursor, text, length); - return cursor.x; -} - -void gfx_text_color(float r, float g, float b, float a) -{ - text_r = r; - text_g = g; - text_b = b; - text_a = a; -} - -/* ft2 texture */ -#include -#include FT_FREETYPE_H - -static FT_Library ft_library; - -#define MAX_CHARACTERS 64 - - -/* GL_LUMINANCE can be good for debugging*/ -static int font_texture_format = GL_ALPHA; - - -static int font_sizes[] = {8,9,10,11,12,13,14,15,16,17,18,19,20,36}; -#define NUM_FONT_SIZES (sizeof(font_sizes)/sizeof(int)) - - -typedef struct FONTCHAR -{ - int id; - - /* these values are scaled to the font size */ - /* width * font_size == real_size */ - float width; - float height; - float offset_x; - float offset_y; - float advance_x; - - float uvs[4]; - int64 touch_time; -} FONTCHAR; - -typedef struct FONTSIZEDATA -{ - int font_size; - FT_Face *face; - - unsigned textures[2]; - int texture_width; - int texture_height; - - int num_x_chars; - int num_y_chars; - - int char_max_width; - int char_max_height; - - FONTCHAR characters[MAX_CHARACTERS*MAX_CHARACTERS]; - - int current_character; -} FONTSIZEDATA; - -typedef struct FONT -{ - char filename[128]; - FT_Face ft_face; - FONTSIZEDATA sizes[NUM_FONT_SIZES]; -} FONT; - -static int font_get_index(int pixelsize) -{ - int i; - for(i = 0; i < NUM_FONT_SIZES; i++) - { - if(font_sizes[i] >= pixelsize) - return i; - } - - return NUM_FONT_SIZES-1; -} - -FONT *gfx_font_load(const char *filename) -{ - int i; - FONT *font = mem_alloc(sizeof(FONT), 1); - - mem_zero(font, sizeof(*font)); - str_copy(font->filename, filename, sizeof(font->filename)); - - if(FT_New_Face(ft_library, font->filename, 0, &font->ft_face)) - { - mem_free(font); - return NULL; - } - - for(i = 0; i < NUM_FONT_SIZES; i++) - font->sizes[i].font_size = -1; - - return font; -}; - -void gfx_font_destroy(FONT *font) -{ - mem_free(font); -} - -void gfx_font_init() -{ - FT_Init_FreeType(&ft_library); -} - -static void grow(unsigned char *in, unsigned char *out, int w, int h) -{ - int y, x; - for(y = 0; y < h; y++) - for(x = 0; x < w; x++) - { - int c = in[y*w+x]; - int s_y, s_x; - - for(s_y = -1; s_y <= 1; s_y++) - for(s_x = -1; s_x <= 1; s_x++) - { - int get_x = x+s_x; - int get_y = y+s_y; - if (get_x >= 0 && get_y >= 0 && get_x < w && get_y < h) - { - int index = get_y*w+get_x; - if(in[index] > c) - c = in[index]; - } - } - - out[y*w+x] = c; - } -} - -static void font_init_texture(FONTSIZEDATA *sizedata, int charwidth, int charheight, int xchars, int ychars) -{ - static int font_memory_usage = 0; - int i; - int width = charwidth*xchars; - int height = charheight*ychars; - void *mem = mem_alloc(width*height, 1); - mem_zero(mem, width*height); - - if(sizedata->textures[0] == 0) - glGenTextures(2, sizedata->textures); - else - font_memory_usage -= sizedata->texture_width*sizedata->texture_height*2; - - sizedata->num_x_chars = xchars; - sizedata->num_y_chars = ychars; - sizedata->texture_width = width; - sizedata->texture_height = height; - sizedata->current_character = 0; - - for(i = 0; i < 2; i++) - { - glBindTexture(GL_TEXTURE_2D, sizedata->textures[i]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, font_texture_format, width, height, 0, font_texture_format, GL_UNSIGNED_BYTE, mem); - font_memory_usage += width*height; - } - - dbg_msg("", "font memory usage: %d", font_memory_usage); - - mem_free(mem); -} - -static void font_increase_texture_size(FONTSIZEDATA *sizedata) -{ - if(sizedata->texture_width < sizedata->texture_height) - sizedata->num_x_chars <<= 1; - else - sizedata->num_y_chars <<= 1; - font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, sizedata->num_x_chars, sizedata->num_y_chars); -} - -static void font_init_index(FONT *font, int index) -{ - int outline_thickness = 1; - FONTSIZEDATA *sizedata = &font->sizes[index]; - - sizedata->font_size = font_sizes[index]; - FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size); - - if(sizedata->font_size >= 18) - outline_thickness = 2; - - { - unsigned glyph_index; - int charcode; - int max_h = 0; - int max_w = 0; - - charcode = FT_Get_First_Char(font->ft_face, &glyph_index); - while(glyph_index != 0) - { - /* do stuff */ - FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT); - - if(font->ft_face->glyph->metrics.width > max_w) max_w = font->ft_face->glyph->metrics.width; - if(font->ft_face->glyph->metrics.height > max_h) max_h = font->ft_face->glyph->metrics.height; - charcode = FT_Get_Next_Char(font->ft_face, charcode, &glyph_index); - } - - max_w = (max_w>>6)+2+outline_thickness*2; - max_h = (max_h>>6)+2+outline_thickness*2; - - for(sizedata->char_max_width = 1; sizedata->char_max_width < max_w; sizedata->char_max_width <<= 1); - for(sizedata->char_max_height = 1; sizedata->char_max_height < max_h; sizedata->char_max_height <<= 1); - } - - //dbg_msg("font", "init size %d, texture size %d %d", font->sizes[index].font_size, w, h); - //FT_New_Face(ft_library, "data/fonts/vera.ttf", 0, &font->ft_face); - font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, 8, 8); -} - -static FONTSIZEDATA *font_get_size(FONT *font, int pixelsize) -{ - int index = font_get_index(pixelsize); - if(font->sizes[index].font_size != font_sizes[index]) - font_init_index(font, index); - return &font->sizes[index]; -} - - -static void font_upload_glyph(FONTSIZEDATA *sizedata, int texnum, int slot_id, int chr, const void *data) -{ - int x = (slot_id%sizedata->num_x_chars) * (sizedata->texture_width/sizedata->num_x_chars); - int y = (slot_id/sizedata->num_x_chars) * (sizedata->texture_height/sizedata->num_y_chars); - - glBindTexture(GL_TEXTURE_2D, sizedata->textures[texnum]); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, - sizedata->texture_width/sizedata->num_x_chars, - sizedata->texture_height/sizedata->num_y_chars, - font_texture_format, GL_UNSIGNED_BYTE, data); -} - -/* 8k of data used for rendering glyphs */ -static unsigned char glyphdata[(4096/64) * (4096/64)]; -static unsigned char glyphdata_outlined[(4096/64) * (4096/64)]; - -static int font_get_slot(FONTSIZEDATA *sizedata) -{ - int char_count = sizedata->num_x_chars*sizedata->num_y_chars; - if(sizedata->current_character < char_count) - { - int i = sizedata->current_character; - sizedata->current_character++; - return i; - } - - /* kick out the oldest */ - /* TODO: remove this linear search */ - { - int oldest = 0; - int i; - for(i = 1; i < char_count; i++) - { - if(sizedata->characters[i].touch_time < sizedata->characters[oldest].touch_time) - oldest = i; - } - - if(time_get()-sizedata->characters[oldest].touch_time < time_freq()) - { - font_increase_texture_size(sizedata); - return font_get_slot(sizedata); - } - - return oldest; - } -} - -static int font_render_glyph(FONT *font, FONTSIZEDATA *sizedata, int chr) -{ - FT_Bitmap *bitmap; - int slot_id = 0; - int slot_w = sizedata->texture_width / sizedata->num_x_chars; - int slot_h = sizedata->texture_height / sizedata->num_y_chars; - int slot_size = slot_w*slot_h; - int outline_thickness = 1; - int x = 1; - int y = 1; - int px, py; - - FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size); - - if(FT_Load_Char(font->ft_face, chr, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP)) - { - dbg_msg("font", "error loading glyph %d", chr); - return -1; - } - - bitmap = &font->ft_face->glyph->bitmap; - - /* fetch slot */ - slot_id = font_get_slot(sizedata); - if(slot_id < 0) - return -1; - - /* adjust spacing */ - if(sizedata->font_size >= 18) - outline_thickness = 2; - x += outline_thickness; - y += outline_thickness; - - /* prepare glyph data */ - mem_zero(glyphdata, slot_size); - - if(bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) - { - for(py = 0; py < bitmap->rows; py++) - for(px = 0; px < bitmap->width; px++) - glyphdata[(py+y)*slot_w+px+x] = bitmap->buffer[py*bitmap->pitch+px]; - } - else if(bitmap->pixel_mode == FT_PIXEL_MODE_MONO) - { - for(py = 0; py < bitmap->rows; py++) - for(px = 0; px < bitmap->width; px++) - { - if(bitmap->buffer[py*bitmap->pitch+px/8]&(1<<(7-(px%8)))) - glyphdata[(py+y)*slot_w+px+x] = 255; - } - } - - if(0) for(py = 0; py < slot_w; py++) - for(px = 0; px < slot_h; px++) - glyphdata[py*slot_w+px] = 255; - - /* upload the glyph */ - font_upload_glyph(sizedata, 0, slot_id, chr, glyphdata); - - if(outline_thickness == 1) - { - grow(glyphdata, glyphdata_outlined, slot_w, slot_h); - font_upload_glyph(sizedata, 1, slot_id, chr, glyphdata_outlined); - } - else - { - grow(glyphdata, glyphdata_outlined, slot_w, slot_h); - grow(glyphdata_outlined, glyphdata, slot_w, slot_h); - font_upload_glyph(sizedata, 1, slot_id, chr, glyphdata); - } - - /* set char info */ - { - FONTCHAR *fontchr = &sizedata->characters[slot_id]; - float scale = 1.0f/sizedata->font_size; - float uscale = 1.0f/sizedata->texture_width; - float vscale = 1.0f/sizedata->texture_height; - int height = bitmap->rows + outline_thickness*2 + 2; - int width = bitmap->width + outline_thickness*2 + 2; - - fontchr->id = chr; - fontchr->height = height * scale; - fontchr->width = width * scale; - fontchr->offset_x = (font->ft_face->glyph->bitmap_left-1) * scale; - fontchr->offset_y = (sizedata->font_size - font->ft_face->glyph->bitmap_top) * scale; - fontchr->advance_x = (font->ft_face->glyph->advance.x>>6) * scale; - - fontchr->uvs[0] = (slot_id%sizedata->num_x_chars) / (float)(sizedata->num_x_chars); - fontchr->uvs[1] = (slot_id/sizedata->num_x_chars) / (float)(sizedata->num_y_chars); - fontchr->uvs[2] = fontchr->uvs[0] + width*uscale; - fontchr->uvs[3] = fontchr->uvs[1] + height*vscale; - } - - return slot_id; -} - -static FONTCHAR *font_get_char(FONT *font, FONTSIZEDATA *sizedata, int chr) -{ - FONTCHAR *fontchr = NULL; - - /* search for the character */ - /* TODO: remove this linear search */ - int i; - for(i = 0; i < sizedata->current_character; i++) - { - if(sizedata->characters[i].id == chr) - { - fontchr = &sizedata->characters[i]; - break; - } - } - - /* check if we need to render the character */ - if(!fontchr) - { - int index = font_render_glyph(font, sizedata, chr); - if(index >= 0) - fontchr = &sizedata->characters[index]; - } - - /* touch the character */ - /* TODO: don't call time_get here */ - if(fontchr) - fontchr->touch_time = time_get(); - - return fontchr; -} - -/* must only be called from the rendering function as the font must be set to the correct size */ -static void font_render_setup(FONT *font, int size) -{ - FT_Set_Pixel_Sizes(font->ft_face, 0, size); -} - -static float font_kerning(FONT *font, int left, int right) -{ - FT_Vector kerning = {0,0}; - FT_Get_Kerning(font->ft_face, left, right, FT_KERNING_DEFAULT, &kerning); - return (kerning.x>>6); -} - -void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length) -{ - FONT *font = cursor->font; - FONTSIZEDATA *sizedata = NULL; - - float screen_x0, screen_y0, screen_x1, screen_y1; - float fake_to_screen_x, fake_to_screen_y; - int actual_x, actual_y; - - int actual_size; - int i; - int got_new_line = 0; - float draw_x, draw_y; - float cursor_x, cursor_y; - const char *end; - - float size = cursor->font_size; - - /* to correct coords, convert to screen coords, round, and convert back */ - gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); - - fake_to_screen_x = (gfx_screenwidth()/(screen_x1-screen_x0)); - fake_to_screen_y = (gfx_screenheight()/(screen_y1-screen_y0)); - actual_x = cursor->x * fake_to_screen_x; - actual_y = cursor->y * fake_to_screen_y; - - cursor_x = actual_x / fake_to_screen_x; - cursor_y = actual_y / fake_to_screen_y; - - /* same with size */ - actual_size = size * fake_to_screen_y; - size = actual_size / fake_to_screen_y; - - /* fetch font data */ - if(!font) - font = default_font; - - if(!font) - return; - - sizedata = font_get_size(font, actual_size); - font_render_setup(font, actual_size); - - /* set length */ - if(length < 0) - length = strlen(text); - - end = text + length; - - /* if we don't want to render, we can just skip the first outline pass */ - i = 1; - if(cursor->flags&TEXTFLAG_RENDER) - i = 0; - - for(;i < 2; i++) - { - const char *current = (char *)text; - const char *end = current+length; - draw_x = cursor_x; - draw_y = cursor_y; - - if(cursor->flags&TEXTFLAG_RENDER) - { - // TODO: Make this better - glEnable(GL_TEXTURE_2D); - if (i == 0) - glBindTexture(GL_TEXTURE_2D, sizedata->textures[1]); - else - glBindTexture(GL_TEXTURE_2D, sizedata->textures[0]); - - gfx_quads_begin(); - if (i == 0) - gfx_setcolor(0.0f, 0.0f, 0.0f, 0.3f*text_a); - else - gfx_setcolor(text_r, text_g, text_b, text_a); - } - - while(current < end) - { - int new_line = 0; - const char *batch_end = end; - if(cursor->line_width > 0 && !(cursor->flags&TEXTFLAG_STOP_AT_END)) - { - int wlen = word_length((char *)current); - TEXT_CURSOR compare = *cursor; - compare.x = draw_x; - compare.y = draw_y; - compare.flags &= ~TEXTFLAG_RENDER; - compare.line_width = -1; - gfx_text_ex(&compare, text, wlen); - - if(compare.x-draw_x > cursor->line_width) - { - /* word can't be fitted in one line, cut it */ - TEXT_CURSOR cutter = *cursor; - cutter.charcount = 0; - cutter.x = draw_x; - cutter.y = draw_y; - cutter.flags &= ~TEXTFLAG_RENDER; - cutter.flags |= TEXTFLAG_STOP_AT_END; - - gfx_text_ex(&cutter, (const char *)current, wlen); - wlen = cutter.charcount; - new_line = 1; - - if(wlen <= 3) /* if we can't place 3 chars of the word on this line, take the next */ - wlen = 0; - } - else if(compare.x-cursor->start_x > cursor->line_width) - { - new_line = 1; - wlen = 0; - } - - batch_end = current + wlen; - } - - while(current < batch_end) - { - const char *tmp; - float advance = 0; - int character = 0; - int nextcharacter = 0; - FONTCHAR *chr; - - // TODO: UTF-8 decode - character = str_utf8_decode(¤t); - tmp = current; - nextcharacter = str_utf8_decode(&tmp); - - if(character == '\n') - { - draw_x = cursor->start_x; - draw_y += size; - draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */ - draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y; - continue; - } - - chr = font_get_char(font, sizedata, character); - - if(chr) - { - if(cursor->flags&TEXTFLAG_RENDER) - { - gfx_quads_setsubset(chr->uvs[0], chr->uvs[1], chr->uvs[2], chr->uvs[3]); - gfx_quads_drawTL(draw_x+chr->offset_x*size, draw_y+chr->offset_y*size, chr->width*size, chr->height*size); - } - - advance = chr->advance_x + font_kerning(font, character, nextcharacter)/size; - } - - if(cursor->flags&TEXTFLAG_STOP_AT_END && draw_x+advance*size-cursor->start_x > cursor->line_width) - { - /* we hit the end of the line, no more to render or count */ - current = end; - break; - } - - draw_x += advance*size; - cursor->charcount++; - } - - if(new_line) - { - draw_x = cursor->start_x; - draw_y += size; - got_new_line = 1; - draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */ - draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y; - } - } - - if(cursor->flags&TEXTFLAG_RENDER) - gfx_quads_end(); - } - - cursor->x = draw_x; - - if(got_new_line) - cursor->y = draw_y; -} diff --git a/src/engine/client/ec_gfx_text.cpp b/src/engine/client/ec_gfx_text.cpp new file mode 100644 index 00000000..d17d1bed --- /dev/null +++ b/src/engine/client/ec_gfx_text.cpp @@ -0,0 +1,669 @@ +#include +#include +#include +#include + +extern IEngineGraphics *Graphics(); + +#ifdef CONF_PLATFORM_MACOSX + #include + #include +#else + #include + #include +#endif + +static int word_length(const char *text) +{ + int s = 1; + while(1) + { + if(*text == 0) + return s-1; + if(*text == '\n' || *text == '\t' || *text == ' ') + return s; + text++; + s++; + } +} + +static float text_r=1; +static float text_g=1; +static float text_b=1; +static float text_a=1; + +static struct FONT *default_font = 0; +void gfx_text_set_default_font(struct FONT *font) +{ + default_font = font; +} + + +void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags) +{ + mem_zero(cursor, sizeof(*cursor)); + cursor->font_size = font_size; + cursor->start_x = x; + cursor->start_y = y; + cursor->x = x; + cursor->y = y; + cursor->line_count = 1; + cursor->line_width = -1; + cursor->flags = flags; + cursor->charcount = 0; +} + + +void gfx_text(void *font_set_v, float x, float y, float size, const char *text, int max_width) +{ + TEXT_CURSOR cursor; + gfx_text_set_cursor(&cursor, x, y, size, TEXTFLAG_RENDER); + cursor.line_width = max_width; + gfx_text_ex(&cursor, text, -1); +} + +float gfx_text_width(void *font_set_v, float size, const char *text, int length) +{ + TEXT_CURSOR cursor; + gfx_text_set_cursor(&cursor, 0, 0, size, 0); + gfx_text_ex(&cursor, text, length); + return cursor.x; +} + +void gfx_text_color(float r, float g, float b, float a) +{ + text_r = r; + text_g = g; + text_b = b; + text_a = a; +} + +/* ft2 texture */ +#include +#include FT_FREETYPE_H + +static FT_Library ft_library; + +#define MAX_CHARACTERS 64 + + +/* GL_LUMINANCE can be good for debugging*/ +static int font_texture_format = GL_ALPHA; + + +static int font_sizes[] = {8,9,10,11,12,13,14,15,16,17,18,19,20,36}; +#define NUM_FONT_SIZES (sizeof(font_sizes)/sizeof(int)) + + +typedef struct FONTCHAR +{ + int id; + + /* these values are scaled to the font size */ + /* width * font_size == real_size */ + float width; + float height; + float offset_x; + float offset_y; + float advance_x; + + float uvs[4]; + int64 touch_time; +} FONTCHAR; + +typedef struct FONTSIZEDATA +{ + int font_size; + FT_Face *face; + + unsigned textures[2]; + int texture_width; + int texture_height; + + int num_x_chars; + int num_y_chars; + + int char_max_width; + int char_max_height; + + FONTCHAR characters[MAX_CHARACTERS*MAX_CHARACTERS]; + + int current_character; +} FONTSIZEDATA; + +typedef struct FONT +{ + char filename[128]; + FT_Face ft_face; + FONTSIZEDATA sizes[NUM_FONT_SIZES]; +} FONT; + +static int font_get_index(int pixelsize) +{ + for(unsigned i = 0; i < NUM_FONT_SIZES; i++) + { + if(font_sizes[i] >= pixelsize) + return i; + } + + return NUM_FONT_SIZES-1; +} + +FONT *gfx_font_load(const char *filename) +{ + FONT *font = (FONT *)mem_alloc(sizeof(FONT), 1); + + mem_zero(font, sizeof(*font)); + str_copy(font->filename, filename, sizeof(font->filename)); + + if(FT_New_Face(ft_library, font->filename, 0, &font->ft_face)) + { + mem_free(font); + return NULL; + } + + for(unsigned i = 0; i < NUM_FONT_SIZES; i++) + font->sizes[i].font_size = -1; + + return font; +}; + +void gfx_font_destroy(FONT *font) +{ + mem_free(font); +} + +void gfx_font_init() +{ + FT_Init_FreeType(&ft_library); +} + +static void grow(unsigned char *in, unsigned char *out, int w, int h) +{ + int y, x; + for(y = 0; y < h; y++) + for(x = 0; x < w; x++) + { + int c = in[y*w+x]; + int s_y, s_x; + + for(s_y = -1; s_y <= 1; s_y++) + for(s_x = -1; s_x <= 1; s_x++) + { + int get_x = x+s_x; + int get_y = y+s_y; + if (get_x >= 0 && get_y >= 0 && get_x < w && get_y < h) + { + int index = get_y*w+get_x; + if(in[index] > c) + c = in[index]; + } + } + + out[y*w+x] = c; + } +} + +static void font_init_texture(FONTSIZEDATA *sizedata, int charwidth, int charheight, int xchars, int ychars) +{ + static int font_memory_usage = 0; + int i; + int width = charwidth*xchars; + int height = charheight*ychars; + void *mem = mem_alloc(width*height, 1); + mem_zero(mem, width*height); + + if(sizedata->textures[0] == 0) + glGenTextures(2, sizedata->textures); + else + font_memory_usage -= sizedata->texture_width*sizedata->texture_height*2; + + sizedata->num_x_chars = xchars; + sizedata->num_y_chars = ychars; + sizedata->texture_width = width; + sizedata->texture_height = height; + sizedata->current_character = 0; + + for(i = 0; i < 2; i++) + { + glBindTexture(GL_TEXTURE_2D, sizedata->textures[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, font_texture_format, width, height, 0, font_texture_format, GL_UNSIGNED_BYTE, mem); + font_memory_usage += width*height; + } + + dbg_msg("", "font memory usage: %d", font_memory_usage); + + mem_free(mem); +} + +static void font_increase_texture_size(FONTSIZEDATA *sizedata) +{ + if(sizedata->texture_width < sizedata->texture_height) + sizedata->num_x_chars <<= 1; + else + sizedata->num_y_chars <<= 1; + font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, sizedata->num_x_chars, sizedata->num_y_chars); +} + +static void font_init_index(FONT *font, int index) +{ + int outline_thickness = 1; + FONTSIZEDATA *sizedata = &font->sizes[index]; + + sizedata->font_size = font_sizes[index]; + FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size); + + if(sizedata->font_size >= 18) + outline_thickness = 2; + + { + unsigned glyph_index; + int charcode; + int max_h = 0; + int max_w = 0; + + charcode = FT_Get_First_Char(font->ft_face, &glyph_index); + while(glyph_index != 0) + { + /* do stuff */ + FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT); + + if(font->ft_face->glyph->metrics.width > max_w) max_w = font->ft_face->glyph->metrics.width; + if(font->ft_face->glyph->metrics.height > max_h) max_h = font->ft_face->glyph->metrics.height; + charcode = FT_Get_Next_Char(font->ft_face, charcode, &glyph_index); + } + + max_w = (max_w>>6)+2+outline_thickness*2; + max_h = (max_h>>6)+2+outline_thickness*2; + + for(sizedata->char_max_width = 1; sizedata->char_max_width < max_w; sizedata->char_max_width <<= 1); + for(sizedata->char_max_height = 1; sizedata->char_max_height < max_h; sizedata->char_max_height <<= 1); + } + + //dbg_msg("font", "init size %d, texture size %d %d", font->sizes[index].font_size, w, h); + //FT_New_Face(ft_library, "data/fonts/vera.ttf", 0, &font->ft_face); + font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, 8, 8); +} + +static FONTSIZEDATA *font_get_size(FONT *font, int pixelsize) +{ + int index = font_get_index(pixelsize); + if(font->sizes[index].font_size != font_sizes[index]) + font_init_index(font, index); + return &font->sizes[index]; +} + + +static void font_upload_glyph(FONTSIZEDATA *sizedata, int texnum, int slot_id, int chr, const void *data) +{ + int x = (slot_id%sizedata->num_x_chars) * (sizedata->texture_width/sizedata->num_x_chars); + int y = (slot_id/sizedata->num_x_chars) * (sizedata->texture_height/sizedata->num_y_chars); + + glBindTexture(GL_TEXTURE_2D, sizedata->textures[texnum]); + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, + sizedata->texture_width/sizedata->num_x_chars, + sizedata->texture_height/sizedata->num_y_chars, + font_texture_format, GL_UNSIGNED_BYTE, data); +} + +/* 8k of data used for rendering glyphs */ +static unsigned char glyphdata[(4096/64) * (4096/64)]; +static unsigned char glyphdata_outlined[(4096/64) * (4096/64)]; + +static int font_get_slot(FONTSIZEDATA *sizedata) +{ + int char_count = sizedata->num_x_chars*sizedata->num_y_chars; + if(sizedata->current_character < char_count) + { + int i = sizedata->current_character; + sizedata->current_character++; + return i; + } + + /* kick out the oldest */ + /* TODO: remove this linear search */ + { + int oldest = 0; + int i; + for(i = 1; i < char_count; i++) + { + if(sizedata->characters[i].touch_time < sizedata->characters[oldest].touch_time) + oldest = i; + } + + if(time_get()-sizedata->characters[oldest].touch_time < time_freq()) + { + font_increase_texture_size(sizedata); + return font_get_slot(sizedata); + } + + return oldest; + } +} + +static int font_render_glyph(FONT *font, FONTSIZEDATA *sizedata, int chr) +{ + FT_Bitmap *bitmap; + int slot_id = 0; + int slot_w = sizedata->texture_width / sizedata->num_x_chars; + int slot_h = sizedata->texture_height / sizedata->num_y_chars; + int slot_size = slot_w*slot_h; + int outline_thickness = 1; + int x = 1; + int y = 1; + int px, py; + + FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size); + + if(FT_Load_Char(font->ft_face, chr, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP)) + { + dbg_msg("font", "error loading glyph %d", chr); + return -1; + } + + bitmap = &font->ft_face->glyph->bitmap; + + /* fetch slot */ + slot_id = font_get_slot(sizedata); + if(slot_id < 0) + return -1; + + /* adjust spacing */ + if(sizedata->font_size >= 18) + outline_thickness = 2; + x += outline_thickness; + y += outline_thickness; + + /* prepare glyph data */ + mem_zero(glyphdata, slot_size); + + if(bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) + { + for(py = 0; py < bitmap->rows; py++) + for(px = 0; px < bitmap->width; px++) + glyphdata[(py+y)*slot_w+px+x] = bitmap->buffer[py*bitmap->pitch+px]; + } + else if(bitmap->pixel_mode == FT_PIXEL_MODE_MONO) + { + for(py = 0; py < bitmap->rows; py++) + for(px = 0; px < bitmap->width; px++) + { + if(bitmap->buffer[py*bitmap->pitch+px/8]&(1<<(7-(px%8)))) + glyphdata[(py+y)*slot_w+px+x] = 255; + } + } + + if(0) for(py = 0; py < slot_w; py++) + for(px = 0; px < slot_h; px++) + glyphdata[py*slot_w+px] = 255; + + /* upload the glyph */ + font_upload_glyph(sizedata, 0, slot_id, chr, glyphdata); + + if(outline_thickness == 1) + { + grow(glyphdata, glyphdata_outlined, slot_w, slot_h); + font_upload_glyph(sizedata, 1, slot_id, chr, glyphdata_outlined); + } + else + { + grow(glyphdata, glyphdata_outlined, slot_w, slot_h); + grow(glyphdata_outlined, glyphdata, slot_w, slot_h); + font_upload_glyph(sizedata, 1, slot_id, chr, glyphdata); + } + + /* set char info */ + { + FONTCHAR *fontchr = &sizedata->characters[slot_id]; + float scale = 1.0f/sizedata->font_size; + float uscale = 1.0f/sizedata->texture_width; + float vscale = 1.0f/sizedata->texture_height; + int height = bitmap->rows + outline_thickness*2 + 2; + int width = bitmap->width + outline_thickness*2 + 2; + + fontchr->id = chr; + fontchr->height = height * scale; + fontchr->width = width * scale; + fontchr->offset_x = (font->ft_face->glyph->bitmap_left-1) * scale; + fontchr->offset_y = (sizedata->font_size - font->ft_face->glyph->bitmap_top) * scale; + fontchr->advance_x = (font->ft_face->glyph->advance.x>>6) * scale; + + fontchr->uvs[0] = (slot_id%sizedata->num_x_chars) / (float)(sizedata->num_x_chars); + fontchr->uvs[1] = (slot_id/sizedata->num_x_chars) / (float)(sizedata->num_y_chars); + fontchr->uvs[2] = fontchr->uvs[0] + width*uscale; + fontchr->uvs[3] = fontchr->uvs[1] + height*vscale; + } + + return slot_id; +} + +static FONTCHAR *font_get_char(FONT *font, FONTSIZEDATA *sizedata, int chr) +{ + FONTCHAR *fontchr = NULL; + + /* search for the character */ + /* TODO: remove this linear search */ + int i; + for(i = 0; i < sizedata->current_character; i++) + { + if(sizedata->characters[i].id == chr) + { + fontchr = &sizedata->characters[i]; + break; + } + } + + /* check if we need to render the character */ + if(!fontchr) + { + int index = font_render_glyph(font, sizedata, chr); + if(index >= 0) + fontchr = &sizedata->characters[index]; + } + + /* touch the character */ + /* TODO: don't call time_get here */ + if(fontchr) + fontchr->touch_time = time_get(); + + return fontchr; +} + +/* must only be called from the rendering function as the font must be set to the correct size */ +static void font_render_setup(FONT *font, int size) +{ + FT_Set_Pixel_Sizes(font->ft_face, 0, size); +} + +static float font_kerning(FONT *font, int left, int right) +{ + FT_Vector kerning = {0,0}; + FT_Get_Kerning(font->ft_face, left, right, FT_KERNING_DEFAULT, &kerning); + return (kerning.x>>6); +} + + +void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length) +{ + FONT *font = cursor->font; + FONTSIZEDATA *sizedata = NULL; + + float screen_x0, screen_y0, screen_x1, screen_y1; + float fake_to_screen_x, fake_to_screen_y; + int actual_x, actual_y; + + int actual_size; + int i; + int got_new_line = 0; + float draw_x, draw_y; + float cursor_x, cursor_y; + const char *end; + + float size = cursor->font_size; + + /* to correct coords, convert to screen coords, round, and convert back */ + Graphics()->GetScreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); + + fake_to_screen_x = (Graphics()->ScreenWidth()/(screen_x1-screen_x0)); + fake_to_screen_y = (Graphics()->ScreenHeight()/(screen_y1-screen_y0)); + actual_x = cursor->x * fake_to_screen_x; + actual_y = cursor->y * fake_to_screen_y; + + cursor_x = actual_x / fake_to_screen_x; + cursor_y = actual_y / fake_to_screen_y; + + /* same with size */ + actual_size = size * fake_to_screen_y; + size = actual_size / fake_to_screen_y; + + /* fetch font data */ + if(!font) + font = default_font; + + if(!font) + return; + + sizedata = font_get_size(font, actual_size); + font_render_setup(font, actual_size); + + /* set length */ + if(length < 0) + length = strlen(text); + + end = text + length; + + /* if we don't want to render, we can just skip the first outline pass */ + i = 1; + if(cursor->flags&TEXTFLAG_RENDER) + i = 0; + + for(;i < 2; i++) + { + const char *current = (char *)text; + const char *end = current+length; + draw_x = cursor_x; + draw_y = cursor_y; + + if(cursor->flags&TEXTFLAG_RENDER) + { + // TODO: Make this better + glEnable(GL_TEXTURE_2D); + if (i == 0) + glBindTexture(GL_TEXTURE_2D, sizedata->textures[1]); + else + glBindTexture(GL_TEXTURE_2D, sizedata->textures[0]); + + Graphics()->QuadsBegin(); + if (i == 0) + Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.3f*text_a); + else + Graphics()->SetColor(text_r, text_g, text_b, text_a); + } + + while(current < end) + { + int new_line = 0; + const char *batch_end = end; + if(cursor->line_width > 0 && !(cursor->flags&TEXTFLAG_STOP_AT_END)) + { + int wlen = word_length((char *)current); + TEXT_CURSOR compare = *cursor; + compare.x = draw_x; + compare.y = draw_y; + compare.flags &= ~TEXTFLAG_RENDER; + compare.line_width = -1; + gfx_text_ex(&compare, text, wlen); + + if(compare.x-draw_x > cursor->line_width) + { + /* word can't be fitted in one line, cut it */ + TEXT_CURSOR cutter = *cursor; + cutter.charcount = 0; + cutter.x = draw_x; + cutter.y = draw_y; + cutter.flags &= ~TEXTFLAG_RENDER; + cutter.flags |= TEXTFLAG_STOP_AT_END; + + gfx_text_ex(&cutter, (const char *)current, wlen); + wlen = cutter.charcount; + new_line = 1; + + if(wlen <= 3) /* if we can't place 3 chars of the word on this line, take the next */ + wlen = 0; + } + else if(compare.x-cursor->start_x > cursor->line_width) + { + new_line = 1; + wlen = 0; + } + + batch_end = current + wlen; + } + + while(current < batch_end) + { + const char *tmp; + float advance = 0; + int character = 0; + int nextcharacter = 0; + FONTCHAR *chr; + + // TODO: UTF-8 decode + character = str_utf8_decode(¤t); + tmp = current; + nextcharacter = str_utf8_decode(&tmp); + + if(character == '\n') + { + draw_x = cursor->start_x; + draw_y += size; + draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */ + draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y; + continue; + } + + chr = font_get_char(font, sizedata, character); + + if(chr) + { + if(cursor->flags&TEXTFLAG_RENDER) + { + Graphics()->QuadsSetSubset(chr->uvs[0], chr->uvs[1], chr->uvs[2], chr->uvs[3]); + Graphics()->QuadsDrawTL(draw_x+chr->offset_x*size, draw_y+chr->offset_y*size, chr->width*size, chr->height*size); + } + + advance = chr->advance_x + font_kerning(font, character, nextcharacter)/size; + } + + if(cursor->flags&TEXTFLAG_STOP_AT_END && draw_x+advance*size-cursor->start_x > cursor->line_width) + { + /* we hit the end of the line, no more to render or count */ + current = end; + break; + } + + draw_x += advance*size; + cursor->charcount++; + } + + if(new_line) + { + draw_x = cursor->start_x; + draw_y += size; + got_new_line = 1; + draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */ + draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y; + } + } + + if(cursor->flags&TEXTFLAG_RENDER) + Graphics()->QuadsEnd(); + } + + cursor->x = draw_x; + + if(got_new_line) + cursor->y = draw_y; +} diff --git a/src/engine/client/ec_inp.c b/src/engine/client/ec_inp.c deleted file mode 100644 index 495614d6..00000000 --- a/src/engine/client/ec_inp.c +++ /dev/null @@ -1,230 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "SDL.h" - -#include -#include -#include - -static struct -{ - unsigned char presses; - unsigned char releases; -} input_count[2][1024] = {{{0}}, {{0}}}; - -static unsigned char input_state[2][1024] = {{0}, {0}}; - -static int input_current = 0; -static int input_grabbed = 0; - -static unsigned int last_release = 0; -static unsigned int release_delta = -1; - -void inp_mouse_relative(int *x, int *y) -{ - int nx = 0, ny = 0; - float sens = config.inp_mousesens/100.0f; - - if(config.inp_grab) - SDL_GetRelativeMouseState(&nx, &ny); - else - { - if(input_grabbed) - { - SDL_GetMouseState(&nx,&ny); - SDL_WarpMouse(gfx_screenwidth()/2,gfx_screenheight()/2); - nx -= gfx_screenwidth()/2; ny -= gfx_screenheight()/2; - } - } - - *x = nx*sens; - *y = ny*sens; -} - -enum -{ - INPUT_BUFFER_SIZE=32 -}; - -static INPUT_EVENT input_events[INPUT_BUFFER_SIZE]; -static int num_events = 0; - -static void add_event(int unicode, int key, int flags) -{ - if(num_events != INPUT_BUFFER_SIZE) - { - input_events[num_events].unicode = unicode; - input_events[num_events].key = key; - input_events[num_events].flags = flags; - num_events++; - } -} - -int inp_num_events() -{ - return num_events; -} - -void inp_clear_events() -{ - num_events = 0; -} - -INPUT_EVENT inp_get_event(int index) -{ - if(index < 0 || index >= num_events) - { - INPUT_EVENT e = {0,0}; - return e; - } - - return input_events[index]; -} - -void inp_init() -{ - SDL_EnableUNICODE(1); - SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); -} - -void inp_mouse_mode_absolute() -{ - SDL_ShowCursor(1); - input_grabbed = 0; - if(config.inp_grab) - SDL_WM_GrabInput(SDL_GRAB_OFF); -} - -void inp_mouse_mode_relative() -{ - SDL_ShowCursor(0); - input_grabbed = 1; - if(config.inp_grab) - SDL_WM_GrabInput(SDL_GRAB_ON); -} - -int inp_mouse_doubleclick() -{ - return release_delta < (time_freq() >> 2); -} - -void inp_clear_key_states() -{ - mem_zero(input_state, sizeof(input_state)); - mem_zero(input_count, sizeof(input_count)); -} - -int inp_key_presses(int key) -{ - return input_count[input_current][key].presses; -} - -int inp_key_releases(int key) -{ - return input_count[input_current][key].releases; -} - -int inp_key_state(int key) -{ - return input_state[input_current][key]; -} - -int inp_key_pressed(int key) { return input_state[input_current][key]; } -int inp_key_was_pressed(int key) { return input_state[input_current^1][key]; } -int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } -int inp_button_pressed(int button) { return input_state[input_current][button]; } - -void inp_update() -{ - int i; - - if(input_grabbed && !gfx_window_active()) - inp_mouse_mode_absolute(); - - /*if(!input_grabbed && gfx_window_active()) - inp_mouse_mode_relative();*/ - - /* clear and begin count on the other one */ - input_current^=1; - mem_zero(&input_count[input_current], sizeof(input_count[input_current])); - mem_zero(&input_state[input_current], sizeof(input_state[input_current])); - - { - Uint8 *state = SDL_GetKeyState(&i); - if(i >= KEY_LAST) - i = KEY_LAST-1; - mem_copy(input_state[input_current], state, i); - } - - /* these states must always be updated manually because they are not in the GetKeyState from SDL */ - i = SDL_GetMouseState(NULL, NULL); - if(i&SDL_BUTTON(1)) input_state[input_current][KEY_MOUSE_1] = 1; /* 1 is left */ - if(i&SDL_BUTTON(3)) input_state[input_current][KEY_MOUSE_2] = 1; /* 3 is right */ - if(i&SDL_BUTTON(2)) input_state[input_current][KEY_MOUSE_3] = 1; /* 2 is middle */ - if(i&SDL_BUTTON(4)) input_state[input_current][KEY_MOUSE_4] = 1; - if(i&SDL_BUTTON(5)) input_state[input_current][KEY_MOUSE_5] = 1; - if(i&SDL_BUTTON(6)) input_state[input_current][KEY_MOUSE_6] = 1; - if(i&SDL_BUTTON(7)) input_state[input_current][KEY_MOUSE_7] = 1; - if(i&SDL_BUTTON(8)) input_state[input_current][KEY_MOUSE_8] = 1; - - { - SDL_Event event; - - while(SDL_PollEvent(&event)) - { - int key = -1; - int action = INPFLAG_PRESS; - switch (event.type) - { - /* handle keys */ - case SDL_KEYDOWN: - /*if(event.key.keysym.unicode < 255) */ - add_event(event.key.keysym.unicode, 0, 0); - key = event.key.keysym.sym; - break; - case SDL_KEYUP: - action = INPFLAG_RELEASE; - key = event.key.keysym.sym; - break; - - /* handle mouse buttons */ - case SDL_MOUSEBUTTONUP: - action = INPFLAG_RELEASE; - - if(event.button.button == 1) - { - release_delta = time_get() - last_release; - last_release = time_get(); - } - - /* fall through */ - case SDL_MOUSEBUTTONDOWN: - if(event.button.button == SDL_BUTTON_LEFT) key = KEY_MOUSE_1; - if(event.button.button == SDL_BUTTON_RIGHT) key = KEY_MOUSE_2; - if(event.button.button == SDL_BUTTON_MIDDLE) key = KEY_MOUSE_3; - if(event.button.button == SDL_BUTTON_WHEELUP) key = KEY_MOUSE_WHEEL_UP; - if(event.button.button == SDL_BUTTON_WHEELDOWN) key = KEY_MOUSE_WHEEL_DOWN; - if(event.button.button == 6) key = KEY_MOUSE_6; - if(event.button.button == 7) key = KEY_MOUSE_7; - if(event.button.button == 8) key = KEY_MOUSE_8; - break; - - /* other messages */ - case SDL_QUIT: - /* TODO: cleaner exit */ - exit(0); - break; - } - - /* */ - if(key != -1) - { - input_count[input_current][key].presses++; - if(action == INPFLAG_PRESS) - input_state[input_current][key] = 1; - add_event(0, key, action); - } - - } - } -} diff --git a/src/engine/client/ec_inp.cpp b/src/engine/client/ec_inp.cpp new file mode 100644 index 00000000..cf956471 --- /dev/null +++ b/src/engine/client/ec_inp.cpp @@ -0,0 +1,234 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "SDL.h" + +#include +#include +#include +#include + +static struct +{ + unsigned char presses; + unsigned char releases; +} input_count[2][1024] = {{{0}}, {{0}}}; + +static unsigned char input_state[2][1024] = {{0}, {0}}; + +static int input_current = 0; +static int input_grabbed = 0; + +static unsigned int last_release = 0; +static unsigned int release_delta = -1; + +// TODO: Refactor: Remove this +extern IEngineGraphics *Graphics(); + +void inp_mouse_relative(int *x, int *y) +{ + int nx = 0, ny = 0; + float sens = config.inp_mousesens/100.0f; + + if(config.inp_grab) + SDL_GetRelativeMouseState(&nx, &ny); + else + { + if(input_grabbed) + { + SDL_GetMouseState(&nx,&ny); + SDL_WarpMouse(Graphics()->ScreenWidth()/2,Graphics()->ScreenHeight()/2); + nx -= Graphics()->ScreenWidth()/2; ny -= Graphics()->ScreenHeight()/2; + } + } + + *x = nx*sens; + *y = ny*sens; +} + +enum +{ + INPUT_BUFFER_SIZE=32 +}; + +static INPUT_EVENT input_events[INPUT_BUFFER_SIZE]; +static int num_events = 0; + +static void add_event(int unicode, int key, int flags) +{ + if(num_events != INPUT_BUFFER_SIZE) + { + input_events[num_events].unicode = unicode; + input_events[num_events].key = key; + input_events[num_events].flags = flags; + num_events++; + } +} + +int inp_num_events() +{ + return num_events; +} + +void inp_clear_events() +{ + num_events = 0; +} + +INPUT_EVENT inp_get_event(int index) +{ + if(index < 0 || index >= num_events) + { + INPUT_EVENT e = {0,0}; + return e; + } + + return input_events[index]; +} + +void inp_init() +{ + SDL_EnableUNICODE(1); + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); +} + +void inp_mouse_mode_absolute() +{ + SDL_ShowCursor(1); + input_grabbed = 0; + if(config.inp_grab) + SDL_WM_GrabInput(SDL_GRAB_OFF); +} + +void inp_mouse_mode_relative() +{ + SDL_ShowCursor(0); + input_grabbed = 1; + if(config.inp_grab) + SDL_WM_GrabInput(SDL_GRAB_ON); +} + +int inp_mouse_doubleclick() +{ + return release_delta < (time_freq() >> 2); +} + +void inp_clear_key_states() +{ + mem_zero(input_state, sizeof(input_state)); + mem_zero(input_count, sizeof(input_count)); +} + +int inp_key_presses(int key) +{ + return input_count[input_current][key].presses; +} + +int inp_key_releases(int key) +{ + return input_count[input_current][key].releases; +} + +int inp_key_state(int key) +{ + return input_state[input_current][key]; +} + +int inp_key_pressed(int key) { return input_state[input_current][key]; } +int inp_key_was_pressed(int key) { return input_state[input_current^1][key]; } +int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } +int inp_button_pressed(int button) { return input_state[input_current][button]; } + +void inp_update() +{ + int i; + + if(input_grabbed && !Graphics()->WindowActive()) + inp_mouse_mode_absolute(); + + /*if(!input_grabbed && Graphics()->WindowActive()) + inp_mouse_mode_relative();*/ + + /* clear and begin count on the other one */ + input_current^=1; + mem_zero(&input_count[input_current], sizeof(input_count[input_current])); + mem_zero(&input_state[input_current], sizeof(input_state[input_current])); + + { + Uint8 *state = SDL_GetKeyState(&i); + if(i >= KEY_LAST) + i = KEY_LAST-1; + mem_copy(input_state[input_current], state, i); + } + + /* these states must always be updated manually because they are not in the GetKeyState from SDL */ + i = SDL_GetMouseState(NULL, NULL); + if(i&SDL_BUTTON(1)) input_state[input_current][KEY_MOUSE_1] = 1; /* 1 is left */ + if(i&SDL_BUTTON(3)) input_state[input_current][KEY_MOUSE_2] = 1; /* 3 is right */ + if(i&SDL_BUTTON(2)) input_state[input_current][KEY_MOUSE_3] = 1; /* 2 is middle */ + if(i&SDL_BUTTON(4)) input_state[input_current][KEY_MOUSE_4] = 1; + if(i&SDL_BUTTON(5)) input_state[input_current][KEY_MOUSE_5] = 1; + if(i&SDL_BUTTON(6)) input_state[input_current][KEY_MOUSE_6] = 1; + if(i&SDL_BUTTON(7)) input_state[input_current][KEY_MOUSE_7] = 1; + if(i&SDL_BUTTON(8)) input_state[input_current][KEY_MOUSE_8] = 1; + + { + SDL_Event event; + + while(SDL_PollEvent(&event)) + { + int key = -1; + int action = INPFLAG_PRESS; + switch (event.type) + { + /* handle keys */ + case SDL_KEYDOWN: + /*if(event.key.keysym.unicode < 255) */ + add_event(event.key.keysym.unicode, 0, 0); + key = event.key.keysym.sym; + break; + case SDL_KEYUP: + action = INPFLAG_RELEASE; + key = event.key.keysym.sym; + break; + + /* handle mouse buttons */ + case SDL_MOUSEBUTTONUP: + action = INPFLAG_RELEASE; + + if(event.button.button == 1) + { + release_delta = time_get() - last_release; + last_release = time_get(); + } + + /* fall through */ + case SDL_MOUSEBUTTONDOWN: + if(event.button.button == SDL_BUTTON_LEFT) key = KEY_MOUSE_1; + if(event.button.button == SDL_BUTTON_RIGHT) key = KEY_MOUSE_2; + if(event.button.button == SDL_BUTTON_MIDDLE) key = KEY_MOUSE_3; + if(event.button.button == SDL_BUTTON_WHEELUP) key = KEY_MOUSE_WHEEL_UP; + if(event.button.button == SDL_BUTTON_WHEELDOWN) key = KEY_MOUSE_WHEEL_DOWN; + if(event.button.button == 6) key = KEY_MOUSE_6; + if(event.button.button == 7) key = KEY_MOUSE_7; + if(event.button.button == 8) key = KEY_MOUSE_8; + break; + + /* other messages */ + case SDL_QUIT: + /* TODO: cleaner exit */ + exit(0); + break; + } + + /* */ + if(key != -1) + { + input_count[input_current][key].presses++; + if(action == INPFLAG_PRESS) + input_state[input_current][key] = 1; + add_event(0, key, action); + } + + } + } +} diff --git a/src/engine/client/ec_snd.c b/src/engine/client/ec_snd.c deleted file mode 100644 index ac41ec59..00000000 --- a/src/engine/client/ec_snd.c +++ /dev/null @@ -1,465 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include - -#include "SDL.h" - -#include -#include -#include -#include - -enum -{ - NUM_SAMPLES = 512, - NUM_VOICES = 64, - NUM_CHANNELS = 16, - - MAX_FRAMES = 1024 -}; - -typedef struct -{ - short *data; - int num_frames; - int rate; - int channels; - int loop_start; - int loop_end; -} SAMPLE; - -typedef struct -{ - int vol; - int pan; -} CHANNEL; - -typedef struct -{ - SAMPLE *snd; - CHANNEL *channel; - int tick; - int vol; /* 0 - 255 */ - int flags; - int x, y; -} VOICE; - -static SAMPLE samples[NUM_SAMPLES] = { {0} }; -static VOICE voices[NUM_VOICES] = { {0} }; -static CHANNEL channels[NUM_CHANNELS] = { {255, 0} }; - -static LOCK sound_lock = 0; -static int sound_enabled = 0; - -static int center_x = 0; -static int center_y = 0; - -static int mixing_rate = 48000; -static volatile int sound_volume = 100; - -static int next_voice = 0; - -void snd_set_channel(int cid, float vol, float pan) -{ - channels[cid].vol = (int)(vol*255.0f); - channels[cid].pan = (int)(pan*255.0f); /* TODO: this is only on and off right now */ -} - -static int play(int cid, int sid, int flags, float x, float y) -{ - int vid = -1; - int i; - - lock_wait(sound_lock); - - /* search for voice */ - for(i = 0; i < NUM_VOICES; i++) - { - int id = (next_voice + i) % NUM_VOICES; - if(!voices[id].snd) - { - vid = id; - next_voice = id+1; - break; - } - } - - /* voice found, use it */ - if(vid != -1) - { - voices[vid].snd = &samples[sid]; - voices[vid].channel = &channels[cid]; - voices[vid].tick = 0; - voices[vid].vol = 255; - voices[vid].flags = flags; - voices[vid].x = (int)x; - voices[vid].y = (int)y; - } - - lock_release(sound_lock); - return vid; -} - -int snd_play_at(int cid, int sid, int flags, float x, float y) -{ - return play(cid, sid, flags|SNDFLAG_POS, x, y); -} - -int snd_play(int cid, int sid, int flags) -{ - return play(cid, sid, flags, 0, 0); -} - -void snd_stop(int vid) -{ - /* TODO: a nice fade out */ - lock_wait(sound_lock); - voices[vid].snd = 0; - lock_release(sound_lock); -} - -/* TODO: there should be a faster way todo this */ -static short int2short(int i) -{ - if(i > 0x7fff) - return 0x7fff; - else if(i < -0x7fff) - return -0x7fff; - return i; -} - -static int iabs(int i) -{ - if(i<0) - return -i; - return i; -} - -static void mix(short *final_out, unsigned frames) -{ - int mix_buffer[MAX_FRAMES*2] = {0}; - int i, s; - int master_vol; - - /* aquire lock while we are mixing */ - lock_wait(sound_lock); - - master_vol = sound_volume; - - for(i = 0; i < NUM_VOICES; i++) - { - if(voices[i].snd) - { - /* mix voice */ - VOICE *v = &voices[i]; - int *out = mix_buffer; - - int step = v->snd->channels; /* setup input sources */ - short *in_l = &v->snd->data[v->tick*step]; - short *in_r = &v->snd->data[v->tick*step+1]; - - int end = v->snd->num_frames-v->tick; - - int rvol = v->channel->vol; - int lvol = v->channel->vol; - - /* make sure that we don't go outside the sound data */ - if(frames < end) - end = frames; - - /* check if we have a mono sound */ - if(v->snd->channels == 1) - in_r = in_l; - - /* volume calculation */ - if(v->flags&SNDFLAG_POS && v->channel->pan) - { - /* TODO: we should respect the channel panning value */ - const int range = 1500; /* magic value, remove */ - int dx = v->x - center_x; - int dy = v->y - center_y; - int dist = sqrt(dx*dx+dy*dy); /* double here. nasty */ - int p = iabs(dx); - if(dist < range) - { - /* panning */ - if(dx > 0) - lvol = ((range-p)*lvol)/range; - else - rvol = ((range-p)*rvol)/range; - - /* falloff */ - lvol = (lvol*(range-dist))/range; - rvol = (rvol*(range-dist))/range; - } - else - { - lvol = 0; - rvol = 0; - } - } - - /* process all frames */ - for(s = 0; s < end; s++) - { - *out++ += (*in_l)*lvol; - *out++ += (*in_r)*rvol; - in_l += step; - in_r += step; - v->tick++; - } - - /* free voice if not used any more */ - if(v->tick == v->snd->num_frames) - v->snd = 0; - - } - } - - - /* release the lock */ - lock_release(sound_lock); - - { - /* clamp accumulated values */ - /* TODO: this seams slow */ - for(i = 0; i < frames; i++) - { - int j = i<<1; - int vl = ((mix_buffer[j]*master_vol)/101)>>8; - int vr = ((mix_buffer[j+1]*master_vol)/101)>>8; - - final_out[j] = int2short(vl); - final_out[j+1] = int2short(vr); - } - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - swap_endian(final_out, sizeof(short), frames * 2); -#endif -} - -static void sdlcallback(void *unused, Uint8 *stream, int len) -{ - mix((short *)stream, len/2/2); -} - -int snd_init() -{ - SDL_AudioSpec format; - - sound_lock = lock_create(); - - if(!config.snd_enable) - return 0; - - mixing_rate = config.snd_rate; - - /* Set 16-bit stereo audio at 22Khz */ - format.freq = config.snd_rate; - format.format = AUDIO_S16; - format.channels = 2; - format.samples = config.snd_buffer_size; - format.callback = sdlcallback; - format.userdata = NULL; - - /* Open the audio device and start playing sound! */ - if(SDL_OpenAudio(&format, NULL) < 0) - { - dbg_msg("client/sound", "unable to open audio: %s", SDL_GetError()); - return -1; - } - else - dbg_msg("client/sound", "sound init successful"); - - SDL_PauseAudio(0); - - sound_enabled = 1; - snd_update(); /* update the volume */ - return 0; -} - -int snd_update() -{ - /* update volume */ - int wanted_volume = config.snd_volume; - - if(!gfx_window_active() && config.snd_nonactive_mute) - wanted_volume = 0; - - if(wanted_volume != sound_volume) - { - lock_wait(sound_lock); - sound_volume = wanted_volume; - lock_release(sound_lock); - } - - return 0; -} - -int snd_shutdown() -{ - SDL_CloseAudio(); - lock_destroy(sound_lock); - return 0; -} - -int snd_alloc_id() -{ - /* TODO: linear search, get rid of it */ - unsigned sid; - for(sid = 0; sid < NUM_SAMPLES; sid++) - { - if(samples[sid].data == 0x0) - return sid; - } - - return -1; -} - -static void rate_convert(int sid) -{ - SAMPLE *snd = &samples[sid]; - int num_frames = 0; - short *new_data = 0; - int i; - - /* make sure that we need to convert this sound */ - if(!snd->data || snd->rate == mixing_rate) - return; - - /* allocate new data */ - num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate); - new_data = mem_alloc(num_frames*snd->channels*sizeof(short), 1); - - for(i = 0; i < num_frames; i++) - { - /* resample TODO: this should be done better, like linear atleast */ - float a = i/(float)num_frames; - int f = (int)(a*snd->num_frames); - if(f >= snd->num_frames) - f = snd->num_frames-1; - - /* set new data */ - if(snd->channels == 1) - new_data[i] = snd->data[f]; - else if(snd->channels == 2) - { - new_data[i*2] = snd->data[f*2]; - new_data[i*2+1] = snd->data[f*2+1]; - } - } - - /* free old data and apply new */ - mem_free(snd->data); - snd->data = new_data; - snd->num_frames = num_frames; -} - - -static IOHANDLE file = NULL; - -static int read_data(void *buffer, int size) -{ - return io_read(file, buffer, size); -} - -int snd_load_wv(const char *filename) -{ - SAMPLE *snd; - int sid = -1; - char error[100]; - WavpackContext *context; - - /* don't waste memory on sound when we are stress testing */ - if(config.dbg_stress) - return -1; - - /* no need to load sound when we are running with no sound */ - if(!sound_enabled) - return 1; - - file = engine_openfile(filename, IOFLAG_READ); /* TODO: use system.h stuff for this */ - if(!file) - { - dbg_msg("sound/wv", "failed to open %s", filename); - return -1; - } - - sid = snd_alloc_id(); - if(sid < 0) - return -1; - snd = &samples[sid]; - - context = WavpackOpenFileInput(read_data, error); - if (context) - { - int samples = WavpackGetNumSamples(context); - int bitspersample = WavpackGetBitsPerSample(context); - unsigned int samplerate = WavpackGetSampleRate(context); - int channels = WavpackGetNumChannels(context); - int *data; - int *src; - short *dst; - int i; - - snd->channels = channels; - snd->rate = samplerate; - - if(snd->channels > 2) - { - dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename); - return -1; - } - - /* - if(snd->rate != 44100) - { - dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename); - return -1; - }*/ - - if(bitspersample != 16) - { - dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename); - return -1; - } - - data = (int *)mem_alloc(4*samples*channels, 1); - WavpackUnpackSamples(context, data, samples); /* TODO: check return value */ - src = data; - - snd->data = (short *)mem_alloc(2*samples*channels, 1); - dst = snd->data; - - for (i = 0; i < samples*channels; i++) - *dst++ = (short)*src++; - - mem_free(data); - - snd->num_frames = samples; - snd->loop_start = -1; - snd->loop_end = -1; - } - else - { - dbg_msg("sound/wv", "failed to open %s: %s", filename, error); - } - - io_close(file); - file = NULL; - - if(config.debug) - dbg_msg("sound/wv", "loaded %s", filename); - - rate_convert(sid); - return sid; -} - -void snd_set_listener_pos(float x, float y) -{ - center_x = (int)x; - center_y = (int)y; -} diff --git a/src/engine/client/ec_snd.cpp b/src/engine/client/ec_snd.cpp new file mode 100644 index 00000000..3baea982 --- /dev/null +++ b/src/engine/client/ec_snd.cpp @@ -0,0 +1,471 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include +#include + +#include "SDL.h" + +extern "C" { // wavpack + #include +} +#include +#include +#include + +enum +{ + NUM_SAMPLES = 512, + NUM_VOICES = 64, + NUM_CHANNELS = 16, + + MAX_FRAMES = 1024 +}; + +typedef struct +{ + short *data; + int num_frames; + int rate; + int channels; + int loop_start; + int loop_end; +} SAMPLE; + +typedef struct +{ + int vol; + int pan; +} CHANNEL; + +typedef struct +{ + SAMPLE *snd; + CHANNEL *channel; + int tick; + int vol; /* 0 - 255 */ + int flags; + int x, y; +} VOICE; + +static SAMPLE samples[NUM_SAMPLES] = { {0} }; +static VOICE voices[NUM_VOICES] = { {0} }; +static CHANNEL channels[NUM_CHANNELS] = { {255, 0} }; + +static LOCK sound_lock = 0; +static int sound_enabled = 0; + +static int center_x = 0; +static int center_y = 0; + +static int mixing_rate = 48000; +static volatile int sound_volume = 100; + +static int next_voice = 0; + +void snd_set_channel(int cid, float vol, float pan) +{ + channels[cid].vol = (int)(vol*255.0f); + channels[cid].pan = (int)(pan*255.0f); /* TODO: this is only on and off right now */ +} + +static int play(int cid, int sid, int flags, float x, float y) +{ + int vid = -1; + int i; + + lock_wait(sound_lock); + + /* search for voice */ + for(i = 0; i < NUM_VOICES; i++) + { + int id = (next_voice + i) % NUM_VOICES; + if(!voices[id].snd) + { + vid = id; + next_voice = id+1; + break; + } + } + + /* voice found, use it */ + if(vid != -1) + { + voices[vid].snd = &samples[sid]; + voices[vid].channel = &channels[cid]; + voices[vid].tick = 0; + voices[vid].vol = 255; + voices[vid].flags = flags; + voices[vid].x = (int)x; + voices[vid].y = (int)y; + } + + lock_release(sound_lock); + return vid; +} + +int snd_play_at(int cid, int sid, int flags, float x, float y) +{ + return play(cid, sid, flags|SNDFLAG_POS, x, y); +} + +int snd_play(int cid, int sid, int flags) +{ + return play(cid, sid, flags, 0, 0); +} + +void snd_stop(int vid) +{ + /* TODO: a nice fade out */ + lock_wait(sound_lock); + voices[vid].snd = 0; + lock_release(sound_lock); +} + +/* TODO: there should be a faster way todo this */ +static short int2short(int i) +{ + if(i > 0x7fff) + return 0x7fff; + else if(i < -0x7fff) + return -0x7fff; + return i; +} + +static int iabs(int i) +{ + if(i<0) + return -i; + return i; +} + +static void mix(short *final_out, unsigned frames) +{ + int mix_buffer[MAX_FRAMES*2] = {0}; + int master_vol; + + /* aquire lock while we are mixing */ + lock_wait(sound_lock); + + master_vol = sound_volume; + + for(unsigned i = 0; i < NUM_VOICES; i++) + { + if(voices[i].snd) + { + /* mix voice */ + VOICE *v = &voices[i]; + int *out = mix_buffer; + + int step = v->snd->channels; /* setup input sources */ + short *in_l = &v->snd->data[v->tick*step]; + short *in_r = &v->snd->data[v->tick*step+1]; + + unsigned end = v->snd->num_frames-v->tick; + + int rvol = v->channel->vol; + int lvol = v->channel->vol; + + /* make sure that we don't go outside the sound data */ + if(frames < end) + end = frames; + + /* check if we have a mono sound */ + if(v->snd->channels == 1) + in_r = in_l; + + /* volume calculation */ + if(v->flags&SNDFLAG_POS && v->channel->pan) + { + /* TODO: we should respect the channel panning value */ + const int range = 1500; /* magic value, remove */ + int dx = v->x - center_x; + int dy = v->y - center_y; + int dist = sqrt(dx*dx+dy*dy); /* double here. nasty */ + int p = iabs(dx); + if(dist < range) + { + /* panning */ + if(dx > 0) + lvol = ((range-p)*lvol)/range; + else + rvol = ((range-p)*rvol)/range; + + /* falloff */ + lvol = (lvol*(range-dist))/range; + rvol = (rvol*(range-dist))/range; + } + else + { + lvol = 0; + rvol = 0; + } + } + + /* process all frames */ + for(unsigned s = 0; s < end; s++) + { + *out++ += (*in_l)*lvol; + *out++ += (*in_r)*rvol; + in_l += step; + in_r += step; + v->tick++; + } + + /* free voice if not used any more */ + if(v->tick == v->snd->num_frames) + v->snd = 0; + + } + } + + + /* release the lock */ + lock_release(sound_lock); + + { + /* clamp accumulated values */ + /* TODO: this seams slow */ + for(unsigned i = 0; i < frames; i++) + { + int j = i<<1; + int vl = ((mix_buffer[j]*master_vol)/101)>>8; + int vr = ((mix_buffer[j+1]*master_vol)/101)>>8; + + final_out[j] = int2short(vl); + final_out[j+1] = int2short(vr); + } + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + swap_endian(final_out, sizeof(short), frames * 2); +#endif +} + +static void sdlcallback(void *unused, Uint8 *stream, int len) +{ + mix((short *)stream, len/2/2); +} + +int snd_init() +{ + SDL_AudioSpec format; + + sound_lock = lock_create(); + + if(!config.snd_enable) + return 0; + + mixing_rate = config.snd_rate; + + /* Set 16-bit stereo audio at 22Khz */ + format.freq = config.snd_rate; + format.format = AUDIO_S16; + format.channels = 2; + format.samples = config.snd_buffer_size; + format.callback = sdlcallback; + format.userdata = NULL; + + /* Open the audio device and start playing sound! */ + if(SDL_OpenAudio(&format, NULL) < 0) + { + dbg_msg("client/sound", "unable to open audio: %s", SDL_GetError()); + return -1; + } + else + dbg_msg("client/sound", "sound init successful"); + + SDL_PauseAudio(0); + + sound_enabled = 1; + snd_update(); /* update the volume */ + return 0; +} + +// TODO: Refactor: Remove this +extern IEngineGraphics *Graphics(); + + +int snd_update() +{ + /* update volume */ + int wanted_volume = config.snd_volume; + + if(!Graphics()->WindowActive() && config.snd_nonactive_mute) + wanted_volume = 0; + + if(wanted_volume != sound_volume) + { + lock_wait(sound_lock); + sound_volume = wanted_volume; + lock_release(sound_lock); + } + + return 0; +} + +int snd_shutdown() +{ + SDL_CloseAudio(); + lock_destroy(sound_lock); + return 0; +} + +int snd_alloc_id() +{ + /* TODO: linear search, get rid of it */ + unsigned sid; + for(sid = 0; sid < NUM_SAMPLES; sid++) + { + if(samples[sid].data == 0x0) + return sid; + } + + return -1; +} + +static void rate_convert(int sid) +{ + SAMPLE *snd = &samples[sid]; + int num_frames = 0; + short *new_data = 0; + int i; + + /* make sure that we need to convert this sound */ + if(!snd->data || snd->rate == mixing_rate) + return; + + /* allocate new data */ + num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate); + new_data = (short *)mem_alloc(num_frames*snd->channels*sizeof(short), 1); + + for(i = 0; i < num_frames; i++) + { + /* resample TODO: this should be done better, like linear atleast */ + float a = i/(float)num_frames; + int f = (int)(a*snd->num_frames); + if(f >= snd->num_frames) + f = snd->num_frames-1; + + /* set new data */ + if(snd->channels == 1) + new_data[i] = snd->data[f]; + else if(snd->channels == 2) + { + new_data[i*2] = snd->data[f*2]; + new_data[i*2+1] = snd->data[f*2+1]; + } + } + + /* free old data and apply new */ + mem_free(snd->data); + snd->data = new_data; + snd->num_frames = num_frames; +} + + +static IOHANDLE file = NULL; + +static int read_data(void *buffer, int size) +{ + return io_read(file, buffer, size); +} + +int snd_load_wv(const char *filename) +{ + SAMPLE *snd; + int sid = -1; + char error[100]; + WavpackContext *context; + + /* don't waste memory on sound when we are stress testing */ + if(config.dbg_stress) + return -1; + + /* no need to load sound when we are running with no sound */ + if(!sound_enabled) + return 1; + + file = engine_openfile(filename, IOFLAG_READ); /* TODO: use system.h stuff for this */ + if(!file) + { + dbg_msg("sound/wv", "failed to open %s", filename); + return -1; + } + + sid = snd_alloc_id(); + if(sid < 0) + return -1; + snd = &samples[sid]; + + context = WavpackOpenFileInput(read_data, error); + if (context) + { + int samples = WavpackGetNumSamples(context); + int bitspersample = WavpackGetBitsPerSample(context); + unsigned int samplerate = WavpackGetSampleRate(context); + int channels = WavpackGetNumChannels(context); + int *data; + int *src; + short *dst; + int i; + + snd->channels = channels; + snd->rate = samplerate; + + if(snd->channels > 2) + { + dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename); + return -1; + } + + /* + if(snd->rate != 44100) + { + dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename); + return -1; + }*/ + + if(bitspersample != 16) + { + dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename); + return -1; + } + + data = (int *)mem_alloc(4*samples*channels, 1); + WavpackUnpackSamples(context, data, samples); /* TODO: check return value */ + src = data; + + snd->data = (short *)mem_alloc(2*samples*channels, 1); + dst = snd->data; + + for (i = 0; i < samples*channels; i++) + *dst++ = (short)*src++; + + mem_free(data); + + snd->num_frames = samples; + snd->loop_start = -1; + snd->loop_end = -1; + } + else + { + dbg_msg("sound/wv", "failed to open %s: %s", filename, error); + } + + io_close(file); + file = NULL; + + if(config.debug) + dbg_msg("sound/wv", "loaded %s", filename); + + rate_convert(sid); + return sid; +} + +void snd_set_listener_pos(float x, float y) +{ + center_x = (int)x; + center_y = (int)y; +} diff --git a/src/engine/client/ec_srvbrowse.c b/src/engine/client/ec_srvbrowse.c deleted file mode 100644 index 4a85e778..00000000 --- a/src/engine/client/ec_srvbrowse.c +++ /dev/null @@ -1,737 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -extern NETCLIENT *net; - - -/* ------ server browse ---- */ -/* TODO: move all this to a separate file */ - -typedef struct SERVERENTRY_t SERVERENTRY; -struct SERVERENTRY_t -{ - NETADDR addr; - int64 request_time; - int got_info; - SERVER_INFO info; - - SERVERENTRY *next_ip; /* ip hashed list */ - - SERVERENTRY *prev_req; /* request list */ - SERVERENTRY *next_req; -}; - -static HEAP *serverlist_heap = 0; -static SERVERENTRY **serverlist = 0; -static int *sorted_serverlist = 0; - -enum -{ - MAX_FAVORITES=256 -}; - -static NETADDR favorite_servers[MAX_FAVORITES]; -static int num_favorite_servers = 0; - -static SERVERENTRY *serverlist_ip[256] = {0}; /* ip hash list */ - -static SERVERENTRY *first_req_server = 0; /* request list */ -static SERVERENTRY *last_req_server = 0; -static int num_requests = 0; - -static int need_refresh = 0; - -static int num_sorted_servers = 0; -static int num_sorted_servers_capacity = 0; -static int num_servers = 0; -static int num_server_capacity = 0; - -static int sorthash = 0; -static char filterstring[64] = {0}; -static char filtergametypestring[128] = {0}; - -/* the token is to keep server refresh separated from each other */ -static int current_token = 1; - -static int serverlist_type = 0; -static int64 broadcast_time = 0; - -int client_serverbrowse_lan() { return serverlist_type == BROWSETYPE_LAN; } -int client_serverbrowse_num() { return num_servers; } - -SERVER_INFO *client_serverbrowse_get(int index) -{ - if(index < 0 || index >= num_servers) - return 0; - return &serverlist[index]->info; -} - -int client_serverbrowse_sorted_num() { return num_sorted_servers; } - -SERVER_INFO *client_serverbrowse_sorted_get(int index) -{ - if(index < 0 || index >= num_sorted_servers) - return 0; - return &serverlist[sorted_serverlist[index]]->info; -} - - -int client_serverbrowse_num_requests() -{ - return num_requests; -} - -static int client_serverbrowse_sort_compare_name(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return strcmp(a->info.name, b->info.name); -} - -static int client_serverbrowse_sort_compare_map(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return strcmp(a->info.map, b->info.map); -} - -static int client_serverbrowse_sort_compare_ping(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - if(a->info.latency > b->info.latency) return 1; - if(a->info.latency < b->info.latency) return -1; - return 0; -} - -static int client_serverbrowse_sort_compare_gametype(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return strcmp(a->info.gametype, b->info.gametype); -} - -static int client_serverbrowse_sort_compare_progression(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - if(a->info.progression > b->info.progression) return 1; - if(a->info.progression < b->info.progression) return -1; - return 0; -} - -static int client_serverbrowse_sort_compare_numplayers(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - if(a->info.num_players > b->info.num_players) return 1; - if(a->info.num_players < b->info.num_players) return -1; - return 0; -} - -static void client_serverbrowse_filter() -{ - int i = 0, p = 0; - num_sorted_servers = 0; - - /* allocate the sorted list */ - if(num_sorted_servers_capacity < num_servers) - { - if(sorted_serverlist) - mem_free(sorted_serverlist); - num_sorted_servers_capacity = num_servers; - sorted_serverlist = mem_alloc(num_sorted_servers_capacity*sizeof(int), 1); - } - - /* filter the servers */ - for(i = 0; i < num_servers; i++) - { - int filtered = 0; - - if(config.b_filter_empty && serverlist[i]->info.num_players == 0) - filtered = 1; - else if(config.b_filter_full && serverlist[i]->info.num_players == serverlist[i]->info.max_players) - filtered = 1; - else if(config.b_filter_pw && serverlist[i]->info.flags&SRVFLAG_PASSWORD) - filtered = 1; - else if(config.b_filter_pure && (strcmp(serverlist[i]->info.gametype, "DM") != 0 && strcmp(serverlist[i]->info.gametype, "TDM") != 0 && strcmp(serverlist[i]->info.gametype, "CTF") != 0)) - filtered = 1; - else if(config.b_filter_pure_map && - !(strcmp(serverlist[i]->info.map, "dm1") == 0 || - strcmp(serverlist[i]->info.map, "dm2") == 0 || - strcmp(serverlist[i]->info.map, "dm6") == 0 || - strcmp(serverlist[i]->info.map, "dm7") == 0 || - strcmp(serverlist[i]->info.map, "dm8") == 0 || - strcmp(serverlist[i]->info.map, "dm9") == 0 || - strcmp(serverlist[i]->info.map, "ctf1") == 0 || - strcmp(serverlist[i]->info.map, "ctf2") == 0 || - strcmp(serverlist[i]->info.map, "ctf3") == 0 || - strcmp(serverlist[i]->info.map, "ctf4") == 0 || - strcmp(serverlist[i]->info.map, "ctf5") == 0) - ) - { - filtered = 1; - } - else if(config.b_filter_ping < serverlist[i]->info.latency) - filtered = 1; - else if(config.b_filter_compatversion && strncmp(serverlist[i]->info.version, modc_net_version(), 3) != 0) - filtered = 1; - else - { - if(config.b_filter_string[0] != 0) - { - int matchfound = 0; - - serverlist[i]->info.quicksearch_hit = 0; - - /* match against server name */ - if(str_find_nocase(serverlist[i]->info.name, config.b_filter_string)) - { - matchfound = 1; - serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_SERVERNAME; - } - - /* match against players */ - for(p = 0; p < serverlist[i]->info.num_players; p++) - { - if(str_find_nocase(serverlist[i]->info.players[p].name, config.b_filter_string)) - { - matchfound = 1; - serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_PLAYERNAME; - break; - } - } - - /* match against map */ - if(str_find_nocase(serverlist[i]->info.map, config.b_filter_string)) - { - matchfound = 1; - serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_MAPNAME; - } - - if(!matchfound) - filtered = 1; - } - - if(!filtered && config.b_filter_gametype[0] != 0) - { - /* match against game type */ - if(!str_find_nocase(serverlist[i]->info.gametype, config.b_filter_gametype)) - filtered = 1; - } - } - - if(filtered == 0) - sorted_serverlist[num_sorted_servers++] = i; - } -} - -static int client_serverbrowse_sorthash() -{ - int i = config.b_sort&0xf; - i |= config.b_filter_empty<<4; - i |= config.b_filter_full<<5; - i |= config.b_filter_pw<<6; - i |= config.b_sort_order<<7; - i |= config.b_filter_compatversion<<8; - i |= config.b_filter_pure<<9; - i |= config.b_filter_pure_map<<10; - i |= config.b_filter_ping<<16; - return i; -} - -static void client_serverbrowse_sort() -{ - int i; - - /* create filtered list */ - client_serverbrowse_filter(); - - /* sort */ - if(config.b_sort == BROWSESORT_NAME) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_name); - else if(config.b_sort == BROWSESORT_PING) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_ping); - else if(config.b_sort == BROWSESORT_MAP) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_map); - else if(config.b_sort == BROWSESORT_NUMPLAYERS) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_numplayers); - else if(config.b_sort == BROWSESORT_GAMETYPE) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_gametype); - else if(config.b_sort == BROWSESORT_PROGRESSION) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_progression); - - /* invert the list if requested */ - if(config.b_sort_order) - { - for(i = 0; i < num_sorted_servers/2; i++) - { - int temp = sorted_serverlist[i]; - sorted_serverlist[i] = sorted_serverlist[num_sorted_servers-i-1]; - sorted_serverlist[num_sorted_servers-i-1] = temp; - } - } - - /* set indexes */ - for(i = 0; i < num_sorted_servers; i++) - serverlist[sorted_serverlist[i]]->info.sorted_index = i; - - str_copy(filtergametypestring, config.b_filter_gametype, sizeof(filtergametypestring)); - str_copy(filterstring, config.b_filter_string, sizeof(filterstring)); - sorthash = client_serverbrowse_sorthash(); -} - -static void client_serverbrowse_remove_request(SERVERENTRY *entry) -{ - if(entry->prev_req || entry->next_req || first_req_server == entry) - { - if(entry->prev_req) - entry->prev_req->next_req = entry->next_req; - else - first_req_server = entry->next_req; - - if(entry->next_req) - entry->next_req->prev_req = entry->prev_req; - else - last_req_server = entry->prev_req; - - entry->prev_req = 0; - entry->next_req = 0; - num_requests--; - } -} - -static SERVERENTRY *client_serverbrowse_find(NETADDR *addr) -{ - SERVERENTRY *entry = serverlist_ip[addr->ip[0]]; - - for(; entry; entry = entry->next_ip) - { - if(net_addr_comp(&entry->addr, addr) == 0) - return entry; - } - return (SERVERENTRY*)0; -} - -void client_serverbrowse_queuerequest(SERVERENTRY *entry) -{ - /* add it to the list of servers that we should request info from */ - entry->prev_req = last_req_server; - if(last_req_server) - last_req_server->next_req = entry; - else - first_req_server = entry; - last_req_server = entry; - - num_requests++; -} - -void client_serverbrowse_setinfo(SERVERENTRY *entry, SERVER_INFO *info) -{ - int fav = entry->info.favorite; - entry->info = *info; - entry->info.favorite = fav; - entry->info.netaddr = entry->addr; - - // all these are just for nice compability - if(entry->info.gametype[0] == '0' && entry->info.gametype[1] == 0) - str_copy(entry->info.gametype, "DM", sizeof(entry->info.gametype)); - else if(entry->info.gametype[0] == '1' && entry->info.gametype[1] == 0) - str_copy(entry->info.gametype, "TDM", sizeof(entry->info.gametype)); - else if(entry->info.gametype[0] == '2' && entry->info.gametype[1] == 0) - str_copy(entry->info.gametype, "CTF", sizeof(entry->info.gametype)); - - /*if(!request) - { - entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); - client_serverbrowse_remove_request(entry); - }*/ - - entry->got_info = 1; - client_serverbrowse_sort(); -} - -SERVERENTRY *client_serverbrowse_add(NETADDR *addr) -{ - int hash = addr->ip[0]; - SERVERENTRY *entry = 0; - int i; - - /* create new entry */ - entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY)); - mem_zero(entry, sizeof(SERVERENTRY)); - - /* set the info */ - entry->addr = *addr; - entry->info.netaddr = *addr; - - entry->info.latency = 999; - str_format(entry->info.address, sizeof(entry->info.address), "%d.%d.%d.%d:%d", - addr->ip[0], addr->ip[1], addr->ip[2], - addr->ip[3], addr->port); - str_format(entry->info.name, sizeof(entry->info.name), "\255%d.%d.%d.%d:%d", /* the \255 is to make sure that it's sorted last */ - addr->ip[0], addr->ip[1], addr->ip[2], - addr->ip[3], addr->port); - - /*if(serverlist_type == BROWSETYPE_LAN) - entry->info.latency = (time_get()-broadcast_time)*1000/time_freq();*/ - - /* check if it's a favorite */ - for(i = 0; i < num_favorite_servers; i++) - { - if(net_addr_comp(addr, &favorite_servers[i]) == 0) - entry->info.favorite = 1; - } - - /* add to the hash list */ - entry->next_ip = serverlist_ip[hash]; - serverlist_ip[hash] = entry; - - if(num_servers == num_server_capacity) - { - SERVERENTRY **newlist; - num_server_capacity += 100; - newlist = mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1); - mem_copy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*)); - mem_free(serverlist); - serverlist = newlist; - } - - /* add to list */ - serverlist[num_servers] = entry; - entry->info.server_index = num_servers; - num_servers++; - - return entry; -} - -void client_serverbrowse_set(NETADDR *addr, int type, int token, SERVER_INFO *info) -{ - SERVERENTRY *entry = 0; - if(type == BROWSESET_MASTER_ADD) - { - if(serverlist_type != BROWSETYPE_INTERNET) - return; - - if(!client_serverbrowse_find(addr)) - { - entry = client_serverbrowse_add(addr); - client_serverbrowse_queuerequest(entry); - } - } - else if(type == BROWSESET_FAV_ADD) - { - if(serverlist_type != BROWSETYPE_FAVORITES) - return; - - if(!client_serverbrowse_find(addr)) - { - entry = client_serverbrowse_add(addr); - client_serverbrowse_queuerequest(entry); - } - } - else if(type == BROWSESET_TOKEN) - { - if(token != current_token) - return; - - entry = client_serverbrowse_find(addr); - if(!entry) - entry = client_serverbrowse_add(addr); - if(entry) - { - client_serverbrowse_setinfo(entry, info); - if(serverlist_type == BROWSETYPE_LAN) - entry->info.latency = (time_get()-broadcast_time)*1000/time_freq(); - else - entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); - client_serverbrowse_remove_request(entry); - } - } - else if(type == BROWSESET_OLD_INTERNET) - { - entry = client_serverbrowse_find(addr); - if(entry) - { - client_serverbrowse_setinfo(entry, info); - - if(serverlist_type == BROWSETYPE_LAN) - entry->info.latency = (time_get()-broadcast_time)*1000/time_freq(); - else - entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); - client_serverbrowse_remove_request(entry); - } - } - else if(type == BROWSESET_OLD_LAN) - { - entry = client_serverbrowse_find(addr); - if(entry) - if(!entry) - entry = client_serverbrowse_add(addr); - if(entry) - client_serverbrowse_setinfo(entry, info); - } - - client_serverbrowse_sort(); -} - -void client_serverbrowse_refresh(int type) -{ - /* clear out everything */ - if(serverlist_heap) - memheap_destroy(serverlist_heap); - serverlist_heap = memheap_create(); - num_servers = 0; - num_sorted_servers = 0; - mem_zero(serverlist_ip, sizeof(serverlist_ip)); - first_req_server = 0; - last_req_server = 0; - num_requests = 0; - - /* next token */ - current_token = (current_token+1)&0xff; - - /* */ - serverlist_type = type; - - if(type == BROWSETYPE_LAN) - { - unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1]; - NETCHUNK packet; - int i; - - mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); - buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token; - - packet.client_id = -1; - mem_zero(&packet, sizeof(packet)); - packet.address.ip[0] = 255; - packet.address.ip[1] = 255; - packet.address.ip[2] = 255; - packet.address.ip[3] = 255; - packet.flags = NETSENDFLAG_CONNLESS; - packet.data_size = sizeof(buffer); - packet.data = buffer; - broadcast_time = time_get(); - - for(i = 8303; i <= 8310; i++) - { - packet.address.port = i; - netclient_send(net, &packet); - } - - if(config.debug) - dbg_msg("client", "broadcasting for servers"); - } - else if(type == BROWSETYPE_INTERNET) - need_refresh = 1; - else if(type == BROWSETYPE_FAVORITES) - { - int i; - for(i = 0; i < num_favorite_servers; i++) - client_serverbrowse_set(&favorite_servers[i], BROWSESET_FAV_ADD, -1, 0); - } -} - -static void client_serverbrowse_request_impl(NETADDR *addr, SERVERENTRY *entry) -{ - /*unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];*/ - NETCHUNK p; - - if(config.debug) - { - dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", - addr->ip[0], addr->ip[1], addr->ip[2], - addr->ip[3], addr->port); - } - - /*mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); - buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;*/ - - p.client_id = -1; - p.address = *addr; - p.flags = NETSENDFLAG_CONNLESS; - /*p.data_size = sizeof(buffer); - p.data = buffer; - netclient_send(net, &p);*/ - - /* send old requtest style aswell */ - p.data_size = sizeof(SERVERBROWSE_OLD_GETINFO); - p.data = SERVERBROWSE_OLD_GETINFO; - netclient_send(net, &p); - - if(entry) - entry->request_time = time_get(); -} - -void client_serverbrowse_request(NETADDR *addr) -{ - client_serverbrowse_request_impl(addr, 0); -} - - -void client_serverbrowse_update() -{ - int64 timeout = time_freq(); - int64 now = time_get(); - int count; - SERVERENTRY *entry, *next; - - /* do server list requests */ - if(need_refresh && !mastersrv_refreshing()) - { - NETADDR addr; - NETCHUNK p; - int i; - - need_refresh = 0; - - mem_zero(&p, sizeof(p)); - p.client_id = -1; - p.flags = NETSENDFLAG_CONNLESS; - p.data_size = sizeof(SERVERBROWSE_GETLIST); - p.data = SERVERBROWSE_GETLIST; - - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - addr = mastersrv_get(i); - if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3]) - continue; - - p.address = addr; - netclient_send(net, &p); - } - - if(config.debug) - dbg_msg("client", "requesting server list"); - } - - /* do timeouts */ - entry = first_req_server; - while(1) - { - if(!entry) /* no more entries */ - break; - - next = entry->next_req; - - if(entry->request_time && entry->request_time+timeout < now) - { - /* timeout */ - client_serverbrowse_remove_request(entry); - num_requests--; - } - - entry = next; - } - - /* do timeouts */ - entry = first_req_server; - count = 0; - while(1) - { - if(!entry) /* no more entries */ - break; - - /* no more then 10 concurrent requests */ - if(count == config.b_max_requests) - break; - - if(entry->request_time == 0) - client_serverbrowse_request_impl(&entry->addr, entry); - - count++; - entry = entry->next_req; - } - - /* check if we need to resort */ - /* TODO: remove the strcmp */ - if(sorthash != client_serverbrowse_sorthash() || strcmp(filterstring, config.b_filter_string) != 0 || strcmp(filtergametypestring, config.b_filter_gametype) != 0) - client_serverbrowse_sort(); -} - - -int client_serverbrowse_isfavorite(NETADDR addr) -{ - /* search for the address */ - int i; - for(i = 0; i < num_favorite_servers; i++) - { - if(net_addr_comp(&addr, &favorite_servers[i]) == 0) - return 1; - } - return 0; -} - -void client_serverbrowse_addfavorite(NETADDR addr) -{ - int i; - SERVERENTRY *entry; - - if(num_favorite_servers == MAX_FAVORITES) - return; - - /* make sure that we don't already have the server in our list */ - for(i = 0; i < num_favorite_servers; i++) - { - if(net_addr_comp(&addr, &favorite_servers[i]) == 0) - return; - } - - /* add the server to the list */ - favorite_servers[num_favorite_servers++] = addr; - entry = client_serverbrowse_find(&addr); - if(entry) - entry->info.favorite = 1; - dbg_msg("", "added fav, %p", entry); -} - -void client_serverbrowse_removefavorite(NETADDR addr) -{ - int i; - SERVERENTRY *entry; - - for(i = 0; i < num_favorite_servers; i++) - { - if(net_addr_comp(&addr, &favorite_servers[i]) == 0) - { - mem_move(&favorite_servers[i], &favorite_servers[i+1], num_favorite_servers-(i+1)); - num_favorite_servers--; - - entry = client_serverbrowse_find(&addr); - if(entry) - entry->info.favorite = 0; - - return; - } - } -} - -void client_serverbrowse_save() -{ - int i; - char addrstr[128]; - char buffer[256]; - for(i = 0; i < num_favorite_servers; i++) - { - net_addr_str(&favorite_servers[i], addrstr, sizeof(addrstr)); - str_format(buffer, sizeof(buffer), "add_favorite %s", addrstr); - engine_config_write_line(buffer); - } -} - - -int client_serverbrowse_refreshingmasters() -{ - return mastersrv_refreshing(); -} diff --git a/src/engine/client/ec_srvbrowse.cpp b/src/engine/client/ec_srvbrowse.cpp new file mode 100644 index 00000000..1b04937a --- /dev/null +++ b/src/engine/client/ec_srvbrowse.cpp @@ -0,0 +1,736 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +extern CNetClient m_NetClient; + + +/* ------ server browse ---- */ +/* TODO: move all this to a separate file */ + +typedef struct SERVERENTRY_t SERVERENTRY; +struct SERVERENTRY_t +{ + NETADDR addr; + int64 request_time; + int got_info; + SERVER_INFO info; + + SERVERENTRY *next_ip; /* ip hashed list */ + + SERVERENTRY *prev_req; /* request list */ + SERVERENTRY *next_req; +}; + +static HEAP *serverlist_heap = 0; +static SERVERENTRY **serverlist = 0; +static int *sorted_serverlist = 0; + +enum +{ + MAX_FAVORITES=256 +}; + +static NETADDR favorite_servers[MAX_FAVORITES]; +static int num_favorite_servers = 0; + +static SERVERENTRY *serverlist_ip[256] = {0}; /* ip hash list */ + +static SERVERENTRY *first_req_server = 0; /* request list */ +static SERVERENTRY *last_req_server = 0; +static int num_requests = 0; + +static int need_refresh = 0; + +static int num_sorted_servers = 0; +static int num_sorted_servers_capacity = 0; +static int num_servers = 0; +static int num_server_capacity = 0; + +static int sorthash = 0; +static char filterstring[64] = {0}; +static char filtergametypestring[128] = {0}; + +/* the token is to keep server refresh separated from each other */ +static int current_token = 1; + +static int serverlist_type = 0; +static int64 broadcast_time = 0; + +int client_serverbrowse_lan() { return serverlist_type == BROWSETYPE_LAN; } +int client_serverbrowse_num() { return num_servers; } + +SERVER_INFO *client_serverbrowse_get(int index) +{ + if(index < 0 || index >= num_servers) + return 0; + return &serverlist[index]->info; +} + +int client_serverbrowse_sorted_num() { return num_sorted_servers; } + +SERVER_INFO *client_serverbrowse_sorted_get(int index) +{ + if(index < 0 || index >= num_sorted_servers) + return 0; + return &serverlist[sorted_serverlist[index]]->info; +} + + +int client_serverbrowse_num_requests() +{ + return num_requests; +} + +static int client_serverbrowse_sort_compare_name(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return strcmp(a->info.name, b->info.name); +} + +static int client_serverbrowse_sort_compare_map(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return strcmp(a->info.map, b->info.map); +} + +static int client_serverbrowse_sort_compare_ping(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + if(a->info.latency > b->info.latency) return 1; + if(a->info.latency < b->info.latency) return -1; + return 0; +} + +static int client_serverbrowse_sort_compare_gametype(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return strcmp(a->info.gametype, b->info.gametype); +} + +static int client_serverbrowse_sort_compare_progression(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + if(a->info.progression > b->info.progression) return 1; + if(a->info.progression < b->info.progression) return -1; + return 0; +} + +static int client_serverbrowse_sort_compare_numplayers(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + if(a->info.num_players > b->info.num_players) return 1; + if(a->info.num_players < b->info.num_players) return -1; + return 0; +} + +static void client_serverbrowse_filter() +{ + int i = 0, p = 0; + num_sorted_servers = 0; + + /* allocate the sorted list */ + if(num_sorted_servers_capacity < num_servers) + { + if(sorted_serverlist) + mem_free(sorted_serverlist); + num_sorted_servers_capacity = num_servers; + sorted_serverlist = (int *)mem_alloc(num_sorted_servers_capacity*sizeof(int), 1); + } + + /* filter the servers */ + for(i = 0; i < num_servers; i++) + { + int filtered = 0; + + if(config.b_filter_empty && serverlist[i]->info.num_players == 0) + filtered = 1; + else if(config.b_filter_full && serverlist[i]->info.num_players == serverlist[i]->info.max_players) + filtered = 1; + else if(config.b_filter_pw && serverlist[i]->info.flags&SRVFLAG_PASSWORD) + filtered = 1; + else if(config.b_filter_pure && (strcmp(serverlist[i]->info.gametype, "DM") != 0 && strcmp(serverlist[i]->info.gametype, "TDM") != 0 && strcmp(serverlist[i]->info.gametype, "CTF") != 0)) + filtered = 1; + else if(config.b_filter_pure_map && + !(strcmp(serverlist[i]->info.map, "dm1") == 0 || + strcmp(serverlist[i]->info.map, "dm2") == 0 || + strcmp(serverlist[i]->info.map, "dm6") == 0 || + strcmp(serverlist[i]->info.map, "dm7") == 0 || + strcmp(serverlist[i]->info.map, "dm8") == 0 || + strcmp(serverlist[i]->info.map, "dm9") == 0 || + strcmp(serverlist[i]->info.map, "ctf1") == 0 || + strcmp(serverlist[i]->info.map, "ctf2") == 0 || + strcmp(serverlist[i]->info.map, "ctf3") == 0 || + strcmp(serverlist[i]->info.map, "ctf4") == 0 || + strcmp(serverlist[i]->info.map, "ctf5") == 0) + ) + { + filtered = 1; + } + else if(config.b_filter_ping < serverlist[i]->info.latency) + filtered = 1; + else if(config.b_filter_compatversion && strncmp(serverlist[i]->info.version, modc_net_version(), 3) != 0) + filtered = 1; + else + { + if(config.b_filter_string[0] != 0) + { + int matchfound = 0; + + serverlist[i]->info.quicksearch_hit = 0; + + /* match against server name */ + if(str_find_nocase(serverlist[i]->info.name, config.b_filter_string)) + { + matchfound = 1; + serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_SERVERNAME; + } + + /* match against players */ + for(p = 0; p < serverlist[i]->info.num_players; p++) + { + if(str_find_nocase(serverlist[i]->info.players[p].name, config.b_filter_string)) + { + matchfound = 1; + serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_PLAYERNAME; + break; + } + } + + /* match against map */ + if(str_find_nocase(serverlist[i]->info.map, config.b_filter_string)) + { + matchfound = 1; + serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_MAPNAME; + } + + if(!matchfound) + filtered = 1; + } + + if(!filtered && config.b_filter_gametype[0] != 0) + { + /* match against game type */ + if(!str_find_nocase(serverlist[i]->info.gametype, config.b_filter_gametype)) + filtered = 1; + } + } + + if(filtered == 0) + sorted_serverlist[num_sorted_servers++] = i; + } +} + +static int client_serverbrowse_sorthash() +{ + int i = config.b_sort&0xf; + i |= config.b_filter_empty<<4; + i |= config.b_filter_full<<5; + i |= config.b_filter_pw<<6; + i |= config.b_sort_order<<7; + i |= config.b_filter_compatversion<<8; + i |= config.b_filter_pure<<9; + i |= config.b_filter_pure_map<<10; + i |= config.b_filter_ping<<16; + return i; +} + +static void client_serverbrowse_sort() +{ + int i; + + /* create filtered list */ + client_serverbrowse_filter(); + + /* sort */ + if(config.b_sort == BROWSESORT_NAME) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_name); + else if(config.b_sort == BROWSESORT_PING) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_ping); + else if(config.b_sort == BROWSESORT_MAP) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_map); + else if(config.b_sort == BROWSESORT_NUMPLAYERS) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_numplayers); + else if(config.b_sort == BROWSESORT_GAMETYPE) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_gametype); + else if(config.b_sort == BROWSESORT_PROGRESSION) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_progression); + + /* invert the list if requested */ + if(config.b_sort_order) + { + for(i = 0; i < num_sorted_servers/2; i++) + { + int temp = sorted_serverlist[i]; + sorted_serverlist[i] = sorted_serverlist[num_sorted_servers-i-1]; + sorted_serverlist[num_sorted_servers-i-1] = temp; + } + } + + /* set indexes */ + for(i = 0; i < num_sorted_servers; i++) + serverlist[sorted_serverlist[i]]->info.sorted_index = i; + + str_copy(filtergametypestring, config.b_filter_gametype, sizeof(filtergametypestring)); + str_copy(filterstring, config.b_filter_string, sizeof(filterstring)); + sorthash = client_serverbrowse_sorthash(); +} + +static void client_serverbrowse_remove_request(SERVERENTRY *entry) +{ + if(entry->prev_req || entry->next_req || first_req_server == entry) + { + if(entry->prev_req) + entry->prev_req->next_req = entry->next_req; + else + first_req_server = entry->next_req; + + if(entry->next_req) + entry->next_req->prev_req = entry->prev_req; + else + last_req_server = entry->prev_req; + + entry->prev_req = 0; + entry->next_req = 0; + num_requests--; + } +} + +static SERVERENTRY *client_serverbrowse_find(NETADDR *addr) +{ + SERVERENTRY *entry = serverlist_ip[addr->ip[0]]; + + for(; entry; entry = entry->next_ip) + { + if(net_addr_comp(&entry->addr, addr) == 0) + return entry; + } + return (SERVERENTRY*)0; +} + +void client_serverbrowse_queuerequest(SERVERENTRY *entry) +{ + /* add it to the list of servers that we should request info from */ + entry->prev_req = last_req_server; + if(last_req_server) + last_req_server->next_req = entry; + else + first_req_server = entry; + last_req_server = entry; + + num_requests++; +} + +void client_serverbrowse_setinfo(SERVERENTRY *entry, SERVER_INFO *info) +{ + int fav = entry->info.favorite; + entry->info = *info; + entry->info.favorite = fav; + entry->info.netaddr = entry->addr; + + // all these are just for nice compability + if(entry->info.gametype[0] == '0' && entry->info.gametype[1] == 0) + str_copy(entry->info.gametype, "DM", sizeof(entry->info.gametype)); + else if(entry->info.gametype[0] == '1' && entry->info.gametype[1] == 0) + str_copy(entry->info.gametype, "TDM", sizeof(entry->info.gametype)); + else if(entry->info.gametype[0] == '2' && entry->info.gametype[1] == 0) + str_copy(entry->info.gametype, "CTF", sizeof(entry->info.gametype)); + + /*if(!request) + { + entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); + client_serverbrowse_remove_request(entry); + }*/ + + entry->got_info = 1; + client_serverbrowse_sort(); +} + +SERVERENTRY *client_serverbrowse_add(NETADDR *addr) +{ + int hash = addr->ip[0]; + SERVERENTRY *entry = 0; + int i; + + /* create new entry */ + entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY)); + mem_zero(entry, sizeof(SERVERENTRY)); + + /* set the info */ + entry->addr = *addr; + entry->info.netaddr = *addr; + + entry->info.latency = 999; + str_format(entry->info.address, sizeof(entry->info.address), "%d.%d.%d.%d:%d", + addr->ip[0], addr->ip[1], addr->ip[2], + addr->ip[3], addr->port); + str_format(entry->info.name, sizeof(entry->info.name), "\255%d.%d.%d.%d:%d", /* the \255 is to make sure that it's sorted last */ + addr->ip[0], addr->ip[1], addr->ip[2], + addr->ip[3], addr->port); + + /*if(serverlist_type == BROWSETYPE_LAN) + entry->info.latency = (time_get()-broadcast_time)*1000/time_freq();*/ + + /* check if it's a favorite */ + for(i = 0; i < num_favorite_servers; i++) + { + if(net_addr_comp(addr, &favorite_servers[i]) == 0) + entry->info.favorite = 1; + } + + /* add to the hash list */ + entry->next_ip = serverlist_ip[hash]; + serverlist_ip[hash] = entry; + + if(num_servers == num_server_capacity) + { + SERVERENTRY **newlist; + num_server_capacity += 100; + newlist = (SERVERENTRY **)mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1); + mem_copy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*)); + mem_free(serverlist); + serverlist = newlist; + } + + /* add to list */ + serverlist[num_servers] = entry; + entry->info.server_index = num_servers; + num_servers++; + + return entry; +} + +void client_serverbrowse_set(NETADDR *addr, int type, int token, SERVER_INFO *info) +{ + SERVERENTRY *entry = 0; + if(type == BROWSESET_MASTER_ADD) + { + if(serverlist_type != BROWSETYPE_INTERNET) + return; + + if(!client_serverbrowse_find(addr)) + { + entry = client_serverbrowse_add(addr); + client_serverbrowse_queuerequest(entry); + } + } + else if(type == BROWSESET_FAV_ADD) + { + if(serverlist_type != BROWSETYPE_FAVORITES) + return; + + if(!client_serverbrowse_find(addr)) + { + entry = client_serverbrowse_add(addr); + client_serverbrowse_queuerequest(entry); + } + } + else if(type == BROWSESET_TOKEN) + { + if(token != current_token) + return; + + entry = client_serverbrowse_find(addr); + if(!entry) + entry = client_serverbrowse_add(addr); + if(entry) + { + client_serverbrowse_setinfo(entry, info); + if(serverlist_type == BROWSETYPE_LAN) + entry->info.latency = (time_get()-broadcast_time)*1000/time_freq(); + else + entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); + client_serverbrowse_remove_request(entry); + } + } + else if(type == BROWSESET_OLD_INTERNET) + { + entry = client_serverbrowse_find(addr); + if(entry) + { + client_serverbrowse_setinfo(entry, info); + + if(serverlist_type == BROWSETYPE_LAN) + entry->info.latency = (time_get()-broadcast_time)*1000/time_freq(); + else + entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); + client_serverbrowse_remove_request(entry); + } + } + else if(type == BROWSESET_OLD_LAN) + { + entry = client_serverbrowse_find(addr); + if(entry) + if(!entry) + entry = client_serverbrowse_add(addr); + if(entry) + client_serverbrowse_setinfo(entry, info); + } + + client_serverbrowse_sort(); +} + +void client_serverbrowse_refresh(int type) +{ + /* clear out everything */ + if(serverlist_heap) + memheap_destroy(serverlist_heap); + serverlist_heap = memheap_create(); + num_servers = 0; + num_sorted_servers = 0; + mem_zero(serverlist_ip, sizeof(serverlist_ip)); + first_req_server = 0; + last_req_server = 0; + num_requests = 0; + + /* next token */ + current_token = (current_token+1)&0xff; + + /* */ + serverlist_type = type; + + if(type == BROWSETYPE_LAN) + { + unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1]; + CNetChunk Packet; + int i; + + mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); + Buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token; + + Packet.m_ClientID = -1; + mem_zero(&Packet, sizeof(Packet)); + Packet.m_Address.ip[0] = 255; + Packet.m_Address.ip[1] = 255; + Packet.m_Address.ip[2] = 255; + Packet.m_Address.ip[3] = 255; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(Buffer); + Packet.m_pData = Buffer; + broadcast_time = time_get(); + + for(i = 8303; i <= 8310; i++) + { + Packet.m_Address.port = i; + m_NetClient.Send(&Packet); + } + + if(config.debug) + dbg_msg("client", "broadcasting for servers"); + } + else if(type == BROWSETYPE_INTERNET) + need_refresh = 1; + else if(type == BROWSETYPE_FAVORITES) + { + for(int i = 0; i < num_favorite_servers; i++) + client_serverbrowse_set(&favorite_servers[i], BROWSESET_FAV_ADD, -1, 0); + } +} + +static void client_serverbrowse_request_impl(NETADDR *addr, SERVERENTRY *entry) +{ + /*unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];*/ + CNetChunk Packet; + + if(config.debug) + { + dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", + addr->ip[0], addr->ip[1], addr->ip[2], + addr->ip[3], addr->port); + } + + /*mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); + buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;*/ + + Packet.m_ClientID = -1; + Packet.m_Address = *addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + /*p.data_size = sizeof(buffer); + p.data = buffer; + netclient_send(net, &p);*/ + + /* send old requtest style aswell */ + Packet.m_DataSize = sizeof(SERVERBROWSE_OLD_GETINFO); + Packet.m_pData = SERVERBROWSE_OLD_GETINFO; + m_NetClient.Send(&Packet); + + if(entry) + entry->request_time = time_get(); +} + +void client_serverbrowse_request(NETADDR *addr) +{ + client_serverbrowse_request_impl(addr, 0); +} + + +void client_serverbrowse_update() +{ + int64 timeout = time_freq(); + int64 now = time_get(); + int count; + SERVERENTRY *entry, *next; + + /* do server list requests */ + if(need_refresh && !mastersrv_refreshing()) + { + NETADDR addr; + CNetChunk Packet; + int i; + + need_refresh = 0; + + mem_zero(&Packet, sizeof(Packet)); + Packet.m_ClientID = -1; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_GETLIST); + Packet.m_pData = SERVERBROWSE_GETLIST; + + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + addr = mastersrv_get(i); + if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3]) + continue; + + Packet.m_Address = addr; + m_NetClient.Send(&Packet); + } + + if(config.debug) + dbg_msg("client", "requesting server list"); + } + + /* do timeouts */ + entry = first_req_server; + while(1) + { + if(!entry) /* no more entries */ + break; + + next = entry->next_req; + + if(entry->request_time && entry->request_time+timeout < now) + { + /* timeout */ + client_serverbrowse_remove_request(entry); + num_requests--; + } + + entry = next; + } + + /* do timeouts */ + entry = first_req_server; + count = 0; + while(1) + { + if(!entry) /* no more entries */ + break; + + /* no more then 10 concurrent requests */ + if(count == config.b_max_requests) + break; + + if(entry->request_time == 0) + client_serverbrowse_request_impl(&entry->addr, entry); + + count++; + entry = entry->next_req; + } + + /* check if we need to resort */ + /* TODO: remove the strcmp */ + if(sorthash != client_serverbrowse_sorthash() || strcmp(filterstring, config.b_filter_string) != 0 || strcmp(filtergametypestring, config.b_filter_gametype) != 0) + client_serverbrowse_sort(); +} + + +int client_serverbrowse_isfavorite(NETADDR addr) +{ + /* search for the address */ + int i; + for(i = 0; i < num_favorite_servers; i++) + { + if(net_addr_comp(&addr, &favorite_servers[i]) == 0) + return 1; + } + return 0; +} + +void client_serverbrowse_addfavorite(NETADDR addr) +{ + int i; + SERVERENTRY *entry; + + if(num_favorite_servers == MAX_FAVORITES) + return; + + /* make sure that we don't already have the server in our list */ + for(i = 0; i < num_favorite_servers; i++) + { + if(net_addr_comp(&addr, &favorite_servers[i]) == 0) + return; + } + + /* add the server to the list */ + favorite_servers[num_favorite_servers++] = addr; + entry = client_serverbrowse_find(&addr); + if(entry) + entry->info.favorite = 1; + dbg_msg("", "added fav, %p", entry); +} + +void client_serverbrowse_removefavorite(NETADDR addr) +{ + int i; + SERVERENTRY *entry; + + for(i = 0; i < num_favorite_servers; i++) + { + if(net_addr_comp(&addr, &favorite_servers[i]) == 0) + { + mem_move(&favorite_servers[i], &favorite_servers[i+1], num_favorite_servers-(i+1)); + num_favorite_servers--; + + entry = client_serverbrowse_find(&addr); + if(entry) + entry->info.favorite = 0; + + return; + } + } +} + +void client_serverbrowse_save() +{ + int i; + char addrstr[128]; + char buffer[256]; + for(i = 0; i < num_favorite_servers; i++) + { + net_addr_str(&favorite_servers[i], addrstr, sizeof(addrstr)); + str_format(buffer, sizeof(buffer), "add_favorite %s", addrstr); + engine_config_write_line(buffer); + } +} + + +int client_serverbrowse_refreshingmasters() +{ + return mastersrv_refreshing(); +} diff --git a/src/engine/e_client_interface.h b/src/engine/e_client_interface.h index 7ec5c93d..079eabca 100644 --- a/src/engine/e_client_interface.h +++ b/src/engine/e_client_interface.h @@ -2,10 +2,6 @@ #ifndef ENGINE_CLIENT_INTERFACE_H #define ENGINE_CLIENT_INTERFACE_H -#ifdef __cplusplus -extern "C" { -#endif - #include "e_if_other.h" #include "e_if_client.h" #include "e_if_snd.h" @@ -17,8 +13,4 @@ extern "C" { #include "e_console.h" #include "e_config.h" -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_common_interface.h b/src/engine/e_common_interface.h index 498bb6b4..9c95b48b 100644 --- a/src/engine/e_common_interface.h +++ b/src/engine/e_common_interface.h @@ -2,15 +2,7 @@ #ifndef ENGINE_COMMON_INTERFACE_H #define ENGINE_COMMON_INTERFACE_H -#ifdef __cplusplus -extern "C" { -#endif - #include "e_if_other.h" #include "e_if_msg.h" -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_compression.c b/src/engine/e_compression.c deleted file mode 100644 index 6a534feb..00000000 --- a/src/engine/e_compression.c +++ /dev/null @@ -1,148 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include - -/* 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; - - do - { - 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); - } while(0); - - 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_; - - while(size) - { - unsigned char bit = 0x80; - unsigned char mask = 0; - int dst_move = 1; - int chunk = size < 8 ? size : 8; - int b; - size -= chunk; - - for(b = 0; b < chunk; b++, bit>>=1) - { - if(*src) - { - dst[dst_move] = *src; - mask |= bit; - dst_move++; - } - - src++; - } - - *dst = mask; - dst += dst_move; - } - - return (long)(dst-(unsigned char *)dst_); -} - -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; - - while(src < end) - { - unsigned char bit = 0x80; - unsigned char mask = *src++; - int b; - - for(b = 0; b < 8; b++, bit>>=1) - { - if(mask&bit) - *dst++ = *src++; - else - *dst++ = 0; - } - - if(src > end) - return -1; - } - - return (long)(dst-(unsigned char *)dst_); -} - diff --git a/src/engine/e_compression.cpp b/src/engine/e_compression.cpp new file mode 100644 index 00000000..f4d6e0c0 --- /dev/null +++ b/src/engine/e_compression.cpp @@ -0,0 +1,86 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include + +/* 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; + + do + { + 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); + } while(0); + + 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_); +} + diff --git a/src/engine/e_compression.h b/src/engine/e_compression.h index 39ffd596..be5bf78f 100644 --- a/src/engine/e_compression.h +++ b/src/engine/e_compression.h @@ -5,7 +5,3 @@ 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); - -/* zerobit packing */ -long zerobit_compress(const void *src, int size, void *dst); -long zerobit_decompress(const void *src, int size, void *dst); diff --git a/src/engine/e_config.c b/src/engine/e_config.c deleted file mode 100644 index 67a4c81a..00000000 --- a/src/engine/e_config.c +++ /dev/null @@ -1,49 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include -#include -#include - -#include -#include "e_if_other.h" -#include "e_config.h" -#include "e_linereader.h" -#include "e_engine.h" - -CONFIGURATION config; - -void config_reset() -{ - #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) config.name = def; - #define MACRO_CONFIG_STR(name,len,def,flags,desc) str_copy(config.name, def, len); - - #include "e_config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} - -void config_save() -{ - char linebuf[1024*2]; - - #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(linebuf, sizeof(linebuf), "%s %i", #name, config.name); engine_config_write_line(linebuf); } - #define MACRO_CONFIG_STR(name,len,def,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(linebuf, sizeof(linebuf), "%s %s", #name, config.name); engine_config_write_line(linebuf); } - - #include "e_config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} - -#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) int config_get_ ## name (CONFIGURATION *c) { return c->name; } -#define MACRO_CONFIG_STR(name,len,def,flags,desc) const char *config_get_ ## name (CONFIGURATION *c) { return c->name; } -#include "e_config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR - -#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) void config_set_ ## name (CONFIGURATION *c, int val) { if(min != max) { if (val < min) val = min; if (max != 0 && val > max) val = max; } c->name = val; } -#define MACRO_CONFIG_STR(name,len,def,flags,desc) void config_set_ ## name (CONFIGURATION *c, const char *str) { str_copy(c->name, str, len-1); c->name[sizeof(c->name)-1] = 0; } -#include "e_config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR diff --git a/src/engine/e_config.cpp b/src/engine/e_config.cpp new file mode 100644 index 00000000..67a4c81a --- /dev/null +++ b/src/engine/e_config.cpp @@ -0,0 +1,49 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include +#include +#include + +#include +#include "e_if_other.h" +#include "e_config.h" +#include "e_linereader.h" +#include "e_engine.h" + +CONFIGURATION config; + +void config_reset() +{ + #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) config.name = def; + #define MACRO_CONFIG_STR(name,len,def,flags,desc) str_copy(config.name, def, len); + + #include "e_config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +} + +void config_save() +{ + char linebuf[1024*2]; + + #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(linebuf, sizeof(linebuf), "%s %i", #name, config.name); engine_config_write_line(linebuf); } + #define MACRO_CONFIG_STR(name,len,def,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(linebuf, sizeof(linebuf), "%s %s", #name, config.name); engine_config_write_line(linebuf); } + + #include "e_config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +} + +#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) int config_get_ ## name (CONFIGURATION *c) { return c->name; } +#define MACRO_CONFIG_STR(name,len,def,flags,desc) const char *config_get_ ## name (CONFIGURATION *c) { return c->name; } +#include "e_config_variables.h" +#undef MACRO_CONFIG_INT +#undef MACRO_CONFIG_STR + +#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) void config_set_ ## name (CONFIGURATION *c, int val) { if(min != max) { if (val < min) val = min; if (max != 0 && val > max) val = max; } c->name = val; } +#define MACRO_CONFIG_STR(name,len,def,flags,desc) void config_set_ ## name (CONFIGURATION *c, const char *str) { str_copy(c->name, str, len-1); c->name[sizeof(c->name)-1] = 0; } +#include "e_config_variables.h" +#undef MACRO_CONFIG_INT +#undef MACRO_CONFIG_STR diff --git a/src/engine/e_config.h b/src/engine/e_config.h index 965e08f0..6ca7a8ee 100644 --- a/src/engine/e_config.h +++ b/src/engine/e_config.h @@ -2,10 +2,6 @@ #ifndef _CONFIG_H #define _CONFIG_H -#ifdef __cplusplus -extern "C"{ -#endif - typedef struct { #define MACRO_CONFIG_INT(name,def,min,max,save,desc) int name; @@ -45,8 +41,4 @@ typedef void (*CONFIG_STR_SETTER)(CONFIGURATION *c, const char *str); #undef MACRO_CONFIG_INT #undef MACRO_CONFIG_STR -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_console.c b/src/engine/e_console.c deleted file mode 100644 index 5c0f37f3..00000000 --- a/src/engine/e_console.c +++ /dev/null @@ -1,464 +0,0 @@ -#include -#include "e_if_other.h" -#include "e_console.h" -#include "e_config.h" -#include "e_engine.h" -#include "e_linereader.h" -#include -#include -#include - - -#define CONSOLE_MAX_STR_LENGTH 1024 -/* the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces */ -#define MAX_PARTS (CONSOLE_MAX_STR_LENGTH+1)/2 - -typedef struct -{ - char string_storage[CONSOLE_MAX_STR_LENGTH+1]; - char *args_start; - - const char *command; - const char *args[MAX_PARTS]; - unsigned int num_args; -} PARSE_RESULT; - -static char *str_skipblanks(char *str) -{ - while(*str && (*str == ' ' || *str == '\t' || *str == '\n')) - str++; - return str; -} - -static char *str_skiptoblank(char *str) -{ - while(*str && (*str != ' ' && *str != '\t' && *str != '\n')) - str++; - return str; -} - -/* static int digit(char c) { return '0' <= c && c <= '9'; } */ - -static int console_parse_start(PARSE_RESULT *result, const char *string, int length) -{ - char *str; - int len = sizeof(result->string_storage); - if(length < len) - len = length; - - str_copy(result->string_storage, string, length); - str = result->string_storage; - - /* get command */ - str = str_skipblanks(str); - result->command = str; - str = str_skiptoblank(str); - - if(*str) - { - str[0] = 0; - str++; - } - - result->args_start = str; - result->num_args = 0; - return 0; -} - -static int console_parse_args(PARSE_RESULT *result, const char *format) -{ - char command; - char *str; - int optional = 0; - int error = 0; - - str = result->args_start; - - while(1) - { - /* fetch command */ - command = *format; - format++; - - if(!command) - break; - - if(command == '?') - optional = 1; - else - { - str = str_skipblanks(str); - - if(!(*str)) /* error, non optional command needs value */ - { - if(!optional) - error = 1; - break; - } - - /* add token */ - if(*str == '"') - { - char *dst; - str++; - result->args[result->num_args++] = str; - - dst = str; /* we might have to process escape data */ - while(1) - { - if(str[0] == '"') - break; - else if(str[0] == '\\') - { - if(str[1] == '\\') - str++; /* skip due to escape */ - else if(str[1] == '"') - str++; /* skip due to escape */ - } - else if(str[0] == 0) - return 1; /* return error */ - - *dst = *str; - dst++; - str++; - } - - /* write null termination */ - *dst = 0; - } - else - { - result->args[result->num_args++] = str; - - if(command == 'r') /* rest of the string */ - break; - else if(command == 'i') /* validate int */ - str = str_skiptoblank(str); - else if(command == 'f') /* validate float */ - str = str_skiptoblank(str); - else if(command == 's') /* validate string */ - str = str_skiptoblank(str); - - if(str[0] != 0) /* check for end of string */ - { - str[0] = 0; - str++; - } - } - } - } - - return error; -} - -const char *console_arg_string(void *res, int index) -{ - PARSE_RESULT *result = (PARSE_RESULT *)res; - if (index < 0 || index >= result->num_args) - return ""; - return result->args[index]; -} - -int console_arg_int(void *res, int index) -{ - PARSE_RESULT *result = (PARSE_RESULT *)res; - if (index < 0 || index >= result->num_args) - return 0; - return atoi(result->args[index]); -} - -float console_arg_float(void *res, int index) -{ - PARSE_RESULT *result = (PARSE_RESULT *)res; - if (index < 0 || index >= result->num_args) - return 0.0f; - return atof(result->args[index]); -} - -int console_arg_num(void *result) -{ - return ((PARSE_RESULT *)result)->num_args; -} - -static COMMAND *first_command = 0x0; - -COMMAND *console_find_command(const char *name) -{ - COMMAND *cmd; - for (cmd = first_command; cmd; cmd = cmd->next) - { - if (strcmp(cmd->name, name) == 0) - return cmd; - } - - return 0x0; -} - -void console_register(COMMAND *cmd) -{ - cmd->next = first_command; - first_command = cmd; -} - -static void (*print_callback)(const char *, void *) = 0x0; -static void *print_callback_userdata; - -void console_register_print_callback(void (*callback)(const char *, void *), void *user_data) -{ - print_callback = callback; - print_callback_userdata = user_data; -} - -void console_print(const char *str) -{ - if (print_callback) - print_callback(str, print_callback_userdata); -} - -void console_execute_line_stroked(int stroke, const char *str) -{ - PARSE_RESULT result; - COMMAND *command; - - char strokestr[2] = {'0', 0}; - if(stroke) - strokestr[0] = '1'; - - while(str) - { - const char *end = str; - const char *next_part = 0; - int in_string = 0; - - while(*end) - { - if(*end == '"') - in_string ^= 1; - else if(*end == '\\') /* escape sequences */ - { - if(end[1] == '"') - end++; - } - else if(!in_string) - { - if(*end == ';') /* command separator */ - { - next_part = end+1; - break; - } - else if(*end == '#') /* comment, no need to do anything more */ - break; - } - - end++; - } - - if(console_parse_start(&result, str, (end-str) + 1) != 0) - return; - - command = console_find_command(result.command); - - if(command) - { - int is_stroke_command = 0; - if(result.command[0] == '+') - { - /* insert the stroke direction token */ - result.args[result.num_args] = strokestr; - result.num_args++; - is_stroke_command = 1; - } - - if(stroke || is_stroke_command) - { - if(console_parse_args(&result, command->params)) - { - char buf[256]; - str_format(buf, sizeof(buf), "Invalid arguments... Usage: %s %s", command->name, command->params); - console_print(buf); - } - else - command->callback(&result, command->user_data); - } - } - else - { - char buf[256]; - str_format(buf, sizeof(buf), "No such command: %s.", result.command); - console_print(buf); - } - - str = next_part; - } -} - -void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user) -{ - COMMAND *cmd; - for (cmd = first_command; cmd; cmd = cmd->next) - { - if(cmd->flags&flagmask) - { - if(str_find_nocase(cmd->name, str)) - callback(cmd->name, user); - } - } -} - - -COMMAND *console_get_command(const char *str) -{ - COMMAND *cmd; - for (cmd = first_command; cmd; cmd = cmd->next) - { - if(str_comp_nocase(cmd->name, str) == 0) - return cmd; - } - - return 0x0; -} - -void console_execute_line(const char *str) -{ - console_execute_line_stroked(1, str); -} - -static void console_execute_file_real(const char *filename) -{ - IOHANDLE file; - file = engine_openfile(filename, IOFLAG_READ); - - if(file) - { - char *line; - LINEREADER lr; - - dbg_msg("console", "executing '%s'", filename); - linereader_init(&lr, file); - - while((line = linereader_get(&lr))) - console_execute_line(line); - - io_close(file); - } - else - dbg_msg("console", "failed to open '%s'", filename); -} - -struct EXECFILE -{ - const char *filename; - struct EXECFILE *next; -}; - -void console_execute_file(const char *filename) -{ - static struct EXECFILE *first = 0; - struct EXECFILE this; - struct EXECFILE *cur; - struct EXECFILE *prev; - - /* make sure that this isn't being executed already */ - for(cur = first; cur; cur = cur->next) - if(strcmp(filename, cur->filename) == 0) - return; - - /* push this one to the stack */ - prev = first; - this.filename = filename; - this.next = first; - first = &this; - - /* execute file */ - console_execute_file_real(filename); - - /* pop this one from the stack */ - first = prev; -} - -static void con_echo(void *result, void *user_data) -{ - console_print(console_arg_string(result, 0)); -} - -static void con_exec(void *result, void *user_data) -{ - console_execute_file(console_arg_string(result, 0)); - -} - - -typedef struct -{ - CONFIG_INT_GETTER getter; - CONFIG_INT_SETTER setter; -} INT_VARIABLE_DATA; - -typedef struct -{ - CONFIG_STR_GETTER getter; - CONFIG_STR_SETTER setter; -} STR_VARIABLE_DATA; - -static void int_variable_command(void *result, void *user_data) -{ - INT_VARIABLE_DATA *data = (INT_VARIABLE_DATA *)user_data; - - if(console_arg_num(result)) - data->setter(&config, console_arg_int(result, 0)); - else - { - char buf[1024]; - str_format(buf, sizeof(buf), "Value: %d", data->getter(&config)); - console_print(buf); - } -} - -static void str_variable_command(void *result, void *user_data) -{ - STR_VARIABLE_DATA *data = (STR_VARIABLE_DATA *)user_data; - - if(console_arg_num(result)) - data->setter(&config, console_arg_string(result, 0)); - else - { - char buf[1024]; - str_format(buf, sizeof(buf), "Value: %s", data->getter(&config)); - console_print(buf); - } -} - -static void console_chain(void *result, void *user_data) -{ - COMMANDCHAIN *info = (COMMANDCHAIN *)user_data; - info->chain_callback(result, info->user_data, info->callback, info->callback_user_data); -} - -void console_chain_command(const char *cmd, COMMANDCHAIN *chaininfo, CONSOLE_CHAIN_CALLBACK cb, void *user) -{ - COMMAND *command = console_get_command(cmd); - - /* store info */ - chaininfo->chain_callback = cb; - chaininfo->callback = command->callback; - chaininfo->callback_user_data = command->user_data; - chaininfo->user_data = user; - - /* chain */ - command->callback = console_chain; - command->user_data = chaininfo; -} - -void console_init() -{ - MACRO_REGISTER_COMMAND("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_echo, 0x0, "Echo the text"); - MACRO_REGISTER_COMMAND("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_exec, 0x0, "Execute the specified file"); - - #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) { static INT_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?i", flags, int_variable_command, &data, desc) } - #define MACRO_CONFIG_STR(name,len,def,flags,desc) { static STR_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?r", flags, str_variable_command, &data, desc) } - - #include "e_config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} diff --git a/src/engine/e_console.cpp b/src/engine/e_console.cpp new file mode 100644 index 00000000..c641289d --- /dev/null +++ b/src/engine/e_console.cpp @@ -0,0 +1,464 @@ +#include +#include "e_if_other.h" +#include "e_console.h" +#include "e_config.h" +#include "e_engine.h" +#include "e_linereader.h" +#include +#include +#include + + +#define CONSOLE_MAX_STR_LENGTH 1024 +/* the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces */ +#define MAX_PARTS (CONSOLE_MAX_STR_LENGTH+1)/2 + +typedef struct +{ + char string_storage[CONSOLE_MAX_STR_LENGTH+1]; + char *args_start; + + const char *command; + const char *args[MAX_PARTS]; + unsigned int num_args; +} PARSE_RESULT; + +static char *str_skipblanks(char *str) +{ + while(*str && (*str == ' ' || *str == '\t' || *str == '\n')) + str++; + return str; +} + +static char *str_skiptoblank(char *str) +{ + while(*str && (*str != ' ' && *str != '\t' && *str != '\n')) + str++; + return str; +} + +/* static int digit(char c) { return '0' <= c && c <= '9'; } */ + +static int console_parse_start(PARSE_RESULT *result, const char *string, int length) +{ + char *str; + int len = sizeof(result->string_storage); + if(length < len) + len = length; + + str_copy(result->string_storage, string, length); + str = result->string_storage; + + /* get command */ + str = str_skipblanks(str); + result->command = str; + str = str_skiptoblank(str); + + if(*str) + { + str[0] = 0; + str++; + } + + result->args_start = str; + result->num_args = 0; + return 0; +} + +static int console_parse_args(PARSE_RESULT *result, const char *format) +{ + char command; + char *str; + int optional = 0; + int error = 0; + + str = result->args_start; + + while(1) + { + /* fetch command */ + command = *format; + format++; + + if(!command) + break; + + if(command == '?') + optional = 1; + else + { + str = str_skipblanks(str); + + if(!(*str)) /* error, non optional command needs value */ + { + if(!optional) + error = 1; + break; + } + + /* add token */ + if(*str == '"') + { + char *dst; + str++; + result->args[result->num_args++] = str; + + dst = str; /* we might have to process escape data */ + while(1) + { + if(str[0] == '"') + break; + else if(str[0] == '\\') + { + if(str[1] == '\\') + str++; /* skip due to escape */ + else if(str[1] == '"') + str++; /* skip due to escape */ + } + else if(str[0] == 0) + return 1; /* return error */ + + *dst = *str; + dst++; + str++; + } + + /* write null termination */ + *dst = 0; + } + else + { + result->args[result->num_args++] = str; + + if(command == 'r') /* rest of the string */ + break; + else if(command == 'i') /* validate int */ + str = str_skiptoblank(str); + else if(command == 'f') /* validate float */ + str = str_skiptoblank(str); + else if(command == 's') /* validate string */ + str = str_skiptoblank(str); + + if(str[0] != 0) /* check for end of string */ + { + str[0] = 0; + str++; + } + } + } + } + + return error; +} + +const char *console_arg_string(void *res, unsigned index) +{ + PARSE_RESULT *result = (PARSE_RESULT *)res; + if (index < 0 || index >= result->num_args) + return ""; + return result->args[index]; +} + +int console_arg_int(void *res, unsigned index) +{ + PARSE_RESULT *result = (PARSE_RESULT *)res; + if (index < 0 || index >= result->num_args) + return 0; + return atoi(result->args[index]); +} + +float console_arg_float(void *res, unsigned index) +{ + PARSE_RESULT *result = (PARSE_RESULT *)res; + if (index < 0 || index >= result->num_args) + return 0.0f; + return atof(result->args[index]); +} + +int console_arg_num(void *result) +{ + return ((PARSE_RESULT *)result)->num_args; +} + +static COMMAND *first_command = 0x0; + +COMMAND *console_find_command(const char *name) +{ + COMMAND *cmd; + for (cmd = first_command; cmd; cmd = cmd->next) + { + if (strcmp(cmd->name, name) == 0) + return cmd; + } + + return 0x0; +} + +void console_register(COMMAND *cmd) +{ + cmd->next = first_command; + first_command = cmd; +} + +static void (*print_callback)(const char *, void *) = 0x0; +static void *print_callback_userdata; + +void console_register_print_callback(void (*callback)(const char *, void *), void *user_data) +{ + print_callback = callback; + print_callback_userdata = user_data; +} + +void console_print(const char *str) +{ + if (print_callback) + print_callback(str, print_callback_userdata); +} + +void console_execute_line_stroked(int stroke, const char *str) +{ + PARSE_RESULT result; + COMMAND *command; + + char strokestr[2] = {'0', 0}; + if(stroke) + strokestr[0] = '1'; + + while(str) + { + const char *end = str; + const char *next_part = 0; + int in_string = 0; + + while(*end) + { + if(*end == '"') + in_string ^= 1; + else if(*end == '\\') /* escape sequences */ + { + if(end[1] == '"') + end++; + } + else if(!in_string) + { + if(*end == ';') /* command separator */ + { + next_part = end+1; + break; + } + else if(*end == '#') /* comment, no need to do anything more */ + break; + } + + end++; + } + + if(console_parse_start(&result, str, (end-str) + 1) != 0) + return; + + command = console_find_command(result.command); + + if(command) + { + int is_stroke_command = 0; + if(result.command[0] == '+') + { + /* insert the stroke direction token */ + result.args[result.num_args] = strokestr; + result.num_args++; + is_stroke_command = 1; + } + + if(stroke || is_stroke_command) + { + if(console_parse_args(&result, command->params)) + { + char buf[256]; + str_format(buf, sizeof(buf), "Invalid arguments... Usage: %s %s", command->name, command->params); + console_print(buf); + } + else + command->callback(&result, command->user_data); + } + } + else + { + char buf[256]; + str_format(buf, sizeof(buf), "No such command: %s.", result.command); + console_print(buf); + } + + str = next_part; + } +} + +void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user) +{ + COMMAND *cmd; + for (cmd = first_command; cmd; cmd = cmd->next) + { + if(cmd->flags&flagmask) + { + if(str_find_nocase(cmd->name, str)) + callback(cmd->name, user); + } + } +} + + +COMMAND *console_get_command(const char *str) +{ + COMMAND *cmd; + for (cmd = first_command; cmd; cmd = cmd->next) + { + if(str_comp_nocase(cmd->name, str) == 0) + return cmd; + } + + return 0x0; +} + +void console_execute_line(const char *str) +{ + console_execute_line_stroked(1, str); +} + +static void console_execute_file_real(const char *filename) +{ + IOHANDLE file; + file = engine_openfile(filename, IOFLAG_READ); + + if(file) + { + char *line; + LINEREADER lr; + + dbg_msg("console", "executing '%s'", filename); + linereader_init(&lr, file); + + while((line = linereader_get(&lr))) + console_execute_line(line); + + io_close(file); + } + else + dbg_msg("console", "failed to open '%s'", filename); +} + +struct EXECFILE +{ + const char *filename; + struct EXECFILE *next; +}; + +void console_execute_file(const char *filename) +{ + static struct EXECFILE *first = 0; + struct EXECFILE this_file; + struct EXECFILE *cur; + struct EXECFILE *prev; + + /* make sure that this isn't being executed already */ + for(cur = first; cur; cur = cur->next) + if(strcmp(filename, cur->filename) == 0) + return; + + /* push this one to the stack */ + prev = first; + this_file.filename = filename; + this_file.next = first; + first = &this_file; + + /* execute file */ + console_execute_file_real(filename); + + /* pop this one from the stack */ + first = prev; +} + +static void con_echo(void *result, void *user_data) +{ + console_print(console_arg_string(result, 0)); +} + +static void con_exec(void *result, void *user_data) +{ + console_execute_file(console_arg_string(result, 0)); + +} + + +typedef struct +{ + CONFIG_INT_GETTER getter; + CONFIG_INT_SETTER setter; +} INT_VARIABLE_DATA; + +typedef struct +{ + CONFIG_STR_GETTER getter; + CONFIG_STR_SETTER setter; +} STR_VARIABLE_DATA; + +static void int_variable_command(void *result, void *user_data) +{ + INT_VARIABLE_DATA *data = (INT_VARIABLE_DATA *)user_data; + + if(console_arg_num(result)) + data->setter(&config, console_arg_int(result, 0)); + else + { + char buf[1024]; + str_format(buf, sizeof(buf), "Value: %d", data->getter(&config)); + console_print(buf); + } +} + +static void str_variable_command(void *result, void *user_data) +{ + STR_VARIABLE_DATA *data = (STR_VARIABLE_DATA *)user_data; + + if(console_arg_num(result)) + data->setter(&config, console_arg_string(result, 0)); + else + { + char buf[1024]; + str_format(buf, sizeof(buf), "Value: %s", data->getter(&config)); + console_print(buf); + } +} + +static void console_chain(void *result, void *user_data) +{ + COMMANDCHAIN *info = (COMMANDCHAIN *)user_data; + info->chain_callback(result, info->user_data, info->callback, info->callback_user_data); +} + +void console_chain_command(const char *cmd, COMMANDCHAIN *chaininfo, CONSOLE_CHAIN_CALLBACK cb, void *user) +{ + COMMAND *command = console_get_command(cmd); + + /* store info */ + chaininfo->chain_callback = cb; + chaininfo->callback = command->callback; + chaininfo->callback_user_data = command->user_data; + chaininfo->user_data = user; + + /* chain */ + command->callback = console_chain; + command->user_data = chaininfo; +} + +void console_init() +{ + MACRO_REGISTER_COMMAND("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_echo, 0x0, "Echo the text"); + MACRO_REGISTER_COMMAND("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_exec, 0x0, "Execute the specified file"); + + #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) { static INT_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?i", flags, int_variable_command, &data, desc) } + #define MACRO_CONFIG_STR(name,len,def,flags,desc) { static STR_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?r", flags, str_variable_command, &data, desc) } + + #include "e_config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +} diff --git a/src/engine/e_console.h b/src/engine/e_console.h index b37931b2..a45bac10 100644 --- a/src/engine/e_console.h +++ b/src/engine/e_console.h @@ -1,10 +1,6 @@ #ifndef _CONSOLE_H #define _CONSOLE_H -#ifdef __cplusplus -extern "C"{ -#endif - typedef void (*CONSOLE_CALLBACK)(void *result, void *user_data); typedef void (*CONSOLE_CHAIN_CALLBACK)(void *result, void *user_data, CONSOLE_CALLBACK cb, void *cbuser); @@ -42,15 +38,11 @@ void console_register_print_callback(void (*callback)(const char *, void *user_d int console_result_int(void *result, int index, int *i); int console_result_float(void *result, int index, float *f);*/ -const char *console_arg_string(void *result, int index); -int console_arg_int(void *result, int index); -float console_arg_float(void *result, int index); +const char *console_arg_string(void *result, unsigned index); +int console_arg_int(void *result, unsigned index); +float console_arg_float(void *result, unsigned index); int console_arg_num(void *result); #define MACRO_REGISTER_COMMAND(name, params, flags, func, ptr, help) { static COMMAND cmd = { name, params, flags, func, ptr, help, 0x0}; console_register(&cmd); } -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_datafile.c b/src/engine/e_datafile.c deleted file mode 100644 index 0317f9d0..00000000 --- a/src/engine/e_datafile.c +++ /dev/null @@ -1,677 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "e_datafile.h" -#include "e_engine.h" -#include - -static const int DEBUG=0; - -typedef struct -{ - int type; - int start; - int num; -} DATAFILE_ITEM_TYPE; - -typedef struct -{ - int type_and_id; - int size; -} DATAFILE_ITEM; - -typedef struct -{ - char id[4]; - int version; - int size; - int swaplen; - int num_item_types; - int num_items; - int num_raw_data; - int item_size; - int data_size; -} DATAFILE_HEADER; - -typedef struct -{ - int num_item_types; - int num_items; - int num_raw_data; - int item_size; - int data_size; - char start[4]; -} DATAFILE_DATA; - -typedef struct -{ - DATAFILE_ITEM_TYPE *item_types; - int *item_offsets; - int *data_offsets; - int *data_sizes; - - char *item_start; - char *data_start; -} DATAFILE_INFO; - -struct DATAFILE_t -{ - IOHANDLE file; - DATAFILE_INFO info; - DATAFILE_HEADER header; - int data_start_offset; - char **data_ptrs; - char *data; -}; - -DATAFILE *datafile_load(const char *filename) -{ - DATAFILE *df; - IOHANDLE file; - DATAFILE_HEADER header; - unsigned readsize; - - unsigned *dst; - unsigned char *src; - unsigned j; - int size = 0; - int allocsize = 0; - - (void)dst; - (void)src; - (void)j; - - - dbg_msg("datafile", "datafile loading. filename='%s'", filename); - - file = engine_openfile(filename, IOFLAG_READ); - if(!file) - return 0; - - /* TODO: change this header */ - io_read(file, &header, sizeof(header)); - if(header.id[0] != 'A' || header.id[1] != 'T' || header.id[2] != 'A' || header.id[3] != 'D') - { - if(header.id[0] != 'D' || header.id[1] != 'A' || header.id[2] != 'T' || header.id[3] != 'A') - { - dbg_msg("datafile", "wrong signature. %x %x %x %x", header.id[0], header.id[1], header.id[2], header.id[3]); - return 0; - } - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - swap_endian(&header, sizeof(int), sizeof(header)/sizeof(int)); -#endif - if(header.version != 3 && header.version != 4) - { - dbg_msg("datafile", "wrong version. version=%x", header.version); - return 0; - } - - /* read in the rest except the data */ - size = 0; - size += header.num_item_types*sizeof(DATAFILE_ITEM_TYPE); - size += (header.num_items+header.num_raw_data)*sizeof(int); - if(header.version == 4) - size += header.num_raw_data*sizeof(int); /* v4 has uncompressed data sizes aswell */ - size += header.item_size; - - allocsize = size; - allocsize += sizeof(DATAFILE); /* add space for info structure */ - allocsize += header.num_raw_data*sizeof(void*); /* add space for data pointers */ - - df = (DATAFILE*)mem_alloc(allocsize, 1); - df->header = header; - df->data_start_offset = sizeof(DATAFILE_HEADER) + size; - df->data_ptrs = (char**)(df+1); - df->data = (char *)(df+1)+header.num_raw_data*sizeof(char *); - df->file = file; - - /* clear the data pointers */ - mem_zero(df->data_ptrs, header.num_raw_data*sizeof(void*)); - - /* read types, offsets, sizes and item data */ - readsize = io_read(file, df->data, size); - if(readsize != size) - { - dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize); - return 0; - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - swap_endian(df->data, sizeof(int), header.swaplen / sizeof(int)); -#endif - - if(DEBUG) - { - dbg_msg("datafile", "allocsize=%d", allocsize); - dbg_msg("datafile", "readsize=%d", readsize); - dbg_msg("datafile", "swaplen=%d", header.swaplen); - dbg_msg("datafile", "item_size=%d", df->header.item_size); - } - - df->info.item_types = (DATAFILE_ITEM_TYPE *)df->data; - df->info.item_offsets = (int *)&df->info.item_types[df->header.num_item_types]; - df->info.data_offsets = (int *)&df->info.item_offsets[df->header.num_items]; - df->info.data_sizes = (int *)&df->info.data_offsets[df->header.num_raw_data]; - - if(header.version == 4) - df->info.item_start = (char *)&df->info.data_sizes[df->header.num_raw_data]; - else - df->info.item_start = (char *)&df->info.data_offsets[df->header.num_raw_data]; - df->info.data_start = df->info.item_start + df->header.item_size; - - if(DEBUG) - dbg_msg("datafile", "datafile loading done. datafile='%s'", filename); - - if(DEBUG) - { - /* - for(int i = 0; i < df->data.num_raw_data; i++) - { - void *p = datafile_get_data(df, i); - dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size); - } - - for(int i = 0; i < datafile_num_items(df); i++) - { - int type, id; - void *data = datafile_get_item(df, i, &type, &id); - dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]); - int *idata = (int*)data; - for(int k = 0; k < 3; k++) - dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]); - } - - for(int i = 0; i < df->data.num_item_types; i++) - { - dbg_msg("map", "\t%d: type=%x start=%d num=%d", i, - df->info.item_types[i].type, - df->info.item_types[i].start, - df->info.item_types[i].num); - for(int k = 0; k < df->info.item_types[i].num; k++) - { - int type, id; - datafile_get_item(df, df->info.item_types[i].start+k, &type, &id); - if(type != df->info.item_types[i].type) - dbg_msg("map", "\tERROR"); - } - } - */ - } - - return df; -} - -int datafile_num_data(DATAFILE *df) -{ - return df->header.num_raw_data; -} - -/* always returns the size in the file */ -int datafile_get_datasize(DATAFILE *df, int index) -{ - if(index == df->header.num_raw_data-1) - return df->header.data_size-df->info.data_offsets[index]; - return df->info.data_offsets[index+1]-df->info.data_offsets[index]; -} - -static void *datafile_get_data_impl(DATAFILE *df, int index, int swap) -{ - /* load it if needed */ - if(!df->data_ptrs[index]) - { - /* fetch the data size */ - int datasize = datafile_get_datasize(df, index); - int swapsize = datasize; - - if(df->header.version == 4) - { - /* v4 has compressed data */ - void *temp = (char *)mem_alloc(datasize, 1); - unsigned long uncompressed_size = df->info.data_sizes[index]; - unsigned long s; - - dbg_msg("datafile", "loading data index=%d size=%d uncompressed=%d", index, datasize, uncompressed_size); - df->data_ptrs[index] = (char *)mem_alloc(uncompressed_size, 1); - - /* read the compressed data */ - io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); - io_read(df->file, temp, datasize); - - /* decompress the data, TODO: check for errors */ - s = uncompressed_size; - uncompress((Bytef*)df->data_ptrs[index], &s, (Bytef*)temp, datasize); - swapsize = s; - - /* clean up the temporary buffers */ - mem_free(temp); - } - else - { - /* load the data */ - dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); - df->data_ptrs[index] = (char *)mem_alloc(datasize, 1); - io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); - io_read(df->file, df->data_ptrs[index], datasize); - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - if(swap && swapsize) - swap_endian(df->data_ptrs[index], sizeof(int), swapsize/sizeof(int)); -#endif - } - - return df->data_ptrs[index]; -} - -void *datafile_get_data(DATAFILE *df, int index) -{ - return datafile_get_data_impl(df, index, 0); -} - -void *datafile_get_data_swapped(DATAFILE *df, int index) -{ - return datafile_get_data_impl(df, index, 1); -} - -void datafile_unload_data(DATAFILE *df, int index) -{ - if(index < 0) - return; - - /* */ - mem_free(df->data_ptrs[index]); - df->data_ptrs[index] = 0x0; -} - -int datafile_get_itemsize(DATAFILE *df, int index) -{ - if(index == df->header.num_items-1) - return df->header.item_size-df->info.item_offsets[index]; - return df->info.item_offsets[index+1]-df->info.item_offsets[index]; -} - -void *datafile_get_item(DATAFILE *df, int index, int *type, int *id) -{ - DATAFILE_ITEM *i = (DATAFILE_ITEM *)(df->info.item_start+df->info.item_offsets[index]); - if(type) - *type = (i->type_and_id>>16)&0xffff; /* remove sign extention */ - if(id) - *id = i->type_and_id&0xffff; - return (void *)(i+1); -} - -void datafile_get_type(DATAFILE *df, int type, int *start, int *num) -{ - int i; - for(i = 0; i < df->header.num_item_types; i++) - { - if(df->info.item_types[i].type == type) - { - *start = df->info.item_types[i].start; - *num = df->info.item_types[i].num; - return; - } - } - - *start = 0; - *num = 0; -} - -void *datafile_find_item(DATAFILE *df, int type, int id) -{ - int start, num,i ; - int item_id; - void *item; - - datafile_get_type(df, type, &start, &num); - for(i = 0; i < num; i++) - { - item = datafile_get_item(df, start+i,0, &item_id); - if(id == item_id) - return item; - } - return 0; -} - -int datafile_num_items(DATAFILE *df) -{ - return df->header.num_items; -} - -void datafile_unload(DATAFILE *df) -{ - if(df) - { - /* free the data that is loaded */ - int i; - for(i = 0; i < df->header.num_raw_data; i++) - mem_free(df->data_ptrs[i]); - - io_close(df->file); - mem_free(df); - } -} - -/* DATAFILE output */ -typedef struct -{ - int uncompressed_size; - int compressed_size; - void *compressed_data; -} DATA_INFO; - -typedef struct -{ - int type; - int id; - int size; - int next; - int prev; - void *data; -} ITEM_INFO; - -typedef struct -{ - int num; - int first; - int last; -} ITEMTYPE_INFO; - -/* */ -struct DATAFILE_OUT_t -{ - IOHANDLE file; - int num_items; - int num_datas; - int num_item_types; - ITEMTYPE_INFO item_types[0xffff]; - ITEM_INFO items[1024]; - DATA_INFO datas[1024]; -}; - -DATAFILE_OUT *datafile_create(const char *filename) -{ - int i; - DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1); - df->file = engine_openfile(filename, IOFLAG_WRITE); - if(!df->file) - { - mem_free(df); - return 0; - } - - df->num_items = 0; - df->num_datas = 0; - df->num_item_types = 0; - mem_zero(&df->item_types, sizeof(df->item_types)); - - for(i = 0; i < 0xffff; i++) - { - df->item_types[i].first = -1; - df->item_types[i].last = -1; - } - - return df; -} - -int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data) -{ - df->items[df->num_items].type = type; - df->items[df->num_items].id = id; - df->items[df->num_items].size = size; - - /* - dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size); - int i; - for(i = 0; i < size/4; i++) - dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]); - */ - - /* copy data */ - df->items[df->num_items].data = mem_alloc(size, 1); - mem_copy(df->items[df->num_items].data, data, size); - - if(!df->item_types[type].num) /* count item types */ - df->num_item_types++; - - /* link */ - df->items[df->num_items].prev = df->item_types[type].last; - df->items[df->num_items].next = -1; - - if(df->item_types[type].last != -1) - df->items[df->item_types[type].last].next = df->num_items; - df->item_types[type].last = df->num_items; - - if(df->item_types[type].first == -1) - df->item_types[type].first = df->num_items; - - df->item_types[type].num++; - - df->num_items++; - return df->num_items-1; -} - -int datafile_add_data(DATAFILE_OUT *df, int size, void *data) -{ - DATA_INFO *info = &df->datas[df->num_datas]; - unsigned long s = compressBound(size); - void *compdata = mem_alloc(s, 1); /* temporary buffer that we use duing compression */ - - int result = compress((Bytef*)compdata, &s, (Bytef*)data, size); - if(result != Z_OK) - { - dbg_msg("datafile", "compression error %d", result); - dbg_assert(0, "zlib error"); - } - - info->uncompressed_size = size; - info->compressed_size = (int)s; - info->compressed_data = mem_alloc(info->compressed_size, 1); - mem_copy(info->compressed_data, compdata, info->compressed_size); - mem_free(compdata); - - df->num_datas++; - return df->num_datas-1; -} - -int datafile_add_data_swapped(DATAFILE_OUT *df, int size, void *data) -{ -#if defined(CONF_ARCH_ENDIAN_BIG) - void *swapped = mem_alloc(size, 1); /* temporary buffer that we use duing compression */ - int index; - mem_copy(swapped, data, size); - swap_endian(&swapped, sizeof(int), size/sizeof(int)); - index = datafile_add_data(df, size, swapped); - mem_free(swapped); - return index; -#else - return datafile_add_data(df, size, data); -#endif -} - - -int datafile_finish(DATAFILE_OUT *df) -{ - int itemsize = 0; - int i, count, offset; - int typessize, headersize, offsetsize, filesize, swapsize; - int datasize = 0; - DATAFILE_ITEM_TYPE info; - DATAFILE_ITEM itm; - DATAFILE_HEADER header; - - /* we should now write this file! */ - if(DEBUG) - dbg_msg("datafile", "writing"); - - /* calculate sizes */ - for(i = 0; i < df->num_items; i++) - { - if(DEBUG) - dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(DATAFILE_ITEM)); - itemsize += df->items[i].size + sizeof(DATAFILE_ITEM); - } - - - for(i = 0; i < df->num_datas; i++) - datasize += df->datas[i].compressed_size; - - /* calculate the complete size */ - typessize = df->num_item_types*sizeof(DATAFILE_ITEM_TYPE); - headersize = sizeof(DATAFILE_HEADER); - offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int); - filesize = headersize + typessize + offsetsize + itemsize + datasize; - swapsize = filesize - datasize; - - (void)swapsize; - - if(DEBUG) - dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize); - - /* construct header */ - { - header.id[0] = 'D'; - header.id[1] = 'A'; - header.id[2] = 'T'; - header.id[3] = 'A'; - header.version = 4; - header.size = filesize - 16; - header.swaplen = swapsize - 16; - header.num_item_types = df->num_item_types; - header.num_items = df->num_items; - header.num_raw_data = df->num_datas; - header.item_size = itemsize; - header.data_size = datasize; - - /* TODO: apply swapping */ - /* write header */ - if(DEBUG) - dbg_msg("datafile", "headersize=%d", sizeof(header)); - io_write(df->file, &header, sizeof(header)); - } - - /* write types */ - for(i = 0, count = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write info */ - info.type = i; - info.start = count; - info.num = df->item_types[i].num; - if(DEBUG) - dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num); - io_write(df->file, &info, sizeof(info)); - count += df->item_types[i].num; - } - } - - /* write item offsets */ - for(i = 0, offset = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write all items in of this type */ - int k = df->item_types[i].first; - while(k != -1) - { - if(DEBUG) - dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset); - io_write(df->file, &offset, sizeof(offset)); - offset += df->items[k].size + sizeof(DATAFILE_ITEM); - - /* next */ - k = df->items[k].next; - } - } - } - - /* write data offsets */ - for(i = 0, offset = 0; i < df->num_datas; i++) - { - if(DEBUG) - dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); - io_write(df->file, &offset, sizeof(offset)); - offset += df->datas[i].compressed_size; - } - - /* write data uncompressed sizes */ - for(i = 0, offset = 0; i < df->num_datas; i++) - { - /* - if(DEBUG) - dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); - */ - io_write(df->file, &df->datas[i].uncompressed_size, sizeof(int)); - } - - /* write items */ - for(i = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write all items in of this type */ - int k = df->item_types[i].first; - while(k != -1) - { - itm.type_and_id = (i<<16)|df->items[k].id; - itm.size = df->items[k].size; - if(DEBUG) - dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size); - - io_write(df->file, &itm, sizeof(itm)); - io_write(df->file, df->items[k].data, df->items[k].size); - - /* next */ - k = df->items[k].next; - } - } - } - - /* write data */ - for(i = 0; i < df->num_datas; i++) - { - if(DEBUG) - dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].compressed_size); - io_write(df->file, df->datas[i].compressed_data, df->datas[i].compressed_size); - } - - /* free data */ - for(i = 0; i < df->num_items; i++) - mem_free(df->items[i].data); - - - io_close(df->file); - mem_free(df); - - if(DEBUG) - dbg_msg("datafile", "done"); - return 0; -} - -#define BUFFER_SIZE 64*1024 - -int datafile_crc(const char *filename) -{ - unsigned char buffer[BUFFER_SIZE]; - IOHANDLE file; - int crc = 0; - unsigned bytes = 0; - - file = engine_openfile(filename, IOFLAG_READ); - if(!file) - return 0; - - while(1) - { - bytes = io_read(file, buffer, BUFFER_SIZE); - if(bytes <= 0) - break; - crc = crc32(crc, buffer, bytes); - } - - io_close(file); - - return crc; -} diff --git a/src/engine/e_datafile.cpp b/src/engine/e_datafile.cpp new file mode 100644 index 00000000..0317f9d0 --- /dev/null +++ b/src/engine/e_datafile.cpp @@ -0,0 +1,677 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "e_datafile.h" +#include "e_engine.h" +#include + +static const int DEBUG=0; + +typedef struct +{ + int type; + int start; + int num; +} DATAFILE_ITEM_TYPE; + +typedef struct +{ + int type_and_id; + int size; +} DATAFILE_ITEM; + +typedef struct +{ + char id[4]; + int version; + int size; + int swaplen; + int num_item_types; + int num_items; + int num_raw_data; + int item_size; + int data_size; +} DATAFILE_HEADER; + +typedef struct +{ + int num_item_types; + int num_items; + int num_raw_data; + int item_size; + int data_size; + char start[4]; +} DATAFILE_DATA; + +typedef struct +{ + DATAFILE_ITEM_TYPE *item_types; + int *item_offsets; + int *data_offsets; + int *data_sizes; + + char *item_start; + char *data_start; +} DATAFILE_INFO; + +struct DATAFILE_t +{ + IOHANDLE file; + DATAFILE_INFO info; + DATAFILE_HEADER header; + int data_start_offset; + char **data_ptrs; + char *data; +}; + +DATAFILE *datafile_load(const char *filename) +{ + DATAFILE *df; + IOHANDLE file; + DATAFILE_HEADER header; + unsigned readsize; + + unsigned *dst; + unsigned char *src; + unsigned j; + int size = 0; + int allocsize = 0; + + (void)dst; + (void)src; + (void)j; + + + dbg_msg("datafile", "datafile loading. filename='%s'", filename); + + file = engine_openfile(filename, IOFLAG_READ); + if(!file) + return 0; + + /* TODO: change this header */ + io_read(file, &header, sizeof(header)); + if(header.id[0] != 'A' || header.id[1] != 'T' || header.id[2] != 'A' || header.id[3] != 'D') + { + if(header.id[0] != 'D' || header.id[1] != 'A' || header.id[2] != 'T' || header.id[3] != 'A') + { + dbg_msg("datafile", "wrong signature. %x %x %x %x", header.id[0], header.id[1], header.id[2], header.id[3]); + return 0; + } + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + swap_endian(&header, sizeof(int), sizeof(header)/sizeof(int)); +#endif + if(header.version != 3 && header.version != 4) + { + dbg_msg("datafile", "wrong version. version=%x", header.version); + return 0; + } + + /* read in the rest except the data */ + size = 0; + size += header.num_item_types*sizeof(DATAFILE_ITEM_TYPE); + size += (header.num_items+header.num_raw_data)*sizeof(int); + if(header.version == 4) + size += header.num_raw_data*sizeof(int); /* v4 has uncompressed data sizes aswell */ + size += header.item_size; + + allocsize = size; + allocsize += sizeof(DATAFILE); /* add space for info structure */ + allocsize += header.num_raw_data*sizeof(void*); /* add space for data pointers */ + + df = (DATAFILE*)mem_alloc(allocsize, 1); + df->header = header; + df->data_start_offset = sizeof(DATAFILE_HEADER) + size; + df->data_ptrs = (char**)(df+1); + df->data = (char *)(df+1)+header.num_raw_data*sizeof(char *); + df->file = file; + + /* clear the data pointers */ + mem_zero(df->data_ptrs, header.num_raw_data*sizeof(void*)); + + /* read types, offsets, sizes and item data */ + readsize = io_read(file, df->data, size); + if(readsize != size) + { + dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize); + return 0; + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + swap_endian(df->data, sizeof(int), header.swaplen / sizeof(int)); +#endif + + if(DEBUG) + { + dbg_msg("datafile", "allocsize=%d", allocsize); + dbg_msg("datafile", "readsize=%d", readsize); + dbg_msg("datafile", "swaplen=%d", header.swaplen); + dbg_msg("datafile", "item_size=%d", df->header.item_size); + } + + df->info.item_types = (DATAFILE_ITEM_TYPE *)df->data; + df->info.item_offsets = (int *)&df->info.item_types[df->header.num_item_types]; + df->info.data_offsets = (int *)&df->info.item_offsets[df->header.num_items]; + df->info.data_sizes = (int *)&df->info.data_offsets[df->header.num_raw_data]; + + if(header.version == 4) + df->info.item_start = (char *)&df->info.data_sizes[df->header.num_raw_data]; + else + df->info.item_start = (char *)&df->info.data_offsets[df->header.num_raw_data]; + df->info.data_start = df->info.item_start + df->header.item_size; + + if(DEBUG) + dbg_msg("datafile", "datafile loading done. datafile='%s'", filename); + + if(DEBUG) + { + /* + for(int i = 0; i < df->data.num_raw_data; i++) + { + void *p = datafile_get_data(df, i); + dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size); + } + + for(int i = 0; i < datafile_num_items(df); i++) + { + int type, id; + void *data = datafile_get_item(df, i, &type, &id); + dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]); + int *idata = (int*)data; + for(int k = 0; k < 3; k++) + dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]); + } + + for(int i = 0; i < df->data.num_item_types; i++) + { + dbg_msg("map", "\t%d: type=%x start=%d num=%d", i, + df->info.item_types[i].type, + df->info.item_types[i].start, + df->info.item_types[i].num); + for(int k = 0; k < df->info.item_types[i].num; k++) + { + int type, id; + datafile_get_item(df, df->info.item_types[i].start+k, &type, &id); + if(type != df->info.item_types[i].type) + dbg_msg("map", "\tERROR"); + } + } + */ + } + + return df; +} + +int datafile_num_data(DATAFILE *df) +{ + return df->header.num_raw_data; +} + +/* always returns the size in the file */ +int datafile_get_datasize(DATAFILE *df, int index) +{ + if(index == df->header.num_raw_data-1) + return df->header.data_size-df->info.data_offsets[index]; + return df->info.data_offsets[index+1]-df->info.data_offsets[index]; +} + +static void *datafile_get_data_impl(DATAFILE *df, int index, int swap) +{ + /* load it if needed */ + if(!df->data_ptrs[index]) + { + /* fetch the data size */ + int datasize = datafile_get_datasize(df, index); + int swapsize = datasize; + + if(df->header.version == 4) + { + /* v4 has compressed data */ + void *temp = (char *)mem_alloc(datasize, 1); + unsigned long uncompressed_size = df->info.data_sizes[index]; + unsigned long s; + + dbg_msg("datafile", "loading data index=%d size=%d uncompressed=%d", index, datasize, uncompressed_size); + df->data_ptrs[index] = (char *)mem_alloc(uncompressed_size, 1); + + /* read the compressed data */ + io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); + io_read(df->file, temp, datasize); + + /* decompress the data, TODO: check for errors */ + s = uncompressed_size; + uncompress((Bytef*)df->data_ptrs[index], &s, (Bytef*)temp, datasize); + swapsize = s; + + /* clean up the temporary buffers */ + mem_free(temp); + } + else + { + /* load the data */ + dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); + df->data_ptrs[index] = (char *)mem_alloc(datasize, 1); + io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); + io_read(df->file, df->data_ptrs[index], datasize); + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + if(swap && swapsize) + swap_endian(df->data_ptrs[index], sizeof(int), swapsize/sizeof(int)); +#endif + } + + return df->data_ptrs[index]; +} + +void *datafile_get_data(DATAFILE *df, int index) +{ + return datafile_get_data_impl(df, index, 0); +} + +void *datafile_get_data_swapped(DATAFILE *df, int index) +{ + return datafile_get_data_impl(df, index, 1); +} + +void datafile_unload_data(DATAFILE *df, int index) +{ + if(index < 0) + return; + + /* */ + mem_free(df->data_ptrs[index]); + df->data_ptrs[index] = 0x0; +} + +int datafile_get_itemsize(DATAFILE *df, int index) +{ + if(index == df->header.num_items-1) + return df->header.item_size-df->info.item_offsets[index]; + return df->info.item_offsets[index+1]-df->info.item_offsets[index]; +} + +void *datafile_get_item(DATAFILE *df, int index, int *type, int *id) +{ + DATAFILE_ITEM *i = (DATAFILE_ITEM *)(df->info.item_start+df->info.item_offsets[index]); + if(type) + *type = (i->type_and_id>>16)&0xffff; /* remove sign extention */ + if(id) + *id = i->type_and_id&0xffff; + return (void *)(i+1); +} + +void datafile_get_type(DATAFILE *df, int type, int *start, int *num) +{ + int i; + for(i = 0; i < df->header.num_item_types; i++) + { + if(df->info.item_types[i].type == type) + { + *start = df->info.item_types[i].start; + *num = df->info.item_types[i].num; + return; + } + } + + *start = 0; + *num = 0; +} + +void *datafile_find_item(DATAFILE *df, int type, int id) +{ + int start, num,i ; + int item_id; + void *item; + + datafile_get_type(df, type, &start, &num); + for(i = 0; i < num; i++) + { + item = datafile_get_item(df, start+i,0, &item_id); + if(id == item_id) + return item; + } + return 0; +} + +int datafile_num_items(DATAFILE *df) +{ + return df->header.num_items; +} + +void datafile_unload(DATAFILE *df) +{ + if(df) + { + /* free the data that is loaded */ + int i; + for(i = 0; i < df->header.num_raw_data; i++) + mem_free(df->data_ptrs[i]); + + io_close(df->file); + mem_free(df); + } +} + +/* DATAFILE output */ +typedef struct +{ + int uncompressed_size; + int compressed_size; + void *compressed_data; +} DATA_INFO; + +typedef struct +{ + int type; + int id; + int size; + int next; + int prev; + void *data; +} ITEM_INFO; + +typedef struct +{ + int num; + int first; + int last; +} ITEMTYPE_INFO; + +/* */ +struct DATAFILE_OUT_t +{ + IOHANDLE file; + int num_items; + int num_datas; + int num_item_types; + ITEMTYPE_INFO item_types[0xffff]; + ITEM_INFO items[1024]; + DATA_INFO datas[1024]; +}; + +DATAFILE_OUT *datafile_create(const char *filename) +{ + int i; + DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1); + df->file = engine_openfile(filename, IOFLAG_WRITE); + if(!df->file) + { + mem_free(df); + return 0; + } + + df->num_items = 0; + df->num_datas = 0; + df->num_item_types = 0; + mem_zero(&df->item_types, sizeof(df->item_types)); + + for(i = 0; i < 0xffff; i++) + { + df->item_types[i].first = -1; + df->item_types[i].last = -1; + } + + return df; +} + +int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data) +{ + df->items[df->num_items].type = type; + df->items[df->num_items].id = id; + df->items[df->num_items].size = size; + + /* + dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size); + int i; + for(i = 0; i < size/4; i++) + dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]); + */ + + /* copy data */ + df->items[df->num_items].data = mem_alloc(size, 1); + mem_copy(df->items[df->num_items].data, data, size); + + if(!df->item_types[type].num) /* count item types */ + df->num_item_types++; + + /* link */ + df->items[df->num_items].prev = df->item_types[type].last; + df->items[df->num_items].next = -1; + + if(df->item_types[type].last != -1) + df->items[df->item_types[type].last].next = df->num_items; + df->item_types[type].last = df->num_items; + + if(df->item_types[type].first == -1) + df->item_types[type].first = df->num_items; + + df->item_types[type].num++; + + df->num_items++; + return df->num_items-1; +} + +int datafile_add_data(DATAFILE_OUT *df, int size, void *data) +{ + DATA_INFO *info = &df->datas[df->num_datas]; + unsigned long s = compressBound(size); + void *compdata = mem_alloc(s, 1); /* temporary buffer that we use duing compression */ + + int result = compress((Bytef*)compdata, &s, (Bytef*)data, size); + if(result != Z_OK) + { + dbg_msg("datafile", "compression error %d", result); + dbg_assert(0, "zlib error"); + } + + info->uncompressed_size = size; + info->compressed_size = (int)s; + info->compressed_data = mem_alloc(info->compressed_size, 1); + mem_copy(info->compressed_data, compdata, info->compressed_size); + mem_free(compdata); + + df->num_datas++; + return df->num_datas-1; +} + +int datafile_add_data_swapped(DATAFILE_OUT *df, int size, void *data) +{ +#if defined(CONF_ARCH_ENDIAN_BIG) + void *swapped = mem_alloc(size, 1); /* temporary buffer that we use duing compression */ + int index; + mem_copy(swapped, data, size); + swap_endian(&swapped, sizeof(int), size/sizeof(int)); + index = datafile_add_data(df, size, swapped); + mem_free(swapped); + return index; +#else + return datafile_add_data(df, size, data); +#endif +} + + +int datafile_finish(DATAFILE_OUT *df) +{ + int itemsize = 0; + int i, count, offset; + int typessize, headersize, offsetsize, filesize, swapsize; + int datasize = 0; + DATAFILE_ITEM_TYPE info; + DATAFILE_ITEM itm; + DATAFILE_HEADER header; + + /* we should now write this file! */ + if(DEBUG) + dbg_msg("datafile", "writing"); + + /* calculate sizes */ + for(i = 0; i < df->num_items; i++) + { + if(DEBUG) + dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(DATAFILE_ITEM)); + itemsize += df->items[i].size + sizeof(DATAFILE_ITEM); + } + + + for(i = 0; i < df->num_datas; i++) + datasize += df->datas[i].compressed_size; + + /* calculate the complete size */ + typessize = df->num_item_types*sizeof(DATAFILE_ITEM_TYPE); + headersize = sizeof(DATAFILE_HEADER); + offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int); + filesize = headersize + typessize + offsetsize + itemsize + datasize; + swapsize = filesize - datasize; + + (void)swapsize; + + if(DEBUG) + dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize); + + /* construct header */ + { + header.id[0] = 'D'; + header.id[1] = 'A'; + header.id[2] = 'T'; + header.id[3] = 'A'; + header.version = 4; + header.size = filesize - 16; + header.swaplen = swapsize - 16; + header.num_item_types = df->num_item_types; + header.num_items = df->num_items; + header.num_raw_data = df->num_datas; + header.item_size = itemsize; + header.data_size = datasize; + + /* TODO: apply swapping */ + /* write header */ + if(DEBUG) + dbg_msg("datafile", "headersize=%d", sizeof(header)); + io_write(df->file, &header, sizeof(header)); + } + + /* write types */ + for(i = 0, count = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + /* write info */ + info.type = i; + info.start = count; + info.num = df->item_types[i].num; + if(DEBUG) + dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num); + io_write(df->file, &info, sizeof(info)); + count += df->item_types[i].num; + } + } + + /* write item offsets */ + for(i = 0, offset = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + /* write all items in of this type */ + int k = df->item_types[i].first; + while(k != -1) + { + if(DEBUG) + dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset); + io_write(df->file, &offset, sizeof(offset)); + offset += df->items[k].size + sizeof(DATAFILE_ITEM); + + /* next */ + k = df->items[k].next; + } + } + } + + /* write data offsets */ + for(i = 0, offset = 0; i < df->num_datas; i++) + { + if(DEBUG) + dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); + io_write(df->file, &offset, sizeof(offset)); + offset += df->datas[i].compressed_size; + } + + /* write data uncompressed sizes */ + for(i = 0, offset = 0; i < df->num_datas; i++) + { + /* + if(DEBUG) + dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); + */ + io_write(df->file, &df->datas[i].uncompressed_size, sizeof(int)); + } + + /* write items */ + for(i = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + /* write all items in of this type */ + int k = df->item_types[i].first; + while(k != -1) + { + itm.type_and_id = (i<<16)|df->items[k].id; + itm.size = df->items[k].size; + if(DEBUG) + dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size); + + io_write(df->file, &itm, sizeof(itm)); + io_write(df->file, df->items[k].data, df->items[k].size); + + /* next */ + k = df->items[k].next; + } + } + } + + /* write data */ + for(i = 0; i < df->num_datas; i++) + { + if(DEBUG) + dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].compressed_size); + io_write(df->file, df->datas[i].compressed_data, df->datas[i].compressed_size); + } + + /* free data */ + for(i = 0; i < df->num_items; i++) + mem_free(df->items[i].data); + + + io_close(df->file); + mem_free(df); + + if(DEBUG) + dbg_msg("datafile", "done"); + return 0; +} + +#define BUFFER_SIZE 64*1024 + +int datafile_crc(const char *filename) +{ + unsigned char buffer[BUFFER_SIZE]; + IOHANDLE file; + int crc = 0; + unsigned bytes = 0; + + file = engine_openfile(filename, IOFLAG_READ); + if(!file) + return 0; + + while(1) + { + bytes = io_read(file, buffer, BUFFER_SIZE); + if(bytes <= 0) + break; + crc = crc32(crc, buffer, bytes); + } + + io_close(file); + + return crc; +} diff --git a/src/engine/e_demorec.c b/src/engine/e_demorec.c deleted file mode 100644 index e66173e4..00000000 --- a/src/engine/e_demorec.c +++ /dev/null @@ -1,639 +0,0 @@ - -#include -#include "e_demorec.h" -#include "e_memheap.h" -#include "e_snapshot.h" -#include "e_compression.h" -#include "e_network.h" -#include "e_engine.h" -#include "e_if_other.h" - -static IOHANDLE record_file = 0; -static const unsigned char header_marker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0, 1}; - -/* Record */ -static int record_lasttickmarker = -1; -static int record_lastkeyframe; -static unsigned char record_lastsnapshotdata[MAX_SNAPSHOT_SIZE]; - -int demorec_isrecording() { return record_file != 0; } - -int demorec_record_start(const char *filename, const char *netversion, const char *map, int crc, const char *type) -{ - DEMOREC_HEADER header; - if(record_file) - return -1; - - record_file = engine_openfile(filename, IOFLAG_WRITE); - - if(!record_file) - { - dbg_msg("demorec/record", "Unable to open '%s' for recording", filename); - return -1; - } - - /* write header */ - mem_zero(&header, sizeof(header)); - mem_copy(header.marker, header_marker, sizeof(header.marker)); - str_copy(header.netversion, netversion, sizeof(header.netversion)); - str_copy(header.map, map, sizeof(header.map)); - str_copy(header.type, type, sizeof(header.type)); - header.crc[0] = (crc>>24)&0xff; - header.crc[1] = (crc>>16)&0xff; - header.crc[2] = (crc>>8)&0xff; - header.crc[3] = (crc)&0xff; - io_write(record_file, &header, sizeof(header)); - - record_lastkeyframe = -1; - record_lasttickmarker = -1; - - dbg_msg("demorec/record", "Recording to '%s'", filename); - return 0; -} - -/* - Tickmarker - 7 = Always set - 6 = Keyframe flag - 0-5 = Delta tick - - Normal - 7 = Not set - 5-6 = Type - 0-4 = Size -*/ - -enum -{ - CHUNKTYPEFLAG_TICKMARKER = 0x80, - CHUNKTICKFLAG_KEYFRAME = 0x40, /* only when tickmarker is set*/ - - CHUNKMASK_TICK = 0x3f, - CHUNKMASK_TYPE = 0x60, - CHUNKMASK_SIZE = 0x1f, - - CHUNKTYPE_SNAPSHOT = 1, - CHUNKTYPE_MESSAGE = 2, - CHUNKTYPE_DELTA = 3, - - CHUNKFLAG_BIGSIZE = 0x10 -}; - -static void demorec_record_write_tickmarker(int tick, int keyframe) -{ - if(record_lasttickmarker == -1 || tick-record_lasttickmarker > 63 || keyframe) - { - unsigned char chunk[5]; - chunk[0] = CHUNKTYPEFLAG_TICKMARKER; - chunk[1] = (tick>>24)&0xff; - chunk[2] = (tick>>16)&0xff; - chunk[3] = (tick>>8)&0xff; - chunk[4] = (tick)&0xff; - - if(keyframe) - chunk[0] |= CHUNKTICKFLAG_KEYFRAME; - - io_write(record_file, chunk, sizeof(chunk)); - } - else - { - unsigned char chunk[1]; - chunk[0] = CHUNKTYPEFLAG_TICKMARKER | (tick-record_lasttickmarker); - io_write(record_file, chunk, sizeof(chunk)); - } - - record_lasttickmarker = tick; -} - -static void demorec_record_write(int type, const void *data, int size) -{ - char buffer[64*1024]; - char buffer2[64*1024]; - unsigned char chunk[3]; - - if(!record_file) - return; - - - /* pad the data with 0 so we get an alignment of 4, - else the compression won't work and miss some bytes */ - mem_copy(buffer2, data, size); - while(size&3) - buffer2[size++] = 0; - size = intpack_compress(buffer2, size, buffer); /* buffer2 -> buffer */ - size = netcommon_compress(buffer, size, buffer2, sizeof(buffer2)); /* buffer -> buffer2 */ - - - chunk[0] = ((type&0x3)<<5); - if(size < 30) - { - chunk[0] |= size; - io_write(record_file, chunk, 1); - } - else - { - if(size < 256) - { - chunk[0] |= 30; - chunk[1] = size&0xff; - io_write(record_file, chunk, 2); - } - else - { - chunk[0] |= 31; - chunk[1] = size&0xff; - chunk[2] = size>>8; - io_write(record_file, chunk, 3); - } - } - - io_write(record_file, buffer2, size); -} - -void demorec_record_snapshot(int tick, const void *data, int size) -{ - if(record_lastkeyframe == -1 || (tick-record_lastkeyframe) > SERVER_TICK_SPEED*5) - { - /* write full tickmarker */ - demorec_record_write_tickmarker(tick, 1); - - /* write snapshot */ - demorec_record_write(CHUNKTYPE_SNAPSHOT, data, size); - - record_lastkeyframe = tick; - mem_copy(record_lastsnapshotdata, data, size); - } - else - { - /* create delta, prepend tick */ - char delta_data[MAX_SNAPSHOT_SIZE+sizeof(int)]; - int delta_size; - - /* write tickmarker */ - demorec_record_write_tickmarker(tick, 0); - - delta_size = snapshot_create_delta((SNAPSHOT*)record_lastsnapshotdata, (SNAPSHOT*)data, &delta_data); - if(delta_size) - { - /* record delta */ - demorec_record_write(CHUNKTYPE_DELTA, delta_data, delta_size); - mem_copy(record_lastsnapshotdata, data, size); - } - } -} - -void demorec_record_message(const void *data, int size) -{ - demorec_record_write(CHUNKTYPE_MESSAGE, data, size); -} - -int demorec_record_stop() -{ - if(!record_file) - return -1; - - dbg_msg("demorec/record", "Stopped recording"); - io_close(record_file); - record_file = 0; - return 0; -} - -/* Playback */ -typedef struct KEYFRAME -{ - long filepos; - int tick; -} KEYFRAME; - -typedef struct KEYFRAME_SEARCH -{ - KEYFRAME frame; - struct KEYFRAME_SEARCH *next; -} KEYFRAME_SEARCH; - -static IOHANDLE play_file = 0; -static DEMOREC_PLAYCALLBACK play_callback_snapshot = 0; -static DEMOREC_PLAYCALLBACK play_callback_message = 0; -static KEYFRAME *keyframes = 0; - -static DEMOREC_PLAYBACKINFO playbackinfo; -static unsigned char playback_lastsnapshotdata[MAX_SNAPSHOT_SIZE]; -static int playback_lastsnapshotdata_size = -1; - - -const DEMOREC_PLAYBACKINFO *demorec_playback_info() { return &playbackinfo; } -int demorec_isplaying() { return play_file != 0; } - -int demorec_playback_registercallbacks(DEMOREC_PLAYCALLBACK snapshot_cb, DEMOREC_PLAYCALLBACK message_cb) -{ - play_callback_snapshot = snapshot_cb; - play_callback_message = message_cb; - return 0; -} - -static int read_chunk_header(int *type, int *size, int *tick) -{ - unsigned char chunk = 0; - - *size = 0; - *type = 0; - - if(io_read(play_file, &chunk, sizeof(chunk)) != sizeof(chunk)) - return -1; - - if(chunk&CHUNKTYPEFLAG_TICKMARKER) - { - /* decode tick marker */ - int tickdelta = chunk&(CHUNKMASK_TICK); - *type = chunk&(CHUNKTYPEFLAG_TICKMARKER|CHUNKTICKFLAG_KEYFRAME); - - if(tickdelta == 0) - { - unsigned char tickdata[4]; - if(io_read(play_file, tickdata, sizeof(tickdata)) != sizeof(tickdata)) - return -1; - *tick = (tickdata[0]<<24) | (tickdata[1]<<16) | (tickdata[2]<<8) | tickdata[3]; - } - else - { - *tick += tickdelta; - } - - } - else - { - /* decode normal chunk */ - *type = (chunk&CHUNKMASK_TYPE)>>5; - *size = chunk&CHUNKMASK_SIZE; - - if(*size == 30) - { - unsigned char sizedata[1]; - if(io_read(play_file, sizedata, sizeof(sizedata)) != sizeof(sizedata)) - return -1; - *size = sizedata[0]; - - } - else if(*size == 31) - { - unsigned char sizedata[2]; - if(io_read(play_file, sizedata, sizeof(sizedata)) != sizeof(sizedata)) - return -1; - *size = (sizedata[1]<<8) | sizedata[0]; - } - } - - return 0; -} - -static void scan_file() -{ - long start_pos; - HEAP *heap = 0; - KEYFRAME_SEARCH *first_key = 0; - KEYFRAME_SEARCH *current_key = 0; - /*DEMOREC_CHUNK chunk;*/ - int chunk_size, chunk_type, chunk_tick = 0; - int i; - - heap = memheap_create(); - - start_pos = io_tell(play_file); - playbackinfo.seekable_points = 0; - - while(1) - { - long current_pos = io_tell(play_file); - - if(read_chunk_header(&chunk_type, &chunk_size, &chunk_tick)) - break; - - /* read the chunk */ - if(chunk_type&CHUNKTYPEFLAG_TICKMARKER) - { - if(chunk_type&CHUNKTICKFLAG_KEYFRAME) - { - KEYFRAME_SEARCH *key; - - /* save the position */ - key = memheap_allocate(heap, sizeof(KEYFRAME_SEARCH)); - key->frame.filepos = current_pos; - key->frame.tick = chunk_tick; - key->next = 0; - if(current_key) - current_key->next = key; - if(!first_key) - first_key = key; - current_key = key; - playbackinfo.seekable_points++; - } - - if(playbackinfo.first_tick == -1) - playbackinfo.first_tick = chunk_tick; - playbackinfo.last_tick = chunk_tick; - } - else if(chunk_size) - io_skip(play_file, chunk_size); - - } - - /* copy all the frames to an array instead for fast access */ - keyframes = (KEYFRAME*)mem_alloc(playbackinfo.seekable_points*sizeof(KEYFRAME), 1); - for(current_key = first_key, i = 0; current_key; current_key = current_key->next, i++) - keyframes[i] = current_key->frame; - - /* destroy the temporary heap and seek back to the start */ - memheap_destroy(heap); - io_seek(play_file, start_pos, IOSEEK_START); -} - -static void do_tick() -{ - static char compresseddata[MAX_SNAPSHOT_SIZE]; - static char decompressed[MAX_SNAPSHOT_SIZE]; - static char data[MAX_SNAPSHOT_SIZE]; - int chunk_size, chunk_type, chunk_tick; - int data_size; - int got_snapshot = 0; - - /* update ticks */ - playbackinfo.previous_tick = playbackinfo.current_tick; - playbackinfo.current_tick = playbackinfo.next_tick; - chunk_tick = playbackinfo.current_tick; - - while(1) - { - if(read_chunk_header(&chunk_type, &chunk_size, &chunk_tick)) - { - /* stop on error or eof */ - dbg_msg("demorec", "end of file"); - demorec_playback_pause(); - break; - } - - /* read the chunk */ - if(chunk_size) - { - if(io_read(play_file, compresseddata, chunk_size) != chunk_size) - { - /* stop on error or eof */ - dbg_msg("demorec", "error reading chunk"); - demorec_playback_stop(); - break; - } - - data_size = netcommon_decompress(compresseddata, chunk_size, decompressed, sizeof(decompressed)); - if(data_size < 0) - { - /* stop on error or eof */ - dbg_msg("demorec", "error during network decompression"); - demorec_playback_stop(); - break; - } - - data_size = intpack_decompress(decompressed, data_size, data); - - if(data_size < 0) - { - dbg_msg("demorec", "error during intpack decompression"); - demorec_playback_stop(); - break; - } - } - - if(chunk_type == CHUNKTYPE_DELTA) - { - /* process delta snapshot */ - static char newsnap[MAX_SNAPSHOT_SIZE]; - - got_snapshot = 1; - - data_size = snapshot_unpack_delta((SNAPSHOT*)playback_lastsnapshotdata, (SNAPSHOT*)newsnap, data, data_size); - - if(data_size >= 0) - { - if(play_callback_snapshot) - play_callback_snapshot(newsnap, data_size); - - playback_lastsnapshotdata_size = data_size; - mem_copy(playback_lastsnapshotdata, newsnap, data_size); - } - else - dbg_msg("demorec", "error duing unpacking of delta, err=%d", data_size); - } - else if(chunk_type == CHUNKTYPE_SNAPSHOT) - { - /* process full snapshot */ - got_snapshot = 1; - - playback_lastsnapshotdata_size = data_size; - mem_copy(playback_lastsnapshotdata, data, data_size); - if(play_callback_snapshot) - play_callback_snapshot(data, data_size); - } - else - { - /* if there were no snapshots in this tick, replay the last one */ - if(!got_snapshot && play_callback_snapshot && playback_lastsnapshotdata_size != -1) - { - got_snapshot = 1; - play_callback_snapshot(playback_lastsnapshotdata, playback_lastsnapshotdata_size); - } - - /* check the remaining types */ - if(chunk_type&CHUNKTYPEFLAG_TICKMARKER) - { - playbackinfo.next_tick = chunk_tick; - break; - } - else if(chunk_type == CHUNKTYPE_MESSAGE) - { - if(play_callback_message) - play_callback_message(data, data_size); - } - } - } -} - -void demorec_playback_pause() -{ - playbackinfo.paused = 1; -} - -void demorec_playback_unpause() -{ - if(playbackinfo.paused) - { - /*playbackinfo.start_tick = playbackinfo.current_tick; - playbackinfo.start_time = time_get();*/ - playbackinfo.paused = 0; - } -} - -int demorec_playback_load(const char *filename) -{ - play_file = engine_openfile(filename, IOFLAG_READ); - if(!play_file) - { - dbg_msg("demorec/playback", "could not open '%s'", filename); - return -1; - } - - /* clear the playback info */ - mem_zero(&playbackinfo, sizeof(playbackinfo)); - playbackinfo.first_tick = -1; - playbackinfo.last_tick = -1; - /*playbackinfo.start_tick = -1;*/ - playbackinfo.next_tick = -1; - playbackinfo.current_tick = -1; - playbackinfo.previous_tick = -1; - playbackinfo.speed = 1; - - playback_lastsnapshotdata_size = -1; - - /* read the header */ - io_read(play_file, &playbackinfo.header, sizeof(playbackinfo.header)); - if(mem_comp(playbackinfo.header.marker, header_marker, sizeof(header_marker)) != 0) - { - dbg_msg("demorec/playback", "'%s' is not a demo file", filename); - io_close(play_file); - play_file = 0; - return -1; - } - - /* scan the file for interessting points */ - scan_file(); - - /* ready for playback */ - return 0; -} - -int demorec_playback_nextframe() -{ - do_tick(); - return demorec_isplaying(); -} - -int demorec_playback_play() -{ - /* fill in previous and next tick */ - while(playbackinfo.previous_tick == -1 && demorec_isplaying()) - do_tick(); - - /* set start info */ - /*playbackinfo.start_tick = playbackinfo.previous_tick; - playbackinfo.start_time = time_get();*/ - playbackinfo.current_time = playbackinfo.previous_tick*time_freq()/SERVER_TICK_SPEED; - playbackinfo.last_update = time_get(); - return 0; -} - -int demorec_playback_set(float percent) -{ - int keyframe; - int wanted_tick; - if(!play_file) - return -1; - - /* -5 because we have to have a current tick and previous tick when we do the playback */ - wanted_tick = playbackinfo.first_tick + (int)((playbackinfo.last_tick-playbackinfo.first_tick)*percent) - 5; - - keyframe = (int)(playbackinfo.seekable_points*percent); - - if(keyframe < 0 || keyframe >= playbackinfo.seekable_points) - return -1; - - /* get correct key frame */ - if(keyframes[keyframe].tick < wanted_tick) - while(keyframe < playbackinfo.seekable_points-1 && keyframes[keyframe].tick < wanted_tick) - keyframe++; - - while(keyframe && keyframes[keyframe].tick > wanted_tick) - keyframe--; - - /* seek to the correct keyframe */ - io_seek(play_file, keyframes[keyframe].filepos, IOSEEK_START); - - /*playbackinfo.start_tick = -1;*/ - playbackinfo.next_tick = -1; - playbackinfo.current_tick = -1; - playbackinfo.previous_tick = -1; - - /* playback everything until we hit our tick */ - while(playbackinfo.previous_tick < wanted_tick) - do_tick(); - - demorec_playback_play(); - - return 0; -} - -void demorec_playback_setspeed(float speed) -{ - playbackinfo.speed = speed; -} - -int demorec_playback_update() -{ - int64 now = time_get(); - int64 deltatime = now-playbackinfo.last_update; - playbackinfo.last_update = now; - - if(!demorec_isplaying()) - return 0; - - if(playbackinfo.paused) - { - - } - else - { - int64 freq = time_freq(); - playbackinfo.current_time += (int64)(deltatime*(double)playbackinfo.speed); - - while(1) - { - int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED; - - /* break if we are ready */ - if(curtick_start > playbackinfo.current_time) - break; - - /* do one more tick */ - do_tick(); - - if(playbackinfo.paused) - return 0; - } - - /* update intratick */ - { - int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED; - int64 prevtick_start = (playbackinfo.previous_tick)*freq/SERVER_TICK_SPEED; - playbackinfo.intratick = (playbackinfo.current_time - prevtick_start) / (float)(curtick_start-prevtick_start); - playbackinfo.ticktime = (playbackinfo.current_time - prevtick_start) / (float)freq; - } - - if(playbackinfo.current_tick == playbackinfo.previous_tick || - playbackinfo.current_tick == playbackinfo.next_tick) - { - dbg_msg("demorec/playback", "tick error prev=%d cur=%d next=%d", - playbackinfo.previous_tick, playbackinfo.current_tick, playbackinfo.next_tick); - } - } - - return 0; -} - -int demorec_playback_stop(const char *filename) -{ - if(!play_file) - return -1; - - dbg_msg("demorec/playback", "Stopped playback"); - io_close(play_file); - play_file = 0; - mem_free(keyframes); - keyframes = 0; - return 0; -} diff --git a/src/engine/e_demorec.cpp b/src/engine/e_demorec.cpp new file mode 100644 index 00000000..1bab1b8d --- /dev/null +++ b/src/engine/e_demorec.cpp @@ -0,0 +1,640 @@ +#include +#include "e_demorec.h" +#include "e_memheap.h" +#include "e_snapshot.h" +#include "e_compression.h" +#include "e_network.h" +#include "e_engine.h" +#include "e_if_other.h" + +static IOHANDLE record_file = 0; +static const unsigned char header_marker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0, 1}; + +/* Record */ +static int record_lasttickmarker = -1; +static int record_lastkeyframe; +static unsigned char record_lastsnapshotdata[CSnapshot::MAX_SIZE]; + +int demorec_isrecording() { return record_file != 0; } + +int demorec_record_start(const char *filename, const char *netversion, const char *map, int crc, const char *type) +{ + DEMOREC_HEADER header; + if(record_file) + return -1; + + record_file = engine_openfile(filename, IOFLAG_WRITE); + + if(!record_file) + { + dbg_msg("demorec/record", "Unable to open '%s' for recording", filename); + return -1; + } + + /* write header */ + mem_zero(&header, sizeof(header)); + mem_copy(header.marker, header_marker, sizeof(header.marker)); + str_copy(header.netversion, netversion, sizeof(header.netversion)); + str_copy(header.map, map, sizeof(header.map)); + str_copy(header.type, type, sizeof(header.type)); + header.crc[0] = (crc>>24)&0xff; + header.crc[1] = (crc>>16)&0xff; + header.crc[2] = (crc>>8)&0xff; + header.crc[3] = (crc)&0xff; + io_write(record_file, &header, sizeof(header)); + + record_lastkeyframe = -1; + record_lasttickmarker = -1; + + dbg_msg("demorec/record", "Recording to '%s'", filename); + return 0; +} + +/* + Tickmarker + 7 = Always set + 6 = Keyframe flag + 0-5 = Delta tick + + Normal + 7 = Not set + 5-6 = Type + 0-4 = Size +*/ + +enum +{ + CHUNKTYPEFLAG_TICKMARKER = 0x80, + CHUNKTICKFLAG_KEYFRAME = 0x40, /* only when tickmarker is set*/ + + CHUNKMASK_TICK = 0x3f, + CHUNKMASK_TYPE = 0x60, + CHUNKMASK_SIZE = 0x1f, + + CHUNKTYPE_SNAPSHOT = 1, + CHUNKTYPE_MESSAGE = 2, + CHUNKTYPE_DELTA = 3, + + CHUNKFLAG_BIGSIZE = 0x10 +}; + +static void demorec_record_write_tickmarker(int tick, int keyframe) +{ + if(record_lasttickmarker == -1 || tick-record_lasttickmarker > 63 || keyframe) + { + unsigned char chunk[5]; + chunk[0] = CHUNKTYPEFLAG_TICKMARKER; + chunk[1] = (tick>>24)&0xff; + chunk[2] = (tick>>16)&0xff; + chunk[3] = (tick>>8)&0xff; + chunk[4] = (tick)&0xff; + + if(keyframe) + chunk[0] |= CHUNKTICKFLAG_KEYFRAME; + + io_write(record_file, chunk, sizeof(chunk)); + } + else + { + unsigned char chunk[1]; + chunk[0] = CHUNKTYPEFLAG_TICKMARKER | (tick-record_lasttickmarker); + io_write(record_file, chunk, sizeof(chunk)); + } + + record_lasttickmarker = tick; +} + +static void demorec_record_write(int type, const void *data, int size) +{ + char buffer[64*1024]; + char buffer2[64*1024]; + unsigned char chunk[3]; + + if(!record_file) + return; + + + /* pad the data with 0 so we get an alignment of 4, + else the compression won't work and miss some bytes */ + mem_copy(buffer2, data, size); + while(size&3) + buffer2[size++] = 0; + size = intpack_compress(buffer2, size, buffer); /* buffer2 -> buffer */ + size = CNetBase::Compress(buffer, size, buffer2, sizeof(buffer2)); /* buffer -> buffer2 */ + + + chunk[0] = ((type&0x3)<<5); + if(size < 30) + { + chunk[0] |= size; + io_write(record_file, chunk, 1); + } + else + { + if(size < 256) + { + chunk[0] |= 30; + chunk[1] = size&0xff; + io_write(record_file, chunk, 2); + } + else + { + chunk[0] |= 31; + chunk[1] = size&0xff; + chunk[2] = size>>8; + io_write(record_file, chunk, 3); + } + } + + io_write(record_file, buffer2, size); +} + +void demorec_record_snapshot(int tick, const void *data, int size) +{ + if(record_lastkeyframe == -1 || (tick-record_lastkeyframe) > SERVER_TICK_SPEED*5) + { + /* write full tickmarker */ + demorec_record_write_tickmarker(tick, 1); + + /* write snapshot */ + demorec_record_write(CHUNKTYPE_SNAPSHOT, data, size); + + record_lastkeyframe = tick; + mem_copy(record_lastsnapshotdata, data, size); + } + else + { + /* create delta, prepend tick */ + char delta_data[CSnapshot::MAX_SIZE+sizeof(int)]; + int delta_size; + + /* write tickmarker */ + demorec_record_write_tickmarker(tick, 0); + + delta_size = CSnapshot::CreateDelta((CSnapshot*)record_lastsnapshotdata, (CSnapshot*)data, &delta_data); + if(delta_size) + { + /* record delta */ + demorec_record_write(CHUNKTYPE_DELTA, delta_data, delta_size); + mem_copy(record_lastsnapshotdata, data, size); + } + } +} + +void demorec_record_message(const void *data, int size) +{ + demorec_record_write(CHUNKTYPE_MESSAGE, data, size); +} + +int demorec_record_stop() +{ + if(!record_file) + return -1; + + dbg_msg("demorec/record", "Stopped recording"); + io_close(record_file); + record_file = 0; + return 0; +} + +/* Playback */ +typedef struct KEYFRAME +{ + long filepos; + int tick; +} KEYFRAME; + +typedef struct KEYFRAME_SEARCH +{ + KEYFRAME frame; + struct KEYFRAME_SEARCH *next; +} KEYFRAME_SEARCH; + +static IOHANDLE play_file = 0; +static DEMOREC_PLAYCALLBACK play_callback_snapshot = 0; +static DEMOREC_PLAYCALLBACK play_callback_message = 0; +static KEYFRAME *keyframes = 0; + +static DEMOREC_PLAYBACKINFO playbackinfo; +static unsigned char playback_lastsnapshotdata[CSnapshot::MAX_SIZE]; +static int playback_lastsnapshotdata_size = -1; + + +const DEMOREC_PLAYBACKINFO *demorec_playback_info() { return &playbackinfo; } +int demorec_isplaying() { return play_file != 0; } + +int demorec_playback_registercallbacks(DEMOREC_PLAYCALLBACK snapshot_cb, DEMOREC_PLAYCALLBACK message_cb) +{ + play_callback_snapshot = snapshot_cb; + play_callback_message = message_cb; + return 0; +} + +static int read_chunk_header(int *type, int *size, int *tick) +{ + unsigned char chunk = 0; + + *size = 0; + *type = 0; + + if(io_read(play_file, &chunk, sizeof(chunk)) != sizeof(chunk)) + return -1; + + if(chunk&CHUNKTYPEFLAG_TICKMARKER) + { + /* decode tick marker */ + int tickdelta = chunk&(CHUNKMASK_TICK); + *type = chunk&(CHUNKTYPEFLAG_TICKMARKER|CHUNKTICKFLAG_KEYFRAME); + + if(tickdelta == 0) + { + unsigned char tickdata[4]; + if(io_read(play_file, tickdata, sizeof(tickdata)) != sizeof(tickdata)) + return -1; + *tick = (tickdata[0]<<24) | (tickdata[1]<<16) | (tickdata[2]<<8) | tickdata[3]; + } + else + { + *tick += tickdelta; + } + + } + else + { + /* decode normal chunk */ + *type = (chunk&CHUNKMASK_TYPE)>>5; + *size = chunk&CHUNKMASK_SIZE; + + if(*size == 30) + { + unsigned char sizedata[1]; + if(io_read(play_file, sizedata, sizeof(sizedata)) != sizeof(sizedata)) + return -1; + *size = sizedata[0]; + + } + else if(*size == 31) + { + unsigned char sizedata[2]; + if(io_read(play_file, sizedata, sizeof(sizedata)) != sizeof(sizedata)) + return -1; + *size = (sizedata[1]<<8) | sizedata[0]; + } + } + + return 0; +} + +static void scan_file() +{ + long start_pos; + HEAP *heap = 0; + KEYFRAME_SEARCH *first_key = 0; + KEYFRAME_SEARCH *current_key = 0; + /*DEMOREC_CHUNK chunk;*/ + int chunk_size, chunk_type, chunk_tick = 0; + int i; + + heap = memheap_create(); + + start_pos = io_tell(play_file); + playbackinfo.seekable_points = 0; + + while(1) + { + long current_pos = io_tell(play_file); + + if(read_chunk_header(&chunk_type, &chunk_size, &chunk_tick)) + break; + + /* read the chunk */ + if(chunk_type&CHUNKTYPEFLAG_TICKMARKER) + { + if(chunk_type&CHUNKTICKFLAG_KEYFRAME) + { + KEYFRAME_SEARCH *key; + + /* save the position */ + key = (KEYFRAME_SEARCH *)memheap_allocate(heap, sizeof(KEYFRAME_SEARCH)); + key->frame.filepos = current_pos; + key->frame.tick = chunk_tick; + key->next = 0; + if(current_key) + current_key->next = key; + if(!first_key) + first_key = key; + current_key = key; + playbackinfo.seekable_points++; + } + + if(playbackinfo.first_tick == -1) + playbackinfo.first_tick = chunk_tick; + playbackinfo.last_tick = chunk_tick; + } + else if(chunk_size) + io_skip(play_file, chunk_size); + + } + + /* copy all the frames to an array instead for fast access */ + keyframes = (KEYFRAME*)mem_alloc(playbackinfo.seekable_points*sizeof(KEYFRAME), 1); + for(current_key = first_key, i = 0; current_key; current_key = current_key->next, i++) + keyframes[i] = current_key->frame; + + /* destroy the temporary heap and seek back to the start */ + memheap_destroy(heap); + io_seek(play_file, start_pos, IOSEEK_START); +} + +static void do_tick() +{ + static char compresseddata[CSnapshot::MAX_SIZE]; + static char decompressed[CSnapshot::MAX_SIZE]; + static char data[CSnapshot::MAX_SIZE]; + int chunk_type, chunk_tick, chunk_size; + int data_size; + int got_snapshot = 0; + + /* update ticks */ + playbackinfo.previous_tick = playbackinfo.current_tick; + playbackinfo.current_tick = playbackinfo.next_tick; + chunk_tick = playbackinfo.current_tick; + + while(1) + { + if(read_chunk_header(&chunk_type, &chunk_size, &chunk_tick)) + { + /* stop on error or eof */ + dbg_msg("demorec", "end of file"); + demorec_playback_pause(); + break; + } + + /* read the chunk */ + if(chunk_size) + { + if(io_read(play_file, compresseddata, chunk_size) != (unsigned)chunk_size) + { + /* stop on error or eof */ + dbg_msg("demorec", "error reading chunk"); + demorec_playback_stop(); + break; + } + + data_size = CNetBase::Decompress(compresseddata, chunk_size, decompressed, sizeof(decompressed)); + if(data_size < 0) + { + /* stop on error or eof */ + dbg_msg("demorec", "error during network decompression"); + demorec_playback_stop(); + break; + } + + data_size = intpack_decompress(decompressed, data_size, data); + + if(data_size < 0) + { + dbg_msg("demorec", "error during intpack decompression"); + demorec_playback_stop(); + break; + } + } + + if(chunk_type == CHUNKTYPE_DELTA) + { + /* process delta snapshot */ + static char newsnap[CSnapshot::MAX_SIZE]; + + got_snapshot = 1; + + data_size = CSnapshot::UnpackDelta((CSnapshot*)playback_lastsnapshotdata, (CSnapshot*)newsnap, data, data_size); + + if(data_size >= 0) + { + if(play_callback_snapshot) + play_callback_snapshot(newsnap, data_size); + + playback_lastsnapshotdata_size = data_size; + mem_copy(playback_lastsnapshotdata, newsnap, data_size); + } + else + dbg_msg("demorec", "error duing unpacking of delta, err=%d", data_size); + } + else if(chunk_type == CHUNKTYPE_SNAPSHOT) + { + /* process full snapshot */ + got_snapshot = 1; + + playback_lastsnapshotdata_size = data_size; + mem_copy(playback_lastsnapshotdata, data, data_size); + if(play_callback_snapshot) + play_callback_snapshot(data, data_size); + } + else + { + /* if there were no snapshots in this tick, replay the last one */ + if(!got_snapshot && play_callback_snapshot && playback_lastsnapshotdata_size != -1) + { + got_snapshot = 1; + play_callback_snapshot(playback_lastsnapshotdata, playback_lastsnapshotdata_size); + } + + /* check the remaining types */ + if(chunk_type&CHUNKTYPEFLAG_TICKMARKER) + { + playbackinfo.next_tick = chunk_tick; + break; + } + else if(chunk_type == CHUNKTYPE_MESSAGE) + { + if(play_callback_message) + play_callback_message(data, data_size); + } + } + } +} + +void demorec_playback_pause() +{ + playbackinfo.paused = 1; +} + +void demorec_playback_unpause() +{ + if(playbackinfo.paused) + { + /*playbackinfo.start_tick = playbackinfo.current_tick; + playbackinfo.start_time = time_get();*/ + playbackinfo.paused = 0; + } +} + +int demorec_playback_load(const char *filename) +{ + play_file = engine_openfile(filename, IOFLAG_READ); + if(!play_file) + { + dbg_msg("demorec/playback", "could not open '%s'", filename); + return -1; + } + + /* clear the playback info */ + mem_zero(&playbackinfo, sizeof(playbackinfo)); + playbackinfo.first_tick = -1; + playbackinfo.last_tick = -1; + /*playbackinfo.start_tick = -1;*/ + playbackinfo.next_tick = -1; + playbackinfo.current_tick = -1; + playbackinfo.previous_tick = -1; + playbackinfo.speed = 1; + + playback_lastsnapshotdata_size = -1; + + /* read the header */ + io_read(play_file, &playbackinfo.header, sizeof(playbackinfo.header)); + if(mem_comp(playbackinfo.header.marker, header_marker, sizeof(header_marker)) != 0) + { + dbg_msg("demorec/playback", "'%s' is not a demo file", filename); + io_close(play_file); + play_file = 0; + return -1; + } + + /* scan the file for interessting points */ + scan_file(); + + /* ready for playback */ + return 0; +} + +int demorec_playback_nextframe() +{ + do_tick(); + return demorec_isplaying(); +} + +int demorec_playback_play() +{ + /* fill in previous and next tick */ + while(playbackinfo.previous_tick == -1 && demorec_isplaying()) + do_tick(); + + /* set start info */ + /*playbackinfo.start_tick = playbackinfo.previous_tick; + playbackinfo.start_time = time_get();*/ + playbackinfo.current_time = playbackinfo.previous_tick*time_freq()/SERVER_TICK_SPEED; + playbackinfo.last_update = time_get(); + return 0; +} + +int demorec_playback_set(float percent) +{ + int keyframe; + int wanted_tick; + if(!play_file) + return -1; + + /* -5 because we have to have a current tick and previous tick when we do the playback */ + wanted_tick = playbackinfo.first_tick + (int)((playbackinfo.last_tick-playbackinfo.first_tick)*percent) - 5; + + keyframe = (int)(playbackinfo.seekable_points*percent); + + if(keyframe < 0 || keyframe >= playbackinfo.seekable_points) + return -1; + + /* get correct key frame */ + if(keyframes[keyframe].tick < wanted_tick) + while(keyframe < playbackinfo.seekable_points-1 && keyframes[keyframe].tick < wanted_tick) + keyframe++; + + while(keyframe && keyframes[keyframe].tick > wanted_tick) + keyframe--; + + /* seek to the correct keyframe */ + io_seek(play_file, keyframes[keyframe].filepos, IOSEEK_START); + + /*playbackinfo.start_tick = -1;*/ + playbackinfo.next_tick = -1; + playbackinfo.current_tick = -1; + playbackinfo.previous_tick = -1; + + /* playback everything until we hit our tick */ + while(playbackinfo.previous_tick < wanted_tick) + do_tick(); + + demorec_playback_play(); + + return 0; +} + +void demorec_playback_setspeed(float speed) +{ + playbackinfo.speed = speed; +} + +int demorec_playback_update() +{ + int64 now = time_get(); + int64 deltatime = now-playbackinfo.last_update; + playbackinfo.last_update = now; + + if(!demorec_isplaying()) + return 0; + + if(playbackinfo.paused) + { + + } + else + { + int64 freq = time_freq(); + playbackinfo.current_time += (int64)(deltatime*(double)playbackinfo.speed); + + while(1) + { + int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED; + + /* break if we are ready */ + if(curtick_start > playbackinfo.current_time) + break; + + /* do one more tick */ + do_tick(); + + if(playbackinfo.paused) + return 0; + } + + /* update intratick */ + { + int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED; + int64 prevtick_start = (playbackinfo.previous_tick)*freq/SERVER_TICK_SPEED; + playbackinfo.intratick = (playbackinfo.current_time - prevtick_start) / (float)(curtick_start-prevtick_start); + playbackinfo.ticktime = (playbackinfo.current_time - prevtick_start) / (float)freq; + } + + if(playbackinfo.current_tick == playbackinfo.previous_tick || + playbackinfo.current_tick == playbackinfo.next_tick) + { + dbg_msg("demorec/playback", "tick error prev=%d cur=%d next=%d", + playbackinfo.previous_tick, playbackinfo.current_tick, playbackinfo.next_tick); + } + } + + return 0; +} + +int demorec_playback_stop() +{ + if(!play_file) + return -1; + + dbg_msg("demorec/playback", "Stopped playback"); + io_close(play_file); + play_file = 0; + mem_free(keyframes); + keyframes = 0; + return 0; +} + + diff --git a/src/engine/e_demorec.h b/src/engine/e_demorec.h index 482debb4..9716b463 100644 --- a/src/engine/e_demorec.h +++ b/src/engine/e_demorec.h @@ -2,10 +2,6 @@ #ifndef _DEMOREC_H #define _DEMOREC_H -#ifdef __cplusplus -extern "C"{ -#endif - typedef struct DEMOREC_HEADER { char marker[8]; @@ -70,8 +66,4 @@ const DEMOREC_PLAYBACKINFO *demorec_playback_info(); int demorec_isplaying(); int demorec_playback_stop(); -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_engine.c b/src/engine/e_engine.c deleted file mode 100644 index 7afbb2ce..00000000 --- a/src/engine/e_engine.c +++ /dev/null @@ -1,596 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include "e_linereader.h" - -/* compiled-in data-dir path */ -#define DATA_DIR "data" - -static JOBPOOL hostlookuppool; -static int engine_find_datadir(char *argv0); - -static void con_dbg_dumpmem(void *result, void *user_data) -{ - mem_debug_dump(); -} - -static void con_dbg_lognetwork(void *result, void *user_data) -{ - netcommon_openlog("network_sent.dat", "network_recv.dat"); -} - - -static char application_save_path[512] = {0}; -static char datadir[512] = {0}; - -const char *engine_savepath(const char *filename, char *buffer, int max) -{ - str_format(buffer, max, "%s/%s", application_save_path, filename); - return buffer; -} - -void engine_init(const char *appname) -{ - dbg_logger_stdout(); - dbg_logger_debugger(); - - /* */ - dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING); -#ifdef CONF_ARCH_ENDIAN_LITTLE - dbg_msg("engine", "arch is little endian"); -#elif defined(CONF_ARCH_ENDIAN_BIG) - dbg_msg("engine", "arch is big endian"); -#else - dbg_msg("engine", "unknown endian"); -#endif - - /* init the network */ - net_init(); - netcommon_init(); - - /* create storage location */ - { - char path[1024] = {0}; - fs_storage_path(appname, application_save_path, sizeof(application_save_path)); - if(fs_makedir(application_save_path) == 0) - { - str_format(path, sizeof(path), "%s/screenshots", application_save_path); - fs_makedir(path); - - str_format(path, sizeof(path), "%s/maps", application_save_path); - fs_makedir(path); - - str_format(path, sizeof(path), "%s/downloadedmaps", application_save_path); - fs_makedir(path); - - str_format(path, sizeof(path), "%s/demos", application_save_path); - fs_makedir(path); - } - } - - /* init console and add the console logger */ - console_init(); - dbg_logger(console_print); - - jobs_initpool(&hostlookuppool, 1); - - MACRO_REGISTER_COMMAND("dbg_dumpmem", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_dumpmem, 0x0, "Dump the memory"); - MACRO_REGISTER_COMMAND("dbg_lognetwork", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_lognetwork, 0x0, "Log the network"); - - /* reset the config */ - config_reset(); -} - - -void engine_listdir(int types, const char *path, FS_LISTDIR_CALLBACK cb, void *user) -{ - char buffer[1024]; - - /* list current directory */ - if(types&LISTDIRTYPE_CURRENT) - { - fs_listdir(path, cb, user); - } - - /* list users directory */ - if(types&LISTDIRTYPE_SAVE) - { - engine_savepath(path, buffer, sizeof(buffer)); - fs_listdir(buffer, cb, user); - } - - /* list datadir directory */ - if(types&LISTDIRTYPE_DATA) - { - str_format(buffer, sizeof(buffer), "%s/%s", datadir, path); - fs_listdir(buffer, cb, user); - } -} - -void engine_getpath(char *buffer, int buffer_size, const char *filename, int flags) -{ - if(flags&IOFLAG_WRITE) - engine_savepath(filename, buffer, buffer_size); - else - { - IOHANDLE handle = 0; - - /* check current directory */ - handle = io_open(filename, flags); - if(handle) - { - str_copy(buffer, filename, buffer_size); - io_close(handle); - return; - } - - /* check user directory */ - engine_savepath(filename, buffer, buffer_size); - handle = io_open(buffer, flags); - if(handle) - { - io_close(handle); - return; - } - - /* check normal data directory */ - str_format(buffer, buffer_size, "%s/%s", datadir, filename); - handle = io_open(buffer, flags); - if(handle) - { - io_close(handle); - return; - } - } - - buffer[0] = 0; -} - -IOHANDLE engine_openfile(const char *filename, int flags) -{ - char buffer[1024]; - - if(flags&IOFLAG_WRITE) - { - engine_savepath(filename, buffer, sizeof(buffer)); - return io_open(buffer, flags); - } - else - { - IOHANDLE handle = 0; - - /* check current directory */ - handle = io_open(filename, flags); - if(handle) - return handle; - - /* check user directory */ - engine_savepath(filename, buffer, sizeof(buffer)); - handle = io_open(buffer, flags); - if(handle) - return handle; - - /* check normal data directory */ - str_format(buffer, sizeof(buffer), "%s/%s", datadir, filename); - handle = io_open(buffer, flags); - if(handle) - return handle; - } - return 0; -} - -void engine_parse_arguments(int argc, char **argv) -{ - /* load the configuration */ - int i; - - /* check for datadir override */ - for(i = 1; i < argc; i++) - { - if(argv[i][0] == '-' && argv[i][1] == 'd' && argv[i][2] == 0 && argc - i > 1) - { - str_copy(datadir, argv[i+1], sizeof(datadir)); - i++; - } - } - - /* search for data directory */ - engine_find_datadir(argv[0]); - - dbg_msg("engine/datadir", "paths used:"); - dbg_msg("engine/datadir", "\t."); - dbg_msg("engine/datadir", "\t%s", application_save_path); - dbg_msg("engine/datadir", "\t%s", datadir); - dbg_msg("engine/datadir", "saving files to: %s", application_save_path); - - - /* check for scripts to execute */ - for(i = 1; i < argc; i++) - { - if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1) - { - console_execute_file(argv[i+1]); - i++; - } - } - - /* search arguments for overrides */ - { - int i; - for(i = 1; i < argc; i++) - console_execute_line(argv[i]); - } - - console_execute_file("autoexec.cfg"); - - /* open logfile if needed */ - if(config.logfile[0]) - dbg_logger_file(config.logfile); - - /* set default servers and load from disk*/ - mastersrv_default(); - mastersrv_load(); -} - - -static IOHANDLE config_file = 0; - -int engine_config_write_start() -{ - config_save("settings.cfg"); - config_file = engine_openfile("settings.cfg", IOFLAG_WRITE); - if(config_file == 0) - return -1; - return 0; -} - -void engine_config_write_line(const char *line) -{ - if(config_file) - { -#if defined(CONF_FAMILY_WINDOWS) - static const char newline[] = "\r\n"; -#else - static const char newline[] = "\n"; -#endif - io_write(config_file, line, strlen(line)); - io_write(config_file, newline, sizeof(newline)-1); - } -} - -void engine_config_write_stop() -{ - io_close(config_file); - config_file = 0; -} -/* -void engine_writeconfig() -{ -}*/ - -static int perf_tick = 1; -static PERFORMACE_INFO *current = 0; - -void perf_init() -{ -} - -void perf_next() -{ - perf_tick++; - current = 0; -} - -void perf_start(PERFORMACE_INFO *info) -{ - if(info->tick != perf_tick) - { - info->parent = current; - info->first_child = 0; - info->next_child = 0; - - if(info->parent) - { - info->next_child = info->parent->first_child; - info->parent->first_child = info; - } - - info->tick = perf_tick; - info->biggest = 0; - info->total = 0; - } - - current = info; - current->start = time_get(); -} - -void perf_end() -{ - if(!current) - return; - - current->last_delta = time_get()-current->start; - current->total += current->last_delta; - - if(current->last_delta > current->biggest) - current->biggest = current->last_delta; - - current = current->parent; -} - -static void perf_dump_imp(PERFORMACE_INFO *info, int indent) -{ - char buf[512] = {0}; - int64 freq = time_freq(); - int i; - - for(i = 0; i < indent; i++) - buf[i] = ' '; - - str_format(&buf[indent], sizeof(buf)-indent, "%-20s %8.2f %8.2f", info->name, info->total*1000/(float)freq, info->biggest*1000/(float)freq); - dbg_msg("perf", "%s", buf); - - info = info->first_child; - while(info) - { - perf_dump_imp(info, indent+2); - info = info->next_child; - } -} - -void perf_dump(PERFORMACE_INFO *top) -{ - perf_dump_imp(top, 0); -} - -/* master server functions */ -typedef struct -{ - char hostname[128]; - NETADDR addr; - - HOSTLOOKUP lookup; -} MASTER_INFO; - -static MASTER_INFO master_servers[MAX_MASTERSERVERS] = {{{0}}}; -static int needs_update = -1; - -int mastersrv_refresh_addresses() -{ - int i; - - if(needs_update != -1) - return 0; - - dbg_msg("engine/mastersrv", "refreshing master server addresses"); - - /* add lookup jobs */ - for(i = 0; i < MAX_MASTERSERVERS; i++) - engine_hostlookup(&master_servers[i].lookup, master_servers[i].hostname); - - needs_update = 1; - return 0; -} - -void mastersrv_update() -{ - int i; - - /* check if we need to update */ - if(needs_update != 1) - return; - needs_update = 0; - - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - if(jobs_status(&master_servers[i].lookup.job) != JOBSTATUS_DONE) - needs_update = 1; - else - { - master_servers[i].addr = master_servers[i].lookup.addr; - master_servers[i].addr.port = 8300; - } - } - - if(!needs_update) - { - dbg_msg("engine/mastersrv", "saving addresses"); - mastersrv_save(); - } -} - -int mastersrv_refreshing() -{ - return needs_update; -} - -NETADDR mastersrv_get(int index) -{ - return master_servers[index].addr; -} - -const char *mastersrv_name(int index) -{ - return master_servers[index].hostname; -} - -void mastersrv_dump_servers() -{ - int i; - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - dbg_msg("mastersrv", "#%d = %d.%d.%d.%d", i, - master_servers[i].addr.ip[0], master_servers[i].addr.ip[1], - master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]); - } -} - -void mastersrv_default() -{ - int i; - mem_zero(master_servers, sizeof(master_servers)); - for(i = 0; i < MAX_MASTERSERVERS; i++) - sprintf(master_servers[i].hostname, "master%d.teeworlds.com", i+1); -} - -int mastersrv_load() -{ - LINEREADER lr; - IOHANDLE file; - int count = 0; - - /* try to open file */ - file = engine_openfile("masters.cfg", IOFLAG_READ); - if(!file) - return -1; - - linereader_init(&lr, file); - while(1) - { - MASTER_INFO info = {{0}}; - int ip[4]; - const char *line = linereader_get(&lr); - if(!line) - break; - - /* parse line */ - if(sscanf(line, "%s %d.%d.%d.%d", info.hostname, &ip[0], &ip[1], &ip[2], &ip[3]) == 5) - { - info.addr.ip[0] = (unsigned char)ip[0]; - info.addr.ip[1] = (unsigned char)ip[1]; - info.addr.ip[2] = (unsigned char)ip[2]; - info.addr.ip[3] = (unsigned char)ip[3]; - info.addr.port = 8300; - if(count != MAX_MASTERSERVERS) - { - master_servers[count] = info; - count++; - } - else - dbg_msg("engine/mastersrv", "warning: skipped master server '%s' due to limit of %d", line, MAX_MASTERSERVERS); - } - else - dbg_msg("engine/mastersrv", "warning: couldn't parse master server '%s'", line); - } - - io_close(file); - return 0; -} - -int mastersrv_save() -{ - IOHANDLE file; - int i; - - /* try to open file */ - file = engine_openfile("masters.cfg", IOFLAG_WRITE); - if(!file) - return -1; - - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - char buf[1024]; - str_format(buf, sizeof(buf), "%s %d.%d.%d.%d\n", master_servers[i].hostname, - master_servers[i].addr.ip[0], master_servers[i].addr.ip[1], - master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]); - - io_write(file, buf, strlen(buf)); - } - - io_close(file); - return 0; -} - - -int hostlookup_thread(void *user) -{ - HOSTLOOKUP *lookup = (HOSTLOOKUP *)user; - net_host_lookup(lookup->hostname, &lookup->addr, NETTYPE_IPV4); - return 0; -} - -void engine_hostlookup(HOSTLOOKUP *lookup, const char *hostname) -{ - str_copy(lookup->hostname, hostname, sizeof(lookup->hostname)); - jobs_add(&hostlookuppool, &lookup->job, hostlookup_thread, lookup); -} - -static int engine_find_datadir(char *argv0) -{ - /* 1) use provided data-dir override */ - if(datadir[0]) - { - if(fs_is_dir(datadir)) - return 0; - else - { - dbg_msg("engine/datadir", "specified data-dir '%s' does not exist", datadir); - return -1; - } - } - - /* 2) use data-dir in PWD if present */ - if(fs_is_dir("data")) - { - strcpy(datadir, "data"); - return 0; - } - - /* 3) use compiled-in data-dir if present */ - if (fs_is_dir(DATA_DIR)) - { - strcpy(datadir, DATA_DIR); - return 0; - } - - /* 4) check for usable path in argv[0] */ - { - unsigned int pos = strrchr(argv0, '/') - argv0; - - if (pos < sizeof(datadir)) - { - char basedir[sizeof(datadir)]; - strncpy(basedir, argv0, pos); - basedir[pos] = '\0'; - str_format(datadir, sizeof(datadir), "%s/data", basedir); - - if (fs_is_dir(datadir)) - return 0; - } - } - -#if defined(CONF_FAMILY_UNIX) - /* 5) check for all default locations */ - { - const char *sdirs[] = { - "/usr/share/teeworlds", - "/usr/local/share/teeworlds" - "/opt/teeworlds" - }; - const int sdirs_count = sizeof(sdirs) / sizeof(sdirs[0]); - - int i; - for (i = 0; i < sdirs_count; i++) - { - if (fs_is_dir(sdirs[i])) - { - strcpy(datadir, sdirs[i]); - return 0; - } - } - } -#endif - - /* no data-dir found */ - dbg_msg("engine/datadir", "warning no data directory found"); - return -1; -} diff --git a/src/engine/e_engine.cpp b/src/engine/e_engine.cpp new file mode 100644 index 00000000..4475478a --- /dev/null +++ b/src/engine/e_engine.cpp @@ -0,0 +1,596 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include "e_linereader.h" + +/* compiled-in data-dir path */ +#define DATA_DIR "data" + +static JOBPOOL hostlookuppool; +static int engine_find_datadir(char *argv0); + +static void con_dbg_dumpmem(void *result, void *user_data) +{ + mem_debug_dump(); +} + +static void con_dbg_lognetwork(void *result, void *user_data) +{ + CNetBase::OpenLog("network_sent.dat", "network_recv.dat"); +} + + +static char application_save_path[512] = {0}; +static char datadir[512] = {0}; + +const char *engine_savepath(const char *filename, char *buffer, int max) +{ + str_format(buffer, max, "%s/%s", application_save_path, filename); + return buffer; +} + +void engine_init(const char *appname) +{ + dbg_logger_stdout(); + dbg_logger_debugger(); + + /* */ + dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING); +#ifdef CONF_ARCH_ENDIAN_LITTLE + dbg_msg("engine", "arch is little endian"); +#elif defined(CONF_ARCH_ENDIAN_BIG) + dbg_msg("engine", "arch is big endian"); +#else + dbg_msg("engine", "unknown endian"); +#endif + + /* init the network */ + net_init(); + CNetBase::Init(); + + /* create storage location */ + { + char path[1024] = {0}; + fs_storage_path(appname, application_save_path, sizeof(application_save_path)); + if(fs_makedir(application_save_path) == 0) + { + str_format(path, sizeof(path), "%s/screenshots", application_save_path); + fs_makedir(path); + + str_format(path, sizeof(path), "%s/maps", application_save_path); + fs_makedir(path); + + str_format(path, sizeof(path), "%s/downloadedmaps", application_save_path); + fs_makedir(path); + + str_format(path, sizeof(path), "%s/demos", application_save_path); + fs_makedir(path); + } + } + + /* init console and add the console logger */ + console_init(); + dbg_logger(console_print); + + jobs_initpool(&hostlookuppool, 1); + + MACRO_REGISTER_COMMAND("dbg_dumpmem", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_dumpmem, 0x0, "Dump the memory"); + MACRO_REGISTER_COMMAND("dbg_lognetwork", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_lognetwork, 0x0, "Log the network"); + + /* reset the config */ + config_reset(); +} + + +void engine_listdir(int types, const char *path, FS_LISTDIR_CALLBACK cb, void *user) +{ + char buffer[1024]; + + /* list current directory */ + if(types&LISTDIRTYPE_CURRENT) + { + fs_listdir(path, cb, user); + } + + /* list users directory */ + if(types&LISTDIRTYPE_SAVE) + { + engine_savepath(path, buffer, sizeof(buffer)); + fs_listdir(buffer, cb, user); + } + + /* list datadir directory */ + if(types&LISTDIRTYPE_DATA) + { + str_format(buffer, sizeof(buffer), "%s/%s", datadir, path); + fs_listdir(buffer, cb, user); + } +} + +void engine_getpath(char *buffer, int buffer_size, const char *filename, int flags) +{ + if(flags&IOFLAG_WRITE) + engine_savepath(filename, buffer, buffer_size); + else + { + IOHANDLE handle = 0; + + /* check current directory */ + handle = io_open(filename, flags); + if(handle) + { + str_copy(buffer, filename, buffer_size); + io_close(handle); + return; + } + + /* check user directory */ + engine_savepath(filename, buffer, buffer_size); + handle = io_open(buffer, flags); + if(handle) + { + io_close(handle); + return; + } + + /* check normal data directory */ + str_format(buffer, buffer_size, "%s/%s", datadir, filename); + handle = io_open(buffer, flags); + if(handle) + { + io_close(handle); + return; + } + } + + buffer[0] = 0; +} + +IOHANDLE engine_openfile(const char *filename, int flags) +{ + char buffer[1024]; + + if(flags&IOFLAG_WRITE) + { + engine_savepath(filename, buffer, sizeof(buffer)); + return io_open(buffer, flags); + } + else + { + IOHANDLE handle = 0; + + /* check current directory */ + handle = io_open(filename, flags); + if(handle) + return handle; + + /* check user directory */ + engine_savepath(filename, buffer, sizeof(buffer)); + handle = io_open(buffer, flags); + if(handle) + return handle; + + /* check normal data directory */ + str_format(buffer, sizeof(buffer), "%s/%s", datadir, filename); + handle = io_open(buffer, flags); + if(handle) + return handle; + } + return 0; +} + +void engine_parse_arguments(int argc, char **argv) +{ + /* load the configuration */ + int i; + + /* check for datadir override */ + for(i = 1; i < argc; i++) + { + if(argv[i][0] == '-' && argv[i][1] == 'd' && argv[i][2] == 0 && argc - i > 1) + { + str_copy(datadir, argv[i+1], sizeof(datadir)); + i++; + } + } + + /* search for data directory */ + engine_find_datadir(argv[0]); + + dbg_msg("engine/datadir", "paths used:"); + dbg_msg("engine/datadir", "\t."); + dbg_msg("engine/datadir", "\t%s", application_save_path); + dbg_msg("engine/datadir", "\t%s", datadir); + dbg_msg("engine/datadir", "saving files to: %s", application_save_path); + + + /* check for scripts to execute */ + for(i = 1; i < argc; i++) + { + if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1) + { + console_execute_file(argv[i+1]); + i++; + } + } + + /* search arguments for overrides */ + { + int i; + for(i = 1; i < argc; i++) + console_execute_line(argv[i]); + } + + console_execute_file("autoexec.cfg"); + + /* open logfile if needed */ + if(config.logfile[0]) + dbg_logger_file(config.logfile); + + /* set default servers and load from disk*/ + mastersrv_default(); + mastersrv_load(); +} + + +static IOHANDLE config_file = 0; + +int engine_config_write_start() +{ + config_save(); + config_file = engine_openfile("settings.cfg", IOFLAG_WRITE); + if(config_file == 0) + return -1; + return 0; +} + +void engine_config_write_line(const char *line) +{ + if(config_file) + { +#if defined(CONF_FAMILY_WINDOWS) + static const char newline[] = "\r\n"; +#else + static const char newline[] = "\n"; +#endif + io_write(config_file, line, strlen(line)); + io_write(config_file, newline, sizeof(newline)-1); + } +} + +void engine_config_write_stop() +{ + io_close(config_file); + config_file = 0; +} +/* +void engine_writeconfig() +{ +}*/ + +static int perf_tick = 1; +static PERFORMACE_INFO *current = 0; + +void perf_init() +{ +} + +void perf_next() +{ + perf_tick++; + current = 0; +} + +void perf_start(PERFORMACE_INFO *info) +{ + if(info->tick != perf_tick) + { + info->parent = current; + info->first_child = 0; + info->next_child = 0; + + if(info->parent) + { + info->next_child = info->parent->first_child; + info->parent->first_child = info; + } + + info->tick = perf_tick; + info->biggest = 0; + info->total = 0; + } + + current = info; + current->start = time_get(); +} + +void perf_end() +{ + if(!current) + return; + + current->last_delta = time_get()-current->start; + current->total += current->last_delta; + + if(current->last_delta > current->biggest) + current->biggest = current->last_delta; + + current = current->parent; +} + +static void perf_dump_imp(PERFORMACE_INFO *info, int indent) +{ + char buf[512] = {0}; + int64 freq = time_freq(); + int i; + + for(i = 0; i < indent; i++) + buf[i] = ' '; + + str_format(&buf[indent], sizeof(buf)-indent, "%-20s %8.2f %8.2f", info->name, info->total*1000/(float)freq, info->biggest*1000/(float)freq); + dbg_msg("perf", "%s", buf); + + info = info->first_child; + while(info) + { + perf_dump_imp(info, indent+2); + info = info->next_child; + } +} + +void perf_dump(PERFORMACE_INFO *top) +{ + perf_dump_imp(top, 0); +} + +/* master server functions */ +typedef struct +{ + char hostname[128]; + NETADDR addr; + + HOSTLOOKUP lookup; +} MASTER_INFO; + +static MASTER_INFO master_servers[MAX_MASTERSERVERS] = {{{0}}}; +static int needs_update = -1; + +int mastersrv_refresh_addresses() +{ + int i; + + if(needs_update != -1) + return 0; + + dbg_msg("engine/mastersrv", "refreshing master server addresses"); + + /* add lookup jobs */ + for(i = 0; i < MAX_MASTERSERVERS; i++) + engine_hostlookup(&master_servers[i].lookup, master_servers[i].hostname); + + needs_update = 1; + return 0; +} + +void mastersrv_update() +{ + int i; + + /* check if we need to update */ + if(needs_update != 1) + return; + needs_update = 0; + + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + if(jobs_status(&master_servers[i].lookup.job) != JOBSTATUS_DONE) + needs_update = 1; + else + { + master_servers[i].addr = master_servers[i].lookup.addr; + master_servers[i].addr.port = 8300; + } + } + + if(!needs_update) + { + dbg_msg("engine/mastersrv", "saving addresses"); + mastersrv_save(); + } +} + +int mastersrv_refreshing() +{ + return needs_update; +} + +NETADDR mastersrv_get(int index) +{ + return master_servers[index].addr; +} + +const char *mastersrv_name(int index) +{ + return master_servers[index].hostname; +} + +void mastersrv_dump_servers() +{ + int i; + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + dbg_msg("mastersrv", "#%d = %d.%d.%d.%d", i, + master_servers[i].addr.ip[0], master_servers[i].addr.ip[1], + master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]); + } +} + +void mastersrv_default() +{ + int i; + mem_zero(master_servers, sizeof(master_servers)); + for(i = 0; i < MAX_MASTERSERVERS; i++) + sprintf(master_servers[i].hostname, "master%d.teeworlds.com", i+1); +} + +int mastersrv_load() +{ + LINEREADER lr; + IOHANDLE file; + int count = 0; + + /* try to open file */ + file = engine_openfile("masters.cfg", IOFLAG_READ); + if(!file) + return -1; + + linereader_init(&lr, file); + while(1) + { + MASTER_INFO info = {{0}}; + int ip[4]; + const char *line = linereader_get(&lr); + if(!line) + break; + + /* parse line */ + if(sscanf(line, "%s %d.%d.%d.%d", info.hostname, &ip[0], &ip[1], &ip[2], &ip[3]) == 5) + { + info.addr.ip[0] = (unsigned char)ip[0]; + info.addr.ip[1] = (unsigned char)ip[1]; + info.addr.ip[2] = (unsigned char)ip[2]; + info.addr.ip[3] = (unsigned char)ip[3]; + info.addr.port = 8300; + if(count != MAX_MASTERSERVERS) + { + master_servers[count] = info; + count++; + } + else + dbg_msg("engine/mastersrv", "warning: skipped master server '%s' due to limit of %d", line, MAX_MASTERSERVERS); + } + else + dbg_msg("engine/mastersrv", "warning: couldn't parse master server '%s'", line); + } + + io_close(file); + return 0; +} + +int mastersrv_save() +{ + IOHANDLE file; + int i; + + /* try to open file */ + file = engine_openfile("masters.cfg", IOFLAG_WRITE); + if(!file) + return -1; + + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + char buf[1024]; + str_format(buf, sizeof(buf), "%s %d.%d.%d.%d\n", master_servers[i].hostname, + master_servers[i].addr.ip[0], master_servers[i].addr.ip[1], + master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]); + + io_write(file, buf, strlen(buf)); + } + + io_close(file); + return 0; +} + + +int hostlookup_thread(void *user) +{ + HOSTLOOKUP *lookup = (HOSTLOOKUP *)user; + net_host_lookup(lookup->hostname, &lookup->addr, NETTYPE_IPV4); + return 0; +} + +void engine_hostlookup(HOSTLOOKUP *lookup, const char *hostname) +{ + str_copy(lookup->hostname, hostname, sizeof(lookup->hostname)); + jobs_add(&hostlookuppool, &lookup->job, hostlookup_thread, lookup); +} + +static int engine_find_datadir(char *argv0) +{ + /* 1) use provided data-dir override */ + if(datadir[0]) + { + if(fs_is_dir(datadir)) + return 0; + else + { + dbg_msg("engine/datadir", "specified data-dir '%s' does not exist", datadir); + return -1; + } + } + + /* 2) use data-dir in PWD if present */ + if(fs_is_dir("data")) + { + strcpy(datadir, "data"); + return 0; + } + + /* 3) use compiled-in data-dir if present */ + if (fs_is_dir(DATA_DIR)) + { + strcpy(datadir, DATA_DIR); + return 0; + } + + /* 4) check for usable path in argv[0] */ + { + unsigned int pos = strrchr(argv0, '/') - argv0; + + if (pos < sizeof(datadir)) + { + char basedir[sizeof(datadir)]; + strncpy(basedir, argv0, pos); + basedir[pos] = '\0'; + str_format(datadir, sizeof(datadir), "%s/data", basedir); + + if (fs_is_dir(datadir)) + return 0; + } + } + +#if defined(CONF_FAMILY_UNIX) + /* 5) check for all default locations */ + { + const char *sdirs[] = { + "/usr/share/teeworlds", + "/usr/local/share/teeworlds" + "/opt/teeworlds" + }; + const int sdirs_count = sizeof(sdirs) / sizeof(sdirs[0]); + + int i; + for (i = 0; i < sdirs_count; i++) + { + if (fs_is_dir(sdirs[i])) + { + strcpy(datadir, sdirs[i]); + return 0; + } + } + } +#endif + + /* no data-dir found */ + dbg_msg("engine/datadir", "warning no data directory found"); + return -1; +} diff --git a/src/engine/e_huffman.c b/src/engine/e_huffman.c deleted file mode 100644 index 43914010..00000000 --- a/src/engine/e_huffman.c +++ /dev/null @@ -1,276 +0,0 @@ -#include /* memset */ -#include "e_huffman.h" - -typedef struct HUFFMAN_CONSTRUCT_NODE -{ - unsigned short node_id; - int frequency; -} HUFFMAN_CONSTRUCT_NODE; - -static void huffman_setbits_r(HUFFMAN_STATE *huff, HUFFMAN_NODE *node, int bits, int depth) -{ - if(node->leafs[1] != 0xffff) - huffman_setbits_r(huff, &huff->nodes[node->leafs[1]], bits|(1<leafs[0] != 0xffff) - huffman_setbits_r(huff, &huff->nodes[node->leafs[0]], bits, depth+1); - - if(node->num_bits) - { - node->bits = bits; - node->num_bits = depth; - } -} - -/* TODO: this should be something faster, but it's enough for now */ -void bubblesort(HUFFMAN_CONSTRUCT_NODE **list, int size) -{ - int i, changed = 1; - HUFFMAN_CONSTRUCT_NODE *temp; - - while(changed) - { - changed = 0; - for(i = 0; i < size-1; i++) - { - if(list[i]->frequency < list[i+1]->frequency) - { - temp = list[i]; - list[i] = list[i+1]; - list[i+1] = temp; - changed = 1; - } - } - } -} - -static void huffman_construct_tree(HUFFMAN_STATE *huff, const unsigned *frequencies) -{ - HUFFMAN_CONSTRUCT_NODE nodes_left_storage[HUFFMAN_MAX_SYMBOLS]; - HUFFMAN_CONSTRUCT_NODE *nodes_left[HUFFMAN_MAX_SYMBOLS]; - int num_nodes_left = HUFFMAN_MAX_SYMBOLS; - int i; - - /* add the symbols */ - for(i = 0; i < HUFFMAN_MAX_SYMBOLS; i++) - { - huff->nodes[i].num_bits = -1; - huff->nodes[i].symbol = i; - huff->nodes[i].leafs[0] = -1; - huff->nodes[i].leafs[1] = -1; - - if(i == HUFFMAN_EOF_SYMBOL) - nodes_left_storage[i].frequency = 1; - else - nodes_left_storage[i].frequency = frequencies[i]; - nodes_left_storage[i].node_id = i; - nodes_left[i] = &nodes_left_storage[i]; - - } - huff->num_nodes = HUFFMAN_MAX_SYMBOLS; - - /* construct the table */ - while(num_nodes_left > 1) - { - /* we can't rely on stdlib's qsort for this, it can generate different results on different implementations */ - bubblesort(nodes_left, num_nodes_left); - - huff->nodes[huff->num_nodes].num_bits = 0; - huff->nodes[huff->num_nodes].leafs[0] = nodes_left[num_nodes_left-1]->node_id; - huff->nodes[huff->num_nodes].leafs[1] = nodes_left[num_nodes_left-2]->node_id; - nodes_left[num_nodes_left-2]->node_id = huff->num_nodes; - nodes_left[num_nodes_left-2]->frequency = nodes_left[num_nodes_left-1]->frequency + nodes_left[num_nodes_left-2]->frequency; - - huff->num_nodes++; - num_nodes_left--; - } - - /* set start node */ - huff->start_node = &huff->nodes[huff->num_nodes-1]; - - /* build symbol bits */ - huffman_setbits_r(huff, huff->start_node, 0, 0); -} - -void huffman_init(HUFFMAN_STATE *huff, const unsigned *frequencies) -{ - int i; - - /* make sure to cleanout every thing */ - memset(huff, 0, sizeof(HUFFMAN_STATE)); - - /* construct the tree */ - huffman_construct_tree(huff, frequencies); - - /* build decode LUT */ - for(i = 0; i < HUFFMAN_LUTSIZE; i++) - { - unsigned bits = i; - int k; - HUFFMAN_NODE *node = huff->start_node; - for(k = 0; k < HUFFMAN_LUTBITS; k++) - { - node = &huff->nodes[node->leafs[bits&1]]; - bits >>= 1; - - if(!node) - break; - - if(node->num_bits) - { - huff->decode_lut[i] = node; - break; - } - } - - if(k == HUFFMAN_LUTBITS) - huff->decode_lut[i] = node; - } - -} - -/*****************************************************************/ -int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size) -{ - /* this macro loads a symbol for a byte into bits and bitcount */ -#define HUFFMAN_MACRO_LOADSYMBOL(sym) \ - bits |= huff->nodes[sym].bits << bitcount; \ - bitcount += huff->nodes[sym].num_bits; - - /* this macro writes the symbol stored in bits and bitcount to the dst pointer */ -#define HUFFMAN_MACRO_WRITE() \ - while(bitcount >= 8) \ - { \ - *dst++ = (unsigned char)(bits&0xff); \ - if(dst == dst_end) \ - return -1; \ - bits >>= 8; \ - bitcount -= 8; \ - } - - /* setup buffer pointers */ - const unsigned char *src = (const unsigned char *)input; - const unsigned char *src_end = src + input_size; - unsigned char *dst = (unsigned char *)output; - unsigned char *dst_end = dst + output_size; - - /* symbol variables */ - unsigned bits = 0; - unsigned bitcount = 0; - - /* make sure that we have data that we want to compress */ - if(input_size) - { - /* {A} load the first symbol */ - int symbol = *src++; - - while(src != src_end) - { - /* {B} load the symbol */ - HUFFMAN_MACRO_LOADSYMBOL(symbol) - - /* {C} fetch next symbol, this is done here because it will reduce dependency in the code */ - symbol = *src++; - - /* {B} write the symbol loaded at */ - HUFFMAN_MACRO_WRITE() - } - - /* write the last symbol loaded from {C} or {A} in the case of only 1 byte input buffer */ - HUFFMAN_MACRO_LOADSYMBOL(symbol) - HUFFMAN_MACRO_WRITE() - } - - /* write EOF symbol */ - HUFFMAN_MACRO_LOADSYMBOL(HUFFMAN_EOF_SYMBOL) - HUFFMAN_MACRO_WRITE() - - /* write out the last bits */ - *dst++ = bits; - - /* return the size of the output */ - return (int)(dst - (const unsigned char *)output); - - /* remove macros */ -#undef HUFFMAN_MACRO_LOADSYMBOL -#undef HUFFMAN_MACRO_WRITE -} - -/*****************************************************************/ -int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size) -{ - /* setup buffer pointers */ - unsigned char *dst = (unsigned char *)output; - unsigned char *src = (unsigned char *)input; - unsigned char *dst_end = dst + output_size; - unsigned char *src_end = src + input_size; - - unsigned bits = 0; - unsigned bitcount = 0; - - HUFFMAN_NODE *eof = &huff->nodes[HUFFMAN_EOF_SYMBOL]; - HUFFMAN_NODE *node = 0; - - while(1) - { - /* {A} try to load a node now, this will reduce dependency at location {D} */ - node = 0; - if(bitcount >= HUFFMAN_LUTBITS) - node = huff->decode_lut[bits&HUFFMAN_LUTMASK]; - - /* {B} fill with new bits */ - while(bitcount < 24 && src != src_end) - { - bits |= (*src++) << bitcount; - bitcount += 8; - } - - /* {C} load symbol now if we didn't that earlier at location {A} */ - if(!node) - node = huff->decode_lut[bits&HUFFMAN_LUTMASK]; - - /* {D} check if we hit a symbol already */ - if(node->num_bits) - { - /* remove the bits for that symbol */ - bits >>= node->num_bits; - bitcount -= node->num_bits; - } - else - { - /* remove the bits that the lut checked up for us */ - bits >>= HUFFMAN_LUTBITS; - bitcount -= HUFFMAN_LUTBITS; - - /* walk the tree bit by bit */ - while(1) - { - /* traverse tree */ - node = &huff->nodes[node->leafs[bits&1]]; - - /* remove bit */ - bitcount--; - bits >>= 1; - - /* check if we hit a symbol */ - if(node->num_bits) - break; - - /* no more bits, decoding error */ - if(bitcount == 0) - return -1; - } - } - - /* check for eof */ - if(node == eof) - break; - - /* output character */ - if(dst == dst_end) - return -1; - *dst++ = node->symbol; - } - - /* return the size of the decompressed buffer */ - return (int)(dst - (const unsigned char *)output); -} diff --git a/src/engine/e_huffman.cpp b/src/engine/e_huffman.cpp new file mode 100644 index 00000000..43914010 --- /dev/null +++ b/src/engine/e_huffman.cpp @@ -0,0 +1,276 @@ +#include /* memset */ +#include "e_huffman.h" + +typedef struct HUFFMAN_CONSTRUCT_NODE +{ + unsigned short node_id; + int frequency; +} HUFFMAN_CONSTRUCT_NODE; + +static void huffman_setbits_r(HUFFMAN_STATE *huff, HUFFMAN_NODE *node, int bits, int depth) +{ + if(node->leafs[1] != 0xffff) + huffman_setbits_r(huff, &huff->nodes[node->leafs[1]], bits|(1<leafs[0] != 0xffff) + huffman_setbits_r(huff, &huff->nodes[node->leafs[0]], bits, depth+1); + + if(node->num_bits) + { + node->bits = bits; + node->num_bits = depth; + } +} + +/* TODO: this should be something faster, but it's enough for now */ +void bubblesort(HUFFMAN_CONSTRUCT_NODE **list, int size) +{ + int i, changed = 1; + HUFFMAN_CONSTRUCT_NODE *temp; + + while(changed) + { + changed = 0; + for(i = 0; i < size-1; i++) + { + if(list[i]->frequency < list[i+1]->frequency) + { + temp = list[i]; + list[i] = list[i+1]; + list[i+1] = temp; + changed = 1; + } + } + } +} + +static void huffman_construct_tree(HUFFMAN_STATE *huff, const unsigned *frequencies) +{ + HUFFMAN_CONSTRUCT_NODE nodes_left_storage[HUFFMAN_MAX_SYMBOLS]; + HUFFMAN_CONSTRUCT_NODE *nodes_left[HUFFMAN_MAX_SYMBOLS]; + int num_nodes_left = HUFFMAN_MAX_SYMBOLS; + int i; + + /* add the symbols */ + for(i = 0; i < HUFFMAN_MAX_SYMBOLS; i++) + { + huff->nodes[i].num_bits = -1; + huff->nodes[i].symbol = i; + huff->nodes[i].leafs[0] = -1; + huff->nodes[i].leafs[1] = -1; + + if(i == HUFFMAN_EOF_SYMBOL) + nodes_left_storage[i].frequency = 1; + else + nodes_left_storage[i].frequency = frequencies[i]; + nodes_left_storage[i].node_id = i; + nodes_left[i] = &nodes_left_storage[i]; + + } + huff->num_nodes = HUFFMAN_MAX_SYMBOLS; + + /* construct the table */ + while(num_nodes_left > 1) + { + /* we can't rely on stdlib's qsort for this, it can generate different results on different implementations */ + bubblesort(nodes_left, num_nodes_left); + + huff->nodes[huff->num_nodes].num_bits = 0; + huff->nodes[huff->num_nodes].leafs[0] = nodes_left[num_nodes_left-1]->node_id; + huff->nodes[huff->num_nodes].leafs[1] = nodes_left[num_nodes_left-2]->node_id; + nodes_left[num_nodes_left-2]->node_id = huff->num_nodes; + nodes_left[num_nodes_left-2]->frequency = nodes_left[num_nodes_left-1]->frequency + nodes_left[num_nodes_left-2]->frequency; + + huff->num_nodes++; + num_nodes_left--; + } + + /* set start node */ + huff->start_node = &huff->nodes[huff->num_nodes-1]; + + /* build symbol bits */ + huffman_setbits_r(huff, huff->start_node, 0, 0); +} + +void huffman_init(HUFFMAN_STATE *huff, const unsigned *frequencies) +{ + int i; + + /* make sure to cleanout every thing */ + memset(huff, 0, sizeof(HUFFMAN_STATE)); + + /* construct the tree */ + huffman_construct_tree(huff, frequencies); + + /* build decode LUT */ + for(i = 0; i < HUFFMAN_LUTSIZE; i++) + { + unsigned bits = i; + int k; + HUFFMAN_NODE *node = huff->start_node; + for(k = 0; k < HUFFMAN_LUTBITS; k++) + { + node = &huff->nodes[node->leafs[bits&1]]; + bits >>= 1; + + if(!node) + break; + + if(node->num_bits) + { + huff->decode_lut[i] = node; + break; + } + } + + if(k == HUFFMAN_LUTBITS) + huff->decode_lut[i] = node; + } + +} + +/*****************************************************************/ +int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size) +{ + /* this macro loads a symbol for a byte into bits and bitcount */ +#define HUFFMAN_MACRO_LOADSYMBOL(sym) \ + bits |= huff->nodes[sym].bits << bitcount; \ + bitcount += huff->nodes[sym].num_bits; + + /* this macro writes the symbol stored in bits and bitcount to the dst pointer */ +#define HUFFMAN_MACRO_WRITE() \ + while(bitcount >= 8) \ + { \ + *dst++ = (unsigned char)(bits&0xff); \ + if(dst == dst_end) \ + return -1; \ + bits >>= 8; \ + bitcount -= 8; \ + } + + /* setup buffer pointers */ + const unsigned char *src = (const unsigned char *)input; + const unsigned char *src_end = src + input_size; + unsigned char *dst = (unsigned char *)output; + unsigned char *dst_end = dst + output_size; + + /* symbol variables */ + unsigned bits = 0; + unsigned bitcount = 0; + + /* make sure that we have data that we want to compress */ + if(input_size) + { + /* {A} load the first symbol */ + int symbol = *src++; + + while(src != src_end) + { + /* {B} load the symbol */ + HUFFMAN_MACRO_LOADSYMBOL(symbol) + + /* {C} fetch next symbol, this is done here because it will reduce dependency in the code */ + symbol = *src++; + + /* {B} write the symbol loaded at */ + HUFFMAN_MACRO_WRITE() + } + + /* write the last symbol loaded from {C} or {A} in the case of only 1 byte input buffer */ + HUFFMAN_MACRO_LOADSYMBOL(symbol) + HUFFMAN_MACRO_WRITE() + } + + /* write EOF symbol */ + HUFFMAN_MACRO_LOADSYMBOL(HUFFMAN_EOF_SYMBOL) + HUFFMAN_MACRO_WRITE() + + /* write out the last bits */ + *dst++ = bits; + + /* return the size of the output */ + return (int)(dst - (const unsigned char *)output); + + /* remove macros */ +#undef HUFFMAN_MACRO_LOADSYMBOL +#undef HUFFMAN_MACRO_WRITE +} + +/*****************************************************************/ +int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size) +{ + /* setup buffer pointers */ + unsigned char *dst = (unsigned char *)output; + unsigned char *src = (unsigned char *)input; + unsigned char *dst_end = dst + output_size; + unsigned char *src_end = src + input_size; + + unsigned bits = 0; + unsigned bitcount = 0; + + HUFFMAN_NODE *eof = &huff->nodes[HUFFMAN_EOF_SYMBOL]; + HUFFMAN_NODE *node = 0; + + while(1) + { + /* {A} try to load a node now, this will reduce dependency at location {D} */ + node = 0; + if(bitcount >= HUFFMAN_LUTBITS) + node = huff->decode_lut[bits&HUFFMAN_LUTMASK]; + + /* {B} fill with new bits */ + while(bitcount < 24 && src != src_end) + { + bits |= (*src++) << bitcount; + bitcount += 8; + } + + /* {C} load symbol now if we didn't that earlier at location {A} */ + if(!node) + node = huff->decode_lut[bits&HUFFMAN_LUTMASK]; + + /* {D} check if we hit a symbol already */ + if(node->num_bits) + { + /* remove the bits for that symbol */ + bits >>= node->num_bits; + bitcount -= node->num_bits; + } + else + { + /* remove the bits that the lut checked up for us */ + bits >>= HUFFMAN_LUTBITS; + bitcount -= HUFFMAN_LUTBITS; + + /* walk the tree bit by bit */ + while(1) + { + /* traverse tree */ + node = &huff->nodes[node->leafs[bits&1]]; + + /* remove bit */ + bitcount--; + bits >>= 1; + + /* check if we hit a symbol */ + if(node->num_bits) + break; + + /* no more bits, decoding error */ + if(bitcount == 0) + return -1; + } + } + + /* check for eof */ + if(node == eof) + break; + + /* output character */ + if(dst == dst_end) + return -1; + *dst++ = node->symbol; + } + + /* return the size of the decompressed buffer */ + return (int)(dst - (const unsigned char *)output); +} diff --git a/src/engine/e_huffman.h b/src/engine/e_huffman.h index c4e20223..635c74a1 100644 --- a/src/engine/e_huffman.h +++ b/src/engine/e_huffman.h @@ -1,10 +1,6 @@ #ifndef __HUFFMAN_HEADER__ #define __HUFFMAN_HEADER__ -#ifdef __cplusplus -extern "C" { -#endif - enum { HUFFMAN_EOF_SYMBOL = 256, @@ -84,8 +80,4 @@ int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, voi */ int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size); -#ifdef __cplusplus -} -#endif - #endif /* __HUFFMAN_HEADER__ */ diff --git a/src/engine/e_if_other.h b/src/engine/e_if_other.h index 5c839750..3fbbd81e 100644 --- a/src/engine/e_if_other.h +++ b/src/engine/e_if_other.h @@ -69,11 +69,12 @@ void perf_init(); void perf_next(); void perf_start(PERFORMACE_INFO *info); void perf_end(); -void perf_dump(); +void perf_dump(PERFORMACE_INFO *top); int gfx_init(); void gfx_shutdown(); void gfx_swap(); + int gfx_window_active(); int gfx_window_open(); diff --git a/src/engine/e_jobs.c b/src/engine/e_jobs.c deleted file mode 100644 index e87b395a..00000000 --- a/src/engine/e_jobs.c +++ /dev/null @@ -1,76 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "e_jobs.h" - -void worker_thread(void *user) -{ - JOBPOOL *pool = (JOBPOOL *)user; - - while(1) - { - JOB *job = 0; - - /* fetch job from queue */ - lock_wait(pool->lock); - if(pool->first_job) - { - job = pool->first_job; - pool->first_job = pool->first_job->next; - if(pool->first_job) - pool->first_job->prev = 0; - else - pool->last_job = 0; - } - lock_release(pool->lock); - - /* do the job if we have one */ - if(job) - { - job->status = JOBSTATUS_RUNNING; - job->result = job->func(job->func_data); - job->status = JOBSTATUS_DONE; - } - else - thread_sleep(10); - } - -} - -int jobs_initpool(JOBPOOL *pool, int num_threads) -{ - int i; - - /* empty the pool */ - mem_zero(pool, sizeof(JOBPOOL)); - pool->lock = lock_create(); - - /* start threads */ - for(i = 0; i < num_threads; i++) - thread_create(worker_thread, pool); - return 0; -} - -int jobs_add(JOBPOOL *pool, JOB *job, JOBFUNC func, void *data) -{ - mem_zero(job, sizeof(JOB)); - job->func = func; - job->func_data = data; - - lock_wait(pool->lock); - - /* add job to queue */ - job->prev = pool->last_job; - if(pool->last_job) - pool->last_job->next = job; - pool->last_job = job; - if(!pool->first_job) - pool->first_job = job; - - lock_release(pool->lock); - return 0; -} - -int jobs_status(JOB *job) -{ - return job->status; -} diff --git a/src/engine/e_jobs.cpp b/src/engine/e_jobs.cpp new file mode 100644 index 00000000..e87b395a --- /dev/null +++ b/src/engine/e_jobs.cpp @@ -0,0 +1,76 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "e_jobs.h" + +void worker_thread(void *user) +{ + JOBPOOL *pool = (JOBPOOL *)user; + + while(1) + { + JOB *job = 0; + + /* fetch job from queue */ + lock_wait(pool->lock); + if(pool->first_job) + { + job = pool->first_job; + pool->first_job = pool->first_job->next; + if(pool->first_job) + pool->first_job->prev = 0; + else + pool->last_job = 0; + } + lock_release(pool->lock); + + /* do the job if we have one */ + if(job) + { + job->status = JOBSTATUS_RUNNING; + job->result = job->func(job->func_data); + job->status = JOBSTATUS_DONE; + } + else + thread_sleep(10); + } + +} + +int jobs_initpool(JOBPOOL *pool, int num_threads) +{ + int i; + + /* empty the pool */ + mem_zero(pool, sizeof(JOBPOOL)); + pool->lock = lock_create(); + + /* start threads */ + for(i = 0; i < num_threads; i++) + thread_create(worker_thread, pool); + return 0; +} + +int jobs_add(JOBPOOL *pool, JOB *job, JOBFUNC func, void *data) +{ + mem_zero(job, sizeof(JOB)); + job->func = func; + job->func_data = data; + + lock_wait(pool->lock); + + /* add job to queue */ + job->prev = pool->last_job; + if(pool->last_job) + pool->last_job->next = job; + pool->last_job = job; + if(!pool->first_job) + pool->first_job = job; + + lock_release(pool->lock); + return 0; +} + +int jobs_status(JOB *job) +{ + return job->status; +} diff --git a/src/engine/e_keynames.c b/src/engine/e_keynames.c deleted file mode 100644 index c81744b9..00000000 --- a/src/engine/e_keynames.c +++ /dev/null @@ -1,524 +0,0 @@ -/* AUTO GENERATED! DO NOT EDIT MANUALLY! */ - -#include - -static const char key_strings[512][16] = -{ - - "first", - "&1", - "&2", - "&3", - "&4", - "&5", - "&6", - "&7", - "backspace", - "tab", - "&10", - "&11", - "clear", - "return", - "&14", - "&15", - "&16", - "&17", - "&18", - "pause", - "&20", - "&21", - "&22", - "&23", - "&24", - "&25", - "&26", - "escape", - "&28", - "&29", - "&30", - "&31", - "space", - "exclaim", - "quotedbl", - "hash", - "dollar", - "&37", - "ampersand", - "quote", - "leftparen", - "rightparen", - "asterisk", - "plus", - "comma", - "minus", - "period", - "slash", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "colon", - "semicolon", - "less", - "equals", - "greater", - "question", - "at", - "&65", - "&66", - "&67", - "&68", - "&69", - "&70", - "&71", - "&72", - "&73", - "&74", - "&75", - "&76", - "&77", - "&78", - "&79", - "&80", - "&81", - "&82", - "&83", - "&84", - "&85", - "&86", - "&87", - "&88", - "&89", - "&90", - "leftbracket", - "backslash", - "rightbracket", - "caret", - "underscore", - "backquote", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "&123", - "&124", - "&125", - "&126", - "delete", - "&128", - "&129", - "&130", - "&131", - "&132", - "&133", - "&134", - "&135", - "&136", - "&137", - "&138", - "&139", - "&140", - "&141", - "&142", - "&143", - "&144", - "&145", - "&146", - "&147", - "&148", - "&149", - "&150", - "&151", - "&152", - "&153", - "&154", - "&155", - "&156", - "&157", - "&158", - "&159", - "world_0", - "world_1", - "world_2", - "world_3", - "world_4", - "world_5", - "world_6", - "world_7", - "world_8", - "world_9", - "world_10", - "world_11", - "world_12", - "world_13", - "world_14", - "world_15", - "world_16", - "world_17", - "world_18", - "world_19", - "world_20", - "world_21", - "world_22", - "world_23", - "world_24", - "world_25", - "world_26", - "world_27", - "world_28", - "world_29", - "world_30", - "world_31", - "world_32", - "world_33", - "world_34", - "world_35", - "world_36", - "world_37", - "world_38", - "world_39", - "world_40", - "world_41", - "world_42", - "world_43", - "world_44", - "world_45", - "world_46", - "world_47", - "world_48", - "world_49", - "world_50", - "world_51", - "world_52", - "world_53", - "world_54", - "world_55", - "world_56", - "world_57", - "world_58", - "world_59", - "world_60", - "world_61", - "world_62", - "world_63", - "world_64", - "world_65", - "world_66", - "world_67", - "world_68", - "world_69", - "world_70", - "world_71", - "world_72", - "world_73", - "world_74", - "world_75", - "world_76", - "world_77", - "world_78", - "world_79", - "world_80", - "world_81", - "world_82", - "world_83", - "world_84", - "world_85", - "world_86", - "world_87", - "world_88", - "world_89", - "world_90", - "world_91", - "world_92", - "world_93", - "world_94", - "world_95", - "kp0", - "kp1", - "kp2", - "kp3", - "kp4", - "kp5", - "kp6", - "kp7", - "kp8", - "kp9", - "kp_period", - "kp_divide", - "kp_multiply", - "kp_minus", - "kp_plus", - "kp_enter", - "kp_equals", - "up", - "down", - "right", - "left", - "insert", - "home", - "end", - "pageup", - "pagedown", - "f1", - "f2", - "f3", - "f4", - "f5", - "f6", - "f7", - "f8", - "f9", - "f10", - "f11", - "f12", - "f13", - "f14", - "f15", - "&297", - "&298", - "&299", - "numlock", - "capslock", - "scrollock", - "rshift", - "lshift", - "rctrl", - "lctrl", - "ralt", - "lalt", - "rmeta", - "lmeta", - "lsuper", - "rsuper", - "mode", - "compose", - "help", - "print", - "sysreq", - "break", - "menu", - "power", - "euro", - "undo", - "mouse1", - "mouse2", - "mouse3", - "mouse4", - "mouse5", - "mouse6", - "mouse7", - "mouse8", - "mousewheelup", - "mousewheeldown", - "&333", - "&334", - "&335", - "&336", - "&337", - "&338", - "&339", - "&340", - "&341", - "&342", - "&343", - "&344", - "&345", - "&346", - "&347", - "&348", - "&349", - "&350", - "&351", - "&352", - "&353", - "&354", - "&355", - "&356", - "&357", - "&358", - "&359", - "&360", - "&361", - "&362", - "&363", - "&364", - "&365", - "&366", - "&367", - "&368", - "&369", - "&370", - "&371", - "&372", - "&373", - "&374", - "&375", - "&376", - "&377", - "&378", - "&379", - "&380", - "&381", - "&382", - "&383", - "&384", - "&385", - "&386", - "&387", - "&388", - "&389", - "&390", - "&391", - "&392", - "&393", - "&394", - "&395", - "&396", - "&397", - "&398", - "&399", - "&400", - "&401", - "&402", - "&403", - "&404", - "&405", - "&406", - "&407", - "&408", - "&409", - "&410", - "&411", - "&412", - "&413", - "&414", - "&415", - "&416", - "&417", - "&418", - "&419", - "&420", - "&421", - "&422", - "&423", - "&424", - "&425", - "&426", - "&427", - "&428", - "&429", - "&430", - "&431", - "&432", - "&433", - "&434", - "&435", - "&436", - "&437", - "&438", - "&439", - "&440", - "&441", - "&442", - "&443", - "&444", - "&445", - "&446", - "&447", - "&448", - "&449", - "&450", - "&451", - "&452", - "&453", - "&454", - "&455", - "&456", - "&457", - "&458", - "&459", - "&460", - "&461", - "&462", - "&463", - "&464", - "&465", - "&466", - "&467", - "&468", - "&469", - "&470", - "&471", - "&472", - "&473", - "&474", - "&475", - "&476", - "&477", - "&478", - "&479", - "&480", - "&481", - "&482", - "&483", - "&484", - "&485", - "&486", - "&487", - "&488", - "&489", - "&490", - "&491", - "&492", - "&493", - "&494", - "&495", - "&496", - "&497", - "&498", - "&499", - "&500", - "&501", - "&502", - "&503", - "&504", - "&505", - "&506", - "&507", - "&508", - "&509", - "&510", - "&511", -}; - -const char *inp_key_name(int k) { if (k >= 0 && k < 512) return key_strings[k]; else return key_strings[0]; } -int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, "-?-")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; } - diff --git a/src/engine/e_keynames.cpp b/src/engine/e_keynames.cpp new file mode 100644 index 00000000..c81744b9 --- /dev/null +++ b/src/engine/e_keynames.cpp @@ -0,0 +1,524 @@ +/* AUTO GENERATED! DO NOT EDIT MANUALLY! */ + +#include + +static const char key_strings[512][16] = +{ + + "first", + "&1", + "&2", + "&3", + "&4", + "&5", + "&6", + "&7", + "backspace", + "tab", + "&10", + "&11", + "clear", + "return", + "&14", + "&15", + "&16", + "&17", + "&18", + "pause", + "&20", + "&21", + "&22", + "&23", + "&24", + "&25", + "&26", + "escape", + "&28", + "&29", + "&30", + "&31", + "space", + "exclaim", + "quotedbl", + "hash", + "dollar", + "&37", + "ampersand", + "quote", + "leftparen", + "rightparen", + "asterisk", + "plus", + "comma", + "minus", + "period", + "slash", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "colon", + "semicolon", + "less", + "equals", + "greater", + "question", + "at", + "&65", + "&66", + "&67", + "&68", + "&69", + "&70", + "&71", + "&72", + "&73", + "&74", + "&75", + "&76", + "&77", + "&78", + "&79", + "&80", + "&81", + "&82", + "&83", + "&84", + "&85", + "&86", + "&87", + "&88", + "&89", + "&90", + "leftbracket", + "backslash", + "rightbracket", + "caret", + "underscore", + "backquote", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "&123", + "&124", + "&125", + "&126", + "delete", + "&128", + "&129", + "&130", + "&131", + "&132", + "&133", + "&134", + "&135", + "&136", + "&137", + "&138", + "&139", + "&140", + "&141", + "&142", + "&143", + "&144", + "&145", + "&146", + "&147", + "&148", + "&149", + "&150", + "&151", + "&152", + "&153", + "&154", + "&155", + "&156", + "&157", + "&158", + "&159", + "world_0", + "world_1", + "world_2", + "world_3", + "world_4", + "world_5", + "world_6", + "world_7", + "world_8", + "world_9", + "world_10", + "world_11", + "world_12", + "world_13", + "world_14", + "world_15", + "world_16", + "world_17", + "world_18", + "world_19", + "world_20", + "world_21", + "world_22", + "world_23", + "world_24", + "world_25", + "world_26", + "world_27", + "world_28", + "world_29", + "world_30", + "world_31", + "world_32", + "world_33", + "world_34", + "world_35", + "world_36", + "world_37", + "world_38", + "world_39", + "world_40", + "world_41", + "world_42", + "world_43", + "world_44", + "world_45", + "world_46", + "world_47", + "world_48", + "world_49", + "world_50", + "world_51", + "world_52", + "world_53", + "world_54", + "world_55", + "world_56", + "world_57", + "world_58", + "world_59", + "world_60", + "world_61", + "world_62", + "world_63", + "world_64", + "world_65", + "world_66", + "world_67", + "world_68", + "world_69", + "world_70", + "world_71", + "world_72", + "world_73", + "world_74", + "world_75", + "world_76", + "world_77", + "world_78", + "world_79", + "world_80", + "world_81", + "world_82", + "world_83", + "world_84", + "world_85", + "world_86", + "world_87", + "world_88", + "world_89", + "world_90", + "world_91", + "world_92", + "world_93", + "world_94", + "world_95", + "kp0", + "kp1", + "kp2", + "kp3", + "kp4", + "kp5", + "kp6", + "kp7", + "kp8", + "kp9", + "kp_period", + "kp_divide", + "kp_multiply", + "kp_minus", + "kp_plus", + "kp_enter", + "kp_equals", + "up", + "down", + "right", + "left", + "insert", + "home", + "end", + "pageup", + "pagedown", + "f1", + "f2", + "f3", + "f4", + "f5", + "f6", + "f7", + "f8", + "f9", + "f10", + "f11", + "f12", + "f13", + "f14", + "f15", + "&297", + "&298", + "&299", + "numlock", + "capslock", + "scrollock", + "rshift", + "lshift", + "rctrl", + "lctrl", + "ralt", + "lalt", + "rmeta", + "lmeta", + "lsuper", + "rsuper", + "mode", + "compose", + "help", + "print", + "sysreq", + "break", + "menu", + "power", + "euro", + "undo", + "mouse1", + "mouse2", + "mouse3", + "mouse4", + "mouse5", + "mouse6", + "mouse7", + "mouse8", + "mousewheelup", + "mousewheeldown", + "&333", + "&334", + "&335", + "&336", + "&337", + "&338", + "&339", + "&340", + "&341", + "&342", + "&343", + "&344", + "&345", + "&346", + "&347", + "&348", + "&349", + "&350", + "&351", + "&352", + "&353", + "&354", + "&355", + "&356", + "&357", + "&358", + "&359", + "&360", + "&361", + "&362", + "&363", + "&364", + "&365", + "&366", + "&367", + "&368", + "&369", + "&370", + "&371", + "&372", + "&373", + "&374", + "&375", + "&376", + "&377", + "&378", + "&379", + "&380", + "&381", + "&382", + "&383", + "&384", + "&385", + "&386", + "&387", + "&388", + "&389", + "&390", + "&391", + "&392", + "&393", + "&394", + "&395", + "&396", + "&397", + "&398", + "&399", + "&400", + "&401", + "&402", + "&403", + "&404", + "&405", + "&406", + "&407", + "&408", + "&409", + "&410", + "&411", + "&412", + "&413", + "&414", + "&415", + "&416", + "&417", + "&418", + "&419", + "&420", + "&421", + "&422", + "&423", + "&424", + "&425", + "&426", + "&427", + "&428", + "&429", + "&430", + "&431", + "&432", + "&433", + "&434", + "&435", + "&436", + "&437", + "&438", + "&439", + "&440", + "&441", + "&442", + "&443", + "&444", + "&445", + "&446", + "&447", + "&448", + "&449", + "&450", + "&451", + "&452", + "&453", + "&454", + "&455", + "&456", + "&457", + "&458", + "&459", + "&460", + "&461", + "&462", + "&463", + "&464", + "&465", + "&466", + "&467", + "&468", + "&469", + "&470", + "&471", + "&472", + "&473", + "&474", + "&475", + "&476", + "&477", + "&478", + "&479", + "&480", + "&481", + "&482", + "&483", + "&484", + "&485", + "&486", + "&487", + "&488", + "&489", + "&490", + "&491", + "&492", + "&493", + "&494", + "&495", + "&496", + "&497", + "&498", + "&499", + "&500", + "&501", + "&502", + "&503", + "&504", + "&505", + "&506", + "&507", + "&508", + "&509", + "&510", + "&511", +}; + +const char *inp_key_name(int k) { if (k >= 0 && k < 512) return key_strings[k]; else return key_strings[0]; } +int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, "-?-")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; } + diff --git a/src/engine/e_linereader.c b/src/engine/e_linereader.c deleted file mode 100644 index 57ba9a85..00000000 --- a/src/engine/e_linereader.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "e_linereader.h" - -void linereader_init(LINEREADER *lr, IOHANDLE io) -{ - lr->buffer_max_size = 4*1024; - lr->buffer_size = 0; - lr->buffer_pos = 0; - lr->io = io; -} - -char *linereader_get(LINEREADER *lr) -{ - unsigned line_start = lr->buffer_pos; - - while(1) - { - if(lr->buffer_pos >= lr->buffer_size) - { - /* fetch more */ - - /* move the remaining part to the front */ - unsigned read; - unsigned left = lr->buffer_size - line_start; - - if(line_start > lr->buffer_size) - left = 0; - if(left) - mem_move(lr->buffer, &lr->buffer[line_start], left); - lr->buffer_pos = left; - - /* fill the buffer */ - read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos); - lr->buffer_size = left + read; - line_start = 0; - - if(!read) - { - if(left) - { - lr->buffer[left] = 0; /* return the last line */ - lr->buffer_pos = left; - lr->buffer_size = left; - return lr->buffer; - } - else - return 0x0; /* we are done! */ - } - } - else - { - if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r') - { - /* line found */ - lr->buffer[lr->buffer_pos] = 0; - lr->buffer_pos++; - return &lr->buffer[line_start]; - } - else - lr->buffer_pos++; - } - } -} diff --git a/src/engine/e_linereader.cpp b/src/engine/e_linereader.cpp new file mode 100644 index 00000000..57ba9a85 --- /dev/null +++ b/src/engine/e_linereader.cpp @@ -0,0 +1,62 @@ +#include "e_linereader.h" + +void linereader_init(LINEREADER *lr, IOHANDLE io) +{ + lr->buffer_max_size = 4*1024; + lr->buffer_size = 0; + lr->buffer_pos = 0; + lr->io = io; +} + +char *linereader_get(LINEREADER *lr) +{ + unsigned line_start = lr->buffer_pos; + + while(1) + { + if(lr->buffer_pos >= lr->buffer_size) + { + /* fetch more */ + + /* move the remaining part to the front */ + unsigned read; + unsigned left = lr->buffer_size - line_start; + + if(line_start > lr->buffer_size) + left = 0; + if(left) + mem_move(lr->buffer, &lr->buffer[line_start], left); + lr->buffer_pos = left; + + /* fill the buffer */ + read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos); + lr->buffer_size = left + read; + line_start = 0; + + if(!read) + { + if(left) + { + lr->buffer[left] = 0; /* return the last line */ + lr->buffer_pos = left; + lr->buffer_size = left; + return lr->buffer; + } + else + return 0x0; /* we are done! */ + } + } + else + { + if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r') + { + /* line found */ + lr->buffer[lr->buffer_pos] = 0; + lr->buffer_pos++; + return &lr->buffer[line_start]; + } + else + lr->buffer_pos++; + } + } +} diff --git a/src/engine/e_map.c b/src/engine/e_map.c deleted file mode 100644 index a2048310..00000000 --- a/src/engine/e_map.c +++ /dev/null @@ -1,66 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "e_datafile.h" - -static DATAFILE *map = 0; - -void *map_get_data(int index) -{ - return datafile_get_data(map, index); -} - -void *map_get_data_swapped(int index) -{ - return datafile_get_data_swapped(map, index); -} - -void map_unload_data(int index) -{ - datafile_unload_data(map, index); -} - -void *map_get_item(int index, int *type, int *id) -{ - return datafile_get_item(map, index, type, id); -} - -void map_get_type(int type, int *start, int *num) -{ - datafile_get_type(map, type, start, num); -} - -void *map_find_item(int type, int id) -{ - return datafile_find_item(map, type, id); -} - -int map_num_items() -{ - return datafile_num_items(map); -} - -void map_unload() -{ - datafile_unload(map); - map = 0x0; -} - -int map_is_loaded() -{ - return map != 0; -} - -int map_load(const char *mapname) -{ - char buf[512]; - str_format(buf, sizeof(buf), "maps/%s.map", mapname); - map = datafile_load(buf); - return map != 0; -} - -void map_set(void *m) -{ - if(map) - map_unload(); - map = (DATAFILE*)m; -} diff --git a/src/engine/e_map.cpp b/src/engine/e_map.cpp new file mode 100644 index 00000000..a2048310 --- /dev/null +++ b/src/engine/e_map.cpp @@ -0,0 +1,66 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "e_datafile.h" + +static DATAFILE *map = 0; + +void *map_get_data(int index) +{ + return datafile_get_data(map, index); +} + +void *map_get_data_swapped(int index) +{ + return datafile_get_data_swapped(map, index); +} + +void map_unload_data(int index) +{ + datafile_unload_data(map, index); +} + +void *map_get_item(int index, int *type, int *id) +{ + return datafile_get_item(map, index, type, id); +} + +void map_get_type(int type, int *start, int *num) +{ + datafile_get_type(map, type, start, num); +} + +void *map_find_item(int type, int id) +{ + return datafile_find_item(map, type, id); +} + +int map_num_items() +{ + return datafile_num_items(map); +} + +void map_unload() +{ + datafile_unload(map); + map = 0x0; +} + +int map_is_loaded() +{ + return map != 0; +} + +int map_load(const char *mapname) +{ + char buf[512]; + str_format(buf, sizeof(buf), "maps/%s.map", mapname); + map = datafile_load(buf); + return map != 0; +} + +void map_set(void *m) +{ + if(map) + map_unload(); + map = (DATAFILE*)m; +} diff --git a/src/engine/e_memheap.c b/src/engine/e_memheap.c deleted file mode 100644 index 7589e2c1..00000000 --- a/src/engine/e_memheap.c +++ /dev/null @@ -1,102 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include - -typedef struct CHUNK_t -{ - char *memory; - char *current; - char *end; - struct CHUNK_t *next; -} CHUNK; - -typedef struct -{ - CHUNK *current; -} HEAP; - -/* how large each chunk should be */ -static const int chunksize = 1024*64; - -/* allocates a new chunk to be used */ -static CHUNK *memheap_newchunk() -{ - CHUNK *chunk; - char *mem; - - /* allocate memory */ - mem = (char*)mem_alloc(sizeof(CHUNK)+chunksize, 1); - if(!mem) - return 0x0; - - /* the chunk structure is located in the begining of the chunk */ - /* init it and return the chunk */ - chunk = (CHUNK*)mem; - chunk->memory = (char*)(chunk+1); - chunk->current = chunk->memory; - chunk->end = chunk->memory + chunksize; - chunk->next = (CHUNK *)0x0; - return chunk; -} - -/******************/ -static void *memheap_allocate_from_chunk(CHUNK *chunk, unsigned int size) -{ - char *mem; - - /* check if we need can fit the allocation */ - if(chunk->current + size > chunk->end) - return (void*)0x0; - - /* get memory and move the pointer forward */ - mem = chunk->current; - chunk->current += size; - return mem; -} - -/* creates a heap */ -HEAP *memheap_create() -{ - CHUNK *chunk; - HEAP *heap; - - /* allocate a chunk and allocate the heap structure on that chunk */ - chunk = memheap_newchunk(); - heap = (HEAP *)memheap_allocate_from_chunk(chunk, sizeof(HEAP)); - heap->current = chunk; - return heap; -} - -/* destroys the heap */ -void memheap_destroy(HEAP *heap) -{ - CHUNK *chunk = heap->current; - CHUNK *next; - - while(chunk) - { - next = chunk->next; - mem_free(chunk); - chunk = next; - } -} - -/* */ -void *memheap_allocate(HEAP *heap, unsigned int size) -{ - char *mem; - - /* try to allocate from current chunk */ - mem = (char *)memheap_allocate_from_chunk(heap->current, size); - if(!mem) - { - /* allocate new chunk and add it to the heap */ - CHUNK *chunk = memheap_newchunk(); - chunk->next = heap->current; - heap->current = chunk; - - /* try to allocate again */ - mem = (char *)memheap_allocate_from_chunk(heap->current, size); - } - - return mem; -} diff --git a/src/engine/e_memheap.cpp b/src/engine/e_memheap.cpp new file mode 100644 index 00000000..fe157e86 --- /dev/null +++ b/src/engine/e_memheap.cpp @@ -0,0 +1,102 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include + +struct CHUNK +{ + char *memory; + char *current; + char *end; + CHUNK *next; +}; + +struct HEAP +{ + CHUNK *current; +}; + +/* how large each chunk should be */ +static const int chunksize = 1024*64; + +/* allocates a new chunk to be used */ +static CHUNK *memheap_newchunk() +{ + CHUNK *chunk; + char *mem; + + /* allocate memory */ + mem = (char*)mem_alloc(sizeof(CHUNK)+chunksize, 1); + if(!mem) + return 0x0; + + /* the chunk structure is located in the begining of the chunk */ + /* init it and return the chunk */ + chunk = (CHUNK*)mem; + chunk->memory = (char*)(chunk+1); + chunk->current = chunk->memory; + chunk->end = chunk->memory + chunksize; + chunk->next = (CHUNK *)0x0; + return chunk; +} + +/******************/ +static void *memheap_allocate_from_chunk(CHUNK *chunk, unsigned int size) +{ + char *mem; + + /* check if we need can fit the allocation */ + if(chunk->current + size > chunk->end) + return (void*)0x0; + + /* get memory and move the pointer forward */ + mem = chunk->current; + chunk->current += size; + return mem; +} + +/* creates a heap */ +HEAP *memheap_create() +{ + CHUNK *chunk; + HEAP *heap; + + /* allocate a chunk and allocate the heap structure on that chunk */ + chunk = memheap_newchunk(); + heap = (HEAP *)memheap_allocate_from_chunk(chunk, sizeof(HEAP)); + heap->current = chunk; + return heap; +} + +/* destroys the heap */ +void memheap_destroy(HEAP *heap) +{ + CHUNK *chunk = heap->current; + CHUNK *next; + + while(chunk) + { + next = chunk->next; + mem_free(chunk); + chunk = next; + } +} + +/* */ +void *memheap_allocate(HEAP *heap, unsigned int size) +{ + char *mem; + + /* try to allocate from current chunk */ + mem = (char *)memheap_allocate_from_chunk(heap->current, size); + if(!mem) + { + /* allocate new chunk and add it to the heap */ + CHUNK *chunk = memheap_newchunk(); + chunk->next = heap->current; + heap->current = chunk; + + /* try to allocate again */ + mem = (char *)memheap_allocate_from_chunk(heap->current, size); + } + + return mem; +} diff --git a/src/engine/e_memheap.h b/src/engine/e_memheap.h index af3c0b29..b4391ec7 100644 --- a/src/engine/e_memheap.h +++ b/src/engine/e_memheap.h @@ -1,6 +1,6 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -typedef struct HEAP_t HEAP; +struct HEAP; HEAP *memheap_create(); void memheap_destroy(HEAP *heap); void *memheap_allocate(HEAP *heap, unsigned int size); diff --git a/src/engine/e_msg.c b/src/engine/e_msg.c deleted file mode 100644 index f9efc2bf..00000000 --- a/src/engine/e_msg.c +++ /dev/null @@ -1,70 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "e_common_interface.h" -#include "e_packer.h" - -/* message packing */ -static PACKER msg_packer; -static MSG_INFO pack_info; -static int packer_failed = 0; - -void msg_pack_int(int i) { packer_add_int(&msg_packer, i); } -void msg_pack_string(const char *p, int limit) { packer_add_string(&msg_packer, p, limit); } -void msg_pack_raw(const void *data, int size) { packer_add_raw(&msg_packer, (const unsigned char *)data, size); } - -void msg_pack_start_system(int msg, int flags) -{ - packer_reset(&msg_packer); - pack_info.msg = (msg<<1)|1; - pack_info.flags = flags; - packer_failed = 0; - - msg_pack_int(pack_info.msg); -} - -void msg_pack_start(int msg, int flags) -{ - packer_reset(&msg_packer); - pack_info.msg = msg<<1; - pack_info.flags = flags; - packer_failed = 0; - - msg_pack_int(pack_info.msg); -} - -void msg_pack_end() -{ - if(msg_packer.error) - { - packer_failed = 1; - pack_info.size = 0; - pack_info.data = (unsigned char *)""; - } - else - { - pack_info.size = packer_size(&msg_packer); - pack_info.data = packer_data(&msg_packer); - } -} - -const MSG_INFO *msg_get_info() -{ - if(packer_failed) - return 0; - return &pack_info; -} - -/* message unpacking */ -static UNPACKER msg_unpacker; -int msg_unpack_start(const void *data, int data_size, int *system) -{ - int msg; - unpacker_reset(&msg_unpacker, (const unsigned char *)data, data_size); - msg = msg_unpack_int(); - *system = msg&1; - return msg>>1; -} - -int msg_unpack_int() { return unpacker_get_int(&msg_unpacker); } -const char *msg_unpack_string() { return unpacker_get_string(&msg_unpacker); } -const unsigned char *msg_unpack_raw(int size) { return unpacker_get_raw(&msg_unpacker, size); } -int msg_unpack_error() { return msg_unpacker.error; } diff --git a/src/engine/e_msg.cpp b/src/engine/e_msg.cpp new file mode 100644 index 00000000..999a0ff0 --- /dev/null +++ b/src/engine/e_msg.cpp @@ -0,0 +1,70 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_common_interface.h" +#include "e_packer.h" + +/* message packing */ +static CPacker msg_packer; +static MSG_INFO pack_info; +static int packer_failed = 0; + +void msg_pack_int(int i) { msg_packer.AddInt(i); } +void msg_pack_string(const char *p, int limit) { msg_packer.AddString(p, limit); } +void msg_pack_raw(const void *data, int size) { msg_packer.AddRaw((const unsigned char *)data, size); } + +void msg_pack_start_system(int msg, int flags) +{ + msg_packer.Reset(); + pack_info.msg = (msg<<1)|1; + pack_info.flags = flags; + packer_failed = 0; + + msg_pack_int(pack_info.msg); +} + +void msg_pack_start(int msg, int flags) +{ + msg_packer.Reset(); + pack_info.msg = msg<<1; + pack_info.flags = flags; + packer_failed = 0; + + msg_pack_int(pack_info.msg); +} + +void msg_pack_end() +{ + if(msg_packer.Error()) + { + packer_failed = 1; + pack_info.size = 0; + pack_info.data = (unsigned char *)""; + } + else + { + pack_info.size = msg_packer.Size(); + pack_info.data = msg_packer.Data(); + } +} + +const MSG_INFO *msg_get_info() +{ + if(packer_failed) + return 0; + return &pack_info; +} + +/* message unpacking */ +static CUnpacker msg_unpacker; +int msg_unpack_start(const void *data, int data_size, int *system) +{ + int msg; + msg_unpacker.Reset((const unsigned char *)data, data_size); + msg = msg_unpack_int(); + *system = msg&1; + return msg>>1; +} + +int msg_unpack_int() { return msg_unpacker.GetInt(); } +const char *msg_unpack_string() { return msg_unpacker.GetString(); } +const unsigned char *msg_unpack_raw(int size) { return msg_unpacker.GetRaw(size); } +int msg_unpack_error() { return msg_unpacker.Error(); } diff --git a/src/engine/e_network.c b/src/engine/e_network.c deleted file mode 100644 index 3c32de1d..00000000 --- a/src/engine/e_network.c +++ /dev/null @@ -1,351 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include - -#include /* strlen */ - -#include "e_config.h" -#include "e_engine.h" -#include "e_network.h" -#include "e_network_internal.h" -#include "e_huffman.h" - -void recvinfo_clear(NETRECVINFO *info) -{ - info->valid = 0; -} - -void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid) -{ - info->addr = *addr; - info->conn = conn; - info->client_id = cid; - info->current_chunk = 0; - info->valid = 1; -} - - -int seq_in_backroom(int seq, int ack) -{ - int bottom = (ack-NET_MAX_SEQUENCE/2); - if(bottom < 0) - { - if(seq <= ack) - return 1; - if(seq >= (bottom + NET_MAX_SEQUENCE)) - return 1; - } - else - { - if(seq <= ack && seq >= bottom) - return 1; - } - - return 0; -} - -/* TODO: rename this function */ -int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk) -{ - NETCHUNKHEADER header; - unsigned char *end = info->data.chunk_data + info->data.data_size; - int i; - - while(1) - { - unsigned char *data = info->data.chunk_data; - - /* check for old data to unpack */ - if(!info->valid || info->current_chunk >= info->data.num_chunks) - { - recvinfo_clear(info); - return 0; - } - - /* TODO: add checking here so we don't read too far */ - for(i = 0; i < info->current_chunk; i++) - { - data = unpack_chunk_header(data, &header); - data += header.size; - } - - /* unpack the header */ - data = unpack_chunk_header(data, &header); - info->current_chunk++; - - if(data+header.size > end) - { - recvinfo_clear(info); - return 0; - } - - /* handle sequence stuff */ - if(info->conn && (header.flags&NET_CHUNKFLAG_VITAL)) - { - if(header.sequence == (info->conn->ack+1)%NET_MAX_SEQUENCE) - { - /* in sequence */ - info->conn->ack = (info->conn->ack+1)%NET_MAX_SEQUENCE; - } - else - { - /* old packet that we already got */ - if(seq_in_backroom(header.sequence, info->conn->ack)) - continue; - - /* out of sequence, request resend */ - if(config.debug) - dbg_msg("conn", "asking for resend %d %d", header.sequence, (info->conn->ack+1)%NET_MAX_SEQUENCE); - conn_want_resend(info->conn); - continue; /* take the next chunk in the packet */ - } - } - - /* fill in the info */ - chunk->client_id = info->client_id; - chunk->address = info->addr; - chunk->flags = 0; - chunk->data_size = header.size; - chunk->data = data; - return 1; - } -} - - -static IOHANDLE datalog_sent = 0; -static IOHANDLE datalog_recv = 0; -static HUFFMAN_STATE huffmanstate; - -#define COMPRESSION 1 - -/* packs the data tight and sends it */ -void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size) -{ - unsigned char buffer[NET_MAX_PACKETSIZE]; - buffer[0] = 0xff; - buffer[1] = 0xff; - buffer[2] = 0xff; - buffer[3] = 0xff; - buffer[4] = 0xff; - buffer[5] = 0xff; - mem_copy(&buffer[6], data, data_size); - net_udp_send(socket, addr, buffer, 6+data_size); -} - -int netcommon_compress(const void *data, int data_size, void *output, int output_size) -{ - return huffman_compress(&huffmanstate, data, data_size, output, output_size); -} - -int netcommon_decompress(const void *data, int data_size, void *output, int output_size) -{ - return huffman_decompress(&huffmanstate, data, data_size, output, output_size); -} - -void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet) -{ - unsigned char buffer[NET_MAX_PACKETSIZE]; - int compressed_size = -1; - int final_size = -1; - - /* log the data */ - if(datalog_sent) - { - int type = 1; - io_write(datalog_sent, &type, sizeof(type)); - io_write(datalog_sent, &packet->data_size, sizeof(packet->data_size)); - io_write(datalog_sent, &packet->chunk_data, packet->data_size); - io_flush(datalog_sent); - } - - /* compress if its enabled */ - if(COMPRESSION) - compressed_size = huffman_compress(&huffmanstate, packet->chunk_data, packet->data_size, &buffer[3], NET_MAX_PACKETSIZE-4); - - /* check if the compression was enabled, successful and good enough */ - if(compressed_size > 0 && compressed_size < packet->data_size) - { - final_size = compressed_size; - packet->flags |= NET_PACKETFLAG_COMPRESSION; - } - else - { - /* use uncompressed data */ - final_size = packet->data_size; - mem_copy(&buffer[3], packet->chunk_data, packet->data_size); - packet->flags &= ~NET_PACKETFLAG_COMPRESSION; - } - - /* set header and send the packet if all things are good */ - if(final_size >= 0) - { - final_size += NET_PACKETHEADERSIZE; - buffer[0] = ((packet->flags<<4)&0xf0)|((packet->ack>>8)&0xf); - buffer[1] = packet->ack&0xff; - buffer[2] = packet->num_chunks; - net_udp_send(socket, addr, buffer, final_size); - - /* log raw socket data */ - if(datalog_sent) - { - int type = 0; - io_write(datalog_sent, &type, sizeof(type)); - io_write(datalog_sent, &final_size, sizeof(final_size)); - io_write(datalog_sent, buffer, final_size); - io_flush(datalog_sent); - } - } -} - -/* TODO: rename this function */ -int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet) -{ - /* check the size */ - if(size < NET_PACKETHEADERSIZE || size > NET_MAX_PACKETSIZE) - { - dbg_msg("", "packet too small, %d", size); - return -1; - } - - /* log the data */ - if(datalog_recv) - { - int type = 0; - io_write(datalog_recv, &type, sizeof(type)); - io_write(datalog_recv, &size, sizeof(size)); - io_write(datalog_recv, buffer, size); - io_flush(datalog_recv); - } - - /* read the packet */ - packet->flags = buffer[0]>>4; - packet->ack = ((buffer[0]&0xf)<<8) | buffer[1]; - packet->num_chunks = buffer[2]; - packet->data_size = size - NET_PACKETHEADERSIZE; - - if(packet->flags&NET_PACKETFLAG_CONNLESS) - { - if(size < 6) - { - dbg_msg("", "connection less packet too small, %d", size); - return -1; - } - - packet->flags = NET_PACKETFLAG_CONNLESS; - packet->ack = 0; - packet->num_chunks = 0; - packet->data_size = size - 6; - mem_copy(packet->chunk_data, &buffer[6], packet->data_size); - } - else - { - if(packet->flags&NET_PACKETFLAG_COMPRESSION) - packet->data_size = huffman_decompress(&huffmanstate, &buffer[3], packet->data_size, packet->chunk_data, sizeof(packet->chunk_data)); - else - mem_copy(packet->chunk_data, &buffer[3], packet->data_size); - } - - /* check for errors */ - if(packet->data_size < 0) - { - if(config.debug) - dbg_msg("network", "error during packet decoding"); - return -1; - } - - /* log the data */ - if(datalog_recv) - { - int type = 1; - io_write(datalog_recv, &type, sizeof(type)); - io_write(datalog_recv, &packet->data_size, sizeof(packet->data_size)); - io_write(datalog_recv, packet->chunk_data, packet->data_size); - io_flush(datalog_recv); - } - - /* return success */ - return 0; -} - - -/* TODO: change the arguments of this function */ -unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence) -{ - data[0] = ((flags&3)<<6)|((size>>4)&0x3f); - data[1] = (size&0xf); - if(flags&NET_CHUNKFLAG_VITAL) - { - data[1] |= (sequence>>2)&0xf0; - data[2] = sequence&0xff; - return data + 3; - } - return data + 2; -} - -unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header) -{ - header->flags = (data[0]>>6)&3; - header->size = ((data[0]&0x3f)<<4) | (data[1]&0xf); - header->sequence = -1; - if(header->flags&NET_CHUNKFLAG_VITAL) - { - header->sequence = ((data[1]&0xf0)<<2) | data[2]; - return data + 3; - } - return data + 2; -} - - -void send_controlmsg(NETSOCKET socket, NETADDR *addr, int ack, int controlmsg, const void *extra, int extra_size) -{ - NETPACKETCONSTRUCT construct; - construct.flags = NET_PACKETFLAG_CONTROL; - construct.ack = ack; - construct.num_chunks = 0; - construct.data_size = 1+extra_size; - construct.chunk_data[0] = controlmsg; - mem_copy(&construct.chunk_data[1], extra, extra_size); - - /* send the control message */ - send_packet(socket, addr, &construct); -} - -void netcommon_openlog(const char *sentlog, const char *recvlog) -{ - if(sentlog) - { - datalog_sent = engine_openfile(sentlog, IOFLAG_WRITE); - if(datalog_sent) - dbg_msg("network", "logging sent packages to '%s'", sentlog); - else - dbg_msg("network", "failed to open for logging '%s'", sentlog); - } - - if(recvlog) - { - datalog_recv = engine_openfile(recvlog, IOFLAG_WRITE); - if(recvlog) - dbg_msg("network", "logging recv packages to '%s'", recvlog); - else - dbg_msg("network", "failed to open for logging '%s'", recvlog); - } -} - -static const unsigned freq_table[256+1] = { - 1<<30,4545,2657,431,1950,919,444,482,2244,617,838,542,715,1814,304,240,754,212,647,186, - 283,131,146,166,543,164,167,136,179,859,363,113,157,154,204,108,137,180,202,176, - 872,404,168,134,151,111,113,109,120,126,129,100,41,20,16,22,18,18,17,19, - 16,37,13,21,362,166,99,78,95,88,81,70,83,284,91,187,77,68,52,68, - 59,66,61,638,71,157,50,46,69,43,11,24,13,19,10,12,12,20,14,9, - 20,20,10,10,15,15,12,12,7,19,15,14,13,18,35,19,17,14,8,5, - 15,17,9,15,14,18,8,10,2173,134,157,68,188,60,170,60,194,62,175,71, - 148,67,167,78,211,67,156,69,1674,90,174,53,147,89,181,51,174,63,163,80, - 167,94,128,122,223,153,218,77,200,110,190,73,174,69,145,66,277,143,141,60, - 136,53,180,57,142,57,158,61,166,112,152,92,26,22,21,28,20,26,30,21, - 32,27,20,17,23,21,30,22,22,21,27,25,17,27,23,18,39,26,15,21, - 12,18,18,27,20,18,15,19,11,17,33,12,18,15,19,18,16,26,17,18, - 9,10,25,22,22,17,20,16,6,16,15,20,14,18,24,335,1517}; - -void netcommon_init() -{ - huffman_init(&huffmanstate, freq_table); -} diff --git a/src/engine/e_network.cpp b/src/engine/e_network.cpp new file mode 100644 index 00000000..ac753e50 --- /dev/null +++ b/src/engine/e_network.cpp @@ -0,0 +1,347 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include + +#include /* strlen */ + +#include "e_config.h" +#include "e_engine.h" +#include "e_network.h" +#include "e_huffman.h" + +void CNetRecvUnpacker::Clear() +{ + m_Valid = false; +} + +void CNetRecvUnpacker::Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientID) +{ + m_Addr = *pAddr; + m_pConnection = pConnection; + m_ClientID = ClientID; + m_CurrentChunk = 0; + m_Valid = true; +} + +/* TODO: rename this function */ +int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk) +{ + CNetChunkHeader Header; + unsigned char *pEnd = m_Data.m_aChunkData + m_Data.m_DataSize; + + while(1) + { + unsigned char *pData = m_Data.m_aChunkData; + + /* check for old data to unpack */ + if(!m_Valid || m_CurrentChunk >= m_Data.m_NumChunks) + { + Clear(); + return 0; + } + + /* TODO: add checking here so we don't read too far */ + for(int i = 0; i < m_CurrentChunk; i++) + { + pData = Header.Unpack(pData); + pData += Header.m_Size; + } + + /* unpack the header */ + pData = Header.Unpack(pData); + m_CurrentChunk++; + + if(pData+Header.m_Size > pEnd) + { + Clear(); + return 0; + } + + /* handle sequence stuff */ + if(m_pConnection && (Header.m_Flags&NET_CHUNKFLAG_VITAL)) + { + if(Header.m_Sequence == (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE) + { + /* in sequence */ + m_pConnection->m_Ack = (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE; + } + else + { + /* old packet that we already got */ + if(CNetBase::IsSeqInBackroom(Header.m_Sequence, m_pConnection->m_Ack)) + continue; + + /* out of sequence, request resend */ + if(config.debug) + dbg_msg("conn", "asking for resend %d %d", Header.m_Sequence, (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE); + m_pConnection->SignalResend(); + continue; /* take the next chunk in the packet */ + } + } + + /* fill in the info */ + pChunk->m_ClientID = m_ClientID; + pChunk->m_Address = m_Addr; + pChunk->m_Flags = 0; + pChunk->m_DataSize = Header.m_Size; + pChunk->m_pData = pData; + return 1; + } +} + +/* packs the data tight and sends it */ +void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize) +{ + unsigned char aBuffer[NET_MAX_PACKETSIZE]; + aBuffer[0] = 0xff; + aBuffer[1] = 0xff; + aBuffer[2] = 0xff; + aBuffer[3] = 0xff; + aBuffer[4] = 0xff; + aBuffer[5] = 0xff; + mem_copy(&aBuffer[6], pData, DataSize); + net_udp_send(Socket, pAddr, aBuffer, 6+DataSize); +} + +void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket) +{ + unsigned char aBuffer[NET_MAX_PACKETSIZE]; + int CompressedSize = -1; + int FinalSize = -1; + + /* log the data */ + if(ms_DataLogSent) + { + int type = 1; + io_write(ms_DataLogSent, &type, sizeof(type)); + io_write(ms_DataLogSent, &pPacket->m_DataSize, sizeof(pPacket->m_DataSize)); + io_write(ms_DataLogSent, &pPacket->m_aChunkData, pPacket->m_DataSize); + io_flush(ms_DataLogSent); + } + + /* compress */ + CompressedSize = huffman_compress(&ms_HuffmanState, pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4); + + /* check if the compression was enabled, successful and good enough */ + if(CompressedSize > 0 && CompressedSize < pPacket->m_DataSize) + { + FinalSize = CompressedSize; + pPacket->m_Flags |= NET_PACKETFLAG_COMPRESSION; + } + else + { + /* use uncompressed data */ + FinalSize = pPacket->m_DataSize; + mem_copy(&aBuffer[3], pPacket->m_aChunkData, pPacket->m_DataSize); + pPacket->m_Flags &= ~NET_PACKETFLAG_COMPRESSION; + } + + /* set header and send the packet if all things are good */ + if(FinalSize >= 0) + { + FinalSize += NET_PACKETHEADERSIZE; + aBuffer[0] = ((pPacket->m_Flags<<4)&0xf0)|((pPacket->m_Ack>>8)&0xf); + aBuffer[1] = pPacket->m_Ack&0xff; + aBuffer[2] = pPacket->m_NumChunks; + net_udp_send(Socket, pAddr, aBuffer, FinalSize); + + /* log raw socket data */ + if(ms_DataLogSent) + { + int type = 0; + io_write(ms_DataLogSent, &type, sizeof(type)); + io_write(ms_DataLogSent, &FinalSize, sizeof(FinalSize)); + io_write(ms_DataLogSent, aBuffer, FinalSize); + io_flush(ms_DataLogSent); + } + } +} + +/* TODO: rename this function */ +int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket) +{ + /* check the size */ + if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE) + { + dbg_msg("", "packet too small, %d", Size); + return -1; + } + + /* log the data */ + if(ms_DataLogRecv) + { + int type = 0; + io_write(ms_DataLogRecv, &type, sizeof(type)); + io_write(ms_DataLogRecv, &Size, sizeof(Size)); + io_write(ms_DataLogRecv, pBuffer, Size); + io_flush(ms_DataLogRecv); + } + + /* read the packet */ + pPacket->m_Flags = pBuffer[0]>>4; + pPacket->m_Ack = ((pBuffer[0]&0xf)<<8) | pBuffer[1]; + pPacket->m_NumChunks = pBuffer[2]; + pPacket->m_DataSize = Size - NET_PACKETHEADERSIZE; + + if(pPacket->m_Flags&NET_PACKETFLAG_CONNLESS) + { + if(Size < 6) + { + dbg_msg("", "connection less packet too small, %d", Size); + return -1; + } + + pPacket->m_Flags = NET_PACKETFLAG_CONNLESS; + pPacket->m_Ack = 0; + pPacket->m_NumChunks = 0; + pPacket->m_DataSize = Size - 6; + mem_copy(pPacket->m_aChunkData, &pBuffer[6], pPacket->m_DataSize); + } + else + { + if(pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION) + pPacket->m_DataSize = huffman_decompress(&ms_HuffmanState, &pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData)); + else + mem_copy(pPacket->m_aChunkData, &pBuffer[3], pPacket->m_DataSize); + } + + /* check for errors */ + if(pPacket->m_DataSize < 0) + { + if(config.debug) + dbg_msg("network", "error during packet decoding"); + return -1; + } + + /* log the data */ + if(ms_DataLogRecv) + { + int type = 1; + io_write(ms_DataLogRecv, &type, sizeof(type)); + io_write(ms_DataLogRecv, &pPacket->m_DataSize, sizeof(pPacket->m_DataSize)); + io_write(ms_DataLogRecv, pPacket->m_aChunkData, pPacket->m_DataSize); + io_flush(ms_DataLogRecv); + } + + /* return success */ + return 0; +} + + +void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize) +{ + CNetPacketConstruct Construct; + Construct.m_Flags = NET_PACKETFLAG_CONTROL; + Construct.m_Ack = Ack; + Construct.m_NumChunks = 0; + Construct.m_DataSize = 1+ExtraSize; + Construct.m_aChunkData[0] = ControlMsg; + mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize); + + /* send the control message */ + CNetBase::SendPacket(Socket, pAddr, &Construct); +} + + + +unsigned char *CNetChunkHeader::Pack(unsigned char *pData) +{ + pData[0] = ((m_Flags&3)<<6)|((m_Size>>4)&0x3f); + pData[1] = (m_Size&0xf); + if(m_Flags&NET_CHUNKFLAG_VITAL) + { + pData[1] |= (m_Sequence>>2)&0xf0; + pData[2] = m_Sequence&0xff; + return pData + 3; + } + return pData + 2; +} + +unsigned char *CNetChunkHeader::Unpack(unsigned char *pData) +{ + m_Flags = (pData[0]>>6)&3; + m_Size = ((pData[0]&0x3f)<<4) | (pData[1]&0xf); + m_Sequence = -1; + if(m_Flags&NET_CHUNKFLAG_VITAL) + { + m_Sequence = ((pData[1]&0xf0)<<2) | pData[2]; + return pData + 3; + } + return pData + 2; +} + + +int CNetBase::IsSeqInBackroom(int Seq, int Ack) +{ + int Bottom = (Ack-NET_MAX_SEQUENCE/2); + if(Bottom < 0) + { + if(Seq <= Ack) + return 1; + if(Seq >= (Bottom + NET_MAX_SEQUENCE)) + return 1; + } + else + { + if(Seq <= Ack && Seq >= Bottom) + return 1; + } + + return 0; +} + +IOHANDLE CNetBase::ms_DataLogSent = 0; +IOHANDLE CNetBase::ms_DataLogRecv = 0; +HUFFMAN_STATE CNetBase::ms_HuffmanState; + + +void CNetBase::OpenLog(const char *pSentLog, const char *pRecvLog) +{ + if(pSentLog) + { + ms_DataLogSent = engine_openfile(pSentLog, IOFLAG_WRITE); + if(ms_DataLogSent) + dbg_msg("network", "logging sent packages to '%s'", pSentLog); + else + dbg_msg("network", "failed to open for logging '%s'", pSentLog); + } + + if(pRecvLog) + { + ms_DataLogRecv = engine_openfile(pRecvLog, IOFLAG_WRITE); + if(ms_DataLogRecv) + dbg_msg("network", "logging recv packages to '%s'", pRecvLog); + else + dbg_msg("network", "failed to open for logging '%s'", pRecvLog); + } +} + +int CNetBase::Compress(const void *pData, int DataSize, void *pOutput, int OutputSize) +{ + return huffman_compress(&ms_HuffmanState, pData, DataSize, pOutput, OutputSize); +} + +int CNetBase::Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize) +{ + return huffman_decompress(&ms_HuffmanState, pData, DataSize, pOutput, OutputSize); +} + + +static const unsigned gs_aFreqTable[256+1] = { + 1<<30,4545,2657,431,1950,919,444,482,2244,617,838,542,715,1814,304,240,754,212,647,186, + 283,131,146,166,543,164,167,136,179,859,363,113,157,154,204,108,137,180,202,176, + 872,404,168,134,151,111,113,109,120,126,129,100,41,20,16,22,18,18,17,19, + 16,37,13,21,362,166,99,78,95,88,81,70,83,284,91,187,77,68,52,68, + 59,66,61,638,71,157,50,46,69,43,11,24,13,19,10,12,12,20,14,9, + 20,20,10,10,15,15,12,12,7,19,15,14,13,18,35,19,17,14,8,5, + 15,17,9,15,14,18,8,10,2173,134,157,68,188,60,170,60,194,62,175,71, + 148,67,167,78,211,67,156,69,1674,90,174,53,147,89,181,51,174,63,163,80, + 167,94,128,122,223,153,218,77,200,110,190,73,174,69,145,66,277,143,141,60, + 136,53,180,57,142,57,158,61,166,112,152,92,26,22,21,28,20,26,30,21, + 32,27,20,17,23,21,30,22,22,21,27,25,17,27,23,18,39,26,15,21, + 12,18,18,27,20,18,15,19,11,17,33,12,18,15,19,18,16,26,17,18, + 9,10,25,22,22,17,20,16,6,16,15,20,14,18,24,335,1517}; + +void CNetBase::Init() +{ + huffman_init(&ms_HuffmanState, gs_aFreqTable); +} diff --git a/src/engine/e_network.h b/src/engine/e_network.h index 04453cf9..19eca8da 100644 --- a/src/engine/e_network.h +++ b/src/engine/e_network.h @@ -2,38 +2,26 @@ #define ENGINE_NETWORK_H /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_ringbuffer.h" +#include "e_huffman.h" -typedef struct -{ - /* -1 means that it's a stateless packet */ - /* 0 on the client means the server */ - int client_id; - NETADDR address; /* only used when client_id == -1 */ - int flags; - int data_size; - const void *data; -} NETCHUNK; - - -typedef struct -{ - NETADDR addr; - int expires; -} NETBANINFO; +/* -/*typedef struct -{ - int send_bytes; - int recv_bytes; - int send_packets; - int recv_packets; - - int resend_packets; - int resend_bytes; -} NETSTATS;*/ +CURRENT: + packet header: 3 bytes + unsigned char flags_ack; // 4bit flags, 4bit ack + unsigned char ack; // 8 bit ack + unsigned char num_chunks; // 8 bit chunks + + (unsigned char padding[3]) // 24 bit extra incase it's a connection less packet + // this is to make sure that it's compatible with the + // old protocol -typedef struct NETSERVER NETSERVER; -typedef struct NETCLIENT NETCLIENT; + chunk header: 2-3 bytes + unsigned char flags_size; // 2bit flags, 6 bit size + unsigned char size_seq; // 4bit size, 4bit seq + (unsigned char seq;) // 8bit seq, if vital flag is set +*/ enum { @@ -50,97 +38,310 @@ enum NETBANTYPE_DROP=2 }; + +enum +{ + NET_VERSION = 2, + + NET_MAX_CHUNKSIZE = 1024, + NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16, + NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16, + NET_MAX_CHUNKHEADERSIZE = 5, + NET_PACKETHEADERSIZE = 3, + NET_MAX_CLIENTS = 16, + NET_MAX_SEQUENCE = 1<<10, + NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1, + + NET_CONNSTATE_OFFLINE=0, + NET_CONNSTATE_CONNECT=1, + NET_CONNSTATE_PENDING=2, + NET_CONNSTATE_ONLINE=3, + NET_CONNSTATE_ERROR=4, + + NET_PACKETFLAG_CONTROL=1, + NET_PACKETFLAG_CONNLESS=2, + NET_PACKETFLAG_RESEND=4, + NET_PACKETFLAG_COMPRESSION=8, + + NET_CHUNKFLAG_VITAL=1, + NET_CHUNKFLAG_RESEND=2, + + NET_CTRLMSG_KEEPALIVE=0, + NET_CTRLMSG_CONNECT=1, + NET_CTRLMSG_CONNECTACCEPT=2, + NET_CTRLMSG_ACCEPT=3, + NET_CTRLMSG_CLOSE=4, + + NET_SERVER_MAXBANS=1024, + + NET_CONN_BUFFERSIZE=1024*16, + + NET_ENUM_TERMINATOR +}; + + typedef int (*NETFUNC_DELCLIENT)(int cid, void *user); typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user); -/* both */ -void netcommon_openlog(const char *sentlog, const char *recvlog); -void netcommon_init(); -int netcommon_compress(const void *data, int data_size, void *output, int output_size); -int netcommon_decompress(const void *data, int data_size, void *output, int output_size); +struct CNetChunk +{ + /* -1 means that it's a stateless packet */ + /* 0 on the client means the server */ + int m_ClientID; + NETADDR m_Address; /* only used when client_id == -1 */ + int m_Flags; + int m_DataSize; + const void *m_pData; +}; -/* server side */ -NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags); -int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user); -int netserver_recv(NETSERVER *s, NETCHUNK *chunk); -int netserver_send(NETSERVER *s, NETCHUNK *chunk); -int netserver_close(NETSERVER *s); -int netserver_update(NETSERVER *s); -NETSOCKET netserver_socket(NETSERVER *s); -int netserver_drop(NETSERVER *s, int client_id, const char *reason); -int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr); -int netserver_max_clients(NETSERVER *s); - -int netserver_ban_add(NETSERVER *s, NETADDR addr, int seconds); -int netserver_ban_remove(NETSERVER *s, NETADDR addr); -int netserver_ban_num(NETSERVER *s); /* caution, slow */ -int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info); /* caution, slow */ - -/*void netserver_stats(NETSERVER *s, NETSTATS *stats);*/ +class CNetChunkHeader +{ +public: + int m_Flags; + int m_Size; + int m_Sequence; + + unsigned char *Pack(unsigned char *pData); + unsigned char *Unpack(unsigned char *pData); +}; -/* client side */ -NETCLIENT *netclient_open(NETADDR bindaddr, int flags); -int netclient_disconnect(NETCLIENT *c, const char *reason); -int netclient_connect(NETCLIENT *c, NETADDR *addr); -int netclient_recv(NETCLIENT *c, NETCHUNK *chunk); -int netclient_send(NETCLIENT *c, NETCHUNK *chunk); -int netclient_close(NETCLIENT *c); -int netclient_update(NETCLIENT *c); -int netclient_state(NETCLIENT *c); -int netclient_flush(NETCLIENT *c); -int netclient_gotproblems(NETCLIENT *c); -/*void netclient_stats(NETCLIENT *c, NETSTATS *stats);*/ -int netclient_error_string_reset(NETCLIENT *c); -const char *netclient_error_string(NETCLIENT *c); - -#ifdef __cplusplus -class net_server +class CNetChunkResend +{ +public: + int m_Flags; + int m_DataSize; + unsigned char *m_pData; + + int m_Sequence; + int64 m_LastSendTime; + int64 m_FirstSendTime; +}; + +class CNetPacketConstruct { - NETSERVER *ptr; public: - net_server() : ptr(0) {} - ~net_server() { close(); } + int m_Flags; + int m_Ack; + int m_NumChunks; + int m_DataSize; + unsigned char m_aChunkData[NET_MAX_PAYLOAD]; +}; + + +class CNetConnection +{ + // TODO: is this needed because this needs to be aware of + // the ack sequencing number and is also responible for updating + // that. this should be fixed. + friend class CNetRecvUnpacker; +private: + unsigned short m_Sequence; + unsigned short m_Ack; + unsigned m_State; + + int m_Token; + int m_RemoteClosed; + + TStaticRingBuffer m_Buffer; + + int64 m_LastUpdateTime; + int64 m_LastRecvTime; + int64 m_LastSendTime; + + char m_ErrorString[256]; - int open(NETADDR bindaddr, int max, int flags) { ptr = netserver_open(bindaddr, max, flags); return ptr != 0; } - int close() { int r = netserver_close(ptr); ptr = 0; return r; } + CNetPacketConstruct m_Construct; - int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) - { return netserver_set_callbacks(ptr, new_client, del_client, user); } + NETADDR m_PeerAddr; + NETSOCKET m_Socket; + NETSTATS m_Stats; - int recv(NETCHUNK *chunk) { return netserver_recv(ptr, chunk); } - int send(NETCHUNK *chunk) { return netserver_send(ptr, chunk); } - int update() { return netserver_update(ptr); } + // + void Reset(); + void ResetStats(); + void SetError(const char *pString); + void AckChunks(int Ack); - int drop(int client_id, const char *reason) { return netserver_drop(ptr, client_id, reason); } + void QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence); + void SendControl(int ControlMsg, const void *pExtra, int ExtraSize); + void ResendChunk(CNetChunkResend *pResend); + void Resend(); - int max_clients() { return netserver_max_clients(ptr); } - /*void stats(NETSTATS *stats) { netserver_stats(ptr, stats); }*/ +public: + void Init(NETSOCKET Socket); + int Connect(NETADDR *pAddr); + void Disconnect(const char *pReason); + + int Update(); + int Flush(); + + int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr); + void QueueChunk(int Flags, int DataSize, const void *pData); + + const char *ErrorString(); + void SignalResend(); + int State() const { return m_State; } + NETADDR PeerAddress() const { return m_PeerAddr; } + + void ResetErrorString() { m_ErrorString[0] = 0; } + const char *ErrorString() const { return m_ErrorString; } + + // Needed for GotProblems in NetClient + int64 LastRecvTime() const { return m_LastRecvTime; } + + int AckSequence() const { return m_Ack; } +}; + +struct CNetRecvUnpacker +{ +public: + bool m_Valid; + + NETADDR m_Addr; + CNetConnection *m_pConnection; + int m_CurrentChunk; + int m_ClientID; + CNetPacketConstruct m_Data; + unsigned char m_aBuffer[NET_MAX_PACKETSIZE]; + + CNetRecvUnpacker() { Clear(); } + void Clear(); + void Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientID); + int FetchChunk(CNetChunk *pChunk); +}; + +/* server side */ +class CNetServer +{ +public: + struct CBanInfo + { + NETADDR m_Addr; + int m_Expires; + }; + +private: + class CSlot + { + public: + CNetConnection m_Connection; + }; + + class CBan + { + public: + CBanInfo m_Info; + + /* hash list */ + CBan *m_pHashNext; + CBan *m_pHashPrev; + + /* used or free list */ + CBan *m_pNext; + CBan *m_pPrev; + }; + + + NETSOCKET m_Socket; + CSlot m_aSlots[NET_MAX_CLIENTS]; + int m_MaxClients; + + CBan *m_aBans[256]; + CBan m_BanPool[NET_SERVER_MAXBANS]; + CBan *m_BanPool_FirstFree; + CBan *m_BanPool_FirstUsed; + + NETFUNC_NEWCLIENT m_pfnNewClient; + NETFUNC_DELCLIENT m_pfnDelClient; + void *m_UserPtr; + + CNetRecvUnpacker m_RecvUnpacker; + + void BanRemoveByObject(CBan *ban); + +public: + int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); + + // + bool Open(NETADDR bindaddr, int MaxClients, int Flags); + int Close(); + + // + int Recv(CNetChunk *pChunk); + int Send(CNetChunk *pChunk); + int Update(); + + // + int Drop(int ClientID, const char *Reason); + + // banning + int BanAdd(NETADDR Addr, int Seconds); + int BanRemove(NETADDR Addr); + int BanNum(); /* caution, slow */ + int BanGet(int Index, CBanInfo *pInfo); /* caution, slow */ + + // status requests + NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } + NETSOCKET Socket() const { return m_Socket; } + int MaxClients() const { return m_MaxClients; } }; -class net_client + +/* client side */ +class CNetClient { - NETCLIENT *ptr; + NETADDR m_ServerAddr; + CNetConnection m_Connection; + CNetRecvUnpacker m_RecvUnpacker; + NETSOCKET m_Socket; public: - net_client() : ptr(0) {} - ~net_client() { close(); } + // openness + bool Open(NETADDR BindAddr, int Flags); + int Close(); - int open(NETADDR bindaddr, int flags) { ptr = netclient_open(bindaddr, flags); return ptr != 0; } - int close() { int r = netclient_close(ptr); ptr = 0; return r; } + // connection state + int Disconnect(const char *Reason); + int Connect(NETADDR *Addr); - int connect(NETADDR *addr) { return netclient_connect(ptr, addr); } - int disconnect(const char *reason) { return netclient_disconnect(ptr, reason); } + // communication + int Recv(CNetChunk *Chunk); + int Send(CNetChunk *Chunk); - int recv(NETCHUNK *chunk) { return netclient_recv(ptr, chunk); } - int send(NETCHUNK *chunk) { return netclient_send(ptr, chunk); } - int update() { return netclient_update(ptr); } + // pumping + int Update(); + int Flush(); + + int ResetErrorString(); - const char *error_string() { return netclient_error_string(ptr); } + // error and state + int State(); + int GotProblems(); + const char *ErrorString(); +}; + + + +// TODO: both, fix these. This feels like a junk class for stuff that doesn't fit anywere +class CNetBase +{ + static IOHANDLE ms_DataLogSent; + static IOHANDLE ms_DataLogRecv; + static HUFFMAN_STATE ms_HuffmanState; +public: + static void OpenLog(const char *pSentlog, const char *pRecvlog); + static void Init(); + static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize); + static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize); - int state() { return netclient_state(ptr); } - /*void stats(NETSTATS *stats) { netclient_stats(ptr, stats); }*/ + static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize); + static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize); + static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket); + static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket); + + /* The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not */ + static int IsSeqInBackroom(int Seq, int Ack); }; -#endif #endif diff --git a/src/engine/e_network_client.c b/src/engine/e_network_client.c deleted file mode 100644 index 75f7c538..00000000 --- a/src/engine/e_network_client.c +++ /dev/null @@ -1,154 +0,0 @@ -#include -#include "e_network.h" -#include "e_network_internal.h" - -struct NETCLIENT -{ - NETADDR server_addr; - NETSOCKET socket; - - NETRECVINFO recv; - NETCONNECTION conn; -}; - -NETCLIENT *netclient_open(NETADDR bindaddr, int flags) -{ - NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1); - mem_zero(client, sizeof(NETCLIENT)); - client->socket = net_udp_create(bindaddr); - conn_init(&client->conn, client->socket); - return client; -} - -int netclient_close(NETCLIENT *c) -{ - /* TODO: implement me */ - return 0; -} - - -int netclient_disconnect(NETCLIENT *c, const char *reason) -{ - dbg_msg("netclient", "disconnected. reason=\"%s\"", reason); - conn_disconnect(&c->conn, reason); - return 0; -} - -int netclient_update(NETCLIENT *c) -{ - conn_update(&c->conn); - if(c->conn.state == NET_CONNSTATE_ERROR) - netclient_disconnect(c, conn_error(&c->conn)); - return 0; -} - -int netclient_connect(NETCLIENT *c, NETADDR *addr) -{ - conn_connect(&c->conn, addr); - return 0; -} - -int netclient_error_string_reset(NETCLIENT *c) -{ - mem_zero(c->conn.error_string, sizeof(c->conn.error_string)); - return 0; -} - -int netclient_recv(NETCLIENT *c, NETCHUNK *chunk) -{ - while(1) - { - NETADDR addr; - int bytes; - - /* check for a chunk */ - if(recvinfo_fetch_chunk(&c->recv, chunk)) - return 1; - - /* TODO: empty the recvinfo */ - bytes = net_udp_recv(c->socket, &addr, c->recv.buffer, NET_MAX_PACKETSIZE); - - /* no more packets for now */ - if(bytes <= 0) - break; - - if(unpack_packet(c->recv.buffer, bytes, &c->recv.data) == 0) - { - if(c->recv.data.flags&NET_PACKETFLAG_CONNLESS) - { - chunk->flags = NETSENDFLAG_CONNLESS; - chunk->client_id = -1; - chunk->address = addr; - chunk->data_size = c->recv.data.data_size; - chunk->data = c->recv.data.chunk_data; - return 1; - } - else - { - if(conn_feed(&c->conn, &c->recv.data, &addr)) - recvinfo_start(&c->recv, &addr, &c->conn, 0); - } - } - } - return 0; -} - -int netclient_send(NETCLIENT *c, NETCHUNK *chunk) -{ - if(chunk->data_size >= NET_MAX_PAYLOAD) - { - dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", chunk->data_size); - return -1; - } - - if(chunk->flags&NETSENDFLAG_CONNLESS) - { - /* send connectionless packet */ - send_packet_connless(c->socket, &chunk->address, chunk->data, chunk->data_size); - } - else - { - int f = 0; - dbg_assert(chunk->client_id == 0, "errornous client id"); - - if(chunk->flags&NETSENDFLAG_VITAL) - f = NET_CHUNKFLAG_VITAL; - - conn_queue_chunk(&c->conn, f, chunk->data_size, chunk->data); - - if(chunk->flags&NETSENDFLAG_FLUSH) - conn_flush(&c->conn); - } - return 0; -} - -int netclient_state(NETCLIENT *c) -{ - if(c->conn.state == NET_CONNSTATE_ONLINE) - return NETSTATE_ONLINE; - if(c->conn.state == NET_CONNSTATE_OFFLINE) - return NETSTATE_OFFLINE; - return NETSTATE_CONNECTING; -} - -int netclient_flush(NETCLIENT *c) -{ - return conn_flush(&c->conn); -} - -int netclient_gotproblems(NETCLIENT *c) -{ - if(time_get() - c->conn.last_recv_time > time_freq()) - return 1; - return 0; -} - -void netclient_stats(NETCLIENT *c, NETSTATS *stats) -{ - *stats = c->conn.stats; -} - -const char *netclient_error_string(NETCLIENT *c) -{ - return conn_error(&c->conn); -} diff --git a/src/engine/e_network_client.cpp b/src/engine/e_network_client.cpp new file mode 100644 index 00000000..ce243b32 --- /dev/null +++ b/src/engine/e_network_client.cpp @@ -0,0 +1,139 @@ +#include +#include "e_network.h" + +bool CNetClient::Open(NETADDR BindAddr, int Flags) +{ + // clean it + mem_zero(this, sizeof(*this)); + + // open socket + m_Socket = net_udp_create(BindAddr); + m_Connection.Init(m_Socket); + return true; +} + +int CNetClient::Close() +{ + /* TODO: implement me */ + return 0; +} + + +int CNetClient::Disconnect(const char *pReason) +{ + dbg_msg("netclient", "disconnected. reason=\"%s\"", pReason); + m_Connection.Disconnect(pReason); + return 0; +} + +int CNetClient::Update() +{ + m_Connection.Update(); + if(m_Connection.State() == NET_CONNSTATE_ERROR) + Disconnect(m_Connection.ErrorString()); + return 0; +} + +int CNetClient::Connect(NETADDR *pAddr) +{ + m_Connection.Connect(pAddr); + return 0; +} + +int CNetClient::ResetErrorString() +{ + m_Connection.ResetErrorString(); + return 0; +} + +int CNetClient::Recv(CNetChunk *pChunk) +{ + while(1) + { + /* check for a chunk */ + if(m_RecvUnpacker.FetchChunk(pChunk)) + return 1; + + /* TODO: empty the recvinfo */ + NETADDR Addr; + int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); + + /* no more packets for now */ + if(Bytes <= 0) + break; + + if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) + { + if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) + { + pChunk->m_Flags = NETSENDFLAG_CONNLESS; + pChunk->m_ClientID = -1; + pChunk->m_Address = Addr; + pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; + pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; + return 1; + } + else + { + if(m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) + m_RecvUnpacker.Start(&Addr, &m_Connection, 0); + } + } + } + return 0; +} + +int CNetClient::Send(CNetChunk *pChunk) +{ + if(pChunk->m_DataSize >= NET_MAX_PAYLOAD) + { + dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", pChunk->m_DataSize); + return -1; + } + + if(pChunk->m_Flags&NETSENDFLAG_CONNLESS) + { + /* send connectionless packet */ + CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize); + } + else + { + int Flags = 0; + dbg_assert(pChunk->m_ClientID == 0, "errornous client id"); + + if(pChunk->m_Flags&NETSENDFLAG_VITAL) + Flags = NET_CHUNKFLAG_VITAL; + + m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData); + + if(pChunk->m_Flags&NETSENDFLAG_FLUSH) + m_Connection.Flush(); + } + return 0; +} + +int CNetClient::State() +{ + if(m_Connection.State() == NET_CONNSTATE_ONLINE) + return NETSTATE_ONLINE; + if(m_Connection.State() == NET_CONNSTATE_OFFLINE) + return NETSTATE_OFFLINE; + return NETSTATE_CONNECTING; +} + +int CNetClient::Flush() +{ + return m_Connection.Flush(); +} + +int CNetClient::GotProblems() +{ + if(time_get() - m_Connection.LastRecvTime() > time_freq()) + return 1; + return 0; +} + +const char *CNetClient::ErrorString() +{ + return m_Connection.ErrorString(); +} diff --git a/src/engine/e_network_conn.c b/src/engine/e_network_conn.c deleted file mode 100644 index 583dbe65..00000000 --- a/src/engine/e_network_conn.c +++ /dev/null @@ -1,366 +0,0 @@ -#include -#include -#include "e_config.h" -#include "e_network_internal.h" - -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->remote_closed = 0; - - conn->state = NET_CONNSTATE_OFFLINE; - conn->last_send_time = 0; - conn->last_recv_time = 0; - conn->last_update_time = 0; - conn->token = -1; - mem_zero(&conn->peeraddr, sizeof(conn->peeraddr)); - - conn->buffer = ringbuf_init(conn->buffer_memory, sizeof(conn->buffer_memory), 0); - - mem_zero(&conn->construct, sizeof(conn->construct)); -} - - -const char *conn_error(NETCONNECTION *conn) -{ - return conn->error_string; -} - -static void conn_set_error(NETCONNECTION *conn, const char *str) -{ - str_copy(conn->error_string, str, sizeof(conn->error_string)); -} - -void conn_init(NETCONNECTION *conn, NETSOCKET socket) -{ - conn_reset(conn); - conn_reset_stats(conn); - conn->socket = socket; - mem_zero(conn->error_string, sizeof(conn->error_string)); -} - - -static void conn_ack(NETCONNECTION *conn, int ack) -{ - - while(1) - { - NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer); - if(!resend) - break; - - if(seq_in_backroom(resend->sequence, ack)) - ringbuf_popfirst(conn->buffer); - else - break; - } -} - -void conn_want_resend(NETCONNECTION *conn) -{ - conn->construct.flags |= NET_PACKETFLAG_RESEND; -} - -int conn_flush(NETCONNECTION *conn) -{ - int num_chunks = conn->construct.num_chunks; - if(!num_chunks && !conn->construct.flags) - return 0; - - /* send of the packets */ - conn->construct.ack = conn->ack; - send_packet(conn->socket, &conn->peeraddr, &conn->construct); - - /* update send times */ - conn->last_send_time = time_get(); - - /* clear construct so we can start building a new package */ - mem_zero(&conn->construct, sizeof(conn->construct)); - return num_chunks; -} - -static void conn_queue_chunk_ex(NETCONNECTION *conn, int flags, int data_size, const void *data, int sequence) -{ - unsigned char *chunk_data; - - /* check if we have space for it, if not, flush the connection */ - if(conn->construct.data_size + data_size + NET_MAX_CHUNKHEADERSIZE > sizeof(conn->construct.chunk_data)) - conn_flush(conn); - - /* pack all the data */ - chunk_data = &conn->construct.chunk_data[conn->construct.data_size]; - chunk_data = pack_chunk_header(chunk_data, flags, data_size, sequence); - mem_copy(chunk_data, data, data_size); - chunk_data += data_size; - - /* */ - conn->construct.num_chunks++; - conn->construct.data_size = (int)(chunk_data-conn->construct.chunk_data); - - /* set packet flags aswell */ - - if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND)) - { - /* save packet if we need to resend */ - NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_allocate(conn->buffer, sizeof(NETCHUNKDATA)+data_size); - if(resend) - { - resend->sequence = sequence; - resend->flags = flags; - resend->data_size = data_size; - resend->data = (unsigned char *)(resend+1); - resend->first_send_time = time_get(); - resend->last_send_time = resend->first_send_time; - mem_copy(resend->data, data, data_size); - } - else - { - /* out of buffer */ - conn_disconnect(conn, "too weak connection (out of buffer)"); - } - } -} - -void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data) -{ - if(flags&NET_CHUNKFLAG_VITAL) - conn->seq = (conn->seq+1)%NET_MAX_SEQUENCE; - conn_queue_chunk_ex(conn, flags, data_size, data, conn->seq); -} - - -static void conn_send_control(NETCONNECTION *conn, int controlmsg, const void *extra, int extra_size) -{ - /* send the control message */ - conn->last_send_time = time_get(); - send_controlmsg(conn->socket, &conn->peeraddr, conn->ack, controlmsg, extra, extra_size); -} - -static void conn_resend_chunk(NETCONNECTION *conn, NETCHUNKDATA *resend) -{ - conn_queue_chunk_ex(conn, resend->flags|NET_CHUNKFLAG_RESEND, resend->data_size, resend->data, resend->sequence); - resend->last_send_time = time_get(); -} - -static void conn_resend(NETCONNECTION *conn) -{ - int resend_count = 0; - int first = 0, last = 0; - void *item = ringbuf_first(conn->buffer); - - while(item) - { - NETCHUNKDATA *resend = item; - - if(resend_count == 0) - first = resend->sequence; - last = resend->sequence; - - conn_resend_chunk(conn, resend); - item = ringbuf_next(conn->buffer, item); - resend_count++; - } - - if(config.debug) - dbg_msg("conn", "resent %d packets (%d to %d)", resend_count, first, last); -} - -int conn_connect(NETCONNECTION *conn, NETADDR *addr) -{ - if(conn->state != NET_CONNSTATE_OFFLINE) - return -1; - - /* init connection */ - conn_reset(conn); - conn->peeraddr = *addr; - mem_zero(conn->error_string, sizeof(conn->error_string)); - conn->state = NET_CONNSTATE_CONNECT; - conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0); - return 0; -} - -void conn_disconnect(NETCONNECTION *conn, const char *reason) -{ - if(conn->state == NET_CONNSTATE_OFFLINE) - return; - - if(conn->remote_closed == 0) - { - if(reason) - conn_send_control(conn, NET_CTRLMSG_CLOSE, reason, strlen(reason)+1); - else - conn_send_control(conn, NET_CTRLMSG_CLOSE, 0, 0); - - conn->error_string[0] = 0; - if(reason) - str_copy(conn->error_string, reason, sizeof(conn->error_string)); - } - - conn_reset(conn); -} - -int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr) -{ - int64 now = time_get(); - conn->last_recv_time = now; - - /* check if resend is requested */ - if(packet->flags&NET_PACKETFLAG_RESEND) - conn_resend(conn); - - /* */ - if(packet->flags&NET_PACKETFLAG_CONTROL) - { - int ctrlmsg = packet->chunk_data[0]; - - if(ctrlmsg == NET_CTRLMSG_CLOSE) - { - conn->state = NET_CONNSTATE_ERROR; - conn->remote_closed = 1; - - if(packet->data_size) - { - /* make sure to sanitize the error string form the other party*/ - char str[128]; - if(packet->data_size < 128) - str_copy(str, (char *)packet->chunk_data, packet->data_size); - else - str_copy(str, (char *)packet->chunk_data, 128); - str_sanitize_strong(str); - - /* set the error string */ - conn_set_error(conn, str); - } - else - conn_set_error(conn, "no reason given"); - - if(config.debug) - dbg_msg("conn", "closed reason='%s'", conn_error(conn)); - return 0; - } - else - { - if(conn->state == NET_CONNSTATE_OFFLINE) - { - if(ctrlmsg == NET_CTRLMSG_CONNECT) - { - /* send response and init connection */ - conn_reset(conn); - conn->state = NET_CONNSTATE_PENDING; - conn->peeraddr = *addr; - conn->last_send_time = now; - conn->last_recv_time = now; - conn->last_update_time = now; - conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0); - if(config.debug) - dbg_msg("connection", "got connection, sending connect+accept"); - } - } - else if(conn->state == NET_CONNSTATE_CONNECT) - { - /* connection made */ - if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) - { - conn_send_control(conn, NET_CTRLMSG_ACCEPT, 0, 0); - conn->state = NET_CONNSTATE_ONLINE; - if(config.debug) - dbg_msg("connection", "got connect+accept, sending accept. connection online"); - } - } - else if(conn->state == NET_CONNSTATE_ONLINE) - { - /* connection made */ - /* - if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) - { - - }*/ - } - } - } - else - { - if(conn->state == NET_CONNSTATE_PENDING) - { - conn->state = NET_CONNSTATE_ONLINE; - if(config.debug) - dbg_msg("connection", "connecting online"); - } - } - - if(conn->state == NET_CONNSTATE_ONLINE) - { - - conn_ack(conn, packet->ack); - } - - return 1; -} - -int conn_update(NETCONNECTION *conn) -{ - int64 now = time_get(); - - if(conn->state == NET_CONNSTATE_OFFLINE || conn->state == NET_CONNSTATE_ERROR) - return 0; - - /* check for timeout */ - if(conn->state != NET_CONNSTATE_OFFLINE && - conn->state != NET_CONNSTATE_CONNECT && - (now-conn->last_recv_time) > time_freq()*10) - { - conn->state = NET_CONNSTATE_ERROR; - conn_set_error(conn, "timeout"); - } - - /* fix resends */ - if(ringbuf_first(conn->buffer)) - { - NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer); - - /* check if we have some really old stuff laying around and abort if not acked */ - if(now-resend->first_send_time > time_freq()*10) - { - conn->state = NET_CONNSTATE_ERROR; - conn_set_error(conn, "too weak connection (not acked for 10 seconds)"); - } - else - { - /* resend packet if we havn't got it acked in 1 second */ - if(now-resend->last_send_time > time_freq()) - conn_resend_chunk(conn, resend); - } - } - - /* send keep alives if nothing has happend for 250ms */ - if(conn->state == NET_CONNSTATE_ONLINE) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* flush connection after 500ms if needed */ - { - int num_flushed_chunks = conn_flush(conn); - if(num_flushed_chunks && config.debug) - dbg_msg("connection", "flushed connection due to timeout. %d chunks.", num_flushed_chunks); - } - - if(time_get()-conn->last_send_time > time_freq()) - conn_send_control(conn, NET_CTRLMSG_KEEPALIVE, 0, 0); - } - else if(conn->state == NET_CONNSTATE_CONNECT) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */ - conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0); - } - else if(conn->state == NET_CONNSTATE_PENDING) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */ - conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0); - } - - return 0; -} diff --git a/src/engine/e_network_conn.cpp b/src/engine/e_network_conn.cpp new file mode 100644 index 00000000..a54c9ce3 --- /dev/null +++ b/src/engine/e_network_conn.cpp @@ -0,0 +1,349 @@ +#include +#include +#include "e_config.h" +#include "e_network.h" + +void CNetConnection::ResetStats() +{ + mem_zero(&m_Stats, sizeof(m_Stats)); +} + +void CNetConnection::Reset() +{ + m_Sequence = 0; + m_Ack = 0; + m_RemoteClosed = 0; + + m_State = NET_CONNSTATE_OFFLINE; + m_LastSendTime = 0; + m_LastRecvTime = 0; + m_LastUpdateTime = 0; + m_Token = -1; + mem_zero(&m_PeerAddr, sizeof(m_PeerAddr)); + + m_Buffer.Init(); + + mem_zero(&m_Construct, sizeof(m_Construct)); +} + +const char *CNetConnection::ErrorString() +{ + return m_ErrorString; +} + +void CNetConnection::SetError(const char *pString) +{ + str_copy(m_ErrorString, pString, sizeof(m_ErrorString)); +} + +void CNetConnection::Init(NETSOCKET Socket) +{ + Reset(); + ResetStats(); + + m_Socket = Socket; + mem_zero(m_ErrorString, sizeof(m_ErrorString)); +} + +void CNetConnection::AckChunks(int Ack) +{ + while(1) + { + CNetChunkResend *pResend = m_Buffer.First(); + if(!pResend) + break; + + if(CNetBase::IsSeqInBackroom(pResend->m_Sequence, Ack)) + m_Buffer.PopFirst(); + else + break; + } +} + +void CNetConnection::SignalResend() +{ + m_Construct.m_Flags |= NET_PACKETFLAG_RESEND; +} + +int CNetConnection::Flush() +{ + int NumChunks = m_Construct.m_NumChunks; + if(!NumChunks && !m_Construct.m_Flags) + return 0; + + /* send of the packets */ + m_Construct.m_Ack = m_Ack; + CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct); + + /* update send times */ + m_LastSendTime = time_get(); + + /* clear construct so we can start building a new package */ + mem_zero(&m_Construct, sizeof(m_Construct)); + return NumChunks; +} + +void CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence) +{ + unsigned char *pChunkData; + + /* check if we have space for it, if not, flush the connection */ + if(m_Construct.m_DataSize + DataSize + NET_MAX_CHUNKHEADERSIZE > (int)sizeof(m_Construct.m_aChunkData)) + Flush(); + + /* pack all the data */ + CNetChunkHeader Header; + Header.m_Flags = Flags; + Header.m_Size = DataSize; + Header.m_Sequence = Sequence; + pChunkData = &m_Construct.m_aChunkData[m_Construct.m_DataSize]; + pChunkData = Header.Pack(pChunkData); + mem_copy(pChunkData, pData, DataSize); + pChunkData += DataSize; + + /* */ + m_Construct.m_NumChunks++; + m_Construct.m_DataSize = (int)(pChunkData-m_Construct.m_aChunkData); + + /* set packet flags aswell */ + + if(Flags&NET_CHUNKFLAG_VITAL && !(Flags&NET_CHUNKFLAG_RESEND)) + { + /* save packet if we need to resend */ + CNetChunkResend *pResend = m_Buffer.Allocate(sizeof(CNetChunkResend)+DataSize); + if(pResend) + { + pResend->m_Sequence = Sequence; + pResend->m_Flags = Flags; + pResend->m_DataSize = DataSize; + pResend->m_pData = (unsigned char *)(pResend+1); + pResend->m_FirstSendTime = time_get(); + pResend->m_LastSendTime = pResend->m_FirstSendTime; + mem_copy(pResend->m_pData, pData, DataSize); + } + else + { + /* out of buffer */ + Disconnect("too weak connection (out of buffer)"); + } + } +} + +void CNetConnection::QueueChunk(int Flags, int DataSize, const void *pData) +{ + if(Flags&NET_CHUNKFLAG_VITAL) + m_Sequence = (m_Sequence+1)%NET_MAX_SEQUENCE; + QueueChunkEx(Flags, DataSize, pData, m_Sequence); +} + +void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSize) +{ + /* send the control message */ + m_LastSendTime = time_get(); + CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize); +} + +void CNetConnection::ResendChunk(CNetChunkResend *pResend) +{ + QueueChunkEx(pResend->m_Flags|NET_CHUNKFLAG_RESEND, pResend->m_DataSize, pResend->m_pData, pResend->m_Sequence); + pResend->m_LastSendTime = time_get(); +} + +void CNetConnection::Resend() +{ + for(CNetChunkResend *pResend = m_Buffer.First(); pResend; m_Buffer.Next(pResend)) + ResendChunk(pResend); +} + +int CNetConnection::Connect(NETADDR *pAddr) +{ + if(State() != NET_CONNSTATE_OFFLINE) + return -1; + + /* init connection */ + Reset(); + m_PeerAddr = *pAddr; + mem_zero(m_ErrorString, sizeof(m_ErrorString)); + m_State = NET_CONNSTATE_CONNECT; + SendControl(NET_CTRLMSG_CONNECT, 0, 0); + return 0; +} + +void CNetConnection::Disconnect(const char *pReason) +{ + if(State() == NET_CONNSTATE_OFFLINE) + return; + + if(m_RemoteClosed == 0) + { + if(pReason) + SendControl(NET_CTRLMSG_CLOSE, pReason, strlen(pReason)+1); + else + SendControl(NET_CTRLMSG_CLOSE, 0, 0); + + m_ErrorString[0] = 0; + if(pReason) + str_copy(m_ErrorString, pReason, sizeof(m_ErrorString)); + } + + Reset(); +} + +int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) +{ + int64 now = time_get(); + m_LastRecvTime = now; + + /* check if resend is requested */ + if(pPacket->m_Flags&NET_PACKETFLAG_RESEND) + Resend(); + + /* */ + if(pPacket->m_Flags&NET_PACKETFLAG_CONTROL) + { + int CtrlMsg = pPacket->m_aChunkData[0]; + + if(CtrlMsg == NET_CTRLMSG_CLOSE) + { + m_State = NET_CONNSTATE_ERROR; + m_RemoteClosed = 1; + + if(pPacket->m_DataSize) + { + /* make sure to sanitize the error string form the other party*/ + char Str[128]; + if(pPacket->m_DataSize < 128) + str_copy(Str, (char *)pPacket->m_aChunkData, pPacket->m_DataSize); + else + str_copy(Str, (char *)pPacket->m_aChunkData, sizeof(Str)); + str_sanitize_strong(Str); + + /* set the error string */ + SetError(Str); + } + else + SetError("no reason given"); + + if(config.debug) + dbg_msg("conn", "closed reason='%s'", ErrorString()); + return 0; + } + else + { + if(State() == NET_CONNSTATE_OFFLINE) + { + if(CtrlMsg == NET_CTRLMSG_CONNECT) + { + /* send response and init connection */ + Reset(); + m_State = NET_CONNSTATE_PENDING; + m_PeerAddr = *pAddr; + m_LastSendTime = now; + m_LastRecvTime = now; + m_LastUpdateTime = now; + SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); + if(config.debug) + dbg_msg("connection", "got connection, sending connect+accept"); + } + } + else if(State() == NET_CONNSTATE_CONNECT) + { + /* connection made */ + if(CtrlMsg == NET_CTRLMSG_CONNECTACCEPT) + { + SendControl(NET_CTRLMSG_ACCEPT, 0, 0); + m_State = NET_CONNSTATE_ONLINE; + if(config.debug) + dbg_msg("connection", "got connect+accept, sending accept. connection online"); + } + } + else if(State() == NET_CONNSTATE_ONLINE) + { + /* connection made */ + /* + if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) + { + + }*/ + } + } + } + else + { + if(State() == NET_CONNSTATE_PENDING) + { + m_State = NET_CONNSTATE_ONLINE; + if(config.debug) + dbg_msg("connection", "connecting online"); + } + } + + if(State() == NET_CONNSTATE_ONLINE) + { + AckChunks(pPacket->m_Ack); + } + + return 1; +} + +int CNetConnection::Update() +{ + int64 now = time_get(); + + if(State() == NET_CONNSTATE_OFFLINE || State() == NET_CONNSTATE_ERROR) + return 0; + + /* check for timeout */ + if(State() != NET_CONNSTATE_OFFLINE && + State() != NET_CONNSTATE_CONNECT && + (now-m_LastRecvTime) > time_freq()*10) + { + m_State = NET_CONNSTATE_ERROR; + SetError("timeout"); + } + + /* fix resends */ + if(m_Buffer.First()) + { + CNetChunkResend *pResend = m_Buffer.First(); + + /* check if we have some really old stuff laying around and abort if not acked */ + if(now-pResend->m_FirstSendTime > time_freq()*10) + { + m_State = NET_CONNSTATE_ERROR; + SetError("too weak connection (not acked for 10 seconds)"); + } + else + { + /* resend packet if we havn't got it acked in 1 second */ + if(now-pResend->m_LastSendTime > time_freq()) + ResendChunk(pResend); + } + } + + /* send keep alives if nothing has happend for 250ms */ + if(State() == NET_CONNSTATE_ONLINE) + { + if(time_get()-m_LastSendTime > time_freq()/2) /* flush connection after 500ms if needed */ + { + int NumFlushedChunks = Flush(); + if(NumFlushedChunks && config.debug) + dbg_msg("connection", "flushed connection due to timeout. %d chunks.", NumFlushedChunks); + } + + if(time_get()-m_LastSendTime > time_freq()) + SendControl(NET_CTRLMSG_KEEPALIVE, 0, 0); + } + else if(State() == NET_CONNSTATE_CONNECT) + { + if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect every 500ms */ + SendControl(NET_CTRLMSG_CONNECT, 0, 0); + } + else if(State() == NET_CONNSTATE_PENDING) + { + if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect/accept every 500ms */ + SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); + } + + return 0; +} diff --git a/src/engine/e_network_internal.h b/src/engine/e_network_internal.h deleted file mode 100644 index f1a25b31..00000000 --- a/src/engine/e_network_internal.h +++ /dev/null @@ -1,156 +0,0 @@ -#include -#include "e_network.h" -#include "e_ringbuffer.h" - -/* - -CURRENT: - packet header: 3 bytes - unsigned char flags_ack; // 4bit flags, 4bit ack - unsigned char ack; // 8 bit ack - unsigned char num_chunks; // 8 bit chunks - - (unsigned char padding[3]) // 24 bit extra incase it's a connection less packet - // this is to make sure that it's compatible with the - // old protocol - - chunk header: 2-3 bytes - unsigned char flags_size; // 2bit flags, 6 bit size - unsigned char size_seq; // 4bit size, 4bit seq - (unsigned char seq;) // 8bit seq, if vital flag is set - - -*/ - -enum -{ - NET_VERSION = 2, - - NET_MAX_CHUNKSIZE = 1024, - NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16, - NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16, - NET_MAX_CHUNKHEADERSIZE = 5, - NET_PACKETHEADERSIZE = 3, - NET_MAX_CLIENTS = 16, - NET_MAX_SEQUENCE = 1<<10, - NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1, - - NET_CONNSTATE_OFFLINE=0, - NET_CONNSTATE_CONNECT=1, - NET_CONNSTATE_PENDING=2, - NET_CONNSTATE_ONLINE=3, - NET_CONNSTATE_ERROR=4, - - NET_PACKETFLAG_CONTROL=1, - NET_PACKETFLAG_CONNLESS=2, - NET_PACKETFLAG_RESEND=4, - NET_PACKETFLAG_COMPRESSION=8, - - NET_CHUNKFLAG_VITAL=1, - NET_CHUNKFLAG_RESEND=2, - - NET_CTRLMSG_KEEPALIVE=0, - NET_CTRLMSG_CONNECT=1, - NET_CTRLMSG_CONNECTACCEPT=2, - NET_CTRLMSG_ACCEPT=3, - NET_CTRLMSG_CLOSE=4, - - NET_SERVER_MAXBANS=1024, - - NET_CONN_BUFFERSIZE=1024*16, - - NET_ENUM_TERMINATOR -}; - - -typedef struct NETPACKETCONSTRUCT -{ - int flags; - int ack; - int num_chunks; - int data_size; - unsigned char chunk_data[NET_MAX_PAYLOAD]; -} NETPACKETCONSTRUCT; - -typedef struct NETCHUNKHEADER -{ - int flags; - int size; - int sequence; -} NETCHUNKHEADER; - -typedef struct -{ - int flags; - int data_size; - unsigned char *data; - - int sequence; - int64 last_send_time; - int64 first_send_time; -} NETCHUNKDATA; - -typedef struct -{ - unsigned short seq; - unsigned short ack; - unsigned state; - - int token; - int remote_closed; - - RINGBUFFER *buffer; - - int64 last_update_time; - int64 last_recv_time; - int64 last_send_time; - - char error_string[256]; - - NETPACKETCONSTRUCT construct; - - NETADDR peeraddr; - NETSOCKET socket; - NETSTATS stats; - - char buffer_memory[NET_CONN_BUFFERSIZE]; -} NETCONNECTION; - -typedef struct NETRECVINFO -{ - NETADDR addr; - NETCONNECTION *conn; - int current_chunk; - int client_id; - int valid; - NETPACKETCONSTRUCT data; - unsigned char buffer[NET_MAX_PACKETSIZE]; -} NETRECVINFO; - -/* */ - -/* connection functions */ -void conn_init(NETCONNECTION *conn, NETSOCKET socket); -int conn_connect(NETCONNECTION *conn, NETADDR *addr); -void conn_disconnect(NETCONNECTION *conn, const char *reason); -int conn_update(NETCONNECTION *conn); -int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr); -void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data); -const char *conn_error(NETCONNECTION *conn); -void conn_want_resend(NETCONNECTION *conn); -int conn_flush(NETCONNECTION *conn); - -/* recvinfo functions */ -void recvinfo_clear(NETRECVINFO *info); -void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid); -int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk); - -/* misc helper functions */ -/* The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not */ -int seq_in_backroom(int seq, int ack); -void send_controlmsg(NETSOCKET socket, NETADDR *addr, int ack, int controlmsg, const void *extra, int extra_size); -void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size); -void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet); -int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet); -unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence); -unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header); diff --git a/src/engine/e_network_server.c b/src/engine/e_network_server.c deleted file mode 100644 index 8ec65504..00000000 --- a/src/engine/e_network_server.c +++ /dev/null @@ -1,484 +0,0 @@ -#include -#include "e_network.h" -#include "e_network_internal.h" - -typedef struct -{ - NETCONNECTION conn; -} NETSLOT; - -typedef struct NETBAN -{ - NETBANINFO info; - - /* hash list */ - struct NETBAN *hashnext; - struct NETBAN *hashprev; - - /* used or free list */ - struct NETBAN *next; - struct NETBAN *prev; -} NETBAN; - -#define MACRO_LIST_LINK_FIRST(object, first, prev, next) \ - { if(first) first->prev = object; \ - object->prev = (void*)0; \ - object->next = first; \ - first = object; } - -#define MACRO_LIST_LINK_AFTER(object, after, prev, next) \ - { object->prev = after; \ - object->next = after->next; \ - after->next = object; \ - if(object->next) \ - object->next->prev = object; \ - } - -#define MACRO_LIST_UNLINK(object, first, prev, next) \ - { if(object->next) object->next->prev = object->prev; \ - if(object->prev) object->prev->next = object->next; \ - else first = object->next; \ - object->next = 0; object->prev = 0; } - -#define MACRO_LIST_FIND(start, next, expression) \ - { while(start && !(expression)) start = start->next; } - -struct NETSERVER -{ - NETSOCKET socket; - NETSLOT slots[NET_MAX_CLIENTS]; - int max_clients; - - NETBAN *bans[256]; - NETBAN banpool[NET_SERVER_MAXBANS]; - NETBAN *banpool_firstfree; - NETBAN *banpool_firstused; - - NETFUNC_NEWCLIENT new_client; - NETFUNC_NEWCLIENT del_client; - void *user_ptr; - - NETRECVINFO recv; -}; - -NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags) -{ - int i; - NETSERVER *server; - NETSOCKET socket = net_udp_create(bindaddr); - if(socket == NETSOCKET_INVALID) - return 0; - - server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1); - mem_zero(server, sizeof(NETSERVER)); - server->socket = socket; - server->max_clients = max_clients; - if(server->max_clients > NET_MAX_CLIENTS) - server->max_clients = NET_MAX_CLIENTS; - if(server->max_clients < 1) - server->max_clients = 1; - - for(i = 0; i < NET_MAX_CLIENTS; i++) - conn_init(&server->slots[i].conn, server->socket); - - /* setup all pointers for bans */ - for(i = 1; i < NET_SERVER_MAXBANS-1; i++) - { - server->banpool[i].next = &server->banpool[i+1]; - server->banpool[i].prev = &server->banpool[i-1]; - } - - server->banpool[0].next = &server->banpool[1]; - server->banpool[NET_SERVER_MAXBANS-1].prev = &server->banpool[NET_SERVER_MAXBANS-2]; - server->banpool_firstfree = &server->banpool[0]; - - return server; -} - -int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) -{ - s->new_client = new_client; - s->del_client = del_client; - s->user_ptr = user; - return 0; -} - -int netserver_max_clients(NETSERVER *s) -{ - return s->max_clients; -} - -int netserver_close(NETSERVER *s) -{ - /* TODO: implement me */ - return 0; -} - -int netserver_drop(NETSERVER *s, int client_id, const char *reason) -{ - /* TODO: insert lots of checks here */ - NETADDR addr; - netserver_client_addr(s, client_id, &addr); - - dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"", - client_id, - addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], - reason - ); - conn_disconnect(&s->slots[client_id].conn, reason); - - if(s->del_client) - s->del_client(client_id, s->user_ptr); - - return 0; -} - -int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info) -{ - NETBAN *ban; - for(ban = s->banpool_firstused; ban && index; ban = ban->next, index--) - {} - - if(!ban) - return 0; - *info = ban->info; - return 1; -} - -int netserver_ban_num(NETSERVER *s) -{ - int count = 0; - NETBAN *ban; - for(ban = s->banpool_firstused; ban; ban = ban->next) - count++; - return count; -} - -static void netserver_ban_remove_by_object(NETSERVER *s, NETBAN *ban) -{ - int iphash = (ban->info.addr.ip[0]+ban->info.addr.ip[1]+ban->info.addr.ip[2]+ban->info.addr.ip[3])&0xff; - dbg_msg("netserver", "removing ban on %d.%d.%d.%d", - ban->info.addr.ip[0], ban->info.addr.ip[1], ban->info.addr.ip[2], ban->info.addr.ip[3]); - MACRO_LIST_UNLINK(ban, s->banpool_firstused, prev, next); - MACRO_LIST_UNLINK(ban, s->bans[iphash], hashprev, hashnext); - MACRO_LIST_LINK_FIRST(ban, s->banpool_firstfree, prev, next); -} - -int netserver_ban_remove(NETSERVER *s, NETADDR addr) -{ - int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; - NETBAN *ban = s->bans[iphash]; - - MACRO_LIST_FIND(ban, hashnext, net_addr_comp(&ban->info.addr, &addr) == 0); - - if(ban) - { - netserver_ban_remove_by_object(s, ban); - return 0; - } - - return -1; -} - -int netserver_ban_add(NETSERVER *s, NETADDR addr, int seconds) -{ - int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; - unsigned stamp = 0xffffffff; - NETBAN *ban; - - /* remove the port */ - addr.port = 0; - - if(seconds) - stamp = time_timestamp() + seconds; - - /* search to see if it already exists */ - ban = s->bans[iphash]; - MACRO_LIST_FIND(ban, hashnext, net_addr_comp(&ban->info.addr, &addr) == 0); - if(ban) - { - /* adjust the ban */ - ban->info.expires = stamp; - return 0; - } - - if(!s->banpool_firstfree) - return -1; - - /* fetch and clear the new ban */ - ban = s->banpool_firstfree; - MACRO_LIST_UNLINK(ban, s->banpool_firstfree, prev, next); - - /* setup the ban info */ - ban->info.expires = stamp; - ban->info.addr = addr; - - /* add it to the ban hash */ - MACRO_LIST_LINK_FIRST(ban, s->bans[iphash], hashprev, hashnext); - - /* insert it into the used list */ - { - if(s->banpool_firstused) - { - NETBAN *insert_after = s->banpool_firstused; - MACRO_LIST_FIND(insert_after, next, stamp < insert_after->info.expires); - - if(insert_after) - insert_after = insert_after->prev; - else - { - /* add to last */ - insert_after = s->banpool_firstused; - while(insert_after->next) - insert_after = insert_after->next; - } - - if(insert_after) - { - MACRO_LIST_LINK_AFTER(ban, insert_after, prev, next); - } - else - { - MACRO_LIST_LINK_FIRST(ban, s->banpool_firstused, prev, next); - } - } - else - { - MACRO_LIST_LINK_FIRST(ban, s->banpool_firstused, prev, next); - } - } - - /* drop banned clients */ - { - char buf[128]; - int i; - NETADDR banaddr; - - if(seconds) - str_format(buf, sizeof(buf), "you have been banned for %d minutes", seconds/60); - else - str_format(buf, sizeof(buf), "you have been banned for life"); - - for(i = 0; i < s->max_clients; i++) - { - banaddr = s->slots[i].conn.peeraddr; - banaddr.port = 0; - - if(net_addr_comp(&addr, &banaddr) == 0) - netserver_drop(s, i, buf); - } - } - return 0; -} - -int netserver_update(NETSERVER *s) -{ - unsigned now = time_timestamp(); - - int i; - for(i = 0; i < s->max_clients; i++) - { - conn_update(&s->slots[i].conn); - if(s->slots[i].conn.state == NET_CONNSTATE_ERROR) - netserver_drop(s, i, conn_error(&s->slots[i].conn)); - } - - /* remove expired bans */ - while(s->banpool_firstused && s->banpool_firstused->info.expires < now) - { - NETBAN *ban = s->banpool_firstused; - netserver_ban_remove_by_object(s, ban); - } - - (void)now; - - return 0; -} - -/* - TODO: chopp up this function into smaller working parts -*/ -int netserver_recv(NETSERVER *s, NETCHUNK *chunk) -{ - unsigned now = time_timestamp(); - - while(1) - { - NETADDR addr; - int i, bytes, found; - - /* check for a chunk */ - if(recvinfo_fetch_chunk(&s->recv, chunk)) - return 1; - - /* TODO: empty the recvinfo */ - bytes = net_udp_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE); - - /* no more packets for now */ - if(bytes <= 0) - break; - - if(unpack_packet(s->recv.buffer, bytes, &s->recv.data) == 0) - { - NETBAN *ban = 0; - NETADDR banaddr = addr; - int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; - found = 0; - banaddr.port = 0; - - /* search a ban */ - for(ban = s->bans[iphash]; ban; ban = ban->hashnext) - { - if(net_addr_comp(&ban->info.addr, &banaddr) == 0) - break; - } - - /* check if we just should drop the packet */ - if(ban) - { - // banned, reply with a message - char banstr[128]; - if(ban->info.expires) - { - int mins = ((ban->info.expires - now)+59)/60; - if(mins == 1) - str_format(banstr, sizeof(banstr), "banned for %d minute", mins); - else - str_format(banstr, sizeof(banstr), "banned for %d minutes", mins); - } - else - str_format(banstr, sizeof(banstr), "banned for life"); - send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, banstr, str_length(banstr)+1); - continue; - } - - if(s->recv.data.flags&NET_PACKETFLAG_CONNLESS) - { - chunk->flags = NETSENDFLAG_CONNLESS; - chunk->client_id = -1; - chunk->address = addr; - chunk->data_size = s->recv.data.data_size; - chunk->data = s->recv.data.chunk_data; - return 1; - } - else - { - /* TODO: check size here */ - if(s->recv.data.flags&NET_PACKETFLAG_CONTROL && s->recv.data.chunk_data[0] == NET_CTRLMSG_CONNECT) - { - found = 0; - - /* check if we already got this client */ - for(i = 0; i < s->max_clients; i++) - { - if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE && - net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) - { - found = 1; /* silent ignore.. we got this client already */ - break; - } - } - - /* client that wants to connect */ - if(!found) - { - for(i = 0; i < s->max_clients; i++) - { - if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE) - { - found = 1; - conn_feed(&s->slots[i].conn, &s->recv.data, &addr); - if(s->new_client) - s->new_client(i, s->user_ptr); - break; - } - } - - if(!found) - { - const char fullmsg[] = "server is full"; - send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, fullmsg, sizeof(fullmsg)); - } - } - } - else - { - /* normal packet, find matching slot */ - for(i = 0; i < s->max_clients; i++) - { - if(net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) - { - if(conn_feed(&s->slots[i].conn, &s->recv.data, &addr)) - { - if(s->recv.data.data_size) - recvinfo_start(&s->recv, &addr, &s->slots[i].conn, i); - } - } - } - } - } - } - } - return 0; -} - -int netserver_send(NETSERVER *s, NETCHUNK *chunk) -{ - if(chunk->data_size >= NET_MAX_PAYLOAD) - { - dbg_msg("netserver", "packet payload too big. %d. dropping packet", chunk->data_size); - return -1; - } - - if(chunk->flags&NETSENDFLAG_CONNLESS) - { - /* send connectionless packet */ - send_packet_connless(s->socket, &chunk->address, chunk->data, chunk->data_size); - } - else - { - int f = 0; - dbg_assert(chunk->client_id >= 0, "errornous client id"); - dbg_assert(chunk->client_id < s->max_clients, "errornous client id"); - - if(chunk->flags&NETSENDFLAG_VITAL) - f = NET_CHUNKFLAG_VITAL; - - conn_queue_chunk(&s->slots[chunk->client_id].conn, f, chunk->data_size, chunk->data); - - if(chunk->flags&NETSENDFLAG_FLUSH) - conn_flush(&s->slots[chunk->client_id].conn); - } - return 0; -} - -void netserver_stats(NETSERVER *s, NETSTATS *stats) -{ - int num_stats = sizeof(NETSTATS)/sizeof(int); - int *istats = (int *)stats; - int c, i; - - mem_zero(stats, sizeof(NETSTATS)); - - for(c = 0; c < s->max_clients; c++) - { - if(s->slots[c].conn.state != NET_CONNSTATE_OFFLINE) - { - int *sstats = (int *)(&(s->slots[c].conn.stats)); - for(i = 0; i < num_stats; i++) - istats[i] += sstats[i]; - } - } -} - -NETSOCKET netserver_socket(NETSERVER *s) -{ - return s->socket; -} - - -int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr) -{ - *addr = s->slots[client_id].conn.peeraddr; - return 1; -} diff --git a/src/engine/e_network_server.cpp b/src/engine/e_network_server.cpp new file mode 100644 index 00000000..995290ef --- /dev/null +++ b/src/engine/e_network_server.cpp @@ -0,0 +1,408 @@ +#include +#include "e_network.h" + +#define MACRO_LIST_LINK_FIRST(object, first, prev, next) \ + { if(first) first->prev = object; \ + object->prev = (struct CBan *)0; \ + object->next = first; \ + first = object; } + +#define MACRO_LIST_LINK_AFTER(object, after, prev, next) \ + { object->prev = after; \ + object->next = after->next; \ + after->next = object; \ + if(object->next) \ + object->next->prev = object; \ + } + +#define MACRO_LIST_UNLINK(object, first, prev, next) \ + { if(object->next) object->next->prev = object->prev; \ + if(object->prev) object->prev->next = object->next; \ + else first = object->next; \ + object->next = 0; object->prev = 0; } + +#define MACRO_LIST_FIND(start, next, expression) \ + { while(start && !(expression)) start = start->next; } + +bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int Flags) +{ + // zero out the whole structure + mem_zero(this, sizeof(*this)); + + // open socket + m_Socket = net_udp_create(BindAddr); + if(m_Socket == NETSOCKET_INVALID) + return false; + + // clamp clients + m_MaxClients = MaxClients; + if(m_MaxClients > NET_MAX_CLIENTS) + m_MaxClients = NET_MAX_CLIENTS; + if(m_MaxClients < 1) + m_MaxClients = 1; + + for(int i = 0; i < NET_MAX_CLIENTS; i++) + m_aSlots[i].m_Connection.Init(m_Socket); + + /* setup all pointers for bans */ + for(int i = 1; i < NET_SERVER_MAXBANS-1; i++) + { + m_BanPool[i].m_pNext = &m_BanPool[i+1]; + m_BanPool[i].m_pPrev = &m_BanPool[i-1]; + } + + m_BanPool[0].m_pNext = &m_BanPool[1]; + m_BanPool[NET_SERVER_MAXBANS-1].m_pPrev = &m_BanPool[NET_SERVER_MAXBANS-2]; + m_BanPool_FirstFree = &m_BanPool[0]; + + return true; +} + +int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser) +{ + m_pfnNewClient = pfnNewClient; + m_pfnDelClient = pfnDelClient; + m_UserPtr = pUser; + return 0; +} + +int CNetServer::Close() +{ + /* TODO: implement me */ + return 0; +} + +int CNetServer::Drop(int ClientID, const char *pReason) +{ + /* TODO: insert lots of checks here */ + NETADDR Addr = ClientAddr(ClientID); + + dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"", + ClientID, + Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], + pReason + ); + + m_aSlots[ClientID].m_Connection.Disconnect(pReason); + + if(m_pfnDelClient) + m_pfnDelClient(ClientID, m_UserPtr); + + return 0; +} + +int CNetServer::BanGet(int Index, CBanInfo *pInfo) +{ + CBan *pBan; + for(pBan = m_BanPool_FirstUsed; pBan && Index; pBan = pBan->m_pNext, Index--) + {} + + if(!pBan) + return 0; + *pInfo = pBan->m_Info; + return 1; +} + +int CNetServer::BanNum() +{ + int Count = 0; + CBan *pBan; + for(pBan = m_BanPool_FirstUsed; pBan; pBan = pBan->m_pNext) + Count++; + return Count; +} + +void CNetServer::BanRemoveByObject(CBan *pBan) +{ + int iphash = (pBan->m_Info.m_Addr.ip[0]+pBan->m_Info.m_Addr.ip[1]+pBan->m_Info.m_Addr.ip[2]+pBan->m_Info.m_Addr.ip[3])&0xff; + dbg_msg("netserver", "removing ban on %d.%d.%d.%d", + pBan->m_Info.m_Addr.ip[0], pBan->m_Info.m_Addr.ip[1], pBan->m_Info.m_Addr.ip[2], pBan->m_Info.m_Addr.ip[3]); + MACRO_LIST_UNLINK(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); + MACRO_LIST_UNLINK(pBan, m_aBans[iphash], m_pHashPrev, m_pHashNext); + MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext); +} + +int CNetServer::BanRemove(NETADDR Addr) +{ + int iphash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff; + CBan *pBan = m_aBans[iphash]; + + MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0); + + if(pBan) + { + BanRemoveByObject(pBan); + return 0; + } + + return -1; +} + +int CNetServer::BanAdd(NETADDR Addr, int Seconds) +{ + int iphash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff; + int Stamp = -1; + CBan *pBan; + + /* remove the port */ + Addr.port = 0; + + if(Seconds) + Stamp = time_timestamp() + Seconds; + + /* search to see if it already exists */ + pBan = m_aBans[iphash]; + MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0); + if(pBan) + { + /* adjust the ban */ + pBan->m_Info.m_Expires = Stamp; + return 0; + } + + if(!m_BanPool_FirstFree) + return -1; + + /* fetch and clear the new ban */ + pBan = m_BanPool_FirstFree; + MACRO_LIST_UNLINK(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext); + + /* setup the ban info */ + pBan->m_Info.m_Expires = Stamp; + pBan->m_Info.m_Addr = Addr; + + /* add it to the ban hash */ + MACRO_LIST_LINK_FIRST(pBan, m_aBans[iphash], m_pHashPrev, m_pHashNext); + + /* insert it into the used list */ + { + if(m_BanPool_FirstUsed) + { + CBan *pInsertAfter = m_BanPool_FirstUsed; + MACRO_LIST_FIND(pInsertAfter, m_pNext, Stamp < pInsertAfter->m_Info.m_Expires); + + if(pInsertAfter) + pInsertAfter = pInsertAfter->m_pPrev; + else + { + /* add to last */ + pInsertAfter = m_BanPool_FirstUsed; + while(pInsertAfter->m_pNext) + pInsertAfter = pInsertAfter->m_pNext; + } + + if(pInsertAfter) + { + MACRO_LIST_LINK_AFTER(pBan, pInsertAfter, m_pPrev, m_pNext); + } + else + { + MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); + } + } + else + { + MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); + } + } + + /* drop banned clients */ + { + char Buf[128]; + NETADDR BanAddr; + + if(Seconds) + str_format(Buf, sizeof(Buf), "you have been banned for %d minutes", Seconds/60); + else + str_format(Buf, sizeof(Buf), "you have been banned for life"); + + for(int i = 0; i < MaxClients(); i++) + { + BanAddr = m_aSlots[i].m_Connection.PeerAddress(); + BanAddr.port = 0; + + if(net_addr_comp(&Addr, &BanAddr) == 0) + Drop(i, Buf); + } + } + return 0; +} + +int CNetServer::Update() +{ + int Now = time_timestamp(); + for(int i = 0; i < MaxClients(); i++) + { + m_aSlots[i].m_Connection.Update(); + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) + Drop(i, m_aSlots[i].m_Connection.ErrorString()); + } + + /* remove expired bans */ + while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires < Now) + { + CBan *pBan = m_BanPool_FirstUsed; + BanRemoveByObject(pBan); + } + + return 0; +} + +/* + TODO: chopp up this function into smaller working parts +*/ +int CNetServer::Recv(CNetChunk *pChunk) +{ + unsigned now = time_timestamp(); + + while(1) + { + NETADDR Addr; + + /* check for a chunk */ + if(m_RecvUnpacker.FetchChunk(pChunk)) + return 1; + + /* TODO: empty the recvinfo */ + int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); + + /* no more packets for now */ + if(Bytes <= 0) + break; + + if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) + { + CBan *pBan = 0; + NETADDR BanAddr = Addr; + int iphash = (BanAddr.ip[0]+BanAddr.ip[1]+BanAddr.ip[2]+BanAddr.ip[3])&0xff; + int Found = 0; + BanAddr.port = 0; + + /* search a ban */ + for(pBan = m_aBans[iphash]; pBan; pBan = pBan->m_pHashNext) + { + if(net_addr_comp(&pBan->m_Info.m_Addr, &BanAddr) == 0) + break; + } + + /* check if we just should drop the packet */ + if(pBan) + { + // banned, reply with a message + char BanStr[128]; + if(pBan->m_Info.m_Expires) + { + int Mins = ((pBan->m_Info.m_Expires - now)+59)/60; + if(Mins == 1) + str_format(BanStr, sizeof(BanStr), "banned for %d minute", Mins); + else + str_format(BanStr, sizeof(BanStr), "banned for %d minutes", Mins); + } + else + str_format(BanStr, sizeof(BanStr), "banned for life"); + CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, BanStr, str_length(BanStr)+1); + continue; + } + + if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) + { + pChunk->m_Flags = NETSENDFLAG_CONNLESS; + pChunk->m_ClientID = -1; + pChunk->m_Address = Addr; + pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; + pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; + return 1; + } + else + { + /* TODO: check size here */ + if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) + { + Found = 0; + + /* check if we already got this client */ + for(int i = 0; i < MaxClients(); i++) + { + NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); + if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE && + net_addr_comp(&PeerAddr, &Addr) == 0) + { + Found = 1; /* silent ignore.. we got this client already */ + break; + } + } + + /* client that wants to connect */ + if(!Found) + { + for(int i = 0; i < MaxClients(); i++) + { + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) + { + Found = 1; + m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr); + if(m_pfnNewClient) + m_pfnNewClient(i, m_UserPtr); + break; + } + } + + if(!Found) + { + const char FullMsg[] = "server is full"; + CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg)); + } + } + } + else + { + /* normal packet, find matching slot */ + for(int i = 0; i < MaxClients(); i++) + { + NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); + if(net_addr_comp(&PeerAddr, &Addr) == 0) + { + if(m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) + { + if(m_RecvUnpacker.m_Data.m_DataSize) + m_RecvUnpacker.Start(&Addr, &m_aSlots[i].m_Connection, i); + } + } + } + } + } + } + } + return 0; +} + +int CNetServer::Send(CNetChunk *pChunk) +{ + if(pChunk->m_DataSize >= NET_MAX_PAYLOAD) + { + dbg_msg("netserver", "packet payload too big. %d. dropping packet", pChunk->m_DataSize); + return -1; + } + + if(pChunk->m_Flags&NETSENDFLAG_CONNLESS) + { + /* send connectionless packet */ + CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize); + } + else + { + int Flags = 0; + dbg_assert(pChunk->m_ClientID >= 0, "errornous client id"); + dbg_assert(pChunk->m_ClientID < MaxClients(), "errornous client id"); + + if(pChunk->m_Flags&NETSENDFLAG_VITAL) + Flags = NET_CHUNKFLAG_VITAL; + + m_aSlots[pChunk->m_ClientID].m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData); + + if(pChunk->m_Flags&NETSENDFLAG_FLUSH) + m_aSlots[pChunk->m_ClientID].m_Connection.Flush(); + } + return 0; +} + diff --git a/src/engine/e_packer.c b/src/engine/e_packer.c deleted file mode 100644 index aee08aed..00000000 --- a/src/engine/e_packer.c +++ /dev/null @@ -1,213 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include /* rand() */ -#include - -#include "e_packer.h" -#include "e_compression.h" -#include "e_engine.h" -#include "e_config.h" - -/* useful for debugging */ -#if 0 - #define packing_error(p) p->error = 1; dbg_break() -#else - #define packing_error(p) p->error = 1 -#endif - -static int stress_get_int() -{ - static const int nasty[] = {-1, 0, 1, 66000, -66000, (-1<<31), 0x7fffffff}; - if(rand()&1) - return rand(); - return nasty[rand()%6]; -} - -static const char *stress_get_string(int *size) -{ - static char noise[1024]; - int i; - int s; - s = (rand()%1024)-1; - for(i = 0; i < s; i++) - noise[i] = (rand()%254)+1; - noise[s] = 0; - if(size) - *size = s; - return noise; -} - - -static int stress_prob(float probability) -{ - if(!config.dbg_stress_network) - return 0; - if(rand()/(float)RAND_MAX < probability) - return 1; - return 0; -} - - -void packer_reset(PACKER *p) -{ - p->error = 0; - p->current = p->buffer; - p->end = p->current + PACKER_BUFFER_SIZE; -} - -void packer_add_int(PACKER *p, int i) -{ - if(p->error) - return; - - if(stress_prob(0.025f)) - i = stress_get_int(); - - /* make sure that we have space enough */ - if(p->end - p->current < 6) - { - dbg_break(); - p->error = 1; - } - else - p->current = vint_pack(p->current, i); -} - -void packer_add_string(PACKER *p, const char *str, int limit) -{ - if(p->error) - return; - - if(stress_prob(0.1f)) - { - str = stress_get_string(0); - limit = 0; - } - - /* */ - if(limit > 0) - { - while(*str && limit != 0) - { - *p->current++ = *str++; - limit--; - - if(p->current >= p->end) - { - packing_error(p); - break; - } - } - *p->current++ = 0; - } - else - { - while(*str) - { - *p->current++ = *str++; - - if(p->current >= p->end) - { - packing_error(p); - break; - } - } - *p->current++ = 0; - } -} - -void packer_add_raw(PACKER *p, const unsigned char *data, int size) -{ - if(p->error) - return; - - if(p->current+size >= p->end) - { - packing_error(p); - return; - } - - while(size) - { - *p->current++ = *data++; - size--; - } -} - -int packer_size(PACKER *p) -{ - return (const unsigned char *)p->current-(const unsigned char *)p->buffer; -} - -const unsigned char *packer_data(PACKER *p) -{ - return (const unsigned char *)p->buffer; -} - -void unpacker_reset(UNPACKER *p, const unsigned char *data, int size) -{ - p->error = 0; - p->start = data; - p->end = p->start + size; - p->current = p->start; -} - -int unpacker_get_int(UNPACKER *p) -{ - int i; - if(p->error) - return 0; - if(p->current >= p->end) - { - packing_error(p); - return 0; - } - - p->current = vint_unpack(p->current, &i); - if(p->current > p->end) - { - packing_error(p); - return 0; - } - return i; -} - -const char *unpacker_get_string(UNPACKER *p) -{ - char *ptr; - if(p->error || p->current >= p->end) - return ""; - - ptr = (char *)p->current; - while(*p->current) /* skip the string */ - { - p->current++; - if(p->current == p->end) - { - packing_error(p); - return ""; - } - } - p->current++; - - /* sanitize all strings */ - str_sanitize(ptr); - return ptr; -} - -const unsigned char *unpacker_get_raw(UNPACKER *p, int size) -{ - const unsigned char *ptr = p->current; - if(p->error) - return 0; - - /* check for nasty sizes */ - if(size < 0 || p->current+size > p->end) - { - packing_error(p); - return 0; - } - - /* "unpack" the data */ - p->current += size; - return ptr; -} diff --git a/src/engine/e_packer.cpp b/src/engine/e_packer.cpp new file mode 100644 index 00000000..0d8aeab3 --- /dev/null +++ b/src/engine/e_packer.cpp @@ -0,0 +1,155 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include /* rand() */ +#include + +#include "e_packer.h" +#include "e_compression.h" +#include "e_engine.h" +#include "e_config.h" + +void CPacker::Reset() +{ + m_Error = 0; + m_pCurrent = m_aBuffer; + m_pEnd = m_pCurrent + PACKER_BUFFER_SIZE; +} + +void CPacker::AddInt(int i) +{ + if(m_Error) + return; + + /* make sure that we have space enough */ + if(m_pEnd - m_pCurrent < 6) + { + dbg_break(); + m_Error = 1; + } + else + m_pCurrent = vint_pack(m_pCurrent, i); +} + +void CPacker::AddString(const char *pStr, int Limit) +{ + if(m_Error) + return; + + /* */ + if(Limit > 0) + { + while(*pStr && Limit != 0) + { + *m_pCurrent++ = *pStr++; + Limit--; + + if(m_pCurrent >= m_pEnd) + { + m_Error = 1; + break; + } + } + *m_pCurrent++ = 0; + } + else + { + while(*pStr) + { + *m_pCurrent++ = *pStr++; + + if(m_pCurrent >= m_pEnd) + { + m_Error = 1; + break; + } + } + *m_pCurrent++ = 0; + } +} + +void CPacker::AddRaw(const unsigned char *pData, int Size) +{ + if(m_Error) + return; + + if(m_pCurrent+Size >= m_pEnd) + { + m_Error = 1; + return; + } + + while(Size) + { + *m_pCurrent++ = *pData++; + Size--; + } +} + + +void CUnpacker::Reset(const unsigned char *pData, int Size) +{ + m_Error = 0; + m_pStart = pData; + m_pEnd = m_pStart + Size; + m_pCurrent = m_pStart; +} + +int CUnpacker::GetInt() +{ + if(m_Error) + return 0; + + if(m_pCurrent >= m_pEnd) + { + m_Error = 1; + return 0; + } + + int i; + m_pCurrent = vint_unpack(m_pCurrent, &i); + if(m_pCurrent > m_pEnd) + { + m_Error = 1; + return 0; + } + return i; +} + +const char *CUnpacker::GetString() +{ + if(m_Error || m_pCurrent >= m_pEnd) + return ""; + + char *pPtr = (char *)m_pCurrent; + while(*m_pCurrent) /* skip the string */ + { + m_pCurrent++; + if(m_pCurrent == m_pEnd) + { + m_Error = 1;; + return ""; + } + } + m_pCurrent++; + + /* sanitize all strings */ + str_sanitize(pPtr); + return pPtr; +} + +const unsigned char *CUnpacker::GetRaw(int Size) +{ + const unsigned char *pPtr = m_pCurrent; + if(m_Error) + return 0; + + /* check for nasty sizes */ + if(Size < 0 || m_pCurrent+Size > m_pEnd) + { + m_Error = 1; + return 0; + } + + /* "unpack" the data */ + m_pCurrent += Size; + return pPtr; +} diff --git a/src/engine/e_packer.h b/src/engine/e_packer.h index 71b99ff7..5dc80e7a 100644 --- a/src/engine/e_packer.h +++ b/src/engine/e_packer.h @@ -1,35 +1,37 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -enum +class CPacker { - PACKER_BUFFER_SIZE=1024*2 -}; + enum + { + PACKER_BUFFER_SIZE=1024*2 + }; -typedef struct -{ + unsigned char m_aBuffer[PACKER_BUFFER_SIZE]; + unsigned char *m_pCurrent; + unsigned char *m_pEnd; + int m_Error; +public: + void Reset(); + void AddInt(int i); + void AddString(const char *pStr, int Limit); + void AddRaw(const unsigned char *pData, int Size); - unsigned char buffer[PACKER_BUFFER_SIZE]; - unsigned char *current; - unsigned char *end; - int error; -} PACKER; + int Size() const { return (int)(m_pCurrent-m_aBuffer); } + const unsigned char *Data() const { return m_aBuffer; } + bool Error() const { return m_Error; } +}; -typedef struct +class CUnpacker { - const unsigned char *current; - const unsigned char *start; - const unsigned char *end; - int error; -} UNPACKER; - -void packer_reset(PACKER *p); -void packer_add_int(PACKER *p, int i); -void packer_add_string(PACKER *p, const char *str, int limit); -void packer_add_raw(PACKER *p, const unsigned char *data, int size); -int packer_size(PACKER *p); -const unsigned char *packer_data(PACKER *p); - -void unpacker_reset(UNPACKER *p, const unsigned char *data, int size); -int unpacker_get_int(UNPACKER *p); -const char *unpacker_get_string(UNPACKER *p); -const unsigned char *unpacker_get_raw(UNPACKER *p, int size); + const unsigned char *m_pStart; + const unsigned char *m_pCurrent; + const unsigned char *m_pEnd; + int m_Error; +public: + void Reset(const unsigned char *pData, int Size); + int GetInt(); + const char *GetString(); + const unsigned char *GetRaw(int Size); + bool Error() const { return m_Error; } +}; diff --git a/src/engine/e_ringbuffer.c b/src/engine/e_ringbuffer.c deleted file mode 100644 index 39dfa3e9..00000000 --- a/src/engine/e_ringbuffer.c +++ /dev/null @@ -1,362 +0,0 @@ -#include - -#include "e_ringbuffer.h" - -typedef struct RBITEM -{ - struct RBITEM *prev; - struct RBITEM *next; - int free; - int size; -} RBITEM; - -/* - -*/ -struct RINGBUFFER -{ - RBITEM *produce; - RBITEM *consume; - - RBITEM *first; - RBITEM *last; - void *memory; - int size; - int flags; -}; - -RINGBUFFER *ringbuf_init(void *memory, int size, int flags) -{ - RINGBUFFER *rb = (RINGBUFFER *)memory; - mem_zero(memory, size); - - rb->memory = rb+1; - rb->size = (size-sizeof(RINGBUFFER))/sizeof(RBITEM)*sizeof(RBITEM); - rb->first = (RBITEM *)rb->memory; - rb->first->free = 1; - rb->first->size = rb->size; - rb->last = rb->first; - rb->produce = rb->first; - rb->consume = rb->first; - - rb->flags = flags; - - return rb; -} - -static RBITEM *ringbuf_nextblock(RINGBUFFER *rb, RBITEM *item) -{ - if(item->next) - return item->next; - return rb->first; -} - -static RBITEM *ringbuf_prevblock(RINGBUFFER *rb, RBITEM *item) -{ - if(item->prev) - return item->prev; - return rb->last; -} - -static RBITEM *ringbuf_mergeback(RINGBUFFER *rb, RBITEM *item) -{ - /* make sure that this block and previous block is free */ - if(!item->free || !item->prev || !item->prev->free) - return item; - - /* merge the blocks */ - item->prev->size += item->size; - item->prev->next = item->next; - - /* fixup pointers */ - if(item->next) - item->next->prev = item->prev; - else - rb->last = item->prev; - - if(item == rb->produce) - rb->produce = item->prev; - - if(item == rb->consume) - rb->consume = item->prev; - - /* return the current block */ - return item->prev; -} - -int ringbuf_popfirst(RINGBUFFER *rb) -{ - if(rb->consume->free) - return 0; - - /* set the free flag */ - rb->consume->free = 1; - - /* previous block is also free, merge them */ - rb->consume = ringbuf_mergeback(rb, rb->consume); - - /* advance the consume pointer */ - rb->consume = ringbuf_nextblock(rb, rb->consume); - while(rb->consume->free && rb->consume != rb->produce) - { - rb->consume = ringbuf_mergeback(rb, rb->consume); - rb->consume = ringbuf_nextblock(rb, rb->consume); - } - - /* in the case that we have catched up with the produce pointer */ - /* we might stand on a free block so merge em */ - ringbuf_mergeback(rb, rb->consume); - return 1; -} - -void *ringbuf_allocate(RINGBUFFER *rb, int size) -{ - int wanted_size = (size+sizeof(RBITEM)+sizeof(RBITEM)-1)/sizeof(RBITEM)*sizeof(RBITEM); - RBITEM *block = 0; - - /* check if we even can fit this block */ - if(wanted_size > rb->size) - return 0; - - while(1) - { - /* check for space */ - if(rb->produce->free) - { - if(rb->produce->size >= wanted_size) - block = rb->produce; - else - { - /* wrap around to try to find a block */ - if(rb->first->free && rb->first->size >= wanted_size) - block = rb->first; - } - } - - if(block) - break; - else - { - /* we have no block, check our policy and see what todo */ - if(rb->flags&RINGBUF_FLAG_RECYCLE) - { - if(!ringbuf_popfirst(rb)) - return 0; - } - else - return 0; - } - } - - /* okey, we have our block */ - - /* split the block if needed */ - if(block->size > wanted_size) - { - RBITEM *new_item = (RBITEM *)((char *)block + wanted_size); - new_item->prev = block; - new_item->next = block->next; - if(new_item->next) - new_item->next->prev = new_item; - block->next = new_item; - - new_item->free = 1; - new_item->size = block->size - wanted_size; - block->size = wanted_size; - - if(!new_item->next) - rb->last = new_item; - } - - - /* set next block */ - rb->produce = ringbuf_nextblock(rb, block); - - /* set as used and return the item pointer */ - block->free = 0; - return block+1; -} - -void *ringbuf_prev(RINGBUFFER *rb, void *current) -{ - RBITEM *item = ((RBITEM *)current) - 1; - - while(1) - { - item = ringbuf_prevblock(rb, item); - if(item == rb->produce) - return 0; - if(!item->free) - return item+1; - } -} - -void *ringbuf_next(RINGBUFFER *rb, void *current) -{ - RBITEM *item = ((RBITEM *)current) - 1; - - while(1) - { - item = ringbuf_nextblock(rb, item); - if(item == rb->produce) - return 0; - if(!item->free) - return item+1; - } -} - -void *ringbuf_first(RINGBUFFER *rb) -{ - if(rb->consume->free) - return 0; - return rb->consume+1; -} - -void *ringbuf_last(RINGBUFFER *rb) -{ - if(!rb->produce->free) - return rb->produce+1; - return ringbuf_prev(rb, rb->produce+1); -} - - -/* debugging and testing stuff */ - -static void ringbuf_debugdump(RINGBUFFER *rb, const char *msg) -{ - RBITEM *cur = rb->first; - - dbg_msg("ringbuf", "-- dumping --"); - - while(cur) - { - char flags[4] = " "; - if(cur->free) - flags[0] = 'F'; - if(cur == rb->consume) - flags[1] = '>'; - if(cur == rb->produce) - flags[2] = '<'; - dbg_msg("ringbuf", "%s %d", flags, cur->size); - cur = cur->next; - } - - dbg_msg("ringbuf", "-- --"); - - if(msg) - dbg_assert(0, msg); -} - - - -static void ringbuf_validate(RINGBUFFER *rb) -{ - RBITEM *prev = 0; - RBITEM *cur = rb->first; - int freechunks = 0; - int got_consume = 0; - int got_produce = 0; - - while(cur) - { - - if(cur->free) - freechunks++; - - if(freechunks > 2) ringbuf_debugdump(rb, "too many free chunks"); - if(prev && prev->free && cur->free) ringbuf_debugdump(rb, "two free chunks next to each other"); - if(cur == rb->consume) got_consume = 1; - if(cur == rb->produce) got_produce = 1; - - dbg_assert(cur->prev == prev, "prev pointers doesn't match"); - dbg_assert(!prev || prev->next == cur, "next pointers doesn't match"); - dbg_assert(cur->next || cur == rb->last, "last isn't last"); - - prev = cur; - cur = cur->next; - } - - if(!got_consume) ringbuf_debugdump(rb, "consume pointer isn't pointing at a valid block"); - if(!got_produce) ringbuf_debugdump(rb, "produce pointer isn't pointing at a valid block"); -} - -int ringbuf_test() -{ - char buffer[256]; - RINGBUFFER *rb; - int i, s, k, m; - int count; - int testcount = 0; - - void *item; - int before; - - - for(k = 100; k < sizeof(buffer); k++) - { - if((k%10) == 0) - dbg_msg("ringbuf", "testing at %d", k); - rb = ringbuf_init(buffer, k, 0); - count = 0; - - for(s = 1; s < sizeof(buffer); s++) - { - for(i = 0; i < k*8; i++, testcount++) - { - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - before = m; - - if(ringbuf_allocate(rb, s)) - { - count++; - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - if(before+1 != m) ringbuf_debugdump(rb, "alloc error"); - if(count != m) ringbuf_debugdump(rb, "count error"); - } - else - { - if(ringbuf_popfirst(rb)) - { - count--; - - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - if(before-1 != m) dbg_msg("", "popping error %d %d", before, m); - if(count != m) ringbuf_debugdump(rb, "count error"); - } - } - - /* remove an item every 10 */ - if((i%10) == 0) - { - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - before = m; - - if(ringbuf_popfirst(rb)) - { - count--; - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - if(before-1 != m) dbg_msg("", "popping error %d %d", before, m); - dbg_assert(count == m, "count error"); - } - } - - /* count items */ - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - if(m != count) ringbuf_debugdump(rb, "wrong number of items during forward count"); - - for(m = 0, item = ringbuf_last(rb); item; item = ringbuf_prev(rb, item), m++); - if(m != count) ringbuf_debugdump(rb, "wrong number of items during backward count"); - - ringbuf_validate(rb); - } - - /* empty the ring buffer */ - while(ringbuf_first(rb)) - ringbuf_popfirst(rb); - ringbuf_validate(rb); - count = 0; - } - } - - return 0; -} diff --git a/src/engine/e_ringbuffer.cpp b/src/engine/e_ringbuffer.cpp new file mode 100644 index 00000000..eb8a8af4 --- /dev/null +++ b/src/engine/e_ringbuffer.cpp @@ -0,0 +1,194 @@ +#include + +#include "e_ringbuffer.h" + +CRingBufferBase::CItem *CRingBufferBase::NextBlock(CItem *pItem) +{ + if(pItem->m_pNext) + return pItem->m_pNext; + return m_pFirst; +} + +CRingBufferBase::CItem *CRingBufferBase::PrevBlock(CItem *pItem) +{ + if(pItem->m_pPrev) + return pItem->m_pPrev; + return m_pLast; +} + +CRingBufferBase::CItem *CRingBufferBase::MergeBack(CItem *pItem) +{ + /* make sure that this block and previous block is free */ + if(!pItem->m_Free || !pItem->m_pPrev || !pItem->m_pPrev->m_Free) + return pItem; + + /* merge the blocks */ + pItem->m_pPrev->m_Size += pItem->m_Size; + pItem->m_pPrev->m_pNext = pItem->m_pNext; + + /* fixup pointers */ + if(pItem->m_pNext) + pItem->m_pNext->m_pPrev = pItem->m_pPrev; + else + m_pLast = pItem->m_pPrev; + + if(pItem == m_pProduce) + m_pProduce = pItem->m_pPrev; + + if(pItem == m_pConsume) + m_pConsume = pItem->m_pPrev; + + /* return the current block */ + return pItem->m_pPrev; +} + +void CRingBufferBase::Init(void *pMemory, int Size, int Flags) +{ + mem_zero(pMemory, Size); + m_Size = (Size)/sizeof(CItem)*sizeof(CItem); + m_pFirst = (CItem *)pMemory; + m_pFirst->m_Free = 1; + m_pFirst->m_Size = m_Size; + m_pLast = m_pFirst; + m_pProduce = m_pFirst; + m_pConsume = m_pFirst; + m_Flags = Flags; + +} + +void *CRingBufferBase::Allocate(int Size) +{ + int WantedSize = (Size+sizeof(CItem)+sizeof(CItem)-1)/sizeof(CItem)*sizeof(CItem); + CItem *pBlock = 0; + + /* check if we even can fit this block */ + if(WantedSize > m_Size) + return 0; + + while(1) + { + /* check for space */ + if(m_pProduce->m_Free) + { + if(m_pProduce->m_Size >= WantedSize) + pBlock = m_pProduce; + else + { + /* wrap around to try to find a block */ + if(m_pFirst->m_Free && m_pFirst->m_Size >= WantedSize) + pBlock = m_pFirst; + } + } + + if(pBlock) + break; + else + { + /* we have no block, check our policy and see what todo */ + if(m_Flags&FLAG_RECYCLE) + { + if(!PopFirst()) + return 0; + } + else + return 0; + } + } + + /* okey, we have our block */ + + /* split the block if needed */ + if(pBlock->m_Size > WantedSize) + { + CItem *pNewItem = (CItem *)((char *)pBlock + WantedSize); + pNewItem->m_pPrev = pBlock; + pNewItem->m_pNext = pBlock->m_pNext; + if(pNewItem->m_pNext) + pNewItem->m_pNext->m_pPrev = pNewItem; + pBlock->m_pNext = pNewItem; + + pNewItem->m_Free = 1; + pNewItem->m_Size = pBlock->m_Size - WantedSize; + pBlock->m_Size = WantedSize; + + if(!pNewItem->m_pNext) + m_pLast = pNewItem; + } + + + /* set next block */ + m_pProduce = NextBlock(pBlock); + + /* set as used and return the item pointer */ + pBlock->m_Free = 0; + return (void *)(pBlock+1); +} + +int CRingBufferBase::PopFirst() +{ + if(m_pConsume->m_Free) + return 0; + + /* set the free flag */ + m_pConsume->m_Free = 1; + + /* previous block is also free, merge them */ + m_pConsume = MergeBack(m_pConsume); + + /* advance the consume pointer */ + m_pConsume = NextBlock(m_pConsume); + while(m_pConsume->m_Free && m_pConsume != m_pProduce) + { + m_pConsume = MergeBack(m_pConsume); + m_pConsume = NextBlock(m_pConsume); + } + + /* in the case that we have catched up with the produce pointer */ + /* we might stand on a free block so merge em */ + MergeBack(m_pConsume); + return 1; +} + + +void *CRingBufferBase::Prev(void *pCurrent) +{ + CItem *pItem = ((CItem *)pCurrent) - 1; + + while(1) + { + pItem = PrevBlock(pItem); + if(pItem == m_pProduce) + return 0; + if(!pItem->m_Free) + return pItem+1; + } +} + +void *CRingBufferBase::Next(void *pCurrent) +{ + CItem *pItem = ((CItem *)pCurrent) - 1; + + while(1) + { + pItem = NextBlock(pItem); + if(pItem == m_pProduce) + return 0; + if(!pItem->m_Free) + return pItem+1; + } +} + +void *CRingBufferBase::First() +{ + if(m_pConsume->m_Free) + return 0; + return (void *)(m_pConsume+1); +} + +void *CRingBufferBase::Last() +{ + if(!m_pProduce->m_Free) + return m_pProduce+1; + return Prev(m_pProduce+1); +} + diff --git a/src/engine/e_ringbuffer.h b/src/engine/e_ringbuffer.h index 6113c19b..b9f7219c 100644 --- a/src/engine/e_ringbuffer.h +++ b/src/engine/e_ringbuffer.h @@ -3,21 +3,64 @@ typedef struct RINGBUFFER RINGBUFFER; -enum +class CRingBufferBase { - /* Will start to destroy items to try to fit the next one */ - RINGBUF_FLAG_RECYCLE=1 + class CItem + { + public: + CItem *m_pPrev; + CItem *m_pNext; + int m_Free; + int m_Size; + }; + + CItem *m_pProduce; + CItem *m_pConsume; + + CItem *m_pFirst; + CItem *m_pLast; + + void *m_pMemory; + int m_Size; + int m_Flags; + + CItem *NextBlock(CItem *pItem); + CItem *PrevBlock(CItem *pItem); + CItem *MergeBack(CItem *pItem); +protected: + void *Allocate(int Size); + + void *Prev(void *pCurrent); + void *Next(void *pCurrent); + void *First(); + void *Last(); + + void Init(void *pMemory, int Size, int Flags); + int PopFirst(); +public: + enum + { + /* Will start to destroy items to try to fit the next one */ + FLAG_RECYCLE=1 + }; }; - -RINGBUFFER *ringbuf_init(void *memory, int size, int flags); -void ringbuf_clear(RINGBUFFER *rb); -void *ringbuf_allocate(RINGBUFFER *rb, int size); -void *ringbuf_prev(RINGBUFFER *rb, void *current); -void *ringbuf_next(RINGBUFFER *rb, void *current); -void *ringbuf_first(RINGBUFFER *rb); -void *ringbuf_last(RINGBUFFER *rb); +template +class TStaticRingBuffer : public CRingBufferBase +{ + unsigned char m_aBuffer[TSIZE]; +public: + TStaticRingBuffer() { Init(); } + + void Init() { CRingBufferBase::Init(m_aBuffer, TSIZE, TFLAGS); } + + T *Allocate(int Size) { return (T*)CRingBufferBase::Allocate(Size); } + int PopFirst() { return CRingBufferBase::PopFirst(); } -int ringbuf_popfirst(RINGBUFFER *rb); + T *Prev(T *pCurrent) { return (T*)CRingBufferBase::Prev(pCurrent); } + T *Next(T *pCurrent) { return (T*)CRingBufferBase::Next(pCurrent); } + T *First() { return (T*)CRingBufferBase::First(); } + T *Last() { return (T*)CRingBufferBase::Last(); } +}; #endif diff --git a/src/engine/e_server_interface.h b/src/engine/e_server_interface.h index 5b1e6327..c325b9a1 100644 --- a/src/engine/e_server_interface.h +++ b/src/engine/e_server_interface.h @@ -2,10 +2,6 @@ #ifndef ENGINE_SERVER_INTERFACE_H #define ENGINE_SERVER_INTERFACE_H -#ifdef __cplusplus -extern "C" { -#endif - #include "e_if_other.h" #include "e_if_server.h" #include "e_if_msg.h" @@ -13,8 +9,4 @@ extern "C" { #include "e_console.h" /* TODO: clean this up*/ -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_snapshot.c b/src/engine/e_snapshot.c deleted file mode 100644 index cce8a06e..00000000 --- a/src/engine/e_snapshot.c +++ /dev/null @@ -1,604 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "e_snapshot.h" -#include "e_engine.h" -#include "e_compression.h" -#include "e_common_interface.h" - - -/* TODO: strange arbitrary number */ -static short item_sizes[64] = {0}; - -void snap_set_staticsize(int itemtype, int size) -{ - item_sizes[itemtype] = size; -} - -int *snapitem_data(SNAPSHOT_ITEM *item) { return (int *)(item+1); } -int snapitem_type(SNAPSHOT_ITEM *item) { return item->type_and_id>>16; } -int snapitem_id(SNAPSHOT_ITEM *item) { return item->type_and_id&0xffff; } -int snapitem_key(SNAPSHOT_ITEM *item) { return item->type_and_id; } - -int *snapshot_offsets(SNAPSHOT *snap) { return (int *)(snap+1); } -char *snapshot_datastart(SNAPSHOT *snap) { return (char*)(snapshot_offsets(snap)+snap->num_items); } - -SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index) -{ return (SNAPSHOT_ITEM *)(snapshot_datastart(snap) + snapshot_offsets(snap)[index]); } - -int snapshot_get_item_datasize(SNAPSHOT *snap, int index) -{ - if(index == snap->num_items-1) - return (snap->data_size - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); - return (snapshot_offsets(snap)[index+1] - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); -} - -int snapshot_get_item_index(SNAPSHOT *snap, int key) -{ - /* TODO: OPT: this should not be a linear search. very bad */ - int i; - for(i = 0; i < snap->num_items; i++) - { - if(snapitem_key(snapshot_get_item(snap, i)) == key) - return i; - } - return -1; -} -typedef struct -{ - int num; - int keys[64]; - int index[64]; -} ITEMLIST; -static ITEMLIST sorted[256]; - -static int snapshot_generate_hash(SNAPSHOT *snap) -{ - int i, key, hashid; - - for(i = 0; i < 256; i++) - sorted[i].num = 0; - - for(i = 0; i < snap->num_items; i++) - { - key = snapitem_key(snapshot_get_item(snap, i)); - hashid = ((key>>8)&0xf0) | (key&0xf); - if(sorted[hashid].num != 64) - { - sorted[hashid].index[sorted[hashid].num] = i; - sorted[hashid].keys[sorted[hashid].num] = key; - sorted[hashid].num++; - } - } - return 0; -} - -int snapshot_get_item_index_hashed(SNAPSHOT *snap, int key) -{ - int hashid = ((key>>8)&0xf0) | (key&0xf); - int i; - for(i = 0; i < sorted[hashid].num; i++) - { - if(sorted[hashid].keys[i] == key) - return sorted[hashid].index[i]; - } - - return -1; -} - -typedef struct -{ - 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]); } - */ -} SNAPSHOT_DELTA; - - -static const int MAX_ITEMS = 512; -static SNAPSHOT_DELTA empty = {0,0,0,{0}}; - -void *snapshot_empty_delta() -{ - return ∅ -} - -int snapshot_crc(SNAPSHOT *snap) -{ - int crc = 0; - int i, b; - SNAPSHOT_ITEM *item; - int size; - - for(i = 0; i < snap->num_items; i++) - { - item = snapshot_get_item(snap, i); - size = snapshot_get_item_datasize(snap, i); - - for(b = 0; b < size/4; b++) - crc += snapitem_data(item)[b]; - } - return crc; -} - -void snapshot_debug_dump(SNAPSHOT *snap) -{ - int size, i, b; - SNAPSHOT_ITEM *item; - - dbg_msg("snapshot", "data_size=%d num_items=%d", snap->data_size, snap->num_items); - for(i = 0; i < snap->num_items; i++) - { - item = snapshot_get_item(snap, i); - size = snapshot_get_item_datasize(snap, i); - dbg_msg("snapshot", "\ttype=%d id=%d", snapitem_type(item), snapitem_id(item)); - for(b = 0; b < size/4; b++) - dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, snapitem_data(item)[b], snapitem_data(item)[b]); - } -} - -static int diff_item(int *past, int *current, int *out, int size) -{ - int needed = 0; - while(size) - { - *out = *current-*past; - needed |= *out; - out++; - past++; - current++; - size--; - } - - return needed; -} - -int snapshot_data_rate[0xffff] = {0}; -int snapshot_data_updates[0xffff] = {0}; -static int snapshot_current = 0; - -static void undiff_item(int *past, int *diff, int *out, int size) -{ - while(size) - { - *out = *past+*diff; - - if(*diff == 0) - snapshot_data_rate[snapshot_current] += 1; - else - { - unsigned char buf[16]; - unsigned char *end = vint_pack(buf, *diff); - snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8; - } - - out++; - past++; - diff++; - size--; - } -} - - -/* TODO: OPT: this should be made much faster */ -int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata) -{ - static PERFORMACE_INFO hash_scope = {"hash", 0}; - SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)dstdata; - int *data = (int *)delta->data; - int i, itemsize, pastindex; - SNAPSHOT_ITEM *fromitem; - SNAPSHOT_ITEM *curitem; - SNAPSHOT_ITEM *pastitem; - int count = 0; - int size_count = 0; - - delta->num_deleted_items = 0; - delta->num_update_items = 0; - delta->num_temp_items = 0; - - perf_start(&hash_scope); - snapshot_generate_hash(to); - perf_end(); - - /* pack deleted stuff */ - { - static PERFORMACE_INFO scope = {"delete", 0}; - perf_start(&scope); - - for(i = 0; i < from->num_items; i++) - { - fromitem = snapshot_get_item(from, i); - if(snapshot_get_item_index_hashed(to, (snapitem_key(fromitem))) == -1) - { - /* deleted */ - delta->num_deleted_items++; - *data = snapitem_key(fromitem); - data++; - } - } - - perf_end(); - } - - perf_start(&hash_scope); - snapshot_generate_hash(from); - perf_end(); - - /* pack updated stuff */ - { - static PERFORMACE_INFO scope = {"update", 0}; - int pastindecies[1024]; - perf_start(&scope); - - /* fetch previous indices */ - /* we do this as a separate pass because it helps the cache */ - { - static PERFORMACE_INFO scope = {"find", 0}; - perf_start(&scope); - for(i = 0; i < to->num_items; i++) - { - curitem = snapshot_get_item(to, i); /* O(1) .. O(n) */ - pastindecies[i] = snapshot_get_item_index_hashed(from, snapitem_key(curitem)); /* O(n) .. O(n^n)*/ - } - perf_end(); - } - - for(i = 0; i < to->num_items; i++) - { - /* do delta */ - itemsize = snapshot_get_item_datasize(to, i); /* O(1) .. O(n) */ - curitem = snapshot_get_item(to, i); /* O(1) .. O(n) */ - pastindex = pastindecies[i]; - - if(pastindex != -1) - { - static PERFORMACE_INFO scope = {"diff", 0}; - int *item_data_dst = data+3; - perf_start(&scope); - - pastitem = snapshot_get_item(from, pastindex); - - if(item_sizes[snapitem_type(curitem)]) - item_data_dst = data+2; - - if(diff_item((int*)snapitem_data(pastitem), (int*)snapitem_data(curitem), item_data_dst, itemsize/4)) - { - - *data++ = snapitem_type(curitem); - *data++ = snapitem_id(curitem); - if(!item_sizes[snapitem_type(curitem)]) - *data++ = itemsize/4; - data += itemsize/4; - delta->num_update_items++; - } - perf_end(); - } - else - { - static PERFORMACE_INFO scope = {"copy", 0}; - perf_start(&scope); - - *data++ = snapitem_type(curitem); - *data++ = snapitem_id(curitem); - if(!item_sizes[snapitem_type(curitem)]) - *data++ = itemsize/4; - - mem_copy(data, snapitem_data(curitem), itemsize); - size_count += itemsize; - data += itemsize/4; - delta->num_update_items++; - count++; - - perf_end(); - } - } - - perf_end(); - } - - 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); -} - -static int range_check(void *end, void *ptr, int size) -{ - if((const char *)ptr + size > (const char *)end) - return -1; - return 0; -} - -int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_size) -{ - SNAPBUILD builder; - SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)srcdata; - int *data = (int *)delta->data; - int *end = (int *)(((char *)srcdata + data_size)); - - SNAPSHOT_ITEM *fromitem; - int i, d, keep, itemsize; - int *deleted; - int id, type, key; - int fromindex; - int *newdata; - - snapbuild_init(&builder); - - /* unpack deleted stuff */ - deleted = data; - data += delta->num_deleted_items; - if(data > end) - return -1; - - /* copy all non deleted stuff */ - for(i = 0; i < from->num_items; i++) - { - /* dbg_assert(0, "fail!"); */ - fromitem = snapshot_get_item(from, i); - itemsize = snapshot_get_item_datasize(from, i); - keep = 1; - for(d = 0; d < delta->num_deleted_items; d++) - { - if(deleted[d] == snapitem_key(fromitem)) - { - keep = 0; - break; - } - } - - if(keep) - { - /* keep it */ - mem_copy( - snapbuild_new_item(&builder, snapitem_type(fromitem), snapitem_id(fromitem), itemsize), - snapitem_data(fromitem), itemsize); - } - } - - /* unpack updated stuff */ - for(i = 0; i < delta->num_update_items; i++) - { - if(data+2 > end) - return -1; - - type = *data++; - id = *data++; - if(item_sizes[type]) - itemsize = item_sizes[type]; - else - { - if(data+1 > end) - return -2; - itemsize = (*data++) * 4; - } - snapshot_current = type; - - if(range_check(end, data, itemsize) || itemsize < 0) return -3; - - key = (type<<16)|id; - - /* create the item if needed */ - newdata = snapbuild_get_item_data(&builder, key); - if(!newdata) - newdata = (int *)snapbuild_new_item(&builder, key>>16, key&0xffff, itemsize); - - /*if(range_check(end, newdata, itemsize)) return -4;*/ - - fromindex = snapshot_get_item_index(from, key); - if(fromindex != -1) - { - /* we got an update so we need to apply the diff */ - undiff_item((int *)snapitem_data(snapshot_get_item(from, fromindex)), data, newdata, itemsize/4); - snapshot_data_updates[snapshot_current]++; - } - else /* no previous, just copy the data */ - { - mem_copy(newdata, data, itemsize); - snapshot_data_rate[snapshot_current] += itemsize*8; - snapshot_data_updates[snapshot_current]++; - } - - data += itemsize/4; - } - - /* finish up */ - return snapbuild_finish(&builder, to); -} - -/* SNAPSTORAGE */ - -void snapstorage_init(SNAPSTORAGE *ss) -{ - ss->first = 0; -} - -void snapstorage_purge_all(SNAPSTORAGE *ss) -{ - SNAPSTORAGE_HOLDER *h = ss->first; - SNAPSTORAGE_HOLDER *next; - - while(h) - { - next = h->next; - mem_free(h); - h = next; - } - - /* no more snapshots in storage */ - ss->first = 0; - ss->last = 0; -} - -void snapstorage_purge_until(SNAPSTORAGE *ss, int tick) -{ - SNAPSTORAGE_HOLDER *next; - SNAPSTORAGE_HOLDER *h = ss->first; - - while(h) - { - next = h->next; - if(h->tick >= tick) - return; /* no more to remove */ - mem_free(h); - - /* did we come to the end of the list? */ - if (!next) - break; - - ss->first = next; - next->prev = 0x0; - - h = next; - } - - /* no more snapshots in storage */ - ss->first = 0; - ss->last = 0; -} - -void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data, int create_alt) -{ - /* allocate memory for holder + snapshot_data */ - SNAPSTORAGE_HOLDER *h; - int total_size = sizeof(SNAPSTORAGE_HOLDER)+data_size; - - if(create_alt) - total_size += data_size; - - h = (SNAPSTORAGE_HOLDER *)mem_alloc(total_size, 1); - - /* set data */ - h->tick = tick; - h->tagtime = tagtime; - h->snap_size = data_size; - h->snap = (SNAPSHOT*)(h+1); - mem_copy(h->snap, data, data_size); - - if(create_alt) /* create alternative if wanted */ - { - h->alt_snap = (SNAPSHOT*)(((char *)h->snap) + data_size); - mem_copy(h->alt_snap, data, data_size); - } - else - h->alt_snap = 0; - - - /* link */ - h->next = 0; - h->prev = ss->last; - if(ss->last) - ss->last->next = h; - else - ss->first = h; - ss->last = h; -} - -int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data, SNAPSHOT **alt_data) -{ - SNAPSTORAGE_HOLDER *h = ss->first; - - while(h) - { - if(h->tick == tick) - { - if(tagtime) - *tagtime = h->tagtime; - if(data) - *data = h->snap; - if(alt_data) - *alt_data = h->alt_snap; - return h->snap_size; - } - - h = h->next; - } - - return -1; -} - -/* SNAPBUILD */ - -void snapbuild_init(SNAPBUILD *sb) -{ - sb->data_size = 0; - sb->num_items = 0; -} - -SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index) -{ - return (SNAPSHOT_ITEM *)&(sb->data[sb->offsets[index]]); -} - -int *snapbuild_get_item_data(SNAPBUILD *sb, int key) -{ - int i; - for(i = 0; i < sb->num_items; i++) - { - if(snapitem_key(snapbuild_get_item(sb, i)) == key) - return (int *)snapitem_data(snapbuild_get_item(sb, i)); - } - return 0; -} - -int snapbuild_finish(SNAPBUILD *sb, void *snapdata) -{ - /* flattern and make the snapshot */ - SNAPSHOT *snap = (SNAPSHOT *)snapdata; - int offset_size = sizeof(int)*sb->num_items; - snap->data_size = sb->data_size; - snap->num_items = sb->num_items; - mem_copy(snapshot_offsets(snap), sb->offsets, offset_size); - mem_copy(snapshot_datastart(snap), sb->data, sb->data_size); - return sizeof(SNAPSHOT) + offset_size + sb->data_size; -} - -void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size) -{ - SNAPSHOT_ITEM *obj = (SNAPSHOT_ITEM *)(sb->data+sb->data_size); - - /*if(stress_prob(0.01f)) - { - size += ((rand()%5) - 2)*4; - if(size < 0) - size = 0; - }*/ - - mem_zero(obj, sizeof(SNAPSHOT_ITEM) + size); - obj->type_and_id = (type<<16)|id; - sb->offsets[sb->num_items] = sb->data_size; - sb->data_size += sizeof(SNAPSHOT_ITEM) + size; - sb->num_items++; - - dbg_assert(sb->data_size < MAX_SNAPSHOT_SIZE, "too much data"); - dbg_assert(sb->num_items < SNAPBUILD_MAX_ITEMS, "too many items"); - - return snapitem_data(obj); -} diff --git a/src/engine/e_snapshot.cpp b/src/engine/e_snapshot.cpp new file mode 100644 index 00000000..65487665 --- /dev/null +++ b/src/engine/e_snapshot.cpp @@ -0,0 +1,571 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "e_snapshot.h" +#include "e_engine.h" +#include "e_compression.h" +#include "e_common_interface.h" + + +/* TODO: strange arbitrary number */ +static short item_sizes[64] = {0}; + +void snap_set_staticsize(int itemtype, int size) +{ + item_sizes[itemtype] = size; +} + +CSnapshotItem *CSnapshot::GetItem(int Index) +{ + return (CSnapshotItem *)(DataStart() + Offsets()[Index]); +} + +int CSnapshot::GetItemSize(int Index) +{ + if(Index == m_NumItems-1) + return (m_DataSize - Offsets()[Index]) - sizeof(CSnapshotItem); + return (Offsets()[Index+1] - Offsets()[Index]) - sizeof(CSnapshotItem); +} + +int CSnapshot::GetItemIndex(int Key) +{ + /* TODO: OPT: this should not be a linear search. very bad */ + for(int i = 0; i < m_NumItems; i++) + { + if(GetItem(i)->Key() == Key) + return i; + } + return -1; +} + +// TODO: clean up this +typedef struct +{ + int num; + int keys[64]; + int index[64]; +} ITEMLIST; + +static ITEMLIST sorted[256]; + +int CSnapshot::GenerateHash() +{ + for(int i = 0; i < 256; i++) + sorted[i].num = 0; + + for(int i = 0; i < m_NumItems; i++) + { + int Key = GetItem(i)->Key(); + int HashID = ((Key>>8)&0xf0) | (Key&0xf); + if(sorted[HashID].num != 64) + { + sorted[HashID].index[sorted[HashID].num] = i; + sorted[HashID].keys[sorted[HashID].num] = Key; + sorted[HashID].num++; + } + } + return 0; +} + +int CSnapshot::GetItemIndexHashed(int Key) +{ + int HashID = ((Key>>8)&0xf0) | (Key&0xf); + for(int i = 0; i < sorted[HashID].num; i++) + { + if(sorted[HashID].keys[i] == Key) + return sorted[HashID].index[i]; + } + + return -1; +} + + + +static const int MAX_ITEMS = 512; +static CSnapshotDelta empty = {0,0,0,{0}}; + +CSnapshotDelta *CSnapshot::EmptyDelta() +{ + return ∅ +} + +int CSnapshot::Crc() +{ + int crc = 0; + + for(int i = 0; i < m_NumItems; i++) + { + CSnapshotItem *pItem = GetItem(i); + int Size = GetItemSize(i); + + for(int b = 0; b < Size/4; b++) + crc += pItem->Data()[b]; + } + return crc; +} + +void CSnapshot::DebugDump() +{ + dbg_msg("snapshot", "data_size=%d num_items=%d", m_DataSize, m_NumItems); + for(int i = 0; i < m_NumItems; i++) + { + CSnapshotItem *pItem = GetItem(i); + int Size = GetItemSize(i); + dbg_msg("snapshot", "\ttype=%d id=%d", pItem->Type(), pItem->ID()); + for(int b = 0; b < Size/4; b++) + dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, pItem->Data()[b], pItem->Data()[b]); + } +} + + +// TODO: remove these +int snapshot_data_rate[0xffff] = {0}; +int snapshot_data_updates[0xffff] = {0}; +static int snapshot_current = 0; + +static int diff_item(int *past, int *current, int *out, int size) +{ + int needed = 0; + while(size) + { + *out = *current-*past; + needed |= *out; + out++; + past++; + current++; + size--; + } + + return needed; +} + +static void undiff_item(int *past, int *diff, int *out, int size) +{ + while(size) + { + *out = *past+*diff; + + if(*diff == 0) + snapshot_data_rate[snapshot_current] += 1; + else + { + unsigned char buf[16]; + unsigned char *end = vint_pack(buf, *diff); + snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8; + } + + out++; + past++; + diff++; + size--; + } +} + + +/* TODO: OPT: this should be made much faster */ +int CSnapshot::CreateDelta(CSnapshot *from, CSnapshot *to, void *dstdata) +{ + static PERFORMACE_INFO hash_scope = {"hash", 0}; + CSnapshotDelta *delta = (CSnapshotDelta *)dstdata; + int *data = (int *)delta->m_pData; + int i, itemsize, pastindex; + CSnapshotItem *pFromItem; + CSnapshotItem *pCurItem; + CSnapshotItem *pPastItem; + int count = 0; + int size_count = 0; + + delta->m_NumDeletedItems = 0; + delta->m_NumUpdateItems = 0; + delta->m_NumTempItems = 0; + + perf_start(&hash_scope); + to->GenerateHash(); + perf_end(); + + /* pack deleted stuff */ + { + static PERFORMACE_INFO scope = {"delete", 0}; + perf_start(&scope); + + for(i = 0; i < from->m_NumItems; i++) + { + pFromItem = from->GetItem(i); + if(to->GetItemIndexHashed(pFromItem->Key()) == -1) + { + /* deleted */ + delta->m_NumDeletedItems++; + *data = pFromItem->Key(); + data++; + } + } + + perf_end(); + } + + perf_start(&hash_scope); + from->GenerateHash(); + perf_end(); + + /* pack updated stuff */ + { + static PERFORMACE_INFO scope = {"update", 0}; + int pastindecies[1024]; + perf_start(&scope); + + /* fetch previous indices */ + /* we do this as a separate pass because it helps the cache */ + { + static PERFORMACE_INFO scope = {"find", 0}; + perf_start(&scope); + for(i = 0; i < to->m_NumItems; i++) + { + pCurItem = to->GetItem(i); /* O(1) .. O(n) */ + pastindecies[i] = from->GetItemIndexHashed(pCurItem->Key()); /* O(n) .. O(n^n)*/ + } + perf_end(); + } + + for(i = 0; i < to->m_NumItems; i++) + { + /* do delta */ + itemsize = to->GetItemSize(i); /* O(1) .. O(n) */ + pCurItem = to->GetItem(i); /* O(1) .. O(n) */ + pastindex = pastindecies[i]; + + if(pastindex != -1) + { + static PERFORMACE_INFO scope = {"diff", 0}; + int *item_data_dst = data+3; + perf_start(&scope); + + pPastItem = from->GetItem(pastindex); + + if(item_sizes[pCurItem->Type()]) + item_data_dst = data+2; + + if(diff_item((int*)pPastItem->Data(), (int*)pCurItem->Data(), item_data_dst, itemsize/4)) + { + + *data++ = pCurItem->Type(); + *data++ = pCurItem->ID(); + if(!item_sizes[pCurItem->Type()]) + *data++ = itemsize/4; + data += itemsize/4; + delta->m_NumUpdateItems++; + } + perf_end(); + } + else + { + static PERFORMACE_INFO scope = {"copy", 0}; + perf_start(&scope); + + *data++ = pCurItem->Type(); + *data++ = pCurItem->ID(); + if(!item_sizes[pCurItem->Type()]) + *data++ = itemsize/4; + + mem_copy(data, pCurItem->Data(), itemsize); + size_count += itemsize; + data += itemsize/4; + delta->m_NumUpdateItems++; + count++; + + perf_end(); + } + } + + perf_end(); + } + + if(0) + { + dbg_msg("snapshot", "%d %d %d", + delta->m_NumDeletedItems, + delta->m_NumUpdateItems, + delta->m_NumTempItems); + } + + /* + // 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->m_NumDeletedItems && !delta->m_NumUpdateItems && !delta->m_NumTempItems) + return 0; + + return (int)((char*)data-(char*)dstdata); +} + +static int range_check(void *end, void *ptr, int size) +{ + if((const char *)ptr + size > (const char *)end) + return -1; + return 0; +} + +int CSnapshot::UnpackDelta(CSnapshot *from, CSnapshot *to, void *srcdata, int data_size) +{ + CSnapshotBuilder builder; + CSnapshotDelta *delta = (CSnapshotDelta *)srcdata; + int *data = (int *)delta->m_pData; + int *end = (int *)(((char *)srcdata + data_size)); + + CSnapshotItem *fromitem; + int i, d, keep, itemsize; + int *deleted; + int id, type, key; + int fromindex; + int *newdata; + + builder.Init(); + + /* unpack deleted stuff */ + deleted = data; + data += delta->m_NumDeletedItems; + if(data > end) + return -1; + + /* copy all non deleted stuff */ + for(i = 0; i < from->m_NumItems; i++) + { + /* dbg_assert(0, "fail!"); */ + fromitem = from->GetItem(i); + itemsize = from->GetItemSize(i); + keep = 1; + for(d = 0; d < delta->m_NumDeletedItems; d++) + { + if(deleted[d] == fromitem->Key()) + { + keep = 0; + break; + } + } + + if(keep) + { + /* keep it */ + mem_copy( + builder.NewItem(fromitem->Type(), fromitem->ID(), itemsize), + fromitem->Data(), itemsize); + } + } + + /* unpack updated stuff */ + for(i = 0; i < delta->m_NumUpdateItems; i++) + { + if(data+2 > end) + return -1; + + type = *data++; + id = *data++; + if(item_sizes[type]) + itemsize = item_sizes[type]; + else + { + if(data+1 > end) + return -2; + itemsize = (*data++) * 4; + } + snapshot_current = type; + + if(range_check(end, data, itemsize) || itemsize < 0) return -3; + + key = (type<<16)|id; + + /* create the item if needed */ + newdata = builder.GetItemData(key); + if(!newdata) + newdata = (int *)builder.NewItem(key>>16, key&0xffff, itemsize); + + /*if(range_check(end, newdata, itemsize)) return -4;*/ + + fromindex = from->GetItemIndex(key); + if(fromindex != -1) + { + /* we got an update so we need to apply the diff */ + undiff_item((int *)from->GetItem(fromindex)->Data(), data, newdata, itemsize/4); + snapshot_data_updates[snapshot_current]++; + } + else /* no previous, just copy the data */ + { + mem_copy(newdata, data, itemsize); + snapshot_data_rate[snapshot_current] += itemsize*8; + snapshot_data_updates[snapshot_current]++; + } + + data += itemsize/4; + } + + /* finish up */ + return builder.Finish(to); +} + +/* CSnapshotStorage */ + +void CSnapshotStorage::Init() +{ + m_pFirst = 0; + m_pLast = 0; +} + +void CSnapshotStorage::PurgeAll() +{ + CHolder *pHolder = m_pFirst; + CHolder *pNext; + + while(pHolder) + { + pNext = pHolder->m_pNext; + mem_free(pHolder); + pHolder = pNext; + } + + /* no more snapshots in storage */ + m_pFirst = 0; + m_pLast = 0; +} + +void CSnapshotStorage::PurgeUntil(int Tick) +{ + CHolder *pHolder = m_pFirst; + CHolder *pNext; + + while(pHolder) + { + pNext = pHolder->m_pNext; + if(pHolder->m_Tick >= Tick) + return; /* no more to remove */ + mem_free(pHolder); + + /* did we come to the end of the list? */ + if (!pNext) + break; + + m_pFirst = pNext; + pNext->m_pPrev = 0x0; + + pHolder = pNext; + } + + /* no more snapshots in storage */ + m_pFirst = 0; + m_pLast = 0; +} + +void CSnapshotStorage::Add(int Tick, int64 Tagtime, int DataSize, void *pData, int CreateAlt) +{ + /* allocate memory for holder + snapshot_data */ + int TotalSize = sizeof(CHolder)+DataSize; + + if(CreateAlt) + TotalSize += DataSize; + + CHolder *pHolder = (CHolder *)mem_alloc(TotalSize, 1); + + /* set data */ + pHolder->m_Tick = Tick; + pHolder->m_Tagtime = Tagtime; + pHolder->m_SnapSize = DataSize; + pHolder->m_pSnap = (CSnapshot*)(pHolder+1); + mem_copy(pHolder->m_pSnap, pData, DataSize); + + if(CreateAlt) /* create alternative if wanted */ + { + pHolder->m_pAltSnap = (CSnapshot*)(((char *)pHolder->m_pSnap) + DataSize); + mem_copy(pHolder->m_pAltSnap, pData, DataSize); + } + else + pHolder->m_pAltSnap = 0; + + + /* link */ + pHolder->m_pNext = 0; + pHolder->m_pPrev = m_pLast; + if(m_pLast) + m_pLast->m_pNext = pHolder; + else + m_pFirst = pHolder; + m_pLast = pHolder; +} + +int CSnapshotStorage::Get(int Tick, int64 *pTagtime, CSnapshot **ppData, CSnapshot **ppAltData) +{ + CHolder *pHolder = m_pFirst; + + while(pHolder) + { + if(pHolder->m_Tick == Tick) + { + if(pTagtime) + *pTagtime = pHolder->m_Tagtime; + if(ppData) + *ppData = pHolder->m_pSnap; + if(ppAltData) + *ppData = pHolder->m_pAltSnap; + return pHolder->m_SnapSize; + } + + pHolder = pHolder->m_pNext; + } + + return -1; +} + +/* CSnapshotBuilder */ + +void CSnapshotBuilder::Init() +{ + m_DataSize = 0; + m_NumItems = 0; +} + +CSnapshotItem *CSnapshotBuilder::GetItem(int Index) +{ + return (CSnapshotItem *)&(m_aData[m_aOffsets[Index]]); +} + +int *CSnapshotBuilder::GetItemData(int key) +{ + int i; + for(i = 0; i < m_NumItems; i++) + { + if(GetItem(i)->Key() == key) + return (int *)GetItem(i)->Data(); + } + return 0; +} + +int CSnapshotBuilder::Finish(void *snapdata) +{ + /* flattern and make the snapshot */ + CSnapshot *pSnap = (CSnapshot *)snapdata; + int OffsetSize = sizeof(int)*m_NumItems; + pSnap->m_DataSize = m_DataSize; + pSnap->m_NumItems = m_NumItems; + mem_copy(pSnap->Offsets(), m_aOffsets, OffsetSize); + mem_copy(pSnap->DataStart(), m_aData, m_DataSize); + return sizeof(CSnapshot) + OffsetSize + m_DataSize; +} + +void *CSnapshotBuilder::NewItem(int Type, int ID, int Size) +{ + CSnapshotItem *pObj = (CSnapshotItem *)(m_aData + m_DataSize); + + mem_zero(pObj, sizeof(CSnapshotItem) + Size); + pObj->m_TypeAndID = (Type<<16)|ID; + m_aOffsets[m_NumItems] = m_DataSize; + m_DataSize += sizeof(CSnapshotItem) + Size; + m_NumItems++; + + dbg_assert(m_DataSize < CSnapshot::MAX_SIZE, "too much data"); + dbg_assert(m_NumItems < MAX_ITEMS, "too many items"); + + return pObj->Data(); +} diff --git a/src/engine/e_snapshot.h b/src/engine/e_snapshot.h index f57c95c0..60dc254c 100644 --- a/src/engine/e_snapshot.h +++ b/src/engine/e_snapshot.h @@ -4,89 +4,115 @@ #include -/* SNAPSHOT */ +/* CSnapshot */ -enum + + +class CSnapshotItem { - MAX_SNAPSHOT_SIZE=64*1024 +public: + int m_TypeAndID; + + int *Data() { return (int *)(this+1); } + int Type() { return m_TypeAndID>>16; } + int ID() { return m_TypeAndID&0xffff; } + int Key() { return m_TypeAndID; } }; -typedef struct +class CSnapshotDelta { - int type_and_id; -} SNAPSHOT_ITEM; +public: + int m_NumDeletedItems; + int m_NumUpdateItems; + int m_NumTempItems; /* needed? */ + int m_pData[1]; +}; -typedef struct +// TODO: hide a lot of these members +class CSnapshot { - int data_size; - int num_items; -} SNAPSHOT; - -int *snapitem_data(SNAPSHOT_ITEM *item); -int snapitem_type(SNAPSHOT_ITEM *item); -int snapitem_id(SNAPSHOT_ITEM *item); -int snapitem_key(SNAPSHOT_ITEM *item); - -int *snapshot_offsets(SNAPSHOT *snap); -char *snapshot_datastart(SNAPSHOT *snap); - -SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index); -int snapshot_get_item_datasize(SNAPSHOT *snap, int index); -int snapshot_get_item_index(SNAPSHOT *snap, int key); +public: + enum + { + MAX_SIZE=64*1024 + }; + + int m_DataSize; + int m_NumItems; + + int *Offsets() const { return (int *)(this+1); } + char *DataStart() const { return (char*)(Offsets()+m_NumItems); } + CSnapshotItem *GetItem(int Index); + int GetItemSize(int Index); + int GetItemIndex(int Key); + + int Crc(); + void DebugDump(); + + // TODO: move these + int GetItemIndexHashed(int Key); + int GenerateHash(); + + // + static CSnapshotDelta *EmptyDelta(); + static int CreateDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pData); + static int UnpackDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pData, int DataSize); +}; -void *snapshot_empty_delta(); -int snapshot_crc(SNAPSHOT *snap); -void snapshot_debug_dump(SNAPSHOT *snap); -int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *data); -int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *data, int data_size); +/* CSnapshotStorage */ -/* SNAPSTORAGE */ -typedef struct SNAPSTORAGE_HOLDER_t +class CSnapshotStorage { - struct SNAPSTORAGE_HOLDER_t *prev; - struct SNAPSTORAGE_HOLDER_t *next; - - int64 tagtime; - int tick; - - int snap_size; - SNAPSHOT *snap; - SNAPSHOT *alt_snap; -} SNAPSTORAGE_HOLDER; - -typedef struct SNAPSTORAGE_t +public: + class CHolder + { + public: + CHolder *m_pPrev; + CHolder *m_pNext; + + int64 m_Tagtime; + int m_Tick; + + int m_SnapSize; + CSnapshot *m_pSnap; + CSnapshot *m_pAltSnap; + }; + + + CHolder *m_pFirst; + CHolder *m_pLast; + + void Init(); + void PurgeAll(); + void PurgeUntil(int Tick); + void Add(int Tick, int64 Tagtime, int DataSize, void *pData, int CreateAlt); + int Get(int Tick, int64 *Tagtime, CSnapshot **pData, CSnapshot **ppAltData); +}; + +class CSnapshotBuilder { - SNAPSTORAGE_HOLDER *first; - SNAPSTORAGE_HOLDER *last; -} SNAPSTORAGE; + enum + { + MAX_ITEMS = 1024*2 + }; -void snapstorage_init(SNAPSTORAGE *ss); -void snapstorage_purge_all(SNAPSTORAGE *ss); -void snapstorage_purge_until(SNAPSTORAGE *ss, int tick); -void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data, int create_alt); -int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data, SNAPSHOT **alt_data); + char m_aData[CSnapshot::MAX_SIZE]; + int m_DataSize; -/* SNAPBUILD */ + int m_aOffsets[MAX_ITEMS]; + int m_NumItems; -enum -{ - SNAPBUILD_MAX_ITEMS = 1024*2 +public: + void Init(); + + void *NewItem(int Type, int ID, int Size); + + CSnapshotItem *GetItem(int Index); + int *GetItemData(int Key); + + int Finish(void *Snapdata); }; -typedef struct SNAPBUILD -{ - char data[MAX_SNAPSHOT_SIZE]; - int data_size; - - int offsets[SNAPBUILD_MAX_ITEMS]; - int num_items; -} SNAPBUILD; - -void snapbuild_init(SNAPBUILD *sb); -SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index); -int *snapbuild_get_item_data(SNAPBUILD *sb, int key); -int snapbuild_finish(SNAPBUILD *sb, void *snapdata); -void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size); #endif /* ENGINE_SNAPSHOT_H */ diff --git a/src/engine/server/es_register.c b/src/engine/server/es_register.c deleted file mode 100644 index b3ac70a6..00000000 --- a/src/engine/server/es_register.c +++ /dev/null @@ -1,272 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -extern NETSERVER *net; - -enum -{ - REGISTERSTATE_START=0, - REGISTERSTATE_UPDATE_ADDRS, - REGISTERSTATE_QUERY_COUNT, - REGISTERSTATE_HEARTBEAT, - REGISTERSTATE_REGISTERED, - REGISTERSTATE_ERROR -}; - -static int register_state = REGISTERSTATE_START; -static int64 register_state_start = 0; -static int register_first = 1; -static int register_count = 0; - -static void register_new_state(int state) -{ - register_state = state; - register_state_start = time_get(); -} - -static void register_send_fwcheckresponse(NETADDR *addr) -{ - NETCHUNK packet; - packet.client_id = -1; - packet.address = *addr; - packet.flags = NETSENDFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE); - packet.data = SERVERBROWSE_FWRESPONSE; - netserver_send(net, &packet); -} - -static void register_send_heartbeat(NETADDR addr) -{ - static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; - unsigned short port = config.sv_port; - NETCHUNK packet; - - mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); - - packet.client_id = -1; - packet.address = addr; - packet.flags = NETSENDFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2; - packet.data = &data; - - /* supply the set port that the master can use if it has problems */ - if(config.sv_external_port) - port = config.sv_external_port; - data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8; - data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff; - netserver_send(net, &packet); -} - -static void register_send_count_request(NETADDR addr) -{ - NETCHUNK packet; - packet.client_id = -1; - packet.address = addr; - packet.flags = NETSENDFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_GETCOUNT); - packet.data = SERVERBROWSE_GETCOUNT; - netserver_send(net, &packet); -} - -typedef struct -{ - NETADDR addr; - int count; - int valid; - int64 last_send; -} MASTERSERVER_INFO; - -static MASTERSERVER_INFO masterserver_info[MAX_MASTERSERVERS] = {{{0}}}; -static int register_registered_server = -1; - -void register_update() -{ - int64 now = time_get(); - int64 freq = time_freq(); - - if(!config.sv_register) - return; - - mastersrv_update(); - - if(register_state == REGISTERSTATE_START) - { - register_count = 0; - register_first = 1; - register_new_state(REGISTERSTATE_UPDATE_ADDRS); - mastersrv_refresh_addresses(); - dbg_msg("register", "refreshing ip addresses"); - } - else if(register_state == REGISTERSTATE_UPDATE_ADDRS) - { - register_registered_server = -1; - - if(!mastersrv_refreshing()) - { - int i; - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - NETADDR addr = mastersrv_get(i); - masterserver_info[i].addr = addr; - masterserver_info[i].count = 0; - - if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3]) - masterserver_info[i].valid = 0; - else - { - masterserver_info[i].valid = 1; - masterserver_info[i].count = -1; - masterserver_info[i].last_send = 0; - } - } - - dbg_msg("register", "fetching server counts"); - register_new_state(REGISTERSTATE_QUERY_COUNT); - } - } - else if(register_state == REGISTERSTATE_QUERY_COUNT) - { - int i; - int left = 0; - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - if(!masterserver_info[i].valid) - continue; - - if(masterserver_info[i].count == -1) - { - left++; - if(masterserver_info[i].last_send+freq < now) - { - masterserver_info[i].last_send = now; - register_send_count_request(masterserver_info[i].addr); - } - } - } - - /* check if we are done or timed out */ - if(left == 0 || now > register_state_start+freq*3) - { - /* choose server */ - int best = -1; - int i; - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - if(!masterserver_info[i].valid || masterserver_info[i].count == -1) - continue; - - if(best == -1 || masterserver_info[i].count < masterserver_info[best].count) - best = i; - } - - /* server chosen */ - register_registered_server = best; - if(register_registered_server == -1) - { - dbg_msg("register", "WARNING: No master servers. Retrying in 60 seconds"); - register_new_state(REGISTERSTATE_ERROR); - } - else - { - dbg_msg("register", "choosen '%s' as master, sending heartbeats", mastersrv_name(register_registered_server)); - masterserver_info[register_registered_server].last_send = 0; - register_new_state(REGISTERSTATE_HEARTBEAT); - } - } - } - else if(register_state == REGISTERSTATE_HEARTBEAT) - { - /* check if we should send heartbeat */ - if(now > masterserver_info[register_registered_server].last_send+freq*15) - { - masterserver_info[register_registered_server].last_send = now; - register_send_heartbeat(masterserver_info[register_registered_server].addr); - } - - if(now > register_state_start+freq*60) - { - dbg_msg("register", "WARNING: Master server is not responding, switching master"); - register_new_state(REGISTERSTATE_START); - } - } - else if(register_state == REGISTERSTATE_REGISTERED) - { - if(register_first) - dbg_msg("register", "server registered"); - - register_first = 0; - - /* check if we should send new heartbeat again */ - if(now > register_state_start+freq) - { - if(register_count == 120) /* redo the whole process after 60 minutes to balance out the master servers */ - register_new_state(REGISTERSTATE_START); - else - { - register_count++; - register_new_state(REGISTERSTATE_HEARTBEAT); - } - } - } - else if(register_state == REGISTERSTATE_ERROR) - { - /* check for restart */ - if(now > register_state_start+freq*60) - register_new_state(REGISTERSTATE_START); - } -} - -static void register_got_count(NETCHUNK *p) -{ - unsigned char *data = (unsigned char *)p->data; - int count = (data[sizeof(SERVERBROWSE_COUNT)]<<8) | data[sizeof(SERVERBROWSE_COUNT)+1]; - int i; - - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - if(net_addr_comp(&masterserver_info[i].addr, &p->address) == 0) - { - masterserver_info[i].count = count; - break; - } - } -} - -int register_process_packet(NETCHUNK *packet) -{ - if(packet->data_size == sizeof(SERVERBROWSE_FWCHECK) && - memcmp(packet->data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) - { - register_send_fwcheckresponse(&packet->address); - return 1; - } - else if(packet->data_size == sizeof(SERVERBROWSE_FWOK) && - memcmp(packet->data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) - { - if(register_first) - dbg_msg("register", "no firewall/nat problems detected"); - register_new_state(REGISTERSTATE_REGISTERED); - return 1; - } - else if(packet->data_size == sizeof(SERVERBROWSE_FWERROR) && - memcmp(packet->data, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0) - { - dbg_msg("register", "ERROR: the master server reports that clients can not connect to this server."); - dbg_msg("register", "ERROR: configure your firewall/nat to let through udp on port %d.", config.sv_port); - register_new_state(REGISTERSTATE_ERROR); - return 1; - } - else if(packet->data_size == sizeof(SERVERBROWSE_COUNT)+2 && - memcmp(packet->data, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0) - { - register_got_count(packet); - return 1; - } - - return 0; -} diff --git a/src/engine/server/es_register.cpp b/src/engine/server/es_register.cpp new file mode 100644 index 00000000..0eb5dba5 --- /dev/null +++ b/src/engine/server/es_register.cpp @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include + +#include + +extern CNetServer *m_pNetServer; + +enum +{ + REGISTERSTATE_START=0, + REGISTERSTATE_UPDATE_ADDRS, + REGISTERSTATE_QUERY_COUNT, + REGISTERSTATE_HEARTBEAT, + REGISTERSTATE_REGISTERED, + REGISTERSTATE_ERROR +}; + +static int register_state = REGISTERSTATE_START; +static int64 register_state_start = 0; +static int register_first = 1; +static int register_count = 0; + +static void register_new_state(int state) +{ + register_state = state; + register_state_start = time_get(); +} + +static void register_send_fwcheckresponse(NETADDR *pAddr) +{ + CNetChunk Packet; + Packet.m_ClientID = -1; + Packet.m_Address = *pAddr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_FWRESPONSE); + Packet.m_pData = SERVERBROWSE_FWRESPONSE; + m_pNetServer->Send(&Packet); +} + +static void register_send_heartbeat(NETADDR addr) +{ + static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; + unsigned short port = config.sv_port; + CNetChunk Packet; + + mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); + + Packet.m_ClientID = -1; + Packet.m_Address = addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_HEARTBEAT) + 2; + Packet.m_pData = &data; + + /* supply the set port that the master can use if it has problems */ + if(config.sv_external_port) + port = config.sv_external_port; + data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8; + data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff; + m_pNetServer->Send(&Packet); +} + +static void register_send_count_request(NETADDR Addr) +{ + CNetChunk Packet; + Packet.m_ClientID = -1; + Packet.m_Address = Addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT); + Packet.m_pData = SERVERBROWSE_GETCOUNT; + m_pNetServer->Send(&Packet); +} + +typedef struct +{ + NETADDR addr; + int count; + int valid; + int64 last_send; +} MASTERSERVER_INFO; + +static MASTERSERVER_INFO masterserver_info[MAX_MASTERSERVERS] = {{{0}}}; +static int register_registered_server = -1; + +void register_update() +{ + int64 now = time_get(); + int64 freq = time_freq(); + + if(!config.sv_register) + return; + + mastersrv_update(); + + if(register_state == REGISTERSTATE_START) + { + register_count = 0; + register_first = 1; + register_new_state(REGISTERSTATE_UPDATE_ADDRS); + mastersrv_refresh_addresses(); + dbg_msg("register", "refreshing ip addresses"); + } + else if(register_state == REGISTERSTATE_UPDATE_ADDRS) + { + register_registered_server = -1; + + if(!mastersrv_refreshing()) + { + int i; + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + NETADDR addr = mastersrv_get(i); + masterserver_info[i].addr = addr; + masterserver_info[i].count = 0; + + if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3]) + masterserver_info[i].valid = 0; + else + { + masterserver_info[i].valid = 1; + masterserver_info[i].count = -1; + masterserver_info[i].last_send = 0; + } + } + + dbg_msg("register", "fetching server counts"); + register_new_state(REGISTERSTATE_QUERY_COUNT); + } + } + else if(register_state == REGISTERSTATE_QUERY_COUNT) + { + int i; + int left = 0; + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + if(!masterserver_info[i].valid) + continue; + + if(masterserver_info[i].count == -1) + { + left++; + if(masterserver_info[i].last_send+freq < now) + { + masterserver_info[i].last_send = now; + register_send_count_request(masterserver_info[i].addr); + } + } + } + + /* check if we are done or timed out */ + if(left == 0 || now > register_state_start+freq*3) + { + /* choose server */ + int best = -1; + int i; + for(i = 0; i < MAX_MASTERSERVERS; i++) + { + if(!masterserver_info[i].valid || masterserver_info[i].count == -1) + continue; + + if(best == -1 || masterserver_info[i].count < masterserver_info[best].count) + best = i; + } + + /* server chosen */ + register_registered_server = best; + if(register_registered_server == -1) + { + dbg_msg("register", "WARNING: No master servers. Retrying in 60 seconds"); + register_new_state(REGISTERSTATE_ERROR); + } + else + { + dbg_msg("register", "choosen '%s' as master, sending heartbeats", mastersrv_name(register_registered_server)); + masterserver_info[register_registered_server].last_send = 0; + register_new_state(REGISTERSTATE_HEARTBEAT); + } + } + } + else if(register_state == REGISTERSTATE_HEARTBEAT) + { + /* check if we should send heartbeat */ + if(now > masterserver_info[register_registered_server].last_send+freq*15) + { + masterserver_info[register_registered_server].last_send = now; + register_send_heartbeat(masterserver_info[register_registered_server].addr); + } + + if(now > register_state_start+freq*60) + { + dbg_msg("register", "WARNING: Master server is not responding, switching master"); + register_new_state(REGISTERSTATE_START); + } + } + else if(register_state == REGISTERSTATE_REGISTERED) + { + if(register_first) + dbg_msg("register", "server registered"); + + register_first = 0; + + /* check if we should send new heartbeat again */ + if(now > register_state_start+freq) + { + if(register_count == 120) /* redo the whole process after 60 minutes to balance out the master servers */ + register_new_state(REGISTERSTATE_START); + else + { + register_count++; + register_new_state(REGISTERSTATE_HEARTBEAT); + } + } + } + else if(register_state == REGISTERSTATE_ERROR) + { + /* check for restart */ + if(now > register_state_start+freq*60) + register_new_state(REGISTERSTATE_START); + } +} + +static void register_got_count(CNetChunk *pChunk) +{ + unsigned char *pData = (unsigned char *)pChunk->m_pData; + int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1]; + + for(int i = 0; i < MAX_MASTERSERVERS; i++) + { + if(net_addr_comp(&masterserver_info[i].addr, &pChunk->m_Address) == 0) + { + masterserver_info[i].count = Count; + break; + } + } +} + +int register_process_packet(CNetChunk *pPacket) +{ + if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) && + memcmp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) + { + register_send_fwcheckresponse(&pPacket->m_Address); + return 1; + } + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWOK) && + memcmp(pPacket->m_pData, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) + { + if(register_first) + dbg_msg("register", "no firewall/nat problems detected"); + register_new_state(REGISTERSTATE_REGISTERED); + return 1; + } + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWERROR) && + memcmp(pPacket->m_pData, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0) + { + dbg_msg("register", "ERROR: the master server reports that clients can not connect to this server."); + dbg_msg("register", "ERROR: configure your firewall/nat to let through udp on port %d.", config.sv_port); + register_new_state(REGISTERSTATE_ERROR); + return 1; + } + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 && + memcmp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0) + { + register_got_count(pPacket); + return 1; + } + + return 0; +} diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c deleted file mode 100644 index 8763351d..00000000 --- a/src/engine/server/es_server.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include - -#if defined(CONF_FAMILY_WINDOWS) - #define _WIN32_WINNT 0x0500 - #include -#endif - -static SNAPBUILD builder; - -static int64 game_start_time; -static int current_tick = 0; -static int run_server = 1; - -static char browseinfo_gametype[16] = {0}; -static int browseinfo_progression = -1; - -static int64 lastheartbeat; -/*static NETADDR4 master_server;*/ - -static char current_map[64]; -static int current_map_crc; -static unsigned char *current_map_data = 0; -static int current_map_size = 0; - -void *snap_new_item(int type, int id, int size) -{ - dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); - dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); - return snapbuild_new_item(&builder, type, id, size); -} - -typedef struct -{ - short next; - short state; /* 0 = free, 1 = alloced, 2 = timed */ - int timeout; -} SNAP_ID; - -static const int MAX_IDS = 16*1024; /* should be lowered */ -static SNAP_ID snap_ids[16*1024]; -static int snap_first_free_id; -static int snap_first_timed_id; -static int snap_last_timed_id; -static int snap_id_usage; -static int snap_id_inusage; -static int snap_id_inited = 0; - -enum -{ - SRVCLIENT_STATE_EMPTY = 0, - SRVCLIENT_STATE_AUTH, - SRVCLIENT_STATE_CONNECTING, - SRVCLIENT_STATE_READY, - SRVCLIENT_STATE_INGAME, - - SRVCLIENT_SNAPRATE_INIT=0, - SRVCLIENT_SNAPRATE_FULL, - SRVCLIENT_SNAPRATE_RECOVER -}; - -typedef struct -{ - int data[MAX_INPUT_SIZE]; - int game_tick; /* the tick that was chosen for the input */ -} CLIENT_INPUT; - -/* */ -typedef struct -{ - /* connection state info */ - int state; - int latency; - int snap_rate; - - int last_acked_snapshot; - int last_input_tick; - SNAPSTORAGE snapshots; - - CLIENT_INPUT latestinput; - CLIENT_INPUT inputs[200]; /* TODO: handle input better */ - int current_input; - - char name[MAX_NAME_LENGTH]; - char clan[MAX_CLANNAME_LENGTH]; - int score; - int authed; -} CLIENT; - -static CLIENT clients[MAX_CLIENTS]; -NETSERVER *net; - -static void snap_init_id() -{ - int i; - if(snap_id_inited) - return; - - for(i = 0; i < MAX_IDS; i++) - { - snap_ids[i].next = i+1; - snap_ids[i].state = 0; - } - - snap_ids[MAX_IDS-1].next = -1; - snap_first_free_id = 0; - snap_first_timed_id = -1; - snap_last_timed_id = -1; - snap_id_usage = 0; - snap_id_inusage = 0; - - snap_id_inited = 1; -} - -static void snap_remove_first_timeout() -{ - int next_timed = snap_ids[snap_first_timed_id].next; - - /* add it to the free list */ - snap_ids[snap_first_timed_id].next = snap_first_free_id; - snap_ids[snap_first_timed_id].state = 0; - snap_first_free_id = snap_first_timed_id; - - /* remove it from the timed list */ - snap_first_timed_id = next_timed; - if(snap_first_timed_id == -1) - snap_last_timed_id = -1; - - snap_id_usage--; -} - -int snap_new_id() -{ - int id; - int64 now = time_get(); - if(!snap_id_inited) - snap_init_id(); - - /* process timed ids */ - while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now) - snap_remove_first_timeout(); - - id = snap_first_free_id; - dbg_assert(id != -1, "id error"); - snap_first_free_id = snap_ids[snap_first_free_id].next; - snap_ids[id].state = 1; - snap_id_usage++; - snap_id_inusage++; - return id; -} - -void snap_timeout_ids() -{ - /* process timed ids */ - while(snap_first_timed_id != -1) - snap_remove_first_timeout(); -} - -void snap_free_id(int id) -{ - dbg_assert(snap_ids[id].state == 1, "id is not alloced"); - - snap_id_inusage--; - snap_ids[id].state = 2; - snap_ids[id].timeout = time_get()+time_freq()*5; - snap_ids[id].next = -1; - - if(snap_last_timed_id != -1) - { - snap_ids[snap_last_timed_id].next = id; - snap_last_timed_id = id; - } - else - { - snap_first_timed_id = id; - snap_last_timed_id = id; - } -} - -int *server_latestinput(int client_id, int *size) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return 0; - return clients[client_id].latestinput.data; -} - -const char *server_clientname(int client_id) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return "(invalid client)"; - return clients[client_id].name; -} - -static const char *str_ltrim(const char *str) -{ - while(*str && *str <= 32) - str++; - return str; -} - -static void str_rtrim(char *str) -{ - int i = str_length(str); - while(i >= 0) - { - if(str[i] > 32) - break; - str[i] = 0; - i--; - } -} - -static int server_try_setclientname(int client_id, const char *name) -{ - int i; - char trimmed_name[64]; - - /* trim the name */ - str_copy(trimmed_name, str_ltrim(name), sizeof(trimmed_name)); - str_rtrim(trimmed_name); - dbg_msg("", "'%s' -> '%s'", name, trimmed_name); - name = trimmed_name; - - - /* check for empty names */ - if(!name[0]) - return -1; - - /* make sure that two clients doesn't have the same name */ - for(i = 0; i < MAX_CLIENTS; i++) - if(i != client_id && clients[i].state >= SRVCLIENT_STATE_READY) - { - if(strcmp(name, clients[i].name) == 0) - return -1; - } - - /* set the client name */ - str_copy(clients[client_id].name, name, MAX_NAME_LENGTH); - return 0; -} - -void server_setclientname(int client_id, const char *name) -{ - char nametry[MAX_NAME_LENGTH]; - int i; - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return; - - if(!name) - return; - - str_copy(nametry, name, MAX_NAME_LENGTH); - if(server_try_setclientname(client_id, nametry)) - { - /* auto rename */ - for(i = 1;; i++) - { - str_format(nametry, MAX_NAME_LENGTH, "(%d)%s", i, name); - if(server_try_setclientname(client_id, nametry) == 0) - break; - } - } -} - -void server_setclientscore(int client_id, int score) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return; - clients[client_id].score = score; -} - -void server_setbrowseinfo(const char *game_type, int progression) -{ - str_copy(browseinfo_gametype, game_type, sizeof(browseinfo_gametype)); - browseinfo_progression = progression; - if(browseinfo_progression > 100) - browseinfo_progression = 100; - if(browseinfo_progression < -1) - browseinfo_progression = -1; -} - -void server_kick(int client_id, const char *reason) -{ - if(client_id < 0 || client_id > MAX_CLIENTS) - return; - - if(clients[client_id].state != SRVCLIENT_STATE_EMPTY) - netserver_drop(net, client_id, reason); -} - -int server_tick() -{ - return current_tick; -} - -int64 server_tick_start_time(int tick) -{ - return game_start_time + (time_freq()*tick)/SERVER_TICK_SPEED; -} - -int server_tickspeed() -{ - return SERVER_TICK_SPEED; -} - -int server_init() -{ - int i; - for(i = 0; i < MAX_CLIENTS; i++) - { - clients[i].state = SRVCLIENT_STATE_EMPTY; - clients[i].name[0] = 0; - clients[i].clan[0] = 0; - snapstorage_init(&clients[i].snapshots); - } - - current_tick = 0; - - return 0; -} - -int server_getclientinfo(int client_id, CLIENT_INFO *info) -{ - dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid"); - dbg_assert(info != 0, "info can not be null"); - - if(clients[client_id].state == SRVCLIENT_STATE_INGAME) - { - info->name = clients[client_id].name; - info->latency = clients[client_id].latency; - return 1; - } - return 0; -} - -int server_send_msg(int client_id) -{ - const MSG_INFO *info = msg_get_info(); - NETCHUNK packet; - if(!info) - return -1; - - mem_zero(&packet, sizeof(NETCHUNK)); - - packet.client_id = client_id; - packet.data = info->data; - packet.data_size = info->size; - - if(info->flags&MSGFLAG_VITAL) - packet.flags |= NETSENDFLAG_VITAL; - if(info->flags&MSGFLAG_FLUSH) - packet.flags |= NETSENDFLAG_FLUSH; - - /* write message to demo recorder */ - if(!(info->flags&MSGFLAG_NORECORD)) - demorec_record_message(info->data, info->size); - - if(!(info->flags&MSGFLAG_NOSEND)) - { - if(client_id == -1) - { - /* broadcast */ - int i; - for(i = 0; i < MAX_CLIENTS; i++) - if(clients[i].state == SRVCLIENT_STATE_INGAME) - { - packet.client_id = i; - netserver_send(net, &packet); - } - } - else - netserver_send(net, &packet); - } - return 0; -} - -static void server_do_snap() -{ - int i; - - { - static PERFORMACE_INFO scope = {"presnap", 0}; - perf_start(&scope); - mods_presnap(); - perf_end(); - } - - /* create snapshot for demo recording */ - if(demorec_isrecording()) - { - char data[MAX_SNAPSHOT_SIZE]; - int snapshot_size; - - /* build snap and possibly add some messages */ - snapbuild_init(&builder); - mods_snap(-1); - snapshot_size = snapbuild_finish(&builder, data); - - /* write snapshot */ - demorec_record_snapshot(server_tick(), data, snapshot_size); - } - - /* create snapshots for all clients */ - for(i = 0; i < MAX_CLIENTS; i++) - { - /* client must be ingame to recive snapshots */ - if(clients[i].state != SRVCLIENT_STATE_INGAME) - continue; - - /* this client is trying to recover, don't spam snapshots */ - if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_RECOVER && (server_tick()%50) != 0) - continue; - - /* this client is trying to recover, don't spam snapshots */ - if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_INIT && (server_tick()%10) != 0) - continue; - - { - char data[MAX_SNAPSHOT_SIZE]; - char deltadata[MAX_SNAPSHOT_SIZE]; - char compdata[MAX_SNAPSHOT_SIZE]; - int snapshot_size; - int crc; - static SNAPSHOT emptysnap; - SNAPSHOT *deltashot = &emptysnap; - int deltashot_size; - int delta_tick = -1; - int deltasize; - static PERFORMACE_INFO scope = {"build", 0}; - perf_start(&scope); - - snapbuild_init(&builder); - - { - static PERFORMACE_INFO scope = {"modsnap", 0}; - perf_start(&scope); - mods_snap(i); - perf_end(); - } - - /* finish snapshot */ - snapshot_size = snapbuild_finish(&builder, data); - crc = snapshot_crc((SNAPSHOT*)data); - - /* remove old snapshos */ - /* keep 3 seconds worth of snapshots */ - snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED*3); - - /* save it the snapshot */ - snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data, 0); - - /* find snapshot that we can preform delta against */ - emptysnap.data_size = 0; - emptysnap.num_items = 0; - - { - deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot, 0); - if(deltashot_size >= 0) - delta_tick = clients[i].last_acked_snapshot; - else - { - /* no acked package found, force client to recover rate */ - if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_FULL) - clients[i].snap_rate = SRVCLIENT_SNAPRATE_RECOVER; - } - } - - /* create delta */ - { - static PERFORMACE_INFO scope = {"delta", 0}; - perf_start(&scope); - deltasize = snapshot_create_delta(deltashot, (SNAPSHOT*)data, deltadata); - perf_end(); - } - - - if(deltasize) - { - /* compress it */ - int snapshot_size; - const int max_size = MAX_SNAPSHOT_PACKSIZE; - int numpackets; - int n, left; - - { - static PERFORMACE_INFO scope = {"compress", 0}; - /*char buffer[512];*/ - perf_start(&scope); - snapshot_size = intpack_compress(deltadata, deltasize, compdata); - - /*str_hex(buffer, sizeof(buffer), compdata, snapshot_size); - dbg_msg("", "deltasize=%d -> %d : %s", deltasize, snapshot_size, buffer);*/ - - perf_end(); - } - - numpackets = (snapshot_size+max_size-1)/max_size; - - for(n = 0, left = snapshot_size; left; n++) - { - int chunk = left < max_size ? left : max_size; - left -= chunk; - - if(numpackets == 1) - msg_pack_start_system(NETMSG_SNAPSINGLE, MSGFLAG_FLUSH); - else - msg_pack_start_system(NETMSG_SNAP, MSGFLAG_FLUSH); - - msg_pack_int(current_tick); - msg_pack_int(current_tick-delta_tick); /* compressed with */ - - if(numpackets != 1) - { - msg_pack_int(numpackets); - msg_pack_int(n); - } - - msg_pack_int(crc); - msg_pack_int(chunk); - msg_pack_raw(&compdata[n*max_size], chunk); - msg_pack_end(); - server_send_msg(i); - } - } - else - { - msg_pack_start_system(NETMSG_SNAPEMPTY, MSGFLAG_FLUSH); - msg_pack_int(current_tick); - msg_pack_int(current_tick-delta_tick); /* compressed with */ - msg_pack_end(); - server_send_msg(i); - } - - perf_end(); - } - } - - mods_postsnap(); -} - - -static void reset_client(int cid) -{ - /* reset input */ - int i; - for(i = 0; i < 200; i++) - clients[cid].inputs[i].game_tick = -1; - clients[cid].current_input = 0; - mem_zero(&clients[cid].latestinput, sizeof(clients[cid].latestinput)); - - snapstorage_purge_all(&clients[cid].snapshots); - clients[cid].last_acked_snapshot = -1; - clients[cid].last_input_tick = -1; - clients[cid].snap_rate = SRVCLIENT_SNAPRATE_INIT; - clients[cid].score = 0; - -} - -static int new_client_callback(int cid, void *user) -{ - clients[cid].state = SRVCLIENT_STATE_AUTH; - clients[cid].name[0] = 0; - clients[cid].clan[0] = 0; - clients[cid].authed = 0; - reset_client(cid); - return 0; -} - -static int del_client_callback(int cid, void *user) -{ - /* notify the mod about the drop */ - if(clients[cid].state >= SRVCLIENT_STATE_READY) - mods_client_drop(cid); - - clients[cid].state = SRVCLIENT_STATE_EMPTY; - clients[cid].name[0] = 0; - clients[cid].clan[0] = 0; - clients[cid].authed = 0; - snapstorage_purge_all(&clients[cid].snapshots); - return 0; -} - -static void server_send_map(int cid) -{ - msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_string(config.sv_map, 0); - msg_pack_int(current_map_crc); - msg_pack_end(); - server_send_msg(cid); -} - -static void server_send_rcon_line(int cid, const char *line) -{ - msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL); - msg_pack_string(line, 512); - msg_pack_end(); - server_send_msg(cid); -} - -static void server_send_rcon_line_authed(const char *line, void *user_data) -{ - static volatile int reentry_guard = 0; - int i; - - if(reentry_guard) return; - reentry_guard++; - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state != SRVCLIENT_STATE_EMPTY && clients[i].authed) - server_send_rcon_line(i, line); - } - - reentry_guard--; -} - -static void server_process_client_packet(NETCHUNK *packet) -{ - int cid = packet->client_id; - NETADDR addr; - - int sys; - int msg = msg_unpack_start(packet->data, packet->data_size, &sys); - - if(clients[cid].state == SRVCLIENT_STATE_AUTH) - { - if(sys && msg == NETMSG_INFO) - { - char version[64]; - const char *password; - str_copy(version, msg_unpack_string(), 64); - if(strcmp(version, mods_net_version()) != 0) - { - /* OH FUCK! wrong version, drop him */ - char reason[256]; - str_format(reason, sizeof(reason), "wrong version. server is running '%s' and client '%s'.", mods_net_version(), version); - netserver_drop(net, cid, reason); - return; - } - - str_copy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); - str_copy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); - password = msg_unpack_string(); - - if(config.password[0] != 0 && strcmp(config.password, password) != 0) - { - /* wrong password */ - netserver_drop(net, cid, "wrong password"); - return; - } - - clients[cid].state = SRVCLIENT_STATE_CONNECTING; - server_send_map(cid); - } - } - else - { - if(sys) - { - /* system message */ - if(msg == NETMSG_REQUEST_MAP_DATA) - { - int chunk = msg_unpack_int(); - int chunk_size = 1024-128; - int offset = chunk * chunk_size; - int last = 0; - - /* drop faulty map data requests */ - if(chunk < 0 || offset > current_map_size) - return; - - if(offset+chunk_size >= current_map_size) - { - chunk_size = current_map_size-offset; - if(chunk_size < 0) - chunk_size = 0; - last = 1; - } - - msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_int(last); - msg_pack_int(current_map_size); - msg_pack_int(chunk_size); - msg_pack_raw(¤t_map_data[offset], chunk_size); - msg_pack_end(); - server_send_msg(cid); - - if(config.debug) - dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size); - } - else if(msg == NETMSG_READY) - { - if(clients[cid].state == SRVCLIENT_STATE_CONNECTING) - { - netserver_client_addr(net, cid, &addr); - - dbg_msg("server", "player is ready. cid=%x ip=%d.%d.%d.%d", - cid, - addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] - ); - clients[cid].state = SRVCLIENT_STATE_READY; - mods_connected(cid); - } - } - else if(msg == NETMSG_ENTERGAME) - { - if(clients[cid].state == SRVCLIENT_STATE_READY) - { - netserver_client_addr(net, cid, &addr); - - dbg_msg("server", "player has entered the game. cid=%x ip=%d.%d.%d.%d", - cid, - addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] - ); - clients[cid].state = SRVCLIENT_STATE_INGAME; - mods_client_enter(cid); - } - } - else if(msg == NETMSG_INPUT) - { - int tick, size, i; - CLIENT_INPUT *input; - int64 tagtime; - - clients[cid].last_acked_snapshot = msg_unpack_int(); - tick = msg_unpack_int(); - size = msg_unpack_int(); - - /* check for errors */ - if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE) - return; - - if(clients[cid].last_acked_snapshot > 0) - clients[cid].snap_rate = SRVCLIENT_SNAPRATE_FULL; - - if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0, 0) >= 0) - clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq()); - - /* add message to report the input timing */ - /* skip packets that are old */ - if(tick > clients[cid].last_input_tick) - { - int time_left = ((server_tick_start_time(tick)-time_get())*1000) / time_freq(); - msg_pack_start_system(NETMSG_INPUTTIMING, 0); - msg_pack_int(tick); - msg_pack_int(time_left); - msg_pack_end(); - server_send_msg(cid); - } - - clients[cid].last_input_tick = tick; - - input = &clients[cid].inputs[clients[cid].current_input]; - - if(tick <= server_tick()) - tick = server_tick()+1; - - input->game_tick = tick; - - for(i = 0; i < size/4; i++) - input->data[i] = msg_unpack_int(); - - mem_copy(clients[cid].latestinput.data, input->data, MAX_INPUT_SIZE*sizeof(int)); - - clients[cid].current_input++; - clients[cid].current_input %= 200; - - /* call the mod with the fresh input data */ - if(clients[cid].state == SRVCLIENT_STATE_INGAME) - mods_client_direct_input(cid, clients[cid].latestinput.data); - } - else if(msg == NETMSG_RCON_CMD) - { - const char *cmd = msg_unpack_string(); - - if(msg_unpack_error() == 0 && clients[cid].authed) - { - dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); - console_execute_line(cmd); - } - } - else if(msg == NETMSG_RCON_AUTH) - { - const char *pw; - msg_unpack_string(); /* login name, not used */ - pw = msg_unpack_string(); - - if(msg_unpack_error() == 0) - { - if(config.sv_rcon_password[0] == 0) - { - server_send_rcon_line(cid, "No rcon password set on server. Set sv_rcon_password to enable the remote console."); - } - else if(strcmp(pw, config.sv_rcon_password) == 0) - { - msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL); - msg_pack_int(1); - msg_pack_end(); - server_send_msg(cid); - - clients[cid].authed = 1; - server_send_rcon_line(cid, "Authentication successful. Remote console access granted."); - dbg_msg("server", "cid=%d authed", cid); - } - else - { - server_send_rcon_line(cid, "Wrong password."); - } - } - } - else if(msg == NETMSG_PING) - { - msg_pack_start_system(NETMSG_PING_REPLY, 0); - msg_pack_end(); - server_send_msg(cid); - } - else - { - char hex[] = "0123456789ABCDEF"; - char buf[512]; - int b; - - for(b = 0; b < packet->data_size && b < 32; b++) - { - buf[b*3] = hex[((const unsigned char *)packet->data)[b]>>4]; - buf[b*3+1] = hex[((const unsigned char *)packet->data)[b]&0xf]; - buf[b*3+2] = ' '; - buf[b*3+3] = 0; - } - - dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size); - dbg_msg("server", "%s", buf); - - } - } - else - { - /* game message */ - if(clients[cid].state >= SRVCLIENT_STATE_READY) - mods_message(msg, cid); - } - } -} - - -int server_ban_add(NETADDR addr, int seconds) -{ - return netserver_ban_add(net, addr, seconds); -} - -int server_ban_remove(NETADDR addr) -{ - return netserver_ban_remove(net, addr); -} - -static void server_send_serverinfo(NETADDR *addr, int token) -{ - NETCHUNK packet; - PACKER p; - char buf[128]; - - /* count the players */ - int player_count = 0; - int i; - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state != SRVCLIENT_STATE_EMPTY) - player_count++; - } - - packer_reset(&p); - - if(token >= 0) - { - /* new token based format */ - packer_add_raw(&p, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); - str_format(buf, sizeof(buf), "%d", token); - packer_add_string(&p, buf, 6); - } - else - { - /* old format */ - packer_add_raw(&p, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)); - } - - packer_add_string(&p, mods_version(), 32); - packer_add_string(&p, config.sv_name, 64); - packer_add_string(&p, config.sv_map, 32); - - /* gametype */ - packer_add_string(&p, browseinfo_gametype, 16); - - /* flags */ - i = 0; - if(config.password[0]) /* password set */ - i |= SRVFLAG_PASSWORD; - str_format(buf, sizeof(buf), "%d", i); - packer_add_string(&p, buf, 2); - - /* progression */ - str_format(buf, sizeof(buf), "%d", browseinfo_progression); - packer_add_string(&p, buf, 4); - - str_format(buf, sizeof(buf), "%d", player_count); packer_add_string(&p, buf, 3); /* num players */ - str_format(buf, sizeof(buf), "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */ - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state != SRVCLIENT_STATE_EMPTY) - { - packer_add_string(&p, clients[i].name, 48); /* player name */ - str_format(buf, sizeof(buf), "%d", clients[i].score); packer_add_string(&p, buf, 6); /* player score */ - } - } - - - packet.client_id = -1; - packet.address = *addr; - packet.flags = NETSENDFLAG_CONNLESS; - packet.data_size = packer_size(&p); - packet.data = packer_data(&p); - netserver_send(net, &packet); -} - -extern int register_process_packet(NETCHUNK *packet); -extern int register_update(); - -static void server_pump_network() -{ - NETCHUNK packet; - - netserver_update(net); - - /* process packets */ - while(netserver_recv(net, &packet)) - { - if(packet.client_id == -1) - { - /* stateless */ - if(!register_process_packet(&packet)) - { - if(packet.data_size == sizeof(SERVERBROWSE_GETINFO)+1 && - memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) - { - server_send_serverinfo(&packet.address, ((unsigned char *)packet.data)[sizeof(SERVERBROWSE_GETINFO)]); - } - - - if(packet.data_size == sizeof(SERVERBROWSE_OLD_GETINFO) && - memcmp(packet.data, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) - { - server_send_serverinfo(&packet.address, -1); - } - } - } - else - server_process_client_packet(&packet); - } -} - -static int server_load_map(const char *mapname) -{ - DATAFILE *df; - char buf[512]; - str_format(buf, sizeof(buf), "maps/%s.map", mapname); - df = datafile_load(buf); - if(!df) - return 0; - - /* stop recording when we change map */ - demorec_record_stop(); - - /* reinit snapshot ids */ - snap_timeout_ids(); - - /* get the crc of the map */ - current_map_crc = datafile_crc(buf); - dbg_msg("server", "%s crc is %08x", buf, current_map_crc); - - str_copy(current_map, mapname, sizeof(current_map)); - map_set(df); - - /* load compelate map into memory for download */ - { - IOHANDLE file = engine_openfile(buf, IOFLAG_READ); - current_map_size = (int)io_length(file); - if(current_map_data) - mem_free(current_map_data); - current_map_data = (unsigned char *)mem_alloc(current_map_size, 1); - io_read(file, current_map_data, current_map_size); - io_close(file); - } - return 1; -} - -static int server_run() -{ - NETADDR bindaddr; - - snap_init_id(); - net_init(); - - /* */ - console_register_print_callback(server_send_rcon_line_authed, 0); - - /* load map */ - if(!server_load_map(config.sv_map)) - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - return -1; - } - - /* start server */ - /* TODO: IPv6 support */ - if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0) - { - /* sweet! */ - bindaddr.port = config.sv_port; - } - else - { - mem_zero(&bindaddr, sizeof(bindaddr)); - bindaddr.port = config.sv_port; - } - - net = netserver_open(bindaddr, config.sv_max_clients, 0); - if(!net) - { - dbg_msg("server", "couldn't open socket. port might already be in use"); - return -1; - } - - netserver_set_callbacks(net, new_client_callback, del_client_callback, 0); - - dbg_msg("server", "server name is '%s'", config.sv_name); - - mods_init(); - dbg_msg("server", "version %s", mods_net_version()); - - /* start game */ - { - int64 reporttime = time_get(); - int reportinterval = 3; - - lastheartbeat = 0; - game_start_time = time_get(); - - if(config.debug) - dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024); - - while(run_server) - { - static PERFORMACE_INFO rootscope = {"root", 0}; - int64 t = time_get(); - int new_ticks = 0; - - perf_start(&rootscope); - - /* load new map TODO: don't poll this */ - if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) - { - config.sv_map_reload = 0; - - /* load map */ - if(server_load_map(config.sv_map)) - { - int c; - - /* new map loaded */ - mods_shutdown(); - - for(c = 0; c < MAX_CLIENTS; c++) - { - if(clients[c].state == SRVCLIENT_STATE_EMPTY) - continue; - - server_send_map(c); - reset_client(c); - clients[c].state = SRVCLIENT_STATE_CONNECTING; - } - - game_start_time = time_get(); - current_tick = 0; - mods_init(); - } - else - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - config_set_sv_map(&config, current_map); - } - } - - while(t > server_tick_start_time(current_tick+1)) - { - current_tick++; - new_ticks++; - - /* apply new input */ - { - static PERFORMACE_INFO scope = {"input", 0}; - int c, i; - - perf_start(&scope); - - for(c = 0; c < MAX_CLIENTS; c++) - { - if(clients[c].state == SRVCLIENT_STATE_EMPTY) - continue; - for(i = 0; i < 200; i++) - { - if(clients[c].inputs[i].game_tick == server_tick()) - { - if(clients[c].state == SRVCLIENT_STATE_INGAME) - mods_client_predicted_input(c, clients[c].inputs[i].data); - break; - } - } - } - - perf_end(); - } - - /* progress game */ - { - static PERFORMACE_INFO scope = {"tick", 0}; - perf_start(&scope); - mods_tick(); - perf_end(); - } - } - - /* snap game */ - if(new_ticks) - { - if(config.sv_high_bandwidth || (current_tick%2) == 0) - { - static PERFORMACE_INFO scope = {"snap", 0}; - perf_start(&scope); - server_do_snap(); - perf_end(); - } - } - - /* master server stuff */ - register_update(); - - - { - static PERFORMACE_INFO scope = {"net", 0}; - perf_start(&scope); - server_pump_network(); - perf_end(); - } - - perf_end(); - - if(reporttime < time_get()) - { - if(config.debug) - { - /* - static NETSTATS prev_stats; - NETSTATS stats; - netserver_stats(net, &stats); - - perf_next(); - - if(config.dbg_pref) - perf_dump(&rootscope); - - dbg_msg("server", "send=%8d recv=%8d", - (stats.send_bytes - prev_stats.send_bytes)/reportinterval, - (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); - - prev_stats = stats; - */ - } - - reporttime += time_freq()*reportinterval; - } - - /* wait for incomming data */ - net_socket_read_wait(netserver_socket(net), 5); - } - } - - mods_shutdown(); - map_unload(); - - if(current_map_data) - mem_free(current_map_data); - return 0; -} - -static void con_kick(void *result, void *user_data) -{ - server_kick(console_arg_int(result, 0), "kicked by console"); -} - -static int str_allnum(const char *str) -{ - while(*str) - { - if(!(*str >= '0' && *str <= '9')) - return 0; - str++; - } - return 1; -} - -static void con_ban(void *result, void *user_data) -{ - NETADDR addr; - char addrstr[128]; - const char *str = console_arg_string(result, 0); - int minutes = 30; - - if(console_arg_num(result) > 1) - minutes = console_arg_int(result, 1); - - if(net_addr_from_str(&addr, str) == 0) - server_ban_add(addr, minutes*60); - else if(str_allnum(str)) - { - NETADDR addr; - int cid = atoi(str); - - if(cid < 0 || cid > MAX_CLIENTS || clients[cid].state == SRVCLIENT_STATE_EMPTY) - { - dbg_msg("server", "invalid client id"); - return; - } - - netserver_client_addr(net, cid, &addr); - server_ban_add(addr, minutes*60); - } - - addr.port = 0; - net_addr_str(&addr, addrstr, sizeof(addrstr)); - - if(minutes) - dbg_msg("server", "banned %s for %d minutes", addrstr, minutes); - else - dbg_msg("server", "banned %s for life", addrstr); -} - -static void con_unban(void *result, void *user_data) -{ - NETADDR addr; - const char *str = console_arg_string(result, 0); - - if(net_addr_from_str(&addr, str) == 0) - server_ban_remove(addr); - else - dbg_msg("server", "invalid network address"); -} - -static void con_bans(void *result, void *user_data) -{ - int i; - unsigned now = time_timestamp(); - NETBANINFO info; - NETADDR addr; - int num = netserver_ban_num(net); - for(i = 0; i < num; i++) - { - unsigned t; - netserver_ban_get(net, i, &info); - addr = info.addr; - - if(info.expires == 0xffffffff) - { - dbg_msg("server", "#%d %d.%d.%d.%d for life", i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]); - } - else - { - t = info.expires - now; - dbg_msg("server", "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], t/60, t%60); - } - } - dbg_msg("server", "%d ban(s)", num); -} - -static void con_status(void *result, void *user_data) -{ - int i; - NETADDR addr; - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state == SRVCLIENT_STATE_INGAME) - { - netserver_client_addr(net, i, &addr); - dbg_msg("server", "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d", - i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port, - clients[i].name, clients[i].score); - } - } -} - -static void con_shutdown(void *result, void *user_data) -{ - run_server = 0; -} - -static void con_record(void *result, void *user_data) -{ - char filename[512]; - str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(result, 0)); - demorec_record_start(filename, mods_net_version(), current_map, current_map_crc, "server"); -} - -static void con_stoprecord(void *result, void *user_data) -{ - demorec_record_stop(); -} - -static void server_register_commands() -{ - MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, con_kick, 0, ""); - MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, con_ban, 0, ""); - MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, con_unban, 0, ""); - MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, con_bans, 0, ""); - MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, con_status, 0, ""); - MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, con_shutdown, 0, ""); - - MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_SERVER, con_record, 0, ""); - MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, con_stoprecord, 0, ""); -} - -int main(int argc, char **argv) -{ -#if defined(CONF_FAMILY_WINDOWS) - int i; - for(i = 1; i < argc; i++) - { - if(strcmp("-s", argv[i]) == 0 || strcmp("--silent", argv[i]) == 0) - { - ShowWindow(GetConsoleWindow(), SW_HIDE); - break; - } - } -#endif - - /* init the engine */ - dbg_msg("server", "starting..."); - engine_init("Teeworlds"); - - /* register all console commands */ - server_register_commands(); - mods_console_init(); - - /* parse the command line arguments */ - engine_parse_arguments(argc, argv); - - /* run the server */ - server_run(); - return 0; -} - diff --git a/src/engine/server/es_server.cpp b/src/engine/server/es_server.cpp new file mode 100644 index 00000000..cc0e6e0f --- /dev/null +++ b/src/engine/server/es_server.cpp @@ -0,0 +1,1969 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#if defined(CONF_FAMILY_WINDOWS) + #define _WIN32_WINNT 0x0500 + #include +#endif + + +extern int register_process_packet(CNetChunk *pPacket); +extern int register_update(); + +static const char *str_ltrim(const char *str) +{ + while(*str && *str <= 32) + str++; + return str; +} + +static void str_rtrim(char *str) +{ + int i = str_length(str); + while(i >= 0) + { + if(str[i] > 32) + break; + str[i] = 0; + i--; + } +} + + +class CSnapIDPool +{ + enum + { + MAX_IDS = 16*1024, + }; + + class CID + { + public: + short m_Next; + short m_State; /* 0 = free, 1 = alloced, 2 = timed */ + int m_Timeout; + }; + + CID m_aIDs[MAX_IDS]; + + int m_FirstFree; + int m_FirstTimed; + int m_LastTimed; + int m_Usage; + int m_InUsage; + +public: + + CSnapIDPool() + { + Reset(); + } + + void Reset() + { + for(int i = 0; i < MAX_IDS; i++) + { + m_aIDs[i].m_Next = i+1; + m_aIDs[i].m_State = 0; + } + + m_aIDs[MAX_IDS-1].m_Next = -1; + m_FirstFree = 0; + m_FirstTimed = -1; + m_LastTimed = -1; + m_Usage = 0; + m_InUsage = 0; + } + + + void RemoveFirstTimeout() + { + int NextTimed = m_aIDs[m_FirstTimed].m_Next; + + /* add it to the free list */ + m_aIDs[m_FirstTimed].m_Next = m_FirstFree; + m_aIDs[m_FirstTimed].m_State = 0; + m_FirstFree = m_FirstTimed; + + /* remove it from the timed list */ + m_FirstTimed = NextTimed; + if(m_FirstTimed == -1) + m_LastTimed = -1; + + m_Usage--; + } + + int NewID() + { + int64 now = time_get(); + + /* process timed ids */ + while(m_FirstTimed != -1 && m_aIDs[m_FirstTimed].m_Timeout < now) + RemoveFirstTimeout(); + + int id = m_FirstFree; + dbg_assert(id != -1, "id error"); + m_FirstFree = m_aIDs[m_FirstFree].m_Next; + m_aIDs[id].m_State = 1; + m_Usage++; + m_InUsage++; + return id; + } + + void TimeoutIDs() + { + /* process timed ids */ + while(m_FirstTimed != -1) + RemoveFirstTimeout(); + } + + void FreeID(int id) + { + dbg_assert(m_aIDs[id].m_State == 1, "id is not alloced"); + + m_InUsage--; + m_aIDs[id].m_State = 2; + m_aIDs[id].m_Timeout = time_get()+time_freq()*5; + m_aIDs[id].m_Next = -1; + + if(m_LastTimed != -1) + { + m_aIDs[m_LastTimed].m_Next = id; + m_LastTimed = id; + } + else + { + m_FirstTimed = id; + m_LastTimed = id; + } + } +}; + +#if 0 +class IGameServer +{ +public: + /* + Function: mods_console_init + TODO + */ + virtual void ConsoleInit(); + + /* + Function: Init + Called when the server is started. + + Remarks: + It's called after the map is loaded so all map items are available. + */ + void Init() = 0; + + /* + Function: Shutdown + Called when the server quits. + + Remarks: + Should be used to clean up all resources used. + */ + void Shutdown() = 0; + + /* + Function: ClientEnter + Called when a client has joined the game. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + + Remarks: + It's called when the client is finished loading and should enter gameplay. + */ + void ClientEnter(int cid) = 0; + + /* + Function: ClientDrop + Called when a client drops from the server. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS + */ + void ClientDrop(int cid) = 0; + + /* + Function: ClientDirectInput + Called when the server recives new input from a client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + input - Pointer to the input data. + size - Size of the data. (NOT IMPLEMENTED YET) + */ + void ClientDirectInput(int cid, void *input) = 0; + + /* + Function: ClientPredictedInput + Called when the server applys the predicted input on the client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + input - Pointer to the input data. + size - Size of the data. (NOT IMPLEMENTED YET) + */ + void ClientPredictedInput(int cid, void *input) = 0; + + + /* + Function: Tick + Called with a regular interval to progress the gameplay. + + Remarks: + The SERVER_TICK_SPEED tells the number of ticks per second. + */ + void Tick() = 0; + + /* + Function: Presnap + Called before the server starts to construct snapshots for the clients. + */ + void Presnap() = 0; + + /* + Function: Snap + Called to create the snapshot for a client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + + Remarks: + The game should make a series of calls to to construct + the snapshot for the client. + */ + void Snap(int cid) = 0; + + /* + Function: PostSnap + Called after the server is done sending the snapshots. + */ + void PostSnap() = 0; + + /* + Function: ClientConnected + TODO + + Arguments: + arg1 - desc + + Returns: + + See Also: + + */ + void ClientConnected(int client_id) = 0; + + + /* + Function: NetVersion + TODO + + Arguments: + arg1 - desc + + Returns: + + See Also: + + */ + const char *NetVersion() = 0; + + /* + Function: Version + TODO + + Arguments: + arg1 - desc + + Returns: + + See Also: + + */ + const char *Version() = 0; + + /* + Function: Message + TODO + + Arguments: + arg1 - desc + + Returns: + + See Also: + + */ + void Message(int msg, int client_id) = 0; +}; + +#endif +/* +class IServer +{ +public: + BanAdd + BanRemove + TickSpeed + Tick + Kick + SetBrowseInfo + SetClientScore + SetClientName + GetClientInfo + LatestInput + ClientName + + SendMessage() + + Map + + virtual int NewSnapID() = 0; + virtual int FreeSnapID(int i) = 0; +};*/ + +class CServer +{ +public: + /* */ + class CClient + { + public: + + enum + { + STATE_EMPTY = 0, + STATE_AUTH, + STATE_CONNECTING, + STATE_READY, + STATE_INGAME, + + SNAPRATE_INIT=0, + SNAPRATE_FULL, + SNAPRATE_RECOVER + }; + + class CInput + { + public: + int m_aData[MAX_INPUT_SIZE]; + int m_GameTick; /* the tick that was chosen for the input */ + }; + + /* connection state info */ + int m_State; + int m_Latency; + int m_SnapRate; + + int m_LastAckedSnapshot; + int m_LastInputTick; + CSnapshotStorage m_Snapshots; + + CInput m_LatestInput; + CInput m_aInputs[200]; /* TODO: handle input better */ + int m_CurrentInput; + + char m_aName[MAX_NAME_LENGTH]; + char m_aClan[MAX_CLANNAME_LENGTH]; + int m_Score; + int m_Authed; + + void Reset() + { + /* reset input */ + for(int i = 0; i < 200; i++) + m_aInputs[i].m_GameTick = -1; + m_CurrentInput = 0; + mem_zero(&m_LatestInput, sizeof(m_LatestInput)); + + m_Snapshots.PurgeAll(); + m_LastAckedSnapshot = -1; + m_LastInputTick = -1; + m_SnapRate = CClient::SNAPRATE_INIT; + m_Score = 0; + } + }; + + CClient m_aClients[MAX_CLIENTS]; + + CSnapshotBuilder m_SnapshotBuilder; + CSnapIDPool m_IDPool; + CNetServer m_NetServer; + + int64 m_GameStartTime; + int m_CurrentTick; + int m_RunServer; + + char m_aBrowseinfoGametype[16]; + int m_BrowseinfoProgression; + + int64 m_Lastheartbeat; + /*static NETADDR4 master_server;*/ + + char m_aCurrentMap[64]; + int m_CurrentMapCrc; + unsigned char *m_pCurrentMapData; + int m_CurrentMapSize; + + CServer() + { + m_CurrentTick = 0; + m_RunServer = 1; + + mem_zero(m_aBrowseinfoGametype, sizeof(m_aBrowseinfoGametype)); + m_BrowseinfoProgression = -1; + + m_pCurrentMapData = 0; + m_CurrentMapSize = 0; + } + + + int TrySetClientName(int ClientID, const char *pName) + { + int i; + char aTrimmedName[64]; + + /* trim the name */ + str_copy(aTrimmedName, str_ltrim(pName), sizeof(aTrimmedName)); + str_rtrim(aTrimmedName); + dbg_msg("", "'%s' -> '%s'", pName, aTrimmedName); + pName = aTrimmedName; + + + /* check for empty names */ + if(!pName[0]) + return -1; + + /* make sure that two clients doesn't have the same name */ + for(i = 0; i < MAX_CLIENTS; i++) + if(i != ClientID && m_aClients[i].m_State >= CClient::STATE_READY) + { + if(strcmp(pName, m_aClients[i].m_aName) == 0) + return -1; + } + + /* set the client name */ + str_copy(m_aClients[ClientID].m_aName, pName, MAX_NAME_LENGTH); + return 0; + } + + + + void SetClientName(int ClientID, const char *pName) + { + if(ClientID < 0 || ClientID > MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY) + return; + + if(!pName) + return; + + char aNameTry[MAX_NAME_LENGTH]; + str_copy(aNameTry, pName, MAX_NAME_LENGTH); + if(TrySetClientName(ClientID, aNameTry)) + { + /* auto rename */ + for(int i = 1;; i++) + { + str_format(aNameTry, MAX_NAME_LENGTH, "(%d)%s", i, pName); + if(TrySetClientName(ClientID, aNameTry) == 0) + break; + } + } + } + + void SetClientScore(int ClientID, int Score) + { + if(ClientID < 0 || ClientID > MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY) + return; + m_aClients[ClientID].m_Score = Score; + } + + void SetBrowseInfo(const char *pGameType, int Progression) + { + str_copy(m_aBrowseinfoGametype, pGameType, sizeof(m_aBrowseinfoGametype)); + m_BrowseinfoProgression = Progression; + if(m_BrowseinfoProgression > 100) + m_BrowseinfoProgression = 100; + if(m_BrowseinfoProgression < -1) + m_BrowseinfoProgression = -1; + } + + void Kick(int ClientID, const char *pReason) + { + if(ClientID < 0 || ClientID > MAX_CLIENTS) + return; + + if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY) + m_NetServer.Drop(ClientID, pReason); + } + + int Tick() + { + return m_CurrentTick; + } + + int64 TickStartTime(int Tick) + { + return m_GameStartTime + (time_freq()*Tick)/SERVER_TICK_SPEED; + } + + int TickSpeed() + { + return SERVER_TICK_SPEED; + } + + int Init() + { + int i; + for(i = 0; i < MAX_CLIENTS; i++) + { + m_aClients[i].m_State = CClient::STATE_EMPTY; + m_aClients[i].m_aName[0] = 0; + m_aClients[i].m_aClan[0] = 0; + m_aClients[i].m_Snapshots.Init(); + } + + m_CurrentTick = 0; + + return 0; + } + + int GetClientInfo(int ClientID, CLIENT_INFO *pInfo) + { + dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid"); + dbg_assert(pInfo != 0, "info can not be null"); + + if(m_aClients[ClientID].m_State == CClient::STATE_INGAME) + { + pInfo->name = m_aClients[ClientID].m_aName; + pInfo->latency = m_aClients[ClientID].m_Latency; + return 1; + } + return 0; + } + + int SendMsg(int ClientID) + { + const MSG_INFO *pInfo = msg_get_info(); + CNetChunk Packet; + if(!pInfo) + return -1; + + mem_zero(&Packet, sizeof(CNetChunk)); + + Packet.m_ClientID = ClientID; + Packet.m_pData = pInfo->data; + Packet.m_DataSize = pInfo->size; + + if(pInfo->flags&MSGFLAG_VITAL) + Packet.m_Flags |= NETSENDFLAG_VITAL; + if(pInfo->flags&MSGFLAG_FLUSH) + Packet.m_Flags |= NETSENDFLAG_FLUSH; + + /* write message to demo recorder */ + if(!(pInfo->flags&MSGFLAG_NORECORD)) + demorec_record_message(pInfo->data, pInfo->size); + + if(!(pInfo->flags&MSGFLAG_NOSEND)) + { + if(ClientID == -1) + { + /* broadcast */ + int i; + for(i = 0; i < MAX_CLIENTS; i++) + if(m_aClients[i].m_State == CClient::STATE_INGAME) + { + Packet.m_ClientID = i; + m_NetServer.Send(&Packet); + } + } + else + m_NetServer.Send(&Packet); + } + return 0; + } + + void DoSnapshot() + { + int i; + + { + static PERFORMACE_INFO scope = {"presnap", 0}; + perf_start(&scope); + mods_presnap(); + perf_end(); + } + + /* create snapshot for demo recording */ + if(demorec_isrecording()) + { + char data[CSnapshot::MAX_SIZE]; + int snapshot_size; + + /* build snap and possibly add some messages */ + m_SnapshotBuilder.Init(); + mods_snap(-1); + snapshot_size = m_SnapshotBuilder.Finish(data); + + /* write snapshot */ + demorec_record_snapshot(Tick(), data, snapshot_size); + } + + /* create snapshots for all clients */ + for(i = 0; i < MAX_CLIENTS; i++) + { + /* client must be ingame to recive snapshots */ + if(m_aClients[i].m_State != CClient::STATE_INGAME) + continue; + + /* this client is trying to recover, don't spam snapshots */ + if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_RECOVER && (Tick()%50) != 0) + continue; + + /* this client is trying to recover, don't spam snapshots */ + if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_INIT && (Tick()%10) != 0) + continue; + + { + char data[CSnapshot::MAX_SIZE]; + char deltadata[CSnapshot::MAX_SIZE]; + char compdata[CSnapshot::MAX_SIZE]; + int snapshot_size; + int crc; + static CSnapshot emptysnap; + CSnapshot *deltashot = &emptysnap; + int deltashot_size; + int delta_tick = -1; + int deltasize; + static PERFORMACE_INFO scope = {"build", 0}; + perf_start(&scope); + + m_SnapshotBuilder.Init(); + + { + static PERFORMACE_INFO scope = {"modsnap", 0}; + perf_start(&scope); + mods_snap(i); + perf_end(); + } + + /* finish snapshot */ + snapshot_size = m_SnapshotBuilder.Finish(data); + crc = ((CSnapshot*)data)->Crc(); + + /* remove old snapshos */ + /* keep 3 seconds worth of snapshots */ + m_aClients[i].m_Snapshots.PurgeUntil(m_CurrentTick-SERVER_TICK_SPEED*3); + + /* save it the snapshot */ + m_aClients[i].m_Snapshots.Add(m_CurrentTick, time_get(), snapshot_size, data, 0); + + /* find snapshot that we can preform delta against */ + emptysnap.m_DataSize = 0; + emptysnap.m_NumItems = 0; + + { + deltashot_size = m_aClients[i].m_Snapshots.Get(m_aClients[i].m_LastAckedSnapshot, 0, &deltashot, 0); + if(deltashot_size >= 0) + delta_tick = m_aClients[i].m_LastAckedSnapshot; + else + { + /* no acked package found, force client to recover rate */ + if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_FULL) + m_aClients[i].m_SnapRate = CClient::SNAPRATE_RECOVER; + } + } + + /* create delta */ + { + static PERFORMACE_INFO scope = {"delta", 0}; + perf_start(&scope); + deltasize = CSnapshot::CreateDelta(deltashot, (CSnapshot*)data, deltadata); + perf_end(); + } + + + if(deltasize) + { + /* compress it */ + int snapshot_size; + const int max_size = MAX_SNAPSHOT_PACKSIZE; + int numpackets; + int n, left; + + { + static PERFORMACE_INFO scope = {"compress", 0}; + /*char buffer[512];*/ + perf_start(&scope); + snapshot_size = intpack_compress(deltadata, deltasize, compdata); + + /*str_hex(buffer, sizeof(buffer), compdata, snapshot_size); + dbg_msg("", "deltasize=%d -> %d : %s", deltasize, snapshot_size, buffer);*/ + + perf_end(); + } + + numpackets = (snapshot_size+max_size-1)/max_size; + + for(n = 0, left = snapshot_size; left; n++) + { + int chunk = left < max_size ? left : max_size; + left -= chunk; + + if(numpackets == 1) + msg_pack_start_system(NETMSG_SNAPSINGLE, MSGFLAG_FLUSH); + else + msg_pack_start_system(NETMSG_SNAP, MSGFLAG_FLUSH); + + msg_pack_int(m_CurrentTick); + msg_pack_int(m_CurrentTick-delta_tick); /* compressed with */ + + if(numpackets != 1) + { + msg_pack_int(numpackets); + msg_pack_int(n); + } + + msg_pack_int(crc); + msg_pack_int(chunk); + msg_pack_raw(&compdata[n*max_size], chunk); + msg_pack_end(); + SendMsg(i); + } + } + else + { + msg_pack_start_system(NETMSG_SNAPEMPTY, MSGFLAG_FLUSH); + msg_pack_int(m_CurrentTick); + msg_pack_int(m_CurrentTick-delta_tick); /* compressed with */ + msg_pack_end(); + SendMsg(i); + } + + perf_end(); + } + } + + mods_postsnap(); + } + + + static int NewClientCallback(int cid, void *pUser) + { + CServer *pThis = (CServer *)pUser; + pThis->m_aClients[cid].m_State = CClient::STATE_AUTH; + pThis->m_aClients[cid].m_aName[0] = 0; + pThis->m_aClients[cid].m_aClan[0] = 0; + pThis->m_aClients[cid].m_Authed = 0; + pThis->m_aClients[cid].Reset(); + return 0; + } + + static int DelClientCallback(int cid, void *pUser) + { + CServer *pThis = (CServer *)pUser; + + /* notify the mod about the drop */ + if(pThis->m_aClients[cid].m_State >= CClient::STATE_READY) + mods_client_drop(cid); + + pThis->m_aClients[cid].m_State = CClient::STATE_EMPTY; + pThis->m_aClients[cid].m_aName[0] = 0; + pThis->m_aClients[cid].m_aClan[0] = 0; + pThis->m_aClients[cid].m_Authed = 0; + pThis->m_aClients[cid].m_Snapshots.PurgeAll(); + return 0; + } + + void SendMap(int cid) + { + msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_string(config.sv_map, 0); + msg_pack_int(m_CurrentMapCrc); + msg_pack_end(); + server_send_msg(cid); + } + + void SendRconLine(int cid, const char *pLine) + { + msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL); + msg_pack_string(pLine, 512); + msg_pack_end(); + server_send_msg(cid); + } + + static void SendRconLineAuthed(const char *pLine, void *pUser) + { + CServer *pThis = (CServer *)pUser; + static volatile int reentry_guard = 0; + int i; + + if(reentry_guard) return; + reentry_guard++; + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed) + pThis->SendRconLine(i, pLine); + } + + reentry_guard--; + } + + void ProcessClientPacket(CNetChunk *pPacket) + { + int cid = pPacket->m_ClientID; + NETADDR addr; + + int sys; + int msg = msg_unpack_start(pPacket->m_pData, pPacket->m_DataSize, &sys); + + if(m_aClients[cid].m_State == CClient::STATE_AUTH) + { + if(sys && msg == NETMSG_INFO) + { + char version[64]; + const char *password; + str_copy(version, msg_unpack_string(), 64); + if(strcmp(version, mods_net_version()) != 0) + { + /* OH FUCK! wrong version, drop him */ + char reason[256]; + str_format(reason, sizeof(reason), "wrong version. server is running '%s' and client '%s'.", mods_net_version(), version); + m_NetServer.Drop(cid, reason); + return; + } + + str_copy(m_aClients[cid].m_aName, msg_unpack_string(), MAX_NAME_LENGTH); + str_copy(m_aClients[cid].m_aClan, msg_unpack_string(), MAX_CLANNAME_LENGTH); + password = msg_unpack_string(); + + if(config.password[0] != 0 && strcmp(config.password, password) != 0) + { + /* wrong password */ + m_NetServer.Drop(cid, "wrong password"); + return; + } + + m_aClients[cid].m_State = CClient::STATE_CONNECTING; + SendMap(cid); + } + } + else + { + if(sys) + { + /* system message */ + if(msg == NETMSG_REQUEST_MAP_DATA) + { + int chunk = msg_unpack_int(); + int chunk_size = 1024-128; + int offset = chunk * chunk_size; + int last = 0; + + /* drop faulty map data requests */ + if(chunk < 0 || offset > m_CurrentMapSize) + return; + + if(offset+chunk_size >= m_CurrentMapSize) + { + chunk_size = m_CurrentMapSize-offset; + if(chunk_size < 0) + chunk_size = 0; + last = 1; + } + + msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_int(last); + msg_pack_int(m_CurrentMapSize); + msg_pack_int(chunk_size); + msg_pack_raw(&m_pCurrentMapData[offset], chunk_size); + msg_pack_end(); + server_send_msg(cid); + + if(config.debug) + dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size); + } + else if(msg == NETMSG_READY) + { + if(m_aClients[cid].m_State == CClient::STATE_CONNECTING) + { + addr = m_NetServer.ClientAddr(cid); + + dbg_msg("server", "player is ready. cid=%x ip=%d.%d.%d.%d", + cid, + addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] + ); + m_aClients[cid].m_State = CClient::STATE_READY; + mods_connected(cid); + } + } + else if(msg == NETMSG_ENTERGAME) + { + if(m_aClients[cid].m_State == CClient::STATE_READY) + { + addr = m_NetServer.ClientAddr(cid); + + dbg_msg("server", "player has entered the game. cid=%x ip=%d.%d.%d.%d", + cid, + addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] + ); + m_aClients[cid].m_State = CClient::STATE_INGAME; + mods_client_enter(cid); + } + } + else if(msg == NETMSG_INPUT) + { + int tick, size, i; + CClient::CInput *pInput; + int64 tagtime; + + m_aClients[cid].m_LastAckedSnapshot = msg_unpack_int(); + tick = msg_unpack_int(); + size = msg_unpack_int(); + + /* check for errors */ + if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE) + return; + + if(m_aClients[cid].m_LastAckedSnapshot > 0) + m_aClients[cid].m_SnapRate = CClient::SNAPRATE_FULL; + + if(m_aClients[cid].m_Snapshots.Get(m_aClients[cid].m_LastAckedSnapshot, &tagtime, 0, 0) >= 0) + m_aClients[cid].m_Latency = (int)(((time_get()-tagtime)*1000)/time_freq()); + + /* add message to report the input timing */ + /* skip packets that are old */ + if(tick > m_aClients[cid].m_LastInputTick) + { + int time_left = ((TickStartTime(tick)-time_get())*1000) / time_freq(); + msg_pack_start_system(NETMSG_INPUTTIMING, 0); + msg_pack_int(tick); + msg_pack_int(time_left); + msg_pack_end(); + server_send_msg(cid); + } + + m_aClients[cid].m_LastInputTick = tick; + + pInput = &m_aClients[cid].m_aInputs[m_aClients[cid].m_CurrentInput]; + + if(tick <= server_tick()) + tick = server_tick()+1; + + pInput->m_GameTick = tick; + + for(i = 0; i < size/4; i++) + pInput->m_aData[i] = msg_unpack_int(); + + mem_copy(m_aClients[cid].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int)); + + m_aClients[cid].m_CurrentInput++; + m_aClients[cid].m_CurrentInput %= 200; + + /* call the mod with the fresh input data */ + if(m_aClients[cid].m_State == CClient::STATE_INGAME) + mods_client_direct_input(cid, m_aClients[cid].m_LatestInput.m_aData); + } + else if(msg == NETMSG_RCON_CMD) + { + const char *cmd = msg_unpack_string(); + + if(msg_unpack_error() == 0 && m_aClients[cid].m_Authed) + { + dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); + console_execute_line(cmd); + } + } + else if(msg == NETMSG_RCON_AUTH) + { + const char *pw; + msg_unpack_string(); /* login name, not used */ + pw = msg_unpack_string(); + + if(msg_unpack_error() == 0) + { + if(config.sv_rcon_password[0] == 0) + { + SendRconLine(cid, "No rcon password set on server. Set sv_rcon_password to enable the remote console."); + } + else if(strcmp(pw, config.sv_rcon_password) == 0) + { + msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL); + msg_pack_int(1); + msg_pack_end(); + server_send_msg(cid); + + m_aClients[cid].m_Authed = 1; + SendRconLine(cid, "Authentication successful. Remote console access granted."); + dbg_msg("server", "cid=%d authed", cid); + } + else + { + SendRconLine(cid, "Wrong password."); + } + } + } + else if(msg == NETMSG_PING) + { + msg_pack_start_system(NETMSG_PING_REPLY, 0); + msg_pack_end(); + server_send_msg(cid); + } + else + { + char hex[] = "0123456789ABCDEF"; + char buf[512]; + int b; + + for(b = 0; b < pPacket->m_DataSize && b < 32; b++) + { + buf[b*3] = hex[((const unsigned char *)pPacket->m_pData)[b]>>4]; + buf[b*3+1] = hex[((const unsigned char *)pPacket->m_pData)[b]&0xf]; + buf[b*3+2] = ' '; + buf[b*3+3] = 0; + } + + dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, pPacket->m_DataSize); + dbg_msg("server", "%s", buf); + + } + } + else + { + /* game message */ + if(m_aClients[cid].m_State >= CClient::STATE_READY) + mods_message(msg, cid); + } + } + } + + void SendServerInfo(NETADDR *addr, int token) + { + CNetChunk Packet; + CPacker p; + char buf[128]; + + /* count the players */ + int player_count = 0; + int i; + for(i = 0; i < MAX_CLIENTS; i++) + { + if(m_aClients[i].m_State != CClient::STATE_EMPTY) + player_count++; + } + + p.Reset(); + + if(token >= 0) + { + /* new token based format */ + p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); + str_format(buf, sizeof(buf), "%d", token); + p.AddString(buf, 6); + } + else + { + /* old format */ + p.AddRaw(SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)); + } + + p.AddString(mods_version(), 32); + p.AddString(config.sv_name, 64); + p.AddString(config.sv_map, 32); + + /* gametype */ + p.AddString(m_aBrowseinfoGametype, 16); + + /* flags */ + i = 0; + if(config.password[0]) /* password set */ + i |= SRVFLAG_PASSWORD; + str_format(buf, sizeof(buf), "%d", i); + p.AddString(buf, 2); + + /* progression */ + str_format(buf, sizeof(buf), "%d", m_BrowseinfoProgression); + p.AddString(buf, 4); + + str_format(buf, sizeof(buf), "%d", player_count); p.AddString(buf, 3); /* num players */ + str_format(buf, sizeof(buf), "%d", m_NetServer.MaxClients()); p.AddString(buf, 3); /* max players */ + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(m_aClients[i].m_State != CClient::STATE_EMPTY) + { + p.AddString(m_aClients[i].m_aName, 48); /* player name */ + str_format(buf, sizeof(buf), "%d", m_aClients[i].m_Score); p.AddString(buf, 6); /* player score */ + } + } + + + Packet.m_ClientID = -1; + Packet.m_Address = *addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = p.Size(); + Packet.m_pData = p.Data(); + m_NetServer.Send(&Packet); + } + + int BanAdd(NETADDR Addr, int Seconds) + { + return m_NetServer.BanAdd(Addr, Seconds); + } + + int BanRemove(NETADDR Addr) + { + return m_NetServer.BanRemove(Addr); + } + + + void PumpNetwork() + { + CNetChunk Packet; + + m_NetServer.Update(); + + /* process packets */ + while(m_NetServer.Recv(&Packet)) + { + if(Packet.m_ClientID == -1) + { + /* stateless */ + if(!register_process_packet(&Packet)) + { + if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 && + memcmp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) + { + SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]); + } + + + if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) && + memcmp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) + { + SendServerInfo(&Packet.m_Address, -1); + } + } + } + else + ProcessClientPacket(&Packet); + } + } + + int LoadMap(const char *pMapName) + { + DATAFILE *df; + char buf[512]; + str_format(buf, sizeof(buf), "maps/%s.map", pMapName); + df = datafile_load(buf); + if(!df) + return 0; + + /* stop recording when we change map */ + demorec_record_stop(); + + /* reinit snapshot ids */ + m_IDPool.TimeoutIDs(); + + /* get the crc of the map */ + m_CurrentMapCrc = datafile_crc(buf); + dbg_msg("server", "%s crc is %08x", buf, m_CurrentMapCrc); + + str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap)); + map_set(df); + + /* load compelate map into memory for download */ + { + IOHANDLE file = engine_openfile(buf, IOFLAG_READ); + m_CurrentMapSize = (int)io_length(file); + if(m_pCurrentMapData) + mem_free(m_pCurrentMapData); + m_pCurrentMapData = (unsigned char *)mem_alloc(m_CurrentMapSize, 1); + io_read(file, m_pCurrentMapData, m_CurrentMapSize); + io_close(file); + } + return 1; + } + + int Run() + { + NETADDR bindaddr; + + //snap_init_id(); + net_init(); + + /* */ + console_register_print_callback(SendRconLineAuthed, this); + + /* load map */ + if(!LoadMap(config.sv_map)) + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + return -1; + } + + /* start server */ + /* TODO: IPv6 support */ + if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0) + { + /* sweet! */ + bindaddr.port = config.sv_port; + } + else + { + mem_zero(&bindaddr, sizeof(bindaddr)); + bindaddr.port = config.sv_port; + } + + + if(!m_NetServer.Open(bindaddr, config.sv_max_clients, 0)) + { + dbg_msg("server", "couldn't open socket. port might already be in use"); + return -1; + } + + m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this); + + dbg_msg("server", "server name is '%s'", config.sv_name); + + mods_init(); + dbg_msg("server", "version %s", mods_net_version()); + + /* start game */ + { + int64 reporttime = time_get(); + int reportinterval = 3; + + m_Lastheartbeat = 0; + m_GameStartTime = time_get(); + + if(config.debug) + dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024); + + while(m_RunServer) + { + static PERFORMACE_INFO rootscope = {"root", 0}; + int64 t = time_get(); + int new_ticks = 0; + + perf_start(&rootscope); + + /* load new map TODO: don't poll this */ + if(strcmp(config.sv_map, m_aCurrentMap) != 0 || config.sv_map_reload) + { + config.sv_map_reload = 0; + + /* load map */ + if(LoadMap(config.sv_map)) + { + int c; + + /* new map loaded */ + mods_shutdown(); + + for(c = 0; c < MAX_CLIENTS; c++) + { + if(m_aClients[c].m_State == CClient::STATE_EMPTY) + continue; + + SendMap(c); + m_aClients[c].Reset(); + m_aClients[c].m_State = CClient::STATE_CONNECTING; + } + + m_GameStartTime = time_get(); + m_CurrentTick = 0; + mods_init(); + } + else + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + config_set_sv_map(&config, m_aCurrentMap); + } + } + + while(t > TickStartTime(m_CurrentTick+1)) + { + m_CurrentTick++; + new_ticks++; + + /* apply new input */ + { + static PERFORMACE_INFO scope = {"input", 0}; + int c, i; + + perf_start(&scope); + + for(c = 0; c < MAX_CLIENTS; c++) + { + if(m_aClients[c].m_State == CClient::STATE_EMPTY) + continue; + for(i = 0; i < 200; i++) + { + if(m_aClients[c].m_aInputs[i].m_GameTick == server_tick()) + { + if(m_aClients[c].m_State == CClient::STATE_INGAME) + mods_client_predicted_input(c, m_aClients[c].m_aInputs[i].m_aData); + break; + } + } + } + + perf_end(); + } + + /* progress game */ + { + static PERFORMACE_INFO scope = {"tick", 0}; + perf_start(&scope); + mods_tick(); + perf_end(); + } + } + + /* snap game */ + if(new_ticks) + { + if(config.sv_high_bandwidth || (m_CurrentTick%2) == 0) + { + static PERFORMACE_INFO scope = {"snap", 0}; + perf_start(&scope); + DoSnapshot(); + perf_end(); + } + } + + /* master server stuff */ + register_update(); + + + { + static PERFORMACE_INFO scope = {"net", 0}; + perf_start(&scope); + PumpNetwork(); + perf_end(); + } + + perf_end(); + + if(reporttime < time_get()) + { + if(config.debug) + { + /* + static NETSTATS prev_stats; + NETSTATS stats; + netserver_stats(net, &stats); + + perf_next(); + + if(config.dbg_pref) + perf_dump(&rootscope); + + dbg_msg("server", "send=%8d recv=%8d", + (stats.send_bytes - prev_stats.send_bytes)/reportinterval, + (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); + + prev_stats = stats; + */ + } + + reporttime += time_freq()*reportinterval; + } + + /* wait for incomming data */ + net_socket_read_wait(m_NetServer.Socket(), 5); + } + } + + mods_shutdown(); + map_unload(); + + if(m_pCurrentMapData) + mem_free(m_pCurrentMapData); + return 0; + } + + static void ConKick(void *pResult, void *pUser) + { + ((CServer *)pUser)->Kick(console_arg_int(pResult, 0), "kicked by console"); + } + + static int str_allnum(const char *str) + { + while(*str) + { + if(!(*str >= '0' && *str <= '9')) + return 0; + str++; + } + return 1; + } + + static void ConBan(void *pResult, void *pUser) + { + NETADDR addr; + char addrstr[128]; + const char *str = console_arg_string(pResult, 0); + int minutes = 30; + + if(console_arg_num(pResult) > 1) + minutes = console_arg_int(pResult, 1); + + if(net_addr_from_str(&addr, str) == 0) + ((CServer *)pUser)->BanAdd(addr, minutes*60); + else if(str_allnum(str)) + { + NETADDR addr; + int cid = atoi(str); + + if(cid < 0 || cid > MAX_CLIENTS || ((CServer *)pUser)->m_aClients[cid].m_State == CClient::STATE_EMPTY) + { + dbg_msg("server", "invalid client id"); + return; + } + + addr = ((CServer *)pUser)->m_NetServer.ClientAddr(cid); + ((CServer *)pUser)->BanAdd(addr, minutes*60); + } + + addr.port = 0; + net_addr_str(&addr, addrstr, sizeof(addrstr)); + + if(minutes) + dbg_msg("server", "banned %s for %d minutes", addrstr, minutes); + else + dbg_msg("server", "banned %s for life", addrstr); + } + + static void ConUnban(void *result, void *pUser) + { + NETADDR addr; + const char *str = console_arg_string(result, 0); + + if(net_addr_from_str(&addr, str) == 0) + ((CServer *)pUser)->BanRemove(addr); + else + dbg_msg("server", "invalid network address"); + } + + static void ConBans(void *pResult, void *pUser) + { + unsigned now = time_timestamp(); + + int num = ((CServer *)pUser)->m_NetServer.BanNum(); + for(int i = 0; i < num; i++) + { + CNetServer::CBanInfo Info; + ((CServer *)pUser)->m_NetServer.BanGet(i, &Info); + NETADDR Addr = Info.m_Addr; + + if(Info.m_Expires == -1) + { + dbg_msg("server", "#%d %d.%d.%d.%d for life", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]); + } + else + { + unsigned t = Info.m_Expires - now; + dbg_msg("server", "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], t/60, t%60); + } + } + dbg_msg("server", "%d ban(s)", num); + } + + static void ConStatus(void *pResult, void *pUser) + { + int i; + NETADDR addr; + for(i = 0; i < MAX_CLIENTS; i++) + { + if(((CServer *)pUser)->m_aClients[i].m_State == CClient::STATE_INGAME) + { + addr = ((CServer *)pUser)->m_NetServer.ClientAddr(i); + dbg_msg("server", "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d", + i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port, + ((CServer *)pUser)->m_aClients[i].m_aName, ((CServer *)pUser)->m_aClients[i].m_Score); + } + } + } + + static void ConShutdown(void *pResult, void *pUser) + { + ((CServer *)pUser)->m_RunServer = 0; + } + + static void ConRecord(void *pResult, void *pUser) + { + char filename[512]; + str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(pResult, 0)); + demorec_record_start(filename, mods_net_version(), ((CServer *)pUser)->m_aCurrentMap, ((CServer *)pUser)->m_CurrentMapCrc, "server"); + } + + static void ConStopRecord(void *pResult, void *pUser) + { + demorec_record_stop(); + } + + + void RegisterCommands() + { + MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, ConKick, this, ""); + MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, ConBan, this, ""); + MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, ConUnban, this, ""); + MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, ConBans, this, ""); + MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, ConStatus, this, ""); + MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, ""); + + MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_SERVER, ConRecord, this, ""); + MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, ""); + } + +}; + + +// UGLY UGLY HACK for now +CServer g_Server; +CNetServer *m_pNetServer; + +int server_tick() { return g_Server.Tick(); } +int server_tickspeed() { return g_Server.TickSpeed(); } +int snap_new_id() { return g_Server.m_IDPool.NewID(); } +void snap_free_id(int id) { return g_Server.m_IDPool.FreeID(id); } +int server_send_msg(int client_id) { return g_Server.SendMsg(client_id); } +void server_setbrowseinfo(const char *game_type, int progression) { g_Server.SetBrowseInfo(game_type, progression); } +void server_setclientname(int client_id, const char *name) { g_Server.SetClientName(client_id, name); } +void server_setclientscore(int client_id, int score) { g_Server.SetClientScore(client_id, score); } + +int server_getclientinfo(int client_id, CLIENT_INFO *info) { return g_Server.GetClientInfo(client_id, info); } + +void *snap_new_item(int type, int id, int size) +{ + dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); + dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); + return g_Server.m_SnapshotBuilder.NewItem(type, id, size); +} + +int *server_latestinput(int client_id, int *size) +{ + if(client_id < 0 || client_id > MAX_CLIENTS || g_Server.m_aClients[client_id].m_State < CServer::CClient::STATE_READY) + return 0; + return g_Server.m_aClients[client_id].m_LatestInput.m_aData; +} + +const char *server_clientname(int client_id) +{ + if(client_id < 0 || client_id > MAX_CLIENTS || g_Server.m_aClients[client_id].m_State < CServer::CClient::STATE_READY) + return "(invalid client)"; + return g_Server.m_aClients[client_id].m_aName; +} + +#if 0 + + +static void reset_client(int cid) +{ + /* reset input */ + int i; + for(i = 0; i < 200; i++) + m_aClients[cid].m_aInputs[i].m_GameTick = -1; + m_aClients[cid].m_CurrentInput = 0; + mem_zero(&m_aClients[cid].m_Latestinput, sizeof(m_aClients[cid].m_Latestinput)); + + m_aClients[cid].m_Snapshots.PurgeAll(); + m_aClients[cid].m_LastAckedSnapshot = -1; + m_aClients[cid].m_LastInputTick = -1; + m_aClients[cid].m_SnapRate = CClient::SNAPRATE_INIT; + m_aClients[cid].m_Score = 0; + +} + + +static int new_client_callback(int cid, void *user) +{ + m_aClients[cid].state = CClient::STATE_AUTH; + m_aClients[cid].name[0] = 0; + m_aClients[cid].clan[0] = 0; + m_aClients[cid].authed = 0; + reset_client(cid); + return 0; +} + +static int del_client_callback(int cid, void *user) +{ + /* notify the mod about the drop */ + if(m_aClients[cid].state >= CClient::STATE_READY) + mods_client_drop(cid); + + m_aClients[cid].state = CClient::STATE_EMPTY; + m_aClients[cid].name[0] = 0; + m_aClients[cid].clan[0] = 0; + m_aClients[cid].authed = 0; + m_aClients[cid].snapshots.PurgeAll(); + return 0; +} +static void server_send_map(int cid) +{ + msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_string(config.sv_map, 0); + msg_pack_int(current_map_crc); + msg_pack_end(); + server_send_msg(cid); +} + +static void server_send_rcon_line(int cid, const char *line) +{ + msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL); + msg_pack_string(line, 512); + msg_pack_end(); + server_send_msg(cid); +} + +static void server_send_rcon_line_authed(const char *line, void *user_data) +{ + static volatile int reentry_guard = 0; + int i; + + if(reentry_guard) return; + reentry_guard++; + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(m_aClients[i].state != CClient::STATE_EMPTY && m_aClients[i].authed) + server_send_rcon_line(i, line); + } + + reentry_guard--; +} + +static void server_pump_network() +{ + CNetChunk Packet; + + m_NetServer.Update(); + + /* process packets */ + while(m_NetServer.Recv(&Packet)) + { + if(Packet.m_ClientID == -1) + { + /* stateless */ + if(!register_process_packet(&Packet)) + { + if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 && + memcmp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) + { + server_send_serverinfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]); + } + + + if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) && + memcmp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) + { + server_send_serverinfo(&Packet.m_Address, -1); + } + } + } + else + server_process_client_packet(&Packet); + } +} + +static int server_load_map(const char *mapname) +{ + DATAFILE *df; + char buf[512]; + str_format(buf, sizeof(buf), "maps/%s.map", mapname); + df = datafile_load(buf); + if(!df) + return 0; + + /* stop recording when we change map */ + demorec_record_stop(); + + /* reinit snapshot ids */ + snap_timeout_ids(); + + /* get the crc of the map */ + current_map_crc = datafile_crc(buf); + dbg_msg("server", "%s crc is %08x", buf, current_map_crc); + + str_copy(current_map, mapname, sizeof(current_map)); + map_set(df); + + /* load compelate map into memory for download */ + { + IOHANDLE file = engine_openfile(buf, IOFLAG_READ); + current_map_size = (int)io_length(file); + if(current_map_data) + mem_free(current_map_data); + current_map_data = (unsigned char *)mem_alloc(current_map_size, 1); + io_read(file, current_map_data, current_map_size); + io_close(file); + } + return 1; +} + +static int server_run() +{ + NETADDR bindaddr; + + snap_init_id(); + net_init(); + + /* */ + console_register_print_callback(server_send_rcon_line_authed, 0); + + /* load map */ + if(!server_load_map(config.sv_map)) + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + return -1; + } + + /* start server */ + /* TODO: IPv6 support */ + if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0) + { + /* sweet! */ + bindaddr.port = config.sv_port; + } + else + { + mem_zero(&bindaddr, sizeof(bindaddr)); + bindaddr.port = config.sv_port; + } + + + if(!m_NetServer.Open(bindaddr, config.sv_max_clients, 0)) + { + dbg_msg("server", "couldn't open socket. port might already be in use"); + return -1; + } + + m_NetServer.SetCallbacks(new_client_callback, del_client_callback, this); + + dbg_msg("server", "server name is '%s'", config.sv_name); + + mods_init(); + dbg_msg("server", "version %s", mods_net_version()); + + /* start game */ + { + int64 reporttime = time_get(); + int reportinterval = 3; + + lastheartbeat = 0; + game_start_time = time_get(); + + if(config.debug) + dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024); + + while(run_server) + { + static PERFORMACE_INFO rootscope = {"root", 0}; + int64 t = time_get(); + int new_ticks = 0; + + perf_start(&rootscope); + + /* load new map TODO: don't poll this */ + if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) + { + config.sv_map_reload = 0; + + /* load map */ + if(server_load_map(config.sv_map)) + { + int c; + + /* new map loaded */ + mods_shutdown(); + + for(c = 0; c < MAX_CLIENTS; c++) + { + if(m_aClients[c].state == CClient::STATE_EMPTY) + continue; + + server_send_map(c); + reset_client(c); + m_aClients[c].state = CClient::STATE_CONNECTING; + } + + game_start_time = time_get(); + current_tick = 0; + mods_init(); + } + else + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + config_set_sv_map(&config, current_map); + } + } + + while(t > server_tick_start_time(current_tick+1)) + { + current_tick++; + new_ticks++; + + /* apply new input */ + { + static PERFORMACE_INFO scope = {"input", 0}; + int c, i; + + perf_start(&scope); + + for(c = 0; c < MAX_CLIENTS; c++) + { + if(m_aClients[c].state == CClient::STATE_EMPTY) + continue; + for(i = 0; i < 200; i++) + { + if(m_aClients[c].inputs[i].game_tick == server_tick()) + { + if(m_aClients[c].state == CClient::STATE_INGAME) + mods_client_predicted_input(c, m_aClients[c].inputs[i].data); + break; + } + } + } + + perf_end(); + } + + /* progress game */ + { + static PERFORMACE_INFO scope = {"tick", 0}; + perf_start(&scope); + mods_tick(); + perf_end(); + } + } + + /* snap game */ + if(new_ticks) + { + if(config.sv_high_bandwidth || (current_tick%2) == 0) + { + static PERFORMACE_INFO scope = {"snap", 0}; + perf_start(&scope); + server_do_snap(); + perf_end(); + } + } + + /* master server stuff */ + register_update(); + + + { + static PERFORMACE_INFO scope = {"net", 0}; + perf_start(&scope); + server_pump_network(); + perf_end(); + } + + perf_end(); + + if(reporttime < time_get()) + { + if(config.debug) + { + /* + static NETSTATS prev_stats; + NETSTATS stats; + netserver_stats(net, &stats); + + perf_next(); + + if(config.dbg_pref) + perf_dump(&rootscope); + + dbg_msg("server", "send=%8d recv=%8d", + (stats.send_bytes - prev_stats.send_bytes)/reportinterval, + (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); + + prev_stats = stats; + */ + } + + reporttime += time_freq()*reportinterval; + } + + /* wait for incomming data */ + net_socket_read_wait(m_NetServer.Socket(), 5); + } + } + + mods_shutdown(); + map_unload(); + + if(current_map_data) + mem_free(current_map_data); + return 0; +} + + +#endif + +int main(int argc, char **argv) +{ + + m_pNetServer = &g_Server.m_NetServer; +#if defined(CONF_FAMILY_WINDOWS) + int i; + for(i = 1; i < argc; i++) + { + if(strcmp("-s", argv[i]) == 0 || strcmp("--silent", argv[i]) == 0) + { + ShowWindow(GetConsoleWindow(), SW_HIDE); + break; + } + } +#endif + + /* init the engine */ + dbg_msg("server", "starting..."); + engine_init("Teeworlds"); + + /* register all console commands */ + + g_Server.RegisterCommands(); + mods_console_init(); + + /* parse the command line arguments */ + engine_parse_arguments(argc, argv); + + /* run the server */ + g_Server.Run(); + return 0; +} + diff --git a/src/game/client/clienthooks.cpp b/src/game/client/clienthooks.cpp index 88a7722a..76fa8dcd 100644 --- a/src/game/client/clienthooks.cpp +++ b/src/game/client/clienthooks.cpp @@ -7,22 +7,21 @@ - // clean hooks -extern "C" void modc_entergame() {} -extern "C" void modc_shutdown() {} -extern "C" void modc_console_init() { gameclient.on_console_init(); } -extern "C" void modc_save_config() { gameclient.on_save(); } -extern "C" void modc_init() { gameclient.on_init(); } -extern "C" void modc_connected() { gameclient.on_connected(); } -extern "C" void modc_predict() { gameclient.on_predict(); } -extern "C" void modc_newsnapshot() { gameclient.on_snapshot(); } -extern "C" int modc_snap_input(int *data) { return gameclient.on_snapinput(data); } -extern "C" void modc_statechange(int state, int old) { gameclient.on_statechange(state, old); } -extern "C" void modc_render() { gameclient.on_render(); } -extern "C" void modc_message(int msgtype) { gameclient.on_message(msgtype); } -extern "C" void modc_rcon_line(const char *line) { gameclient.console->print_line(1, line); } +void modc_entergame() {} +void modc_shutdown() {} +void modc_console_init() { gameclient.on_console_init(); } +void modc_save_config() { gameclient.on_save(); } +void modc_init() { gameclient.on_init(); } +void modc_connected() { gameclient.on_connected(); } +void modc_predict() { gameclient.on_predict(); } +void modc_newsnapshot() { gameclient.on_snapshot(); } +int modc_snap_input(int *data) { return gameclient.on_snapinput(data); } +void modc_statechange(int state, int old) { gameclient.on_statechange(state, old); } +void modc_render() { gameclient.on_render(); } +void modc_message(int msgtype) { gameclient.on_message(msgtype); } +void modc_rcon_line(const char *line) { gameclient.console->print_line(1, line); } -extern "C" const char *modc_net_version() { return GAME_NETVERSION; } -extern "C" const char *modc_getitemname(int type) { return netobj_get_name(type); } +const char *modc_net_version() { return GAME_NETVERSION; } +const char *modc_getitemname(int type) { return netobj_get_name(type); } diff --git a/src/game/client/component.hpp b/src/game/client/component.hpp index 3d5edb86..6534e56b 100644 --- a/src/game/client/component.hpp +++ b/src/game/client/component.hpp @@ -2,13 +2,21 @@ #define GAME_CLIENT_GAMESYSTEM_H #include +#include "gameclient.hpp" class GAMECLIENT; class COMPONENT { protected: + friend class GAMECLIENT; + GAMECLIENT *client; + + // perhaps propagte pointers for these as well + class IGraphics *Graphics() const { return client->Graphics(); } + class CUI *UI() const { return client->UI(); } + class CRenderTools *RenderTools() const { return client->RenderTools(); } public: virtual ~COMPONENT() {} diff --git a/src/game/client/components/broadcast.cpp b/src/game/client/components/broadcast.cpp index 960b004c..b86ed658 100644 --- a/src/game/client/components/broadcast.cpp +++ b/src/game/client/components/broadcast.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -14,12 +15,12 @@ void BROADCAST::on_reset() void BROADCAST::on_render() { - gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300); + Graphics()->MapScreen(0, 0, 300*Graphics()->ScreenAspect(), 300); if(time_get() < broadcast_time) { float w = gfx_text_width(0, 14, broadcast_text, -1); - gfx_text(0, 150*gfx_screenaspect()-w/2, 35, 14, broadcast_text, -1); + gfx_text(0, 150*Graphics()->ScreenAspect()-w/2, 35, 14, broadcast_text, -1); } } diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index 37015337..fdf1d21b 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -1,6 +1,7 @@ #include // strcmp #include +#include #include #include @@ -133,7 +134,7 @@ void CHAT::add_line(int client_id, int team, const char *line) void CHAT::on_render() { - gfx_mapscreen(0,0,300*gfx_screenaspect(),300); + Graphics()->MapScreen(0,0,300*Graphics()->ScreenAspect(),300); float x = 10.0f; float y = 300.0f-20.0f; if(mode != MODE_NONE) diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index 7b806b36..382cb134 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -6,10 +6,9 @@ #include #include +#include -extern "C" { - #include -} +#include #include #include @@ -34,8 +33,8 @@ enum CONSOLE::INSTANCE::INSTANCE(int t) { // init ringbuffers - history = ringbuf_init(history_data, sizeof(history_data), RINGBUF_FLAG_RECYCLE); - backlog = ringbuf_init(backlog_data, sizeof(backlog_data), RINGBUF_FLAG_RECYCLE); + //history = ringbuf_init(history_data, sizeof(history_data), RINGBUF_FLAG_RECYCLE); + //backlog = ringbuf_init(backlog_data, sizeof(backlog_data), RINGBUF_FLAG_RECYCLE); history_entry = 0x0; @@ -83,7 +82,7 @@ void CONSOLE::INSTANCE::on_input(INPUT_EVENT e) { if(input.get_string()[0]) { - char *entry = (char *)ringbuf_allocate(history, input.get_length()+1); + char *entry = history.Allocate(input.get_length()+1); mem_copy(entry, input.get_string(), input.get_length()+1); execute_line(input.get_string()); @@ -97,13 +96,13 @@ void CONSOLE::INSTANCE::on_input(INPUT_EVENT e) { if (history_entry) { - char *test = (char *)ringbuf_prev(history, history_entry); + char *test = history.Prev(history_entry); if (test) history_entry = test; } else - history_entry = (char *)ringbuf_last(history); + history_entry = history.Last(); if (history_entry) { @@ -116,7 +115,7 @@ void CONSOLE::INSTANCE::on_input(INPUT_EVENT e) else if (e.key == KEY_DOWN) { if (history_entry) - history_entry = (char *)ringbuf_next(history, history_entry); + history_entry = history.Next(history_entry); if (history_entry) { @@ -173,7 +172,7 @@ void CONSOLE::INSTANCE::print_line(const char *line) if (len > 255) len = 255; - char *entry = (char *)ringbuf_allocate(backlog, len+1); + char *entry = backlog.Allocate(len+1); mem_copy(entry, line, len+1); } @@ -212,6 +211,7 @@ static float console_scale_func(float t) struct RENDERINFO { + CONSOLE *self; TEXT_CURSOR cursor; const char *current_cmd; int wanted_completion; @@ -225,11 +225,11 @@ void CONSOLE::possible_commands_render_callback(const char *str, void *user) if(info->enum_count == info->wanted_completion) { float tw = gfx_text_width(info->cursor.font, info->cursor.font_size, str, -1); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,0.85f); - draw_round_rect(info->cursor.x-3, info->cursor.y, tw+5, info->cursor.font_size+4, info->cursor.font_size/3); - gfx_quads_end(); + info->self->Graphics()->TextureSet(-1); + info->self->Graphics()->QuadsBegin(); + info->self->Graphics()->SetColor(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,0.85f); + info->self->RenderTools()->draw_round_rect(info->cursor.x-3, info->cursor.y, tw+5, info->cursor.font_size+4, info->cursor.font_size/3); + info->self->Graphics()->QuadsEnd(); gfx_text_color(0.05f, 0.05f, 0.05f,1); gfx_text_ex(&info->cursor, str, -1); @@ -260,7 +260,7 @@ void CONSOLE::possible_commands_render_callback(const char *str, void *user) void CONSOLE::on_render() { - RECT screen = *ui_screen(); + CUIRect screen = *UI()->Screen(); float console_max_height = screen.h*3/5.0f; float console_height; @@ -296,45 +296,45 @@ void CONSOLE::on_render() console_height = console_height_scale*console_max_height; - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); // do console shadow - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolorvertex(0, 0,0,0, 0.5f); - gfx_setcolorvertex(1, 0,0,0, 0.5f); - gfx_setcolorvertex(2, 0,0,0, 0.0f); - gfx_setcolorvertex(3, 0,0,0, 0.0f); - gfx_quads_drawTL(0,console_height,screen.w,10.0f); - gfx_quads_end(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColorVertex(0, 0,0,0, 0.5f); + Graphics()->SetColorVertex(1, 0,0,0, 0.5f); + Graphics()->SetColorVertex(2, 0,0,0, 0.0f); + Graphics()->SetColorVertex(3, 0,0,0, 0.0f); + Graphics()->QuadsDrawTL(0,console_height,screen.w,10.0f); + Graphics()->QuadsEnd(); // do background - gfx_texture_set(data->images[IMAGE_CONSOLE_BG].id); - gfx_quads_begin(); - gfx_setcolor(0.2f, 0.2f, 0.2f,0.9f); + Graphics()->TextureSet(data->images[IMAGE_CONSOLE_BG].id); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0.2f, 0.2f, 0.2f,0.9f); if(console_type != 0) - gfx_setcolor(0.4f, 0.2f, 0.2f,0.9f); - gfx_quads_setsubset(0,-console_height*0.075f,screen.w*0.075f*0.5f,0); - gfx_quads_drawTL(0,0,screen.w,console_height); - gfx_quads_end(); + Graphics()->SetColor(0.4f, 0.2f, 0.2f,0.9f); + Graphics()->QuadsSetSubset(0,-console_height*0.075f,screen.w*0.075f*0.5f,0); + Graphics()->QuadsDrawTL(0,0,screen.w,console_height); + Graphics()->QuadsEnd(); // do small bar shadow - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolorvertex(0, 0,0,0, 0.0f); - gfx_setcolorvertex(1, 0,0,0, 0.0f); - gfx_setcolorvertex(2, 0,0,0, 0.25f); - gfx_setcolorvertex(3, 0,0,0, 0.25f); - gfx_quads_drawTL(0,console_height-20,screen.w,10); - gfx_quads_end(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColorVertex(0, 0,0,0, 0.0f); + Graphics()->SetColorVertex(1, 0,0,0, 0.0f); + Graphics()->SetColorVertex(2, 0,0,0, 0.25f); + Graphics()->SetColorVertex(3, 0,0,0, 0.25f); + Graphics()->QuadsDrawTL(0,console_height-20,screen.w,10); + Graphics()->QuadsEnd(); // do the lower bar - gfx_texture_set(data->images[IMAGE_CONSOLE_BAR].id); - gfx_quads_begin(); - gfx_setcolor(1.0f, 1.0f, 1.0f, 0.9f); - gfx_quads_setsubset(0,0.1f,screen.w*0.015f,1-0.1f); - gfx_quads_drawTL(0,console_height-10.0f,screen.w,10.0f); - gfx_quads_end(); + Graphics()->TextureSet(data->images[IMAGE_CONSOLE_BAR].id); + Graphics()->QuadsBegin(); + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.9f); + Graphics()->QuadsSetSubset(0,0.1f,screen.w*0.015f,1-0.1f); + Graphics()->QuadsDrawTL(0,console_height-10.0f,screen.w,10.0f); + Graphics()->QuadsEnd(); console_height -= 22.0f; @@ -351,6 +351,7 @@ void CONSOLE::on_render() gfx_text_set_cursor(&cursor, x, y, font_size, TEXTFLAG_RENDER); RENDERINFO info; + info.self = this; info.wanted_completion = console->completion_chosen; info.enum_count = 0; info.current_cmd = console->completion_buffer; @@ -407,13 +408,13 @@ void CONSOLE::on_render() // render log y -= row_height; - char *entry = (char *)ringbuf_last(console->backlog); + char *entry = console->backlog.Last(); while (y > 0.0f && entry) { gfx_text(0, x, y, font_size, entry, -1); y -= row_height; - entry = (char *)ringbuf_prev(console->backlog, entry); + entry = console->backlog.Prev(entry); } } } diff --git a/src/game/client/components/console.hpp b/src/game/client/components/console.hpp index 0a4adbda..78da98b8 100644 --- a/src/game/client/components/console.hpp +++ b/src/game/client/components/console.hpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,12 +8,9 @@ class CONSOLE : public COMPONENT class INSTANCE { public: - char history_data[65536]; - struct RINGBUFFER *history; + TStaticRingBuffer backlog; + TStaticRingBuffer history; char *history_entry; - - char backlog_data[65536]; - struct RINGBUFFER *backlog; LINEINPUT input; diff --git a/src/game/client/components/damageind.cpp b/src/game/client/components/damageind.cpp index f585e4d3..7f1991dc 100644 --- a/src/game/client/components/damageind.cpp +++ b/src/game/client/components/damageind.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -44,8 +45,8 @@ void DAMAGEIND::create(vec2 pos, vec2 dir) void DAMAGEIND::on_render() { - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); for(int i = 0; i < num_items;) { vec2 pos = mix(items[i].pos+items[i].dir*75.0f, items[i].pos, clamp((items[i].life-0.60f)/0.15f, 0.0f, 1.0f)); @@ -55,12 +56,12 @@ void DAMAGEIND::on_render() destroy_i(&items[i]); else { - gfx_setcolor(1.0f,1.0f,1.0f, items[i].life/0.1f); - gfx_quads_setrotation(items[i].startangle + items[i].life * 2.0f); - select_sprite(SPRITE_STAR1); - draw_sprite(pos.x, pos.y, 48.0f); + Graphics()->SetColor(1.0f,1.0f,1.0f, items[i].life/0.1f); + Graphics()->QuadsSetRotation(items[i].startangle + items[i].life * 2.0f); + RenderTools()->select_sprite(SPRITE_STAR1); + RenderTools()->draw_sprite(pos.x, pos.y, 48.0f); i++; } } - gfx_quads_end(); + Graphics()->QuadsEnd(); } diff --git a/src/game/client/components/debughud.cpp b/src/game/client/components/debughud.cpp index ada006ea..c7cc559b 100644 --- a/src/game/client/components/debughud.cpp +++ b/src/game/client/components/debughud.cpp @@ -5,6 +5,7 @@ extern "C" { } #include +#include #include #include @@ -23,7 +24,7 @@ void DEBUGHUD::render_netcorrections() if(!config.debug || !gameclient.snap.local_character || !gameclient.snap.local_prev_character) return; - gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300); + Graphics()->MapScreen(0, 0, 300*Graphics()->ScreenAspect(), 300); /*float speed = distance(vec2(netobjects.local_prev_character->x, netobjects.local_prev_character->y), vec2(netobjects.local_character->x, netobjects.local_character->y));*/ @@ -50,7 +51,7 @@ void DEBUGHUD::render_tuning() TUNING_PARAMS standard_tuning; - gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300); + Graphics()->MapScreen(0, 0, 300*Graphics()->ScreenAspect(), 300); float y = 50.0f; int count = 0; @@ -87,9 +88,9 @@ void DEBUGHUD::render_tuning() y = y+count*6; - gfx_texture_set(-1); - gfx_blend_normal(); - gfx_lines_begin(); + Graphics()->TextureSet(-1); + Graphics()->BlendNormal(); + Graphics()->LinesBegin(); float height = 50.0f; float pv = 1; for(int i = 0; i < 100; i++) @@ -97,11 +98,11 @@ void DEBUGHUD::render_tuning() float speed = i/100.0f * 3000; float ramp = velocity_ramp(speed, gameclient.tuning.velramp_start, gameclient.tuning.velramp_range, gameclient.tuning.velramp_curvature); float rampedspeed = (speed * ramp)/1000.0f; - gfx_lines_draw((i-1)*2, y+height-pv*height, i*2, y+height-rampedspeed*height); - //gfx_lines_draw((i-1)*2, 200, i*2, 200); + Graphics()->LinesDraw((i-1)*2, y+height-pv*height, i*2, y+height-rampedspeed*height); + //Graphics()->LinesDraw((i-1)*2, 200, i*2, 200); pv = rampedspeed; } - gfx_lines_end(); + Graphics()->LinesEnd(); gfx_text_color(1,1,1,1); } diff --git a/src/game/client/components/emoticon.cpp b/src/game/client/components/emoticon.cpp index 934b2fcf..8001a306 100644 --- a/src/game/client/components/emoticon.cpp +++ b/src/game/client/components/emoticon.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -70,7 +71,7 @@ void EMOTICON::draw_circle(float x, float y, float r, int segments) float sa2 = sinf(a2); float sa3 = sinf(a3); - gfx_quads_draw_freeform( + client->Graphics()->QuadsDrawFreeform( x, y, x+ca1*r, y+sa1*r, x+ca3*r, y+sa3*r, @@ -107,20 +108,20 @@ void EMOTICON::on_render() if (length(selector_mouse) > 100) selected_emote = (int)(selected_angle / (2*pi) * 12.0f); - RECT screen = *ui_screen(); + CUIRect screen = *UI()->Screen(); - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); - gfx_blend_normal(); + Graphics()->BlendNormal(); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.3f); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0,0,0,0.3f); draw_circle(screen.w/2, screen.h/2, 160, 64); - gfx_quads_end(); + Graphics()->QuadsEnd(); - gfx_texture_set(data->images[IMAGE_EMOTICONS].id); - gfx_quads_begin(); + Graphics()->TextureSet(data->images[IMAGE_EMOTICONS].id); + Graphics()->QuadsBegin(); for (int i = 0; i < 12; i++) { @@ -134,17 +135,17 @@ void EMOTICON::on_render() float nudge_x = 120 * cos(angle); float nudge_y = 120 * sin(angle); - select_sprite(SPRITE_OOP + i); - gfx_quads_draw(screen.w/2 + nudge_x, screen.h/2 + nudge_y, size, size); + RenderTools()->select_sprite(SPRITE_OOP + i); + Graphics()->QuadsDraw(screen.w/2 + nudge_x, screen.h/2 + nudge_y, size, size); } - gfx_quads_end(); + Graphics()->QuadsEnd(); - gfx_texture_set(data->images[IMAGE_CURSOR].id); - gfx_quads_begin(); - gfx_setcolor(1,1,1,1); - gfx_quads_drawTL(selector_mouse.x+screen.w/2,selector_mouse.y+screen.h/2,24,24); - gfx_quads_end(); + Graphics()->TextureSet(data->images[IMAGE_CURSOR].id); + Graphics()->QuadsBegin(); + Graphics()->SetColor(1,1,1,1); + Graphics()->QuadsDrawTL(selector_mouse.x+screen.w/2,selector_mouse.y+screen.h/2,24,24); + Graphics()->QuadsEnd(); } void EMOTICON::emote(int emoticon) diff --git a/src/game/client/components/flow.cpp b/src/game/client/components/flow.cpp index b2f983e6..9ecd4b5c 100644 --- a/src/game/client/components/flow.cpp +++ b/src/game/client/components/flow.cpp @@ -1,3 +1,4 @@ +#include #include #include #include "flow.hpp" @@ -15,17 +16,17 @@ void FLOW::dbg_render() if(!cells) return; - gfx_texture_set(-1); - gfx_lines_begin(); + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { vec2 pos(x*spacing, y*spacing); vec2 vel = cells[y*width+x].vel * 0.01f; - gfx_lines_draw(pos.x, pos.y, pos.x+vel.x, pos.y+vel.y); + Graphics()->LinesDraw(pos.x, pos.y, pos.x+vel.x, pos.y+vel.y); } - gfx_lines_end(); + Graphics()->LinesEnd(); } void FLOW::init() diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index b1926ab4..837322fb 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -1,6 +1,7 @@ #include // memcmp #include +#include #include #include @@ -35,11 +36,11 @@ void HUD::render_goals() int gameflags = gameclient.snap.gameobj->flags; - float whole = 300*gfx_screenaspect(); + float whole = 300*Graphics()->ScreenAspect(); float half = whole/2.0f; - gfx_mapscreen(0,0,300*gfx_screenaspect(),300); + Graphics()->MapScreen(0,0,300*Graphics()->ScreenAspect(),300); if(!gameclient.snap.gameobj->sudden_death) { char buf[32]; @@ -71,15 +72,15 @@ void HUD::render_goals() { for(int t = 0; t < 2; t++) { - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); if(t == 0) - gfx_setcolor(1,0,0,0.25f); + Graphics()->SetColor(1,0,0,0.25f); else - gfx_setcolor(0,0,1,0.25f); - draw_round_rect(whole-40, 300-40-15+t*20, 50, 18, 5.0f); - gfx_quads_end(); + Graphics()->SetColor(0,0,1,0.25f); + RenderTools()->draw_round_rect(whole-40, 300-40-15+t*20, 50, 18, 5.0f); + Graphics()->QuadsEnd(); char buf[32]; str_format(buf, sizeof(buf), "%d", t?gameclient.snap.gameobj->teamscore_blue:gameclient.snap.gameobj->teamscore_red); @@ -92,16 +93,16 @@ void HUD::render_goals() { if(gameclient.snap.flags[t]->carried_by == -2 || (gameclient.snap.flags[t]->carried_by == -1 && ((client_tick()/10)&1))) { - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); - if(t == 0) select_sprite(SPRITE_FLAG_RED); - else select_sprite(SPRITE_FLAG_BLUE); + if(t == 0) RenderTools()->select_sprite(SPRITE_FLAG_RED); + else RenderTools()->select_sprite(SPRITE_FLAG_BLUE); float size = 16; - gfx_quads_drawTL(whole-40+5, 300-40-15+t*20+1, size/2, size); - gfx_quads_end(); + Graphics()->QuadsDrawTL(whole-40+5, 300-40-15+t*20+1, size/2, size); + Graphics()->QuadsEnd(); } else if(gameclient.snap.flags[t]->carried_by >= 0) { @@ -112,7 +113,7 @@ void HUD::render_goals() TEE_RENDER_INFO info = gameclient.clients[id].render_info; info.size = 18.0f; - render_tee(ANIMSTATE::get_idle(), &info, EMOTE_NORMAL, vec2(1,0), + RenderTools()->RenderTee(ANIMSTATE::get_idle(), &info, EMOTE_NORMAL, vec2(1,0), vec2(whole-40+10, 300-40-15+9+t*20+1)); } } @@ -127,7 +128,7 @@ void HUD::render_goals() { char buf[256]; float w = gfx_text_width(0, 24, "Warmup", -1); - gfx_text(0, 150*gfx_screenaspect()+-w/2, 50, 24, "Warmup", -1); + gfx_text(0, 150*Graphics()->ScreenAspect()+-w/2, 50, 24, "Warmup", -1); int seconds = gameclient.snap.gameobj->warmup/SERVER_TICK_SPEED; if(seconds < 5) @@ -135,16 +136,16 @@ void HUD::render_goals() else str_format(buf, sizeof(buf), "%d", seconds); w = gfx_text_width(0, 24, buf, -1); - gfx_text(0, 150*gfx_screenaspect()+-w/2, 75, 24, buf, -1); + gfx_text(0, 150*Graphics()->ScreenAspect()+-w/2, 75, 24, buf, -1); } } -static void mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group) +void HUD::mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group) { float points[4]; - mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f, - group->offset_x, group->offset_y, gfx_screenaspect(), 1.0f, points); - gfx_mapscreen(points[0], points[1], points[2], points[3]); + RenderTools()->mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f, + group->offset_x, group->offset_y, Graphics()->ScreenAspect(), 1.0f, points); + Graphics()->MapScreen(points[0], points[1], points[2], points[3]); } void HUD::render_fps() @@ -163,7 +164,7 @@ void HUD::render_connectionwarning() { const char *text = "Connection Problems..."; float w = gfx_text_width(0, 24, text, -1); - gfx_text(0, 150*gfx_screenaspect()-w/2, 50, 24, text, -1); + gfx_text(0, 150*Graphics()->ScreenAspect()-w/2, 50, 24, text, -1); } } @@ -192,11 +193,11 @@ void HUD::render_voting() if(!gameclient.voting->is_voting()) return; - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.40f); - draw_round_rect(-10, 60-2, 100+10+4+5, 28, 5.0f); - gfx_quads_end(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0,0,0,0.40f); + RenderTools()->draw_round_rect(-10, 60-2, 100+10+4+5, 28, 5.0f); + Graphics()->QuadsEnd(); gfx_text_color(1,1,1,1); @@ -208,17 +209,17 @@ void HUD::render_voting() gfx_text(0x0, 5+100-tw, 60, 6, buf, -1); - RECT base = {5, 70, 100, 4}; + CUIRect base = {5, 70, 100, 4}; gameclient.voting->render_bars(base, false); const char *yes_key = gameclient.binds->get_key("vote yes"); const char *no_key = gameclient.binds->get_key("vote no"); str_format(buf, sizeof(buf), "%s - Vote Yes", yes_key); base.y += base.h+1; - ui_do_label(&base, buf, 6.0f, -1); + UI()->DoLabel(&base, buf, 6.0f, -1); str_format(buf, sizeof(buf), "Vote No - %s", no_key); - ui_do_label(&base, buf, 6.0f, 1); + UI()->DoLabel(&base, buf, 6.0f, 1); } void HUD::render_cursor() @@ -227,14 +228,14 @@ void HUD::render_cursor() return; mapscreen_to_group(gameclient.camera->center.x, gameclient.camera->center.y, layers_game_group()); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); // render cursor - select_sprite(data->weapons.id[gameclient.snap.local_character->weapon%NUM_WEAPONS].sprite_cursor); + RenderTools()->select_sprite(data->weapons.id[gameclient.snap.local_character->weapon%NUM_WEAPONS].sprite_cursor); float cursorsize = 64; - draw_sprite(gameclient.controls->target_pos.x, gameclient.controls->target_pos.y, cursorsize); - gfx_quads_end(); + RenderTools()->draw_sprite(gameclient.controls->target_pos.x, gameclient.controls->target_pos.y, cursorsize); + Graphics()->QuadsEnd(); } void HUD::render_healthandammo() @@ -247,40 +248,40 @@ void HUD::render_healthandammo() // render ammo count // render gui stuff - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_mapscreen(0,0,width,300); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->MapScreen(0,0,width,300); - gfx_quads_begin(); + Graphics()->QuadsBegin(); // if weaponstage is active, put a "glow" around the stage ammo - select_sprite(data->weapons.id[gameclient.snap.local_character->weapon%NUM_WEAPONS].sprite_proj); + RenderTools()->select_sprite(data->weapons.id[gameclient.snap.local_character->weapon%NUM_WEAPONS].sprite_proj); for (int i = 0; i < min(gameclient.snap.local_character->ammocount, 10); i++) - gfx_quads_drawTL(x+i*12,y+24,10,10); + Graphics()->QuadsDrawTL(x+i*12,y+24,10,10); - gfx_quads_end(); + Graphics()->QuadsEnd(); - gfx_quads_begin(); + Graphics()->QuadsBegin(); int h = 0; // render health - select_sprite(SPRITE_HEALTH_FULL); + RenderTools()->select_sprite(SPRITE_HEALTH_FULL); for(; h < gameclient.snap.local_character->health; h++) - gfx_quads_drawTL(x+h*12,y,10,10); + Graphics()->QuadsDrawTL(x+h*12,y,10,10); - select_sprite(SPRITE_HEALTH_EMPTY); + RenderTools()->select_sprite(SPRITE_HEALTH_EMPTY); for(; h < 10; h++) - gfx_quads_drawTL(x+h*12,y,10,10); + Graphics()->QuadsDrawTL(x+h*12,y,10,10); // render armor meter h = 0; - select_sprite(SPRITE_ARMOR_FULL); + RenderTools()->select_sprite(SPRITE_ARMOR_FULL); for(; h < gameclient.snap.local_character->armor; h++) - gfx_quads_drawTL(x+h*12,y+12,10,10); + Graphics()->QuadsDrawTL(x+h*12,y+12,10,10); - select_sprite(SPRITE_ARMOR_EMPTY); + RenderTools()->select_sprite(SPRITE_ARMOR_EMPTY); for(; h < 10; h++) - gfx_quads_drawTL(x+h*12,y+12,10,10); - gfx_quads_end(); + Graphics()->QuadsDrawTL(x+h*12,y+12,10,10); + Graphics()->QuadsEnd(); } void HUD::on_render() @@ -288,7 +289,7 @@ void HUD::on_render() if(!gameclient.snap.gameobj) return; - width = 300*gfx_screenaspect(); + width = 300*Graphics()->ScreenAspect(); bool spectate = false; if(gameclient.snap.local_info && gameclient.snap.local_info->team == -1) diff --git a/src/game/client/components/hud.hpp b/src/game/client/components/hud.hpp index 41fa0ff2..92ff0122 100644 --- a/src/game/client/components/hud.hpp +++ b/src/game/client/components/hud.hpp @@ -13,6 +13,8 @@ class HUD : public COMPONENT void render_healthandammo(); void render_goals(); + + void mapscreen_to_group(float center_x, float center_y, struct MAPITEM_GROUP *group); public: HUD(); diff --git a/src/game/client/components/items.cpp b/src/game/client/components/items.cpp index 1d579619..3c9e1b79 100644 --- a/src/game/client/components/items.cpp +++ b/src/game/client/components/items.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -44,10 +45,10 @@ void ITEMS::render_projectile(const NETOBJ_PROJECTILE *current, int itemid) vec2 prevpos = calc_pos(startpos, startvel, curvature, speed, ct-0.001f); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); - select_sprite(data->weapons.id[clamp(current->type, 0, NUM_WEAPONS-1)].sprite_proj); + RenderTools()->select_sprite(data->weapons.id[clamp(current->type, 0, NUM_WEAPONS-1)].sprite_proj); vec2 vel = pos-prevpos; //vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); @@ -57,7 +58,7 @@ void ITEMS::render_projectile(const NETOBJ_PROJECTILE *current, int itemid) { gameclient.effects->smoketrail(pos, vel*-1); gameclient.flow->add(pos, vel*1000*client_frametime(), 10.0f); - gfx_quads_setrotation(client_localtime()*pi*2*2 + itemid); + Graphics()->QuadsSetRotation(client_localtime()*pi*2*2 + itemid); } else { @@ -65,28 +66,28 @@ void ITEMS::render_projectile(const NETOBJ_PROJECTILE *current, int itemid) gameclient.flow->add(pos, vel*1000*client_frametime(), 10.0f); if(length(vel) > 0.00001f) - gfx_quads_setrotation(get_angle(vel)); + Graphics()->QuadsSetRotation(get_angle(vel)); else - gfx_quads_setrotation(0); + Graphics()->QuadsSetRotation(0); } - gfx_quads_draw(pos.x, pos.y, 32, 32); - gfx_quads_setrotation(0); - gfx_quads_end(); + Graphics()->QuadsDraw(pos.x, pos.y, 32, 32); + Graphics()->QuadsSetRotation(0); + Graphics()->QuadsEnd(); } void ITEMS::render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *current) { - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); float angle = 0.0f; float size = 64.0f; if (current->type == POWERUP_WEAPON) { angle = 0; //-pi/6;//-0.25f * pi * 2.0f; - select_sprite(data->weapons.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].sprite_body); + RenderTools()->select_sprite(data->weapons.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].sprite_body); size = data->weapons.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].visual_size; } else @@ -97,7 +98,7 @@ void ITEMS::render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *curren SPRITE_PICKUP_WEAPON, SPRITE_PICKUP_NINJA }; - select_sprite(c[current->type]); + RenderTools()->select_sprite(c[current->type]); if(c[current->type] == SPRITE_PICKUP_NINJA) { @@ -107,13 +108,13 @@ void ITEMS::render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *curren } } - gfx_quads_setrotation(angle); + Graphics()->QuadsSetRotation(angle); float offset = pos.y/32.0f + pos.x/32.0f; pos.x += cosf(client_localtime()*2.0f+offset)*2.5f; pos.y += sinf(client_localtime()*2.0f+offset)*2.5f; - draw_sprite(pos.x, pos.y, size); - gfx_quads_end(); + RenderTools()->draw_sprite(pos.x, pos.y, size); + Graphics()->QuadsEnd(); } void ITEMS::render_flag(const NETOBJ_FLAG *prev, const NETOBJ_FLAG *current) @@ -121,16 +122,16 @@ void ITEMS::render_flag(const NETOBJ_FLAG *prev, const NETOBJ_FLAG *current) float angle = 0.0f; float size = 42.0f; - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); if(current->team == 0) // red team - select_sprite(SPRITE_FLAG_RED); + RenderTools()->select_sprite(SPRITE_FLAG_RED); else - select_sprite(SPRITE_FLAG_BLUE); + RenderTools()->select_sprite(SPRITE_FLAG_BLUE); - gfx_quads_setrotation(angle); + Graphics()->QuadsSetRotation(angle); vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); @@ -142,8 +143,8 @@ void ITEMS::render_flag(const NETOBJ_FLAG *prev, const NETOBJ_FLAG *current) if(gameclient.snap.local_info && current->carried_by == gameclient.snap.local_info->cid) pos = gameclient.local_character_pos; - gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2); - gfx_quads_end(); + Graphics()->QuadsDraw(pos.x, pos.y-size*0.75f, size, size*2); + Graphics()->QuadsEnd(); } @@ -161,19 +162,19 @@ void ITEMS::render_laser(const struct NETOBJ_LASER *current) vec2 out, border; - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); //vec4 inner_color(0.15f,0.35f,0.75f,1.0f); //vec4 outer_color(0.65f,0.85f,1.0f,1.0f); // do outline vec4 outer_color(0.075f,0.075f,0.25f,1.0f); - gfx_setcolor(outer_color.r,outer_color.g,outer_color.b,1.0f); + Graphics()->SetColor(outer_color.r,outer_color.g,outer_color.b,1.0f); out = vec2(dir.y, -dir.x) * (7.0f*ia); - gfx_quads_draw_freeform( + Graphics()->QuadsDrawFreeform( from.x-out.x, from.y-out.y, from.x+out.x, from.y+out.y, pos.x-out.x, pos.y-out.y, @@ -183,34 +184,34 @@ void ITEMS::render_laser(const struct NETOBJ_LASER *current) // do inner vec4 inner_color(0.5f,0.5f,1.0f,1.0f); out = vec2(dir.y, -dir.x) * (5.0f*ia); - gfx_setcolor(inner_color.r, inner_color.g, inner_color.b, 1.0f); // center + Graphics()->SetColor(inner_color.r, inner_color.g, inner_color.b, 1.0f); // center - gfx_quads_draw_freeform( + Graphics()->QuadsDrawFreeform( from.x-out.x, from.y-out.y, from.x+out.x, from.y+out.y, pos.x-out.x, pos.y-out.y, pos.x+out.x, pos.y+out.y ); - gfx_quads_end(); + Graphics()->QuadsEnd(); // render head { - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_PARTICLES].id); - gfx_quads_begin(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(data->images[IMAGE_PARTICLES].id); + Graphics()->QuadsBegin(); int sprites[] = {SPRITE_PART_SPLAT01, SPRITE_PART_SPLAT02, SPRITE_PART_SPLAT03}; - select_sprite(sprites[client_tick()%3]); - gfx_quads_setrotation(client_tick()); - gfx_setcolor(outer_color.r,outer_color.g,outer_color.b,1.0f); - gfx_quads_draw(pos.x, pos.y, 24,24); - gfx_setcolor(inner_color.r, inner_color.g, inner_color.b, 1.0f); - gfx_quads_draw(pos.x, pos.y, 20,20); - gfx_quads_end(); + RenderTools()->select_sprite(sprites[client_tick()%3]); + Graphics()->QuadsSetRotation(client_tick()); + Graphics()->SetColor(outer_color.r,outer_color.g,outer_color.b,1.0f); + Graphics()->QuadsDraw(pos.x, pos.y, 24,24); + Graphics()->SetColor(inner_color.r, inner_color.g, inner_color.b, 1.0f); + Graphics()->QuadsDraw(pos.x, pos.y, 20,20); + Graphics()->QuadsEnd(); } - gfx_blend_normal(); + Graphics()->BlendNormal(); } void ITEMS::on_render() diff --git a/src/game/client/components/killmessages.cpp b/src/game/client/components/killmessages.cpp index 410f2c3d..e6232b7d 100644 --- a/src/game/client/components/killmessages.cpp +++ b/src/game/client/components/killmessages.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -35,10 +36,10 @@ void KILLMESSAGES::on_message(int msgtype, void *rawmsg) void KILLMESSAGES::on_render() { - float width = 400*3.0f*gfx_screenaspect(); + float width = 400*3.0f*Graphics()->ScreenAspect(); float height = 400*3.0f; - gfx_mapscreen(0, 0, width*1.5f, height*1.5f); + Graphics()->MapScreen(0, 0, width*1.5f, height*1.5f); float startx = width*1.5f-10.0f; float y = 20.0f; @@ -66,31 +67,31 @@ void KILLMESSAGES::on_render() { if(killmsgs[r].mode_special&1) { - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); - if(gameclient.clients[killmsgs[r].victim].team == 0) select_sprite(SPRITE_FLAG_BLUE); - else select_sprite(SPRITE_FLAG_RED); + if(gameclient.clients[killmsgs[r].victim].team == 0) RenderTools()->select_sprite(SPRITE_FLAG_BLUE); + else RenderTools()->select_sprite(SPRITE_FLAG_RED); float size = 56.0f; - gfx_quads_drawTL(x, y-16, size/2, size); - gfx_quads_end(); + Graphics()->QuadsDrawTL(x, y-16, size/2, size); + Graphics()->QuadsEnd(); } } - render_tee(ANIMSTATE::get_idle(), &gameclient.clients[killmsgs[r].victim].render_info, EMOTE_PAIN, vec2(-1,0), vec2(x, y+28)); + RenderTools()->RenderTee(ANIMSTATE::get_idle(), &gameclient.clients[killmsgs[r].victim].render_info, EMOTE_PAIN, vec2(-1,0), vec2(x, y+28)); x -= 32.0f; // render weapon x -= 44.0f; if (killmsgs[r].weapon >= 0) { - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - select_sprite(data->weapons.id[killmsgs[r].weapon].sprite_body); - draw_sprite(x, y+28, 96); - gfx_quads_end(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); + RenderTools()->select_sprite(data->weapons.id[killmsgs[r].weapon].sprite_body); + RenderTools()->draw_sprite(x, y+28, 96); + Graphics()->QuadsEnd(); } x -= 52.0f; @@ -100,22 +101,22 @@ void KILLMESSAGES::on_render() { if(killmsgs[r].mode_special&2) { - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); - if(gameclient.clients[killmsgs[r].killer].team == 0) select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); - else select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); + if(gameclient.clients[killmsgs[r].killer].team == 0) RenderTools()->select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); + else RenderTools()->select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); float size = 56.0f; - gfx_quads_drawTL(x-56, y-16, size/2, size); - gfx_quads_end(); + Graphics()->QuadsDrawTL(x-56, y-16, size/2, size); + Graphics()->QuadsEnd(); } } // render killer tee x -= 24.0f; - render_tee(ANIMSTATE::get_idle(), &gameclient.clients[killmsgs[r].killer].render_info, EMOTE_ANGRY, vec2(1,0), vec2(x, y+28)); + RenderTools()->RenderTee(ANIMSTATE::get_idle(), &gameclient.clients[killmsgs[r].killer].render_info, EMOTE_ANGRY, vec2(1,0), vec2(x, y+28)); x -= 32.0f; // render killer name diff --git a/src/game/client/components/mapimages.cpp b/src/game/client/components/mapimages.cpp index 7f839da0..51194853 100644 --- a/src/game/client/components/mapimages.cpp +++ b/src/game/client/components/mapimages.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -13,7 +14,7 @@ void MAPIMAGES::on_mapload() // unload all textures for(int i = 0; i < count; i++) { - gfx_unload_texture(textures[i]); + Graphics()->UnloadTexture(textures[i]); textures[i] = -1; } count = 0; @@ -32,12 +33,12 @@ void MAPIMAGES::on_mapload() char buf[256]; char *name = (char *)map_get_data(img->image_name); str_format(buf, sizeof(buf), "mapres/%s.png", name); - textures[i] = gfx_load_texture(buf, IMG_AUTO, 0); + textures[i] = Graphics()->LoadTexture(buf, IMG_AUTO, 0); } else { void *data = map_get_data(img->image_data); - textures[i] = gfx_load_texture_raw(img->width, img->height, IMG_RGBA, data, IMG_RGBA, 0); + textures[i] = Graphics()->LoadTextureRaw(img->width, img->height, IMG_RGBA, data, IMG_RGBA, 0); map_unload_data(img->image_data); } } diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index 847350e6..75f91521 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -6,6 +8,7 @@ #include #include + #include "maplayers.hpp" MAPLAYERS::MAPLAYERS(int t) @@ -14,16 +17,17 @@ MAPLAYERS::MAPLAYERS(int t) } -static void mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group) +void MAPLAYERS::mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group) { float points[4]; - mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f, - group->offset_x, group->offset_y, gfx_screenaspect(), 1.0f, points); - gfx_mapscreen(points[0], points[1], points[2], points[3]); + RenderTools()->mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f, + group->offset_x, group->offset_y, Graphics()->ScreenAspect(), 1.0f, points); + Graphics()->MapScreen(points[0], points[1], points[2], points[3]); } -static void envelope_eval(float time_offset, int env, float *channels) +void MAPLAYERS::envelope_eval(float time_offset, int env, float *channels, void *user) { + MAPLAYERS *pThis = (MAPLAYERS *)user; channels[0] = 0; channels[1] = 0; channels[2] = 0; @@ -45,7 +49,7 @@ static void envelope_eval(float time_offset, int env, float *channels) return; MAPITEM_ENVELOPE *item = (MAPITEM_ENVELOPE *)map_get_item(start+env, 0, 0); - render_eval_envelope(points+item->start_point, item->num_points, 4, client_localtime()+time_offset, channels); + pThis->RenderTools()->render_eval_envelope(points+item->start_point, item->num_points, 4, client_localtime()+time_offset, channels); } void MAPLAYERS::on_render() @@ -53,8 +57,8 @@ void MAPLAYERS::on_render() if(client_state() != CLIENTSTATE_ONLINE && client_state() != CLIENTSTATE_DEMOPLAYBACK) return; - RECT screen; - gfx_getscreen(&screen.x, &screen.y, &screen.w, &screen.h); + CUIRect screen; + Graphics()->GetScreen(&screen.x, &screen.y, &screen.w, &screen.h); vec2 center = gameclient.camera->center; //float center_x = gameclient.camera->center.x; @@ -71,14 +75,14 @@ void MAPLAYERS::on_render() // set clipping float points[4]; mapscreen_to_group(center.x, center.y, layers_game_group()); - gfx_getscreen(&points[0], &points[1], &points[2], &points[3]); + Graphics()->GetScreen(&points[0], &points[1], &points[2], &points[3]); float x0 = (group->clip_x - points[0]) / (points[2]-points[0]); float y0 = (group->clip_y - points[1]) / (points[3]-points[1]); float x1 = ((group->clip_x+group->clip_w) - points[0]) / (points[2]-points[0]); float y1 = ((group->clip_y+group->clip_h) - points[1]) / (points[3]-points[1]); - gfx_clip_enable((int)(x0*gfx_screenwidth()), (int)(y0*gfx_screenheight()), - (int)((x1-x0)*gfx_screenwidth()), (int)((y1-y0)*gfx_screenheight())); + Graphics()->ClipEnable((int)(x0*Graphics()->ScreenWidth()), (int)(y0*Graphics()->ScreenHeight()), + (int)((x1-x0)*Graphics()->ScreenWidth()), (int)((y1-y0)*Graphics()->ScreenHeight())); } mapscreen_to_group(center.x, center.y, group); @@ -121,43 +125,43 @@ void MAPLAYERS::on_render() { MAPITEM_LAYER_TILEMAP *tmap = (MAPITEM_LAYER_TILEMAP *)layer; if(tmap->image == -1) - gfx_texture_set(-1); + Graphics()->TextureSet(-1); else - gfx_texture_set(gameclient.mapimages->get(tmap->image)); + Graphics()->TextureSet(gameclient.mapimages->get(tmap->image)); TILE *tiles = (TILE *)map_get_data(tmap->data); - gfx_blend_none(); - render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); - gfx_blend_normal(); - render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); + Graphics()->BlendNone(); + RenderTools()->render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); + Graphics()->BlendNormal(); + RenderTools()->render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); } else if(layer->type == LAYERTYPE_QUADS) { MAPITEM_LAYER_QUADS *qlayer = (MAPITEM_LAYER_QUADS *)layer; if(qlayer->image == -1) - gfx_texture_set(-1); + Graphics()->TextureSet(-1); else - gfx_texture_set(gameclient.mapimages->get(qlayer->image)); + Graphics()->TextureSet(gameclient.mapimages->get(qlayer->image)); QUAD *quads = (QUAD *)map_get_data_swapped(qlayer->data); - gfx_blend_none(); - render_quads(quads, qlayer->num_quads, envelope_eval, LAYERRENDERFLAG_OPAQUE); - gfx_blend_normal(); - render_quads(quads, qlayer->num_quads, envelope_eval, LAYERRENDERFLAG_TRANSPARENT); + Graphics()->BlendNone(); + RenderTools()->render_quads(quads, qlayer->num_quads, LAYERRENDERFLAG_OPAQUE, envelope_eval, this); + Graphics()->BlendNormal(); + RenderTools()->render_quads(quads, qlayer->num_quads, LAYERRENDERFLAG_TRANSPARENT, envelope_eval, this); } //layershot_end(); } } if(!config.gfx_noclip) - gfx_clip_disable(); + Graphics()->ClipDisable(); } if(!config.gfx_noclip) - gfx_clip_disable(); + Graphics()->ClipDisable(); // reset the screen like it was before - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); } diff --git a/src/game/client/components/maplayers.hpp b/src/game/client/components/maplayers.hpp index c2919f08..c8b154b2 100644 --- a/src/game/client/components/maplayers.hpp +++ b/src/game/client/components/maplayers.hpp @@ -3,6 +3,9 @@ class MAPLAYERS : public COMPONENT { int type; + + void mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group); + static void envelope_eval(float time_offset, int env, float *channels, void *user); public: enum { diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 168c514d..5f1bbf42 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -12,6 +12,7 @@ #include "skins.hpp" #include +#include #include #include @@ -94,356 +95,366 @@ MENUS::MENUS() last_input = time_get(); } -vec4 MENUS::button_color_mul(const void *id) +vec4 MENUS::button_color_mul(const void *pID) { - if(ui_active_item() == id) + if(UI()->ActiveItem() == pID) return vec4(1,1,1,0.5f); - else if(ui_hot_item() == id) + else if(UI()->HotItem() == pID) return vec4(1,1,1,1.5f); return vec4(1,1,1,1); } -void MENUS::ui_draw_browse_icon(int what, const RECT *r) +int MENUS::DoButton_BrowseIcon(int What, const CUIRect *pRect) { - gfx_texture_set(data->images[IMAGE_BROWSEICONS].id); - gfx_quads_begin(); - select_sprite(what); - gfx_quads_drawTL(r->x,r->y,r->w,r->h); - gfx_quads_end(); + Graphics()->TextureSet(data->images[IMAGE_BROWSEICONS].id); + + Graphics()->QuadsBegin(); + RenderTools()->select_sprite(What); + Graphics()->QuadsDrawTL(pRect->x, pRect->y, pRect->w, pRect->h); + Graphics()->QuadsEnd(); + + return 0; } -void MENUS::ui_draw_menu_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int MENUS::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - ui_draw_rect(r, vec4(1,1,1,0.5f)*button_color_mul(id), CORNER_ALL, 5.0f); - ui_do_label(r, text, r->h*fontmod_height, 0); + RenderTools()->DrawUIRect(pRect, vec4(1,1,1,0.5f)*button_color_mul(pID), CUI::CORNER_ALL, 5.0f); + UI()->DoLabel(pRect, pText, pRect->h*fontmod_height, 0); + return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -void MENUS::ui_draw_keyselect_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int MENUS::DoButton_KeySelect(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - ui_draw_rect(r, vec4(1,1,1,0.5f)*button_color_mul(id), CORNER_ALL, 5.0f); - ui_do_label(r, text, r->h*fontmod_height, 0); + RenderTools()->DrawUIRect(pRect, vec4(1,1,1,0.5f)*button_color_mul(pID), CUI::CORNER_ALL, 5.0f); + UI()->DoLabel(pRect, pText, pRect->h*fontmod_height, 0); + return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -void MENUS::ui_draw_menu_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int MENUS::DoButton_MenuTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Corners) { - int corners = CORNER_T; - vec4 colormod(1,1,1,1); - if(extra) - corners = *(int *)extra; + vec4 ColorMod(1,1,1,1); - if(checked) - ui_draw_rect(r, color_tabbar_active, corners, 10.0f); + if(Checked) + RenderTools()->DrawUIRect(pRect, color_tabbar_active, Corners, 10.0f); else - ui_draw_rect(r, color_tabbar_inactive, corners, 10.0f); - ui_do_label(r, text, r->h*fontmod_height, 0); + RenderTools()->DrawUIRect(pRect, color_tabbar_inactive, Corners, 10.0f); + UI()->DoLabel(pRect, pText, pRect->h*fontmod_height, 0); + + return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -void MENUS::ui_draw_settings_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int MENUS::DoButton_SettingsTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - if(checked) - ui_draw_rect(r, color_tabbar_active, CORNER_R, 10.0f); + if(Checked) + RenderTools()->DrawUIRect(pRect, color_tabbar_active, CUI::CORNER_R, 10.0f); else - ui_draw_rect(r, color_tabbar_inactive, CORNER_R, 10.0f); - ui_do_label(r, text, r->h*fontmod_height, 0); + RenderTools()->DrawUIRect(pRect, color_tabbar_inactive, CUI::CORNER_R, 10.0f); + UI()->DoLabel(pRect, pText, pRect->h*fontmod_height, 0); + return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -void MENUS::ui_draw_grid_header(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int MENUS::DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect) +//void MENUS::ui_draw_grid_header(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) { - if(checked) - ui_draw_rect(r, vec4(1,1,1,0.5f), CORNER_T, 5.0f); - RECT t; - ui_vsplit_l(r, 5.0f, 0, &t); - ui_do_label(&t, text, r->h*fontmod_height, -1); + if(Checked) + RenderTools()->DrawUIRect(pRect, vec4(1,1,1,0.5f), CUI::CORNER_T, 5.0f); + CUIRect t; + pRect->VSplitLeft(5.0f, 0, &t); + UI()->DoLabel(&t, pText, pRect->h*fontmod_height, -1); + return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -void MENUS::ui_draw_list_row(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int MENUS::DoButton_ListRow(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - if(checked) + if(Checked) { - RECT sr = *r; - ui_margin(&sr, 1.5f, &sr); - ui_draw_rect(&sr, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f); + CUIRect sr = *pRect; + sr.Margin(1.5f, &sr); + RenderTools()->DrawUIRect(&sr, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 4.0f); } - ui_do_label(r, text, r->h*fontmod_height, -1); + UI()->DoLabel(pRect, pText, pRect->h*fontmod_height, -1); + return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -void MENUS::ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const RECT *r) +int MENUS::DoButton_CheckBox_Common(const void *pID, const char *pText, const char *pBoxText, const CUIRect *pRect) +//void MENUS::ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const CUIRect *r, const void *extra) { - RECT c = *r; - RECT t = *r; + CUIRect c = *pRect; + CUIRect t = *pRect; c.w = c.h; t.x += c.w; t.w -= c.w; - ui_vsplit_l(&t, 5.0f, 0, &t); + t.VSplitLeft(5.0f, 0, &t); - ui_margin(&c, 2.0f, &c); - ui_draw_rect(&c, vec4(1,1,1,0.25f)*button_color_mul(id), CORNER_ALL, 3.0f); + c.Margin(2.0f, &c); + RenderTools()->DrawUIRect(&c, vec4(1,1,1,0.25f)*button_color_mul(pID), CUI::CORNER_ALL, 3.0f); c.y += 2; - ui_do_label(&c, boxtext, r->h*fontmod_height*0.6f, 0); - ui_do_label(&t, text, r->h*fontmod_height*0.8f, -1); + UI()->DoLabel(&c, pBoxText, pRect->h*fontmod_height*0.6f, 0); + UI()->DoLabel(&t, pText, pRect->h*fontmod_height*0.8f, -1); + return UI()->DoButtonLogic(pID, pText, 0, pRect); } -void MENUS::ui_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int MENUS::DoButton_CheckBox(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - ui_draw_checkbox_common(id, text, checked?"X":"", r); + return DoButton_CheckBox_Common(pID, pText, Checked?"X":"", pRect); } -void MENUS::ui_draw_checkbox_number(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int MENUS::DoButton_CheckBox_Number(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { char buf[16]; - str_format(buf, sizeof(buf), "%d", checked); - ui_draw_checkbox_common(id, text, buf, r); + str_format(buf, sizeof(buf), "%d", Checked); + return DoButton_CheckBox_Common(pID, pText, buf, pRect); } -int MENUS::ui_do_edit_box(void *id, const RECT *rect, char *str, unsigned str_size, float font_size, bool hidden) +int MENUS::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden) { - int inside = ui_mouse_inside(rect); - int r = 0; - static int at_index = 0; + int Inside = UI()->MouseInside(pRect); + int ReturnValue = 0; + static int AtIndex = 0; - if(ui_last_active_item() == id) + if(UI()->LastActiveItem() == pID) { - int len = strlen(str); + int Len = strlen(pStr); - if (inside && ui_mouse_button(0)) + if(Inside && UI()->MouseButton(0)) { - int mx_rel = (int)(ui_mouse_x() - rect->x); + int mx_rel = (int)(UI()->MouseX() - pRect->x); - for (int i = 1; i <= len; i++) + for (int i = 1; i <= Len; i++) { - if (gfx_text_width(0, font_size, str, i) + 10 > mx_rel) + if (gfx_text_width(0, FontSize, pStr, i) + 10 > mx_rel) { - at_index = i - 1; + AtIndex = i - 1; break; } - if (i == len) - at_index = len; + if (i == Len) + AtIndex = Len; } } for(int i = 0; i < num_inputevents; i++) { - len = strlen(str); - LINEINPUT::manipulate(inputevents[i], str, str_size, &len, &at_index); + Len = strlen(pStr); + LINEINPUT::manipulate(inputevents[i], pStr, StrSize, &Len, &AtIndex); } } - bool just_got_active = false; + bool JustGotActive = false; - if(ui_active_item() == id) + if(UI()->ActiveItem() == pID) { - if(!ui_mouse_button(0)) - ui_set_active_item(0); + if(!UI()->MouseButton(0)) + UI()->SetActiveItem(0); } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == pID) { - if(ui_mouse_button(0)) + if(UI()->MouseButton(0)) { - if (ui_last_active_item() != id) - just_got_active = true; - ui_set_active_item(id); + if (UI()->LastActiveItem() != pID) + JustGotActive = true; + UI()->SetActiveItem(pID); } } - if(inside) - ui_set_hot_item(id); + if(Inside) + UI()->SetHotItem(pID); - RECT textbox = *rect; - ui_draw_rect(&textbox, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); - ui_vmargin(&textbox, 5.0f, &textbox); + CUIRect Textbox = *pRect; + RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); + Textbox.VMargin(5.0f, &Textbox); - const char *display_str = str; - char stars[128]; + const char *pDisplayStr = pStr; + char aStars[128]; - if(hidden) + if(Hidden) { - unsigned s = strlen(str); - if(s >= sizeof(stars)) - s = sizeof(stars)-1; - memset(stars, '*', s); - stars[s] = 0; - display_str = stars; + unsigned s = strlen(pStr); + if(s >= sizeof(aStars)) + s = sizeof(aStars)-1; + memset(aStars, '*', s); + aStars[s] = 0; + pDisplayStr = aStars; } - ui_do_label(&textbox, display_str, font_size, -1); + UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1); - if (ui_last_active_item() == id && !just_got_active) + if (UI()->LastActiveItem() == pID && !JustGotActive) { - float w = gfx_text_width(0, font_size, display_str, at_index); - textbox.x += w*ui_scale(); - ui_do_label(&textbox, "_", font_size, -1); + float w = gfx_text_width(0, FontSize, pDisplayStr, AtIndex); + Textbox.x += w*UI()->Scale(); + UI()->DoLabel(&Textbox, "_", FontSize, -1); } - return r; + return ReturnValue; } -float MENUS::ui_do_scrollbar_v(const void *id, const RECT *rect, float current) +float MENUS::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current) { - RECT handle; - static float offset_y; - ui_hsplit_t(rect, 33, &handle, 0); + CUIRect Handle; + static float OffsetY; + pRect->HSplitTop(33, &Handle, 0); - handle.y += (rect->h-handle.h)*current; + Handle.y += (pRect->h-Handle.h)*Current; /* logic */ - float ret = current; - int inside = ui_mouse_inside(&handle); + float ReturnValue = Current; + int Inside = UI()->MouseInside(&Handle); - if(ui_active_item() == id) + if(UI()->ActiveItem() == pID) { - if(!ui_mouse_button(0)) - ui_set_active_item(0); + if(!UI()->MouseButton(0)) + UI()->SetActiveItem(0); - float min = rect->y; - float max = rect->h-handle.h; - float cur = ui_mouse_y()-offset_y; - ret = (cur-min)/max; - if(ret < 0.0f) ret = 0.0f; - if(ret > 1.0f) ret = 1.0f; + float min = pRect->y; + float max = pRect->h-Handle.h; + float cur = UI()->MouseY()-OffsetY; + ReturnValue = (cur-min)/max; + if(ReturnValue < 0.0f) ReturnValue = 0.0f; + if(ReturnValue > 1.0f) ReturnValue = 1.0f; } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == pID) { - if(ui_mouse_button(0)) + if(UI()->MouseButton(0)) { - ui_set_active_item(id); - offset_y = ui_mouse_y()-handle.y; + UI()->SetActiveItem(pID); + OffsetY = UI()->MouseY()-Handle.y; } } - if(inside) - ui_set_hot_item(id); + if(Inside) + UI()->SetHotItem(pID); // render - RECT rail; - ui_vmargin(rect, 5.0f, &rail); - ui_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); - - RECT slider = handle; - slider.w = rail.x-slider.x; - ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_L, 2.5f); - slider.x = rail.x+rail.w; - ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_R, 2.5f); - - slider = handle; - ui_margin(&slider, 5.0f, &slider); - ui_draw_rect(&slider, vec4(1,1,1,0.25f)*button_color_mul(id), CORNER_ALL, 2.5f); + CUIRect Rail; + pRect->VMargin(5.0f, &Rail); + RenderTools()->DrawUIRect(&Rail, vec4(1,1,1,0.25f), 0, 0.0f); + + CUIRect Slider = Handle; + Slider.w = Rail.x-Slider.x; + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_L, 2.5f); + Slider.x = Rail.x+Rail.w; + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_R, 2.5f); + + Slider = Handle; + Slider.Margin(5.0f, &Slider); + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*button_color_mul(pID), CUI::CORNER_ALL, 2.5f); - return ret; + return ReturnValue; } -float MENUS::ui_do_scrollbar_h(const void *id, const RECT *rect, float current) +float MENUS::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current) { - RECT handle; - static float offset_x; - ui_vsplit_l(rect, 33, &handle, 0); + CUIRect Handle; + static float OffsetX; + pRect->VSplitLeft(33, &Handle, 0); - handle.x += (rect->w-handle.w)*current; + Handle.x += (pRect->w-Handle.w)*Current; /* logic */ - float ret = current; - int inside = ui_mouse_inside(&handle); + float ReturnValue = Current; + int Inside = UI()->MouseInside(&Handle); - if(ui_active_item() == id) + if(UI()->ActiveItem() == pID) { - if(!ui_mouse_button(0)) - ui_set_active_item(0); + if(!UI()->MouseButton(0)) + UI()->SetActiveItem(0); - float min = rect->x; - float max = rect->w-handle.w; - float cur = ui_mouse_x()-offset_x; - ret = (cur-min)/max; - if(ret < 0.0f) ret = 0.0f; - if(ret > 1.0f) ret = 1.0f; + float min = pRect->x; + float max = pRect->w-Handle.w; + float cur = UI()->MouseX()-OffsetX; + ReturnValue = (cur-min)/max; + if(ReturnValue < 0.0f) ReturnValue = 0.0f; + if(ReturnValue > 1.0f) ReturnValue = 1.0f; } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == pID) { - if(ui_mouse_button(0)) + if(UI()->MouseButton(0)) { - ui_set_active_item(id); - offset_x = ui_mouse_x()-handle.x; + UI()->SetActiveItem(pID); + OffsetX = UI()->MouseX()-Handle.x; } } - if(inside) - ui_set_hot_item(id); + if(Inside) + UI()->SetHotItem(pID); // render - RECT rail; - ui_hmargin(rect, 5.0f, &rail); - ui_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); - - RECT slider = handle; - slider.h = rail.y-slider.y; - ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_T, 2.5f); - slider.y = rail.y+rail.h; - ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_B, 2.5f); - - slider = handle; - ui_margin(&slider, 5.0f, &slider); - ui_draw_rect(&slider, vec4(1,1,1,0.25f)*button_color_mul(id), CORNER_ALL, 2.5f); + CUIRect Rail; + pRect->HMargin(5.0f, &Rail); + RenderTools()->DrawUIRect(&Rail, vec4(1,1,1,0.25f), 0, 0.0f); + + CUIRect Slider = Handle; + Slider.h = Rail.y-Slider.y; + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_T, 2.5f); + Slider.y = Rail.y+Rail.h; + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_B, 2.5f); + + Slider = Handle; + Slider.Margin(5.0f, &Slider); + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*button_color_mul(pID), CUI::CORNER_ALL, 2.5f); - return ret; + return ReturnValue; } -int MENUS::ui_do_key_reader(void *id, const RECT *rect, int key) +int MENUS::DoKeyReader(void *pID, const CUIRect *pRect, int Key) { // process - static void *grabbed_id = 0; - static bool mouse_released = true; - int inside = ui_mouse_inside(rect); - int new_key = key; + static void *pGrabbedID = 0; + static bool MouseReleased = true; + int Inside = UI()->MouseInside(pRect); + int NewKey = Key; - if(!ui_mouse_button(0) && grabbed_id == id) - mouse_released = true; + if(!UI()->MouseButton(0) && pGrabbedID == pID) + MouseReleased = true; - if(ui_active_item() == id) + if(UI()->ActiveItem() == pID) { if(binder.got_key) { - new_key = binder.key.key; + NewKey = binder.key.key; binder.got_key = false; - ui_set_active_item(0); - mouse_released = false; - grabbed_id = id; + UI()->SetActiveItem(0); + MouseReleased = false; + pGrabbedID = pID; } } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == pID) { - if(ui_mouse_button(0) && mouse_released) + if(UI()->MouseButton(0) && MouseReleased) { binder.take_key = true; binder.got_key = false; - ui_set_active_item(id); + UI()->SetActiveItem(pID); } } - if(inside) - ui_set_hot_item(id); + if(Inside) + UI()->SetHotItem(pID); // draw - if (ui_active_item() == id) - ui_draw_keyselect_button(id, "???", 0, rect, 0); + if (UI()->ActiveItem() == pID) + DoButton_KeySelect(pID, "???", 0, pRect); else { - if(key == 0) - ui_draw_keyselect_button(id, "", 0, rect, 0); + if(Key == 0) + DoButton_KeySelect(pID, "", 0, pRect); else - ui_draw_keyselect_button(id, inp_key_name(key), 0, rect, 0); + DoButton_KeySelect(pID, inp_key_name(Key), 0, pRect); } - return new_key; + return NewKey; } -int MENUS::render_menubar(RECT r) +int MENUS::render_menubar(CUIRect r) { - RECT box = r; - RECT button; + CUIRect box = r; + CUIRect button; int active_page = config.ui_page; int new_page = -1; @@ -456,46 +467,43 @@ int MENUS::render_menubar(RECT r) /* offline menus */ if(0) // this is not done yet { - ui_vsplit_l(&box, 90.0f, &button, &box); + box.VSplitLeft(90.0f, &button, &box); static int news_button=0; - if (ui_do_button(&news_button, localize("News"), active_page==PAGE_NEWS, &button, ui_draw_menu_tab_button, 0)) + if (DoButton_MenuTab(&news_button, localize("News"), active_page==PAGE_NEWS, &button, 0)) new_page = PAGE_NEWS; - ui_vsplit_l(&box, 30.0f, 0, &box); + box.VSplitLeft(30.0f, 0, &box); } - ui_vsplit_l(&box, 100.0f, &button, &box); + box.VSplitLeft(100.0f, &button, &box); static int internet_button=0; - int corners = CORNER_TL; - if (ui_do_button(&internet_button, localize("Internet"), active_page==PAGE_INTERNET, &button, ui_draw_menu_tab_button, &corners)) + if(DoButton_MenuTab(&internet_button, localize("Internet"), active_page==PAGE_INTERNET, &button, CUI::CORNER_TL)) { client_serverbrowse_refresh(BROWSETYPE_INTERNET); new_page = PAGE_INTERNET; } - //ui_vsplit_l(&box, 4.0f, 0, &box); - ui_vsplit_l(&box, 80.0f, &button, &box); + //box.VSplitLeft(4.0f, 0, &box); + box.VSplitLeft(80.0f, &button, &box); static int lan_button=0; - corners = 0; - if (ui_do_button(&lan_button, localize("LAN"), active_page==PAGE_LAN, &button, ui_draw_menu_tab_button, &corners)) + if(DoButton_MenuTab(&lan_button, localize("LAN"), active_page==PAGE_LAN, &button, 0)) { client_serverbrowse_refresh(BROWSETYPE_LAN); new_page = PAGE_LAN; } - //ui_vsplit_l(&box, 4.0f, 0, &box); - ui_vsplit_l(&box, 110.0f, &button, &box); + //box.VSplitLeft(4.0f, 0, &box); + box.VSplitLeft(110.0f, &button, &box); static int favorites_button=0; - corners = CORNER_TR; - if (ui_do_button(&favorites_button, localize("Favorites"), active_page==PAGE_FAVORITES, &button, ui_draw_menu_tab_button, &corners)) + if(DoButton_MenuTab(&favorites_button, localize("Favorites"), active_page==PAGE_FAVORITES, &button, CUI::CORNER_TR)) { client_serverbrowse_refresh(BROWSETYPE_FAVORITES); new_page = PAGE_FAVORITES; } - ui_vsplit_l(&box, 4.0f*5, 0, &box); - ui_vsplit_l(&box, 100.0f, &button, &box); + box.VSplitLeft(4.0f*5, 0, &box); + box.VSplitLeft(100.0f, &button, &box); static int demos_button=0; - if (ui_do_button(&demos_button, localize("Demos"), active_page==PAGE_DEMOS, &button, ui_draw_menu_tab_button, 0)) + if(DoButton_MenuTab(&demos_button, localize("Demos"), active_page==PAGE_DEMOS, &button, 0)) { demolist_populate(); new_page = PAGE_DEMOS; @@ -504,44 +512,44 @@ int MENUS::render_menubar(RECT r) else { /* online menus */ - ui_vsplit_l(&box, 90.0f, &button, &box); + box.VSplitLeft(90.0f, &button, &box); static int game_button=0; - if (ui_do_button(&game_button, localize("Game"), active_page==PAGE_GAME, &button, ui_draw_menu_tab_button, 0)) + if(DoButton_MenuTab(&game_button, localize("Game"), active_page==PAGE_GAME, &button, 0)) new_page = PAGE_GAME; - ui_vsplit_l(&box, 4.0f, 0, &box); - ui_vsplit_l(&box, 140.0f, &button, &box); + box.VSplitLeft(4.0f, 0, &box); + box.VSplitLeft(140.0f, &button, &box); static int server_info_button=0; - if (ui_do_button(&server_info_button, localize("Server info"), active_page==PAGE_SERVER_INFO, &button, ui_draw_menu_tab_button, 0)) + if(DoButton_MenuTab(&server_info_button, localize("Server info"), active_page==PAGE_SERVER_INFO, &button, 0)) new_page = PAGE_SERVER_INFO; - ui_vsplit_l(&box, 4.0f, 0, &box); - ui_vsplit_l(&box, 140.0f, &button, &box); + box.VSplitLeft(4.0f, 0, &box); + box.VSplitLeft(140.0f, &button, &box); static int callvote_button=0; - if (ui_do_button(&callvote_button, localize("Call vote"), active_page==PAGE_CALLVOTE, &button, ui_draw_menu_tab_button, 0)) + if(DoButton_MenuTab(&callvote_button, localize("Call vote"), active_page==PAGE_CALLVOTE, &button, 0)) new_page = PAGE_CALLVOTE; - ui_vsplit_l(&box, 30.0f, 0, &box); + box.VSplitLeft(30.0f, 0, &box); } /* - ui_vsplit_r(&box, 110.0f, &box, &button); + box.VSplitRight(110.0f, &box, &button); static int system_button=0; - if (ui_do_button(&system_button, "System", config.ui_page==PAGE_SYSTEM, &button, ui_draw_menu_tab_button, 0)) + if (UI()->DoButton(&system_button, "System", config.ui_page==PAGE_SYSTEM, &button)) config.ui_page = PAGE_SYSTEM; - ui_vsplit_r(&box, 30.0f, &box, 0); + box.VSplitRight(30.0f, &box, 0); */ - ui_vsplit_r(&box, 90.0f, &box, &button); + box.VSplitRight(90.0f, &box, &button); static int quit_button=0; - if (ui_do_button(&quit_button, localize("Quit"), 0, &button, ui_draw_menu_tab_button, 0)) + if(DoButton_MenuTab(&quit_button, localize("Quit"), 0, &button, 0)) popup = POPUP_QUIT; - ui_vsplit_r(&box, 10.0f, &box, &button); - ui_vsplit_r(&box, 130.0f, &box, &button); + box.VSplitRight(10.0f, &box, &button); + box.VSplitRight(130.0f, &box, &button); static int settings_button=0; - if (ui_do_button(&settings_button, localize("Settings"), active_page==PAGE_SETTINGS, &button, ui_draw_menu_tab_button, 0)) + if(DoButton_MenuTab(&settings_button, localize("Settings"), active_page==PAGE_SETTINGS, &button, 0)) new_page = PAGE_SETTINGS; if(new_page != -1) @@ -570,8 +578,8 @@ void MENUS::render_loading(float percent) vec3 rgb = hsl_to_rgb(vec3(config.ui_color_hue/255.0f, config.ui_color_sat/255.0f, config.ui_color_lht/255.0f)); gui_color = vec4(rgb.r, rgb.g, rgb.b, config.ui_color_alpha/255.0f); - RECT screen = *ui_screen(); - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + CUIRect screen = *UI()->Screen(); + Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); render_background(); @@ -582,37 +590,37 @@ void MENUS::render_loading(float percent) float x = screen.w/2-w/2; float y = screen.h/2-h/2; - gfx_blend_normal(); + Graphics()->BlendNormal(); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.50f); - draw_round_rect(x, y, w, h, 40.0f); - gfx_quads_end(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0,0,0,0.50f); + RenderTools()->draw_round_rect(x, y, w, h, 40.0f); + Graphics()->QuadsEnd(); const char *caption = localize("Loading"); tw = gfx_text_width(0, 48.0f, caption, -1); - RECT r; + CUIRect r; r.x = x; r.y = y+20; r.w = w; r.h = h; - ui_do_label(&r, caption, 48.0f, 0, -1); + UI()->DoLabel(&r, caption, 48.0f, 0, -1); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(1,1,1,0.75f); - draw_round_rect(x+40, y+h-75, (w-80)*percent, 25, 5.0f); - gfx_quads_end(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(1,1,1,0.75f); + RenderTools()->draw_round_rect(x+40, y+h-75, (w-80)*percent, 25, 5.0f); + Graphics()->QuadsEnd(); gfx_swap(); } -void MENUS::render_news(RECT main_view) +void MENUS::render_news(CUIRect main_view) { - ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); + RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_ALL, 10.0f); } void MENUS::on_init() @@ -677,8 +685,8 @@ void MENUS::popup_message(const char *topic, const char *body, const char *butto int MENUS::render() { - RECT screen = *ui_screen(); - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + CUIRect screen = *UI()->Screen(); + Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); static bool first = true; if(first) @@ -702,17 +710,17 @@ int MENUS::render() color_tabbar_active = color_tabbar_active_outgame; } - RECT tab_bar; - RECT main_view; + CUIRect tab_bar; + CUIRect main_view; // some margin around the screen - ui_margin(&screen, 10.0f, &screen); + screen.Margin(10.0f, &screen); if(popup == POPUP_NONE) { // do tab bar - ui_hsplit_t(&screen, 24.0f, &tab_bar, &main_view); - ui_vmargin(&tab_bar, 20.0f, &tab_bar); + screen.HSplitTop(24.0f, &tab_bar, &main_view); + tab_bar.VMargin(20.0f, &tab_bar); render_menubar(tab_bar); // news is not implemented yet @@ -750,8 +758,8 @@ int MENUS::render() else { // make sure that other windows doesn't do anything funnay! - //ui_set_hot_item(0); - //ui_set_active_item(0); + //UI()->SetHotItem(0); + //UI()->SetActiveItem(0); char buf[128]; const char *title = ""; const char *extra_text = ""; @@ -809,109 +817,109 @@ int MENUS::render() extra_align = -1; } - RECT box, part; + CUIRect box, part; box = screen; - ui_vmargin(&box, 150.0f, &box); - ui_hmargin(&box, 150.0f, &box); + box.VMargin(150.0f, &box); + box.HMargin(150.0f, &box); // render the box - ui_draw_rect(&box, vec4(0,0,0,0.5f), CORNER_ALL, 15.0f); + RenderTools()->DrawUIRect(&box, vec4(0,0,0,0.5f), CUI::CORNER_ALL, 15.0f); - ui_hsplit_t(&box, 20.f, &part, &box); - ui_hsplit_t(&box, 24.f, &part, &box); - ui_do_label(&part, title, 24.f, 0); - ui_hsplit_t(&box, 20.f, &part, &box); - ui_hsplit_t(&box, 24.f, &part, &box); - ui_vmargin(&part, 20.f, &part); + box.HSplitTop(20.f, &part, &box); + box.HSplitTop(24.f, &part, &box); + UI()->DoLabel(&part, title, 24.f, 0); + box.HSplitTop(20.f, &part, &box); + box.HSplitTop(24.f, &part, &box); + part.VMargin(20.f, &part); if(extra_align == -1) - ui_do_label(&part, extra_text, 20.f, -1, (int)part.w); + UI()->DoLabel(&part, extra_text, 20.f, -1, (int)part.w); else - ui_do_label(&part, extra_text, 20.f, 0, -1); + UI()->DoLabel(&part, extra_text, 20.f, 0, -1); if(popup == POPUP_QUIT) { - RECT yes, no; - ui_hsplit_b(&box, 20.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); - ui_vmargin(&part, 80.0f, &part); + CUIRect yes, no; + box.HSplitBottom(20.f, &box, &part); + box.HSplitBottom(24.f, &box, &part); + part.VMargin(80.0f, &part); - ui_vsplit_mid(&part, &no, &yes); + part.VSplitMid(&no, &yes); - ui_vmargin(&yes, 20.0f, &yes); - ui_vmargin(&no, 20.0f, &no); + yes.VMargin(20.0f, &yes); + no.VMargin(20.0f, &no); static int button_abort = 0; - if(ui_do_button(&button_abort, localize("No"), 0, &no, ui_draw_menu_button, 0) || escape_pressed) + if(DoButton_Menu(&button_abort, localize("No"), 0, &no) || escape_pressed) popup = POPUP_NONE; static int button_tryagain = 0; - if(ui_do_button(&button_tryagain, localize("Yes"), 0, &yes, ui_draw_menu_button, 0) || enter_pressed) + if(DoButton_Menu(&button_tryagain, localize("Yes"), 0, &yes) || enter_pressed) client_quit(); } else if(popup == POPUP_PASSWORD) { - RECT label, textbox, tryagain, abort; + CUIRect label, textbox, tryagain, abort; - ui_hsplit_b(&box, 20.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); - ui_vmargin(&part, 80.0f, &part); + box.HSplitBottom(20.f, &box, &part); + box.HSplitBottom(24.f, &box, &part); + part.VMargin(80.0f, &part); - ui_vsplit_mid(&part, &abort, &tryagain); + part.VSplitMid(&abort, &tryagain); - ui_vmargin(&tryagain, 20.0f, &tryagain); - ui_vmargin(&abort, 20.0f, &abort); + tryagain.VMargin(20.0f, &tryagain); + abort.VMargin(20.0f, &abort); static int button_abort = 0; - if(ui_do_button(&button_abort, localize("Abort"), 0, &abort, ui_draw_menu_button, 0) || escape_pressed) + if(DoButton_Menu(&button_abort, localize("Abort"), 0, &abort) || escape_pressed) popup = POPUP_NONE; static int button_tryagain = 0; - if(ui_do_button(&button_tryagain, localize("Try again"), 0, &tryagain, ui_draw_menu_button, 0) || enter_pressed) + if(DoButton_Menu(&button_tryagain, localize("Try again"), 0, &tryagain) || enter_pressed) { client_connect(config.ui_server_address); } - ui_hsplit_b(&box, 60.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); + box.HSplitBottom(60.f, &box, &part); + box.HSplitBottom(24.f, &box, &part); - ui_vsplit_l(&part, 60.0f, 0, &label); - ui_vsplit_l(&label, 100.0f, 0, &textbox); - ui_vsplit_l(&textbox, 20.0f, 0, &textbox); - ui_vsplit_r(&textbox, 60.0f, &textbox, 0); - ui_do_label(&label, localize("Password"), 20, -1); - ui_do_edit_box(&config.password, &textbox, config.password, sizeof(config.password), 14.0f, true); + part.VSplitLeft(60.0f, 0, &label); + label.VSplitLeft(100.0f, 0, &textbox); + textbox.VSplitLeft(20.0f, 0, &textbox); + textbox.VSplitRight(60.0f, &textbox, 0); + UI()->DoLabel(&label, localize("Password"), 20, -1); + DoEditBox(&config.password, &textbox, config.password, sizeof(config.password), 14.0f, true); } else if(popup == POPUP_FIRST_LAUNCH) { - RECT label, textbox; + CUIRect label, textbox; - ui_hsplit_b(&box, 20.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); - ui_vmargin(&part, 80.0f, &part); + box.HSplitBottom(20.f, &box, &part); + box.HSplitBottom(24.f, &box, &part); + part.VMargin(80.0f, &part); static int enter_button = 0; - if(ui_do_button(&enter_button, localize("Enter"), 0, &part, ui_draw_menu_button, 0) || enter_pressed) + if(DoButton_Menu(&enter_button, localize("Enter"), 0, &part) || enter_pressed) popup = POPUP_NONE; - ui_hsplit_b(&box, 40.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); + box.HSplitBottom(40.f, &box, &part); + box.HSplitBottom(24.f, &box, &part); - ui_vsplit_l(&part, 60.0f, 0, &label); - ui_vsplit_l(&label, 100.0f, 0, &textbox); - ui_vsplit_l(&textbox, 20.0f, 0, &textbox); - ui_vsplit_r(&textbox, 60.0f, &textbox, 0); - ui_do_label(&label, localize("Nickname"), 20, -1); - ui_do_edit_box(&config.player_name, &textbox, config.player_name, sizeof(config.player_name), 14.0f); + part.VSplitLeft(60.0f, 0, &label); + label.VSplitLeft(100.0f, 0, &textbox); + textbox.VSplitLeft(20.0f, 0, &textbox); + textbox.VSplitRight(60.0f, &textbox, 0); + UI()->DoLabel(&label, localize("Nickname"), 20, -1); + DoEditBox(&config.player_name, &textbox, config.player_name, sizeof(config.player_name), 14.0f); } else { - ui_hsplit_b(&box, 20.f, &box, &part); - ui_hsplit_b(&box, 24.f, &box, &part); - ui_vmargin(&part, 120.0f, &part); + box.HSplitBottom(20.f, &box, &part); + box.HSplitBottom(24.f, &box, &part); + part.VMargin(120.0f, &part); static int button = 0; - if(ui_do_button(&button, button_text, 0, &part, ui_draw_menu_button, 0) || escape_pressed || enter_pressed) + if(DoButton_Menu(&button, button_text, 0, &part) || escape_pressed || enter_pressed) { if(popup == POPUP_CONNECTING) client_disconnect(); @@ -949,8 +957,8 @@ bool MENUS::on_mousemove(float x, float y) mouse_pos.y += y; if(mouse_pos.x < 0) mouse_pos.x = 0; if(mouse_pos.y < 0) mouse_pos.y = 0; - if(mouse_pos.x > gfx_screenwidth()) mouse_pos.x = gfx_screenwidth(); - if(mouse_pos.y > gfx_screenheight()) mouse_pos.y = gfx_screenheight(); + if(mouse_pos.x > Graphics()->ScreenWidth()) mouse_pos.x = Graphics()->ScreenWidth(); + if(mouse_pos.y > Graphics()->ScreenHeight()) mouse_pos.y = Graphics()->ScreenHeight(); return true; } @@ -993,8 +1001,8 @@ void MENUS::on_statechange(int new_state, int old_state) if(strstr(client_error_string(), "password")) { popup = POPUP_PASSWORD; - ui_set_hot_item(&config.password); - ui_set_active_item(&config.password); + UI()->SetHotItem(&config.password); + UI()->SetActiveItem(&config.password); } else popup = POPUP_DISCONNECTED; @@ -1028,10 +1036,10 @@ void MENUS::on_render() gfx_text_set_cursor(&cursor, 10, 30, 15, TEXTFLAG_RENDER); gfx_text_ex(&cursor, "ようこそ - ガイド", -1); - //gfx_texture_set(-1); - gfx_quads_begin(); - gfx_quads_drawTL(60, 60, 5000, 5000); - gfx_quads_end(); + //Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->QuadsDrawTL(60, 60, 5000, 5000); + Graphics()->QuadsEnd(); return;*/ if(client_state() != CLIENTSTATE_ONLINE && client_state() != CLIENTSTATE_DEMOPLAYBACK) @@ -1039,8 +1047,8 @@ void MENUS::on_render() if(client_state() == CLIENTSTATE_DEMOPLAYBACK) { - RECT screen = *ui_screen(); - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + CUIRect screen = *UI()->Screen(); + Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); render_demoplayer(screen); } @@ -1081,36 +1089,36 @@ void MENUS::on_render() gui_color.a); // update the ui - RECT *screen = ui_screen(); - float mx = (mouse_pos.x/(float)gfx_screenwidth())*screen->w; - float my = (mouse_pos.y/(float)gfx_screenheight())*screen->h; + CUIRect *screen = UI()->Screen(); + float mx = (mouse_pos.x/(float)Graphics()->ScreenWidth())*screen->w; + float my = (mouse_pos.y/(float)Graphics()->ScreenHeight())*screen->h; int buttons = 0; if(inp_key_pressed(KEY_MOUSE_1)) buttons |= 1; if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2; if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4; - ui_update(mx,my,mx*3.0f,my*3.0f,buttons); + UI()->Update(mx,my,mx*3.0f,my*3.0f,buttons); // render if(client_state() != CLIENTSTATE_DEMOPLAYBACK) render(); // render cursor - gfx_texture_set(data->images[IMAGE_CURSOR].id); - gfx_quads_begin(); - gfx_setcolor(1,1,1,1); - gfx_quads_drawTL(mx,my,24,24); - gfx_quads_end(); + Graphics()->TextureSet(data->images[IMAGE_CURSOR].id); + Graphics()->QuadsBegin(); + Graphics()->SetColor(1,1,1,1); + Graphics()->QuadsDrawTL(mx,my,24,24); + Graphics()->QuadsEnd(); // render debug information if(config.debug) { - RECT screen = *ui_screen(); - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h); + CUIRect screen = *UI()->Screen(); + Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); char buf[512]; - str_format(buf, sizeof(buf), "%p %p %p", ui_hot_item(), ui_active_item(), ui_last_active_item()); + str_format(buf, sizeof(buf), "%p %p %p", UI()->HotItem(), UI()->ActiveItem(), UI()->LastActiveItem()); TEXT_CURSOR cursor; gfx_text_set_cursor(&cursor, 10, 10, 10, TEXTFLAG_RENDER); gfx_text_ex(&cursor, buf, -1); @@ -1125,53 +1133,53 @@ static int texture_blob = -1; void MENUS::render_background() { - //gfx_clear(1,1,1); + //Graphics()->Clear(1,1,1); //render_sunrays(0,0); if(texture_blob == -1) - texture_blob = gfx_load_texture("blob.png", IMG_AUTO, 0); + texture_blob = Graphics()->LoadTexture("blob.png", IMG_AUTO, 0); - float sw = 300*gfx_screenaspect(); + float sw = 300*Graphics()->ScreenAspect(); float sh = 300; - gfx_mapscreen(0, 0, sw, sh); + Graphics()->MapScreen(0, 0, sw, sh); - RECT s = *ui_screen(); + CUIRect s = *UI()->Screen(); // render background color - gfx_texture_set(-1); - gfx_quads_begin(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); //vec4 bottom(gui_color.r*0.3f, gui_color.g*0.3f, gui_color.b*0.3f, 1.0f); //vec4 bottom(0, 0, 0, 1.0f); vec4 bottom(gui_color.r, gui_color.g, gui_color.b, 1.0f); vec4 top(gui_color.r, gui_color.g, gui_color.b, 1.0f); - gfx_setcolorvertex(0, top.r, top.g, top.b, top.a); - gfx_setcolorvertex(1, top.r, top.g, top.b, top.a); - gfx_setcolorvertex(2, bottom.r, bottom.g, bottom.b, bottom.a); - gfx_setcolorvertex(3, bottom.r, bottom.g, bottom.b, bottom.a); - gfx_quads_drawTL(0, 0, sw, sh); - gfx_quads_end(); + Graphics()->SetColorVertex(0, top.r, top.g, top.b, top.a); + Graphics()->SetColorVertex(1, top.r, top.g, top.b, top.a); + Graphics()->SetColorVertex(2, bottom.r, bottom.g, bottom.b, bottom.a); + Graphics()->SetColorVertex(3, bottom.r, bottom.g, bottom.b, bottom.a); + Graphics()->QuadsDrawTL(0, 0, sw, sh); + Graphics()->QuadsEnd(); // render the tiles - gfx_texture_set(-1); - gfx_quads_begin(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); float size = 15.0f; float offset_time = fmod(client_localtime()*0.15f, 2.0f); for(int y = -2; y < (int)(sw/size); y++) for(int x = -2; x < (int)(sh/size); x++) { - gfx_setcolor(0,0,0,0.045f); - gfx_quads_drawTL((x-offset_time)*size*2+(y&1)*size, (y+offset_time)*size, size, size); + Graphics()->SetColor(0,0,0,0.045f); + Graphics()->QuadsDrawTL((x-offset_time)*size*2+(y&1)*size, (y+offset_time)*size, size, size); } - gfx_quads_end(); + Graphics()->QuadsEnd(); // render border fade - gfx_texture_set(texture_blob); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5f); - gfx_quads_drawTL(-100, -100, sw+200, sh+200); - gfx_quads_end(); + Graphics()->TextureSet(texture_blob); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0,0,0,0.5f); + Graphics()->QuadsDrawTL(-100, -100, sw+200, sh+200); + Graphics()->QuadsEnd(); // restore screen - {RECT screen = *ui_screen(); - gfx_mapscreen(screen.x, screen.y, screen.w, screen.h);} + {CUIRect screen = *UI()->Screen(); + Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h);} } diff --git a/src/game/client/components/menus.hpp b/src/game/client/components/menus.hpp index 15369dbc..02759403 100644 --- a/src/game/client/components/menus.hpp +++ b/src/game/client/components/menus.hpp @@ -26,39 +26,58 @@ class MENUS : public COMPONENT static vec4 color_tabbar_inactive; static vec4 color_tabbar_active; - static vec4 button_color_mul(const void *id); + vec4 button_color_mul(const void *pID); - static void ui_draw_demoplayer_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); + int DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + int DoButton_MenuTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Corners); + int DoButton_SettingsTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect); - static void ui_draw_browse_icon(int what, const RECT *r); - static void ui_draw_menu_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); - static void ui_draw_keyselect_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); - static void ui_draw_menu_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); - static void ui_draw_settings_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); - static void ui_draw_grid_header(const void *id, const char *text, int checked, const RECT *r, const void *extra); - static void ui_draw_list_row(const void *id, const char *text, int checked, const RECT *r, const void *extra); - static void ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const RECT *r); - static void ui_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, const void *extra); - static void ui_draw_checkbox_number(const void *id, const char *text, int checked, const RECT *r, const void *extra); - static int ui_do_edit_box(void *id, const RECT *rect, char *str, unsigned str_size, float font_size, bool hidden=false); + int DoButton_CheckBox_Common(const void *pID, const char *pText, const char *pBoxText, const CUIRect *pRect); + int DoButton_CheckBox(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + int DoButton_CheckBox_Number(const void *pID, const char *pText, int Checked, const CUIRect *pRect); - static float ui_do_scrollbar_v(const void *id, const RECT *rect, float current); - static float ui_do_scrollbar_h(const void *id, const RECT *rect, float current); + /*static void ui_draw_menu_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + static void ui_draw_keyselect_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + static void ui_draw_menu_tab_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + static void ui_draw_settings_tab_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + */ - static int ui_do_key_reader(void *id, const RECT *rect, int key); - static void ui_do_getbuttons(int start, int stop, RECT view); + int DoButton_BrowseIcon(int Checked, const CUIRect *pRect); + int DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + int DoButton_ListRow(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + + //static void ui_draw_browse_icon(int what, const CUIRect *r); + //static void ui_draw_grid_header(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + + /*static void ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const CUIRect *r, const void *extra); + static void ui_draw_checkbox(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + static void ui_draw_checkbox_number(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + */ + int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden=false); + //static int ui_do_edit_box(void *id, const CUIRect *rect, char *str, unsigned str_size, float font_size, bool hidden=false); + + float DoScrollbarV(const void *pID, const CUIRect *pRect, float Current); + float DoScrollbarH(const void *pID, const CUIRect *pRect, float Current); + int DoButton_KeySelect(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + int DoKeyReader(void *pID, const CUIRect *pRect, int Key); + + //static int ui_do_key_reader(void *id, const CUIRect *rect, int key); + void ui_do_getbuttons(int start, int stop, CUIRect view); struct LISTBOXITEM { int visible; int selected; - RECT rect; + CUIRect rect; + CUIRect hitrect; }; - static void ui_do_listbox_start(void *id, const RECT *rect, float row_height, const char *title, int num_items, int selected_index); - static LISTBOXITEM ui_do_listbox_nextitem(void *id); - static int ui_do_listbox_end(); + void ui_do_listbox_start(void *id, const CUIRect *rect, float row_height, const char *title, int num_items, int selected_index); + LISTBOXITEM ui_do_listbox_nextitem(void *id); + static LISTBOXITEM ui_do_listbox_nextrow(); + int ui_do_listbox_end(); //static void demolist_listdir_callback(const char *name, int is_dir, void *user); //static void demolist_list_callback(const RECT *rect, int index, void *user); @@ -143,39 +162,42 @@ class MENUS : public COMPONENT // found in menus.cpp int render(); - void render_background(); + //void render_background(); //void render_loading(float percent); - int render_menubar(RECT r); - void render_news(RECT main_view); + int render_menubar(CUIRect r); + void render_news(CUIRect main_view); // found in menus_demo.cpp - void render_demoplayer(RECT main_view); - void render_demolist(RECT main_view); + void render_demoplayer(CUIRect main_view); + void render_demolist(CUIRect main_view); // found in menus_ingame.cpp - void render_game(RECT main_view); - void render_serverinfo(RECT main_view); - void render_servercontrol(RECT main_view); - void render_servercontrol_kick(RECT main_view); - void render_servercontrol_server(RECT main_view); + void render_game(CUIRect main_view); + void render_serverinfo(CUIRect main_view); + void render_servercontrol(CUIRect main_view); + void render_servercontrol_kick(CUIRect main_view); + void render_servercontrol_server(CUIRect main_view); // found in menus_browser.cpp int selected_index; - void render_serverbrowser_serverlist(RECT view); - void render_serverbrowser_serverdetail(RECT view); - void render_serverbrowser_filters(RECT view); - void render_serverbrowser(RECT main_view); + void render_serverbrowser_serverlist(CUIRect view); + void render_serverbrowser_serverdetail(CUIRect view); + void render_serverbrowser_filters(CUIRect view); + void render_serverbrowser(CUIRect main_view); // found in menus_settings.cpp - void render_settings_general(RECT main_view); - void render_settings_player(RECT main_view); - void render_settings_controls(RECT main_view); - void render_settings_graphics(RECT main_view); - void render_settings_sound(RECT main_view); - void render_settings(RECT main_view); + void render_settings_general(CUIRect main_view); + void render_settings_player(CUIRect main_view); + void render_settings_controls(CUIRect main_view); + void render_settings_graphics(CUIRect main_view); + void render_settings_sound(CUIRect main_view); + void render_settings(CUIRect main_view); void set_active(bool active); public: + void render_background(); + + static MENUS_KEYBINDER binder; MENUS(); diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index a7f21084..dcf68f8d 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -12,17 +12,17 @@ #include #include -void MENUS::render_serverbrowser_serverlist(RECT view) +void MENUS::render_serverbrowser_serverlist(CUIRect view) { - RECT headers; - RECT status; + CUIRect headers; + CUIRect status; - ui_hsplit_t(&view, listheader_height, &headers, &view); - ui_hsplit_b(&view, 28.0f, &view, &status); + view.HSplitTop(listheader_height, &headers, &view); + view.HSplitBottom(28.0f, &view, &status); // split of the scrollbar - ui_draw_rect(&headers, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui_vsplit_r(&headers, 20.0f, &headers, 0); + RenderTools()->DrawUIRect(&headers, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); + headers.VSplitRight(20.0f, &headers, 0); struct column { @@ -32,8 +32,8 @@ void MENUS::render_serverbrowser_serverlist(RECT view) int direction; float width; int flags; - RECT rect; - RECT spacer; + CUIRect rect; + CUIRect spacer; }; enum @@ -72,12 +72,12 @@ void MENUS::render_serverbrowser_serverlist(RECT view) { if(cols[i].direction == -1) { - ui_vsplit_l(&headers, cols[i].width, &cols[i].rect, &headers); + headers.VSplitLeft(cols[i].width, &cols[i].rect, &headers); if(i+1 < num_cols) { //cols[i].flags |= SPACER; - ui_vsplit_l(&headers, 2, &cols[i].spacer, &headers); + headers.VSplitLeft(2, &cols[i].spacer, &headers); } } } @@ -86,8 +86,8 @@ void MENUS::render_serverbrowser_serverlist(RECT view) { if(cols[i].direction == 1) { - ui_vsplit_r(&headers, cols[i].width, &headers, &cols[i].rect); - ui_vsplit_r(&headers, 2, &headers, &cols[i].spacer); + headers.VSplitRight(cols[i].width, &headers, &cols[i].rect); + headers.VSplitRight(2, &headers, &cols[i].spacer); } } @@ -100,7 +100,7 @@ void MENUS::render_serverbrowser_serverlist(RECT view) // do headers for(int i = 0; i < num_cols; i++) { - if(ui_do_button(cols[i].caption, cols[i].caption, config.b_sort == cols[i].sort, &cols[i].rect, ui_draw_grid_header, 0)) + if(DoButton_GridHeader(cols[i].caption, cols[i].caption, config.b_sort == cols[i].sort, &cols[i].rect)) { if(cols[i].sort != -1) { @@ -113,33 +113,33 @@ void MENUS::render_serverbrowser_serverlist(RECT view) } } - ui_draw_rect(&view, vec4(0,0,0,0.15f), 0, 0); + RenderTools()->DrawUIRect(&view, vec4(0,0,0,0.15f), 0, 0); - RECT scroll; - ui_vsplit_r(&view, 15, &view, &scroll); + CUIRect scroll; + view.VSplitRight(15, &view, &scroll); int num_servers = client_serverbrowse_sorted_num(); // display important messages in the middle of the screen so no // users misses it { - RECT msgbox = view; + CUIRect msgbox = view; msgbox.y += view.h/3; if(active_page == PAGE_INTERNET && client_serverbrowse_refreshingmasters()) - ui_do_label(&msgbox, localize("Refreshing master servers"), 16.0f, 0); + UI()->DoLabel(&msgbox, localize("Refreshing master servers"), 16.0f, 0); else if(!client_serverbrowse_num()) - ui_do_label(&msgbox, localize("No servers found"), 16.0f, 0); + UI()->DoLabel(&msgbox, localize("No servers found"), 16.0f, 0); else if(client_serverbrowse_num() && !num_servers) - ui_do_label(&msgbox, localize("No servers match your filter criteria"), 16.0f, 0); + UI()->DoLabel(&msgbox, localize("No servers match your filter criteria"), 16.0f, 0); } int num = (int)(view.h/cols[0].rect.h); static int scrollbar = 0; static float scrollvalue = 0; //static int selected_index = -1; - ui_hmargin(&scroll, 5.0f, &scroll); - scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); + scroll.HMargin(5.0f, &scroll); + scrollvalue = DoScrollbarV(&scrollbar, &scroll, scrollvalue); int scrollnum = num_servers-num+10; if(scrollnum > 0) @@ -156,13 +156,13 @@ void MENUS::render_serverbrowser_serverlist(RECT view) scrollnum = 0; // set clipping - ui_clip_enable(&view); + UI()->ClipEnable(&view); int start = (int)(scrollnum*scrollvalue); if(start < 0) start = 0; - RECT original_view = view; + CUIRect original_view = view; view.y -= scrollvalue*scrollnum*cols[0].rect.h; int new_selected = -1; @@ -180,20 +180,20 @@ void MENUS::render_serverbrowser_serverlist(RECT view) { int item_index = i; SERVER_INFO *item = client_serverbrowse_sorted_get(item_index); - RECT row; - RECT select_hit_box; + CUIRect row; + CUIRect select_hit_box; int selected = strcmp(item->address, config.ui_server_address) == 0; //selected_index==item_index; - ui_hsplit_t(&view, 17.0f, &row, &view); + view.HSplitTop(17.0f, &row, &view); select_hit_box = row; if(selected) { selected_index = i; - RECT r = row; - ui_margin(&r, 1.5f, &r); - ui_draw_rect(&r, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f); + CUIRect r = row; + r.Margin(1.5f, &r); + RenderTools()->DrawUIRect(&r, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 4.0f); } @@ -206,7 +206,7 @@ void MENUS::render_serverbrowser_serverlist(RECT view) select_hit_box.y = original_view.y; } - if(ui_do_button(item, "", selected, &select_hit_box, 0, 0)) + if(UI()->DoButtonLogic(item, "", selected, &select_hit_box)) { new_selected = item_index; } @@ -218,7 +218,7 @@ void MENUS::render_serverbrowser_serverlist(RECT view) for(int c = 0; c < num_cols; c++) { - RECT button; + CUIRect button; char temp[64]; button.x = cols[c].rect.x; button.y = row.y; @@ -228,12 +228,12 @@ void MENUS::render_serverbrowser_serverlist(RECT view) //int s = 0; int id = cols[c].id; - //s = ui_do_button(item, "L", l, &button, ui_draw_browse_icon, 0); + //s = UI()->DoButton(item, "L", l, &button, ui_draw_browse_icon, 0); if(id == COL_FLAG_LOCK) { if(item->flags & SRVFLAG_PASSWORD) - ui_draw_browse_icon(SPRITE_BROWSE_LOCK, &button); + DoButton_BrowseIcon(SPRITE_BROWSE_LOCK, &button); } else if(id == COL_FLAG_PURE) { @@ -244,13 +244,13 @@ void MENUS::render_serverbrowser_serverlist(RECT view) else { // unpure - ui_draw_browse_icon(SPRITE_BROWSE_UNPURE, &button); + DoButton_BrowseIcon(SPRITE_BROWSE_UNPURE, &button); } } else if(id == COL_FLAG_FAV) { if(item->favorite) - ui_draw_browse_icon(SPRITE_BROWSE_HEART, &button); + DoButton_BrowseIcon(SPRITE_BROWSE_HEART, &button); } else if(id == COL_NAME) { @@ -277,36 +277,36 @@ void MENUS::render_serverbrowser_serverlist(RECT view) gfx_text_ex(&cursor, item->name, -1); } else if(id == COL_MAP) - ui_do_label(&button, item->map, 12.0f, -1); + UI()->DoLabel(&button, item->map, 12.0f, -1); else if(id == COL_PLAYERS) { str_format(temp, sizeof(temp), "%i/%i", item->num_players, item->max_players); if(config.b_filter_string[0] && (item->quicksearch_hit&BROWSEQUICK_PLAYERNAME)) gfx_text_color(0.4f,0.4f,1.0f,1); - ui_do_label(&button, temp, 12.0f, 1); + UI()->DoLabel(&button, temp, 12.0f, 1); gfx_text_color(1,1,1,1); } else if(id == COL_PING) { str_format(temp, sizeof(temp), "%i", item->latency); - ui_do_label(&button, temp, 12.0f, 1); + UI()->DoLabel(&button, temp, 12.0f, 1); } else if(id == COL_VERSION) { const char *version = item->version; if(strcmp(version, "0.3 e2d7973c6647a13c") == 0) // TODO: remove me later on version = "0.3.0"; - ui_do_label(&button, version, 12.0f, 1); + UI()->DoLabel(&button, version, 12.0f, 1); } else if(id == COL_GAMETYPE) { - ui_do_label(&button, item->gametype, 12.0f, 0); + UI()->DoLabel(&button, item->gametype, 12.0f, 0); } } } - ui_clip_disable(); + UI()->ClipDisable(); if(new_selected != -1) { @@ -317,84 +317,84 @@ void MENUS::render_serverbrowser_serverlist(RECT view) client_connect(config.ui_server_address); } - ui_draw_rect(&status, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui_margin(&status, 5.0f, &status); + RenderTools()->DrawUIRect(&status, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); + status.Margin(5.0f, &status); // render quick search - RECT quicksearch; - ui_vsplit_l(&status, 250.0f, &quicksearch, &status); + CUIRect quicksearch; + status.VSplitLeft(250.0f, &quicksearch, &status); const char *label = localize("Quick search"); - ui_do_label(&quicksearch, label, 14.0f, -1); - ui_vsplit_l(&quicksearch, gfx_text_width(0, 14.0f, label, -1), 0, &quicksearch); - ui_vsplit_l(&quicksearch, 5, 0, &quicksearch); - ui_do_edit_box(&config.b_filter_string, &quicksearch, config.b_filter_string, sizeof(config.b_filter_string), 14.0f); + UI()->DoLabel(&quicksearch, label, 14.0f, -1); + quicksearch.VSplitLeft(gfx_text_width(0, 14.0f, label, -1), 0, &quicksearch); + quicksearch.VSplitLeft(5, 0, &quicksearch); + DoEditBox(&config.b_filter_string, &quicksearch, config.b_filter_string, sizeof(config.b_filter_string), 14.0f); // render status char buf[128]; str_format(buf, sizeof(buf), localize("%d of %d servers, %d players"), client_serverbrowse_sorted_num(), client_serverbrowse_num(), num_players); - ui_vsplit_r(&status, gfx_text_width(0, 14.0f, buf, -1), 0, &status); - ui_do_label(&status, buf, 14.0f, -1); + status.VSplitRight(gfx_text_width(0, 14.0f, buf, -1), 0, &status); + UI()->DoLabel(&status, buf, 14.0f, -1); } -void MENUS::render_serverbrowser_filters(RECT view) +void MENUS::render_serverbrowser_filters(CUIRect view) { // filters - RECT button; + CUIRect button; - ui_hsplit_t(&view, 5.0f, 0, &view); - ui_vsplit_l(&view, 5.0f, 0, &view); - ui_vsplit_r(&view, 5.0f, &view, 0); - ui_hsplit_b(&view, 5.0f, &view, 0); + view.HSplitTop(5.0f, 0, &view); + view.VSplitLeft(5.0f, 0, &view); + view.VSplitRight(5.0f, &view, 0); + view.HSplitBottom(5.0f, &view, 0); // render filters - ui_hsplit_t(&view, 20.0f, &button, &view); - if (ui_do_button(&config.b_filter_empty, localize("Has people playing"), config.b_filter_empty, &button, ui_draw_checkbox, 0)) + view.HSplitTop(20.0f, &button, &view); + if (DoButton_CheckBox(&config.b_filter_empty, localize("Has people playing"), config.b_filter_empty, &button)) config.b_filter_empty ^= 1; - ui_hsplit_t(&view, 20.0f, &button, &view); - if (ui_do_button(&config.b_filter_full, localize("Server not full"), config.b_filter_full, &button, ui_draw_checkbox, 0)) + view.HSplitTop(20.0f, &button, &view); + if (DoButton_CheckBox(&config.b_filter_full, localize("Server not full"), config.b_filter_full, &button)) config.b_filter_full ^= 1; - ui_hsplit_t(&view, 20.0f, &button, &view); - if (ui_do_button(&config.b_filter_pw, localize("No password"), config.b_filter_pw, &button, ui_draw_checkbox, 0)) + view.HSplitTop(20.0f, &button, &view); + if (DoButton_CheckBox(&config.b_filter_pw, localize("No password"), config.b_filter_pw, &button)) config.b_filter_pw ^= 1; - ui_hsplit_t(&view, 20.0f, &button, &view); - if (ui_do_button((char *)&config.b_filter_compatversion, localize("Compatible version"), config.b_filter_compatversion, &button, ui_draw_checkbox, 0)) + view.HSplitTop(20.0f, &button, &view); + if (DoButton_CheckBox((char *)&config.b_filter_compatversion, localize("Compatible version"), config.b_filter_compatversion, &button)) config.b_filter_compatversion ^= 1; - ui_hsplit_t(&view, 20.0f, &button, &view); - if (ui_do_button((char *)&config.b_filter_pure, localize("Standard gametype"), config.b_filter_pure, &button, ui_draw_checkbox, 0)) + view.HSplitTop(20.0f, &button, &view); + if (DoButton_CheckBox((char *)&config.b_filter_pure, localize("Standard gametype"), config.b_filter_pure, &button)) config.b_filter_pure ^= 1; - ui_hsplit_t(&view, 20.0f, &button, &view); - /*ui_vsplit_l(&button, 20.0f, 0, &button);*/ - if (ui_do_button((char *)&config.b_filter_pure_map, localize("Standard map"), config.b_filter_pure_map, &button, ui_draw_checkbox, 0)) + view.HSplitTop(20.0f, &button, &view); + /*button.VSplitLeft(20.0f, 0, &button);*/ + if (DoButton_CheckBox((char *)&config.b_filter_pure_map, localize("Standard map"), config.b_filter_pure_map, &button)) config.b_filter_pure_map ^= 1; - ui_hsplit_t(&view, 20.0f, &button, &view); - ui_do_label(&button, localize("Game types"), 14.0f, -1); - ui_vsplit_l(&button, 95.0f, 0, &button); - ui_margin(&button, 1.0f, &button); - ui_do_edit_box(&config.b_filter_gametype, &button, config.b_filter_gametype, sizeof(config.b_filter_gametype), 14.0f); + view.HSplitTop(20.0f, &button, &view); + UI()->DoLabel(&button, localize("Game types"), 14.0f, -1); + button.VSplitLeft(95.0f, 0, &button); + button.Margin(1.0f, &button); + DoEditBox(&config.b_filter_gametype, &button, config.b_filter_gametype, sizeof(config.b_filter_gametype), 14.0f); { - ui_hsplit_t(&view, 20.0f, &button, &view); - RECT editbox; - ui_vsplit_l(&button, 40.0f, &editbox, &button); - ui_vsplit_l(&button, 5.0f, &button, &button); + view.HSplitTop(20.0f, &button, &view); + CUIRect editbox; + button.VSplitLeft(40.0f, &editbox, &button); + button.VSplitLeft(5.0f, &button, &button); char buf[8]; str_format(buf, sizeof(buf), "%d", config.b_filter_ping); - ui_do_edit_box(&config.b_filter_ping, &editbox, buf, sizeof(buf), 14.0f); + DoEditBox(&config.b_filter_ping, &editbox, buf, sizeof(buf), 14.0f); config.b_filter_ping = atoi(buf); - ui_do_label(&button, localize("Maximum ping"), 14.0f, -1); + UI()->DoLabel(&button, localize("Maximum ping"), 14.0f, -1); } - ui_hsplit_b(&view, button_height, &view, &button); + view.HSplitBottom(button_height, &view, &button); static int clear_button = 0; - if(ui_do_button(&clear_button, localize("Reset filter"), 0, &button, ui_draw_menu_button, 0)) + if(DoButton_Menu(&clear_button, localize("Reset filter"), 0, &button)) { config.b_filter_full = 0; config.b_filter_empty = 0; @@ -407,48 +407,48 @@ void MENUS::render_serverbrowser_filters(RECT view) } } -void MENUS::render_serverbrowser_serverdetail(RECT view) +void MENUS::render_serverbrowser_serverdetail(CUIRect view) { - RECT server_details = view; - RECT server_scoreboard, server_header; + CUIRect server_details = view; + CUIRect server_scoreboard, server_header; SERVER_INFO *selected_server = client_serverbrowse_sorted_get(selected_index); - //ui_vsplit_l(&server_details, 10.0f, 0x0, &server_details); + //server_details.VSplitLeft(10.0f, 0x0, &server_details); // split off a piece to use for scoreboard - ui_hsplit_t(&server_details, 140.0f, &server_details, &server_scoreboard); - ui_hsplit_b(&server_details, 10.0f, &server_details, 0x0); + server_details.HSplitTop(140.0f, &server_details, &server_scoreboard); + server_details.HSplitBottom(10.0f, &server_details, 0x0); // server details const float font_size = 12.0f; - ui_hsplit_t(&server_details, 20.0f, &server_header, &server_details); - ui_draw_rect(&server_header, vec4(1,1,1,0.25f), CORNER_T, 4.0f); - ui_draw_rect(&server_details, vec4(0,0,0,0.15f), CORNER_B, 4.0f); - ui_vsplit_l(&server_header, 8.0f, 0x0, &server_header); - ui_do_label(&server_header, localize("Server details"), font_size+2.0f, -1); + server_details.HSplitTop(20.0f, &server_header, &server_details); + RenderTools()->DrawUIRect(&server_header, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f); + RenderTools()->DrawUIRect(&server_details, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f); + server_header.VSplitLeft(8.0f, 0x0, &server_header); + UI()->DoLabel(&server_header, localize("Server details"), font_size+2.0f, -1); - ui_vsplit_l(&server_details, 5.0f, 0x0, &server_details); + server_details.VSplitLeft(5.0f, 0x0, &server_details); - ui_margin(&server_details, 3.0f, &server_details); + server_details.Margin(3.0f, &server_details); if (selected_server) { - RECT row; + CUIRect row; static LOC_CONSTSTRING labels[] = { localize("Version"), localize("Game type"), localize("Ping")}; - RECT left_column; - RECT right_column; + CUIRect left_column; + CUIRect right_column; // { - RECT button; - ui_hsplit_b(&server_details, 20.0f, &server_details, &button); + CUIRect button; + server_details.HSplitBottom(20.0f, &server_details, &button); static int add_fav_button = 0; - if (ui_do_button(&add_fav_button, localize("Favorite"), selected_server->favorite, &button, ui_draw_checkbox, 0)) + if(DoButton_CheckBox(&add_fav_button, localize("Favorite"), selected_server->favorite, &button)) { if(selected_server->favorite) client_serverbrowse_removefavorite(selected_server->netaddr); @@ -456,55 +456,55 @@ void MENUS::render_serverbrowser_serverdetail(RECT view) client_serverbrowse_addfavorite(selected_server->netaddr); } } - //ui_do_label(&row, temp, font_size, -1); + //UI()->DoLabel(&row, temp, font_size, -1); - ui_vsplit_l(&server_details, 5.0f, 0x0, &server_details); - ui_vsplit_l(&server_details, 80.0f, &left_column, &right_column); + server_details.VSplitLeft(5.0f, 0x0, &server_details); + server_details.VSplitLeft(80.0f, &left_column, &right_column); for (unsigned int i = 0; i < sizeof(labels) / sizeof(labels[0]); i++) { - ui_hsplit_t(&left_column, 15.0f, &row, &left_column); - ui_do_label(&row, labels[i], font_size, -1); + left_column.HSplitTop(15.0f, &row, &left_column); + UI()->DoLabel(&row, labels[i], font_size, -1); } - ui_hsplit_t(&right_column, 15.0f, &row, &right_column); - ui_do_label(&row, selected_server->version, font_size, -1); + right_column.HSplitTop(15.0f, &row, &right_column); + UI()->DoLabel(&row, selected_server->version, font_size, -1); - ui_hsplit_t(&right_column, 15.0f, &row, &right_column); - ui_do_label(&row, selected_server->gametype, font_size, -1); + right_column.HSplitTop(15.0f, &row, &right_column); + UI()->DoLabel(&row, selected_server->gametype, font_size, -1); char temp[16]; str_format(temp, sizeof(temp), "%d", selected_server->latency); - ui_hsplit_t(&right_column, 15.0f, &row, &right_column); - ui_do_label(&row, temp, font_size, -1); + right_column.HSplitTop(15.0f, &row, &right_column); + UI()->DoLabel(&row, temp, font_size, -1); } // server scoreboard - ui_hsplit_b(&server_scoreboard, 10.0f, &server_scoreboard, 0x0); - ui_hsplit_t(&server_scoreboard, 20.0f, &server_header, &server_scoreboard); - ui_draw_rect(&server_header, vec4(1,1,1,0.25f), CORNER_T, 4.0f); - ui_draw_rect(&server_scoreboard, vec4(0,0,0,0.15f), CORNER_B, 4.0f); - ui_vsplit_l(&server_header, 8.0f, 0x0, &server_header); - ui_do_label(&server_header, localize("Scoreboard"), font_size+2.0f, -1); + server_scoreboard.HSplitBottom(10.0f, &server_scoreboard, 0x0); + server_scoreboard.HSplitTop(20.0f, &server_header, &server_scoreboard); + RenderTools()->DrawUIRect(&server_header, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f); + RenderTools()->DrawUIRect(&server_scoreboard, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f); + server_header.VSplitLeft(8.0f, 0x0, &server_header); + UI()->DoLabel(&server_header, localize("Scoreboard"), font_size+2.0f, -1); - ui_vsplit_l(&server_scoreboard, 5.0f, 0x0, &server_scoreboard); + server_scoreboard.VSplitLeft(5.0f, 0x0, &server_scoreboard); - ui_margin(&server_scoreboard, 3.0f, &server_scoreboard); + server_scoreboard.Margin(3.0f, &server_scoreboard); if (selected_server) { for (int i = 0; i < selected_server->num_players; i++) { - RECT row; + CUIRect row; char temp[16]; - ui_hsplit_t(&server_scoreboard, 16.0f, &row, &server_scoreboard); + server_scoreboard.HSplitTop(16.0f, &row, &server_scoreboard); str_format(temp, sizeof(temp), "%d", selected_server->players[i].score); - ui_do_label(&row, temp, font_size, -1); + UI()->DoLabel(&row, temp, font_size, -1); - ui_vsplit_l(&row, 25.0f, 0x0, &row); + row.VSplitLeft(25.0f, 0x0, &row); TEXT_CURSOR cursor; gfx_text_set_cursor(&cursor, row.x, row.y, 12.0f, TEXTFLAG_RENDER); @@ -532,12 +532,12 @@ void MENUS::render_serverbrowser_serverdetail(RECT view) } } -void MENUS::render_serverbrowser(RECT main_view) +void MENUS::render_serverbrowser(CUIRect main_view) { - ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); + RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_ALL, 10.0f); - RECT view; - ui_margin(&main_view, 10.0f, &view); + CUIRect view; + main_view.Margin(10.0f, &view); /* +-----------------+ +------+ @@ -551,44 +551,44 @@ void MENUS::render_serverbrowser(RECT main_view) */ - //RECT filters; - RECT status_toolbar; - RECT toolbox; - RECT button_box; + //CUIRect filters; + CUIRect status_toolbar; + CUIRect toolbox; + CUIRect button_box; // split off a piece for filters, details and scoreboard - ui_vsplit_r(&view, 200.0f, &view, &toolbox); - ui_hsplit_b(&toolbox, 80.0f, &toolbox, &button_box); - ui_hsplit_b(&view, button_height+5.0f, &view, &status_toolbar); + view.VSplitRight(200.0f, &view, &toolbox); + toolbox.HSplitBottom(80.0f, &toolbox, &button_box); + view.HSplitBottom(button_height+5.0f, &view, &status_toolbar); render_serverbrowser_serverlist(view); static int toolbox_page = 0; - ui_vsplit_l(&toolbox, 5.0f, 0, &toolbox); + toolbox.VSplitLeft(5.0f, 0, &toolbox); // do tabbar { - RECT tab_bar; - RECT tabbutton0, tabbutton1; - ui_hsplit_t(&toolbox, 22.0f, &tab_bar, &toolbox); + CUIRect tab_bar; + CUIRect tabbutton0, tabbutton1; + toolbox.HSplitTop(22.0f, &tab_bar, &toolbox); - ui_vsplit_mid(&tab_bar, &tabbutton0, &tabbutton1); - ui_vsplit_r(&tabbutton0, 5.0f, &tabbutton0, 0); - ui_vsplit_l(&tabbutton1, 5.0f, 0, &tabbutton1); + tab_bar.VSplitMid(&tabbutton0, &tabbutton1); + tabbutton0.VSplitRight(5.0f, &tabbutton0, 0); + tabbutton1.VSplitLeft(5.0f, 0, &tabbutton1); static int filters_tab = 0; - if (ui_do_button(&filters_tab, localize("Filter"), toolbox_page==0, &tabbutton0, ui_draw_menu_tab_button, 0)) + if (DoButton_MenuTab(&filters_tab, localize("Filter"), toolbox_page==0, &tabbutton0, 0)) toolbox_page = 0; static int info_tab = 0; - if (ui_do_button(&info_tab, localize("Info"), toolbox_page==1, &tabbutton1, ui_draw_menu_tab_button, 0)) + if (DoButton_MenuTab(&info_tab, localize("Info"), toolbox_page==1, &tabbutton1, 0)) toolbox_page = 1; } - ui_draw_rect(&toolbox, vec4(0,0,0,0.15f), 0, 0); + RenderTools()->DrawUIRect(&toolbox, vec4(0,0,0,0.15f), 0, 0); - ui_hsplit_t(&toolbox, 5.0f, 0, &toolbox); + toolbox.HSplitTop(5.0f, 0, &toolbox); if(toolbox_page == 0) render_serverbrowser_filters(toolbox); @@ -596,14 +596,14 @@ void MENUS::render_serverbrowser(RECT main_view) render_serverbrowser_serverdetail(toolbox); { - ui_hsplit_t(&status_toolbar, 5.0f, 0, &status_toolbar); + status_toolbar.HSplitTop(5.0f, 0, &status_toolbar); - RECT button; - //ui_vsplit_r(&buttons, 20.0f, &buttons, &button); - ui_vsplit_r(&status_toolbar, 110.0f, &status_toolbar, &button); - ui_vmargin(&button, 2.0f, &button); + CUIRect button; + //buttons.VSplitRight(20.0f, &buttons, &button); + status_toolbar.VSplitRight(110.0f, &status_toolbar, &button); + button.VMargin(2.0f, &button); static int refresh_button = 0; - if(ui_do_button(&refresh_button, localize("Refresh"), 0, &button, ui_draw_menu_button, 0)) + if(DoButton_Menu(&refresh_button, localize("Refresh"), 0, &button)) { if(config.ui_page == PAGE_INTERNET) client_serverbrowse_refresh(BROWSETYPE_INTERNET); @@ -618,31 +618,31 @@ void MENUS::render_serverbrowser(RECT main_view) str_format(buf, sizeof(buf), localize("Teeworlds %s is out! Download it at www.teeworlds.com!"), client_latestversion()); else str_format(buf, sizeof(buf), localize("Current version: %s"), GAME_VERSION); - ui_do_label(&status_toolbar, buf, 14.0f, -1); + UI()->DoLabel(&status_toolbar, buf, 14.0f, -1); } // do the button box { - ui_vsplit_l(&button_box, 5.0f, 0, &button_box); - ui_vsplit_r(&button_box, 5.0f, &button_box, 0); + button_box.VSplitLeft(5.0f, 0, &button_box); + button_box.VSplitRight(5.0f, &button_box, 0); - RECT button; - ui_hsplit_b(&button_box, button_height, &button_box, &button); - ui_vsplit_r(&button, 120.0f, 0, &button); - ui_vmargin(&button, 2.0f, &button); - //ui_vmargin(&button, 2.0f, &button); + CUIRect button; + button_box.HSplitBottom(button_height, &button_box, &button); + button.VSplitRight(120.0f, 0, &button); + button.VMargin(2.0f, &button); + //button.VMargin(2.0f, &button); static int join_button = 0; - if(ui_do_button(&join_button, localize("Connect"), 0, &button, ui_draw_menu_button, 0) || enter_pressed) + if(DoButton_Menu(&join_button, localize("Connect"), 0, &button) || enter_pressed) { client_connect(config.ui_server_address); enter_pressed = false; } - ui_hsplit_b(&button_box, 5.0f, &button_box, &button); - ui_hsplit_b(&button_box, 20.0f, &button_box, &button); - ui_do_edit_box(&config.ui_server_address, &button, config.ui_server_address, sizeof(config.ui_server_address), 14.0f); - ui_hsplit_b(&button_box, 20.0f, &button_box, &button); - ui_do_label(&button, localize("Host address"), 14.0f, -1); + button_box.HSplitBottom(5.0f, &button_box, &button); + button_box.HSplitBottom(20.0f, &button_box, &button); + DoEditBox(&config.ui_server_address, &button, config.ui_server_address, sizeof(config.ui_server_address), 14.0f); + button_box.HSplitBottom(20.0f, &button_box, &button); + UI()->DoLabel(&button, localize("Host address"), 14.0f, -1); } } diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 594ee3df..07019d46 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -17,13 +17,14 @@ #include "menus.hpp" -void MENUS::ui_draw_demoplayer_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int MENUS::DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - ui_draw_rect(r, vec4(1,1,1, checked ? 0.10f : 0.5f)*button_color_mul(id), CORNER_ALL, 5.0f); - ui_do_label(r, text, 14.0f, 0); + RenderTools()->DrawUIRect(pRect, vec4(1,1,1, Checked ? 0.10f : 0.5f)*button_color_mul(pID), CUI::CORNER_ALL, 5.0f); + UI()->DoLabel(pRect, pText, 14.0f, 0); + return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -void MENUS::render_demoplayer(RECT main_view) +void MENUS::render_demoplayer(CUIRect main_view) { const DEMOPLAYBACK_INFO *info = client_demoplayer_getinfo(); @@ -37,20 +38,20 @@ void MENUS::render_demoplayer(RECT main_view) else total_height = seekbar_height+margins*2; - ui_hsplit_b(&main_view, total_height, 0, &main_view); - ui_vsplit_l(&main_view, 250.0f, 0, &main_view); - ui_vsplit_r(&main_view, 250.0f, &main_view, 0); + main_view.HSplitBottom(total_height, 0, &main_view); + main_view.VSplitLeft(250.0f, 0, &main_view); + main_view.VSplitRight(250.0f, &main_view, 0); - ui_draw_rect(&main_view, color_tabbar_active, CORNER_T, 10.0f); + RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_T, 10.0f); - ui_margin(&main_view, 5.0f, &main_view); + main_view.Margin(5.0f, &main_view); - RECT seekbar, buttonbar; + CUIRect seekbar, buttonbar; if(menu_active) { - ui_hsplit_t(&main_view, seekbar_height, &seekbar, &buttonbar); - ui_hsplit_t(&buttonbar, margins, 0, &buttonbar); + main_view.HSplitTop(seekbar_height, &seekbar, &buttonbar); + buttonbar.HSplitTop(margins, 0, &buttonbar); } else seekbar = main_view; @@ -61,33 +62,33 @@ void MENUS::render_demoplayer(RECT main_view) void *id = &seekbar_id; char buffer[128]; - ui_draw_rect(&seekbar, vec4(0,0,0,0.5f), CORNER_ALL, 5.0f); + RenderTools()->DrawUIRect(&seekbar, vec4(0,0,0,0.5f), CUI::CORNER_ALL, 5.0f); int current_tick = info->current_tick - info->first_tick; int total_ticks = info->last_tick - info->first_tick; float amount = current_tick/(float)total_ticks; - RECT filledbar = seekbar; + CUIRect filledbar = seekbar; filledbar.w = 10.0f + (filledbar.w-10.0f)*amount; - ui_draw_rect(&filledbar, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); + RenderTools()->DrawUIRect(&filledbar, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); str_format(buffer, sizeof(buffer), "%d:%02d / %d:%02d", current_tick/SERVER_TICK_SPEED/60, (current_tick/SERVER_TICK_SPEED)%60, total_ticks/SERVER_TICK_SPEED/60, (total_ticks/SERVER_TICK_SPEED)%60); - ui_do_label(&seekbar, buffer, seekbar.h*0.70f, 0); + UI()->DoLabel(&seekbar, buffer, seekbar.h*0.70f, 0); // do the logic - int inside = ui_mouse_inside(&seekbar); + int inside = UI()->MouseInside(&seekbar); - if(ui_active_item() == id) + if(UI()->ActiveItem() == id) { - if(!ui_mouse_button(0)) - ui_set_active_item(0); + if(!UI()->MouseButton(0)) + UI()->SetActiveItem(0); else { - float amount = (ui_mouse_x()-seekbar.x)/(float)seekbar.w; + float amount = (UI()->MouseX()-seekbar.x)/(float)seekbar.w; if(amount > 0 && amount < 1.0f) { gameclient.on_reset(); @@ -97,43 +98,43 @@ void MENUS::render_demoplayer(RECT main_view) } } } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == id) { - if(ui_mouse_button(0)) - ui_set_active_item(id); + if(UI()->MouseButton(0)) + UI()->SetActiveItem(id); } if(inside) - ui_set_hot_item(id); + UI()->SetHotItem(id); } if(menu_active) { // do buttons - RECT button; + CUIRect button; // pause button - ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar); + buttonbar.VSplitLeft(buttonbar_height, &button, &buttonbar); static int pause_button = 0; - if(ui_do_button(&pause_button, "| |", info->paused, &button, ui_draw_demoplayer_button, 0)) + if(DoButton_DemoPlayer(&pause_button, "| |", info->paused, &button)) client_demoplayer_setpause(!info->paused); // play button - ui_vsplit_l(&buttonbar, margins, 0, &buttonbar); - ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar); + buttonbar.VSplitLeft(margins, 0, &buttonbar); + buttonbar.VSplitLeft(buttonbar_height, &button, &buttonbar); static int play_button = 0; - if(ui_do_button(&play_button, ">", !info->paused, &button, ui_draw_demoplayer_button, 0)) + if(DoButton_DemoPlayer(&play_button, ">", !info->paused, &button)) { client_demoplayer_setpause(0); client_demoplayer_setspeed(1.0f); } // slowdown - ui_vsplit_l(&buttonbar, margins, 0, &buttonbar); - ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar); + buttonbar.VSplitLeft(margins, 0, &buttonbar); + buttonbar.VSplitLeft(buttonbar_height, &button, &buttonbar); static int slowdown_button = 0; - if(ui_do_button(&slowdown_button, "<<", 0, &button, ui_draw_demoplayer_button, 0)) + if(DoButton_DemoPlayer(&slowdown_button, "<<", 0, &button)) { if(info->speed > 4.0f) client_demoplayer_setspeed(4.0f); else if(info->speed > 2.0f) client_demoplayer_setspeed(2.0f); @@ -143,10 +144,10 @@ void MENUS::render_demoplayer(RECT main_view) } // fastforward - ui_vsplit_l(&buttonbar, margins, 0, &buttonbar); - ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar); + buttonbar.VSplitLeft(margins, 0, &buttonbar); + buttonbar.VSplitLeft(buttonbar_height, &button, &buttonbar); static int fastforward_button = 0; - if(ui_do_button(&fastforward_button, ">>", 0, &button, ui_draw_demoplayer_button, 0)) + if(DoButton_DemoPlayer(&fastforward_button, ">>", 0, &button)) { if(info->speed < 0.5f) client_demoplayer_setspeed(0.5f); else if(info->speed < 1.0f) client_demoplayer_setspeed(1.0f); @@ -156,24 +157,24 @@ void MENUS::render_demoplayer(RECT main_view) } // speed meter - ui_vsplit_l(&buttonbar, margins*3, 0, &buttonbar); + buttonbar.VSplitLeft(margins*3, 0, &buttonbar); char buffer[64]; if(info->speed >= 1.0f) str_format(buffer, sizeof(buffer), "x%.0f", info->speed); else str_format(buffer, sizeof(buffer), "x%.1f", info->speed); - ui_do_label(&buttonbar, buffer, button.h*0.7f, -1); + UI()->DoLabel(&buttonbar, buffer, button.h*0.7f, -1); // close button - ui_vsplit_r(&buttonbar, buttonbar_height*3, &buttonbar, &button); + buttonbar.VSplitRight(buttonbar_height*3, &buttonbar, &button); static int exit_button = 0; - if(ui_do_button(&exit_button, localize("Close"), 0, &button, ui_draw_demoplayer_button, 0)) + if(DoButton_DemoPlayer(&exit_button, localize("Close"), 0, &button)) client_disconnect(); } } -static RECT listbox_originalview; -static RECT listbox_view; +static CUIRect listbox_originalview; +static CUIRect listbox_view; static float listbox_rowheight; static int listbox_itemindex; static int listbox_selected_index; @@ -181,27 +182,27 @@ static int listbox_new_selected; static int listbox_doneevents; static int listbox_numitems; -void MENUS::ui_do_listbox_start(void *id, const RECT *rect, float row_height, const char *title, int num_items, int selected_index) +void MENUS::ui_do_listbox_start(void *id, const CUIRect *rect, float row_height, const char *title, int num_items, int selected_index) { - RECT scroll, row; - RECT view = *rect; - RECT header, footer; + CUIRect scroll, row; + CUIRect view = *rect; + CUIRect header, footer; // draw header - ui_hsplit_t(&view, listheader_height, &header, &view); - ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui_do_label(&header, title, header.h*fontmod_height, 0); + view.HSplitTop(listheader_height, &header, &view); + RenderTools()->DrawUIRect(&header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); + UI()->DoLabel(&header, title, header.h*fontmod_height, 0); // draw footers - ui_hsplit_b(&view, listheader_height, &view, &footer); - ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui_vsplit_l(&footer, 10.0f, 0, &footer); + view.HSplitBottom(listheader_height, &view, &footer); + RenderTools()->DrawUIRect(&footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); + footer.VSplitLeft(10.0f, 0, &footer); // background - ui_draw_rect(&view, vec4(0,0,0,0.15f), 0, 0); + RenderTools()->DrawUIRect(&view, vec4(0,0,0,0.15f), 0, 0); // prepare the scroll - ui_vsplit_r(&view, 15, &view, &scroll); + view.VSplitRight(15, &view, &scroll); // setup the variables listbox_originalview = view; @@ -215,7 +216,7 @@ void MENUS::ui_do_listbox_start(void *id, const RECT *rect, float row_height, co // do the scrollbar - ui_hsplit_t(&view, listbox_rowheight, &row, 0); + view.HSplitTop(listbox_rowheight, &row, 0); int num_viewable = (int)(listbox_originalview.h/row.h) + 1; int num = num_items-num_viewable+1; @@ -223,8 +224,8 @@ void MENUS::ui_do_listbox_start(void *id, const RECT *rect, float row_height, co num = 0; static float scrollvalue = 0; - ui_hmargin(&scroll, 5.0f, &scroll); - scrollvalue = ui_do_scrollbar_v(id, &scroll, scrollvalue); + scroll.HMargin(5.0f, &scroll); + scrollvalue = DoScrollbarV(id, &scroll, scrollvalue); int start = (int)(num*scrollvalue); if(start < 0) @@ -232,19 +233,62 @@ void MENUS::ui_do_listbox_start(void *id, const RECT *rect, float row_height, co // the list listbox_view = listbox_originalview; - ui_vmargin(&listbox_view, 5.0f, &listbox_view); - ui_clip_enable(&listbox_view); + listbox_view.VMargin(5.0f, &listbox_view); + UI()->ClipEnable(&listbox_view); listbox_view.y -= scrollvalue*num*row.h; } -MENUS::LISTBOXITEM MENUS::ui_do_listbox_nextitem(void *id) +MENUS::LISTBOXITEM MENUS::ui_do_listbox_nextrow() { - RECT row; LISTBOXITEM item = {0}; - ui_hsplit_t(&listbox_view, listbox_rowheight /*-2.0f*/, &row, &listbox_view); - //ui_hsplit_t(&listbox_view, 2.0f, 0, &listbox_view); + listbox_view.HSplitTop(listbox_rowheight /*-2.0f*/, &item.rect, &listbox_view); + item.visible = 1; + //item.rect = row; + + item.hitrect = item.rect; + + //CUIRect select_hit_box = item.rect; + + if(listbox_selected_index == listbox_itemindex) + item.selected = 1; + + // make sure that only those in view can be selected + if(item.rect.y+item.rect.h > listbox_originalview.y) + { + + if(item.hitrect.y < item.hitrect.y) // clip the selection + { + item.hitrect.h -= listbox_originalview.y-item.hitrect.y; + item.hitrect.y = listbox_originalview.y; + } + + } + else + item.visible = 0; - RECT select_hit_box = row; + // check if we need to do more + if(item.rect.y > listbox_originalview.y+listbox_originalview.h) + item.visible = 0; + + listbox_itemindex++; + return item; +} + +MENUS::LISTBOXITEM MENUS::ui_do_listbox_nextitem(void *id) +{ + int this_itemindex = listbox_itemindex; + + LISTBOXITEM item = ui_do_listbox_nextrow(); + + if(UI()->DoButtonLogic(id, "", listbox_selected_index == listbox_itemindex, &item.hitrect)) + listbox_new_selected = listbox_itemindex; + + //CUIRect row; + //LISTBOXITEM item = {0}; + //listbox_view.HSplitTop(listbox_rowheight /*-2.0f*/, &row, &listbox_view); + //listbox_view.HSplitTop(2.0f, 0, &listbox_view); + /* + CUIRect select_hit_box = row; item.visible = 1; if(listbox_selected_index == listbox_itemindex) @@ -260,19 +304,17 @@ MENUS::LISTBOXITEM MENUS::ui_do_listbox_nextitem(void *id) select_hit_box.y = listbox_originalview.y; } - if(ui_do_button(id, "", listbox_selected_index==listbox_itemindex, &select_hit_box, 0, 0)) + if(UI()->DoButton(id, "", listbox_selected_index==listbox_itemindex, &select_hit_box, 0, 0)) listbox_new_selected = listbox_itemindex; } else item.visible = 0; item.rect = row; + */ - // check if we need to do more - if(row.y > listbox_originalview.y+listbox_originalview.h) - item.visible = 0; - if(listbox_selected_index==listbox_itemindex) + if(listbox_selected_index == this_itemindex) { if(!listbox_doneevents) { @@ -294,21 +336,18 @@ MENUS::LISTBOXITEM MENUS::ui_do_listbox_nextitem(void *id) } //selected_index = i; - RECT r = row; - ui_margin(&r, 1.5f, &r); - ui_draw_rect(&r, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f); + CUIRect r = item.rect; + r.Margin(1.5f, &r); + RenderTools()->DrawUIRect(&r, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 4.0f); } - listbox_itemindex++; - - ui_vmargin(&item.rect, 5.0f, &item.rect); - + //listbox_itemindex++; return item; } int MENUS::ui_do_listbox_end() { - ui_clip_disable(); + UI()->ClipDisable(); return listbox_new_selected; } @@ -346,7 +385,7 @@ void MENUS::demolist_populate() } -void MENUS::render_demolist(RECT main_view) +void MENUS::render_demolist(CUIRect main_view) { static int inited = 0; if(!inited) @@ -354,12 +393,12 @@ void MENUS::render_demolist(RECT main_view) inited = 1; // render background - ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); - ui_margin(&main_view, 10.0f, &main_view); + RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_ALL, 10.0f); + main_view.Margin(10.0f, &main_view); - RECT buttonbar; - ui_hsplit_b(&main_view, button_height+5.0f, &main_view, &buttonbar); - ui_hsplit_t(&buttonbar, 5.0f, 0, &buttonbar); + CUIRect buttonbar; + main_view.HSplitBottom(button_height+5.0f, &main_view, &buttonbar); + buttonbar.HSplitTop(5.0f, 0, &buttonbar); static int selected_item = -1; static int demolist_id = 0; @@ -370,23 +409,23 @@ void MENUS::render_demolist(RECT main_view) { LISTBOXITEM item = ui_do_listbox_nextitem((void*)(&r.front())); if(item.visible) - ui_do_label(&item.rect, r.front().name, item.rect.h*fontmod_height, -1); + UI()->DoLabel(&item.rect, r.front().name, item.rect.h*fontmod_height, -1); } selected_item = ui_do_listbox_end(); - RECT refresh_rect, play_rect; - ui_vsplit_r(&buttonbar, 250.0f, &buttonbar, &refresh_rect); - ui_vsplit_r(&refresh_rect, 130.0f, &refresh_rect, &play_rect); - ui_vsplit_r(&play_rect, 120.0f, 0x0, &play_rect); + CUIRect refresh_rect, play_rect; + buttonbar.VSplitRight(250.0f, &buttonbar, &refresh_rect); + refresh_rect.VSplitRight(130.0f, &refresh_rect, &play_rect); + play_rect.VSplitRight(120.0f, 0x0, &play_rect); static int refresh_button = 0; - if(ui_do_button(&refresh_button, localize("Refresh"), 0, &refresh_rect, ui_draw_menu_button, 0)) + if(DoButton_Menu(&refresh_button, localize("Refresh"), 0, &refresh_rect)) { demolist_populate(); } static int play_button = 0; - if(ui_do_button(&play_button, localize("Play"), 0, &play_rect, ui_draw_menu_button, 0)) + if(DoButton_Menu(&play_button, localize("Play"), 0, &play_rect)) { if(selected_item >= 0 && selected_item < demos.size()) { diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 83673631..d31a15bd 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -17,30 +17,30 @@ #include "motd.hpp" #include "voting.hpp" -void MENUS::render_game(RECT main_view) +void MENUS::render_game(CUIRect main_view) { - RECT button; - //RECT votearea; - ui_hsplit_t(&main_view, 45.0f, &main_view, 0); - ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); - - ui_hsplit_t(&main_view, 10.0f, 0, &main_view); - ui_hsplit_t(&main_view, 25.0f, &main_view, 0); - ui_vmargin(&main_view, 10.0f, &main_view); + CUIRect button; + //CUIRect votearea; + main_view.HSplitTop(45.0f, &main_view, 0); + RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_ALL, 10.0f); + + main_view.HSplitTop(10.0f, 0, &main_view); + main_view.HSplitTop(25.0f, &main_view, 0); + main_view.VMargin(10.0f, &main_view); - ui_vsplit_r(&main_view, 120.0f, &main_view, &button); + main_view.VSplitRight(120.0f, &main_view, &button); static int disconnect_button = 0; - if(ui_do_button(&disconnect_button, localize("Disconnect"), 0, &button, ui_draw_menu_button, 0)) + if(DoButton_Menu(&disconnect_button, localize("Disconnect"), 0, &button)) client_disconnect(); if(gameclient.snap.local_info && gameclient.snap.gameobj) { if(gameclient.snap.local_info->team != -1) { - ui_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui_vsplit_l(&main_view, 120.0f, &button, &main_view); + main_view.VSplitLeft(10.0f, &button, &main_view); + main_view.VSplitLeft(120.0f, &button, &main_view); static int spectate_button = 0; - if(ui_do_button(&spectate_button, localize("Spectate"), 0, &button, ui_draw_menu_button, 0)) + if(DoButton_Menu(&spectate_button, localize("Spectate"), 0, &button)) { gameclient.send_switch_team(-1); set_active(false); @@ -51,10 +51,10 @@ void MENUS::render_game(RECT main_view) { if(gameclient.snap.local_info->team != 0) { - ui_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui_vsplit_l(&main_view, 120.0f, &button, &main_view); + main_view.VSplitLeft(10.0f, &button, &main_view); + main_view.VSplitLeft(120.0f, &button, &main_view); static int spectate_button = 0; - if(ui_do_button(&spectate_button, localize("Join red"), 0, &button, ui_draw_menu_button, 0)) + if(DoButton_Menu(&spectate_button, localize("Join red"), 0, &button)) { gameclient.send_switch_team(0); set_active(false); @@ -63,10 +63,10 @@ void MENUS::render_game(RECT main_view) if(gameclient.snap.local_info->team != 1) { - ui_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui_vsplit_l(&main_view, 120.0f, &button, &main_view); + main_view.VSplitLeft(10.0f, &button, &main_view); + main_view.VSplitLeft(120.0f, &button, &main_view); static int spectate_button = 0; - if(ui_do_button(&spectate_button, localize("Join blue"), 0, &button, ui_draw_menu_button, 0)) + if(DoButton_Menu(&spectate_button, localize("Join blue"), 0, &button)) { gameclient.send_switch_team(1); set_active(false); @@ -77,10 +77,10 @@ void MENUS::render_game(RECT main_view) { if(gameclient.snap.local_info->team != 0) { - ui_vsplit_l(&main_view, 10.0f, &button, &main_view); - ui_vsplit_l(&main_view, 120.0f, &button, &main_view); + main_view.VSplitLeft(10.0f, &button, &main_view); + main_view.VSplitLeft(120.0f, &button, &main_view); static int spectate_button = 0; - if(ui_do_button(&spectate_button, localize("Join game"), 0, &button, ui_draw_menu_button, 0)) + if(DoButton_Menu(&spectate_button, localize("Join game"), 0, &button)) { gameclient.send_switch_team(0); set_active(false); @@ -90,58 +90,58 @@ void MENUS::render_game(RECT main_view) } /* - RECT bars; - ui_hsplit_t(&votearea, 10.0f, 0, &votearea); - ui_hsplit_t(&votearea, 25.0f + 10.0f*3 + 25.0f, &votearea, &bars); + CUIRect bars; + votearea.HSplitTop(10.0f, 0, &votearea); + votearea.HSplitTop(25.0f + 10.0f*3 + 25.0f, &votearea, &bars); - ui_draw_rect(&votearea, color_tabbar_active, CORNER_ALL, 10.0f); + RenderTools()->DrawUIRect(&votearea, color_tabbar_active, CUI::CORNER_ALL, 10.0f); - ui_vmargin(&votearea, 20.0f, &votearea); - ui_hmargin(&votearea, 10.0f, &votearea); + votearea.VMargin(20.0f, &votearea); + votearea.HMargin(10.0f, &votearea); - ui_hsplit_b(&votearea, 35.0f, &votearea, &bars); + votearea.HSplitBottom(35.0f, &votearea, &bars); if(gameclient.voting->is_voting()) { // do yes button - ui_vsplit_l(&votearea, 50.0f, &button, &votearea); + votearea.VSplitLeft(50.0f, &button, &votearea); static int yes_button = 0; - if(ui_do_button(&yes_button, "Yes", 0, &button, ui_draw_menu_button, 0)) + if(UI()->DoButton(&yes_button, "Yes", 0, &button, ui_draw_menu_button, 0)) gameclient.voting->vote(1); // do no button - ui_vsplit_l(&votearea, 5.0f, 0, &votearea); - ui_vsplit_l(&votearea, 50.0f, &button, &votearea); + votearea.VSplitLeft(5.0f, 0, &votearea); + votearea.VSplitLeft(50.0f, &button, &votearea); static int no_button = 0; - if(ui_do_button(&no_button, "No", 0, &button, ui_draw_menu_button, 0)) + if(UI()->DoButton(&no_button, "No", 0, &button, ui_draw_menu_button, 0)) gameclient.voting->vote(-1); // do time left - ui_vsplit_r(&votearea, 50.0f, &votearea, &button); + votearea.VSplitRight(50.0f, &votearea, &button); char buf[256]; str_format(buf, sizeof(buf), "%d", gameclient.voting->seconds_left()); - ui_do_label(&button, buf, 24.0f, 0); + UI()->DoLabel(&button, buf, 24.0f, 0); // do description and command - ui_vsplit_l(&votearea, 5.0f, 0, &votearea); - ui_do_label(&votearea, gameclient.voting->vote_description(), 14.0f, -1); - ui_hsplit_t(&votearea, 16.0f, 0, &votearea); - ui_do_label(&votearea, gameclient.voting->vote_command(), 10.0f, -1); + votearea.VSplitLeft(5.0f, 0, &votearea); + UI()->DoLabel(&votearea, gameclient.voting->vote_description(), 14.0f, -1); + votearea.HSplitTop(16.0f, 0, &votearea); + UI()->DoLabel(&votearea, gameclient.voting->vote_command(), 10.0f, -1); // do bars - ui_hsplit_t(&bars, 10.0f, 0, &bars); - ui_hmargin(&bars, 5.0f, &bars); + bars.HSplitTop(10.0f, 0, &bars); + bars.HMargin(5.0f, &bars); gameclient.voting->render_bars(bars, true); } else { - ui_do_label(&votearea, "No vote in progress", 18.0f, -1); + UI()->DoLabel(&votearea, "No vote in progress", 18.0f, -1); }*/ } -void MENUS::render_serverinfo(RECT main_view) +void MENUS::render_serverinfo(CUIRect main_view) { // fetch server info SERVER_INFO current_server_info; @@ -164,9 +164,9 @@ void MENUS::render_serverinfo(RECT main_view) } // render background - ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f); + RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_ALL, 10.0f); - RECT view, serverinfo, gameinfo, motd; + CUIRect view, serverinfo, gameinfo, motd; float x = 0.0f; float y = 0.0f; @@ -174,14 +174,14 @@ void MENUS::render_serverinfo(RECT main_view) char buf[1024]; // set view to use for all sub-modules - ui_margin(&main_view, 10.0f, &view); + main_view.Margin(10.0f, &view); /* serverinfo */ - ui_hsplit_t(&view, view.h/2-5.0f, &serverinfo, &motd); - ui_vsplit_l(&serverinfo, view.w/2-5.0f, &serverinfo, &gameinfo); - ui_draw_rect(&serverinfo, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f); + view.HSplitTop(view.h/2-5.0f, &serverinfo, &motd); + serverinfo.VSplitLeft(view.w/2-5.0f, &serverinfo, &gameinfo); + RenderTools()->DrawUIRect(&serverinfo, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - ui_margin(&serverinfo, 5.0f, &serverinfo); + serverinfo.Margin(5.0f, &serverinfo); x = 5.0f; y = 0.0f; @@ -208,11 +208,11 @@ void MENUS::render_serverinfo(RECT main_view) gfx_text(0, serverinfo.x+x, serverinfo.y+y, 20, buf, 250); { - RECT button; + CUIRect button; int is_favorite = client_serverbrowse_isfavorite(current_server_info.netaddr); - ui_hsplit_b(&serverinfo, 20.0f, &serverinfo, &button); + serverinfo.HSplitBottom(20.0f, &serverinfo, &button); static int add_fav_button = 0; - if (ui_do_button(&add_fav_button, localize("Favorite"), is_favorite, &button, ui_draw_checkbox, 0)) + if(DoButton_CheckBox(&add_fav_button, localize("Favorite"), is_favorite, &button)) { if(is_favorite) client_serverbrowse_removefavorite(current_server_info.netaddr); @@ -222,10 +222,10 @@ void MENUS::render_serverinfo(RECT main_view) } /* gameinfo */ - ui_vsplit_l(&gameinfo, 10.0f, 0x0, &gameinfo); - ui_draw_rect(&gameinfo, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f); + gameinfo.VSplitLeft(10.0f, 0x0, &gameinfo); + RenderTools()->DrawUIRect(&gameinfo, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - ui_margin(&gameinfo, 5.0f, &gameinfo); + gameinfo.Margin(5.0f, &gameinfo); x = 5.0f; y = 0.0f; @@ -253,9 +253,9 @@ void MENUS::render_serverinfo(RECT main_view) gfx_text(0, gameinfo.x+x, gameinfo.y+y, 20, buf, 250); /* motd */ - ui_hsplit_t(&motd, 10.0f, 0, &motd); - ui_draw_rect(&motd, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f); - ui_margin(&motd, 5.0f, &motd); + motd.HSplitTop(10.0f, 0, &motd); + RenderTools()->DrawUIRect(&motd, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + motd.Margin(5.0f, &motd); y = 0.0f; x = 5.0f; gfx_text(0, motd.x+x, motd.y+y, 32, localize("MOTD"), -1); @@ -268,14 +268,14 @@ static const char *format_command(const char *cmd) return cmd; } -void MENUS::render_servercontrol_server(RECT main_view) +void MENUS::render_servercontrol_server(CUIRect main_view) { int num_options = 0; for(VOTING::VOTEOPTION *option = gameclient.voting->first; option; option = option->next) num_options++; static int votelist = 0; - RECT list = main_view; + CUIRect list = main_view; ui_do_listbox_start(&votelist, &list, 24.0f, localize("Settings"), num_options, callvote_selectedoption); for(VOTING::VOTEOPTION *option = gameclient.voting->first; option; option = option->next) @@ -283,62 +283,62 @@ void MENUS::render_servercontrol_server(RECT main_view) LISTBOXITEM item = ui_do_listbox_nextitem(option); if(item.visible) - ui_do_label(&item.rect, format_command(option->command), 16.0f, -1); + UI()->DoLabel(&item.rect, format_command(option->command), 16.0f, -1); } callvote_selectedoption = ui_do_listbox_end(); } -void MENUS::render_servercontrol_kick(RECT main_view) +void MENUS::render_servercontrol_kick(CUIRect main_view) { // draw header - RECT header, footer; - ui_hsplit_t(&main_view, 20, &header, &main_view); - ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui_do_label(&header, localize("Players"), 18.0f, 0); + CUIRect header, footer; + main_view.HSplitTop(20, &header, &main_view); + RenderTools()->DrawUIRect(&header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); + UI()->DoLabel(&header, localize("Players"), 18.0f, 0); // draw footers - ui_hsplit_b(&main_view, 20, &main_view, &footer); - ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui_vsplit_l(&footer, 10.0f, 0, &footer); + main_view.HSplitBottom(20, &main_view, &footer); + RenderTools()->DrawUIRect(&footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); + footer.VSplitLeft(10.0f, 0, &footer); // players - ui_draw_rect(&main_view, vec4(0,0,0,0.15f), 0, 0); - RECT list = main_view; + RenderTools()->DrawUIRect(&main_view, vec4(0,0,0,0.15f), 0, 0); + CUIRect list = main_view; for(int i = 0; i < MAX_CLIENTS; i++) { if(!gameclient.snap.player_infos[i]) continue; - RECT button; - ui_hsplit_t(&list, button_height, &button, &list); + CUIRect button; + list.HSplitTop(button_height, &button, &list); - if(ui_do_button((char *)&gameclient.snap+i, "", callvote_selectedplayer == i, &button, ui_draw_list_row, 0)) + if(DoButton_ListRow((char *)&gameclient.snap+i, "", callvote_selectedplayer == i, &button)) callvote_selectedplayer = i; TEE_RENDER_INFO info = gameclient.clients[i].render_info; info.size = button.h; - render_tee(ANIMSTATE::get_idle(), &info, EMOTE_NORMAL, vec2(1,0), vec2(button.x+button.h/2, button.y+button.h/2)); + RenderTools()->RenderTee(ANIMSTATE::get_idle(), &info, EMOTE_NORMAL, vec2(1,0), vec2(button.x+button.h/2, button.y+button.h/2)); button.x += button.h; - ui_do_label(&button, gameclient.clients[i].name, 18.0f, -1); + UI()->DoLabel(&button, gameclient.clients[i].name, 18.0f, -1); } } -void MENUS::render_servercontrol(RECT main_view) +void MENUS::render_servercontrol(CUIRect main_view) { static int control_page = 0; // render background - RECT temp, tabbar; - ui_vsplit_r(&main_view, 120.0f, &main_view, &tabbar); - ui_draw_rect(&main_view, color_tabbar_active, CORNER_B|CORNER_TL, 10.0f); - ui_hsplit_t(&tabbar, 50.0f, &temp, &tabbar); - ui_draw_rect(&temp, color_tabbar_active, CORNER_R, 10.0f); + CUIRect temp, tabbar; + main_view.VSplitRight(120.0f, &main_view, &tabbar); + RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_B|CUI::CORNER_TL, 10.0f); + tabbar.HSplitTop(50.0f, &temp, &tabbar); + RenderTools()->DrawUIRect(&temp, color_tabbar_active, CUI::CORNER_R, 10.0f); - ui_hsplit_t(&main_view, 10.0f, 0, &main_view); + main_view.HSplitTop(10.0f, 0, &main_view); - RECT button; + CUIRect button; const char *tabs[] = { localize("Settings"), @@ -347,9 +347,9 @@ void MENUS::render_servercontrol(RECT main_view) for(int i = 0; i < num_tabs; i++) { - ui_hsplit_t(&tabbar, 10, &button, &tabbar); - ui_hsplit_t(&tabbar, 26, &button, &tabbar); - if(ui_do_button(tabs[i], tabs[i], control_page == i, &button, ui_draw_settings_tab_button, 0)) + tabbar.HSplitTop(10, &button, &tabbar); + tabbar.HSplitTop(26, &button, &tabbar); + if(DoButton_SettingsTab(tabs[i], tabs[i], control_page == i, &button)) { control_page = i; callvote_selectedplayer = -1; @@ -357,10 +357,10 @@ void MENUS::render_servercontrol(RECT main_view) } } - ui_margin(&main_view, 10.0f, &main_view); - RECT bottom; - ui_hsplit_b(&main_view, button_height + 5*2, &main_view, &bottom); - ui_hmargin(&bottom, 5.0f, &bottom); + main_view.Margin(10.0f, &main_view); + CUIRect bottom; + main_view.HSplitBottom(button_height + 5*2, &main_view, &bottom); + bottom.HMargin(5.0f, &bottom); // render page if(control_page == 0) @@ -370,11 +370,11 @@ void MENUS::render_servercontrol(RECT main_view) { - RECT button; - ui_vsplit_r(&bottom, 120.0f, &bottom, &button); + CUIRect button; + bottom.VSplitRight(120.0f, &bottom, &button); static int callvote_button = 0; - if(ui_do_button(&callvote_button, localize("Call vote"), 0, &button, ui_draw_menu_button, 0)) + if(DoButton_Menu(&callvote_button, localize("Call vote"), 0, &button)) { if(control_page == 0) { diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 86325ab9..05b4d047 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -5,6 +5,7 @@ #include // atoi #include +#include #include #include @@ -43,33 +44,31 @@ bool MENUS_KEYBINDER::on_input(INPUT_EVENT e) return false; } -void MENUS::render_settings_player(RECT main_view) +void MENUS::render_settings_player(CUIRect main_view) { - RECT button; - RECT skinselection; - ui_vsplit_l(&main_view, 300.0f, &main_view, &skinselection); - - - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + CUIRect button; + CUIRect othersection; + main_view.VSplitLeft(300.0f, &main_view, &othersection); + main_view.HSplitTop(20.0f, &button, &main_view); // render settings { char buf[128]; - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + main_view.HSplitTop(20.0f, &button, &main_view); str_format(buf, sizeof(buf), "%s:", localize("Name")); - ui_do_label(&button, buf, 14.0, -1); - ui_vsplit_l(&button, 80.0f, 0, &button); - ui_vsplit_l(&button, 180.0f, &button, 0); - if(ui_do_edit_box(config.player_name, &button, config.player_name, sizeof(config.player_name), 14.0f)) + UI()->DoLabel(&button, buf, 14.0, -1); + button.VSplitLeft(80.0f, 0, &button); + button.VSplitLeft(180.0f, &button, 0); + if(DoEditBox(config.player_name, &button, config.player_name, sizeof(config.player_name), 14.0f)) need_sendinfo = true; // extra spacing - ui_hsplit_t(&main_view, 10.0f, 0, &main_view); + main_view.HSplitTop(10.0f, 0, &main_view); static int dynamic_camera_button = 0; - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if(ui_do_button(&dynamic_camera_button, localize("Dynamic Camera"), config.cl_mouse_deadzone != 0, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&dynamic_camera_button, localize("Dynamic Camera"), config.cl_mouse_deadzone != 0, &button)) { if(config.cl_mouse_deadzone) @@ -86,26 +85,26 @@ void MENUS::render_settings_player(RECT main_view) } } - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.cl_autoswitch_weapons, localize("Switch weapon on pickup"), config.cl_autoswitch_weapons, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&config.cl_autoswitch_weapons, localize("Switch weapon on pickup"), config.cl_autoswitch_weapons, &button)) config.cl_autoswitch_weapons ^= 1; - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.cl_nameplates, localize("Show name plates"), config.cl_nameplates, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&config.cl_nameplates, localize("Show name plates"), config.cl_nameplates, &button)) config.cl_nameplates ^= 1; //if(config.cl_nameplates) { - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui_vsplit_l(&button, 15.0f, 0, &button); - if (ui_do_button(&config.cl_nameplates_always, localize("Always show name plates"), config.cl_nameplates_always, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + button.VSplitLeft(15.0f, 0, &button); + if(DoButton_CheckBox(&config.cl_nameplates_always, localize("Always show name plates"), config.cl_nameplates_always, &button)) config.cl_nameplates_always ^= 1; } - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); + main_view.HSplitTop(20.0f, &button, &main_view); - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.player_color_body, localize("Custom colors"), config.player_use_custom_color, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&config.player_color_body, localize("Custom colors"), config.player_use_custom_color, &button)) { config.player_use_custom_color = config.player_use_custom_color?0:1; need_sendinfo = true; @@ -129,27 +128,27 @@ void MENUS::render_settings_player(RECT main_view) for(int i = 0; i < 2; i++) { - RECT text; - ui_hsplit_t(&main_view, 20.0f, &text, &main_view); - ui_vsplit_l(&text, 15.0f, 0, &text); - ui_do_label(&text, parts[i], 14.0f, -1); + CUIRect text; + main_view.HSplitTop(20.0f, &text, &main_view); + text.VSplitLeft(15.0f, 0, &text); + UI()->DoLabel(&text, parts[i], 14.0f, -1); int prevcolor = *colors[i]; int color = 0; for(int s = 0; s < 3; s++) { - RECT text; - ui_hsplit_t(&main_view, 19.0f, &button, &main_view); - ui_vsplit_l(&button, 30.0f, 0, &button); - ui_vsplit_l(&button, 70.0f, &text, &button); - ui_vsplit_r(&button, 5.0f, &button, 0); - ui_hsplit_t(&button, 4.0f, 0, &button); + CUIRect text; + main_view.HSplitTop(19.0f, &button, &main_view); + button.VSplitLeft(30.0f, 0, &button); + button.VSplitLeft(70.0f, &text, &button); + button.VSplitRight(5.0f, &button, 0); + button.HSplitTop(4.0f, 0, &button); float k = ((prevcolor>>((2-s)*8))&0xff) / 255.0f; - k = ui_do_scrollbar_h(&color_slider[i][s], &button, k); + k = DoScrollbarH(&color_slider[i][s], &button, k); color <<= 8; color += clamp((int)(k*255), 0, 255); - ui_do_label(&text, labels[s], 15.0f, -1); + UI()->DoLabel(&text, labels[s], 15.0f, -1); } @@ -157,35 +156,103 @@ void MENUS::render_settings_player(RECT main_view) need_sendinfo = true; *colors[i] = color; - ui_hsplit_t(&main_view, 5.0f, 0, &main_view); + main_view.HSplitTop(5.0f, 0, &main_view); } } + + // render skinselector + static int skinselector_id = 0; + ui_do_listbox_start(&skinselector_id, &main_view, 50, localize("Skins"), (gameclient.skins->num()+3)/4, 0); + + for(int skin_id = 0; skin_id < gameclient.skins->num(); ) + { + LISTBOXITEM item = ui_do_listbox_nextrow(); + CUIRect boxes[4]; + CUIRect first_half, second_half; + item.rect.VSplitMid(&first_half, &second_half); + first_half.VSplitMid(&boxes[0], &boxes[1]); + second_half.VSplitMid(&boxes[2], &boxes[3]); + + for(int i = 0; i < 4 && skin_id < gameclient.skins->num(); i++, skin_id++) + { + //CUIRect r = item. + const SKINS::SKIN *s = gameclient.skins->get(skin_id); + + TEE_RENDER_INFO info; + info.texture = s->org_texture; + info.color_body = vec4(1,1,1,1); + info.color_feet = vec4(1,1,1,1); + if(config.player_use_custom_color) + { + info.color_body = gameclient.skins->get_color(config.player_color_body); + info.color_feet = gameclient.skins->get_color(config.player_color_feet); + info.texture = s->color_texture; + } + + info.size = UI()->Scale()*50.0f; + + CUIRect icon = boxes[i]; //item.rect; + //button.VSplitLeft(50.0f, &icon, &text); + + /*if(UI()->DoButton(s, "", selected, &button, ui_draw_list_row, 0)) + { + config_set_player_skin(&config, s->name); + need_sendinfo = true; + }*/ + + //text.HSplitTop(12.0f, 0, &text); // some margin from the top + //UI()->DoLabel(&text, buf, 18.0f, 0); + + icon.HSplitTop(5.0f, 0, &icon); // some margin from the top + RenderTools()->RenderTee(ANIMSTATE::get_idle(), &info, 0, vec2(1, 0), vec2(icon.x+icon.w/2, icon.y+icon.h/2)); + + if(config.debug) + { + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(s->blood_color.r, s->blood_color.g, s->blood_color.b, 1.0f); + Graphics()->QuadsDrawTL(icon.x, icon.y, 12, 12); + Graphics()->QuadsEnd(); + } + } + } + + int new_selection = ui_do_listbox_end(); + (void)new_selection; + //main_view + } + + // render skinselector + /* + { + + //othersection } // draw header - RECT header, footer; - ui_hsplit_t(&skinselection, 20, &header, &skinselection); - ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui_do_label(&header, localize("Skins"), 18.0f, 0); + CUIRect header, footer; + skinselection.HSplitTop(20, &header, &skinselection); + RenderTools()->DrawUIRect(&header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); + UI()->DoLabel(&header, localize("Skins"), 18.0f, 0); // draw footers - ui_hsplit_b(&skinselection, 20, &skinselection, &footer); - ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui_vsplit_l(&footer, 10.0f, 0, &footer); + skinselection.HSplitBottom(20, &skinselection, &footer); + RenderTools()->DrawUIRect(&footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); + footer.VSplitLeft(10.0f, 0, &footer); // modes - ui_draw_rect(&skinselection, vec4(0,0,0,0.15f), 0, 0); + RenderTools()->DrawUIRect(&skinselection, vec4(0,0,0,0.15f), 0, 0); - RECT scroll; - ui_vsplit_r(&skinselection, 15, &skinselection, &scroll); + CUIRect scroll; + skinselection.VSplitRight(15, &skinselection, &scroll); - RECT list = skinselection; - ui_hsplit_t(&list, 50, &button, &list); + CUIRect list = skinselection; + list.HSplitTop(50, &button, &list); int num = (int)(skinselection.h/button.h); static float scrollvalue = 0; static int scrollbar = 0; - ui_hmargin(&scroll, 5.0f, &scroll); + scroll.HMargin(5.0f, &scroll); scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); int start = (int)((gameclient.skins->num()-num)*scrollvalue); @@ -220,35 +287,35 @@ void MENUS::render_settings_player(RECT main_view) info.texture = s->color_texture; } - info.size = ui_scale()*50.0f; + info.size = UI()->Scale()*50.0f; - RECT icon; - RECT text; - ui_vsplit_l(&button, 50.0f, &icon, &text); + CUIRect icon; + CUIRect text; + button.VSplitLeft(50.0f, &icon, &text); - if(ui_do_button(s, "", selected, &button, ui_draw_list_row, 0)) + if(UI()->DoButton(s, "", selected, &button, ui_draw_list_row, 0)) { config_set_player_skin(&config, s->name); need_sendinfo = true; } - ui_hsplit_t(&text, 12.0f, 0, &text); // some margin from the top - ui_do_label(&text, buf, 18.0f, 0); + text.HSplitTop(12.0f, 0, &text); // some margin from the top + UI()->DoLabel(&text, buf, 18.0f, 0); - ui_hsplit_t(&icon, 5.0f, 0, &icon); // some margin from the top - render_tee(ANIMSTATE::get_idle(), &info, 0, vec2(1, 0), vec2(icon.x+icon.w/2, icon.y+icon.h/2)); + icon.HSplitTop(5.0f, 0, &icon); // some margin from the top + RenderTools()->RenderTee(ANIMSTATE::get_idle(), &info, 0, vec2(1, 0), vec2(icon.x+icon.w/2, icon.y+icon.h/2)); if(config.debug) { - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(s->blood_color.r, s->blood_color.g, s->blood_color.b, 1.0f); - gfx_quads_drawTL(icon.x, icon.y, 12, 12); - gfx_quads_end(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(s->blood_color.r, s->blood_color.g, s->blood_color.b, 1.0f); + Graphics()->QuadsDrawTL(icon.x, icon.y, 12, 12); + Graphics()->QuadsEnd(); } - ui_hsplit_t(&list, 50, &button, &list); - } + list.HSplitTop(50, &button, &list); + }*/ } typedef void (*assign_func_callback)(CONFIGURATION *config, int value); @@ -288,31 +355,31 @@ static KEYINFO keys[] = const int key_count = sizeof(keys) / sizeof(KEYINFO); -void MENUS::ui_do_getbuttons(int start, int stop, RECT view) +void MENUS::ui_do_getbuttons(int start, int stop, CUIRect view) { for (int i = start; i < stop; i++) { KEYINFO &key = keys[i]; - RECT button, label; - ui_hsplit_t(&view, 20.0f, &button, &view); - ui_vsplit_l(&button, 130.0f, &label, &button); + CUIRect button, label; + view.HSplitTop(20.0f, &button, &view); + button.VSplitLeft(130.0f, &label, &button); char buf[64]; str_format(buf, sizeof(buf), "%s:", (const char *)key.name); - ui_do_label(&label, key.name, 14.0f, -1); + UI()->DoLabel(&label, key.name, 14.0f, -1); int oldid = key.keyid; - int newid = ui_do_key_reader((void *)&keys[i].name, &button, oldid); + int newid = DoKeyReader((void *)&keys[i].name, &button, oldid); if(newid != oldid) { gameclient.binds->bind(oldid, ""); gameclient.binds->bind(newid, keys[i].command); } - ui_hsplit_t(&view, 5.0f, 0, &view); + view.HSplitTop(5.0f, 0, &view); } } -void MENUS::render_settings_controls(RECT main_view) +void MENUS::render_settings_controls(CUIRect main_view) { // this is kinda slow, but whatever for(int i = 0; i < key_count; i++) @@ -332,28 +399,28 @@ void MENUS::render_settings_controls(RECT main_view) } } - RECT movement_settings, weapon_settings, voting_settings, chat_settings, misc_settings, reset_button; - ui_vsplit_l(&main_view, main_view.w/2-5.0f, &movement_settings, &voting_settings); + CUIRect movement_settings, weapon_settings, voting_settings, chat_settings, misc_settings, reset_button; + main_view.VSplitLeft(main_view.w/2-5.0f, &movement_settings, &voting_settings); /* movement settings */ { - ui_hsplit_t(&movement_settings, main_view.h/2-5.0f, &movement_settings, &weapon_settings); - ui_draw_rect(&movement_settings, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f); - ui_margin(&movement_settings, 10.0f, &movement_settings); + movement_settings.HSplitTop(main_view.h/2-5.0f, &movement_settings, &weapon_settings); + RenderTools()->DrawUIRect(&movement_settings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + movement_settings.Margin(10.0f, &movement_settings); gfx_text(0, movement_settings.x, movement_settings.y, 14, localize("Movement"), -1); - ui_hsplit_t(&movement_settings, 14.0f+5.0f+10.0f, 0, &movement_settings); + movement_settings.HSplitTop(14.0f+5.0f+10.0f, 0, &movement_settings); { - RECT button, label; - ui_hsplit_t(&movement_settings, 20.0f, &button, &movement_settings); - ui_vsplit_l(&button, 130.0f, &label, &button); - ui_do_label(&label, localize("Mouse sens."), 14.0f, -1); - ui_hmargin(&button, 2.0f, &button); - config.inp_mousesens = (int)(ui_do_scrollbar_h(&config.inp_mousesens, &button, (config.inp_mousesens-5)/500.0f)*500.0f)+5; + CUIRect button, label; + movement_settings.HSplitTop(20.0f, &button, &movement_settings); + button.VSplitLeft(130.0f, &label, &button); + UI()->DoLabel(&label, localize("Mouse sens."), 14.0f, -1); + button.HMargin(2.0f, &button); + config.inp_mousesens = (int)(DoScrollbarV(&config.inp_mousesens, &button, (config.inp_mousesens-5)/500.0f)*500.0f)+5; //*key.key = ui_do_key_reader(key.key, &button, *key.key); - ui_hsplit_t(&movement_settings, 20.0f, 0, &movement_settings); + movement_settings.HSplitTop(20.0f, 0, &movement_settings); } ui_do_getbuttons(0, 5, movement_settings); @@ -362,65 +429,65 @@ void MENUS::render_settings_controls(RECT main_view) /* weapon settings */ { - ui_hsplit_t(&weapon_settings, 10.0f, 0, &weapon_settings); - ui_draw_rect(&weapon_settings, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f); - ui_margin(&weapon_settings, 10.0f, &weapon_settings); + weapon_settings.HSplitTop(10.0f, 0, &weapon_settings); + RenderTools()->DrawUIRect(&weapon_settings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + weapon_settings.Margin(10.0f, &weapon_settings); gfx_text(0, weapon_settings.x, weapon_settings.y, 14, localize("Weapon"), -1); - ui_hsplit_t(&weapon_settings, 14.0f+5.0f+10.0f, 0, &weapon_settings); + weapon_settings.HSplitTop(14.0f+5.0f+10.0f, 0, &weapon_settings); ui_do_getbuttons(5, 12, weapon_settings); } /* voting settings */ { - ui_vsplit_l(&voting_settings, 10.0f, 0, &voting_settings); - ui_hsplit_t(&voting_settings, main_view.h/4-5.0f, &voting_settings, &chat_settings); - ui_draw_rect(&voting_settings, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f); - ui_margin(&voting_settings, 10.0f, &voting_settings); + voting_settings.VSplitLeft(10.0f, 0, &voting_settings); + voting_settings.HSplitTop(main_view.h/4-5.0f, &voting_settings, &chat_settings); + RenderTools()->DrawUIRect(&voting_settings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + voting_settings.Margin(10.0f, &voting_settings); gfx_text(0, voting_settings.x, voting_settings.y, 14, localize("Voting"), -1); - ui_hsplit_t(&voting_settings, 14.0f+5.0f+10.0f, 0, &voting_settings); + voting_settings.HSplitTop(14.0f+5.0f+10.0f, 0, &voting_settings); ui_do_getbuttons(12, 14, voting_settings); } /* chat settings */ { - ui_hsplit_t(&chat_settings, 10.0f, 0, &chat_settings); - ui_hsplit_t(&chat_settings, main_view.h/4-10.0f, &chat_settings, &misc_settings); - ui_draw_rect(&chat_settings, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f); - ui_margin(&chat_settings, 10.0f, &chat_settings); + chat_settings.HSplitTop(10.0f, 0, &chat_settings); + chat_settings.HSplitTop(main_view.h/4-10.0f, &chat_settings, &misc_settings); + RenderTools()->DrawUIRect(&chat_settings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + chat_settings.Margin(10.0f, &chat_settings); gfx_text(0, chat_settings.x, chat_settings.y, 14, localize("Chat"), -1); - ui_hsplit_t(&chat_settings, 14.0f+5.0f+10.0f, 0, &chat_settings); + chat_settings.HSplitTop(14.0f+5.0f+10.0f, 0, &chat_settings); ui_do_getbuttons(14, 16, chat_settings); } /* misc settings */ { - ui_hsplit_t(&misc_settings, 10.0f, 0, &misc_settings); - ui_hsplit_t(&misc_settings, main_view.h/2-5.0f-45.0f, &misc_settings, &reset_button); - ui_draw_rect(&misc_settings, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f); - ui_margin(&misc_settings, 10.0f, &misc_settings); + misc_settings.HSplitTop(10.0f, 0, &misc_settings); + misc_settings.HSplitTop(main_view.h/2-5.0f-45.0f, &misc_settings, &reset_button); + RenderTools()->DrawUIRect(&misc_settings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + misc_settings.Margin(10.0f, &misc_settings); gfx_text(0, misc_settings.x, misc_settings.y, 14, localize("Miscellaneous"), -1); - ui_hsplit_t(&misc_settings, 14.0f+5.0f+10.0f, 0, &misc_settings); + misc_settings.HSplitTop(14.0f+5.0f+10.0f, 0, &misc_settings); ui_do_getbuttons(16, 21, misc_settings); } // defaults - ui_hsplit_t(&reset_button, 10.0f, 0, &reset_button); + reset_button.HSplitTop(10.0f, 0, &reset_button); static int default_button = 0; - if (ui_do_button((void*)&default_button, localize("Reset to defaults"), 0, &reset_button, ui_draw_menu_button, 0)) + if(DoButton_Menu((void*)&default_button, localize("Reset to defaults"), 0, &reset_button)) gameclient.binds->set_defaults(); } -void MENUS::render_settings_graphics(RECT main_view) +void MENUS::render_settings_graphics(CUIRect main_view) { - RECT button; + CUIRect button; char buf[128]; static const int MAX_RESOLUTIONS = 256; @@ -430,44 +497,44 @@ void MENUS::render_settings_graphics(RECT main_view) if(num_modes == -1) num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); - RECT modelist; - ui_vsplit_l(&main_view, 300.0f, &main_view, &modelist); + CUIRect modelist; + main_view.VSplitLeft(300.0f, &main_view, &modelist); // draw allmodes switch - RECT header, footer; - ui_hsplit_t(&modelist, 20, &button, &modelist); - if (ui_do_button(&config.gfx_display_all_modes, localize("Show only supported"), config.gfx_display_all_modes^1, &button, ui_draw_checkbox, 0)) + CUIRect header, footer; + modelist.HSplitTop(20, &button, &modelist); + if(DoButton_CheckBox(&config.gfx_display_all_modes, localize("Show only supported"), config.gfx_display_all_modes^1, &button)) { config.gfx_display_all_modes ^= 1; num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); } // draw header - ui_hsplit_t(&modelist, 20, &header, &modelist); - ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f); - ui_do_label(&header, localize("Display Modes"), 14.0f, 0); + modelist.HSplitTop(20, &header, &modelist); + RenderTools()->DrawUIRect(&header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); + UI()->DoLabel(&header, localize("Display Modes"), 14.0f, 0); // draw footers - ui_hsplit_b(&modelist, 20, &modelist, &footer); + modelist.HSplitBottom(20, &modelist, &footer); str_format(buf, sizeof(buf), "%s: %dx%d %d bit", localize("Current"), config.gfx_screen_width, config.gfx_screen_height, config.gfx_color_depth); - ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f); - ui_vsplit_l(&footer, 10.0f, 0, &footer); - ui_do_label(&footer, buf, 14.0f, -1); + RenderTools()->DrawUIRect(&footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); + footer.VSplitLeft(10.0f, 0, &footer); + UI()->DoLabel(&footer, buf, 14.0f, -1); // modes - ui_draw_rect(&modelist, vec4(0,0,0,0.15f), 0, 0); + RenderTools()->DrawUIRect(&modelist, vec4(0,0,0,0.15f), 0, 0); - RECT scroll; - ui_vsplit_r(&modelist, 15, &modelist, &scroll); + CUIRect scroll; + modelist.VSplitRight(15, &modelist, &scroll); - RECT list = modelist; - ui_hsplit_t(&list, 20, &button, &list); + CUIRect list = modelist; + list.HSplitTop(20, &button, &list); int num = (int)(modelist.h/button.h); static float scrollvalue = 0; static int scrollbar = 0; - ui_hmargin(&scroll, 5.0f, &scroll); - scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); + scroll.HMargin(5.0f, &scroll); + scrollvalue = DoScrollbarV(&scrollbar, &scroll, scrollvalue); int start = (int)((num_modes-num)*scrollvalue); if(start < 0) @@ -490,7 +557,7 @@ void MENUS::render_settings_graphics(RECT main_view) } str_format(buf, sizeof(buf), " %dx%d %d bit", modes[i].width, modes[i].height, depth); - if(ui_do_button(&modes[i], buf, selected, &button, ui_draw_list_row, 0)) + if(DoButton_ListRow(&modes[i], buf, selected, &button)) { config.gfx_color_depth = depth; config.gfx_screen_width = modes[i].width; @@ -499,58 +566,58 @@ void MENUS::render_settings_graphics(RECT main_view) need_restart = true; } - ui_hsplit_t(&list, 20, &button, &list); + list.HSplitTop(20, &button, &list); } // switches - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_fullscreen, localize("Fullscreen"), config.gfx_fullscreen, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&config.gfx_fullscreen, localize("Fullscreen"), config.gfx_fullscreen, &button)) { config.gfx_fullscreen ^= 1; need_restart = true; } - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_vsync, localize("V-Sync"), config.gfx_vsync, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&config.gfx_vsync, localize("V-Sync"), config.gfx_vsync, &button)) { config.gfx_vsync ^= 1; need_restart = true; } - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_fsaa_samples, localize("FSAA samples"), config.gfx_fsaa_samples, &button, ui_draw_checkbox_number, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox_Number(&config.gfx_fsaa_samples, localize("FSAA samples"), config.gfx_fsaa_samples, &button)) { config.gfx_fsaa_samples = (config.gfx_fsaa_samples+1)%17; need_restart = true; } - ui_hsplit_t(&main_view, 40.0f, &button, &main_view); - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_texture_quality, localize("Quality Textures"), config.gfx_texture_quality, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(40.0f, &button, &main_view); + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&config.gfx_texture_quality, localize("Quality Textures"), config.gfx_texture_quality, &button)) { config.gfx_texture_quality ^= 1; need_restart = true; } - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_texture_compression, localize("Texture Compression"), config.gfx_texture_compression, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&config.gfx_texture_compression, localize("Texture Compression"), config.gfx_texture_compression, &button)) { config.gfx_texture_compression ^= 1; need_restart = true; } - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.gfx_high_detail, localize("High Detail"), config.gfx_high_detail, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&config.gfx_high_detail, localize("High Detail"), config.gfx_high_detail, &button)) config.gfx_high_detail ^= 1; // - RECT text; - ui_hsplit_t(&main_view, 20.0f, 0, &main_view); - ui_hsplit_t(&main_view, 20.0f, &text, &main_view); - //ui_vsplit_l(&text, 15.0f, 0, &text); - ui_do_label(&text, localize("UI Color"), 14.0f, -1); + CUIRect text; + main_view.HSplitTop(20.0f, 0, &main_view); + main_view.HSplitTop(20.0f, &text, &main_view); + //text.VSplitLeft(15.0f, 0, &text); + UI()->DoLabel(&text, localize("UI Color"), 14.0f, -1); const char *labels[] = { localize("Hue"), @@ -560,27 +627,27 @@ void MENUS::render_settings_graphics(RECT main_view) int *color_slider[4] = {&config.ui_color_hue, &config.ui_color_sat, &config.ui_color_lht, &config.ui_color_alpha}; for(int s = 0; s < 4; s++) { - RECT text; - ui_hsplit_t(&main_view, 19.0f, &button, &main_view); - ui_vmargin(&button, 15.0f, &button); - ui_vsplit_l(&button, 50.0f, &text, &button); - ui_vsplit_r(&button, 5.0f, &button, 0); - ui_hsplit_t(&button, 4.0f, 0, &button); + CUIRect text; + main_view.HSplitTop(19.0f, &button, &main_view); + button.VMargin(15.0f, &button); + button.VSplitLeft(50.0f, &text, &button); + button.VSplitRight(5.0f, &button, 0); + button.HSplitTop(4.0f, 0, &button); float k = (*color_slider[s]) / 255.0f; - k = ui_do_scrollbar_h(color_slider[s], &button, k); + k = DoScrollbarH(color_slider[s], &button, k); *color_slider[s] = (int)(k*255.0f); - ui_do_label(&text, labels[s], 15.0f, -1); + UI()->DoLabel(&text, labels[s], 15.0f, -1); } } -void MENUS::render_settings_sound(RECT main_view) +void MENUS::render_settings_sound(CUIRect main_view) { - RECT button; - ui_vsplit_l(&main_view, 300.0f, &main_view, 0); + CUIRect button; + main_view.VSplitLeft(300.0f, &main_view, 0); - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.snd_enable, localize("Use sounds"), config.snd_enable, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&config.snd_enable, localize("Use sounds"), config.snd_enable, &button)) { config.snd_enable ^= 1; need_restart = true; @@ -589,19 +656,19 @@ void MENUS::render_settings_sound(RECT main_view) if(!config.snd_enable) return; - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - if (ui_do_button(&config.snd_nonactive_mute, localize("Mute when not active"), config.snd_nonactive_mute, &button, ui_draw_checkbox, 0)) + main_view.HSplitTop(20.0f, &button, &main_view); + if(DoButton_CheckBox(&config.snd_nonactive_mute, localize("Mute when not active"), config.snd_nonactive_mute, &button)) config.snd_nonactive_mute ^= 1; // sample rate box { char buf[64]; str_format(buf, sizeof(buf), "%d", config.snd_rate); - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui_do_label(&button, localize("Sample rate"), 14.0f, -1); - ui_vsplit_l(&button, 110.0f, 0, &button); - ui_vsplit_l(&button, 180.0f, &button, 0); - ui_do_edit_box(&config.snd_rate, &button, buf, sizeof(buf), 14.0f); + main_view.HSplitTop(20.0f, &button, &main_view); + UI()->DoLabel(&button, localize("Sample rate"), 14.0f, -1); + button.VSplitLeft(110.0f, 0, &button); + button.VSplitLeft(180.0f, &button, 0); + DoEditBox(&config.snd_rate, &button, buf, sizeof(buf), 14.0f); int before = config.snd_rate; config.snd_rate = atoi(buf); @@ -614,14 +681,14 @@ void MENUS::render_settings_sound(RECT main_view) // volume slider { - RECT button, label; - ui_hsplit_t(&main_view, 5.0f, &button, &main_view); - ui_hsplit_t(&main_view, 20.0f, &button, &main_view); - ui_vsplit_l(&button, 110.0f, &label, &button); - ui_hmargin(&button, 2.0f, &button); - ui_do_label(&label, localize("Sound volume"), 14.0f, -1); - config.snd_volume = (int)(ui_do_scrollbar_h(&config.snd_volume, &button, config.snd_volume/100.0f)*100.0f); - ui_hsplit_t(&main_view, 20.0f, 0, &main_view); + CUIRect button, label; + main_view.HSplitTop(5.0f, &button, &main_view); + main_view.HSplitTop(20.0f, &button, &main_view); + button.VSplitLeft(110.0f, &label, &button); + button.HMargin(2.0f, &button); + UI()->DoLabel(&label, localize("Sound volume"), 14.0f, -1); + config.snd_volume = (int)(DoScrollbarH(&config.snd_volume, &button, config.snd_volume/100.0f)*100.0f); + main_view.HSplitTop(20.0f, 0, &main_view); } } @@ -660,7 +727,7 @@ void gather_languages(const char *name, int is_dir, void *user) languages.add(LANGUAGE(nicename, filename)); } -void MENUS::render_settings_general(RECT main_view) +void MENUS::render_settings_general(CUIRect main_view) { static int lanuagelist = 0; static int selected_language = 0; @@ -680,7 +747,7 @@ void MENUS::render_settings_general(RECT main_view) int old_selected = selected_language; - RECT list = main_view; + CUIRect list = main_view; ui_do_listbox_start(&lanuagelist, &list, 24.0f, localize("Language"), languages.size(), selected_language); for(sorted_array::range r = languages.all(); !r.empty(); r.pop_front()) @@ -688,7 +755,7 @@ void MENUS::render_settings_general(RECT main_view) LISTBOXITEM item = ui_do_listbox_nextitem(&r.front()); if(item.visible) - ui_do_label(&item.rect, r.front().name, 16.0f, -1); + UI()->DoLabel(&item.rect, r.front().name, 16.0f, -1); } selected_language = ui_do_listbox_end(); @@ -700,20 +767,20 @@ void MENUS::render_settings_general(RECT main_view) } } -void MENUS::render_settings(RECT main_view) +void MENUS::render_settings(CUIRect main_view) { static int settings_page = 0; // render background - RECT temp, tabbar; - ui_vsplit_r(&main_view, 120.0f, &main_view, &tabbar); - ui_draw_rect(&main_view, color_tabbar_active, CORNER_B|CORNER_TL, 10.0f); - ui_hsplit_t(&tabbar, 50.0f, &temp, &tabbar); - ui_draw_rect(&temp, color_tabbar_active, CORNER_R, 10.0f); + CUIRect temp, tabbar; + main_view.VSplitRight(120.0f, &main_view, &tabbar); + RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_B|CUI::CORNER_TL, 10.0f); + tabbar.HSplitTop(50.0f, &temp, &tabbar); + RenderTools()->DrawUIRect(&temp, color_tabbar_active, CUI::CORNER_R, 10.0f); - ui_hsplit_t(&main_view, 10.0f, 0, &main_view); + main_view.HSplitTop(10.0f, 0, &main_view); - RECT button; + CUIRect button; const char *tabs[] = { localize("General"), @@ -721,17 +788,18 @@ void MENUS::render_settings(RECT main_view) localize("Controls"), localize("Graphics"), localize("Sound")}; + int num_tabs = (int)(sizeof(tabs)/sizeof(*tabs)); for(int i = 0; i < num_tabs; i++) { - ui_hsplit_t(&tabbar, 10, &button, &tabbar); - ui_hsplit_t(&tabbar, 26, &button, &tabbar); - if(ui_do_button(tabs[i], tabs[i], settings_page == i, &button, ui_draw_settings_tab_button, 0)) + tabbar.HSplitTop(10, &button, &tabbar); + tabbar.HSplitTop(26, &button, &tabbar); + if(DoButton_SettingsTab(tabs[i], tabs[i], settings_page == i, &button)) settings_page = i; } - ui_margin(&main_view, 10.0f, &main_view); + main_view.Margin(10.0f, &main_view); if(settings_page == 0) render_settings_general(main_view); @@ -746,8 +814,8 @@ void MENUS::render_settings(RECT main_view) if(need_restart) { - RECT restart_warning; - ui_hsplit_b(&main_view, 40, &main_view, &restart_warning); - ui_do_label(&restart_warning, localize("You must restart the game for all settings to take effect."), 15.0f, -1, 220); + CUIRect restart_warning; + main_view.HSplitBottom(40, &main_view, &restart_warning); + UI()->DoLabel(&restart_warning, localize("You must restart the game for all settings to take effect."), 15.0f, -1, 220); } } diff --git a/src/game/client/components/motd.cpp b/src/game/client/components/motd.cpp index 0d1eacba..ba85f7f8 100644 --- a/src/game/client/components/motd.cpp +++ b/src/game/client/components/motd.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -28,22 +29,22 @@ void MOTD::on_render() if(!is_active()) return; - float width = 400*3.0f*gfx_screenaspect(); + float width = 400*3.0f*Graphics()->ScreenAspect(); float height = 400*3.0f; - gfx_mapscreen(0, 0, width, height); + Graphics()->MapScreen(0, 0, width, height); float h = 800.0f; float w = 650.0f; float x = width/2 - w/2; float y = 150.0f; - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5f); - draw_round_rect(x, y, w, h, 40.0f); - gfx_quads_end(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0,0,0,0.5f); + RenderTools()->draw_round_rect(x, y, w, h, 40.0f); + Graphics()->QuadsEnd(); gfx_text(0, x+40.0f, y+40.0f, 32.0f, server_motd, (int)(w-80.0f)); } diff --git a/src/game/client/components/particles.cpp b/src/game/client/components/particles.cpp index ef6dbbe9..61fcf738 100644 --- a/src/game/client/components/particles.cpp +++ b/src/game/client/components/particles.cpp @@ -1,4 +1,6 @@ #include +#include + #include #include #include @@ -124,31 +126,31 @@ void PARTICLES::on_render() void PARTICLES::render_group(int group) { - gfx_blend_normal(); + Graphics()->BlendNormal(); //gfx_blend_additive(); - gfx_texture_set(data->images[IMAGE_PARTICLES].id); - gfx_quads_begin(); + Graphics()->TextureSet(data->images[IMAGE_PARTICLES].id); + Graphics()->QuadsBegin(); int i = first_part[group]; while(i != -1) { - select_sprite(particles[i].spr); + RenderTools()->select_sprite(particles[i].spr); float a = particles[i].life / particles[i].life_span; vec2 p = particles[i].pos; float size = mix(particles[i].start_size, particles[i].end_size, a); - gfx_quads_setrotation(particles[i].rot); + Graphics()->QuadsSetRotation(particles[i].rot); - gfx_setcolor( + Graphics()->SetColor( particles[i].color.r, particles[i].color.g, particles[i].color.b, particles[i].color.a); // pow(a, 0.75f) * - gfx_quads_draw(p.x, p.y, size, size); + Graphics()->QuadsDraw(p.x, p.y, size, size); i = particles[i].next_part; } - gfx_quads_end(); - gfx_blend_normal(); + Graphics()->QuadsEnd(); + Graphics()->BlendNormal(); } diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 6d6bcc37..e1fee3e8 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -40,23 +41,23 @@ void PLAYERS::render_hand(TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, floa hand_pos += dirx * post_rot_offset.x; hand_pos += diry * post_rot_offset.y; - //gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id); - gfx_texture_set(info->texture); - gfx_quads_begin(); - gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a); + //Graphics()->TextureSet(data->images[IMAGE_CHAR_DEFAULT].id); + Graphics()->TextureSet(info->texture); + Graphics()->QuadsBegin(); + Graphics()->SetColor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a); // two passes for (int i = 0; i < 2; i++) { bool outline = i == 0; - select_sprite(outline?SPRITE_TEE_HAND_OUTLINE:SPRITE_TEE_HAND, 0, 0, 0); - gfx_quads_setrotation(angle); - gfx_quads_draw(hand_pos.x, hand_pos.y, 2*basesize, 2*basesize); + RenderTools()->select_sprite(outline?SPRITE_TEE_HAND_OUTLINE:SPRITE_TEE_HAND, 0, 0, 0); + Graphics()->QuadsSetRotation(angle); + Graphics()->QuadsDraw(hand_pos.x, hand_pos.y, 2*basesize, 2*basesize); } - gfx_quads_setrotation(0); - gfx_quads_end(); + Graphics()->QuadsSetRotation(0); + Graphics()->QuadsEnd(); } inline float normalize_angular(float f) @@ -237,9 +238,9 @@ void PLAYERS::render_player( // draw hook if (prev.hook_state>0 && player.hook_state>0) { - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - //gfx_quads_begin(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); + //Graphics()->QuadsBegin(); vec2 pos = position; vec2 hook_pos; @@ -260,36 +261,36 @@ void PLAYERS::render_player( float d = distance(pos, hook_pos); vec2 dir = normalize(pos-hook_pos); - gfx_quads_setrotation(get_angle(dir)+pi); + Graphics()->QuadsSetRotation(get_angle(dir)+pi); // render head - select_sprite(SPRITE_HOOK_HEAD); - gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16); + RenderTools()->select_sprite(SPRITE_HOOK_HEAD); + Graphics()->QuadsDraw(hook_pos.x, hook_pos.y, 24,16); // render chain - select_sprite(SPRITE_HOOK_CHAIN); + RenderTools()->select_sprite(SPRITE_HOOK_CHAIN); int i = 0; for(float f = 24; f < d && i < 1024; f += 24, i++) { vec2 p = hook_pos + dir*f; - gfx_quads_draw(p.x, p.y,24,16); + Graphics()->QuadsDraw(p.x, p.y,24,16); } - gfx_quads_setrotation(0); - gfx_quads_end(); + Graphics()->QuadsSetRotation(0); + Graphics()->QuadsEnd(); render_hand(&render_info, position, normalize(hook_pos-pos), -pi/2, vec2(20, 0)); } // draw gun { - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - gfx_quads_setrotation(state.attach.angle*pi*2+angle); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); + Graphics()->QuadsSetRotation(state.attach.angle*pi*2+angle); // normal weapons int iw = clamp(player.weapon, 0, NUM_WEAPONS-1); - select_sprite(data->weapons.id[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); + RenderTools()->select_sprite(data->weapons.id[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); vec2 dir = direction; float recoil = 0.0f; @@ -302,14 +303,14 @@ void PLAYERS::render_player( // if attack is under way, bash stuffs if(direction.x < 0) { - gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); + Graphics()->QuadsSetRotation(-pi/2-state.attach.angle*pi*2); p.x -= data->weapons.id[iw].offsetx; } else { - gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); + Graphics()->QuadsSetRotation(-pi/2+state.attach.angle*pi*2); } - draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); + RenderTools()->draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); } else if (player.weapon == WEAPON_NINJA) { @@ -318,16 +319,16 @@ void PLAYERS::render_player( if(direction.x < 0) { - gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); + Graphics()->QuadsSetRotation(-pi/2-state.attach.angle*pi*2); p.x -= data->weapons.id[iw].offsetx; gameclient.effects->powerupshine(p+vec2(32,0), vec2(32,12)); } else { - gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); + Graphics()->QuadsSetRotation(-pi/2+state.attach.angle*pi*2); gameclient.effects->powerupshine(p-vec2(32,0), vec2(32,12)); } - draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); + RenderTools()->draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); // HADOKEN if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons.id[iw].num_sprite_muzzles) @@ -339,14 +340,14 @@ void PLAYERS::render_player( vec2 dir = vec2(player_char->x,player_char->y) - vec2(prev_char->x, prev_char->y); dir = normalize(dir); float hadokenangle = get_angle(dir); - gfx_quads_setrotation(hadokenangle); + Graphics()->QuadsSetRotation(hadokenangle); //float offsety = -data->weapons[iw].muzzleoffsety; - select_sprite(data->weapons.id[iw].sprite_muzzles[itex], 0); + RenderTools()->select_sprite(data->weapons.id[iw].sprite_muzzles[itex], 0); vec2 diry(-dir.y,dir.x); p = position; float offsetx = data->weapons.id[iw].muzzleoffsetx; p -= dir * offsetx; - draw_sprite(p.x, p.y, 160.0f); + RenderTools()->draw_sprite(p.x, p.y, 160.0f); } } } @@ -359,7 +360,7 @@ void PLAYERS::render_player( recoil = sinf(a*pi); p = position + dir * data->weapons.id[iw].offsetx - dir*recoil*10.0f; p.y += data->weapons.id[iw].offsety; - draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); + RenderTools()->draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); } if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN) @@ -379,18 +380,18 @@ void PLAYERS::render_player( if (alpha > 0.0f && data->weapons.id[iw].sprite_muzzles[itex]) { float offsety = -data->weapons.id[iw].muzzleoffsety; - select_sprite(data->weapons.id[iw].sprite_muzzles[itex], direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); + RenderTools()->select_sprite(data->weapons.id[iw].sprite_muzzles[itex], direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); if(direction.x < 0) offsety = -offsety; vec2 diry(-dir.y,dir.x); vec2 muzzlepos = p + dir * data->weapons.id[iw].muzzleoffsetx + diry * offsety; - draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons.id[iw].visual_size); + RenderTools()->draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons.id[iw].visual_size); } } } - gfx_quads_end(); + Graphics()->QuadsEnd(); switch (player.weapon) { @@ -408,27 +409,27 @@ void PLAYERS::render_player( TEE_RENDER_INFO ghost = render_info; ghost.color_body.a = 0.5f; ghost.color_feet.a = 0.5f; - render_tee(&state, &ghost, player.emote, direction, ghost_position); // render ghost + RenderTools()->RenderTee(&state, &ghost, player.emote, direction, ghost_position); // render ghost } render_info.size = 64.0f; // force some settings render_info.color_body.a = 1.0f; render_info.color_feet.a = 1.0f; - render_tee(&state, &render_info, player.emote, direction, position); + RenderTools()->RenderTee(&state, &render_info, player.emote, direction, position); if(player.player_state == PLAYERSTATE_CHATTING) { - gfx_texture_set(data->images[IMAGE_EMOTICONS].id); - gfx_quads_begin(); - select_sprite(SPRITE_DOTDOT); - gfx_quads_draw(position.x + 24, position.y - 40, 64,64); - gfx_quads_end(); + Graphics()->TextureSet(data->images[IMAGE_EMOTICONS].id); + Graphics()->QuadsBegin(); + RenderTools()->select_sprite(SPRITE_DOTDOT); + Graphics()->QuadsDraw(position.x + 24, position.y - 40, 64,64); + Graphics()->QuadsEnd(); } if (gameclient.clients[info.cid].emoticon_start != -1 && gameclient.clients[info.cid].emoticon_start + 2 * client_tickspeed() > client_tick()) { - gfx_texture_set(data->images[IMAGE_EMOTICONS].id); - gfx_quads_begin(); + Graphics()->TextureSet(data->images[IMAGE_EMOTICONS].id); + Graphics()->QuadsBegin(); int since_start = client_tick() - gameclient.clients[info.cid].emoticon_start; int from_end = gameclient.clients[info.cid].emoticon_start + 2 * client_tickspeed() - client_tick(); @@ -448,13 +449,13 @@ void PLAYERS::render_player( float wiggle_angle = sin(5*wiggle); - gfx_quads_setrotation(pi/6*wiggle_angle); + Graphics()->QuadsSetRotation(pi/6*wiggle_angle); - gfx_setcolor(1.0f,1.0f,1.0f,a); + Graphics()->SetColor(1.0f,1.0f,1.0f,a); // client_datas::emoticon is an offset from the first emoticon - select_sprite(SPRITE_OOP + gameclient.clients[info.cid].emoticon); - gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h); - gfx_quads_end(); + RenderTools()->select_sprite(SPRITE_OOP + gameclient.clients[info.cid].emoticon); + Graphics()->QuadsDraw(position.x, position.y - 23 - 32*h, 64, 64*h); + Graphics()->QuadsEnd(); } } diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp index 2978eff9..e8db1aed 100644 --- a/src/game/client/components/scoreboard.cpp +++ b/src/game/client/components/scoreboard.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -33,12 +34,12 @@ void SCOREBOARD::render_goals(float x, float y, float w) { float h = 50.0f; - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5f); - draw_round_rect(x-10.f, y-10.f, w, h, 10.0f); - gfx_quads_end(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0,0,0,0.5f); + RenderTools()->draw_round_rect(x-10.f, y-10.f, w, h, 10.0f); + Graphics()->QuadsEnd(); // render goals //y = ystart+h-54; @@ -76,12 +77,12 @@ void SCOREBOARD::render_spectators(float x, float y, float w) str_format(buffer, sizeof(buffer), "%s: ", localize("Spectators")); - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5f); - draw_round_rect(x-10.f, y-10.f, w, h, 10.0f); - gfx_quads_end(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0,0,0,0.5f); + RenderTools()->draw_round_rect(x-10.f, y-10.f, w, h, 10.0f); + Graphics()->QuadsEnd(); for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++) { @@ -109,12 +110,12 @@ void SCOREBOARD::render_scoreboard(float x, float y, float w, int team, const ch //float ystart = y; float h = 750.0f; - gfx_blend_normal(); - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(0,0,0,0.5f); - draw_round_rect(x-10.f, y-10.f, w, h, 17.0f); - gfx_quads_end(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(0,0,0,0.5f); + RenderTools()->draw_round_rect(x-10.f, y-10.f, w, h, 17.0f); + Graphics()->QuadsEnd(); // render title if(!title) @@ -210,11 +211,11 @@ void SCOREBOARD::render_scoreboard(float x, float y, float w, int team, const ch if(info->local) { // background so it's easy to find the local player - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(1,1,1,0.25f); - draw_round_rect(x, y, w-20, line_height*0.95f, 17.0f); - gfx_quads_end(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); + Graphics()->SetColor(1,1,1,0.25f); + RenderTools()->draw_round_rect(x, y, w-20, line_height*0.95f, 17.0f); + Graphics()->QuadsEnd(); } str_format(buf, sizeof(buf), "%4d", info->score); @@ -230,21 +231,21 @@ void SCOREBOARD::render_scoreboard(float x, float y, float w, int team, const ch if((gameclient.snap.flags[0] && gameclient.snap.flags[0]->carried_by == info->cid) || (gameclient.snap.flags[1] && gameclient.snap.flags[1]->carried_by == info->cid)) { - gfx_blend_normal(); - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); + Graphics()->BlendNormal(); + Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->QuadsBegin(); - if(info->team == 0) select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); - else select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); + if(info->team == 0) RenderTools()->select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); + else RenderTools()->select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); float size = 64.0f; - gfx_quads_drawTL(x+55, y-15, size/2, size); - gfx_quads_end(); + Graphics()->QuadsDrawTL(x+55, y-15, size/2, size); + Graphics()->QuadsEnd(); } TEE_RENDER_INFO teeinfo = gameclient.clients[info->cid].render_info; teeinfo.size *= tee_sizemod; - render_tee(ANIMSTATE::get_idle(), &teeinfo, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28+tee_offset)); + RenderTools()->RenderTee(ANIMSTATE::get_idle(), &teeinfo, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28+tee_offset)); y += line_height; @@ -278,10 +279,10 @@ void SCOREBOARD::on_render() gameclient.motd->clear(); - float width = 400*3.0f*gfx_screenaspect(); + float width = 400*3.0f*Graphics()->ScreenAspect(); float height = 400*3.0f; - gfx_mapscreen(0, 0, width, height); + Graphics()->MapScreen(0, 0, width, height); float w = 650.0f; diff --git a/src/game/client/components/skins.cpp b/src/game/client/components/skins.cpp index 451a77f3..ad3607a1 100644 --- a/src/game/client/components/skins.cpp +++ b/src/game/client/components/skins.cpp @@ -6,12 +6,10 @@ #include #include +#include #include -extern "C" -{ - #include -} +#include #include "skins.hpp" @@ -32,13 +30,13 @@ void SKINS::skinscan(const char *name, int is_dir, void *user) char buf[512]; str_format(buf, sizeof(buf), "skins/%s", name); IMAGE_INFO info; - if(!gfx_load_png(&info, buf)) + if(!self->Graphics()->LoadPNG(&info, buf)) { dbg_msg("game", "failed to load skin from %s", name); return; } - self->skins[self->num_skins].org_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data, info.format, 0); + self->skins[self->num_skins].org_texture = self->Graphics()->LoadTextureRaw(info.width, info.height, info.format, info.data, info.format, 0); int body_size = 96; // body size unsigned char *d = (unsigned char *)info.data; @@ -111,7 +109,7 @@ void SKINS::skinscan(const char *name, int is_dir, void *user) } } - self->skins[self->num_skins].color_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data, info.format, 0); + self->skins[self->num_skins].color_texture = self->Graphics()->LoadTextureRaw(info.width, info.height, info.format, info.data, info.format, 0); mem_free(info.data); // set skin data diff --git a/src/game/client/components/voting.cpp b/src/game/client/components/voting.cpp index 190ba224..dcf5c954 100644 --- a/src/game/client/components/voting.cpp +++ b/src/game/client/components/voting.cpp @@ -143,30 +143,30 @@ void VOTING::on_render() } -void VOTING::render_bars(RECT bars, bool text) +void VOTING::render_bars(CUIRect bars, bool text) { - ui_draw_rect(&bars, vec4(0.8f,0.8f,0.8f,0.5f), CORNER_ALL, bars.h/3); + RenderTools()->DrawUIRect(&bars, vec4(0.8f,0.8f,0.8f,0.5f), CUI::CORNER_ALL, bars.h/3); - RECT splitter = bars; + CUIRect splitter = bars; splitter.x = splitter.x+splitter.w/2; splitter.w = splitter.h/2.0f; splitter.x -= splitter.w/2; - ui_draw_rect(&splitter, vec4(0.4f,0.4f,0.4f,0.5f), CORNER_ALL, splitter.h/4); + RenderTools()->DrawUIRect(&splitter, vec4(0.4f,0.4f,0.4f,0.5f), CUI::CORNER_ALL, splitter.h/4); if(total) { - RECT pass_area = bars; + CUIRect pass_area = bars; if(yes) { - RECT yes_area = bars; + CUIRect yes_area = bars; yes_area.w *= yes/(float)total; - ui_draw_rect(&yes_area, vec4(0.2f,0.9f,0.2f,0.85f), CORNER_ALL, bars.h/3); + RenderTools()->DrawUIRect(&yes_area, vec4(0.2f,0.9f,0.2f,0.85f), CUI::CORNER_ALL, bars.h/3); if(text) { char buf[256]; str_format(buf, sizeof(buf), "%d", yes); - ui_do_label(&yes_area, buf, bars.h*0.75f, 0); + UI()->DoLabel(&yes_area, buf, bars.h*0.75f, 0); } pass_area.x += yes_area.w; @@ -175,16 +175,16 @@ void VOTING::render_bars(RECT bars, bool text) if(no) { - RECT no_area = bars; + CUIRect no_area = bars; no_area.w *= no/(float)total; no_area.x = (bars.x + bars.w)-no_area.w; - ui_draw_rect(&no_area, vec4(0.9f,0.2f,0.2f,0.85f), CORNER_ALL, bars.h/3); + RenderTools()->DrawUIRect(&no_area, vec4(0.9f,0.2f,0.2f,0.85f), CUI::CORNER_ALL, bars.h/3); if(text) { char buf[256]; str_format(buf, sizeof(buf), "%d", no); - ui_do_label(&no_area, buf, bars.h*0.75f, 0); + UI()->DoLabel(&no_area, buf, bars.h*0.75f, 0); } pass_area.w -= no_area.w; @@ -194,7 +194,7 @@ void VOTING::render_bars(RECT bars, bool text) { char buf[256]; str_format(buf, sizeof(buf), "%d", pass); - ui_do_label(&pass_area, buf, bars.h*0.75f, 0); + UI()->DoLabel(&pass_area, buf, bars.h*0.75f, 0); } } } diff --git a/src/game/client/components/voting.hpp b/src/game/client/components/voting.hpp index 3341edea..e04e1840 100644 --- a/src/game/client/components/voting.hpp +++ b/src/game/client/components/voting.hpp @@ -1,10 +1,6 @@ #include #include - -extern "C" -{ - #include -} +#include class VOTING : public COMPONENT { @@ -38,7 +34,7 @@ public: virtual void on_message(int msgtype, void *rawmsg); virtual void on_render(); - void render_bars(RECT bars, bool text); + void render_bars(CUIRect bars, bool text); void callvote_kick(int client_id); void callvote_option(int option); diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index fa2f4de0..25a42620 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -269,6 +270,13 @@ void GAMECLIENT::on_init() // set the language localization.load(config.cl_languagefile); + // propagate pointers + m_UI.SetGraphics(Graphics()); + m_RenderTools.m_pGraphics = Graphics(); + m_RenderTools.m_pUI = UI(); + for(int i = 0; i < all.num; i++) + all.components[i]->client = this; + // init all components for(int i = 0; i < all.num; i++) all.components[i]->on_init(); @@ -297,7 +305,7 @@ void GAMECLIENT::on_init() for(int i = 0; i < data->num_images; i++) { gameclient.menus->render_loading(load_current/load_total); - data->images[i].id = gfx_load_texture(data->images[i].filename, IMG_AUTO, 0); + data->images[i].id = Graphics()->LoadTexture(data->images[i].filename, IMG_AUTO, 0); load_current++; } @@ -366,7 +374,7 @@ void GAMECLIENT::on_connected() { layers_init(); col_init(); - render_tilemap_generate_skip(); + RenderTools()->render_tilemap_generate_skip(); for(int i = 0; i < all.num; i++) { @@ -450,6 +458,21 @@ static void evolve(NETOBJ_CHARACTER *character, int tick) void GAMECLIENT::on_render() { + /*Graphics()->Clear(1,0,0); + + menus->render_background(); + return;*/ + /* + Graphics()->Clear(1,0,0); + Graphics()->MapScreen(0,0,100,100); + + Graphics()->QuadsBegin(); + Graphics()->SetColor(1,1,1,1); + Graphics()->QuadsDraw(50, 50, 30, 30); + Graphics()->QuadsEnd(); + + return;*/ + // update the local character position update_local_character_pos(); @@ -972,3 +995,17 @@ void GAMECLIENT::conchain_special_infoupdate(void *result, void *user_data, CONS if(console_arg_num(result)) ((GAMECLIENT*)user_data)->send_info(false); } + +void GAMECLIENT::SetEngine(class IEngine *pEngine) +{ + m_pEngine = pEngine; + + // digg out some pointers + m_pGraphics = m_pEngine->Graphics(); +} + +IGameClient *CreateGameClient(IEngine *pEngine) +{ + gameclient.SetEngine(pEngine); + return &gameclient; +} diff --git a/src/game/client/gameclient.hpp b/src/game/client/gameclient.hpp index e354430d..1da40de0 100644 --- a/src/game/client/gameclient.hpp +++ b/src/game/client/gameclient.hpp @@ -1,10 +1,13 @@ +#ifndef FILE_GAMECLIENT_HPP +#define FILE_GAMECLIENT_HPP #include #include +#include #include #include "render.hpp" -class GAMECLIENT +class GAMECLIENT : public IGameClient { class STACK { @@ -24,6 +27,9 @@ class GAMECLIENT STACK all; STACK input; + class IGraphics *m_pGraphics; + class IEngine *m_pEngine; + CUI m_UI; void dispatch_input(); void process_events(); @@ -38,6 +44,12 @@ class GAMECLIENT static void conchain_special_infoupdate(void *result, void *user_data, CONSOLE_CALLBACK cb, void *cbuser); public: + class IGraphics *Graphics() const { return m_pGraphics; } + class CUI *UI() { return &m_UI; } + class CRenderTools *RenderTools() { return &m_RenderTools; } + + void SetEngine(class IEngine *pEngine); + bool suppress_events; bool new_tick; bool new_predicted_tick; @@ -120,6 +132,8 @@ public: CLIENT_DATA clients[MAX_CLIENTS]; + CRenderTools m_RenderTools; + void on_reset(); // hooks @@ -158,6 +172,10 @@ public: class VOTING *voting; }; + +// TODO: Refactor: Remove this extern GAMECLIENT gameclient; extern const char *localize(const char *str); + +#endif diff --git a/src/game/client/render.cpp b/src/game/client/render.cpp index f63e8692..f271c7d2 100644 --- a/src/game/client/render.cpp +++ b/src/game/client/render.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -13,13 +14,15 @@ static float sprite_w_scale; static float sprite_h_scale; + + /* static void layershot_begin() { if(!config.cl_layershot) return; - gfx_clear(0,0,0); + Graphics()->Clear(0,0,0); } static void layershot_end() @@ -33,7 +36,7 @@ static void layershot_end() config.cl_layershot++; }*/ -void select_sprite(SPRITE *spr, int flags, int sx, int sy) +void CRenderTools::select_sprite(SPRITE *spr, int flags, int sx, int sy) { int x = spr->x+sx; int y = spr->y+sy; @@ -66,22 +69,22 @@ void select_sprite(SPRITE *spr, int flags, int sx, int sy) x2 = temp; } - gfx_quads_setsubset(x1, y1, x2, y2); + Graphics()->QuadsSetSubset(x1, y1, x2, y2); } -void select_sprite(int id, int flags, int sx, int sy) +void CRenderTools::select_sprite(int id, int flags, int sx, int sy) { if(id < 0 || id > data->num_sprites) return; select_sprite(&data->sprites[id], flags, sx, sy); } -void draw_sprite(float x, float y, float size) +void CRenderTools::draw_sprite(float x, float y, float size) { - gfx_quads_draw(x, y, size*sprite_w_scale, size*sprite_h_scale); + Graphics()->QuadsDraw(x, y, size*sprite_w_scale, size*sprite_h_scale); } -void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners) +void CRenderTools::draw_round_rect_ext(float x, float y, float w, float h, float r, int corners) { int num = 8; for(int i = 0; i < num; i+=2) @@ -97,69 +100,73 @@ void draw_round_rect_ext(float x, float y, float w, float h, float r, int corner float sa3 = sinf(a3); if(corners&1) // TL - gfx_quads_draw_freeform( + Graphics()->QuadsDrawFreeform( x+r, y+r, x+(1-ca1)*r, y+(1-sa1)*r, x+(1-ca3)*r, y+(1-sa3)*r, x+(1-ca2)*r, y+(1-sa2)*r); if(corners&2) // TR - gfx_quads_draw_freeform( + Graphics()->QuadsDrawFreeform( x+w-r, y+r, x+w-r+ca1*r, y+(1-sa1)*r, x+w-r+ca3*r, y+(1-sa3)*r, x+w-r+ca2*r, y+(1-sa2)*r); if(corners&4) // BL - gfx_quads_draw_freeform( + Graphics()->QuadsDrawFreeform( x+r, y+h-r, x+(1-ca1)*r, y+h-r+sa1*r, x+(1-ca3)*r, y+h-r+sa3*r, x+(1-ca2)*r, y+h-r+sa2*r); if(corners&8) // BR - gfx_quads_draw_freeform( + Graphics()->QuadsDrawFreeform( x+w-r, y+h-r, x+w-r+ca1*r, y+h-r+sa1*r, x+w-r+ca3*r, y+h-r+sa3*r, x+w-r+ca2*r, y+h-r+sa2*r); } - gfx_quads_drawTL(x+r, y+r, w-r*2, h-r*2); // center - gfx_quads_drawTL(x+r, y, w-r*2, r); // top - gfx_quads_drawTL(x+r, y+h-r, w-r*2, r); // bottom - gfx_quads_drawTL(x, y+r, r, h-r*2); // left - gfx_quads_drawTL(x+w-r, y+r, r, h-r*2); // right + Graphics()->QuadsDrawTL(x+r, y+r, w-r*2, h-r*2); // center + Graphics()->QuadsDrawTL(x+r, y, w-r*2, r); // top + Graphics()->QuadsDrawTL(x+r, y+h-r, w-r*2, r); // bottom + Graphics()->QuadsDrawTL(x, y+r, r, h-r*2); // left + Graphics()->QuadsDrawTL(x+w-r, y+r, r, h-r*2); // right - if(!(corners&1)) gfx_quads_drawTL(x, y, r, r); // TL - if(!(corners&2)) gfx_quads_drawTL(x+w, y, -r, r); // TR - if(!(corners&4)) gfx_quads_drawTL(x, y+h, r, -r); // BL - if(!(corners&8)) gfx_quads_drawTL(x+w, y+h, -r, -r); // BR + if(!(corners&1)) Graphics()->QuadsDrawTL(x, y, r, r); // TL + if(!(corners&2)) Graphics()->QuadsDrawTL(x+w, y, -r, r); // TR + if(!(corners&4)) Graphics()->QuadsDrawTL(x, y+h, r, -r); // BL + if(!(corners&8)) Graphics()->QuadsDrawTL(x+w, y+h, -r, -r); // BR } -void draw_round_rect(float x, float y, float w, float h, float r) +void CRenderTools::draw_round_rect(float x, float y, float w, float h, float r) { draw_round_rect_ext(x,y,w,h,r,0xf); } -void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding) +void CRenderTools::DrawUIRect(const CUIRect *r, vec4 color, int corners, float rounding) { - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(color.r, color.g, color.b, color.a); - draw_round_rect_ext(r->x,r->y,r->w,r->h,rounding*ui_scale(), corners); - gfx_quads_end(); + Graphics()->TextureSet(-1); + + // TODO: FIX US + Graphics()->QuadsBegin(); + Graphics()->SetColor(color.r, color.g, color.b, color.a); + draw_round_rect_ext(r->x,r->y,r->w,r->h,rounding*UI()->Scale(), corners); + Graphics()->QuadsEnd(); } -void render_tee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos) +void CRenderTools::RenderTee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos) { vec2 direction = dir; vec2 position = pos; - //gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id); - gfx_texture_set(info->texture); - gfx_quads_begin(); - //gfx_quads_draw(pos.x, pos.y-128, 128, 128); + //Graphics()->TextureSet(data->images[IMAGE_CHAR_DEFAULT].id); + Graphics()->TextureSet(info->texture); + + // TODO: FIX ME + Graphics()->QuadsBegin(); + //Graphics()->QuadsDraw(pos.x, pos.y-128, 128, 128); // first pass we draw the outline // second pass we draw the filling @@ -173,13 +180,13 @@ void render_tee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec float basesize = info->size; if(f == 1) { - gfx_quads_setrotation(anim->body.angle*pi*2); + Graphics()->QuadsSetRotation(anim->body.angle*pi*2); // draw body - gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, 1.0f); + Graphics()->SetColor(info->color_body.r, info->color_body.g, info->color_body.b, 1.0f); vec2 body_pos = position + vec2(anim->body.x, anim->body.y)*animscale; select_sprite(outline?SPRITE_TEE_BODY_OUTLINE:SPRITE_TEE_BODY, 0, 0, 0); - gfx_quads_draw(body_pos.x, body_pos.y, basesize, basesize); + Graphics()->QuadsDraw(body_pos.x, body_pos.y, basesize, basesize); // draw eyes if(p == 1) @@ -207,8 +214,8 @@ void render_tee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec float h = emote == EMOTE_BLINK ? basesize*0.15f : eyescale; float eyeseparation = (0.075f - 0.010f*fabs(direction.x))*basesize; vec2 offset = vec2(direction.x*0.125f, -0.05f+direction.y*0.10f)*basesize; - gfx_quads_draw(body_pos.x-eyeseparation+offset.x, body_pos.y+offset.y, eyescale, h); - gfx_quads_draw(body_pos.x+eyeseparation+offset.x, body_pos.y+offset.y, -eyescale, h); + Graphics()->QuadsDraw(body_pos.x-eyeseparation+offset.x, body_pos.y+offset.y, eyescale, h); + Graphics()->QuadsDraw(body_pos.x+eyeseparation+offset.x, body_pos.y+offset.y, -eyescale, h); } } @@ -218,7 +225,7 @@ void render_tee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec float w = basesize; float h = basesize/2; - gfx_quads_setrotation(foot->angle*pi*2); + Graphics()->QuadsSetRotation(foot->angle*pi*2); bool indicate = !info->got_airjump && config.cl_airjumpindicator; float cs = 1.0f; // color scale @@ -232,12 +239,12 @@ void render_tee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec cs = 0.5f; } - gfx_setcolor(info->color_feet.r*cs, info->color_feet.g*cs, info->color_feet.b*cs, 1.0f); - gfx_quads_draw(position.x+foot->x*animscale, position.y+foot->y*animscale, w, h); + Graphics()->SetColor(info->color_feet.r*cs, info->color_feet.g*cs, info->color_feet.b*cs, 1.0f); + Graphics()->QuadsDraw(position.x+foot->x*animscale, position.y+foot->y*animscale, w, h); } } - gfx_quads_end(); + Graphics()->QuadsEnd(); } @@ -262,7 +269,7 @@ static void calc_screen_params(float amount, float wmax, float hmax, float aspec } } -void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y, +void CRenderTools::mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y, float offset_x, float offset_y, float aspect, float zoom, float *points) { float width, height; @@ -277,7 +284,7 @@ void mapscreen_to_world(float center_x, float center_y, float parallax_x, float points[3] = offset_y+center_y+height/2; } -void render_tilemap_generate_skip() +void CRenderTools::render_tilemap_generate_skip() { for(int g = 0; g < layers_num_groups(); g++) { diff --git a/src/game/client/render.hpp b/src/game/client/render.hpp index 9ce555d2..8e99b432 100644 --- a/src/game/client/render.hpp +++ b/src/game/client/render.hpp @@ -7,6 +7,7 @@ #include "../mapitems.hpp" #include "ui.hpp" + struct TEE_RENDER_INFO { TEE_RENDER_INFO() @@ -37,32 +38,44 @@ enum TILERENDERFLAG_EXTEND=4, }; -//typedef struct SPRITE; -void select_sprite(struct SPRITE *spr, int flags=0, int sx=0, int sy=0); -void select_sprite(int id, int flags=0, int sx=0, int sy=0); +class CRenderTools +{ +public: + class IGraphics *m_pGraphics; + class CUI *m_pUI; + + class IGraphics *Graphics() const { return m_pGraphics; } + class CUI *UI() const { return m_pUI; } -void draw_sprite(float x, float y, float size); + //typedef struct SPRITE; -// rects -void draw_round_rect(float x, float y, float w, float h, float r); -void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners); -void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding); + void select_sprite(struct SPRITE *spr, int flags=0, int sx=0, int sy=0); + void select_sprite(int id, int flags=0, int sx=0, int sy=0); -// larger rendering methods -void render_tilemap_generate_skip(); + void draw_sprite(float x, float y, float size); -// object render methods (gc_render_obj.cpp) -void render_tee(class ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos); + // rects + void draw_round_rect(float x, float y, float w, float h, float r); + void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners); + + void DrawUIRect(const CUIRect *r, vec4 color, int corners, float rounding); -// map render methods (gc_render_map.cpp) -void render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result); -void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, int env, float *channels), int flags); -void render_tilemap(TILE *tiles, int w, int h, float scale, vec4 color, int flags); + // larger rendering methods + void render_tilemap_generate_skip(); -// helpers -void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y, - float offset_x, float offset_y, float aspect, float zoom, float *points); + // object render methods (gc_render_obj.cpp) + void RenderTee(class ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos); + // map render methods (gc_render_map.cpp) + static void render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result); + void render_quads(QUAD *quads, int num_quads, int flags, void (*eval)(float time_offset, int env, float *channels, void *user), void *user); + void render_tilemap(TILE *tiles, int w, int h, float scale, vec4 color, int flags); + + // helpers + void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y, + float offset_x, float offset_y, float aspect, float zoom, float *points); + +}; #endif diff --git a/src/game/client/render_map.cpp b/src/game/client/render_map.cpp index 118a4d73..ea3b8420 100644 --- a/src/game/client/render_map.cpp +++ b/src/game/client/render_map.cpp @@ -2,10 +2,11 @@ #include #include #include +#include #include "render.hpp" -void render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result) +void CRenderTools::render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result) { if(num_points == 0) { @@ -77,9 +78,9 @@ static void rotate(POINT *center, POINT *point, float rotation) point->y = (int)(x * sinf(rotation) + y * cosf(rotation) + center->y); } -void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, int env, float *channels), int renderflags) +void CRenderTools::render_quads(QUAD *quads, int num_quads, int renderflags, void (*eval)(float time_offset, int env, float *channels, void *user), void *user) { - gfx_quads_begin(); + Graphics()->QuadsBegin(); float conv = 1/255.0f; for(int i = 0; i < num_quads; i++) { @@ -90,7 +91,7 @@ void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, in if(q->color_env >= 0) { float channels[4]; - eval(q->color_env_offset/1000.0f, q->color_env, channels); + eval(q->color_env_offset/1000.0f, q->color_env, channels, user); r = channels[0]; g = channels[1]; b = channels[2]; @@ -106,7 +107,7 @@ void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, in if(!opaque && !(renderflags&LAYERRENDERFLAG_TRANSPARENT)) continue; - gfx_quads_setsubset_free( + Graphics()->QuadsSetSubsetFree( fx2f(q->texcoords[0].x), fx2f(q->texcoords[0].y), fx2f(q->texcoords[1].x), fx2f(q->texcoords[1].y), fx2f(q->texcoords[2].x), fx2f(q->texcoords[2].y), @@ -121,17 +122,17 @@ void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, in if(q->pos_env >= 0) { float channels[4]; - eval(q->pos_env_offset/1000.0f, q->pos_env, channels); + eval(q->pos_env_offset/1000.0f, q->pos_env, channels, user); offset_x = channels[0]; offset_y = channels[1]; rot = channels[2]/360.0f*pi*2; } - gfx_setcolorvertex(0, q->colors[0].r*conv*r, q->colors[0].g*conv*g, q->colors[0].b*conv*b, q->colors[0].a*conv*a); - gfx_setcolorvertex(1, q->colors[1].r*conv*r, q->colors[1].g*conv*g, q->colors[1].b*conv*b, q->colors[1].a*conv*a); - gfx_setcolorvertex(2, q->colors[2].r*conv*r, q->colors[2].g*conv*g, q->colors[2].b*conv*b, q->colors[2].a*conv*a); - gfx_setcolorvertex(3, q->colors[3].r*conv*r, q->colors[3].g*conv*g, q->colors[3].b*conv*b, q->colors[3].a*conv*a); + Graphics()->SetColorVertex(0, q->colors[0].r*conv*r, q->colors[0].g*conv*g, q->colors[0].b*conv*b, q->colors[0].a*conv*a); + Graphics()->SetColorVertex(1, q->colors[1].r*conv*r, q->colors[1].g*conv*g, q->colors[1].b*conv*b, q->colors[1].a*conv*a); + Graphics()->SetColorVertex(2, q->colors[2].r*conv*r, q->colors[2].g*conv*g, q->colors[2].b*conv*b, q->colors[2].a*conv*a); + Graphics()->SetColorVertex(3, q->colors[3].r*conv*r, q->colors[3].g*conv*g, q->colors[3].b*conv*b, q->colors[3].a*conv*a); POINT *points = q->points; @@ -150,30 +151,30 @@ void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, in rotate(&q->points[4], &rotated[3], rot); } - gfx_quads_draw_freeform( + Graphics()->QuadsDrawFreeform( fx2f(points[0].x)+offset_x, fx2f(points[0].y)+offset_y, fx2f(points[1].x)+offset_x, fx2f(points[1].y)+offset_y, fx2f(points[2].x)+offset_x, fx2f(points[2].y)+offset_y, fx2f(points[3].x)+offset_x, fx2f(points[3].y)+offset_y ); } - gfx_quads_end(); + Graphics()->QuadsEnd(); } -void render_tilemap(TILE *tiles, int w, int h, float scale, vec4 color, int renderflags) +void CRenderTools::render_tilemap(TILE *tiles, int w, int h, float scale, vec4 color, int renderflags) { - //gfx_texture_set(img_get(tmap->image)); + //Graphics()->TextureSet(img_get(tmap->image)); float screen_x0, screen_y0, screen_x1, screen_y1; - gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); - //gfx_mapscreen(screen_x0-50, screen_y0-50, screen_x1+50, screen_y1+50); + Graphics()->GetScreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); + //Graphics()->MapScreen(screen_x0-50, screen_y0-50, screen_x1+50, screen_y1+50); // calculate the final pixelsize for the tiles float tile_pixelsize = 1024/32.0f; - float final_tilesize = scale/(screen_x1-screen_x0) * gfx_screenwidth(); + float final_tilesize = scale/(screen_x1-screen_x0) * Graphics()->ScreenWidth(); float final_tilesize_scale = final_tilesize/tile_pixelsize; - gfx_quads_begin(); - gfx_setcolor(color.r, color.g, color.b, color.a); + Graphics()->QuadsBegin(); + Graphics()->SetColor(color.r, color.g, color.b, color.a); int starty = (int)(screen_y0/scale)-1; int startx = (int)(screen_x0/scale)-1; @@ -262,13 +263,13 @@ void render_tilemap(TILE *tiles, int w, int h, float scale, vec4 color, int rend v1 = tmp; } - gfx_quads_setsubset(u0,v0,u1,v1); - gfx_quads_drawTL(x*scale, y*scale, scale, scale); + Graphics()->QuadsSetSubset(u0,v0,u1,v1); + Graphics()->QuadsDrawTL(x*scale, y*scale, scale, scale); } } x += tiles[c].skip; } - gfx_quads_end(); - gfx_mapscreen(screen_x0, screen_y0, screen_x1, screen_y1); + Graphics()->QuadsEnd(); + Graphics()->MapScreen(screen_x0, screen_y0, screen_x1, screen_y1); } diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 1361b85c..4aaaf32f 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -3,101 +3,95 @@ #include #include +#include #include "ui.hpp" /******************************************************** UI *********************************************************/ -static const void *hot_item = 0; -static const void *active_item = 0; -static const void *last_active_item = 0; -static const void *becomming_hot_item = 0; -static float mouse_x, mouse_y; /* in gui space */ -static float mouse_wx, mouse_wy; /* in world space */ -static unsigned mouse_buttons = 0; -static unsigned last_mouse_buttons = 0; - -float ui_mouse_x() { return mouse_x; } -float ui_mouse_y() { return mouse_y; } -float ui_mouse_world_x() { return mouse_wx; } -float ui_mouse_world_y() { return mouse_wy; } -int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; } -int ui_mouse_button_clicked(int index) { return ui_mouse_button(index) && !((last_mouse_buttons>>index)&1) ; } - -void ui_set_hot_item(const void *id) { becomming_hot_item = id; } -void ui_set_active_item(const void *id) { active_item = id; if (id) last_active_item = id; } -void ui_clear_last_active_item() { last_active_item = 0; } -const void *ui_hot_item() { return hot_item; } -const void *ui_next_hot_item() { return becomming_hot_item; } -const void *ui_active_item() { return active_item; } -const void *ui_last_active_item() { return last_active_item; } - -int ui_update(float mx, float my, float mwx, float mwy, int buttons) + +CUI::CUI() +{ + m_pHotItem = 0; + m_pActiveItem = 0; + m_pLastActiveItem = 0; + m_pBecommingHotItem = 0; + + m_MouseX = 0; + m_MouseY = 0; + m_MouseWorldX = 0; + m_MouseWorldY = 0; + m_MouseButtons = 0; + m_LastMouseButtons = 0; + + m_Screen.x = 0; + m_Screen.y = 0; + m_Screen.w = 848.0f; + m_Screen.h = 480.0f; +} + +int CUI::Update(float mx, float my, float mwx, float mwy, int Buttons) { - mouse_x = mx; - mouse_y = my; - mouse_wx = mwx; - mouse_wy = mwy; - last_mouse_buttons = mouse_buttons; - mouse_buttons = buttons; - hot_item = becomming_hot_item; - if(active_item) - hot_item = active_item; - becomming_hot_item = 0; + m_MouseX = mx; + m_MouseY = my; + m_MouseWorldX = mwx; + m_MouseWorldY = mwy; + m_LastMouseButtons = m_MouseButtons; + m_MouseButtons = Buttons; + m_pHotItem = m_pBecommingHotItem; + if(m_pActiveItem) + m_pHotItem = m_pActiveItem; + m_pBecommingHotItem = 0; return 0; } -/* -bool ui_ -*/ -int ui_mouse_inside(const RECT *r) + +int CUI::MouseInside(const CUIRect *r) { - if(mouse_x >= r->x && mouse_x <= r->x+r->w && mouse_y >= r->y && mouse_y <= r->y+r->h) + if(m_MouseX >= r->x && m_MouseX <= r->x+r->w && m_MouseY >= r->y && m_MouseY <= r->y+r->h) return 1; return 0; } -static RECT screen = { 0.0f, 0.0f, 848.0f, 480.0f }; - -RECT *ui_screen() +CUIRect *CUI::Screen() { - float aspect = gfx_screenaspect(); + float aspect = Graphics()->ScreenAspect(); float w, h; h = 600; w = aspect*h; - screen.w = w; - screen.h = h; + m_Screen.w = w; + m_Screen.h = h; - return &screen; + return &m_Screen; } -void ui_set_scale(float s) +void CUI::SetScale(float s) { - config.ui_scale = (int)(s*100.0f); + //config.UI()->Scale = (int)(s*100.0f); } -float ui_scale() +/*float CUI::Scale() { - return config.ui_scale/100.0f; -} + return config.UI()->Scale/100.0f; +}*/ -void ui_clip_enable(const RECT *r) +void CUI::ClipEnable(const CUIRect *r) { - float xscale = gfx_screenwidth()/ui_screen()->w; - float yscale = gfx_screenheight()/ui_screen()->h; - gfx_clip_enable((int)(r->x*xscale), (int)(r->y*yscale), (int)(r->w*xscale), (int)(r->h*yscale)); + float xscale = Graphics()->ScreenWidth()/Screen()->w; + float yscale = Graphics()->ScreenHeight()/Screen()->h; + Graphics()->ClipEnable((int)(r->x*xscale), (int)(r->y*yscale), (int)(r->w*xscale), (int)(r->h*yscale)); } -void ui_clip_disable() +void CUI::ClipDisable() { - gfx_clip_disable(); + Graphics()->ClipDisable(); } -void ui_hsplit_t(const RECT *original, float cut, RECT *top, RECT *bottom) +void CUIRect::HSplitTop(float cut, CUIRect *top, CUIRect *bottom) const { - RECT r = *original; - cut *= ui_scale(); + CUIRect r = *this; + cut *= Scale(); if (top) { @@ -116,10 +110,10 @@ void ui_hsplit_t(const RECT *original, float cut, RECT *top, RECT *bottom) } } -void ui_hsplit_b(const RECT *original, float cut, RECT *top, RECT *bottom) +void CUIRect::HSplitBottom(float cut, CUIRect *top, CUIRect *bottom) const { - RECT r = *original; - cut *= ui_scale(); + CUIRect r = *this; + cut *= Scale(); if (top) { @@ -139,9 +133,9 @@ void ui_hsplit_b(const RECT *original, float cut, RECT *top, RECT *bottom) } -void ui_vsplit_mid(const RECT *original, RECT *left, RECT *right) +void CUIRect::VSplitMid(CUIRect *left, CUIRect *right) const { - RECT r = *original; + CUIRect r = *this; float cut = r.w/2; if (left) @@ -161,10 +155,10 @@ void ui_vsplit_mid(const RECT *original, RECT *left, RECT *right) } } -void ui_vsplit_l(const RECT *original, float cut, RECT *left, RECT *right) +void CUIRect::VSplitLeft(float cut, CUIRect *left, CUIRect *right) const { - RECT r = *original; - cut *= ui_scale(); + CUIRect r = *this; + cut *= Scale(); if (left) { @@ -183,10 +177,10 @@ void ui_vsplit_l(const RECT *original, float cut, RECT *left, RECT *right) } } -void ui_vsplit_r(const RECT *original, float cut, RECT *left, RECT *right) +void CUIRect::VSplitRight(float cut, CUIRect *left, CUIRect *right) const { - RECT r = *original; - cut *= ui_scale(); + CUIRect r = *this; + cut *= Scale(); if (left) { @@ -205,10 +199,10 @@ void ui_vsplit_r(const RECT *original, float cut, RECT *left, RECT *right) } } -void ui_margin(const RECT *original, float cut, RECT *other_rect) +void CUIRect::Margin(float cut, CUIRect *other_rect) const { - RECT r = *original; - cut *= ui_scale(); + CUIRect r = *this; + cut *= Scale(); other_rect->x = r.x + cut; other_rect->y = r.y + cut; @@ -216,10 +210,10 @@ void ui_margin(const RECT *original, float cut, RECT *other_rect) other_rect->h = r.h - 2*cut; } -void ui_vmargin(const RECT *original, float cut, RECT *other_rect) +void CUIRect::VMargin(float cut, CUIRect *other_rect) const { - RECT r = *original; - cut *= ui_scale(); + CUIRect r = *this; + cut *= Scale(); other_rect->x = r.x + cut; other_rect->y = r.y; @@ -227,10 +221,10 @@ void ui_vmargin(const RECT *original, float cut, RECT *other_rect) other_rect->h = r.h; } -void ui_hmargin(const RECT *original, float cut, RECT *other_rect) +void CUIRect::HMargin(float cut, CUIRect *other_rect) const { - RECT r = *original; - cut *= ui_scale(); + CUIRect r = *this; + cut *= Scale(); other_rect->x = r.x; other_rect->y = r.y + cut; @@ -238,50 +232,87 @@ void ui_hmargin(const RECT *original, float cut, RECT *other_rect) other_rect->h = r.h - 2*cut; } - -int ui_do_button(const void *id, const char *text, int checked, const RECT *r, ui_draw_button_func draw_func, const void *extra) +int CUI::DoButtonLogic(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { /* logic */ + int ReturnValue = 0; + int Inside = MouseInside(pRect); + static int ButtonUsed = 0; + + if(ActiveItem() == pID) + { + if(!MouseButton(ButtonUsed)) + { + if(Inside && Checked >= 0) + ReturnValue = 1+ButtonUsed; + SetActiveItem(0); + } + } + else if(HotItem() == pID) + { + if(MouseButton(0)) + { + SetActiveItem(pID); + ButtonUsed = 0; + } + + if(MouseButton(1)) + { + SetActiveItem(pID); + ButtonUsed = 1; + } + } + + if(Inside) + SetHotItem(pID); + + return ReturnValue; +} +/* +int CUI::DoButton(const void *id, const char *text, int checked, const CUIRect *r, ui_draw_button_func draw_func, const void *extra) +{ + // logic int ret = 0; - int inside = ui_mouse_inside(r); + int inside = ui_MouseInside(r); static int button_used = 0; - if(ui_active_item() == id) + if(ui_ActiveItem() == id) { - if(!ui_mouse_button(button_used)) + if(!ui_MouseButton(button_used)) { if(inside && checked >= 0) ret = 1+button_used; - ui_set_active_item(0); + ui_SetActiveItem(0); } } - else if(ui_hot_item() == id) + else if(ui_HotItem() == id) { - if(ui_mouse_button(0)) + if(ui_MouseButton(0)) { - ui_set_active_item(id); + ui_SetActiveItem(id); button_used = 0; } - if(ui_mouse_button(1)) + if(ui_MouseButton(1)) { - ui_set_active_item(id); + ui_SetActiveItem(id); button_used = 1; } } if(inside) - ui_set_hot_item(id); + ui_SetHotItem(id); if(draw_func) draw_func(id, text, checked, r, extra); return ret; -} +}*/ -void ui_do_label(const RECT *r, const char *text, float size, int align, int max_width) +void CUI::DoLabel(const CUIRect *r, const char *text, float size, int align, int max_width) { - gfx_blend_normal(); - size *= ui_scale(); + // TODO: FIX ME!!!! + //Graphics()->BlendNormal(); + size *= Scale(); if(align == 0) { float tw = gfx_text_width(0, size, text, max_width); diff --git a/src/game/client/ui.hpp b/src/game/client/ui.hpp index 7a6cb5de..96f6c48b 100644 --- a/src/game/client/ui.hpp +++ b/src/game/client/ui.hpp @@ -2,64 +2,93 @@ #ifndef FILE_GAME_CLIENT_UI_H #define FILE_GAME_CLIENT_UI_H -typedef struct +class CUIRect { + // TODO: Refactor: Redo UI scaling + float Scale() const { return 1.0f; } +public: float x, y, w, h; -} RECT; + + void HSplitTop(float Cut, CUIRect *pTop, CUIRect *pBottom) const; + void HSplitBottom(float Cut, CUIRect *pTop, CUIRect *pBottom) const; + void VSplitMid(CUIRect *pLeft, CUIRect *pRight) const; + void VSplitLeft(float Cut, CUIRect *pLeft, CUIRect *pRight) const; + void VSplitRight(float Cut, CUIRect *pLeft, CUIRect *pRight) const; + + void Margin(float Cut, CUIRect *pOtherRect) const; + void VMargin(float Cut, CUIRect *pOtherRect) const; + void HMargin(float Cut, CUIRect *pOtherRect) const; + +}; -enum +class CUI { - CORNER_TL=1, - CORNER_TR=2, - CORNER_BL=4, - CORNER_BR=8, + const void *m_pHotItem; + const void *m_pActiveItem; + const void *m_pLastActiveItem; + const void *m_pBecommingHotItem; + float m_MouseX, m_MouseY; /* in gui space */ + float m_MouseWorldX, m_MouseWorldY; /* in world space */ + unsigned m_MouseButtons; + unsigned m_LastMouseButtons; - CORNER_T=CORNER_TL|CORNER_TR, - CORNER_B=CORNER_BL|CORNER_BR, - CORNER_R=CORNER_TR|CORNER_BR, - CORNER_L=CORNER_TL|CORNER_BL, + CUIRect m_Screen; + class IGraphics *m_pGraphics; - CORNER_ALL=CORNER_T|CORNER_B -}; +public: + // TODO: Refactor: Fill this in + void SetGraphics(class IGraphics *pGraphics) { m_pGraphics = pGraphics; } + class IGraphics *Graphics() { return m_pGraphics; } -int ui_update(float mx, float my, float mwx, float mwy, int buttons); + CUI(); -float ui_mouse_x(); -float ui_mouse_y(); -float ui_mouse_world_x(); -float ui_mouse_world_y(); -int ui_mouse_button(int index); -int ui_mouse_button_clicked(int index); + enum + { + CORNER_TL=1, + CORNER_TR=2, + CORNER_BL=4, + CORNER_BR=8, + + CORNER_T=CORNER_TL|CORNER_TR, + CORNER_B=CORNER_BL|CORNER_BR, + CORNER_R=CORNER_TR|CORNER_BR, + CORNER_L=CORNER_TL|CORNER_BL, + + CORNER_ALL=CORNER_T|CORNER_B + }; -void ui_set_hot_item(const void *id); -void ui_set_active_item(const void *id); -void ui_clear_last_active_item(); -const void *ui_hot_item(); -const void *ui_next_hot_item(); -const void *ui_active_item(); -const void *ui_last_active_item(); + int Update(float mx, float my, float mwx, float mwy, int buttons); -int ui_mouse_inside(const RECT *r); + float MouseX() const { return m_MouseX; } + float MouseY() const { return m_MouseY; } + float MouseWorldX() const { return m_MouseWorldX; } + float MouseWorldY() const { return m_MouseWorldY; } + int MouseButton(int Index) const { return (m_MouseButtons>>Index)&1; } + int MouseButtonClicked(int Index) { return MouseButton(Index) && !((m_LastMouseButtons>>Index)&1) ; } -RECT *ui_screen(); -void ui_set_scale(float s); -float ui_scale(); -void ui_clip_enable(const RECT *r); -void ui_clip_disable(); + void SetHotItem(const void *pID) { m_pBecommingHotItem = pID; } + void SetActiveItem(const void *pID) { m_pActiveItem = pID; if (pID) m_pLastActiveItem = pID; } + void ClearLastActiveItem() { m_pLastActiveItem = 0; } + const void *HotItem() const { return m_pHotItem; } + const void *NextHotItem() const { return m_pBecommingHotItem; } + const void *ActiveItem() const { return m_pActiveItem; } + const void *LastActiveItem() const { return m_pLastActiveItem; } -void ui_hsplit_t(const RECT *original, float cut, RECT *top, RECT *bottom); -void ui_hsplit_b(const RECT *original, float cut, RECT *top, RECT *bottom); -void ui_vsplit_mid(const RECT *original, RECT *left, RECT *right); -void ui_vsplit_l(const RECT *original, float cut, RECT *left, RECT *right); -void ui_vsplit_r(const RECT *original, float cut, RECT *left, RECT *right); + int MouseInside(const CUIRect *r); -void ui_margin(const RECT *original, float cut, RECT *other_rect); -void ui_vmargin(const RECT *original, float cut, RECT *other_rect); -void ui_hmargin(const RECT *original, float cut, RECT *other_rect); + CUIRect *Screen(); + void ClipEnable(const CUIRect *r); + void ClipDisable(); + + // TODO: Refactor: Redo UI scaling + void SetScale(float s); + float Scale() const { return 1.0f; } -typedef void (*ui_draw_button_func)(const void *id, const char *text, int checked, const RECT *r, const void *extra); -int ui_do_button(const void *id, const char *text, int checked, const RECT *r, ui_draw_button_func draw_func, const void *extra); -void ui_do_label(const RECT *r, const char *text, float size, int align, int max_width = -1); + int DoButtonLogic(const void *pID, const char *pText /* TODO: Refactor: Remove */, int Checked, const CUIRect *pRect); + + // TODO: Refactor: Remove this? + void DoLabel(const CUIRect *r, const char *text, float size, int align, int max_width = -1); +}; #endif diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/ed_editor.cpp index d56a0506..17b891fc 100644 --- a/src/game/editor/ed_editor.cpp +++ b/src/game/editor/ed_editor.cpp @@ -6,12 +6,11 @@ #include #include -extern "C" { - #include - #include - #include - #include -} +#include +#include +#include +#include +#include #include #include @@ -25,10 +24,19 @@ static int background_texture = 0; static int cursor_texture = 0; static int entities_texture = 0; +enum +{ + BUTTON_CONTEXT=1, +}; -static const void *ui_got_context = 0; -EDITOR editor; + +EDITOR_IMAGE::~EDITOR_IMAGE() +{ + editor->Graphics()->UnloadTexture(tex_id); +} + +static const void *ui_got_context = 0; LAYERGROUP::LAYERGROUP() { @@ -52,7 +60,7 @@ LAYERGROUP::~LAYERGROUP() clear(); } -void LAYERGROUP::convert(RECT *rect) +void LAYERGROUP::convert(CUIRect *rect) { rect->x += offset_x; rect->y += offset_y; @@ -60,52 +68,53 @@ void LAYERGROUP::convert(RECT *rect) void LAYERGROUP::mapping(float *points) { - mapscreen_to_world( - editor.world_offset_x, editor.world_offset_y, + m_pMap->editor->RenderTools()->mapscreen_to_world( + m_pMap->editor->world_offset_x, m_pMap->editor->world_offset_y, parallax_x/100.0f, parallax_y/100.0f, offset_x, offset_y, - gfx_screenaspect(), editor.world_zoom, points); + m_pMap->editor->Graphics()->ScreenAspect(), m_pMap->editor->world_zoom, points); - points[0] += editor.editor_offset_x; - points[1] += editor.editor_offset_y; - points[2] += editor.editor_offset_x; - points[3] += editor.editor_offset_y; + points[0] += m_pMap->editor->editor_offset_x; + points[1] += m_pMap->editor->editor_offset_y; + points[2] += m_pMap->editor->editor_offset_x; + points[3] += m_pMap->editor->editor_offset_y; } void LAYERGROUP::mapscreen() { float points[4]; mapping(points); - gfx_mapscreen(points[0], points[1], points[2], points[3]); + m_pMap->editor->Graphics()->MapScreen(points[0], points[1], points[2], points[3]); } void LAYERGROUP::render() { mapscreen(); + IGraphics *pGraphics = m_pMap->editor->Graphics(); if(use_clipping) { float points[4]; - editor.map.game_group->mapping(points); + m_pMap->game_group->mapping(points); float x0 = (clip_x - points[0]) / (points[2]-points[0]); float y0 = (clip_y - points[1]) / (points[3]-points[1]); float x1 = ((clip_x+clip_w) - points[0]) / (points[2]-points[0]); float y1 = ((clip_y+clip_h) - points[1]) / (points[3]-points[1]); - gfx_clip_enable((int)(x0*gfx_screenwidth()), (int)(y0*gfx_screenheight()), - (int)((x1-x0)*gfx_screenwidth()), (int)((y1-y0)*gfx_screenheight())); + pGraphics->ClipEnable((int)(x0*pGraphics->ScreenWidth()), (int)(y0*pGraphics->ScreenHeight()), + (int)((x1-x0)*pGraphics->ScreenWidth()), (int)((y1-y0)*pGraphics->ScreenHeight())); } for(int i = 0; i < layers.len(); i++) { - if(layers[i]->visible && layers[i] != editor.map.game_layer) + if(layers[i]->visible && layers[i] != m_pMap->game_layer) { - if(editor.show_detail || !(layers[i]->flags&LAYERFLAG_DETAIL)) + if(m_pMap->editor->show_detail || !(layers[i]->flags&LAYERFLAG_DETAIL)) layers[i]->render(); } } - gfx_clip_disable(); + pGraphics->ClipDisable(); } bool LAYERGROUP::is_empty() const { return layers.len() == 0; } @@ -178,21 +187,105 @@ void EDITOR_IMAGE::analyse_tileflags() *********************************************************/ // copied from gc_menu.cpp, should be more generalized -//extern int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden=false); +//extern int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false); -int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden=false) +int EDITOR::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden) { - int inside = ui_mouse_inside(rect); + int Inside = UI()->MouseInside(pRect); + int ReturnValue = 0; + static int AtIndex = 0; + + if(UI()->LastActiveItem() == pID) + { + int Len = strlen(pStr); + + if(Inside && UI()->MouseButton(0)) + { + int mx_rel = (int)(UI()->MouseX() - pRect->x); + + for (int i = 1; i <= Len; i++) + { + if (gfx_text_width(0, FontSize, pStr, i) + 10 > mx_rel) + { + AtIndex = i - 1; + break; + } + + if (i == Len) + AtIndex = Len; + } + } + + for(int i = 0; i < inp_num_events(); i++) + { + Len = strlen(pStr); + LINEINPUT::manipulate(inp_get_event(i), pStr, StrSize, &Len, &AtIndex); + } + } + + bool JustGotActive = false; + + if(UI()->ActiveItem() == pID) + { + if(!UI()->MouseButton(0)) + UI()->SetActiveItem(0); + } + else if(UI()->HotItem() == pID) + { + if(UI()->MouseButton(0)) + { + if (UI()->LastActiveItem() != pID) + JustGotActive = true; + UI()->SetActiveItem(pID); + } + } + + if(Inside) + UI()->SetHotItem(pID); + + CUIRect Textbox = *pRect; + RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); + Textbox.VMargin(5.0f, &Textbox); + + const char *pDisplayStr = pStr; + char aStars[128]; + + if(Hidden) + { + unsigned s = strlen(pStr); + if(s >= sizeof(aStars)) + s = sizeof(aStars)-1; + memset(aStars, '*', s); + aStars[s] = 0; + pDisplayStr = aStars; + } + + UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1); + + if (UI()->LastActiveItem() == pID && !JustGotActive) + { + float w = gfx_text_width(0, FontSize, pDisplayStr, AtIndex); + Textbox.x += w*UI()->Scale(); + UI()->DoLabel(&Textbox, "_", FontSize, -1); + } + + return ReturnValue; +} + +/* +int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false) +{ + int inside = UI()->MouseInside(rect); int r = 0; static int at_index = 0; - if(ui_last_active_item() == id) + if(UI()->LastActiveItem() == id) { int len = strlen(str); - if (inside && ui_mouse_button(0)) + if (inside && UI()->MouseButton(0)) { - int mx_rel = (int)(ui_mouse_x() - rect->x); + int mx_rel = (int)(UI()->MouseX() - rect->x); for (int i = 1; i <= len; i++) { @@ -218,27 +311,27 @@ int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float fo bool just_got_active = false; - if(ui_active_item() == id) + if(UI()->ActiveItem() == id) { - if(!ui_mouse_button(0)) - ui_set_active_item(0); + if(!UI()->MouseButton(0)) + UI()->SetActiveItem(0); } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == id) { - if(ui_mouse_button(0)) + if(UI()->MouseButton(0)) { - if (ui_last_active_item() != id) + if (UI()->LastActiveItem() != id) just_got_active = true; - ui_set_active_item(id); + UI()->SetActiveItem(id); } } if(inside) - ui_set_hot_item(id); + UI()->SetHotItem(id); - RECT textbox = *rect; - ui_draw_rect(&textbox, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f); - ui_vmargin(&textbox, 5.0f, &textbox); + CUIRect textbox = *rect; + RenderTools()->DrawUIRect(&textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); + textbox.VMargin(5.0f, &textbox); const char *display_str = str; char stars[128]; @@ -253,197 +346,251 @@ int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float fo display_str = stars; } - ui_do_label(&textbox, display_str, font_size, -1); + UI()->DoLabel(&textbox, display_str, font_size, -1); - if (ui_last_active_item() == id && !just_got_active) + if (UI()->LastActiveItem() == id && !just_got_active) { float w = gfx_text_width(0, font_size, display_str, at_index); - textbox.x += w*ui_scale(); - ui_do_label(&textbox, "_", font_size, -1); + textbox.x += w*UI()->Scale(); + UI()->DoLabel(&textbox, "_", font_size, -1); } return r; } +*/ -vec4 button_color_mul(const void *id) +vec4 EDITOR::button_color_mul(const void *id) { - if(ui_active_item() == id) + if(UI()->ActiveItem() == id) return vec4(1,1,1,0.5f); - else if(ui_hot_item() == id) + else if(UI()->HotItem() == id) return vec4(1,1,1,1.5f); return vec4(1,1,1,1); } -float ui_do_scrollbar_v(const void *id, const RECT *rect, float current) +float EDITOR::ui_do_scrollbar_v(const void *id, const CUIRect *rect, float current) { - RECT handle; + CUIRect handle; static float offset_y; - ui_hsplit_t(rect, 33, &handle, 0); + rect->HSplitTop(33, &handle, 0); handle.y += (rect->h-handle.h)*current; /* logic */ float ret = current; - int inside = ui_mouse_inside(&handle); + int inside = UI()->MouseInside(&handle); - if(ui_active_item() == id) + if(UI()->ActiveItem() == id) { - if(!ui_mouse_button(0)) - ui_set_active_item(0); + if(!UI()->MouseButton(0)) + UI()->SetActiveItem(0); float min = rect->y; float max = rect->h-handle.h; - float cur = ui_mouse_y()-offset_y; + float cur = UI()->MouseY()-offset_y; ret = (cur-min)/max; if(ret < 0.0f) ret = 0.0f; if(ret > 1.0f) ret = 1.0f; } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == id) { - if(ui_mouse_button(0)) + if(UI()->MouseButton(0)) { - ui_set_active_item(id); - offset_y = ui_mouse_y()-handle.y; + UI()->SetActiveItem(id); + offset_y = UI()->MouseY()-handle.y; } } if(inside) - ui_set_hot_item(id); + UI()->SetHotItem(id); // render - RECT rail; - ui_vmargin(rect, 5.0f, &rail); - ui_draw_rect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); + CUIRect rail; + rect->VMargin(5.0f, &rail); + RenderTools()->DrawUIRect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); - RECT slider = handle; + CUIRect slider = handle; slider.w = rail.x-slider.x; - ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_L, 2.5f); + RenderTools()->DrawUIRect(&slider, vec4(1,1,1,0.25f), CUI::CORNER_L, 2.5f); slider.x = rail.x+rail.w; - ui_draw_rect(&slider, vec4(1,1,1,0.25f), CORNER_R, 2.5f); + RenderTools()->DrawUIRect(&slider, vec4(1,1,1,0.25f), CUI::CORNER_R, 2.5f); slider = handle; - ui_margin(&slider, 5.0f, &slider); - ui_draw_rect(&slider, vec4(1,1,1,0.25f)*button_color_mul(id), CORNER_ALL, 2.5f); + slider.Margin(5.0f, &slider); + RenderTools()->DrawUIRect(&slider, vec4(1,1,1,0.25f)*button_color_mul(id), CUI::CORNER_ALL, 2.5f); return ret; } -static vec4 get_button_color(const void *id, int checked) +vec4 EDITOR::get_button_color(const void *id, int checked) { if(checked < 0) return vec4(0,0,0,0.5f); if(checked > 0) { - if(ui_hot_item() == id) + if(UI()->HotItem() == id) return vec4(1,0,0,0.75f); return vec4(1,0,0,0.5f); } - if(ui_hot_item() == id) + if(UI()->HotItem() == id) return vec4(1,1,1,0.75f); return vec4(1,1,1,0.5f); } -void draw_editor_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int EDITOR::DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), CORNER_ALL, 3.0f); - ui_do_label(r, text, 10, 0, -1); + if(UI()->MouseInside(pRect)) + { + if(Flags&BUTTON_CONTEXT) + ui_got_context = pID; + if(tooltip) + tooltip = pToolTip; + } + + if(UI()->HotItem() == pID && pToolTip) + tooltip = (const char *)pToolTip; + + return UI()->DoButtonLogic(pID, pText, Checked, pRect); + + // Draw here + //return UI()->DoButton(id, text, checked, r, draw_func, 0); +} + + +int EDITOR::DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) +{ + RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_ALL, 3.0f); + UI()->DoLabel(pRect, pText, 10, 0, -1); + return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -static void draw_editor_button_file(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int EDITOR::DoButton_File(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - if(ui_hot_item() == id) - ui_draw_rect(r, get_button_color(id, checked), CORNER_ALL, 3.0f); + if(UI()->HotItem() == pID) + RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_ALL, 3.0f); - RECT t = *r; - ui_vmargin(&t, 5.0f, &t); - ui_do_label(&t, text, 10, -1, -1); + CUIRect t = *pRect; + t.VMargin(5.0f, &t); + UI()->DoLabel(&t, pText, 10, -1, -1); + return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -static void draw_editor_button_menu(const void *id, const char *text, int checked, const RECT *rect, const void *extra) +//static void draw_editor_button_menu(const void *id, const char *text, int checked, const CUIRect *rect, const void *extra) +int EDITOR::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { /* - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - if(ui_hot_item() == id) - ui_draw_rect(r, get_button_color(id, checked), CORNER_ALL, 3.0f); + if(UI()->HotItem() == id) if(extra) editor.tooltip = (const char *)extra; + if(UI()->HotItem() == id) + RenderTools()->DrawUIRect(r, get_button_color(id, checked), CUI::CORNER_ALL, 3.0f); */ - RECT r = *rect; + CUIRect r = *pRect; /* if(ui_popups[id == id) { - ui_draw_rect(&r, vec4(0.5f,0.5f,0.5f,0.75f), CORNER_T, 3.0f); - ui_margin(&r, 1.0f, &r); - ui_draw_rect(&r, vec4(0,0,0,0.75f), CORNER_T, 3.0f); + RenderTools()->DrawUIRect(&r, vec4(0.5f,0.5f,0.5f,0.75f), CUI::CORNER_T, 3.0f); + r.Margin(1.0f, &r); + RenderTools()->DrawUIRect(&r, vec4(0,0,0,0.75f), CUI::CORNER_T, 3.0f); } else*/ - ui_draw_rect(&r, vec4(0.5f,0.5f,0.5f, 1.0f), CORNER_T, 3.0f); + RenderTools()->DrawUIRect(&r, vec4(0.5f,0.5f,0.5f, 1.0f), CUI::CORNER_T, 3.0f); - r = *rect; - ui_vmargin(&r, 5.0f, &r); - ui_do_label(&r, text, 10, -1, -1); + r = *pRect; + r.VMargin(5.0f, &r); + UI()->DoLabel(&r, pText, 10, -1, -1); + + return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); - //RECT t = *r; + //CUIRect t = *r; } -void draw_editor_button_menuitem(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int EDITOR::DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - if(ui_hot_item() == id || checked) - ui_draw_rect(r, get_button_color(id, checked), CORNER_ALL, 3.0f); + if(UI()->HotItem() == pID || Checked) + RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_ALL, 3.0f); - RECT t = *r; - ui_vmargin(&t, 5.0f, &t); - ui_do_label(&t, text, 10, -1, -1); + CUIRect t = *pRect; + t.VMargin(5.0f, &t); + UI()->DoLabel(&t, pText, 10, -1, -1); + return DoButton_Editor_Common(pID, pText, Checked, pRect, 0, 0); } -static void draw_editor_button_l(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int EDITOR::DoButton_ButtonL(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), CORNER_L, 3.0f); - ui_do_label(r, text, 10, 0, -1); + RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_L, 3.0f); + UI()->DoLabel(pRect, pText, 10, 0, -1); + return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -static void draw_editor_button_m(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int EDITOR::DoButton_ButtonM(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), 0, 3.0f); - ui_do_label(r, text, 10, 0, -1); + RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), 0, 3.0f); + UI()->DoLabel(pRect, pText, 10, 0, -1); + return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -static void draw_editor_button_r(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int EDITOR::DoButton_ButtonR(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), CORNER_R, 3.0f); - ui_do_label(r, text, 10, 0, -1); + RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_R, 3.0f); + UI()->DoLabel(pRect, pText, 10, 0, -1); + return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -static void draw_inc_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int EDITOR::DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - if(ui_hot_item == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), CORNER_R, 3.0f); - ui_do_label(r, text?text:">", 10, 0, -1); + RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_R, 3.0f); + UI()->DoLabel(pRect, pText?pText:">", 10, 0, -1); + return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -static void draw_dec_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +int EDITOR::DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - if(ui_hot_item == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), CORNER_L, 3.0f); - ui_do_label(r, text?text:"<", 10, 0, -1); + RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_L, 3.0f); + UI()->DoLabel(pRect, pText?pText:"<", 10, 0, -1); + return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -enum +/* +static void draw_editor_button_l(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) { - BUTTON_CONTEXT=1, -}; + RenderTools()->DrawUIRect(r, get_button_color(id, checked), CUI::CORNER_L, 3.0f); + UI()->DoLabel(r, text, 10, 0, -1); +} + +static void draw_editor_button_m(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) +{ + if(UI()->HotItem() == id) if(extra) editor.tooltip = (const char *)extra; + RenderTools()->DrawUIRect(r, get_button_color(id, checked), 0, 3.0f); + UI()->DoLabel(r, text, 10, 0, -1); +} + +static void draw_editor_button_r(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) +{ + if(UI()->HotItem() == id) if(extra) editor.tooltip = (const char *)extra; + RenderTools()->DrawUIRect(r, get_button_color(id, checked), CUI::CORNER_R, 3.0f); + UI()->DoLabel(r, text, 10, 0, -1); +} + +static void draw_inc_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) +{ + if(UI()->HotItem == id) if(extra) editor.tooltip = (const char *)extra; + RenderTools()->DrawUIRect(r, get_button_color(id, checked), CUI::CORNER_R, 3.0f); + UI()->DoLabel(r, text?text:">", 10, 0, -1); +} -int do_editor_button(const void *id, const char *text, int checked, const RECT *r, ui_draw_button_func draw_func, int flags, const char *tooltip) +static void draw_dec_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) { - if(ui_mouse_inside(r)) + if(UI()->HotItem == id) if(extra) editor.tooltip = (const char *)extra; + RenderTools()->DrawUIRect(r, get_button_color(id, checked), CUI::CORNER_L, 3.0f); + UI()->DoLabel(r, text?text:"<", 10, 0, -1); +} + +int do_editor_button(const void *id, const char *text, int checked, const CUIRect *r, ui_draw_button_func draw_func, int flags, const char *tooltip) +{ + if(UI()->MouseInside(r)) { if(flags&BUTTON_CONTEXT) ui_got_context = id; @@ -451,46 +598,46 @@ int do_editor_button(const void *id, const char *text, int checked, const RECT * editor.tooltip = tooltip; } - return ui_do_button(id, text, checked, r, draw_func, 0); -} + return UI()->DoButton(id, text, checked, r, draw_func, 0); +}*/ -static void render_background(RECT view, int texture, float size, float brightness) +void EDITOR::render_background(CUIRect view, int texture, float size, float brightness) { - gfx_texture_set(texture); - gfx_blend_normal(); - gfx_quads_begin(); - gfx_setcolor(brightness,brightness,brightness,1.0f); - gfx_quads_setsubset(0,0, view.w/size, view.h/size); - gfx_quads_drawTL(view.x, view.y, view.w, view.h); - gfx_quads_end(); + Graphics()->TextureSet(texture); + Graphics()->BlendNormal(); + Graphics()->QuadsBegin(); + Graphics()->SetColor(brightness,brightness,brightness,1.0f); + Graphics()->QuadsSetSubset(0,0, view.w/size, view.h/size); + Graphics()->QuadsDrawTL(view.x, view.y, view.w, view.h); + Graphics()->QuadsEnd(); } static LAYERGROUP brush; static LAYER_TILES tileset_picker(16, 16); -static int ui_do_value_selector(void *id, RECT *r, const char *label, int current, int min, int max, float scale) +int EDITOR::ui_do_value_selector(void *id, CUIRect *r, const char *label, int current, int min, int max, float scale) { /* logic */ static float value; int ret = 0; - int inside = ui_mouse_inside(r); + int inside = UI()->MouseInside(r); - if(ui_active_item() == id) + if(UI()->ActiveItem() == id) { - if(!ui_mouse_button(0)) + if(!UI()->MouseButton(0)) { if(inside) ret = 1; - editor.lock_mouse = false; - ui_set_active_item(0); + lock_mouse = false; + UI()->SetActiveItem(0); } else { if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - value += editor.mouse_delta_x*0.05f; + value += mouse_delta_x*0.05f; else - value += editor.mouse_delta_x; + value += mouse_delta_x; if(fabs(value) > scale) { @@ -504,31 +651,31 @@ static int ui_do_value_selector(void *id, RECT *r, const char *label, int curren } } } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == id) { - if(ui_mouse_button(0)) + if(UI()->MouseButton(0)) { - editor.lock_mouse = true; + lock_mouse = true; value = 0; - ui_set_active_item(id); + UI()->SetActiveItem(id); } } if(inside) - ui_set_hot_item(id); + UI()->SetHotItem(id); // render char buf[128]; sprintf(buf, "%s %d", label, current); - ui_draw_rect(r, get_button_color(id, 0), CORNER_ALL, 5.0f); - ui_do_label(r, buf, 10, 0, -1); + RenderTools()->DrawUIRect(r, get_button_color(id, 0), CUI::CORNER_ALL, 5.0f); + UI()->DoLabel(r, buf, 10, 0, -1); return current; } LAYERGROUP *EDITOR::get_selected_group() { - if(selected_group >= 0 && selected_group < editor.map.groups.len()) - return editor.map.groups[selected_group]; + if(selected_group >= 0 && selected_group < map.groups.len()) + return map.groups[selected_group]; return 0x0; } @@ -538,7 +685,7 @@ LAYER *EDITOR::get_selected_layer(int index) if(!group) return 0x0; - if(selected_layer >= 0 && selected_layer < editor.map.groups[selected_group]->layers.len()) + if(selected_layer >= 0 && selected_layer < map.groups[selected_group]->layers.len()) return group->layers[selected_layer]; return 0x0; } @@ -561,108 +708,108 @@ QUAD *EDITOR::get_selected_quad() return 0; } -static void callback_open_map(const char *filename) { editor.load(filename); } -static void callback_append_map(const char *filename) { editor.append(filename); } -static void callback_save_map(const char *filename) { editor.save(filename); } +static void callback_open_map(const char *filename, void *user) { ((EDITOR*)user)->load(filename); } +static void callback_append_map(const char *filename, void *user) { ((EDITOR*)user)->append(filename); } +static void callback_save_map(const char *filename, void *user) { ((EDITOR*)user)->save(filename); } -static void do_toolbar(RECT toolbar) +void EDITOR::do_toolbar(CUIRect toolbar) { - RECT button; + CUIRect button; // ctrl+o to open if(inp_key_down('o') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL))) - editor.invoke_file_dialog(LISTDIRTYPE_ALL, "Open Map", "Open", "maps/", "", callback_open_map); + invoke_file_dialog(LISTDIRTYPE_ALL, "Open Map", "Open", "maps/", "", callback_open_map, this); // ctrl+s to save if(inp_key_down('s') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL))) - editor.invoke_file_dialog(LISTDIRTYPE_SAVE, "Save Map", "Save", "maps/", "", callback_save_map); + invoke_file_dialog(LISTDIRTYPE_SAVE, "Save Map", "Save", "maps/", "", callback_save_map, this); // detail button - ui_vsplit_l(&toolbar, 30.0f, &button, &toolbar); + toolbar.VSplitLeft(30.0f, &button, &toolbar); static int hq_button = 0; - if(do_editor_button(&hq_button, "Detail", editor.show_detail, &button, draw_editor_button, 0, "[ctrl+h] Toggle High Detail") || + if(DoButton_Editor(&hq_button, "Detail", show_detail, &button, 0, "[ctrl+h] Toggle High Detail") || (inp_key_down('h') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))) { - editor.show_detail = !editor.show_detail; + show_detail = !show_detail; } - ui_vsplit_l(&toolbar, 5.0f, 0, &toolbar); + toolbar.VSplitLeft(5.0f, 0, &toolbar); // animation button - ui_vsplit_l(&toolbar, 30.0f, &button, &toolbar); + toolbar.VSplitLeft(30.0f, &button, &toolbar); static int animate_button = 0; - if(do_editor_button(&animate_button, "Anim", editor.animate, &button, draw_editor_button, 0, "[ctrl+m] Toggle animation") || + if(DoButton_Editor(&animate_button, "Anim", animate, &button, 0, "[ctrl+m] Toggle animation") || (inp_key_down('m') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))) { - editor.animate_start = time_get(); - editor.animate = !editor.animate; + animate_start = time_get(); + animate = !animate; } - ui_vsplit_l(&toolbar, 5.0f, 0, &toolbar); + toolbar.VSplitLeft(5.0f, 0, &toolbar); // proof button - ui_vsplit_l(&toolbar, 30.0f, &button, &toolbar); + toolbar.VSplitLeft(30.0f, &button, &toolbar); static int proof_button = 0; - if(do_editor_button(&proof_button, "Proof", editor.proof_borders, &button, draw_editor_button, 0, "[ctrl-p] Toggles proof borders. These borders represent what a player maximum can see.") || + if(DoButton_Editor(&proof_button, "Proof", proof_borders, &button, 0, "[ctrl-p] Toggles proof borders. These borders represent what a player maximum can see.") || (inp_key_down('p') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))) { - editor.proof_borders = !editor.proof_borders; + proof_borders = !proof_borders; } - ui_vsplit_l(&toolbar, 15.0f, 0, &toolbar); + toolbar.VSplitLeft(15.0f, 0, &toolbar); // zoom group - ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); + toolbar.VSplitLeft(16.0f, &button, &toolbar); static int zoom_out_button = 0; - if(do_editor_button(&zoom_out_button, "ZO", 0, &button, draw_editor_button_l, 0, "[NumPad-] Zoom out") || inp_key_down(KEY_KP_MINUS)) - editor.zoom_level += 50; + if(DoButton_ButtonL(&zoom_out_button, "ZO", 0, &button, 0, "[NumPad-] Zoom out") || inp_key_down(KEY_KP_MINUS)) + zoom_level += 50; - ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); + toolbar.VSplitLeft(16.0f, &button, &toolbar); static int zoom_normal_button = 0; - if(do_editor_button(&zoom_normal_button, "1:1", 0, &button, draw_editor_button_m, 0, "[NumPad*] Zoom to normal and remove editor offset") || inp_key_down(KEY_KP_MULTIPLY)) + if(DoButton_ButtonM(&zoom_normal_button, "1:1", 0, &button, 0, "[NumPad*] Zoom to normal and remove editor offset") || inp_key_down(KEY_KP_MULTIPLY)) { - editor.editor_offset_x = 0; - editor.editor_offset_y = 0; - editor.zoom_level = 100; + editor_offset_x = 0; + editor_offset_y = 0; + zoom_level = 100; } - ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); + toolbar.VSplitLeft(16.0f, &button, &toolbar); static int zoom_in_button = 0; - if(do_editor_button(&zoom_in_button, "ZI", 0, &button, draw_editor_button_r, 0, "[NumPad+] Zoom in") || inp_key_down(KEY_KP_PLUS)) - editor.zoom_level -= 50; + if(DoButton_ButtonR(&zoom_in_button, "ZI", 0, &button, 0, "[NumPad+] Zoom in") || inp_key_down(KEY_KP_PLUS)) + zoom_level -= 50; - ui_vsplit_l(&toolbar, 15.0f, 0, &toolbar); + toolbar.VSplitLeft(15.0f, 0, &toolbar); // animation speed - ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); + toolbar.VSplitLeft(16.0f, &button, &toolbar); static int anim_faster_button = 0; - if(do_editor_button(&anim_faster_button, "A+", 0, &button, draw_editor_button_l, 0, "Increase animation speed")) - editor.animate_speed += 0.5f; + if(DoButton_ButtonL(&anim_faster_button, "A+", 0, &button, 0, "Increase animation speed")) + animate_speed += 0.5f; - ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); + toolbar.VSplitLeft(16.0f, &button, &toolbar); static int anim_normal_button = 0; - if(do_editor_button(&anim_normal_button, "1", 0, &button, draw_editor_button_m, 0, "Normal animation speed")) - editor.animate_speed = 1.0f; + if(DoButton_ButtonM(&anim_normal_button, "1", 0, &button, 0, "Normal animation speed")) + animate_speed = 1.0f; - ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); + toolbar.VSplitLeft(16.0f, &button, &toolbar); static int anim_slower_button = 0; - if(do_editor_button(&anim_slower_button, "A-", 0, &button, draw_editor_button_r, 0, "Decrease animation speed")) + if(DoButton_ButtonR(&anim_slower_button, "A-", 0, &button, 0, "Decrease animation speed")) { - if(editor.animate_speed > 0.5f) - editor.animate_speed -= 0.5f; + if(animate_speed > 0.5f) + animate_speed -= 0.5f; } - if(inp_key_presses(KEY_MOUSE_WHEEL_UP) && editor.dialog == DIALOG_NONE) - editor.zoom_level -= 20; + if(inp_key_presses(KEY_MOUSE_WHEEL_UP) && dialog == DIALOG_NONE) + zoom_level -= 20; - if(inp_key_presses(KEY_MOUSE_WHEEL_DOWN) && editor.dialog == DIALOG_NONE) - editor.zoom_level += 20; + if(inp_key_presses(KEY_MOUSE_WHEEL_DOWN) && dialog == DIALOG_NONE) + zoom_level += 20; - if(editor.zoom_level < 50) - editor.zoom_level = 50; - editor.world_zoom = editor.zoom_level/100.0f; + if(zoom_level < 50) + zoom_level = 50; + world_zoom = zoom_level/100.0f; - ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar); + toolbar.VSplitLeft(10.0f, &button, &toolbar); // brush manipulation @@ -670,41 +817,41 @@ static void do_toolbar(RECT toolbar) int enabled = brush.is_empty()?-1:0; // flip buttons - ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar); + toolbar.VSplitLeft(20.0f, &button, &toolbar); static int flipx_button = 0; - if(do_editor_button(&flipx_button, "^X", enabled, &button, draw_editor_button_l, 0, "[N] Flip brush horizontal") || inp_key_down('n')) + if(DoButton_ButtonL(&flipx_button, "^X", enabled, &button, 0, "[N] Flip brush horizontal") || inp_key_down('n')) { for(int i = 0; i < brush.layers.len(); i++) brush.layers[i]->brush_flip_x(); } - ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar); + toolbar.VSplitLeft(20.0f, &button, &toolbar); static int flipy_button = 0; - if(do_editor_button(&flipy_button, "^Y", enabled, &button, draw_editor_button_r, 0, "[M] Flip brush vertical") || inp_key_down('m')) + if(DoButton_ButtonR(&flipy_button, "^Y", enabled, &button, 0, "[M] Flip brush vertical") || inp_key_down('m')) { for(int i = 0; i < brush.layers.len(); i++) brush.layers[i]->brush_flip_y(); } // rotate buttons - ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar); + toolbar.VSplitLeft(20.0f, &button, &toolbar); - ui_vsplit_l(&toolbar, 30.0f, &button, &toolbar); + toolbar.VSplitLeft(30.0f, &button, &toolbar); static int rotation_amount = 90; rotation_amount = ui_do_value_selector(&rotation_amount, &button, "", rotation_amount, 1, 360, 2.0f); - ui_vsplit_l(&toolbar, 5.0f, &button, &toolbar); - ui_vsplit_l(&toolbar, 30.0f, &button, &toolbar); + toolbar.VSplitLeft(5.0f, &button, &toolbar); + toolbar.VSplitLeft(30.0f, &button, &toolbar); static int ccw_button = 0; - if(do_editor_button(&ccw_button, "CCW", enabled, &button, draw_editor_button_l, 0, "[R] Rotates the brush counter clockwise") || inp_key_down('r')) + if(DoButton_ButtonL(&ccw_button, "CCW", enabled, &button, 0, "[R] Rotates the brush counter clockwise") || inp_key_down('r')) { for(int i = 0; i < brush.layers.len(); i++) brush.layers[i]->brush_rotate(-rotation_amount/360.0f*pi*2); } - ui_vsplit_l(&toolbar, 30.0f, &button, &toolbar); + toolbar.VSplitLeft(30.0f, &button, &toolbar); static int cw_button = 0; - if(do_editor_button(&cw_button, "CW", enabled, &button, draw_editor_button_r, 0, "[T] Rotates the brush clockwise") || inp_key_down('t')) + if(DoButton_ButtonR(&cw_button, "CW", enabled, &button, 0, "[T] Rotates the brush clockwise") || inp_key_down('t')) { for(int i = 0; i < brush.layers.len(); i++) brush.layers[i]->brush_rotate(rotation_amount/360.0f*pi*2); @@ -714,18 +861,18 @@ static void do_toolbar(RECT toolbar) // quad manipulation { // do add button - ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar); - ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar); + toolbar.VSplitLeft(10.0f, &button, &toolbar); + toolbar.VSplitLeft(60.0f, &button, &toolbar); static int new_button = 0; - LAYER_QUADS *qlayer = (LAYER_QUADS *)editor.get_selected_layer_type(0, LAYERTYPE_QUADS); - //LAYER_TILES *tlayer = (LAYER_TILES *)editor.get_selected_layer_type(0, LAYERTYPE_TILES); - if(do_editor_button(&new_button, "Add Quad", qlayer?0:-1, &button, draw_editor_button, 0, "Adds a new quad")) + LAYER_QUADS *qlayer = (LAYER_QUADS *)get_selected_layer_type(0, LAYERTYPE_QUADS); + //LAYER_TILES *tlayer = (LAYER_TILES *)get_selected_layer_type(0, LAYERTYPE_TILES); + if(DoButton_Editor(&new_button, "Add Quad", qlayer?0:-1, &button, 0, "Adds a new quad")) { if(qlayer) { float mapping[4]; - LAYERGROUP *g = editor.get_selected_group(); + LAYERGROUP *g = get_selected_group(); g->mapping(mapping); int add_x = f2fx(mapping[0] + (mapping[2]-mapping[0])/2); int add_y = f2fx(mapping[1] + (mapping[3]-mapping[1])/2); @@ -749,7 +896,7 @@ static void rotate(POINT *center, POINT *point, float rotation) point->y = (int)(x * sinf(rotation) + y * cosf(rotation) + center->y); } -static void do_quad(QUAD *q, int index) +void EDITOR::do_quad(QUAD *q, int index) { enum { @@ -767,8 +914,8 @@ static void do_quad(QUAD *q, int index) static float last_wy; static int operation = OP_NONE; static float rotate_angle = 0; - float wx = ui_mouse_world_x(); - float wy = ui_mouse_world_y(); + float wx = UI()->MouseWorldX(); + float wy = UI()->MouseWorldY(); // get pivot float center_x = fx2f(q->points[4].x); @@ -777,16 +924,16 @@ static void do_quad(QUAD *q, int index) float dx = (center_x - wx); float dy = (center_y - wy); if(dx*dx+dy*dy < 10*10) - ui_set_hot_item(id); + UI()->SetHotItem(id); // draw selection background - if(editor.selected_quad == index) + if(selected_quad == index) { - gfx_setcolor(0,0,0,1); - gfx_quads_draw(center_x, center_y, 7.0f, 7.0f); + Graphics()->SetColor(0,0,0,1); + Graphics()->QuadsDraw(center_x, center_y, 7.0f, 7.0f); } - if(ui_active_item() == id) + if(UI()->ActiveItem() == id) { // check if we only should move pivot if(operation == OP_MOVE_PIVOT) @@ -812,47 +959,47 @@ static void do_quad(QUAD *q, int index) } } - rotate_angle += (editor.mouse_delta_x) * 0.002f; + rotate_angle += (mouse_delta_x) * 0.002f; last_wx = wx; last_wy = wy; if(operation == OP_CONTEXT_MENU) { - if(!ui_mouse_button(1)) + if(!UI()->MouseButton(1)) { static int quad_popup_id = 0; - ui_invoke_popup_menu(&quad_popup_id, 0, ui_mouse_x(), ui_mouse_y(), 120, 150, popup_quad); - editor.lock_mouse = false; + ui_invoke_popup_menu(&quad_popup_id, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, popup_quad); + lock_mouse = false; operation = OP_NONE; - ui_set_active_item(0); + UI()->SetActiveItem(0); } } else { - if(!ui_mouse_button(0)) + if(!UI()->MouseButton(0)) { - editor.lock_mouse = false; + lock_mouse = false; operation = OP_NONE; - ui_set_active_item(0); + UI()->SetActiveItem(0); } } - gfx_setcolor(1,1,1,1); + Graphics()->SetColor(1,1,1,1); } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == id) { ui_got_context = id; - gfx_setcolor(1,1,1,1); - editor.tooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate"; + Graphics()->SetColor(1,1,1,1); + tooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate"; - if(ui_mouse_button(0)) + if(UI()->MouseButton(0)) { if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) operation = OP_MOVE_PIVOT; else if(inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)) { - editor.lock_mouse = true; + lock_mouse = true; operation = OP_ROTATE; rotate_angle = 0; rotate_points[0] = q->points[0]; @@ -863,31 +1010,31 @@ static void do_quad(QUAD *q, int index) else operation = OP_MOVE_ALL; - ui_set_active_item(id); - editor.selected_quad = index; + UI()->SetActiveItem(id); + selected_quad = index; last_wx = wx; last_wy = wy; } - if(ui_mouse_button(1)) + if(UI()->MouseButton(1)) { - editor.selected_quad = index; + selected_quad = index; operation = OP_CONTEXT_MENU; - ui_set_active_item(id); + UI()->SetActiveItem(id); } } else - gfx_setcolor(0,1,0,1); + Graphics()->SetColor(0,1,0,1); - gfx_quads_draw(center_x, center_y, 5.0f, 5.0f); + Graphics()->QuadsDraw(center_x, center_y, 5.0f, 5.0f); } -static void do_quad_point(QUAD *q, int quad_index, int v) +void EDITOR::do_quad_point(QUAD *q, int quad_index, int v) { void *id = &q->points[v]; - float wx = ui_mouse_world_x(); - float wy = ui_mouse_world_y(); + float wx = UI()->MouseWorldX(); + float wy = UI()->MouseWorldY(); float px = fx2f(q->points[v].x); float py = fx2f(q->points[v].y); @@ -895,13 +1042,13 @@ static void do_quad_point(QUAD *q, int quad_index, int v) float dx = (px - wx); float dy = (py - wy); if(dx*dx+dy*dy < 10*10) - ui_set_hot_item(id); + UI()->SetHotItem(id); // draw selection background - if(editor.selected_quad == quad_index && editor.selected_points&(1<SetColor(0,0,0,1); + Graphics()->QuadsDraw(px, py, 7.0f, 7.0f); } enum @@ -915,10 +1062,10 @@ static void do_quad_point(QUAD *q, int quad_index, int v) static bool moved; static int operation = OP_NONE; - if(ui_active_item() == id) + if(UI()->ActiveItem() == id) { - float dx = editor.mouse_delta_wx; - float dy = editor.mouse_delta_wy; + float dx = mouse_delta_wx; + float dy = mouse_delta_wy; if(!moved) { if(dx*dx+dy*dy > 0.5f) @@ -930,7 +1077,7 @@ static void do_quad_point(QUAD *q, int quad_index, int v) if(operation == OP_MOVEPOINT) { for(int m = 0; m < 4; m++) - if(editor.selected_points&(1<points[m].x += f2fx(dx); q->points[m].y += f2fx(dy); @@ -939,7 +1086,7 @@ static void do_quad_point(QUAD *q, int quad_index, int v) else if(operation == OP_MOVEUV) { for(int m = 0; m < 4; m++) - if(editor.selected_points&(1<texcoords[m].x += f2fx(dx*0.001f); q->texcoords[m].y += f2fx(dy*0.001f); @@ -949,106 +1096,106 @@ static void do_quad_point(QUAD *q, int quad_index, int v) if(operation == OP_CONTEXT_MENU) { - if(!ui_mouse_button(1)) + if(!UI()->MouseButton(1)) { static int point_popup_id = 0; - ui_invoke_popup_menu(&point_popup_id, 0, ui_mouse_x(), ui_mouse_y(), 120, 150, popup_point); - ui_set_active_item(0); + ui_invoke_popup_menu(&point_popup_id, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, popup_point); + UI()->SetActiveItem(0); } } else { - if(!ui_mouse_button(0)) + if(!UI()->MouseButton(0)) { if(!moved) { if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - editor.selected_points ^= 1<SetActiveItem(0); } } - gfx_setcolor(1,1,1,1); + Graphics()->SetColor(1,1,1,1); } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == id) { ui_got_context = id; - gfx_setcolor(1,1,1,1); - editor.tooltip = "Left mouse button to move. Hold shift to move the texture."; + Graphics()->SetColor(1,1,1,1); + tooltip = "Left mouse button to move. Hold shift to move the texture."; - if(ui_mouse_button(0)) + if(UI()->MouseButton(0)) { - ui_set_active_item(id); + UI()->SetActiveItem(id); moved = false; if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) { operation = OP_MOVEUV; - editor.lock_mouse = true; + lock_mouse = true; } else operation = OP_MOVEPOINT; - if(!(editor.selected_points&(1<MouseButton(1)) { operation = OP_CONTEXT_MENU; - editor.selected_quad = quad_index; - ui_set_active_item(id); + selected_quad = quad_index; + UI()->SetActiveItem(id); } } else - gfx_setcolor(1,0,0,1); + Graphics()->SetColor(1,0,0,1); - gfx_quads_draw(px, py, 5.0f, 5.0f); + Graphics()->QuadsDraw(px, py, 5.0f, 5.0f); } -static void do_map_editor(RECT view, RECT toolbar) +void EDITOR::do_map_editor(CUIRect view, CUIRect toolbar) { - //ui_clip_enable(&view); + //UI()->ClipEnable(&view); - bool show_picker = inp_key_pressed(KEY_SPACE) != 0 && editor.dialog == DIALOG_NONE; + bool show_picker = inp_key_pressed(KEY_SPACE) != 0 && dialog == DIALOG_NONE; // render all good stuff if(!show_picker) { - for(int g = 0; g < editor.map.groups.len(); g++) + for(int g = 0; g < map.groups.len(); g++) { - if(editor.map.groups[g]->visible) - editor.map.groups[g]->render(); - //ui_clip_enable(&view); + if(map.groups[g]->visible) + map.groups[g]->render(); + //UI()->ClipEnable(&view); } // render the game above everything else - if(editor.map.game_group->visible && editor.map.game_layer->visible) + if(map.game_group->visible && map.game_layer->visible) { - editor.map.game_group->mapscreen(); - editor.map.game_layer->render(); + map.game_group->mapscreen(); + map.game_layer->render(); } } static void *editor_id = (void *)&editor_id; - int inside = ui_mouse_inside(&view); + int inside = UI()->MouseInside(&view); // fetch mouse position - float wx = ui_mouse_world_x(); - float wy = ui_mouse_world_y(); - float mx = ui_mouse_x(); - float my = ui_mouse_y(); + float wx = UI()->MouseWorldX(); + float wy = UI()->MouseWorldY(); + float mx = UI()->MouseX(); + float my = UI()->MouseY(); static float start_wx = 0; static float start_wy = 0; @@ -1067,7 +1214,7 @@ static void do_map_editor(RECT view, RECT toolbar) // remap the screen so it can display the whole tileset if(show_picker) { - RECT screen = *ui_screen(); + CUIRect screen = *UI()->Screen(); float size = 32.0*16.0f; float w = size*(screen.w/view.w); float h = size*(screen.h/view.h); @@ -1075,8 +1222,8 @@ static void do_map_editor(RECT view, RECT toolbar) float y = -(view.y/screen.h)*h; wx = x+w*mx/screen.w; wy = y+h*my/screen.h; - gfx_mapscreen(x, y, x+w, y+h); - LAYER_TILES *t = (LAYER_TILES *)editor.get_selected_layer_type(0, LAYERTYPE_TILES); + Graphics()->MapScreen(x, y, x+w, y+h); + LAYER_TILES *t = (LAYER_TILES *)get_selected_layer_type(0, LAYERTYPE_TILES); if(t) { tileset_picker.image = t->image; @@ -1099,11 +1246,11 @@ static void do_map_editor(RECT view, RECT toolbar) } else { - edit_layers[0] = editor.get_selected_layer(0); + edit_layers[0] = get_selected_layer(0); if(edit_layers[0]) num_edit_layers++; - LAYERGROUP *g = editor.get_selected_group(); + LAYERGROUP *g = get_selected_group(); if(g) { g->mapscreen(); @@ -1116,50 +1263,50 @@ static void do_map_editor(RECT view, RECT toolbar) float w, h; edit_layers[i]->get_size(&w, &h); - gfx_texture_set(-1); - gfx_lines_begin(); - gfx_lines_draw(0,0, w,0); - gfx_lines_draw(w,0, w,h); - gfx_lines_draw(w,h, 0,h); - gfx_lines_draw(0,h, 0,0); - gfx_lines_end(); + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); + Graphics()->LinesDraw(0,0, w,0); + Graphics()->LinesDraw(w,0, w,h); + Graphics()->LinesDraw(w,h, 0,h); + Graphics()->LinesDraw(0,h, 0,0); + Graphics()->LinesEnd(); } } } if(inside) { - ui_set_hot_item(editor_id); + UI()->SetHotItem(editor_id); // do global operations like pan and zoom - if(ui_active_item() == 0 && (ui_mouse_button(0) || ui_mouse_button(2))) + if(UI()->ActiveItem() == 0 && (UI()->MouseButton(0) || UI()->MouseButton(2))) { start_wx = wx; start_wy = wy; start_mx = mx; start_my = my; - if(inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL) || ui_mouse_button(2)) + if(inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL) || UI()->MouseButton(2)) { if(inp_key_pressed(KEY_LSHIFT)) operation = OP_PAN_EDITOR; else operation = OP_PAN_WORLD; - ui_set_active_item(editor_id); + UI()->SetActiveItem(editor_id); } } // brush editing - if(ui_hot_item() == editor_id) + if(UI()->HotItem() == editor_id) { if(brush.is_empty()) - editor.tooltip = "Use left mouse button to drag and create a brush."; + tooltip = "Use left mouse button to drag and create a brush."; else - editor.tooltip = "Use left mouse button to paint with the brush. Right button clears the brush."; + tooltip = "Use left mouse button to paint with the brush. Right button clears the brush."; - if(ui_active_item() == editor_id) + if(UI()->ActiveItem() == editor_id) { - RECT r; + CUIRect r; r.x = start_wx; r.y = start_wy; r.w = wx-start_wx; @@ -1190,7 +1337,7 @@ static void do_map_editor(RECT view, RECT toolbar) } else if(operation == OP_BRUSH_GRAB) { - if(!ui_mouse_button(0)) + if(!UI()->MouseButton(0)) { // grab brush dbg_msg("editor", "grabbing %f %f %f %f", r.x, r.y, r.w, r.h); @@ -1207,18 +1354,18 @@ static void do_map_editor(RECT view, RECT toolbar) //editor.map.groups[selected_group]->mapscreen(); for(int k = 0; k < num_edit_layers; k++) edit_layers[k]->brush_selecting(r); - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); } } } else { - if(ui_mouse_button(1)) + if(UI()->MouseButton(1)) brush.clear(); - if(ui_mouse_button(0) && operation == OP_NONE) + if(UI()->MouseButton(0) && operation == OP_NONE) { - ui_set_active_item(editor_id); + UI()->SetActiveItem(editor_id); if(brush.is_empty()) operation = OP_BRUSH_GRAB; @@ -1248,7 +1395,7 @@ static void do_map_editor(RECT view, RECT toolbar) } } - LAYERGROUP *g = editor.get_selected_group(); + LAYERGROUP *g = get_selected_group(); brush.offset_x += g->offset_x; brush.offset_y += g->offset_y; brush.parallax_x = g->parallax_x; @@ -1257,13 +1404,13 @@ static void do_map_editor(RECT view, RECT toolbar) float w, h; brush.get_size(&w, &h); - gfx_texture_set(-1); - gfx_lines_begin(); - gfx_lines_draw(0,0, w,0); - gfx_lines_draw(w,0, w,h); - gfx_lines_draw(w,h, 0,h); - gfx_lines_draw(0,h, 0,0); - gfx_lines_end(); + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); + Graphics()->LinesDraw(0,0, w,0); + Graphics()->LinesDraw(w,0, w,h); + Graphics()->LinesDraw(w,h, 0,h); + Graphics()->LinesDraw(0,h, 0,0); + Graphics()->LinesEnd(); } } @@ -1274,7 +1421,7 @@ static void do_map_editor(RECT view, RECT toolbar) if(!show_picker && brush.is_empty()) { // fetch layers - LAYERGROUP *g = editor.get_selected_group(); + LAYERGROUP *g = get_selected_group(); if(g) g->mapscreen(); @@ -1284,8 +1431,8 @@ static void do_map_editor(RECT view, RECT toolbar) { LAYER_QUADS *layer = (LAYER_QUADS *)edit_layers[k]; - gfx_texture_set(-1); - gfx_quads_begin(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); for(int i = 0; i < layer->quads.len(); i++) { for(int v = 0; v < 4; v++) @@ -1293,68 +1440,68 @@ static void do_map_editor(RECT view, RECT toolbar) do_quad(&layer->quads[i], i); } - gfx_quads_end(); + Graphics()->QuadsEnd(); } } - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); } // do panning - if(ui_active_item() == editor_id) + if(UI()->ActiveItem() == editor_id) { if(operation == OP_PAN_WORLD) { - editor.world_offset_x -= editor.mouse_delta_x*editor.world_zoom; - editor.world_offset_y -= editor.mouse_delta_y*editor.world_zoom; + world_offset_x -= mouse_delta_x*world_zoom; + world_offset_y -= mouse_delta_y*world_zoom; } else if(operation == OP_PAN_EDITOR) { - editor.editor_offset_x -= editor.mouse_delta_x*editor.world_zoom; - editor.editor_offset_y -= editor.mouse_delta_y*editor.world_zoom; + editor_offset_x -= mouse_delta_x*world_zoom; + editor_offset_y -= mouse_delta_y*world_zoom; } // release mouse - if(!ui_mouse_button(0)) + if(!UI()->MouseButton(0)) { operation = OP_NONE; - ui_set_active_item(0); + UI()->SetActiveItem(0); } } } } - if(editor.get_selected_group() && editor.get_selected_group()->use_clipping) + if(get_selected_group() && get_selected_group()->use_clipping) { - LAYERGROUP *g = editor.map.game_group; + LAYERGROUP *g = map.game_group; g->mapscreen(); - gfx_texture_set(-1); - gfx_lines_begin(); - - RECT r; - r.x = editor.get_selected_group()->clip_x; - r.y = editor.get_selected_group()->clip_y; - r.w = editor.get_selected_group()->clip_w; - r.h = editor.get_selected_group()->clip_h; + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); + + CUIRect r; + r.x = get_selected_group()->clip_x; + r.y = get_selected_group()->clip_y; + r.w = get_selected_group()->clip_w; + r.h = get_selected_group()->clip_h; - gfx_setcolor(1,0,0,1); - gfx_lines_draw(r.x, r.y, r.x+r.w, r.y); - gfx_lines_draw(r.x+r.w, r.y, r.x+r.w, r.y+r.h); - gfx_lines_draw(r.x+r.w, r.y+r.h, r.x, r.y+r.h); - gfx_lines_draw(r.x, r.y+r.h, r.x, r.y); + Graphics()->SetColor(1,0,0,1); + Graphics()->LinesDraw(r.x, r.y, r.x+r.w, r.y); + Graphics()->LinesDraw(r.x+r.w, r.y, r.x+r.w, r.y+r.h); + Graphics()->LinesDraw(r.x+r.w, r.y+r.h, r.x, r.y+r.h); + Graphics()->LinesDraw(r.x, r.y+r.h, r.x, r.y); - gfx_lines_end(); + Graphics()->LinesEnd(); } // render screen sizes - if(editor.proof_borders) + if(proof_borders) { - LAYERGROUP *g = editor.map.game_group; + LAYERGROUP *g = map.game_group; g->mapscreen(); - gfx_texture_set(-1); - gfx_lines_begin(); + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); float last_points[4]; float start = 1.0f; //9.0f/16.0f; @@ -1365,28 +1512,28 @@ static void do_map_editor(RECT view, RECT toolbar) float points[4]; float aspect = start + (end-start)*(i/(float)num_steps); - mapscreen_to_world( - editor.world_offset_x, editor.world_offset_y, + RenderTools()->mapscreen_to_world( + world_offset_x, world_offset_y, 1.0f, 1.0f, 0.0f, 0.0f, aspect, 1.0f, points); if(i == 0) { - gfx_lines_draw(points[0], points[1], points[2], points[1]); - gfx_lines_draw(points[0], points[3], points[2], points[3]); + Graphics()->LinesDraw(points[0], points[1], points[2], points[1]); + Graphics()->LinesDraw(points[0], points[3], points[2], points[3]); } if(i != 0) { - gfx_lines_draw(points[0], points[1], last_points[0], last_points[1]); - gfx_lines_draw(points[2], points[1], last_points[2], last_points[1]); - gfx_lines_draw(points[0], points[3], last_points[0], last_points[3]); - gfx_lines_draw(points[2], points[3], last_points[2], last_points[3]); + Graphics()->LinesDraw(points[0], points[1], last_points[0], last_points[1]); + Graphics()->LinesDraw(points[2], points[1], last_points[2], last_points[1]); + Graphics()->LinesDraw(points[0], points[3], last_points[0], last_points[3]); + Graphics()->LinesDraw(points[2], points[3], last_points[2], last_points[3]); } if(i == num_steps) { - gfx_lines_draw(points[0], points[1], points[0], points[3]); - gfx_lines_draw(points[2], points[1], points[2], points[3]); + Graphics()->LinesDraw(points[0], points[1], points[0], points[3]); + Graphics()->LinesDraw(points[2], points[1], points[2], points[3]); } mem_copy(last_points, points, sizeof(points)); @@ -1394,64 +1541,64 @@ static void do_map_editor(RECT view, RECT toolbar) if(1) { - gfx_setcolor(1,0,0,1); + Graphics()->SetColor(1,0,0,1); for(int i = 0; i < 2; i++) { float points[4]; float aspects[] = {4.0f/3.0f, 16.0f/10.0f, 5.0f/4.0f, 16.0f/9.0f}; float aspect = aspects[i]; - mapscreen_to_world( - editor.world_offset_x, editor.world_offset_y, + RenderTools()->mapscreen_to_world( + world_offset_x, world_offset_y, 1.0f, 1.0f, 0.0f, 0.0f, aspect, 1.0f, points); - RECT r; + CUIRect r; r.x = points[0]; r.y = points[1]; r.w = points[2]-points[0]; r.h = points[3]-points[1]; - gfx_lines_draw(r.x, r.y, r.x+r.w, r.y); - gfx_lines_draw(r.x+r.w, r.y, r.x+r.w, r.y+r.h); - gfx_lines_draw(r.x+r.w, r.y+r.h, r.x, r.y+r.h); - gfx_lines_draw(r.x, r.y+r.h, r.x, r.y); - gfx_setcolor(0,1,0,1); + Graphics()->LinesDraw(r.x, r.y, r.x+r.w, r.y); + Graphics()->LinesDraw(r.x+r.w, r.y, r.x+r.w, r.y+r.h); + Graphics()->LinesDraw(r.x+r.w, r.y+r.h, r.x, r.y+r.h); + Graphics()->LinesDraw(r.x, r.y+r.h, r.x, r.y); + Graphics()->SetColor(0,1,0,1); } } - gfx_lines_end(); + Graphics()->LinesEnd(); } - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); - //ui_clip_disable(); + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); + //UI()->ClipDisable(); } -int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val) +int EDITOR::do_properties(CUIRect *toolbox, PROPERTY *props, int *ids, int *new_val) { int change = -1; for(int i = 0; props[i].name; i++) { - RECT slot; - ui_hsplit_t(toolbox, 13.0f, &slot, toolbox); - RECT label, shifter; - ui_vsplit_mid(&slot, &label, &shifter); - ui_hmargin(&shifter, 1.0f, &shifter); - ui_do_label(&label, props[i].name, 10.0f, -1, -1); + CUIRect slot; + toolbox->HSplitTop(13.0f, &slot, toolbox); + CUIRect label, shifter; + slot.VSplitMid(&label, &shifter); + shifter.HMargin(1.0f, &shifter); + UI()->DoLabel(&label, props[i].name, 10.0f, -1, -1); if(props[i].type == PROPTYPE_INT_STEP) { - RECT inc, dec; + CUIRect inc, dec; char buf[64]; - ui_vsplit_r(&shifter, 10.0f, &shifter, &inc); - ui_vsplit_l(&shifter, 10.0f, &dec, &shifter); + shifter.VSplitRight(10.0f, &shifter, &inc); + shifter.VSplitLeft(10.0f, &dec, &shifter); sprintf(buf, "%d", props[i].value); - ui_draw_rect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f); - ui_do_label(&shifter, buf, 10.0f, 0, -1); + RenderTools()->DrawUIRect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f); + UI()->DoLabel(&shifter, buf, 10.0f, 0, -1); - if(do_editor_button(&ids[i], 0, 0, &dec, draw_dec_button, 0, "Decrease")) + if(DoButton_ButtonDec(&ids[i], 0, 0, &dec, 0, "Decrease")) { if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) *new_val = props[i].value-5; @@ -1459,7 +1606,7 @@ int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val *new_val = props[i].value-1; change = i; } - if(do_editor_button(((char *)&ids[i])+1, 0, 0, &inc, draw_inc_button, 0, "Increase")) + if(DoButton_ButtonInc(((char *)&ids[i])+1, 0, 0, &inc, 0, "Increase")) { if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) *new_val = props[i].value+5; @@ -1470,14 +1617,14 @@ int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val } else if(props[i].type == PROPTYPE_BOOL) { - RECT no, yes; - ui_vsplit_mid(&shifter, &no, &yes); - if(do_editor_button(&ids[i], "No", !props[i].value, &no, draw_dec_button, 0, "")) + CUIRect no, yes; + shifter.VSplitMid(&no, &yes); + if(DoButton_ButtonDec(&ids[i], "No", !props[i].value, &no, 0, "")) { *new_val = 0; change = i; } - if(do_editor_button(((char *)&ids[i])+1, "Yes", props[i].value, &yes, draw_inc_button, 0, "")) + if(DoButton_ButtonInc(((char *)&ids[i])+1, "Yes", props[i].value, &yes, 0, "")) { *new_val = 1; change = i; @@ -1505,9 +1652,9 @@ int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val if(c != 3) { - ui_hsplit_t(toolbox, 13.0f, &slot, toolbox); - ui_vsplit_mid(&slot, 0, &shifter); - ui_hmargin(&shifter, 1.0f, &shifter); + toolbox->HSplitTop(13.0f, &slot, toolbox); + slot.VSplitMid(0, &shifter); + shifter.HMargin(1.0f, &shifter); } } @@ -1523,10 +1670,10 @@ int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val if(props[i].value < 0) strcpy(buf, "None"); else - sprintf(buf, "%s", editor.map.images[props[i].value]->name); + sprintf(buf, "%s", map.images[props[i].value]->name); - if(do_editor_button(&ids[i], buf, 0, &shifter, draw_editor_button, 0, 0)) - popup_select_image_invoke(props[i].value, ui_mouse_x(), ui_mouse_y()); + if(DoButton_Editor(&ids[i], buf, 0, &shifter, 0, 0)) + popup_select_image_invoke(props[i].value, UI()->MouseX(), UI()->MouseY()); int r = popup_select_image_result(); if(r >= -1) @@ -1540,90 +1687,90 @@ int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val return change; } -static void render_layers(RECT toolbox, RECT toolbar, RECT view) +void EDITOR::render_layers(CUIRect toolbox, CUIRect toolbar, CUIRect view) { - RECT layersbox = toolbox; + CUIRect layersbox = toolbox; - if(!editor.gui_active) + if(!gui_active) return; - RECT slot, button; + CUIRect slot, button; char buf[64]; int valid_group = 0; int valid_layer = 0; - if(editor.selected_group >= 0 && editor.selected_group < editor.map.groups.len()) + if(selected_group >= 0 && selected_group < map.groups.len()) valid_group = 1; - if(valid_group && editor.selected_layer >= 0 && editor.selected_layer < editor.map.groups[editor.selected_group]->layers.len()) + if(valid_group && selected_layer >= 0 && selected_layer < map.groups[selected_group]->layers.len()) valid_layer = 1; // render layers { - for(int g = 0; g < editor.map.groups.len(); g++) + for(int g = 0; g < map.groups.len(); g++) { - RECT visible_toggle; - ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox); - ui_vsplit_l(&slot, 12, &visible_toggle, &slot); - if(do_editor_button(&editor.map.groups[g]->visible, editor.map.groups[g]->visible?"V":"H", 0, &visible_toggle, draw_editor_button_l, 0, "Toggle group visibility")) - editor.map.groups[g]->visible = !editor.map.groups[g]->visible; - - sprintf(buf, "#%d %s", g, editor.map.groups[g]->name); - if(int result = do_editor_button(&editor.map.groups[g], buf, g==editor.selected_group, &slot, draw_editor_button_r, + CUIRect visible_toggle; + layersbox.HSplitTop(12.0f, &slot, &layersbox); + slot.VSplitLeft(12, &visible_toggle, &slot); + if(DoButton_ButtonL(&map.groups[g]->visible, map.groups[g]->visible?"V":"H", 0, &visible_toggle, 0, "Toggle group visibility")) + map.groups[g]->visible = !map.groups[g]->visible; + + sprintf(buf, "#%d %s", g, map.groups[g]->name); + if(int result = DoButton_ButtonR(&map.groups[g], buf, g==selected_group, &slot, BUTTON_CONTEXT, "Select group. Right click for properties.")) { - editor.selected_group = g; - editor.selected_layer = 0; + selected_group = g; + selected_layer = 0; static int group_popup_id = 0; if(result == 2) - ui_invoke_popup_menu(&group_popup_id, 0, ui_mouse_x(), ui_mouse_y(), 120, 200, popup_group); + ui_invoke_popup_menu(&group_popup_id, 0, UI()->MouseX(), UI()->MouseY(), 120, 200, popup_group); } - ui_hsplit_t(&layersbox, 2.0f, &slot, &layersbox); + layersbox.HSplitTop(2.0f, &slot, &layersbox); - for(int i = 0; i < editor.map.groups[g]->layers.len(); i++) + for(int i = 0; i < map.groups[g]->layers.len(); i++) { //visible - ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox); - ui_vsplit_l(&slot, 12.0f, 0, &button); - ui_vsplit_l(&button, 15, &visible_toggle, &button); + layersbox.HSplitTop(12.0f, &slot, &layersbox); + slot.VSplitLeft(12.0f, 0, &button); + button.VSplitLeft(15, &visible_toggle, &button); - if(do_editor_button(&editor.map.groups[g]->layers[i]->visible, editor.map.groups[g]->layers[i]->visible?"V":"H", 0, &visible_toggle, draw_editor_button_l, 0, "Toggle layer visibility")) - editor.map.groups[g]->layers[i]->visible = !editor.map.groups[g]->layers[i]->visible; + if(DoButton_ButtonL(&map.groups[g]->layers[i]->visible, map.groups[g]->layers[i]->visible?"V":"H", 0, &visible_toggle, 0, "Toggle layer visibility")) + map.groups[g]->layers[i]->visible = !map.groups[g]->layers[i]->visible; - sprintf(buf, "#%d %s ", i, editor.map.groups[g]->layers[i]->type_name); - if(int result = do_editor_button(editor.map.groups[g]->layers[i], buf, g==editor.selected_group&&i==editor.selected_layer, &button, draw_editor_button_r, + sprintf(buf, "#%d %s ", i, map.groups[g]->layers[i]->type_name); + if(int result = DoButton_ButtonR(map.groups[g]->layers[i], buf, g==selected_group&&i==selected_layer, &button, BUTTON_CONTEXT, "Select layer. Right click for properties.")) { - editor.selected_layer = i; - editor.selected_group = g; + selected_layer = i; + selected_group = g; static int layer_popup_id = 0; if(result == 2) - ui_invoke_popup_menu(&layer_popup_id, 0, ui_mouse_x(), ui_mouse_y(), 120, 150, popup_layer); + ui_invoke_popup_menu(&layer_popup_id, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, popup_layer); } - ui_hsplit_t(&layersbox, 2.0f, &slot, &layersbox); + layersbox.HSplitTop(2.0f, &slot, &layersbox); } - ui_hsplit_t(&layersbox, 5.0f, &slot, &layersbox); + layersbox.HSplitTop(5.0f, &slot, &layersbox); } } { - ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox); + layersbox.HSplitTop(12.0f, &slot, &layersbox); static int new_group_button = 0; - if(do_editor_button(&new_group_button, "Add Group", 0, &slot, draw_editor_button, 0, "Adds a new group")) + if(DoButton_Editor(&new_group_button, "Add Group", 0, &slot, 0, "Adds a new group")) { - editor.map.new_group(); - editor.selected_group = editor.map.groups.len()-1; + map.new_group(); + selected_group = map.groups.len()-1; } } - ui_hsplit_t(&layersbox, 5.0f, &slot, &layersbox); + layersbox.HSplitTop(5.0f, &slot, &layersbox); } @@ -1659,31 +1806,33 @@ static void extract_name(const char *filename, char *name) dbg_msg("", "%s %s %d %d", filename, name, start, end); } -static void replace_image(const char *filename) +void EDITOR::replace_image(const char *filename, void *user) { - EDITOR_IMAGE imginfo; - if(!gfx_load_png(&imginfo, filename)) + EDITOR *editor = (EDITOR *)user; + EDITOR_IMAGE imginfo(editor); + if(!editor->Graphics()->LoadPNG(&imginfo, filename)) return; - EDITOR_IMAGE *img = editor.map.images[editor.selected_image]; - gfx_unload_texture(img->tex_id); + EDITOR_IMAGE *img = editor->map.images[editor->selected_image]; + editor->Graphics()->UnloadTexture(img->tex_id); *img = imginfo; extract_name(filename, img->name); - img->tex_id = gfx_load_texture_raw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO, 0); + img->tex_id = editor->Graphics()->LoadTextureRaw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO, 0); } -static void add_image(const char *filename) +void EDITOR::add_image(const char *filename, void *user) { - EDITOR_IMAGE imginfo; - if(!gfx_load_png(&imginfo, filename)) + EDITOR *editor = (EDITOR *)user; + EDITOR_IMAGE imginfo(editor); + if(!editor->Graphics()->LoadPNG(&imginfo, filename)) return; - EDITOR_IMAGE *img = new EDITOR_IMAGE; + EDITOR_IMAGE *img = new EDITOR_IMAGE(editor); *img = imginfo; - img->tex_id = gfx_load_texture_raw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO, 0); + img->tex_id = editor->Graphics()->LoadTextureRaw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO, 0); img->external = 1; // external by default extract_name(filename, img->name); - editor.map.images.add(img); + editor->map.images.add(img); } @@ -1696,20 +1845,20 @@ static void modify_index_deleted(int *index) *index = *index - 1; } -static int popup_image(RECT view) +int EDITOR::popup_image(EDITOR *pEditor, CUIRect view) { static int replace_button = 0; static int remove_button = 0; - RECT slot; - ui_hsplit_t(&view, 2.0f, &slot, &view); - ui_hsplit_t(&view, 12.0f, &slot, &view); - EDITOR_IMAGE *img = editor.map.images[editor.selected_image]; + CUIRect slot; + view.HSplitTop(2.0f, &slot, &view); + view.HSplitTop(12.0f, &slot, &view); + EDITOR_IMAGE *img = pEditor->map.images[pEditor->selected_image]; static int external_button = 0; if(img->external) { - if(do_editor_button(&external_button, "Embedd", 0, &slot, draw_editor_button_menuitem, 0, "Embedds the image into the map file.")) + if(pEditor->DoButton_MenuItem(&external_button, "Embedd", 0, &slot, 0, "Embedds the image into the map file.")) { img->external = 0; return 1; @@ -1717,29 +1866,29 @@ static int popup_image(RECT view) } else { - if(do_editor_button(&external_button, "Make external", 0, &slot, draw_editor_button_menuitem, 0, "Removes the image from the map file.")) + if(pEditor->DoButton_MenuItem(&external_button, "Make external", 0, &slot, 0, "Removes the image from the map file.")) { img->external = 1; return 1; } } - ui_hsplit_t(&view, 10.0f, &slot, &view); - ui_hsplit_t(&view, 12.0f, &slot, &view); - if(do_editor_button(&replace_button, "Replace", 0, &slot, draw_editor_button_menuitem, 0, "Replaces the image with a new one")) + view.HSplitTop(10.0f, &slot, &view); + view.HSplitTop(12.0f, &slot, &view); + if(pEditor->DoButton_MenuItem(&replace_button, "Replace", 0, &slot, 0, "Replaces the image with a new one")) { - editor.invoke_file_dialog(LISTDIRTYPE_ALL, "Replace Image", "Replace", "mapres/", "", replace_image); + pEditor->invoke_file_dialog(LISTDIRTYPE_ALL, "Replace Image", "Replace", "mapres/", "", replace_image, pEditor); return 1; } - ui_hsplit_t(&view, 10.0f, &slot, &view); - ui_hsplit_t(&view, 12.0f, &slot, &view); - if(do_editor_button(&remove_button, "Remove", 0, &slot, draw_editor_button_menuitem, 0, "Removes the image from the map")) + view.HSplitTop(10.0f, &slot, &view); + view.HSplitTop(12.0f, &slot, &view); + if(pEditor->DoButton_MenuItem(&remove_button, "Remove", 0, &slot, 0, "Removes the image from the map")) { delete img; - editor.map.images.removebyindex(editor.selected_image); - modify_index_deleted_index = editor.selected_image; - editor.map.modify_image_index(modify_index_deleted); + pEditor->map.images.removebyindex(pEditor->selected_image); + modify_index_deleted_index = pEditor->selected_image; + pEditor->map.modify_image_index(modify_index_deleted); return 1; } @@ -1747,76 +1896,77 @@ static int popup_image(RECT view) } -static void render_images(RECT toolbox, RECT toolbar, RECT view) +void EDITOR::render_images(CUIRect toolbox, CUIRect toolbar, CUIRect view) { for(int e = 0; e < 2; e++) // two passes, first embedded, then external { - RECT slot; - ui_hsplit_t(&toolbox, 15.0f, &slot, &toolbox); + CUIRect slot; + toolbox.HSplitTop(15.0f, &slot, &toolbox); if(e == 0) - ui_do_label(&slot, "Embedded", 12.0f, 0); + UI()->DoLabel(&slot, "Embedded", 12.0f, 0); else - ui_do_label(&slot, "External", 12.0f, 0); + UI()->DoLabel(&slot, "External", 12.0f, 0); - for(int i = 0; i < editor.map.images.len(); i++) + for(int i = 0; i < map.images.len(); i++) { - if((e && !editor.map.images[i]->external) || - (!e && editor.map.images[i]->external)) + if((e && !map.images[i]->external) || + (!e && map.images[i]->external)) { continue; } char buf[128]; - sprintf(buf, "%s", editor.map.images[i]->name); - ui_hsplit_t(&toolbox, 12.0f, &slot, &toolbox); + sprintf(buf, "%s", map.images[i]->name); + toolbox.HSplitTop(12.0f, &slot, &toolbox); - if(int result = do_editor_button(&editor.map.images[i], buf, editor.selected_image == i, &slot, draw_editor_button, + if(int result = DoButton_Editor(&map.images[i], buf, selected_image == i, &slot, BUTTON_CONTEXT, "Select image")) { - editor.selected_image = i; + selected_image = i; static int popup_image_id = 0; if(result == 2) - ui_invoke_popup_menu(&popup_image_id, 0, ui_mouse_x(), ui_mouse_y(), 120, 80, popup_image); + ui_invoke_popup_menu(&popup_image_id, 0, UI()->MouseX(), UI()->MouseY(), 120, 80, popup_image); } - ui_hsplit_t(&toolbox, 2.0f, 0, &toolbox); + toolbox.HSplitTop(2.0f, 0, &toolbox); // render image - if(editor.selected_image == i) + if(selected_image == i) { - RECT r; - ui_margin(&view, 10.0f, &r); + CUIRect r; + view.Margin(10.0f, &r); if(r.h < r.w) r.w = r.h; else r.h = r.w; - gfx_texture_set(editor.map.images[i]->tex_id); - gfx_blend_normal(); - gfx_quads_begin(); - gfx_quads_drawTL(r.x, r.y, r.w, r.h); - gfx_quads_end(); + Graphics()->TextureSet(map.images[i]->tex_id); + Graphics()->BlendNormal(); + Graphics()->QuadsBegin(); + Graphics()->QuadsDrawTL(r.x, r.y, r.w, r.h); + Graphics()->QuadsEnd(); } } } - RECT slot; - ui_hsplit_t(&toolbox, 5.0f, &slot, &toolbox); + CUIRect slot; + toolbox.HSplitTop(5.0f, &slot, &toolbox); // new image static int new_image_button = 0; - ui_hsplit_t(&toolbox, 10.0f, &slot, &toolbox); - ui_hsplit_t(&toolbox, 12.0f, &slot, &toolbox); - if(do_editor_button(&new_image_button, "Add", 0, &slot, draw_editor_button, 0, "Load a new image to use in the map")) - editor.invoke_file_dialog(LISTDIRTYPE_ALL, "Add Image", "Add", "mapres/", "", add_image); + toolbox.HSplitTop(10.0f, &slot, &toolbox); + toolbox.HSplitTop(12.0f, &slot, &toolbox); + if(DoButton_Editor(&new_image_button, "Add", 0, &slot, 0, "Load a new image to use in the map")) + invoke_file_dialog(LISTDIRTYPE_ALL, "Add Image", "Add", "mapres/", "", add_image, this); } static int file_dialog_dirtypes = 0; static const char *file_dialog_title = 0; static const char *file_dialog_button_text = 0; -static void (*file_dialog_func)(const char *filename); +static void (*file_dialog_func)(const char *filename, void *user); +static void *file_dialog_user = 0; static char file_dialog_filename[512] = {0}; static char file_dialog_path[512] = {0}; static char file_dialog_complete_filename[512] = {0}; @@ -1825,6 +1975,12 @@ int files_startat = 0; int files_cur = 0; int files_stopat = 999; +struct LISTDIRINFO +{ + CUIRect *rect; + EDITOR *editor; +}; + static void editor_listdir_callback(const char *name, int is_dir, void *user) { if(name[0] == '.' || is_dir) // skip this shit! @@ -1837,13 +1993,14 @@ static void editor_listdir_callback(const char *name, int is_dir, void *user) if(files_cur-1 < files_startat || files_cur > files_stopat) return; - RECT *view = (RECT *)user; - RECT button; - ui_hsplit_t(view, 15.0f, &button, view); - ui_hsplit_t(view, 2.0f, 0, view); + LISTDIRINFO *info = (LISTDIRINFO *)user; + CUIRect *view = info->rect; + CUIRect button; + view->HSplitTop(15.0f, &button, view); + view->HSplitTop(2.0f, 0, view); //char buf[512]; - if(do_editor_button((void*)(10+(int)button.y), name, 0, &button, draw_editor_button_file, 0, 0)) + if(info->editor->DoButton_File((void*)(10+(int)button.y), name, 0, &button, 0, 0)) { strncpy(file_dialog_filename, name, sizeof(file_dialog_filename)); @@ -1854,43 +2011,43 @@ static void editor_listdir_callback(const char *name, int is_dir, void *user) if(inp_mouse_doubleclick()) { if(file_dialog_func) - file_dialog_func(file_dialog_complete_filename); - editor.dialog = DIALOG_NONE; + file_dialog_func(file_dialog_complete_filename, user); + info->editor->dialog = DIALOG_NONE; } } } -static void render_file_dialog() +void EDITOR::render_file_dialog() { // GUI coordsys - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); - - RECT view = *ui_screen(); - ui_draw_rect(&view, vec4(0,0,0,0.25f), 0, 0); - ui_vmargin(&view, 150.0f, &view); - ui_hmargin(&view, 50.0f, &view); - ui_draw_rect(&view, vec4(0,0,0,0.75f), CORNER_ALL, 5.0f); - ui_margin(&view, 10.0f, &view); - - RECT title, filebox, filebox_label, buttonbar, scroll; - ui_hsplit_t(&view, 18.0f, &title, &view); - ui_hsplit_t(&view, 5.0f, 0, &view); // some spacing - ui_hsplit_b(&view, 14.0f, &view, &buttonbar); - ui_hsplit_b(&view, 10.0f, &view, 0); // some spacing - ui_hsplit_b(&view, 14.0f, &view, &filebox); - ui_vsplit_l(&filebox, 50.0f, &filebox_label, &filebox); - ui_vsplit_r(&view, 15.0f, &view, &scroll); + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); + + CUIRect view = *UI()->Screen(); + RenderTools()->DrawUIRect(&view, vec4(0,0,0,0.25f), 0, 0); + view.VMargin(150.0f, &view); + view.HMargin(50.0f, &view); + RenderTools()->DrawUIRect(&view, vec4(0,0,0,0.75f), CUI::CORNER_ALL, 5.0f); + view.Margin(10.0f, &view); + + CUIRect title, filebox, filebox_label, buttonbar, scroll; + view.HSplitTop(18.0f, &title, &view); + view.HSplitTop(5.0f, 0, &view); // some spacing + view.HSplitBottom(14.0f, &view, &buttonbar); + view.HSplitBottom(10.0f, &view, 0); // some spacing + view.HSplitBottom(14.0f, &view, &filebox); + filebox.VSplitLeft(50.0f, &filebox_label, &filebox); + view.VSplitRight(15.0f, &view, &scroll); // title - ui_draw_rect(&title, vec4(1,1,1,0.25f), CORNER_ALL, 5.0f); - ui_vmargin(&title, 10.0f, &title); - ui_do_label(&title, file_dialog_title, 14.0f, -1, -1); + RenderTools()->DrawUIRect(&title, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 5.0f); + title.VMargin(10.0f, &title); + UI()->DoLabel(&title, file_dialog_title, 14.0f, -1, -1); // filebox - ui_do_label(&filebox_label, "Filename:", 10.0f, -1, -1); + UI()->DoLabel(&filebox_label, "Filename:", 10.0f, -1, -1); static int filebox_id = 0; - ui_do_edit_box(&filebox_id, &filebox, file_dialog_filename, sizeof(file_dialog_filename), 10.0f); + DoEditBox(&filebox_id, &filebox, file_dialog_filename, sizeof(file_dialog_filename), 10.0f); file_dialog_complete_filename[0] = 0; strcat(file_dialog_complete_filename, file_dialog_path); @@ -1899,7 +2056,7 @@ static void render_file_dialog() int num = (int)(view.h/17.0); static float scrollvalue = 0; static int scrollbar = 0; - ui_hmargin(&scroll, 5.0f, &scroll); + scroll.HMargin(5.0f, &scroll); scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); int scrollnum = files_num-num+10; @@ -1925,41 +2082,45 @@ static void render_file_dialog() files_cur = 0; // set clipping - ui_clip_enable(&view); + UI()->ClipEnable(&view); // the list - engine_listdir(file_dialog_dirtypes, file_dialog_path, editor_listdir_callback, &view); + LISTDIRINFO info; + info.rect = &view; + info.editor = this; + engine_listdir(file_dialog_dirtypes, file_dialog_path, editor_listdir_callback, &info); // disable clipping again - ui_clip_disable(); + UI()->ClipDisable(); // the buttons static int ok_button = 0; static int cancel_button = 0; - RECT button; - ui_vsplit_r(&buttonbar, 50.0f, &buttonbar, &button); - if(do_editor_button(&ok_button, file_dialog_button_text, 0, &button, draw_editor_button, 0, 0) || inp_key_pressed(KEY_RETURN)) + CUIRect button; + buttonbar.VSplitRight(50.0f, &buttonbar, &button); + if(DoButton_Editor(&ok_button, file_dialog_button_text, 0, &button, 0, 0) || inp_key_pressed(KEY_RETURN)) { if(file_dialog_func) - file_dialog_func(file_dialog_complete_filename); - editor.dialog = DIALOG_NONE; + file_dialog_func(file_dialog_complete_filename, file_dialog_user); + dialog = DIALOG_NONE; } - ui_vsplit_r(&buttonbar, 40.0f, &buttonbar, &button); - ui_vsplit_r(&buttonbar, 50.0f, &buttonbar, &button); - if(do_editor_button(&cancel_button, "Cancel", 0, &button, draw_editor_button, 0, 0) || inp_key_pressed(KEY_ESCAPE)) - editor.dialog = DIALOG_NONE; + buttonbar.VSplitRight(40.0f, &buttonbar, &button); + buttonbar.VSplitRight(50.0f, &buttonbar, &button); + if(DoButton_Editor(&cancel_button, "Cancel", 0, &button, 0, 0) || inp_key_pressed(KEY_ESCAPE)) + dialog = DIALOG_NONE; } void EDITOR::invoke_file_dialog(int listdirtypes, const char *title, const char *button_text, const char *basepath, const char *default_name, - void (*func)(const char *filename)) + void (*func)(const char *filename, void *user), void *user) { file_dialog_dirtypes = listdirtypes; file_dialog_title = title; file_dialog_button_text = button_text; file_dialog_func = func; + file_dialog_user = user; file_dialog_filename[0] = 0; file_dialog_path[0] = 0; @@ -1968,78 +2129,78 @@ void EDITOR::invoke_file_dialog(int listdirtypes, const char *title, const char if(basepath) strncpy(file_dialog_path, basepath, sizeof(file_dialog_path)); - editor.dialog = DIALOG_FILE; + dialog = DIALOG_FILE; } -static void render_modebar(RECT view) +void EDITOR::render_modebar(CUIRect view) { - RECT button; + CUIRect button; // mode buttons { - ui_vsplit_l(&view, 40.0f, &button, &view); + view.VSplitLeft(40.0f, &button, &view); static int tile_button = 0; - if(do_editor_button(&tile_button, "Layers", editor.mode == MODE_LAYERS, &button, draw_editor_button_m, 0, "Switch to edit layers.")) - editor.mode = MODE_LAYERS; + if(DoButton_ButtonM(&tile_button, "Layers", mode == MODE_LAYERS, &button, 0, "Switch to edit layers.")) + mode = MODE_LAYERS; - ui_vsplit_l(&view, 40.0f, &button, &view); + view.VSplitLeft(40.0f, &button, &view); static int img_button = 0; - if(do_editor_button(&img_button, "Images", editor.mode == MODE_IMAGES, &button, draw_editor_button_r, 0, "Switch to manage images.")) - editor.mode = MODE_IMAGES; + if(DoButton_ButtonR(&img_button, "Images", mode == MODE_IMAGES, &button, 0, "Switch to manage images.")) + mode = MODE_IMAGES; } - ui_vsplit_l(&view, 5.0f, 0, &view); + view.VSplitLeft(5.0f, 0, &view); // spacing - //ui_vsplit_l(&view, 10.0f, 0, &view); + //view.VSplitLeft(10.0f, 0, &view); } -static void render_statusbar(RECT view) +void EDITOR::render_statusbar(CUIRect view) { - RECT button; - ui_vsplit_r(&view, 60.0f, &view, &button); + CUIRect button; + view.VSplitRight(60.0f, &view, &button); static int envelope_button = 0; - if(do_editor_button(&envelope_button, "Envelopes", editor.show_envelope_editor, &button, draw_editor_button, 0, "Toggles the envelope editor.")) - editor.show_envelope_editor = (editor.show_envelope_editor+1)%4; + if(DoButton_Editor(&envelope_button, "Envelopes", show_envelope_editor, &button, 0, "Toggles the envelope editor.")) + show_envelope_editor = (show_envelope_editor+1)%4; - if(editor.tooltip) + if(tooltip) { - if(ui_got_context && ui_got_context == ui_hot_item()) + if(ui_got_context && ui_got_context == UI()->HotItem()) { char buf[512]; - sprintf(buf, "%s Right click for context menu.", editor.tooltip); - ui_do_label(&view, buf, 10.0f, -1, -1); + sprintf(buf, "%s Right click for context menu.", tooltip); + UI()->DoLabel(&view, buf, 10.0f, -1, -1); } else - ui_do_label(&view, editor.tooltip, 10.0f, -1, -1); + UI()->DoLabel(&view, tooltip, 10.0f, -1, -1); } } -static void render_envelopeeditor(RECT view) +void EDITOR::render_envelopeeditor(CUIRect view) { - if(editor.selected_envelope < 0) editor.selected_envelope = 0; - if(editor.selected_envelope >= editor.map.envelopes.len()) editor.selected_envelope--; + if(selected_envelope < 0) selected_envelope = 0; + if(selected_envelope >= map.envelopes.len()) selected_envelope--; ENVELOPE *envelope = 0; - if(editor.selected_envelope >= 0 && editor.selected_envelope < editor.map.envelopes.len()) - envelope = editor.map.envelopes[editor.selected_envelope]; + if(selected_envelope >= 0 && selected_envelope < map.envelopes.len()) + envelope = map.envelopes[selected_envelope]; bool show_colorbar = false; if(envelope && envelope->channels == 4) show_colorbar = true; - RECT toolbar, curvebar, colorbar; - ui_hsplit_t(&view, 15.0f, &toolbar, &view); - ui_hsplit_t(&view, 15.0f, &curvebar, &view); - ui_margin(&toolbar, 2.0f, &toolbar); - ui_margin(&curvebar, 2.0f, &curvebar); + CUIRect toolbar, curvebar, colorbar; + view.HSplitTop(15.0f, &toolbar, &view); + view.HSplitTop(15.0f, &curvebar, &view); + toolbar.Margin(2.0f, &toolbar); + curvebar.Margin(2.0f, &curvebar); if(show_colorbar) { - ui_hsplit_t(&view, 20.0f, &colorbar, &view); - ui_margin(&colorbar, 2.0f, &colorbar); + view.HSplitTop(20.0f, &colorbar, &view); + colorbar.Margin(2.0f, &colorbar); render_background(colorbar, checker_texture, 16.0f, 1.0f); } @@ -2047,19 +2208,19 @@ static void render_envelopeeditor(RECT view) // do the toolbar { - RECT button; + CUIRect button; ENVELOPE *new_env = 0; - ui_vsplit_r(&toolbar, 50.0f, &toolbar, &button); + toolbar.VSplitRight(50.0f, &toolbar, &button); static int new_4d_button = 0; - if(do_editor_button(&new_4d_button, "Color+", 0, &button, draw_editor_button, 0, "Creates a new color envelope")) - new_env = editor.map.new_envelope(4); + if(DoButton_Editor(&new_4d_button, "Color+", 0, &button, 0, "Creates a new color envelope")) + new_env = map.new_envelope(4); - ui_vsplit_r(&toolbar, 5.0f, &toolbar, &button); - ui_vsplit_r(&toolbar, 50.0f, &toolbar, &button); + toolbar.VSplitRight(5.0f, &toolbar, &button); + toolbar.VSplitRight(50.0f, &toolbar, &button); static int new_2d_button = 0; - if(do_editor_button(&new_2d_button, "Pos.+", 0, &button, draw_editor_button, 0, "Creates a new pos envelope")) - new_env = editor.map.new_envelope(3); + if(DoButton_Editor(&new_2d_button, "Pos.+", 0, &button, 0, "Creates a new pos envelope")) + new_env = map.new_envelope(3); if(new_env) // add the default points { @@ -2075,33 +2236,33 @@ static void render_envelopeeditor(RECT view) } } - RECT shifter, inc, dec; - ui_vsplit_l(&toolbar, 60.0f, &shifter, &toolbar); - ui_vsplit_r(&shifter, 15.0f, &shifter, &inc); - ui_vsplit_l(&shifter, 15.0f, &dec, &shifter); + CUIRect shifter, inc, dec; + toolbar.VSplitLeft(60.0f, &shifter, &toolbar); + shifter.VSplitRight(15.0f, &shifter, &inc); + shifter.VSplitLeft(15.0f, &dec, &shifter); char buf[512]; - sprintf(buf, "%d/%d", editor.selected_envelope+1, editor.map.envelopes.len()); - ui_draw_rect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f); - ui_do_label(&shifter, buf, 10.0f, 0, -1); + sprintf(buf, "%d/%d", selected_envelope+1, map.envelopes.len()); + RenderTools()->DrawUIRect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f); + UI()->DoLabel(&shifter, buf, 10.0f, 0, -1); static int prev_button = 0; - if(do_editor_button(&prev_button, 0, 0, &dec, draw_dec_button, 0, "Previous Envelope")) - editor.selected_envelope--; + if(DoButton_ButtonDec(&prev_button, 0, 0, &dec, 0, "Previous Envelope")) + selected_envelope--; static int next_button = 0; - if(do_editor_button(&next_button, 0, 0, &inc, draw_inc_button, 0, "Next Envelope")) - editor.selected_envelope++; + if(DoButton_ButtonInc(&next_button, 0, 0, &inc, 0, "Next Envelope")) + selected_envelope++; if(envelope) { - ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar); - ui_vsplit_l(&toolbar, 35.0f, &button, &toolbar); - ui_do_label(&button, "Name:", 10.0f, -1, -1); + toolbar.VSplitLeft(15.0f, &button, &toolbar); + toolbar.VSplitLeft(35.0f, &button, &toolbar); + UI()->DoLabel(&button, "Name:", 10.0f, -1, -1); - ui_vsplit_l(&toolbar, 80.0f, &button, &toolbar); + toolbar.VSplitLeft(80.0f, &button, &toolbar); static int name_box = 0; - ui_do_edit_box(&name_box, &button, envelope->name, sizeof(envelope->name), 10.0f); + DoEditBox(&name_box, &button, envelope->name, sizeof(envelope->name), 10.0f); } } @@ -2113,9 +2274,9 @@ static void render_envelopeeditor(RECT view) if(envelope) { - RECT button; + CUIRect button; - ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar); + toolbar.VSplitLeft(15.0f, &button, &toolbar); static const char *names[4][4] = { {"X", "", "", ""}, @@ -2126,17 +2287,17 @@ static void render_envelopeeditor(RECT view) static int channel_buttons[4] = {0}; int bit = 1; - ui_draw_button_func draw_func; + /*ui_draw_button_func draw_func;*/ for(int i = 0; i < envelope->channels; i++, bit<<=1) { - ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar); + toolbar.VSplitLeft(15.0f, &button, &toolbar); - if(i == 0) draw_func = draw_editor_button_l; + /*if(i == 0) draw_func = draw_editor_button_l; else if(i == envelope->channels-1) draw_func = draw_editor_button_r; - else draw_func = draw_editor_button_m; + else draw_func = draw_editor_button_m;*/ - if(do_editor_button(&channel_buttons[i], names[envelope->channels-1][i], active_channels&bit, &button, draw_func, 0, 0)) + if(DoButton_Editor(&channel_buttons[i], names[envelope->channels-1][i], active_channels&bit, &button, 0, 0)) active_channels ^= bit; } } @@ -2157,19 +2318,19 @@ static void render_envelopeeditor(RECT view) float timescale = end_time/view.w; float valuescale = (top-bottom)/view.h; - if(ui_mouse_inside(&view)) - ui_set_hot_item(&envelope_editor_id); + if(UI()->MouseInside(&view)) + UI()->SetHotItem(&envelope_editor_id); - if(ui_hot_item() == &envelope_editor_id) + if(UI()->HotItem() == &envelope_editor_id) { // do stuff if(envelope) { - if(ui_mouse_button_clicked(1)) + if(UI()->MouseButtonClicked(1)) { // add point - int time = (int)(((ui_mouse_x()-view.x)*timescale)*1000.0f); - //float env_y = (ui_mouse_y()-view.y)/timescale; + int time = (int)(((UI()->MouseX()-view.x)*timescale)*1000.0f); + //float env_y = (UI()->MouseY()-view.y)/timescale; float channels[4]; envelope->eval(time, channels); envelope->add_point(time, @@ -2177,7 +2338,7 @@ static void render_envelopeeditor(RECT view) f2fx(channels[2]), f2fx(channels[3])); } - editor.tooltip = "Press right mouse button to create a new point"; + tooltip = "Press right mouse button to create a new point"; } } @@ -2185,22 +2346,22 @@ static void render_envelopeeditor(RECT view) // render lines { - ui_clip_enable(&view); - gfx_texture_set(-1); - gfx_lines_begin(); + UI()->ClipEnable(&view); + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); for(int c = 0; c < envelope->channels; c++) { if(active_channels&(1<SetColor(colors[c].r,colors[c].g,colors[c].b,1); else - gfx_setcolor(colors[c].r*0.5f,colors[c].g*0.5f,colors[c].b*0.5f,1); + Graphics()->SetColor(colors[c].r*0.5f,colors[c].g*0.5f,colors[c].b*0.5f,1); float prev_x = 0; float results[4]; envelope->eval(0.000001f, results); float prev_value = results[c]; - int steps = (int)((view.w/ui_screen()->w) * gfx_screenwidth()); + int steps = (int)((view.w/UI()->Screen()->w) * Graphics()->ScreenWidth()); for(int i = 1; i <= steps; i++) { float a = i/(float)steps; @@ -2208,13 +2369,13 @@ static void render_envelopeeditor(RECT view) float v = results[c]; v = (v-bottom)/(top-bottom); - gfx_lines_draw(view.x + prev_x*view.w, view.y+view.h - prev_value*view.h, view.x + a*view.w, view.y+view.h - v*view.h); + Graphics()->LinesDraw(view.x + prev_x*view.w, view.y+view.h - prev_value*view.h, view.x + a*view.w, view.y+view.h - v*view.h); prev_x = a; prev_value = v; } } - gfx_lines_end(); - ui_clip_disable(); + Graphics()->LinesEnd(); + UI()->ClipDisable(); } // render curve options @@ -2226,7 +2387,7 @@ static void render_envelopeeditor(RECT view) //dbg_msg("", "%f", end_time); - RECT v; + CUIRect v; v.x = curvebar.x + (t0+(t1-t0)*0.5f) * curvebar.w; v.y = curvebar.y; v.h = curvebar.h; @@ -2237,7 +2398,7 @@ static void render_envelopeeditor(RECT view) "N", "L", "S", "F", "M" }; - if(do_editor_button(id, type_name[envelope->points[i].curvetype], 0, &v, draw_editor_button, 0, "Switch curve type")) + if(DoButton_Editor(id, type_name[envelope->points[i].curvetype], 0, &v, 0, "Switch curve type")) envelope->points[i].curvetype = (envelope->points[i].curvetype+1)%NUM_CURVETYPES; } } @@ -2245,8 +2406,8 @@ static void render_envelopeeditor(RECT view) // render colorbar if(show_colorbar) { - gfx_texture_set(-1); - gfx_quads_begin(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); for(int i = 0; i < envelope->points.len()-1; i++) { float r0 = fx2f(envelope->points[i].values[0]); @@ -2258,24 +2419,24 @@ static void render_envelopeeditor(RECT view) float b1 = fx2f(envelope->points[i+1].values[2]); float a1 = fx2f(envelope->points[i+1].values[3]); - gfx_setcolorvertex(0, r0, g0, b0, a0); - gfx_setcolorvertex(1, r1, g1, b1, a1); - gfx_setcolorvertex(2, r1, g1, b1, a1); - gfx_setcolorvertex(3, r0, g0, b0, a0); + Graphics()->SetColorVertex(0, r0, g0, b0, a0); + Graphics()->SetColorVertex(1, r1, g1, b1, a1); + Graphics()->SetColorVertex(2, r1, g1, b1, a1); + Graphics()->SetColorVertex(3, r0, g0, b0, a0); float x0 = envelope->points[i].time/1000.0f/end_time; // float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom); float x1 = envelope->points[i+1].time/1000.0f/end_time; //float y1 = (fx2f(envelope->points[i+1].values[c])-bottom)/(top-bottom); - RECT v; + CUIRect v; v.x = colorbar.x + x0*colorbar.w; v.y = colorbar.y; v.w = (x1-x0)*colorbar.w; v.h = colorbar.h; - gfx_quads_drawTL(v.x, v.y, v.w, v.h); + Graphics()->QuadsDrawTL(v.x, v.y, v.w, v.h); } - gfx_quads_end(); + Graphics()->QuadsEnd(); } // render handles @@ -2284,8 +2445,8 @@ static void render_envelopeeditor(RECT view) int current_value = 0, current_time = 0; - gfx_texture_set(-1); - gfx_quads_begin(); + Graphics()->TextureSet(-1); + Graphics()->QuadsBegin(); for(int c = 0; c < envelope->channels; c++) { if(!(active_channels&(1<points[i].time/1000.0f/end_time; float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom); - RECT final; + CUIRect final; final.x = view.x + x0*view.w; final.y = view.y+view.h - y0*view.h; final.x -= 2.0f; @@ -2305,26 +2466,26 @@ static void render_envelopeeditor(RECT view) void *id = &envelope->points[i].values[c]; - if(ui_mouse_inside(&final)) - ui_set_hot_item(id); + if(UI()->MouseInside(&final)) + UI()->SetHotItem(id); float colormod = 1.0f; - if(ui_active_item() == id) + if(UI()->ActiveItem() == id) { - if(!ui_mouse_button(0)) + if(!UI()->MouseButton(0)) { - ui_set_active_item(0); + UI()->SetActiveItem(0); move = false; } else { - envelope->points[i].values[c] -= f2fx(editor.mouse_delta_y*valuescale); + envelope->points[i].values[c] -= f2fx(mouse_delta_y*valuescale); if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) { if(i != 0) { - envelope->points[i].time += (int)((editor.mouse_delta_x*timescale)*1000.0f); + envelope->points[i].time += (int)((mouse_delta_x*timescale)*1000.0f); if(envelope->points[i].time < envelope->points[i-1].time) envelope->points[i].time = envelope->points[i-1].time + 1; if(i+1 != envelope->points.len() && envelope->points[i].time > envelope->points[i+1].time) @@ -2334,46 +2495,46 @@ static void render_envelopeeditor(RECT view) } colormod = 100.0f; - gfx_setcolor(1,1,1,1); + Graphics()->SetColor(1,1,1,1); } - else if(ui_hot_item() == id) + else if(UI()->HotItem() == id) { - if(ui_mouse_button(0)) + if(UI()->MouseButton(0)) { selection.clear(); selection.add(i); - ui_set_active_item(id); + UI()->SetActiveItem(id); } // remove point - if(ui_mouse_button_clicked(1)) + if(UI()->MouseButtonClicked(1)) envelope->points.removebyindex(i); colormod = 100.0f; - gfx_setcolor(1,0.75f,0.75f,1); - editor.tooltip = "Left mouse to drag. Hold shift to alter time point aswell. Right click to delete."; + Graphics()->SetColor(1,0.75f,0.75f,1); + tooltip = "Left mouse to drag. Hold shift to alter time point aswell. Right click to delete."; } - if(ui_active_item() == id || ui_hot_item() == id) + if(UI()->ActiveItem() == id || UI()->HotItem() == id) { current_time = envelope->points[i].time; current_value = envelope->points[i].values[c]; } - gfx_setcolor(colors[c].r*colormod, colors[c].g*colormod, colors[c].b*colormod, 1.0f); - gfx_quads_drawTL(final.x, final.y, final.w, final.h); + Graphics()->SetColor(colors[c].r*colormod, colors[c].g*colormod, colors[c].b*colormod, 1.0f); + Graphics()->QuadsDrawTL(final.x, final.y, final.w, final.h); } } - gfx_quads_end(); + Graphics()->QuadsEnd(); char buf[512]; sprintf(buf, "%.3f %.3f", current_time/1000.0f, fx2f(current_value)); - ui_do_label(&toolbar, buf, 10.0f, 0, -1); + UI()->DoLabel(&toolbar, buf, 10.0f, 0, -1); } } } -static int popup_menu_file(RECT view) +int EDITOR::popup_menu_file(EDITOR *pEditor, CUIRect view) { static int new_map_button = 0; static int save_button = 0; @@ -2382,49 +2543,49 @@ static int popup_menu_file(RECT view) static int append_button = 0; static int exit_button = 0; - RECT slot; - ui_hsplit_t(&view, 2.0f, &slot, &view); - ui_hsplit_t(&view, 12.0f, &slot, &view); - if(do_editor_button(&new_map_button, "New", 0, &slot, draw_editor_button_menuitem, 0, "Creates a new map")) + CUIRect slot; + view.HSplitTop(2.0f, &slot, &view); + view.HSplitTop(12.0f, &slot, &view); + if(pEditor->DoButton_MenuItem(&new_map_button, "New", 0, &slot, 0, "Creates a new map")) { - editor.reset(); + pEditor->reset(); return 1; } - ui_hsplit_t(&view, 10.0f, &slot, &view); - ui_hsplit_t(&view, 12.0f, &slot, &view); - if(do_editor_button(&open_button, "Open", 0, &slot, draw_editor_button_menuitem, 0, "Opens a map for editing")) + view.HSplitTop(10.0f, &slot, &view); + view.HSplitTop(12.0f, &slot, &view); + if(pEditor->DoButton_MenuItem(&open_button, "Open", 0, &slot, 0, "Opens a map for editing")) { - editor.invoke_file_dialog(LISTDIRTYPE_ALL, "Open Map", "Open", "maps/", "", callback_open_map); + pEditor->invoke_file_dialog(LISTDIRTYPE_ALL, "Open Map", "Open", "maps/", "", callback_open_map, pEditor); return 1; } - ui_hsplit_t(&view, 10.0f, &slot, &view); - ui_hsplit_t(&view, 12.0f, &slot, &view); - if(do_editor_button(&append_button, "Append", 0, &slot, draw_editor_button_menuitem, 0, "Opens a map and adds everything from that map to the current one")) + view.HSplitTop(10.0f, &slot, &view); + view.HSplitTop(12.0f, &slot, &view); + if(pEditor->DoButton_MenuItem(&append_button, "Append", 0, &slot, 0, "Opens a map and adds everything from that map to the current one")) { - editor.invoke_file_dialog(LISTDIRTYPE_ALL, "Append Map", "Append", "maps/", "", callback_append_map); + pEditor->invoke_file_dialog(LISTDIRTYPE_ALL, "Append Map", "Append", "maps/", "", callback_append_map, pEditor); return 1; } - ui_hsplit_t(&view, 10.0f, &slot, &view); - ui_hsplit_t(&view, 12.0f, &slot, &view); - if(do_editor_button(&save_button, "Save (NOT IMPL)", 0, &slot, draw_editor_button_menuitem, 0, "Saves the current map")) + view.HSplitTop(10.0f, &slot, &view); + view.HSplitTop(12.0f, &slot, &view); + if(pEditor->DoButton_MenuItem(&save_button, "Save (NOT IMPL)", 0, &slot, 0, "Saves the current map")) { return 1; } - ui_hsplit_t(&view, 2.0f, &slot, &view); - ui_hsplit_t(&view, 12.0f, &slot, &view); - if(do_editor_button(&save_as_button, "Save As", 0, &slot, draw_editor_button_menuitem, 0, "Saves the current map under a new name")) + view.HSplitTop(2.0f, &slot, &view); + view.HSplitTop(12.0f, &slot, &view); + if(pEditor->DoButton_MenuItem(&save_as_button, "Save As", 0, &slot, 0, "Saves the current map under a new name")) { - editor.invoke_file_dialog(LISTDIRTYPE_SAVE, "Save Map", "Save", "maps/", "", callback_save_map); + pEditor->invoke_file_dialog(LISTDIRTYPE_SAVE, "Save Map", "Save", "maps/", "", callback_save_map, pEditor); return 1; } - ui_hsplit_t(&view, 10.0f, &slot, &view); - ui_hsplit_t(&view, 12.0f, &slot, &view); - if(do_editor_button(&exit_button, "Exit", 0, &slot, draw_editor_button_menuitem, 0, "Exits from the editor")) + view.HSplitTop(10.0f, &slot, &view); + view.HSplitTop(12.0f, &slot, &view); + if(pEditor->DoButton_MenuItem(&exit_button, "Exit", 0, &slot, 0, "Exits from the editor")) { config.cl_editor = 0; return 1; @@ -2433,22 +2594,22 @@ static int popup_menu_file(RECT view) return 0; } -static void render_menubar(RECT menubar) +void EDITOR::render_menubar(CUIRect menubar) { - static RECT file /*, view, help*/; + static CUIRect file /*, view, help*/; - ui_vsplit_l(&menubar, 60.0f, &file, &menubar); - if(do_editor_button(&file, "File", 0, &file, draw_editor_button_menu, 0, 0)) - ui_invoke_popup_menu(&file, 1, file.x, file.y+file.h-1.0f, 120, 150, popup_menu_file); + menubar.VSplitLeft(60.0f, &file, &menubar); + if(DoButton_Menu(&file, "File", 0, &file, 0, 0)) + ui_invoke_popup_menu(&file, 1, file.x, file.y+file.h-1.0f, 120, 150, popup_menu_file, this); /* - ui_vsplit_l(&menubar, 5.0f, 0, &menubar); - ui_vsplit_l(&menubar, 60.0f, &view, &menubar); + menubar.VSplitLeft(5.0f, 0, &menubar); + menubar.VSplitLeft(60.0f, &view, &menubar); if(do_editor_button(&view, "View", 0, &view, draw_editor_button_menu, 0, 0)) (void)0; - ui_vsplit_l(&menubar, 5.0f, 0, &menubar); - ui_vsplit_l(&menubar, 60.0f, &help, &menubar); + menubar.VSplitLeft(5.0f, 0, &menubar); + menubar.VSplitLeft(60.0f, &help, &menubar); if(do_editor_button(&help, "Help", 0, &help, draw_editor_button_menu, 0, 0)) (void)0; */ @@ -2457,102 +2618,102 @@ static void render_menubar(RECT menubar) void EDITOR::render() { // basic start - gfx_clear(1.0f,0.0f,1.0f); - RECT view = *ui_screen(); - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + Graphics()->Clear(1.0f,0.0f,1.0f); + CUIRect view = *UI()->Screen(); + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); // reset tip - editor.tooltip = 0; + tooltip = 0; // render checker render_background(view, checker_texture, 32.0f, 1.0f); - RECT menubar, modebar, toolbar, statusbar, envelope_editor, toolbox; + CUIRect menubar, modebar, toolbar, statusbar, envelope_editor, toolbox; - if(editor.gui_active) + if(gui_active) { - ui_hsplit_t(&view, 16.0f, &menubar, &view); - ui_vsplit_l(&view, 80.0f, &toolbox, &view); - ui_hsplit_t(&view, 16.0f, &toolbar, &view); - ui_hsplit_b(&view, 16.0f, &view, &statusbar); + view.HSplitTop(16.0f, &menubar, &view); + view.VSplitLeft(80.0f, &toolbox, &view); + view.HSplitTop(16.0f, &toolbar, &view); + view.HSplitBottom(16.0f, &view, &statusbar); - if(editor.show_envelope_editor) + if(show_envelope_editor) { float size = 125.0f; - if(editor.show_envelope_editor == 2) + if(show_envelope_editor == 2) size *= 2.0f; - else if(editor.show_envelope_editor == 3) + else if(show_envelope_editor == 3) size *= 3.0f; - ui_hsplit_b(&view, size, &view, &envelope_editor); + view.HSplitBottom(size, &view, &envelope_editor); } } // a little hack for now - if(editor.mode == MODE_LAYERS) + if(mode == MODE_LAYERS) do_map_editor(view, toolbar); - if(editor.gui_active) + if(gui_active) { float brightness = 0.25f; render_background(menubar, background_texture, 128.0f, brightness*0); - ui_margin(&menubar, 2.0f, &menubar); + menubar.Margin(2.0f, &menubar); render_background(toolbox, background_texture, 128.0f, brightness); - ui_margin(&toolbox, 2.0f, &toolbox); + toolbox.Margin(2.0f, &toolbox); render_background(toolbar, background_texture, 128.0f, brightness); - ui_margin(&toolbar, 2.0f, &toolbar); - ui_vsplit_l(&toolbar, 150.0f, &modebar, &toolbar); + toolbar.Margin(2.0f, &toolbar); + toolbar.VSplitLeft(150.0f, &modebar, &toolbar); render_background(statusbar, background_texture, 128.0f, brightness); - ui_margin(&statusbar, 2.0f, &statusbar); + statusbar.Margin(2.0f, &statusbar); // do the toolbar - if(editor.mode == MODE_LAYERS) + if(mode == MODE_LAYERS) do_toolbar(toolbar); - if(editor.show_envelope_editor) + if(show_envelope_editor) { render_background(envelope_editor, background_texture, 128.0f, brightness); - ui_margin(&envelope_editor, 2.0f, &envelope_editor); + envelope_editor.Margin(2.0f, &envelope_editor); } } - if(editor.mode == MODE_LAYERS) + if(mode == MODE_LAYERS) render_layers(toolbox, toolbar, view); - else if(editor.mode == MODE_IMAGES) + else if(mode == MODE_IMAGES) render_images(toolbox, toolbar, view); - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); - if(editor.gui_active) + if(gui_active) { render_menubar(menubar); render_modebar(modebar); - if(editor.show_envelope_editor) + if(show_envelope_editor) render_envelopeeditor(envelope_editor); } - if(editor.dialog == DIALOG_FILE) + if(dialog == DIALOG_FILE) { static int null_ui_target = 0; - ui_set_hot_item(&null_ui_target); + UI()->SetHotItem(&null_ui_target); render_file_dialog(); } ui_do_popup_menu(); - if(editor.gui_active) + if(gui_active) render_statusbar(statusbar); // if(config.ed_showkeys) { - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); TEXT_CURSOR cursor; gfx_text_set_cursor(&cursor, view.x+10, view.y+view.h-24-10, 24.0f, TEXTFLAG_RENDER); @@ -2569,28 +2730,28 @@ void EDITOR::render() } } - if (editor.show_mouse_pointer) + if(show_mouse_pointer) { // render butt ugly mouse cursor - float mx = ui_mouse_x(); - float my = ui_mouse_y(); - gfx_texture_set(cursor_texture); - gfx_quads_begin(); - if(ui_got_context == ui_hot_item()) - gfx_setcolor(1,0,0,1); - gfx_quads_drawTL(mx,my, 16.0f, 16.0f); - gfx_quads_end(); + float mx = UI()->MouseX(); + float my = UI()->MouseY(); + Graphics()->TextureSet(cursor_texture); + Graphics()->QuadsBegin(); + if(ui_got_context == UI()->HotItem()) + Graphics()->SetColor(1,0,0,1); + Graphics()->QuadsDrawTL(mx,my, 16.0f, 16.0f); + Graphics()->QuadsEnd(); } } void EDITOR::reset(bool create_default) { - editor.map.clean(); + map.clean(); // create default layers if(create_default) - editor.map.create_default(entities_texture); + map.create_default(entities_texture); /* { @@ -2636,28 +2797,30 @@ void MAP::create_default(int entities_texture) game_group->add_layer(game_layer); } -extern "C" void editor_init() +void EDITOR::Init(class IGraphics *pGraphics) { - checker_texture = gfx_load_texture("editor/checker.png", IMG_AUTO, 0); - background_texture = gfx_load_texture("editor/background.png", IMG_AUTO, 0); - cursor_texture = gfx_load_texture("editor/cursor.png", IMG_AUTO, 0); - entities_texture = gfx_load_texture("editor/entities.png", IMG_AUTO, 0); + m_pGraphics = pGraphics; + + checker_texture = Graphics()->LoadTexture("editor/checker.png", IMG_AUTO, 0); + background_texture = Graphics()->LoadTexture("editor/background.png", IMG_AUTO, 0); + cursor_texture = Graphics()->LoadTexture("editor/cursor.png", IMG_AUTO, 0); + entities_texture = Graphics()->LoadTexture("editor/entities.png", IMG_AUTO, 0); tileset_picker.make_palette(); tileset_picker.readonly = true; - editor.reset(); + reset(); } -extern "C" void editor_update_and_render() +void EDITOR::UpdateAndRender() { static int mouse_x = 0; static int mouse_y = 0; - if(editor.animate) - editor.animate_time = (time_get()-editor.animate_start)/(float)time_freq(); + if(animate) + animate_time = (time_get()-animate_start)/(float)time_freq(); else - editor.animate_time = 0; + animate_time = 0; ui_got_context = 0; // handle mouse movement @@ -2665,10 +2828,10 @@ extern "C" void editor_update_and_render() int rx, ry; { inp_mouse_relative(&rx, &ry); - editor.mouse_delta_x = rx; - editor.mouse_delta_y = ry; + mouse_delta_x = rx; + mouse_delta_y = ry; - if(!editor.lock_mouse) + if(!lock_mouse) { mouse_x += rx; mouse_y += ry; @@ -2676,8 +2839,8 @@ extern "C" void editor_update_and_render() if(mouse_x < 0) mouse_x = 0; if(mouse_y < 0) mouse_y = 0; - if(mouse_x > ui_screen()->w) mouse_x = (int)ui_screen()->w; - if(mouse_y > ui_screen()->h) mouse_y = (int)ui_screen()->h; + if(mouse_x > UI()->Screen()->w) mouse_x = (int)UI()->Screen()->w; + if(mouse_y > UI()->Screen()->h) mouse_y = (int)UI()->Screen()->h; // update the ui mx = mouse_x; @@ -2686,7 +2849,7 @@ extern "C" void editor_update_and_render() mwy = 0; // fix correct world x and y - LAYERGROUP *g = editor.get_selected_group(); + LAYERGROUP *g = get_selected_group(); if(g) { float points[4]; @@ -2695,10 +2858,10 @@ extern "C" void editor_update_and_render() float world_width = points[2]-points[0]; float world_height = points[3]-points[1]; - mwx = points[0] + world_width * (mouse_x/ui_screen()->w); - mwy = points[1] + world_height * (mouse_y/ui_screen()->h); - editor.mouse_delta_wx = editor.mouse_delta_x*(world_width / ui_screen()->w); - editor.mouse_delta_wy = editor.mouse_delta_y*(world_height / ui_screen()->h); + mwx = points[0] + world_width * (mouse_x/UI()->Screen()->w); + mwy = points[1] + world_height * (mouse_y/UI()->Screen()->h); + mouse_delta_wx = mouse_delta_x*(world_width / UI()->Screen()->w); + mouse_delta_wy = mouse_delta_y*(world_height / UI()->Screen()->h); } int buttons = 0; @@ -2706,33 +2869,34 @@ extern "C" void editor_update_and_render() if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2; if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4; - ui_update(mx,my,mwx,mwy,buttons); + UI()->Update(mx,my,mwx,mwy,buttons); } // toggle gui if(inp_key_down(KEY_TAB)) - editor.gui_active = !editor.gui_active; + gui_active = !gui_active; if(inp_key_down(KEY_F5)) - editor.save("maps/debug_test2.map"); + save("maps/debug_test2.map"); if(inp_key_down(KEY_F6)) - editor.load("maps/debug_test2.map"); + load("maps/debug_test2.map"); if(inp_key_down(KEY_F8)) - editor.load("maps/debug_test.map"); + load("maps/debug_test.map"); if(inp_key_down(KEY_F10)) - editor.show_mouse_pointer = false; + show_mouse_pointer = false; - editor.render(); + render(); if(inp_key_down(KEY_F10)) { - gfx_screenshot(); - editor.show_mouse_pointer = true; + Graphics()->TakeScreenshot(); + show_mouse_pointer = true; } inp_clear_events(); } +IEditor *CreateEditor() { return new EDITOR; } diff --git a/src/game/editor/ed_editor.hpp b/src/game/editor/ed_editor.hpp index 313a381f..98d1d960 100644 --- a/src/game/editor/ed_editor.hpp +++ b/src/game/editor/ed_editor.hpp @@ -9,16 +9,17 @@ #include "../mapitems.hpp" #include "../client/render.hpp" -extern "C" { - #include - #include - #include -} +#include +#include +#include +#include #include typedef void (*INDEX_MODIFY_FUNC)(int *index); +//CRenderTools m_RenderTools; + // EDITOR SPECIFIC template void swap(T &a, T &b) @@ -96,7 +97,7 @@ public: int eval(float time, float *result) { - render_eval_envelope(points.getptr(), points.len(), channels, time, result); + CRenderTools::render_eval_envelope(points.getptr(), points.len(), channels, time, result); return channels; } @@ -129,6 +130,9 @@ class MAP; class LAYER { public: + class EDITOR *editor; + class IGraphics *Graphics(); + LAYER() { type = LAYERTYPE_INVALID; @@ -136,6 +140,7 @@ public: visible = true; readonly = false; flags = 0; + editor = 0; } virtual ~LAYER() @@ -143,8 +148,8 @@ public: } - virtual void brush_selecting(RECT rect) {} - virtual int brush_grab(LAYERGROUP *brush, RECT rect) { return 0; } + virtual void brush_selecting(CUIRect rect) {} + virtual int brush_grab(LAYERGROUP *brush, CUIRect rect) { return 0; } virtual void brush_draw(LAYER *brush, float x, float y) {} virtual void brush_place(LAYER *brush, float x, float y) {} virtual void brush_flip_x() {} @@ -152,7 +157,7 @@ public: virtual void brush_rotate(float amount) {} virtual void render() {} - virtual int render_properties(RECT *toolbox) { return 0; } + virtual int render_properties(CUIRect *toolbox) { return 0; } virtual void modify_image_index(INDEX_MODIFY_FUNC func) {} virtual void modify_envelope_index(INDEX_MODIFY_FUNC func) {} @@ -170,6 +175,8 @@ public: class LAYERGROUP { public: + class MAP *m_pMap; + array layers; int offset_x; @@ -191,7 +198,7 @@ public: LAYERGROUP(); ~LAYERGROUP(); - void convert(RECT *rect); + void convert(CUIRect *rect); void render(); void mapscreen(); void mapping(float *points); @@ -221,8 +228,11 @@ public: class EDITOR_IMAGE : public IMAGE_INFO { public: - EDITOR_IMAGE() + EDITOR *editor; + + EDITOR_IMAGE(EDITOR *ed) { + editor = editor; tex_id = -1; name[0] = 0; external = 0; @@ -232,10 +242,7 @@ public: format = 0; } - ~EDITOR_IMAGE() - { - gfx_unload_texture(tex_id); - } + ~EDITOR_IMAGE(); void analyse_tileflags(); @@ -250,6 +257,8 @@ class MAP void make_game_group(LAYERGROUP *group); void make_game_layer(LAYER *layer); public: + EDITOR *editor; + MAP() { clean(); @@ -272,6 +281,7 @@ public: LAYERGROUP *new_group() { LAYERGROUP *g = new LAYERGROUP; + g->m_pMap = this; groups.add(g); return g; } @@ -333,9 +343,17 @@ enum PROPTYPE_ENVELOPE, }; -class EDITOR +class EDITOR : public IEditor { -public: + class IGraphics *m_pGraphics; + CRenderTools m_RenderTools; + CUI m_UI; +public: + + class IGraphics *Graphics() { return m_pGraphics; }; + CUI *UI() { return &m_UI; } + CRenderTools *RenderTools() { return &m_RenderTools; } + EDITOR() { mode = MODE_LAYERS; @@ -368,9 +386,12 @@ public: show_envelope_editor = 0; } + virtual void Init(class IGraphics *pGraphics); + virtual void UpdateAndRender(); + void invoke_file_dialog(int listdir_type, const char *title, const char *button_text, const char *basepath, const char *default_name, - void (*func)(const char *filename)); + void (*func)(const char *filename, void *user), void *user); void reset(bool create_default=true); int save(const char *filename); @@ -383,7 +404,7 @@ public: LAYER *get_selected_layer(int index); LAYERGROUP *get_selected_group(); - int do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val); + int do_properties(CUIRect *toolbox, PROPERTY *props, int *ids, int *new_val); int mode; int dialog; @@ -420,9 +441,70 @@ public: int selected_image; MAP map; + + int DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + int DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + + int DoButton_ButtonL(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + int DoButton_ButtonM(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + int DoButton_ButtonR(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + int DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + int DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + + int DoButton_File(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + + int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + int DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags=0, const char *pToolTip=0); + + int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden=false); + //static void draw_editor_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + //static void draw_editor_button_menuitem(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + + void render_background(CUIRect view, int texture, float size, float brightness); + + void ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(EDITOR *pEditor, CUIRect rect), void *extra=0); + void ui_do_popup_menu(); + + int ui_do_value_selector(void *id, CUIRect *r, const char *label, int current, int min, int max, float scale); + + static int popup_group(EDITOR *pEditor, CUIRect view); + static int popup_layer(EDITOR *pEditor, CUIRect view); + static int popup_quad(EDITOR *pEditor, CUIRect view); + static int popup_point(EDITOR *pEditor, CUIRect view); + static int popup_select_image(EDITOR *pEditor, CUIRect view); + static int popup_image(EDITOR *pEditor, CUIRect view); + static int popup_menu_file(EDITOR *pEditor, CUIRect view); + + + void popup_select_image_invoke(int current, float x, float y); + int popup_select_image_result(); + + vec4 button_color_mul(const void *id); + + void do_quad_point(QUAD *q, int quad_index, int v); + void do_map_editor(CUIRect view, CUIRect toolbar); + void do_toolbar(CUIRect toolbar); + void do_quad(QUAD *q, int index); + float ui_do_scrollbar_v(const void *id, const CUIRect *rect, float current); + vec4 get_button_color(const void *id, int checked); + + static void replace_image(const char *filename, void *user); + static void add_image(const char *filename, void *user); + + void render_images(CUIRect toolbox, CUIRect toolbar, CUIRect view); + void render_layers(CUIRect toolbox, CUIRect toolbar, CUIRect view); + void render_modebar(CUIRect view); + void render_statusbar(CUIRect view); + void render_envelopeeditor(CUIRect view); + + void render_menubar(CUIRect menubar); + void render_file_dialog(); }; -extern EDITOR editor; +// make sure to inline this function +inline class IGraphics *LAYER::Graphics() { return editor->Graphics(); } + +//extern EDITOR editor; typedef struct { @@ -443,17 +525,17 @@ public: int convert_x(float x) const; int convert_y(float y) const; - void convert(RECT rect, RECTi *out); - void snap(RECT *rect); + void convert(CUIRect rect, RECTi *out); + void snap(CUIRect *rect); void clamp(RECTi *rect); - virtual void brush_selecting(RECT rect); - virtual int brush_grab(LAYERGROUP *brush, RECT rect); + virtual void brush_selecting(CUIRect rect); + virtual int brush_grab(LAYERGROUP *brush, CUIRect rect); virtual void brush_draw(LAYER *brush, float wx, float wy); virtual void brush_flip_x(); virtual void brush_flip_y(); - virtual int render_properties(RECT *toolbox); + virtual int render_properties(CUIRect *toolbox); virtual void modify_image_index(INDEX_MODIFY_FUNC func); virtual void modify_envelope_index(INDEX_MODIFY_FUNC func); @@ -479,14 +561,14 @@ public: virtual void render(); QUAD *new_quad(); - virtual void brush_selecting(RECT rect); - virtual int brush_grab(LAYERGROUP *brush, RECT rect); + virtual void brush_selecting(CUIRect rect); + virtual int brush_grab(LAYERGROUP *brush, CUIRect rect); virtual void brush_place(LAYER *brush, float wx, float wy); virtual void brush_flip_x(); virtual void brush_flip_y(); virtual void brush_rotate(float amount); - virtual int render_properties(RECT *toolbox); + virtual int render_properties(CUIRect *toolbox); virtual void modify_image_index(INDEX_MODIFY_FUNC func); virtual void modify_envelope_index(INDEX_MODIFY_FUNC func); @@ -503,20 +585,5 @@ public: LAYER_GAME(int w, int h); ~LAYER_GAME(); - virtual int render_properties(RECT *toolbox); + virtual int render_properties(CUIRect *toolbox); }; - -int do_editor_button(const void *id, const char *text, int checked, const RECT *r, ui_draw_button_func draw_func, int flags, const char *tooltip); -void draw_editor_button(const void *id, const char *text, int checked, const RECT *r, const void *extra); -void draw_editor_button_menuitem(const void *id, const char *text, int checked, const RECT *r, const void *extra); - -void ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(RECT rect), void *extra=0); -void ui_do_popup_menu(); - -int popup_group(RECT view); -int popup_layer(RECT view); -int popup_quad(RECT view); -int popup_point(RECT view); - -void popup_select_image_invoke(int current, float x, float y); -int popup_select_image_result(); diff --git a/src/game/editor/ed_io.cpp b/src/game/editor/ed_io.cpp index dfaa0e2c..b8c025fb 100644 --- a/src/game/editor/ed_io.cpp +++ b/src/game/editor/ed_io.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "ed_editor.hpp" template @@ -7,6 +8,7 @@ static int make_version(int i, const T &v) { return (i<<16)+sizeof(T); } // backwards compatiblity +/* void editor_load_old(DATAFILE *df, MAP *map) { class mapres_image @@ -98,7 +100,7 @@ void editor_load_old(DATAFILE *df, MAP *map) // move game layer to correct position for(int i = 0; i < map->groups[0]->layers.len()-1; i++) { - if(map->groups[0]->layers[i] == editor.map.game_layer) + if(map->groups[0]->layers[i] == pEditor->map.game_layer) map->groups[0]->swap_layers(i, i+1); } @@ -141,7 +143,7 @@ void editor_load_old(DATAFILE *df, MAP *map) // copy image data img->data = mem_alloc(img->width*img->height*4, 1); mem_copy(img->data, data, img->width*img->height*4); - img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data, IMG_AUTO, 0); + img->tex_id = Graphics()->LoadTextureRaw(img->width, img->height, img->format, img->data, IMG_AUTO, 0); map->images.add(img); // unload image @@ -186,7 +188,7 @@ void editor_load_old(DATAFILE *df, MAP *map) } } } -} +}*/ int EDITOR::save(const char *filename) { @@ -374,9 +376,10 @@ int MAP::load(const char *filename) if(!item) { // import old map - MAP old_mapstuff; - editor.reset(); + /*MAP old_mapstuff; + editor->reset(); editor_load_old(df, this); + */ } else if(item->version == 1) { @@ -392,7 +395,7 @@ int MAP::load(const char *filename) char *name = (char *)datafile_get_data(df, item->image_name); // copy base info - EDITOR_IMAGE *img = new EDITOR_IMAGE; + EDITOR_IMAGE *img = new EDITOR_IMAGE(editor); img->external = item->external; if(item->external) @@ -401,11 +404,11 @@ int MAP::load(const char *filename) sprintf(buf, "mapres/%s.png", name); // load external - EDITOR_IMAGE imginfo; - if(gfx_load_png(&imginfo, buf)) + EDITOR_IMAGE imginfo(editor); + if(editor->Graphics()->LoadPNG(&imginfo, buf)) { *img = imginfo; - img->tex_id = gfx_load_texture_raw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO, 0); + img->tex_id = editor->Graphics()->LoadTextureRaw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO, 0); img->external = 1; } } @@ -419,7 +422,7 @@ int MAP::load(const char *filename) void *data = datafile_get_data(df, item->image_data); img->data = mem_alloc(img->width*img->height*4, 1); mem_copy(img->data, data, img->width*img->height*4); - img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data, IMG_AUTO, 0); + img->tex_id = editor->Graphics()->LoadTextureRaw(img->width, img->height, img->format, img->data, IMG_AUTO, 0); } // copy image name diff --git a/src/game/editor/ed_layer_game.cpp b/src/game/editor/ed_layer_game.cpp index 60825ec3..9010bc71 100644 --- a/src/game/editor/ed_layer_game.cpp +++ b/src/game/editor/ed_layer_game.cpp @@ -12,7 +12,7 @@ LAYER_GAME::~LAYER_GAME() { } -int LAYER_GAME::render_properties(RECT *toolbox) +int LAYER_GAME::render_properties(CUIRect *toolbox) { int r = LAYER_TILES::render_properties(toolbox); image = -1; diff --git a/src/game/editor/ed_layer_quads.cpp b/src/game/editor/ed_layer_quads.cpp index bbe5fb6d..ce1ba4b6 100644 --- a/src/game/editor/ed_layer_quads.cpp +++ b/src/game/editor/ed_layer_quads.cpp @@ -1,5 +1,7 @@ #include +#include + #include "ed_editor.hpp" #include #include @@ -15,9 +17,10 @@ LAYER_QUADS::~LAYER_QUADS() { } -static void envelope_eval(float time_offset, int env, float *channels) +static void envelope_eval(float time_offset, int env, float *channels, void *user) { - if(env < 0 || env > editor.map.envelopes.len()) + EDITOR *pEditor = (EDITOR *)user; + if(env < 0 || env > pEditor->map.envelopes.len()) { channels[0] = 0; channels[1] = 0; @@ -26,19 +29,19 @@ static void envelope_eval(float time_offset, int env, float *channels) return; } - ENVELOPE *e = editor.map.envelopes[env]; - float t = editor.animate_time+time_offset; - t *= editor.animate_speed; + ENVELOPE *e = pEditor->map.envelopes[env]; + float t = pEditor->animate_time+time_offset; + t *= pEditor->animate_speed; e->eval(t, channels); } void LAYER_QUADS::render() { - gfx_texture_set(-1); - if(image >= 0 && image < editor.map.images.len()) - gfx_texture_set(editor.map.images[image]->tex_id); + Graphics()->TextureSet(-1); + if(image >= 0 && image < editor->map.images.len()) + Graphics()->TextureSet(editor->map.images[image]->tex_id); - render_quads(quads.getptr(), quads.len(), envelope_eval, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT); + editor->RenderTools()->render_quads(quads.getptr(), quads.len(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, envelope_eval, editor); } QUAD *LAYER_QUADS::new_quad() @@ -89,19 +92,19 @@ QUAD *LAYER_QUADS::new_quad() return q; } -void LAYER_QUADS::brush_selecting(RECT rect) +void LAYER_QUADS::brush_selecting(CUIRect rect) { // draw selection rectangle - gfx_texture_set(-1); - gfx_lines_begin(); - gfx_lines_draw(rect.x, rect.y, rect.x+rect.w, rect.y); - gfx_lines_draw(rect.x+rect.w, rect.y, rect.x+rect.w, rect.y+rect.h); - gfx_lines_draw(rect.x+rect.w, rect.y+rect.h, rect.x, rect.y+rect.h); - gfx_lines_draw(rect.x, rect.y+rect.h, rect.x, rect.y); - gfx_lines_end(); + Graphics()->TextureSet(-1); + Graphics()->LinesBegin(); + Graphics()->LinesDraw(rect.x, rect.y, rect.x+rect.w, rect.y); + Graphics()->LinesDraw(rect.x+rect.w, rect.y, rect.x+rect.w, rect.y+rect.h); + Graphics()->LinesDraw(rect.x+rect.w, rect.y+rect.h, rect.x, rect.y+rect.h); + Graphics()->LinesDraw(rect.x, rect.y+rect.h, rect.x, rect.y); + Graphics()->LinesEnd(); } -int LAYER_QUADS::brush_grab(LAYERGROUP *brush, RECT rect) +int LAYER_QUADS::brush_grab(LAYERGROUP *brush, CUIRect rect) { // create new layers LAYER_QUADS *grabbed = new LAYER_QUADS(); @@ -204,7 +207,7 @@ void LAYER_QUADS::get_size(float *w, float *h) extern int selected_points; -int LAYER_QUADS::render_properties(RECT *toolbox) +int LAYER_QUADS::render_properties(CUIRect *toolbox) { // layer props enum @@ -220,12 +223,12 @@ int LAYER_QUADS::render_properties(RECT *toolbox) static int ids[NUM_PROPS] = {0}; int new_val = 0; - int prop = editor.do_properties(toolbox, props, ids, &new_val); + int prop = editor->do_properties(toolbox, props, ids, &new_val); if(prop == PROP_IMAGE) { if(new_val >= 0) - image = new_val%editor.map.images.len(); + image = new_val%editor->map.images.len(); else image = -1; } diff --git a/src/game/editor/ed_layer_tiles.cpp b/src/game/editor/ed_layer_tiles.cpp index 80ccaa3a..0d42cb78 100644 --- a/src/game/editor/ed_layer_tiles.cpp +++ b/src/game/editor/ed_layer_tiles.cpp @@ -1,5 +1,7 @@ #include +#include + #include #include #include "ed_editor.hpp" @@ -33,7 +35,7 @@ void LAYER_TILES::prepare_for_save() { for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) - tiles[y*width+x].flags |= editor.map.images[image]->tileflags[tiles[y*width+x].index]; + tiles[y*width+x].flags |= editor->map.images[image]->tileflags[tiles[y*width+x].index]; } } @@ -46,16 +48,16 @@ void LAYER_TILES::make_palette() void LAYER_TILES::render() { - if(image >= 0 && image < editor.map.images.len()) - tex_id = editor.map.images[image]->tex_id; - gfx_texture_set(tex_id); - render_tilemap(tiles, width, height, 32.0f, vec4(1,1,1,1), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT); + if(image >= 0 && image < editor->map.images.len()) + tex_id = editor->map.images[image]->tex_id; + Graphics()->TextureSet(tex_id); + editor->RenderTools()->render_tilemap(tiles, width, height, 32.0f, vec4(1,1,1,1), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT); } int LAYER_TILES::convert_x(float x) const { return (int)(x/32.0f); } int LAYER_TILES::convert_y(float y) const { return (int)(y/32.0f); } -void LAYER_TILES::convert(RECT rect, RECTi *out) +void LAYER_TILES::convert(CUIRect rect, RECTi *out) { out->x = convert_x(rect.x); out->y = convert_y(rect.y); @@ -63,7 +65,7 @@ void LAYER_TILES::convert(RECT rect, RECTi *out) out->h = convert_y(rect.y+rect.h+31) - out->y; } -void LAYER_TILES::snap(RECT *rect) +void LAYER_TILES::snap(CUIRect *rect) { RECTi out; convert(*rect, &out); @@ -99,20 +101,20 @@ void LAYER_TILES::clamp(RECTi *rect) rect->w = 0; } -void LAYER_TILES::brush_selecting(RECT rect) +void LAYER_TILES::brush_selecting(CUIRect rect) { - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_setcolor(1,1,1,0.4f); + Graphics()->TextureSet(-1); + editor->Graphics()->QuadsBegin(); + editor->Graphics()->SetColor(1,1,1,0.4f); snap(&rect); - gfx_quads_drawTL(rect.x, rect.y, rect.w, rect.h); - gfx_quads_end(); + editor->Graphics()->QuadsDrawTL(rect.x, rect.y, rect.w, rect.h); + editor->Graphics()->QuadsEnd(); char buf[16]; str_format(buf, sizeof(buf), "%d,%d", convert_x(rect.w), convert_y(rect.h)); - gfx_text(0, rect.x+3.0f, rect.y+3.0f, 15.0f*editor.world_zoom, buf, -1); + gfx_text(0, rect.x+3.0f, rect.y+3.0f, 15.0f*editor->world_zoom, buf, -1); } -int LAYER_TILES::brush_grab(LAYERGROUP *brush, RECT rect) +int LAYER_TILES::brush_grab(LAYERGROUP *brush, CUIRect rect) { RECTi r; convert(rect, &r); @@ -204,17 +206,17 @@ void LAYER_TILES::resize(int new_w, int new_h) } -int LAYER_TILES::render_properties(RECT *toolbox) +int LAYER_TILES::render_properties(CUIRect *toolbox) { - RECT button; - ui_hsplit_b(toolbox, 12.0f, toolbox, &button); - bool in_gamegroup = editor.map.game_group->layers.find(this) != -1; - if(editor.map.game_layer == this) + CUIRect button; + toolbox->HSplitBottom(12.0f, toolbox, &button); + bool in_gamegroup = editor->map.game_group->layers.find(this) != -1; + if(editor->map.game_layer == this) in_gamegroup = false; static int colcl_button = 0; - if(do_editor_button(&colcl_button, "Clear Collision", in_gamegroup?0:-1, &button, draw_editor_button, 0, "Removes collision from this layer")) + if(editor->DoButton_Editor(&colcl_button, "Clear Collision", in_gamegroup?0:-1, &button, 0, "Removes collision from this layer")) { - LAYER_TILES *gl = editor.map.game_layer; + LAYER_TILES *gl = editor->map.game_layer; int w = min(gl->width, width); int h = min(gl->height, height); for(int y = 0; y < h; y++) @@ -228,11 +230,11 @@ int LAYER_TILES::render_properties(RECT *toolbox) return 1; } static int col_button = 0; - ui_hsplit_b(toolbox, 5.0f, toolbox, &button); - ui_hsplit_b(toolbox, 12.0f, toolbox, &button); - if(do_editor_button(&col_button, "Make Collision", in_gamegroup?0:-1, &button, draw_editor_button, 0, "Constructs collision from this layer")) + toolbox->HSplitBottom(5.0f, toolbox, &button); + toolbox->HSplitBottom(12.0f, toolbox, &button); + if(editor->DoButton_Editor(&col_button, "Make Collision", in_gamegroup?0:-1, &button, 0, "Constructs collision from this layer")) { - LAYER_TILES *gl = editor.map.game_layer; + LAYER_TILES *gl = editor->map.game_layer; int w = min(gl->width, width); int h = min(gl->height, height); for(int y = 0; y < h; y++) @@ -260,12 +262,12 @@ int LAYER_TILES::render_properties(RECT *toolbox) {0}, }; - if(editor.map.game_layer == this) // remove the image from the selection if this is the game layer + if(editor->map.game_layer == this) // remove the image from the selection if this is the game layer props[2].name = 0; static int ids[NUM_PROPS] = {0}; int new_val = 0; - int prop = editor.do_properties(toolbox, props, ids, &new_val); + int prop = editor->do_properties(toolbox, props, ids, &new_val); if(prop == PROP_WIDTH && new_val > 1) resize(new_val, height); @@ -279,7 +281,7 @@ int LAYER_TILES::render_properties(RECT *toolbox) image = -1; } else - image = new_val%editor.map.images.len(); + image = new_val%editor->map.images.len(); } return 0; diff --git a/src/game/editor/ed_popups.cpp b/src/game/editor/ed_popups.cpp index 13bfe243..59140153 100644 --- a/src/game/editor/ed_popups.cpp +++ b/src/game/editor/ed_popups.cpp @@ -1,20 +1,21 @@ #include +#include #include "ed_editor.hpp" // popup menu handling static struct { - RECT rect; + CUIRect rect; void *id; - int (*func)(RECT rect); + int (*func)(EDITOR *pEditor, CUIRect rect); int is_menu; void *extra; } ui_popups[8]; static int ui_num_popups = 0; -void ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(RECT rect), void *extra) +void EDITOR::ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(EDITOR *pEditor, CUIRect rect), void *extra) { dbg_msg("", "invoked"); ui_popups[ui_num_popups].id = id; @@ -28,39 +29,39 @@ void ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float ui_num_popups++; } -void ui_do_popup_menu() +void EDITOR::ui_do_popup_menu() { for(int i = 0; i < ui_num_popups; i++) { - bool inside = ui_mouse_inside(&ui_popups[i].rect); - ui_set_hot_item(&ui_popups[i].id); + bool inside = UI()->MouseInside(&ui_popups[i].rect); + UI()->SetHotItem(&ui_popups[i].id); - if(ui_active_item() == &ui_popups[i].id) + if(UI()->ActiveItem() == &ui_popups[i].id) { - if(!ui_mouse_button(0)) + if(!UI()->MouseButton(0)) { if(!inside) ui_num_popups--; - ui_set_active_item(0); + UI()->SetActiveItem(0); } } - else if(ui_hot_item() == &ui_popups[i].id) + else if(UI()->HotItem() == &ui_popups[i].id) { - if(ui_mouse_button(0)) - ui_set_active_item(&ui_popups[i].id); + if(UI()->MouseButton(0)) + UI()->SetActiveItem(&ui_popups[i].id); } - int corners = CORNER_ALL; + int corners = CUI::CORNER_ALL; if(ui_popups[i].is_menu) - corners = CORNER_R|CORNER_B; + corners = CUI::CORNER_R|CUI::CORNER_B; - RECT r = ui_popups[i].rect; - ui_draw_rect(&r, vec4(0.5f,0.5f,0.5f,0.75f), corners, 3.0f); - ui_margin(&r, 1.0f, &r); - ui_draw_rect(&r, vec4(0,0,0,0.75f), corners, 3.0f); - ui_margin(&r, 4.0f, &r); + CUIRect r = ui_popups[i].rect; + RenderTools()->DrawUIRect(&r, vec4(0.5f,0.5f,0.5f,0.75f), corners, 3.0f); + r.Margin(1.0f, &r); + RenderTools()->DrawUIRect(&r, vec4(0,0,0,0.75f), corners, 3.0f); + r.Margin(4.0f, &r); - if(ui_popups[i].func(r)) + if(ui_popups[i].func(this, r)) ui_num_popups--; if(inp_key_down(KEY_ESCAPE)) @@ -69,42 +70,42 @@ void ui_do_popup_menu() } -int popup_group(RECT view) +int EDITOR::popup_group(EDITOR *pEditor, CUIRect view) { // remove group button - RECT button; - ui_hsplit_b(&view, 12.0f, &view, &button); + CUIRect button; + view.HSplitBottom(12.0f, &view, &button); static int delete_button = 0; // don't allow deletion of game group - if(editor.map.game_group != editor.get_selected_group() && - do_editor_button(&delete_button, "Delete Group", 0, &button, draw_editor_button, 0, "Delete group")) + if(pEditor->map.game_group != pEditor->get_selected_group() && + pEditor->DoButton_Editor(&delete_button, "Delete Group", 0, &button, 0, "Delete group")) { - editor.map.delete_group(editor.selected_group); + pEditor->map.delete_group(pEditor->selected_group); return 1; } // new tile layer - ui_hsplit_b(&view, 10.0f, &view, &button); - ui_hsplit_b(&view, 12.0f, &view, &button); + view.HSplitBottom(10.0f, &view, &button); + view.HSplitBottom(12.0f, &view, &button); static int new_quad_layer_button = 0; - if(do_editor_button(&new_quad_layer_button, "Add Quads Layer", 0, &button, draw_editor_button, 0, "Creates a new quad layer")) + if(pEditor->DoButton_Editor(&new_quad_layer_button, "Add Quads Layer", 0, &button, 0, "Creates a new quad layer")) { LAYER *l = new LAYER_QUADS; - editor.map.groups[editor.selected_group]->add_layer(l); - editor.selected_layer = editor.map.groups[editor.selected_group]->layers.len()-1; + pEditor->map.groups[pEditor->selected_group]->add_layer(l); + pEditor->selected_layer = pEditor->map.groups[pEditor->selected_group]->layers.len()-1; return 1; } // new quad layer - ui_hsplit_b(&view, 5.0f, &view, &button); - ui_hsplit_b(&view, 12.0f, &view, &button); + view.HSplitBottom(5.0f, &view, &button); + view.HSplitBottom(12.0f, &view, &button); static int new_tile_layer_button = 0; - if(do_editor_button(&new_tile_layer_button, "Add Tile Layer", 0, &button, draw_editor_button, 0, "Creates a new tile layer")) + if(pEditor->DoButton_Editor(&new_tile_layer_button, "Add Tile Layer", 0, &button, 0, "Creates a new tile layer")) { LAYER *l = new LAYER_TILES(50, 50); - editor.map.groups[editor.selected_group]->add_layer(l); - editor.selected_layer = editor.map.groups[editor.selected_group]->layers.len()-1; + pEditor->map.groups[pEditor->selected_group]->add_layer(l); + pEditor->selected_layer = pEditor->map.groups[pEditor->selected_group]->layers.len()-1; return 1; } @@ -124,17 +125,17 @@ int popup_group(RECT view) }; PROPERTY props[] = { - {"Order", editor.selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1}, - {"Pos X", -editor.map.groups[editor.selected_group]->offset_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Pos Y", -editor.map.groups[editor.selected_group]->offset_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Para X", editor.map.groups[editor.selected_group]->parallax_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Para Y", editor.map.groups[editor.selected_group]->parallax_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Order", pEditor->selected_group, PROPTYPE_INT_STEP, 0, pEditor->map.groups.len()-1}, + {"Pos X", -pEditor->map.groups[pEditor->selected_group]->offset_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Pos Y", -pEditor->map.groups[pEditor->selected_group]->offset_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Para X", pEditor->map.groups[pEditor->selected_group]->parallax_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Para Y", pEditor->map.groups[pEditor->selected_group]->parallax_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Use Clipping", editor.map.groups[editor.selected_group]->use_clipping, PROPTYPE_BOOL, 0, 1}, - {"Clip X", editor.map.groups[editor.selected_group]->clip_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Clip Y", editor.map.groups[editor.selected_group]->clip_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Clip W", editor.map.groups[editor.selected_group]->clip_w, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Clip H", editor.map.groups[editor.selected_group]->clip_h, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Use Clipping", pEditor->map.groups[pEditor->selected_group]->use_clipping, PROPTYPE_BOOL, 0, 1}, + {"Clip X", pEditor->map.groups[pEditor->selected_group]->clip_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Clip Y", pEditor->map.groups[pEditor->selected_group]->clip_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Clip W", pEditor->map.groups[pEditor->selected_group]->clip_w, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Clip H", pEditor->map.groups[pEditor->selected_group]->clip_h, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {0}, }; @@ -142,49 +143,49 @@ int popup_group(RECT view) int new_val = 0; // cut the properties that isn't needed - if(editor.get_selected_group()->game_group) + if(pEditor->get_selected_group()->game_group) props[PROP_POS_X].name = 0; - int prop = editor.do_properties(&view, props, ids, &new_val); + int prop = pEditor->do_properties(&view, props, ids, &new_val); if(prop == PROP_ORDER) - editor.selected_group = editor.map.swap_groups(editor.selected_group, new_val); + pEditor->selected_group = pEditor->map.swap_groups(pEditor->selected_group, new_val); // these can not be changed on the game group - if(!editor.get_selected_group()->game_group) + if(!pEditor->get_selected_group()->game_group) { - if(prop == PROP_PARA_X) editor.map.groups[editor.selected_group]->parallax_x = new_val; - else if(prop == PROP_PARA_Y) editor.map.groups[editor.selected_group]->parallax_y = new_val; - else if(prop == PROP_POS_X) editor.map.groups[editor.selected_group]->offset_x = -new_val; - else if(prop == PROP_POS_Y) editor.map.groups[editor.selected_group]->offset_y = -new_val; - else if(prop == PROP_USE_CLIPPING) editor.map.groups[editor.selected_group]->use_clipping = new_val; - else if(prop == PROP_CLIP_X) editor.map.groups[editor.selected_group]->clip_x = new_val; - else if(prop == PROP_CLIP_Y) editor.map.groups[editor.selected_group]->clip_y = new_val; - else if(prop == PROP_CLIP_W) editor.map.groups[editor.selected_group]->clip_w = new_val; - else if(prop == PROP_CLIP_H) editor.map.groups[editor.selected_group]->clip_h = new_val; + if(prop == PROP_PARA_X) pEditor->map.groups[pEditor->selected_group]->parallax_x = new_val; + else if(prop == PROP_PARA_Y) pEditor->map.groups[pEditor->selected_group]->parallax_y = new_val; + else if(prop == PROP_POS_X) pEditor->map.groups[pEditor->selected_group]->offset_x = -new_val; + else if(prop == PROP_POS_Y) pEditor->map.groups[pEditor->selected_group]->offset_y = -new_val; + else if(prop == PROP_USE_CLIPPING) pEditor->map.groups[pEditor->selected_group]->use_clipping = new_val; + else if(prop == PROP_CLIP_X) pEditor->map.groups[pEditor->selected_group]->clip_x = new_val; + else if(prop == PROP_CLIP_Y) pEditor->map.groups[pEditor->selected_group]->clip_y = new_val; + else if(prop == PROP_CLIP_W) pEditor->map.groups[pEditor->selected_group]->clip_w = new_val; + else if(prop == PROP_CLIP_H) pEditor->map.groups[pEditor->selected_group]->clip_h = new_val; } return 0; } -int popup_layer(RECT view) +int EDITOR::popup_layer(EDITOR *pEditor, CUIRect view) { // remove layer button - RECT button; - ui_hsplit_b(&view, 12.0f, &view, &button); + CUIRect button; + view.HSplitBottom(12.0f, &view, &button); static int delete_button = 0; // don't allow deletion of game layer - if(editor.map.game_layer != editor.get_selected_layer(0) && - do_editor_button(&delete_button, "Delete Layer", 0, &button, draw_editor_button, 0, "Deletes the layer")) + if(pEditor->map.game_layer != pEditor->get_selected_layer(0) && + pEditor->DoButton_Editor(&delete_button, "Delete Layer", 0, &button, 0, "Deletes the layer")) { - editor.map.groups[editor.selected_group]->delete_layer(editor.selected_layer); + pEditor->map.groups[pEditor->selected_group]->delete_layer(pEditor->selected_layer); return 1; } - ui_hsplit_b(&view, 10.0f, &view, 0); + view.HSplitBottom(10.0f, &view, 0); - LAYERGROUP *current_group = editor.map.groups[editor.selected_group]; - LAYER *current_layer = editor.get_selected_layer(0); + LAYERGROUP *current_group = pEditor->map.groups[pEditor->selected_group]; + LAYER *current_layer = pEditor->get_selected_layer(0); enum { @@ -195,26 +196,26 @@ int popup_layer(RECT view) }; PROPERTY props[] = { - {"Group", editor.selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1}, - {"Order", editor.selected_layer, PROPTYPE_INT_STEP, 0, current_group->layers.len()}, + {"Group", pEditor->selected_group, PROPTYPE_INT_STEP, 0, pEditor->map.groups.len()-1}, + {"Order", pEditor->selected_layer, PROPTYPE_INT_STEP, 0, current_group->layers.len()}, {"Detail", current_layer->flags&LAYERFLAG_DETAIL, PROPTYPE_BOOL, 0, 1}, {0}, }; static int ids[NUM_PROPS] = {0}; int new_val = 0; - int prop = editor.do_properties(&view, props, ids, &new_val); + int prop = pEditor->do_properties(&view, props, ids, &new_val); if(prop == PROP_ORDER) - editor.selected_layer = current_group->swap_layers(editor.selected_layer, new_val); + pEditor->selected_layer = current_group->swap_layers(pEditor->selected_layer, new_val); else if(prop == PROP_GROUP && current_layer->type != LAYERTYPE_GAME) { - if(new_val >= 0 && new_val < editor.map.groups.len()) + if(new_val >= 0 && new_val < pEditor->map.groups.len()) { current_group->layers.remove(current_layer); - editor.map.groups[new_val]->layers.add(current_layer); - editor.selected_group = new_val; - editor.selected_layer = editor.map.groups[new_val]->layers.len()-1; + pEditor->map.groups[new_val]->layers.add(current_layer); + pEditor->selected_group = new_val; + pEditor->selected_layer = pEditor->map.groups[new_val]->layers.len()-1; } } else if(prop == PROP_HQ) @@ -227,31 +228,31 @@ int popup_layer(RECT view) return current_layer->render_properties(&view); } -int popup_quad(RECT view) +int EDITOR::popup_quad(EDITOR *pEditor, CUIRect view) { - QUAD *quad = editor.get_selected_quad(); + QUAD *quad = pEditor->get_selected_quad(); - RECT button; + CUIRect button; // delete button - ui_hsplit_b(&view, 12.0f, &view, &button); + view.HSplitBottom(12.0f, &view, &button); static int delete_button = 0; - if(do_editor_button(&delete_button, "Delete", 0, &button, draw_editor_button, 0, "Deletes the current quad")) + if(pEditor->DoButton_Editor(&delete_button, "Delete", 0, &button, 0, "Deletes the current quad")) { - LAYER_QUADS *layer = (LAYER_QUADS *)editor.get_selected_layer_type(0, LAYERTYPE_QUADS); + LAYER_QUADS *layer = (LAYER_QUADS *)pEditor->get_selected_layer_type(0, LAYERTYPE_QUADS); if(layer) { - layer->quads.removebyindex(editor.selected_quad); - editor.selected_quad--; + layer->quads.removebyindex(pEditor->selected_quad); + pEditor->selected_quad--; } return 1; } // square button - ui_hsplit_b(&view, 10.0f, &view, &button); - ui_hsplit_b(&view, 12.0f, &view, &button); + view.HSplitBottom(10.0f, &view, &button); + view.HSplitBottom(12.0f, &view, &button); static int sq_button = 0; - if(do_editor_button(&sq_button, "Square", 0, &button, draw_editor_button, 0, "Squares the current quad")) + if(pEditor->DoButton_Editor(&sq_button, "Square", 0, &button, 0, "Squares the current quad")) { int top = quad->points[0].y; int left = quad->points[0].x; @@ -284,9 +285,9 @@ int popup_quad(RECT view) }; PROPERTY props[] = { - {"Pos. Env", quad->pos_env, PROPTYPE_INT_STEP, -1, editor.map.envelopes.len()}, + {"Pos. Env", quad->pos_env, PROPTYPE_INT_STEP, -1, pEditor->map.envelopes.len()}, {"Pos. TO", quad->pos_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Color Env", quad->color_env, PROPTYPE_INT_STEP, -1, editor.map.envelopes.len()}, + {"Color Env", quad->color_env, PROPTYPE_INT_STEP, -1, pEditor->map.envelopes.len()}, {"Color TO", quad->color_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {0}, @@ -294,19 +295,19 @@ int popup_quad(RECT view) static int ids[NUM_PROPS] = {0}; int new_val = 0; - int prop = editor.do_properties(&view, props, ids, &new_val); + int prop = pEditor->do_properties(&view, props, ids, &new_val); - if(prop == PROP_POS_ENV) quad->pos_env = clamp(new_val, -1, editor.map.envelopes.len()-1); + if(prop == PROP_POS_ENV) quad->pos_env = clamp(new_val, -1, pEditor->map.envelopes.len()-1); if(prop == PROP_POS_ENV_OFFSET) quad->pos_env_offset = new_val; - if(prop == PROP_COLOR_ENV) quad->color_env = clamp(new_val, -1, editor.map.envelopes.len()-1); + if(prop == PROP_COLOR_ENV) quad->color_env = clamp(new_val, -1, pEditor->map.envelopes.len()-1); if(prop == PROP_COLOR_ENV_OFFSET) quad->color_env_offset = new_val; return 0; } -int popup_point(RECT view) +int EDITOR::popup_point(EDITOR *pEditor, CUIRect view) { - QUAD *quad = editor.get_selected_quad(); + QUAD *quad = pEditor->get_selected_quad(); enum { @@ -318,7 +319,7 @@ int popup_point(RECT view) for(int v = 0; v < 4; v++) { - if(editor.selected_points&(1<selected_points&(1<colors[v].r<<24; @@ -330,18 +331,18 @@ int popup_point(RECT view) PROPERTY props[] = { - {"Color", color, PROPTYPE_COLOR, -1, editor.map.envelopes.len()}, + {"Color", color, PROPTYPE_COLOR, -1, pEditor->map.envelopes.len()}, {0}, }; static int ids[NUM_PROPS] = {0}; int new_val = 0; - int prop = editor.do_properties(&view, props, ids, &new_val); + int prop = pEditor->do_properties(&view, props, ids, &new_val); if(prop == PROP_COLOR) { for(int v = 0; v < 4; v++) { - if(editor.selected_points&(1<selected_points&(1<colors[v].r = (new_val>>24)&0xff; @@ -360,47 +361,47 @@ int popup_point(RECT view) static int select_image_selected = -100; static int select_image_current = -100; -int popup_select_image(RECT view) +int EDITOR::popup_select_image(EDITOR *pEditor, CUIRect view) { - RECT buttonbar, imageview; - ui_vsplit_l(&view, 80.0f, &buttonbar, &view); - ui_margin(&view, 10.0f, &imageview); + CUIRect buttonbar, imageview; + view.VSplitLeft(80.0f, &buttonbar, &view); + view.Margin(10.0f, &imageview); int show_image = select_image_current; - for(int i = -1; i < editor.map.images.len(); i++) + for(int i = -1; i < pEditor->map.images.len(); i++) { - RECT button; - ui_hsplit_t(&buttonbar, 12.0f, &button, &buttonbar); - ui_hsplit_t(&buttonbar, 2.0f, 0, &buttonbar); + CUIRect button; + buttonbar.HSplitTop(12.0f, &button, &buttonbar); + buttonbar.HSplitTop(2.0f, 0, &buttonbar); - if(ui_mouse_inside(&button)) + if(pEditor->UI()->MouseInside(&button)) show_image = i; if(i == -1) { - if(do_editor_button(&editor.map.images[i], "None", i==select_image_current, &button, draw_editor_button_menuitem, 0, 0)) + if(pEditor->DoButton_MenuItem(&pEditor->map.images[i], "None", i==select_image_current, &button)) select_image_selected = -1; } else { - if(do_editor_button(&editor.map.images[i], editor.map.images[i]->name, i==select_image_current, &button, draw_editor_button_menuitem, 0, 0)) + if(pEditor->DoButton_MenuItem(&pEditor->map.images[i], pEditor->map.images[i]->name, i==select_image_current, &button)) select_image_selected = i; } } - if(show_image >= 0 && show_image < editor.map.images.len()) - gfx_texture_set(editor.map.images[show_image]->tex_id); + if(show_image >= 0 && show_image < pEditor->map.images.len()) + pEditor->Graphics()->TextureSet(pEditor->map.images[show_image]->tex_id); else - gfx_texture_set(-1); - gfx_quads_begin(); - gfx_quads_drawTL(imageview.x, imageview.y, imageview.w, imageview.h); - gfx_quads_end(); + pEditor->Graphics()->TextureSet(-1); + pEditor->Graphics()->QuadsBegin(); + pEditor->Graphics()->QuadsDrawTL(imageview.x, imageview.y, imageview.w, imageview.h); + pEditor->Graphics()->QuadsEnd(); return 0; } -void popup_select_image_invoke(int current, float x, float y) +void EDITOR::popup_select_image_invoke(int current, float x, float y) { static int select_image_popup_id = 0; select_image_selected = -100; @@ -408,7 +409,7 @@ void popup_select_image_invoke(int current, float x, float y) ui_invoke_popup_menu(&select_image_popup_id, 0, x, y, 400, 300, popup_select_image); } -int popup_select_image_result() +int EDITOR::popup_select_image_result() { if(select_image_selected == -100) return -100; diff --git a/src/game/localization.cpp b/src/game/localization.cpp index 202e6ca0..3a1b0411 100644 --- a/src/game/localization.cpp +++ b/src/game/localization.cpp @@ -2,9 +2,7 @@ #include "localization.hpp" #include -extern "C" { #include -} const char *localize(const char *str) { diff --git a/src/game/server/hooks.cpp b/src/game/server/hooks.cpp index 1ae38ce2..89c95f79 100644 --- a/src/game/server/hooks.cpp +++ b/src/game/server/hooks.cpp @@ -7,10 +7,8 @@ #include #include -extern "C" -{ - #include -} +#include + #include #include #include @@ -597,5 +595,5 @@ void mods_postsnap() game.events.clear(); } -extern "C" const char *mods_net_version() { return GAME_NETVERSION; } -extern "C" const char *mods_version() { return GAME_VERSION; } +const char *mods_net_version() { return GAME_NETVERSION; } +const char *mods_version() { return GAME_VERSION; } -- cgit 1.4.1