Skip to content

Commit

Permalink
Don't crash if traffic compression models are unavailable. (#382, #392)
Browse files Browse the repository at this point in the history
Just disable traffic compression.
  • Loading branch information
Xottab-DUTY committed Mar 10, 2020
1 parent c7693a6 commit 9f8b4e9
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 46 deletions.
2 changes: 1 addition & 1 deletion src/xrGame/Level.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ class CLevel : public IGame_Level, public IPureClient
void cl_Process_Spawn(NET_Packet& P);
void ProcessGameEvents();
void ProcessGameSpawns();
void ProcessCompressedUpdate(NET_Packet& P, u8 const compression_type);
void ProcessCompressedUpdate(NET_Packet& P, const Flags8& compression_type);

// Input
void IR_OnKeyboardPress(int key) override;
Expand Down
9 changes: 6 additions & 3 deletions src/xrGame/Level_network_compressed_updates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
#include "xrPhysics/IPHWorld.h"
#include "xrServer_updates_compressor.h"

void CLevel::ProcessCompressedUpdate(NET_Packet& P, u8 const compress_type)
void CLevel::ProcessCompressedUpdate(NET_Packet& P, const Flags8& compress_type)
{
NET_Packet uncompressed_packet;
u16 next_size;
P.r_u16(next_size);
stats.ClientCompressor.Begin();
while (next_size)
{
if (compress_type & eto_ppmd_compression)
if (compress_type.test(eto_ppmd_compression))
{
R_ASSERT(m_trained_stream);
uncompressed_packet.B.count = ppmd_trained_decompress(uncompressed_packet.B.data,
sizeof(uncompressed_packet.B.data), P.B.data + P.r_tell(), next_size, m_trained_stream);
}
else if (compress_type & eto_lzo_compression)
else if (compress_type.test(eto_lzo_compression))
{
R_ASSERT(m_lzo_dictionary.data);
uncompressed_packet.B.count = sizeof(uncompressed_packet.B.data);
Expand Down Expand Up @@ -62,6 +62,9 @@ void CLevel::init_compression()
{
compression::init_ppmd_trained_stream(m_trained_stream);
compression::init_lzo(m_lzo_working_memory, m_lzo_working_buffer, m_lzo_dictionary);
// XXX: if client doesn't support compression, server should know about that
// and either allow connection (and send only uncompressed packets to such client)
// or kick the client with descriptive message
}

void CLevel::deinit_compression()
Expand Down
3 changes: 2 additions & 1 deletion src/xrGame/Level_network_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ void CLevel::ClientReceive()
break;
case M_COMPRESSED_UPDATE_OBJECTS:
{
u8 compression_type = P->r_u8();
Flags8 compression_type;
P->r_u8(compression_type.flags);
ProcessCompressedUpdate(*P, compression_type);
}
break;
Expand Down
20 changes: 18 additions & 2 deletions src/xrGame/console_commands_mp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ extern s32 lag_simmulator_max_ping;
#endif

extern BOOL g_sv_write_updates_bin;
extern u32 g_sv_traffic_optimization_level;
extern Flags8 g_sv_traffic_optimization_level;
extern Flags8 g_sv_available_traffic_optimization_level;

void XRNETSERVER_API DumpNetCompressorStats(bool brief);
BOOL XRNETSERVER_API g_net_compressor_enabled;
Expand Down Expand Up @@ -2033,6 +2034,21 @@ class CCC_CompressorStatus : public IConsole_Command
virtual void Info(TInfo& I) { xr_strcpy(I, "valid arguments is [info info_full on off]"); }
};

class CCC_TrafficOptimizationLevel : public CCC_Integer
{
int dummy;

public:
CCC_TrafficOptimizationLevel(pcstr name) : CCC_Integer(name, &dummy, 0, 7) {}

void Execute(pcstr args) override
{
CCC_Integer::Execute(args);
g_sv_traffic_optimization_level._and(g_sv_available_traffic_optimization_level, static_cast<u8>(dummy));
dummy = g_sv_traffic_optimization_level.get();
}
};

void register_mp_console_commands()
{
CMD1(CCC_Restart, "g_restart");
Expand Down Expand Up @@ -2240,5 +2256,5 @@ void register_mp_console_commands()
CMD1(CCC_GameSpyRegisterUniqueNick, "gs_register_unique_nick");
CMD1(CCC_GameSpyProfile, "gs_profile");
CMD4(CCC_Integer, "sv_write_update_bin", &g_sv_write_updates_bin, 0, 1);
CMD4(CCC_Integer, "sv_traffic_optimization_level", (int*)&g_sv_traffic_optimization_level, 0, 7);
CMD1(CCC_TrafficOptimizationLevel, "sv_traffic_optimization_level");
}
40 changes: 28 additions & 12 deletions src/xrGame/traffic_optimization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,27 @@

namespace compression
{
void init_ppmd_trained_stream(ppmd_trained_stream*& dest)
bool init_ppmd_trained_stream(ppmd_trained_stream*& dest)
{
VERIFY(dest == NULL);
string_path file_name;
FS.update_path(file_name, "$game_config$", "mp" DELIMITER "ppmd_updates.mdl");
R_ASSERT2(FS.exist(file_name), "can't find configs" DELIMITER "mp" DELIMITER "ppmd_updates.mdl");

if (!FS.exist(file_name))
{
Log("! Can't open trained ppmd stream with path:", file_name);
dest = nullptr;
return false;
}
IReader* reader = FS.r_open(file_name);
R_ASSERT(reader);
u32 buffer_size = reader->length();

const size_t buffer_size = reader->length();
u8* buffer = (u8*)xr_malloc(buffer_size);
reader->r(buffer, buffer_size);
FS.r_close(reader);

dest = new compression::ppmd::stream(buffer, buffer_size);
return true;
}

void deinit_ppmd_trained_stream(ppmd_trained_stream*& src)
Expand All @@ -28,24 +35,33 @@ void deinit_ppmd_trained_stream(ppmd_trained_stream*& src)
xr_delete(src);
}

void init_lzo(u8*& dest_wm, u8*& wm_buffer, lzo_dictionary_buffer& dest_dict)
bool init_lzo(u8*& dest_wm, u8*& wm_buffer, lzo_dictionary_buffer& dest_dict)
{
lzo_initialize();
wm_buffer = static_cast<u8*>(xr_malloc(lzo_get_workmem_size() + 16));
// buffer must be alligned to 16 bytes
dest_wm = (u8*)(size_t(wm_buffer + 16) & ~0xf);

string_path file_name;
FS.update_path(file_name, "$game_config$", "mp" DELIMITER "lzo_updates.dic");
R_ASSERT2(FS.exist(file_name), "can't find configs" DELIMITER "mp" DELIMITER "lzo_updates.dic");
if (!FS.exist(file_name))
{
Log("! Can't open lzo dictionary with path:", file_name);
dest_wm = nullptr;
wm_buffer = nullptr;
return false;
}
IReader* reader = FS.r_open(file_name);
u32 buffer_size = reader->length();
R_ASSERT(reader);

const size_t buffer_size = reader->length();
u8* buffer = (u8*)xr_malloc(buffer_size);
reader->r(buffer, buffer_size);
FS.r_close(reader);

dest_dict.data = buffer;
dest_dict.size = buffer_size;

lzo_initialize();
wm_buffer = static_cast<u8*>(xr_malloc(lzo_get_workmem_size() + 16));
// buffer must be alligned to 16 bytes
dest_wm = (u8*)(size_t(wm_buffer + 16) & ~0xf);
return true;
}

void deinit_lzo(u8*& src_wm_buffer, lzo_dictionary_buffer& src_dict)
Expand Down
6 changes: 2 additions & 4 deletions src/xrGame/traffic_optimization.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class stream;
}; // namespace ppmd

using ppmd_trained_stream = ppmd::stream;
void init_ppmd_trained_stream(ppmd_trained_stream*& dest);
bool init_ppmd_trained_stream(ppmd_trained_stream*& dest);
void deinit_ppmd_trained_stream(ppmd_trained_stream*& src);

struct lzo_dictionary_buffer
Expand All @@ -20,7 +20,7 @@ struct lzo_dictionary_buffer
u32 size;
};

void init_lzo(u8*& dest_wm, u8*& wm_buffer, lzo_dictionary_buffer& dest_dict);
bool init_lzo(u8*& dest_wm, u8*& wm_buffer, lzo_dictionary_buffer& dest_dict);
void deinit_lzo(u8*& src_wm_buffer, lzo_dictionary_buffer& src_dict);

} // namespace compression
Expand All @@ -32,5 +32,3 @@ enum enum_traffic_optimization
eto_lzo_compression = 1 << 1,
eto_last_change = 1 << 2,
}; // enum enum_traffic_optimization

extern u32 g_sv_traffic_optimization_level;
2 changes: 0 additions & 2 deletions src/xrGame/xrServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
#include <malloc.h>
#pragma warning(pop)

u32 g_sv_traffic_optimization_level = eto_none;

xrClientData::xrClientData() : IClient(Device.GetTimerGlobal())
{
ps = NULL;
Expand Down
48 changes: 29 additions & 19 deletions src/xrGame/xrServer_updates_compressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "xrMessages.h"

BOOL g_sv_write_updates_bin = FALSE;
Flags8 g_sv_traffic_optimization_level;
Flags8 g_sv_available_traffic_optimization_level;

last_updates_cache::last_updates_cache()
{
Expand Down Expand Up @@ -123,8 +125,11 @@ server_updates_compressor::~server_updates_compressor()

void server_updates_compressor::init_compression()
{
compression::init_ppmd_trained_stream(m_trained_stream);
compression::init_lzo(m_lzo_working_memory, m_lzo_working_buffer, m_lzo_dictionary);
const bool ppmd_available = compression::init_ppmd_trained_stream(m_trained_stream);
const bool lzo_available = compression::init_lzo(m_lzo_working_memory, m_lzo_working_buffer, m_lzo_dictionary);
g_sv_available_traffic_optimization_level.set(eto_ppmd_compression, ppmd_available);
g_sv_available_traffic_optimization_level.set(eto_lzo_compression, lzo_available);
g_sv_available_traffic_optimization_level.set(eto_last_change, true);
}

void server_updates_compressor::deinit_compression()
Expand All @@ -143,11 +148,10 @@ void server_updates_compressor::deinit_compression()
void server_updates_compressor::begin_updates()
{
m_current_update = 0;
if ((g_sv_traffic_optimization_level & eto_ppmd_compression) ||
(g_sv_traffic_optimization_level & eto_lzo_compression))
if (g_sv_traffic_optimization_level.test(eto_ppmd_compression | eto_lzo_compression))
{
m_ready_for_send.front()->w_begin(M_COMPRESSED_UPDATE_OBJECTS);
m_ready_for_send.front()->w_u8(static_cast<u8>(g_sv_traffic_optimization_level));
m_ready_for_send.front()->w_u8(g_sv_traffic_optimization_level.get());
m_acc_buff.write_start();
}
else
Expand All @@ -166,18 +170,17 @@ NET_Packet* server_updates_compressor::goto_next_dest()

if (m_ready_for_send.size() == m_current_update)
{
m_ready_for_send.push_back(new NET_Packet());
new_dest = m_ready_for_send.back();
new_dest = m_ready_for_send.emplace_back(new NET_Packet());
}
else
{
new_dest = m_ready_for_send[m_current_update];
}

if (g_sv_traffic_optimization_level & eto_ppmd_compression)
if (g_sv_traffic_optimization_level.test(eto_ppmd_compression))
{
new_dest->w_begin(M_COMPRESSED_UPDATE_OBJECTS);
m_ready_for_send.front()->w_u8(static_cast<u8>(g_sv_traffic_optimization_level));
m_ready_for_send.front()->w_u8(g_sv_traffic_optimization_level.get());
}
else
{
Expand All @@ -190,22 +193,26 @@ NET_Packet* server_updates_compressor::goto_next_dest()
void server_updates_compressor::flush_accumulative_buffer()
{
NET_Packet* dst_packet = get_current_dest();
if ((g_sv_traffic_optimization_level & eto_ppmd_compression) ||
(g_sv_traffic_optimization_level & eto_lzo_compression))
if (g_sv_traffic_optimization_level.test(eto_ppmd_compression | eto_lzo_compression))
{
CompressStats.Begin();
R_ASSERT(m_trained_stream);
if (g_sv_traffic_optimization_level & eto_ppmd_compression)
if (g_sv_traffic_optimization_level.test(eto_ppmd_compression))
{
R_ASSERT(m_trained_stream);
m_compress_buf.B.count = ppmd_trained_compress(m_compress_buf.B.data, sizeof(m_compress_buf.B.data),
m_acc_buff.B.data, m_acc_buff.B.count, m_trained_stream);
}
else
else if (g_sv_traffic_optimization_level.test(eto_lzo_compression))
{
R_ASSERT(m_lzo_dictionary.data);
m_compress_buf.B.count = sizeof(m_compress_buf.B.data);
lzo_compress_dict(m_acc_buff.B.data, m_acc_buff.B.count, m_compress_buf.B.data, m_compress_buf.B.count,
m_lzo_working_memory, m_lzo_dictionary.data, m_lzo_dictionary.size);
}
else
{
NODEFAULT;
}
CompressStats.End();
//(sizeof(u16)*2 + 1) ::= w_begin(2) + compress_type(1) + zero_end(2)
if (dst_packet->w_tell() + m_compress_buf.B.count + (sizeof(u16) * 2 + 1) < sizeof(dst_packet->B.data))
Expand All @@ -215,21 +222,25 @@ void server_updates_compressor::flush_accumulative_buffer()
m_acc_buff.write_start();
return;
}
dst_packet->w_u16(0);
dst_packet->w_u16(0); // packed end

// prepare new packet immediately
dst_packet = goto_next_dest();
dst_packet->w_u16(static_cast<u16>(m_compress_buf.B.count));
dst_packet->w(m_compress_buf.B.data, m_compress_buf.B.count);
m_acc_buff.write_start();
return;
}
dst_packet->w(m_acc_buff.B.data, m_acc_buff.B.count);
dst_packet->w(m_acc_buff.B.data, m_acc_buff.B.count); // packed end

// prepare new packet immediately
goto_next_dest();
m_acc_buff.w_begin(M_UPDATE_OBJECTS);
}

void server_updates_compressor::write_update_for(u16 const enity, NET_Packet& update)
{
if (g_sv_traffic_optimization_level & eto_last_change)
if (g_sv_traffic_optimization_level.test(eto_last_change))
{
// if (m_updates_cache.get_last_equpdates(enity, update) >= max_eq_packets)
if (m_updates_cache.add_update(enity, update) >= max_eq_packets)
Expand All @@ -251,8 +262,7 @@ void server_updates_compressor::end_updates(
if (m_acc_buff.w_tell() > 2)
flush_accumulative_buffer();

if ((g_sv_traffic_optimization_level & eto_ppmd_compression) ||
(g_sv_traffic_optimization_level & eto_lzo_compression))
if (g_sv_traffic_optimization_level.test(eto_ppmd_compression | eto_lzo_compression))
{
get_current_dest()->w_u16(0);
}
Expand Down
5 changes: 3 additions & 2 deletions src/xrGame/xrServer_updates_compressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#include "traffic_optimization.h"

extern Flags8 g_sv_traffic_optimization_level;
extern Flags8 g_sv_available_traffic_optimization_level;

class last_updates_cache : private Noncopyable
{
public:
Expand Down Expand Up @@ -50,8 +53,6 @@ class server_updates_compressor
static u32 const entities_count = 32;
static u32 const start_compress_buffer_size = 1024 * 150 * entities_count;

enum_traffic_optimization m_traffic_optimization;

NET_Packet m_acc_buff;
NET_Packet m_compress_buf;

Expand Down

0 comments on commit 9f8b4e9

Please sign in to comment.