Prevent buffer overflows when using streams

This commit is contained in:
Chris Cowan 2025-06-27 19:27:22 -07:00 committed by Ricardo Luís Vaz Silva
commit ab7b1642bc
18 changed files with 600 additions and 388 deletions

View file

@ -40,6 +40,7 @@
#include "c_console.h"
#include "c_dispatch.h"
#include "c_cvars.h"
#include "d_protocol.h"
#include "engineerrors.h"
#include "printf.h"
#include "palutil.h"
@ -1265,12 +1266,10 @@ void FilterCompactCVars (TArray<FBaseCVar *> &cvars, uint32_t filter)
}
}
void C_WriteCVars (uint8_t **demo_p, uint32_t filter, bool compact)
void C_WriteCVars (TArrayView<uint8_t>& demo_p, uint32_t filter, bool compact)
{
FString dump = C_GetMassCVarString(filter, compact);
size_t dumplen = dump.Len() + 1; // include terminating \0
memcpy(*demo_p, dump.GetChars(), dumplen);
*demo_p += dumplen;
WriteFString(dump, demo_p);
}
FString C_GetMassCVarString (uint32_t filter, bool compact)
@ -1306,9 +1305,9 @@ FString C_GetMassCVarString (uint32_t filter, bool compact)
return dump;
}
void C_ReadCVars (uint8_t **demo_p)
void C_ReadCVars (TArrayView<uint8_t>& demo_p)
{
char *ptr = *((char **)demo_p);
char *ptr = (char *)demo_p.Data();
char *breakpt;
if (*ptr++ != '\\')
@ -1371,7 +1370,7 @@ void C_ReadCVars (uint8_t **demo_p)
}
}
}
*demo_p += strlen (*((char **)demo_p)) + 1;
AdvanceStream(demo_p, strlen((char*)demo_p.Data()) + 1);
}
struct FCVarBackup

View file

@ -269,7 +269,7 @@ private:
// These need to go away!
friend FString C_GetMassCVarString (uint32_t filter, bool compact);
friend void C_SerializeCVars(FSerializer& arc, const char* label, uint32_t filter);
friend void C_ReadCVars (uint8_t **demo_p);
friend void C_ReadCVars (TArrayView<uint8_t>& demo_p);
friend void C_BackupCVars (void);
friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev);
friend FBaseCVar *FindCVarSub (const char *var_name, int namelen);
@ -288,12 +288,12 @@ private:
// the cvar names are omitted to save space.
FString C_GetMassCVarString (uint32_t filter, bool compact=false);
// Writes all cvars that could effect demo sync to *demo_p. These are
// Writes all cvars that could effect demo sync to demo_p. These are
// cvars that have either CVAR_SERVERINFO or CVAR_DEMOSAVE set.
void C_WriteCVars (uint8_t **demo_p, uint32_t filter, bool compact=false);
void C_WriteCVars (TArrayView<uint8_t>& demo_p, uint32_t filter, bool compact=false);
// Read all cvars from *demo_p and set them appropriately.
void C_ReadCVars (uint8_t **demo_p);
// Read all cvars from demo_p and set them appropriately.
void C_ReadCVars (TArrayView<uint8_t>& demo_p);
void C_InstallHandlers(ConsoleCallbacks* cb);

View file

@ -191,10 +191,10 @@ size_t Net_SetEngineInfo(uint8_t*& stream);
bool Net_VerifyEngine(uint8_t*& stream);
void Net_SetupUserInfo();
const char* Net_GetClientName(int client, unsigned int charLimit);
size_t Net_SetUserInfo(int client, uint8_t*& stream);
size_t Net_ReadUserInfo(int client, uint8_t*& stream);
size_t Net_ReadGameInfo(uint8_t*& stream);
size_t Net_SetGameInfo(uint8_t*& stream);
void Net_SetUserInfo(int client, TArrayView<uint8_t>& stream);
void Net_ReadUserInfo(int client, TArrayView<uint8_t>& stream);
void Net_ReadGameInfo(TArrayView<uint8_t>& stream);
void Net_SetGameInfo(TArrayView<uint8_t>& stream);
static SOCKET CreateUDPSocket()
{
@ -833,7 +833,7 @@ static bool Host_CheckForConnections(void* connected)
{
if (Connected[RemoteClient].Status == CSTAT_CONNECTING)
{
uint8_t* stream = &NetBuffer[2];
TArrayView<uint8_t> stream = TArrayView(&NetBuffer[2], MAX_MSGLEN-2);
Net_ReadUserInfo(RemoteClient, stream);
Connected[RemoteClient].Status = CSTAT_WAITING;
I_NetClientConnected(RemoteClient, 16u);
@ -886,8 +886,9 @@ static bool Host_CheckForConnections(void* connected)
memcpy(&NetBuffer[3], GameID, 8);
NetBufferLength = 11u;
uint8_t* stream = &NetBuffer[NetBufferLength];
NetBufferLength += Net_SetGameInfo(stream);
TArrayView<uint8_t> stream = TArrayView(&NetBuffer[NetBufferLength], MAX_MSGLEN - NetBufferLength);
Net_SetGameInfo(stream);
NetBufferLength += stream.Data() - &NetBuffer[NetBufferLength];
SendPacket(con.Address);
clientReady = false;
}
@ -911,8 +912,9 @@ static bool Host_CheckForConnections(void* connected)
NetBufferLength += addrSize;
}
uint8_t* stream = &NetBuffer[NetBufferLength];
NetBufferLength += Net_SetUserInfo(i, stream);
TArrayView<uint8_t> stream = TArrayView(&NetBuffer[NetBufferLength], MAX_MSGLEN - NetBufferLength);
Net_SetUserInfo(i, stream);
NetBufferLength += stream.Data() - &NetBuffer[NetBufferLength];
SendPacket(con.Address);
}
clientReady = false;
@ -1131,7 +1133,7 @@ static bool Guest_ContactHost(void* unused)
{
TicDup = clamp<int>(NetBuffer[2], 1, MAXTICDUP);
memcpy(GameID, &NetBuffer[3], 8);
uint8_t* stream = &NetBuffer[11];
TArrayView<uint8_t> stream = TArrayView(&NetBuffer[11], MAX_MSGLEN - 11);
Net_ReadGameInfo(stream);
Connected[consoleplayer].bHasGameInfo = true;
}
@ -1158,7 +1160,7 @@ static bool Guest_ContactHost(void* unused)
{
Connected[c].Status = CSTAT_READY;
}
uint8_t* stream = &NetBuffer[byte];
TArrayView<uint8_t> stream = TArrayView(&NetBuffer[byte], MAX_MSGLEN - byte);
Net_ReadUserInfo(c, stream);
SetClientAck(consoleplayer, c, true);
@ -1199,8 +1201,9 @@ static bool Guest_ContactHost(void* unused)
NetBuffer[1] = PRE_USER_INFO;
NetBufferLength = 2u;
uint8_t* stream = &NetBuffer[NetBufferLength];
NetBufferLength += Net_SetUserInfo(consoleplayer, stream);
TArrayView<uint8_t> stream = TArrayView(&NetBuffer[NetBufferLength], MAX_MSGLEN - NetBufferLength);
Net_SetUserInfo(consoleplayer, stream);
NetBufferLength += stream.Data() - &NetBuffer[NetBufferLength];
SendPacket(Connected[0].Address);
}
else if (con.Status == CSTAT_WAITING)

View file

@ -265,6 +265,11 @@ FString &FString::operator = (const char *copyStr)
return *this;
}
TArrayView<uint8_t> FString::GetTArrayView()
{
return TArrayView((uint8_t*)Chars, Len() + 1);
}
void FString::Format (const char *fmt, ...)
{
va_list arglist;

View file

@ -166,6 +166,8 @@ public:
const char *GetChars() const { return Chars; }
TArrayView<uint8_t> GetTArrayView();
const char &operator[] (int index) const { return Chars[index]; }
#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
// Compiling 32-bit Windows source with MSVC: size_t is typedefed to an

View file

@ -158,7 +158,7 @@ void D_ProcessEvents(void);
void G_BuildTiccmd(usercmd_t *cmd);
void D_DoAdvanceDemo(void);
static void RunScript(uint8_t **stream, AActor *pawn, int snum, int argn, int always);
static void RunScript(TArrayView<uint8_t>& stream, AActor *pawn, int snum, int argn, int always);
extern bool advancedemo;
@ -297,7 +297,7 @@ public:
if (CurrentStream != nullptr)
{
AddBytes(1);
WriteInt8(it, &CurrentStream);
UncheckedWriteInt8(it, &CurrentStream);
}
return *this;
}
@ -307,7 +307,7 @@ public:
if (CurrentStream != nullptr)
{
AddBytes(2);
WriteInt16(it, &CurrentStream);
UncheckedWriteInt16(it, &CurrentStream);
}
return *this;
}
@ -317,7 +317,7 @@ public:
if (CurrentStream != nullptr)
{
AddBytes(4);
WriteInt32(it, &CurrentStream);
UncheckedWriteInt32(it, &CurrentStream);
}
return *this;
}
@ -327,7 +327,7 @@ public:
if (CurrentStream != nullptr)
{
AddBytes(8);
WriteInt64(it, &CurrentStream);
UncheckedWriteInt64(it, &CurrentStream);
}
return *this;
}
@ -337,7 +337,7 @@ public:
if (CurrentStream != nullptr)
{
AddBytes(4);
WriteFloat(it, &CurrentStream);
UncheckedWriteFloat(it, &CurrentStream);
}
return *this;
}
@ -347,7 +347,7 @@ public:
if (CurrentStream != nullptr)
{
AddBytes(8);
WriteDouble(it, &CurrentStream);
UncheckedWriteDouble(it, &CurrentStream);
}
return *this;
}
@ -357,7 +357,7 @@ public:
if (CurrentStream != nullptr)
{
AddBytes(strlen(it) + 1);
WriteString(it, &CurrentStream);
UncheckedWriteString(it, &CurrentStream);
}
return *this;
}
@ -603,24 +603,24 @@ static size_t GetNetBufferSize()
if (NetBufferLength < totalBytes + playerCount * padding)
return totalBytes + playerCount * padding;
uint8_t* skipper = &NetBuffer[totalBytes];
TArrayView<uint8_t> skipper = TArrayView(&NetBuffer[totalBytes], MAX_MSGLEN - totalBytes);
for (int p = 0; p < playerCount; ++p)
{
++skipper;
AdvanceStream(skipper, 1);
if (NetMode == NET_PacketServer && RemoteClient == Net_Arbitrator)
skipper += 2;
AdvanceStream(skipper, 2);
for (int i = 0; i < ranTics; ++i)
skipper += 3;
AdvanceStream(skipper, 3);
for (int i = 0; i < numTics; ++i)
{
++skipper;
AdvanceStream(skipper, 1);
SkipUserCmdMessage(skipper);
}
}
return int(skipper - NetBuffer);
return int(skipper.Data() - NetBuffer);
}
//
@ -992,14 +992,19 @@ static void GetPackets()
// Each tic within a given packet is given a sequence number to ensure that things were put
// back together correctly. Normally this wouldn't matter as much but since we need to keep
// clients in lock step a misordered packet will instantly cause a desync.
TArray<uint8_t*> data = {};
TArray<TArrayView<uint8_t>> data = {}; // each contained TArrayView represents a packet.
for (int t = 0; t < totalTics; ++t)
{
// Try and reorder the tics if they're all there but end up out of order.
const int ofs = NetBuffer[curByte++];
data.Insert(ofs, &NetBuffer[curByte]);
uint8_t* skipper = &NetBuffer[curByte];
curByte += SkipUserCmdMessage(skipper);
TArrayView<uint8_t> skipper = TArrayView(&NetBuffer[curByte], MAX_MSGLEN - curByte);
SkipUserCmdMessage(skipper);
TArrayView<uint8_t> packet = TArrayView(&NetBuffer[curByte], skipper.Data() - &NetBuffer[curByte]);
data.Insert(ofs, packet);
curByte += skipper.Data() - &NetBuffer[curByte];
}
// If it's from a previous waiting period, the commands are no longer relevant.
@ -1722,38 +1727,29 @@ void NetUpdate(int tics)
// Client commands.
uint8_t* cmd = &NetBuffer[size];
TArrayView<uint8_t> cmd = TArrayView(&NetBuffer[size], MAX_MSGLEN - size);
for (int i = 0; i < playerCount; ++i)
{
cmd[0] = playerNums[i];
++cmd;
WriteInt8(playerNums[i], cmd);
auto& clientState = ClientStates[playerNums[i]];
// Time used to track latency since in packet server mode we want each
// client's latency to the server itself.
if (NetMode == NET_PacketServer && consoleplayer == Net_Arbitrator)
{
cmd[0] = (clientState.AverageLatency >> 8);
++cmd;
cmd[0] = clientState.AverageLatency;
++cmd;
WriteInt16(clientState.AverageLatency, cmd);
}
for (int r = 0; r < sendCon; ++r)
{
cmd[0] = r;
++cmd;
WriteInt8(r, cmd);
const int tic = (baseConsistency + curTicOfs + r) % BACKUPTICS;
cmd[0] = (clientState.LocalConsistency[tic] >> 8);
++cmd;
cmd[0] = clientState.LocalConsistency[tic];
++cmd;
WriteInt16(clientState.LocalConsistency[tic], cmd);
}
for (int t = 0; t < sendTics; ++t)
{
cmd[0] = t;
++cmd;
WriteInt8(t, cmd);
int curTic = sequenceNum + curTicOfs + t, lastTic = curTic - 1;
if (playerNums[i] == consoleplayer)
@ -1763,11 +1759,7 @@ void NetUpdate(int tics)
// Write out the net events before the user commands so inputs can
// be used as a marker for when the given command ends.
auto& stream = NetEvents.Streams[curTic % BACKUPTICS];
if (stream.Used)
{
memcpy(cmd, stream.Stream, stream.Used);
cmd += stream.Used;
}
WriteBytes(TArrayView(stream.Stream, stream.Used), cmd);
WriteUserCmdMessage(LocalCmds[realTic],
realLastTic >= 0 ? &LocalCmds[realLastTic] : nullptr, cmd);
@ -1776,13 +1768,8 @@ void NetUpdate(int tics)
{
auto& netTic = clientState.Tics[curTic % BACKUPTICS];
int len;
uint8_t* data = netTic.Data.GetData(&len);
if (data != nullptr)
{
memcpy(cmd, data, len);
cmd += len;
}
auto data = netTic.Data.GetTArrayView();
WriteBytes(data, cmd);
WriteUserCmdMessage(netTic.Command,
lastTic >= 0 ? &clientState.Tics[lastTic % BACKUPTICS].Command : nullptr, cmd);
@ -1790,9 +1777,9 @@ void NetUpdate(int tics)
}
}
HSendPacket(client, int(cmd - NetBuffer));
HSendPacket(client, int(cmd.Data() - NetBuffer));
if (net_extratic && !isSelf)
HSendPacket(client, int(cmd - NetBuffer));
HSendPacket(client, int(cmd.Data() - NetBuffer));
}
}
}
@ -1834,70 +1821,52 @@ const char* Net_GetClientName(int client, unsigned int charLimit = 0u)
return players[client].userinfo.GetName(charLimit);
}
size_t Net_SetUserInfo(int client, uint8_t*& stream)
void Net_SetUserInfo(int client, TArrayView<uint8_t>& stream)
{
auto str = D_GetUserInfoStrings(client, true);
const size_t userSize = str.Len() + 1;
memcpy(stream, str.GetChars(), userSize);
return userSize;
WriteFString(str, stream);
}
size_t Net_ReadUserInfo(int client, uint8_t*& stream)
void Net_ReadUserInfo(int client, TArrayView<uint8_t>& stream)
{
const uint8_t* start = stream;
D_ReadUserInfoStrings(client, &stream, false);
return stream - start;
D_ReadUserInfoStrings(client, stream, false);
}
size_t Net_SetGameInfo(uint8_t*& stream)
void Net_SetGameInfo(TArrayView<uint8_t>& stream)
{
const uint8_t* start = stream;
WriteString(startmap.GetChars(), &stream);
WriteInt32(rngseed, &stream);
C_WriteCVars(&stream, CVAR_SERVERINFO, true);
WriteFString(startmap, stream);
WriteInt32(rngseed, stream);
C_WriteCVars(stream, CVAR_SERVERINFO, true);
auto load = Args->CheckValue("-loadgame");
if (load != nullptr)
{
stream[0] = true;
const size_t len = strlen(load) + 1;
memcpy(&stream[1], load, len);
stream += len;
WriteInt8(1, stream);
WriteString(load, stream);
}
else
{
stream[0] = false;
WriteInt8(0, stream);
}
return (stream + 1) - start;
}
size_t Net_ReadGameInfo(uint8_t*& stream)
void Net_ReadGameInfo(TArrayView<uint8_t>& stream)
{
const uint8_t* start = stream;
startmap = ReadStringConst(&stream);
rngseed = ReadInt32(&stream);
C_ReadCVars(&stream);
startmap = ReadStringConst(stream);
rngseed = ReadInt32(stream);
C_ReadCVars(stream);
if (stream[0])
if (ReadInt8(stream))
{
++stream;
const size_t len = strlen((char*)stream) + 1;
auto load = ReadString(stream);
// Don't override the existing argument in case they need to use
// a custom savefile name.
if (!Args->CheckParm("-loadgame"))
{
Args->AppendArg("-loadgame");
Args->AppendArg((char*)stream);
Args->AppendArg(load);
}
stream += len;
}
else
{
++stream;
}
return stream - start;
}
// Connects players to each other if needed.
@ -2412,6 +2381,11 @@ uint8_t *FDynamicBuffer::GetData(int *len)
return m_Len ? m_Data : nullptr;
}
TArrayView<uint8_t> FDynamicBuffer::GetTArrayView()
{
return TArrayView(m_Data, m_Len);
}
static int RemoveClass(FLevelLocals *Level, const PClass *cls)
{
AActor *actor;
@ -2447,7 +2421,7 @@ static int RemoveClass(FLevelLocals *Level, const PClass *cls)
// [RH] Execute a special "ticcmd". The type byte should
// have already been read, and the stream is positioned
// at the beginning of the command's actual data.
void Net_DoCommand(int cmd, uint8_t **stream, int player)
void Net_DoCommand(int cmd, TArrayView<uint8_t>& stream, int player)
{
uint8_t pos = 0;
const char* s = nullptr;
@ -3040,7 +3014,7 @@ void Net_DoCommand(int cmd, uint8_t **stream, int player)
}
// Used by DEM_RUNSCRIPT, DEM_RUNSCRIPT2, and DEM_RUNNAMEDSCRIPT
static void RunScript(uint8_t **stream, AActor *pawn, int snum, int argn, int always)
static void RunScript(TArrayView<uint8_t>& stream, AActor *pawn, int snum, int argn, int always)
{
// Scripts can be invoked without a level loaded, e.g. via puke(name) CCMD in fullscreen console
if (pawn == nullptr)
@ -3061,44 +3035,44 @@ static void RunScript(uint8_t **stream, AActor *pawn, int snum, int argn, int al
// not to execute them. Right now this is making setting up net commands a nightmare.
// Reads through the network stream but doesn't actually execute any command. Used for getting the size of a stream.
// The skip amount is the number of bytes the command possesses. This should mirror the bytes in Net_DoCommand().
void Net_SkipCommand(int cmd, uint8_t **stream)
void Net_SkipCommand(int cmd, TArrayView<uint8_t>& stream)
{
size_t skip = 0;
switch (cmd)
{
case DEM_SAY:
skip = strlen((char *)(*stream + 1)) + 2;
skip = strlen((char *)(stream.Data() + 1)) + 2;
break;
case DEM_ADDBOT:
skip = strlen((char *)(*stream + 1)) + 6;
skip = strlen((char *)(stream.Data() + 1)) + 6;
break;
case DEM_GIVECHEAT:
case DEM_TAKECHEAT:
skip = strlen((char *)(*stream)) + 5;
skip = strlen((char *)(stream.Data())) + 5;
break;
case DEM_SETINV:
skip = strlen((char *)(*stream)) + 6;
skip = strlen((char *)(stream.Data())) + 6;
break;
case DEM_NETEVENT:
skip = strlen((char *)(*stream)) + 15;
skip = strlen((char *)(stream.Data())) + 15;
break;
case DEM_ZSC_CMD:
skip = strlen((char*)(*stream)) + 1;
skip += (((*stream)[skip] << 8) | (*stream)[skip + 1]) + 2;
skip = strlen((char*)(stream.Data())) + 1;
skip += (stream[skip] << 8) | (stream[skip + 1]) + 2;
break;
case DEM_SUMMON2:
case DEM_SUMMONFRIEND2:
case DEM_SUMMONFOE2:
skip = strlen((char *)(*stream)) + 26;
skip = strlen((char *)(stream.Data())) + 26;
break;
case DEM_CHANGEMAP2:
skip = strlen((char *)(*stream + 1)) + 2;
skip = strlen((char *)(stream.Data() + 1)) + 2;
break;
case DEM_MUSICCHANGE:
case DEM_PRINT:
@ -3114,7 +3088,7 @@ void Net_SkipCommand(int cmd, uint8_t **stream)
case DEM_MORPHEX:
case DEM_KILLCLASSCHEAT:
case DEM_MDK:
skip = strlen((char *)(*stream)) + 1;
skip = strlen((char *)(stream.Data())) + 1;
break;
case DEM_WARPCHEAT:
@ -3141,14 +3115,14 @@ void Net_SkipCommand(int cmd, uint8_t **stream)
break;
case DEM_SAVEGAME:
skip = strlen((char *)(*stream)) + 1;
skip += strlen((char *)(*stream) + skip) + 1;
skip = strlen((char *)(stream.Data())) + 1;
skip += strlen((char *)(stream.Data()) + skip) + 1;
break;
case DEM_SINFCHANGEDXOR:
case DEM_SINFCHANGED:
{
uint8_t t = **stream;
uint8_t t = stream[0];
skip = 1 + (t & 63);
if (cmd == DEM_SINFCHANGED)
{
@ -3162,7 +3136,7 @@ void Net_SkipCommand(int cmd, uint8_t **stream)
skip += 4;
break;
case CVAR_String:
skip += strlen((char*)(*stream + skip)) + 1;
skip += strlen((char*)(stream.Data() + skip)) + 1;
break;
}
}
@ -3175,16 +3149,16 @@ void Net_SkipCommand(int cmd, uint8_t **stream)
case DEM_RUNSCRIPT:
case DEM_RUNSCRIPT2:
skip = 3 + *(*stream + 2) * 4;
skip = 3 + *(stream.Data() + 2) * 4;
break;
case DEM_RUNNAMEDSCRIPT:
skip = strlen((char *)(*stream)) + 2;
skip += ((*(*stream + skip - 1)) & 127) * 4;
skip = strlen((char *)(stream.Data())) + 2;
skip += ((*(stream.Data() + skip - 1)) & 127) * 4;
break;
case DEM_RUNSPECIAL:
skip = 3 + *(*stream + 2) * 4;
skip = 3 + *(stream.Data() + 2) * 4;
break;
case DEM_CONVREPLY:
@ -3195,14 +3169,14 @@ void Net_SkipCommand(int cmd, uint8_t **stream)
case DEM_SETSLOTPNUM:
{
skip = 2 + (cmd == DEM_SETSLOTPNUM);
for (int numweapons = (*stream)[skip-1]; numweapons > 0; --numweapons)
skip += 1 + ((*stream)[skip] >> 7);
for (int numweapons = stream[skip-1]; numweapons > 0; --numweapons)
skip += 1 + (stream[skip] >> 7);
}
break;
case DEM_ADDSLOT:
case DEM_ADDSLOTDEFAULT:
skip = 2 + ((*stream)[1] >> 7);
skip = 2 + (stream[1] >> 7);
break;
case DEM_SETPITCHLIMIT:
@ -3210,7 +3184,7 @@ void Net_SkipCommand(int cmd, uint8_t **stream)
break;
}
*stream += skip;
AdvanceStream(stream, skip);
}
// This was taken out of shared_hud, because UI code shouldn't do low level calculations that may change if the backing implementation changes.

View file

@ -65,6 +65,7 @@ public:
void SetData(const uint8_t* data, int len);
uint8_t* GetData(int* len = nullptr);
TArrayView<uint8_t> GetTArrayView();
private:
uint8_t* m_Data;
@ -149,8 +150,8 @@ void Net_WriteDouble(double);
void Net_WriteString(const char *);
void Net_WriteBytes(const uint8_t *, int len);
void Net_DoCommand(int cmd, uint8_t **stream, int player);
void Net_SkipCommand(int cmd, uint8_t **stream);
void Net_DoCommand(int cmd, TArrayView<uint8_t>& stream, int player);
void Net_SkipCommand(int cmd, TArrayView<uint8_t>& stream);
bool Net_CheckCutsceneReady();
void Net_AdvanceCutscene();

View file

@ -56,10 +56,10 @@ void D_UserInfoChanged (FBaseCVar *info);
bool D_SendServerInfoChange (FBaseCVar *cvar, UCVarValue value, ECVarType type);
bool D_SendServerFlagChange (FBaseCVar *cvar, int bitnum, bool set, bool silent);
void D_DoServerInfoChange (uint8_t **stream, bool singlebit);
void D_DoServerInfoChange (TArrayView<uint8_t>& stream, bool singlebit);
FString D_GetUserInfoStrings(int pnum, bool compact = false);
void D_ReadUserInfoStrings (int player, uint8_t **stream, bool update);
void D_ReadUserInfoStrings (int player, TArrayView<uint8_t>& stream, bool update);
struct FPlayerColorSet;
void D_GetPlayerColor (int player, float *h, float *s, float *v, FPlayerColorSet **colorset);

View file

@ -249,9 +249,9 @@ void D_PickRandomTeam (int player)
{
static char teamline[8] = "\\team\\X";
uint8_t *foo = (uint8_t *)teamline;
auto foo = TArrayView((uint8_t *)teamline, sizeof(teamline));
teamline[6] = (char)D_PickRandomTeam() + '0';
D_ReadUserInfoStrings (player, &foo, teamplay);
D_ReadUserInfoStrings (player, foo, teamplay);
}
int D_PickRandomTeam ()
@ -581,7 +581,7 @@ void D_UserInfoChanged (FBaseCVar *cvar)
Net_WriteString (foo);
}
static const char *SetServerVar (char *name, ECVarType type, uint8_t **stream, bool singlebit)
static const char *SetServerVar (char *name, ECVarType type, TArrayView<uint8_t>& stream, bool singlebit)
{
FBaseCVar *var = FindCVar (name, NULL);
UCVarValue value;
@ -711,7 +711,7 @@ bool D_SendServerFlagChange (FBaseCVar *cvar, int bitnum, bool set, bool silent)
return false;
}
void D_DoServerInfoChange (uint8_t **stream, bool singlebit)
void D_DoServerInfoChange (TArrayView<uint8_t>& stream, bool singlebit)
{
const char *value;
char name[64];
@ -723,8 +723,8 @@ void D_DoServerInfoChange (uint8_t **stream, bool singlebit)
len &= 0x3f;
if (len == 0)
return;
memcpy (name, *stream, len);
*stream += len;
auto dst = TArrayView((uint8_t*)name, len);
ReadBytes(dst, stream);
name[len] = 0;
if ( (value = SetServerVar (name, (ECVarType)type, stream, singlebit)) && netgame)
@ -808,12 +808,12 @@ FString D_GetUserInfoStrings(int pnum, bool compact)
return result;
}
void D_ReadUserInfoStrings (int pnum, uint8_t **stream, bool update)
void D_ReadUserInfoStrings (int pnum, TArrayView<uint8_t>& stream, bool update)
{
userinfo_t *info = &players[pnum].userinfo;
TArray<FName> compact_names(info->CountUsed());
FBaseCVar **cvar_ptr;
const char *ptr = *((const char **)stream);
const char *ptr = (const char *)stream.Data();
const char *breakpt;
FString value;
bool compact;
@ -944,7 +944,7 @@ void D_ReadUserInfoStrings (int pnum, uint8_t **stream, bool update)
}
}
}
*stream += strlen (*((char **)stream)) + 1;
AdvanceStream(stream, strlen((char*)stream.Data()) + 1);
}
void WriteUserInfo(FSerializer &arc, userinfo_t &info)

View file

@ -38,7 +38,10 @@
#include "cmdlib.h"
#include "serializer.h"
char* ReadString(uint8_t **stream)
// Unchecked stream functions.
// Use the checked versions instead unless you're checking the stream size yourself!
char* UncheckedReadString(uint8_t **stream)
{
char *string = *((char **)stream);
@ -46,65 +49,65 @@ char* ReadString(uint8_t **stream)
return copystring(string);
}
const char* ReadStringConst(uint8_t **stream)
const char* UncheckedReadStringConst(uint8_t **stream)
{
const char *string = *((const char **)stream);
*stream += strlen(string) + 1;
return string;
}
uint8_t ReadInt8(uint8_t **stream)
uint8_t UncheckedReadInt8(uint8_t **stream)
{
uint8_t v = **stream;
*stream += 1;
return v;
}
int16_t ReadInt16(uint8_t **stream)
int16_t UncheckedReadInt16(uint8_t **stream)
{
int16_t v = (((*stream)[0]) << 8) | (((*stream)[1]));
*stream += 2;
return v;
}
int32_t ReadInt32(uint8_t **stream)
int32_t UncheckedReadInt32(uint8_t **stream)
{
int32_t v = (((*stream)[0]) << 24) | (((*stream)[1]) << 16) | (((*stream)[2]) << 8) | (((*stream)[3]));
*stream += 4;
return v;
}
int64_t ReadInt64(uint8_t** stream)
int64_t UncheckedReadInt64(uint8_t **stream)
{
int64_t v = (int64_t((*stream)[0]) << 56) | (int64_t((*stream)[1]) << 48) | (int64_t((*stream)[2]) << 40) | (int64_t((*stream)[3]) << 32)
| (int64_t((*stream)[4]) << 24) | (int64_t((*stream)[5]) << 16) | (int64_t((*stream)[6]) << 8) | (int64_t((*stream)[7]));
| (int64_t((*stream)[4]) << 24) | (int64_t((*stream)[5]) << 16) | (int64_t((*stream)[6]) << 8) | (int64_t((*stream)[7]));
*stream += 8;
return v;
}
float ReadFloat(uint8_t **stream)
float UncheckedReadFloat(uint8_t **stream)
{
union
{
int32_t i;
float f;
} fakeint;
fakeint.i = ReadInt32 (stream);
fakeint.i = UncheckedReadInt32(stream);
return fakeint.f;
}
double ReadDouble(uint8_t** stream)
double UncheckedReadDouble(uint8_t **stream)
{
union
{
int64_t i;
double f;
} fakeint;
fakeint.i = ReadInt64(stream);
fakeint.i = UncheckedReadInt64(stream);
return fakeint.f;
}
void WriteString(const char *string, uint8_t **stream)
void UncheckedWriteString(const char *string, uint8_t **stream)
{
char *p = *((char **)stream);
@ -116,20 +119,20 @@ void WriteString(const char *string, uint8_t **stream)
*stream = (uint8_t *)p;
}
void WriteInt8(uint8_t v, uint8_t **stream)
void UncheckedWriteInt8(uint8_t v, uint8_t **stream)
{
**stream = v;
*stream += 1;
}
void WriteInt16(int16_t v, uint8_t **stream)
void UncheckedWriteInt16(int16_t v, uint8_t **stream)
{
(*stream)[0] = v >> 8;
(*stream)[1] = v & 255;
*stream += 2;
}
void WriteInt32(int32_t v, uint8_t **stream)
void UncheckedWriteInt32(int32_t v, uint8_t **stream)
{
(*stream)[0] = v >> 24;
(*stream)[1] = (v >> 16) & 255;
@ -138,7 +141,7 @@ void WriteInt32(int32_t v, uint8_t **stream)
*stream += 4;
}
void WriteInt64(int64_t v, uint8_t** stream)
void UncheckedWriteInt64(int64_t v, uint8_t **stream)
{
(*stream)[0] = v >> 56;
(*stream)[1] = (v >> 48) & 255;
@ -151,7 +154,231 @@ void WriteInt64(int64_t v, uint8_t** stream)
*stream += 8;
}
void WriteFloat(float v, uint8_t **stream)
void UncheckedWriteFloat(float v, uint8_t **stream)
{
union
{
int32_t i;
float f;
} fakeint;
fakeint.f = v;
UncheckedWriteInt32(fakeint.i, stream);
}
void UncheckedWriteDouble(double v, uint8_t **stream)
{
union
{
int64_t i;
double f;
} fakeint;
fakeint.f = v;
UncheckedWriteInt64(fakeint.i, stream);
}
void AdvanceStream(TArrayView<uint8_t>& stream, size_t bytes)
{
assert(bytes <= stream.Size());
stream = TArrayView(stream.Data() + bytes, stream.Size() - bytes);
}
// Checked stream functions
char* ReadString(TArrayView<uint8_t>& stream)
{
char *string = (char*)stream.Data();
size_t len = strnlen(string, stream.Size());
if (len == stream.Size())
{
I_Error("Attempted to read past end of stream");
}
AdvanceStream(stream, len + 1);
return copystring(string);
}
const char* ReadStringConst(TArrayView<uint8_t>& stream)
{
const char* string = (const char*)stream.Data();
size_t len = strnlen(string, stream.Size());
if (len == stream.Size())
{
I_Error("Attempted to read past end of stream");
}
AdvanceStream(stream, len + 1);
return string;
}
void ReadBytes(TArrayView<uint8_t>& dst, TArrayView<uint8_t>& stream)
{
if (dst.Size() > stream.Size())
{
I_Error("Attempted to read past end of stream");
}
if (dst.Size())
{
memcpy(dst.Data(), stream.Data(), dst.Size());
AdvanceStream(stream, dst.Size());
}
}
uint8_t ReadInt8(TArrayView<uint8_t>& stream)
{
if (stream.Size() < 1)
{
I_Error("Attempted to read past end of stream");
}
uint8_t v = stream[0];
AdvanceStream(stream, 1);
return v;
}
int16_t ReadInt16(TArrayView<uint8_t>& stream)
{
if (stream.Size() < 2)
{
I_Error("Attempted to read past end of stream");
}
int16_t v = ((stream[0]) << 8) | stream[1];
AdvanceStream(stream, 2);
return v;
}
int32_t ReadInt32(TArrayView<uint8_t>& stream)
{
if (stream.Size() < 4)
{
I_Error("Attempted to read past end of stream");
}
int32_t v = (stream[0] << 24) | (stream[1] << 16) | (stream[2] << 8) | stream[3];
AdvanceStream(stream, 4);
return v;
}
int64_t ReadInt64(TArrayView<uint8_t>& stream)
{
if (stream.Size() < 8)
{
I_Error("Attempted to read past end of stream");
}
int64_t v = (int64_t(stream[0]) << 56) | (int64_t(stream[1]) << 48) | (int64_t(stream[2]) << 40) | (int64_t(stream[3]) << 32)
| (int64_t(stream[4]) << 24) | (int64_t(stream[5]) << 16) | (int64_t(stream[6]) << 8) | int64_t(stream[7]);
AdvanceStream(stream, 8);
return v;
}
float ReadFloat(TArrayView<uint8_t>& stream)
{
union
{
int32_t i;
float f;
} fakeint;
fakeint.i = ReadInt32 (stream);
return fakeint.f;
}
double ReadDouble(TArrayView<uint8_t>& stream)
{
union
{
int64_t i;
double f;
} fakeint;
fakeint.i = ReadInt64(stream);
return fakeint.f;
}
void WriteString(const char *string, TArrayView<uint8_t>& stream)
{
char *p = (char *)stream.Data();
unsigned int remaining = stream.Size();
while (*string) {
if (remaining-- == 0)
{
I_Error("Attempted to write past end of stream");
}
*p++ = *string++;
}
if (remaining == 0)
{
I_Error("Attempted to write past end of stream");
}
*p++ = 0;
AdvanceStream(stream, p - (char*)stream.Data());
}
void WriteBytes(const TArrayView<uint8_t>& source, TArrayView<uint8_t>& stream)
{
if (source.Size() > stream.Size())
{
I_Error("Attempted to write past end of stream");
}
if (source.Size())
{
memcpy(stream.Data(), source.Data(), source.Size());
AdvanceStream(stream, source.Size());
}
}
void WriteFString(FString& string, TArrayView<uint8_t>& stream)
{
WriteBytes(string.GetTArrayView(), stream);
}
void WriteInt8(uint8_t v, TArrayView<uint8_t>& stream)
{
if (stream.Size() < 1)
{
I_Error("Attempted to write past end of stream");
}
stream[0] = v;
AdvanceStream(stream, 1);
}
void WriteInt16(int16_t v, TArrayView<uint8_t>& stream)
{
if (stream.Size() < 2)
{
I_Error("Attempted to write past end of stream");
}
stream[0] = v >> 8;
stream[1] = v & 255;
AdvanceStream(stream, 2);
}
void WriteInt32(int32_t v, TArrayView<uint8_t>& stream)
{
if (stream.Size() < 4)
{
I_Error("Attempted to write past end of stream");
}
stream[0] = v >> 24;
stream[1] = (v >> 16) & 255;
stream[2] = (v >> 8) & 255;
stream[3] = v & 255;
AdvanceStream(stream, 4);
}
void WriteInt64(int64_t v, TArrayView<uint8_t>& stream)
{
if (stream.Size() < 8)
{
I_Error("Attempted to write past end of stream");
}
stream[0] = v >> 56;
stream[1] = (v >> 48) & 255;
stream[2] = (v >> 40) & 255;
stream[3] = (v >> 32) & 255;
stream[4] = (v >> 24) & 255;
stream[5] = (v >> 16) & 255;
stream[6] = (v >> 8) & 255;
stream[7] = v & 255;
AdvanceStream(stream, 8);
}
void WriteFloat(float v, TArrayView<uint8_t>& stream)
{
union
{
@ -162,7 +389,7 @@ void WriteFloat(float v, uint8_t **stream)
WriteInt32 (fakeint.i, stream);
}
void WriteDouble(double v, uint8_t** stream)
void WriteDouble(double v, TArrayView<uint8_t>& stream)
{
union
{
@ -193,11 +420,8 @@ FSerializer& Serialize(FSerializer& arc, const char* key, usercmd_t& cmd, usercm
return arc;
}
// Returns the number of bytes read
int UnpackUserCmd(usercmd_t& cmd, const usercmd_t* basis, uint8_t*& stream)
void UnpackUserCmd(usercmd_t& cmd, const usercmd_t* basis, TArrayView<uint8_t>& stream)
{
const uint8_t* start = stream;
if (basis != nullptr)
{
if (basis != &cmd)
@ -208,7 +432,7 @@ int UnpackUserCmd(usercmd_t& cmd, const usercmd_t* basis, uint8_t*& stream)
memset(&cmd, 0, sizeof(usercmd_t));
}
uint8_t flags = ReadInt8(&stream);
uint8_t flags = ReadInt8(stream);
if (flags)
{
// We can support up to 29 buttons using 1 to 4 bytes to store them. The most
@ -216,19 +440,19 @@ int UnpackUserCmd(usercmd_t& cmd, const usercmd_t* basis, uint8_t*& stream)
// should be read in excluding the last one which supports all 8 bits.
if (flags & UCMDF_BUTTONS)
{
uint8_t in = ReadInt8(&stream);
uint8_t in = ReadInt8(stream);
uint32_t buttons = (cmd.buttons & ~0x7F) | (in & 0x7F);
if (in & MoreButtons)
{
in = ReadInt8(&stream);
in = ReadInt8(stream);
buttons = (buttons & ~(0x7F << 7)) | ((in & 0x7F) << 7);
if (in & MoreButtons)
{
in = ReadInt8(&stream);
in = ReadInt8(stream);
buttons = (buttons & ~(0x7F << 14)) | ((in & 0x7F) << 14);
if (in & MoreButtons)
{
in = ReadInt8(&stream);
in = ReadInt8(stream);
buttons = (buttons & ~(0xFF << 21)) | (in << 21);
}
}
@ -236,26 +460,24 @@ int UnpackUserCmd(usercmd_t& cmd, const usercmd_t* basis, uint8_t*& stream)
cmd.buttons = buttons;
}
if (flags & UCMDF_PITCH)
cmd.pitch = ReadInt16(&stream);
cmd.pitch = ReadInt16(stream);
if (flags & UCMDF_YAW)
cmd.yaw = ReadInt16(&stream);
cmd.yaw = ReadInt16(stream);
if (flags & UCMDF_FORWARDMOVE)
cmd.forwardmove = ReadInt16(&stream);
cmd.forwardmove = ReadInt16(stream);
if (flags & UCMDF_SIDEMOVE)
cmd.sidemove = ReadInt16(&stream);
cmd.sidemove = ReadInt16(stream);
if (flags & UCMDF_UPMOVE)
cmd.upmove = ReadInt16(&stream);
cmd.upmove = ReadInt16(stream);
if (flags & UCMDF_ROLL)
cmd.roll = ReadInt16(&stream);
cmd.roll = ReadInt16(stream);
}
return int(stream - start);
}
int PackUserCmd(const usercmd_t& cmd, const usercmd_t* basis, uint8_t*& stream)
void PackUserCmd(const usercmd_t& cmd, const usercmd_t* basis, TArrayView<uint8_t>& stream)
{
uint8_t flags = 0;
uint8_t* start = stream;
auto flagsPosition = TArrayView(stream.Data(), 1);
usercmd_t blank;
if (basis == nullptr)
@ -264,7 +486,7 @@ int PackUserCmd(const usercmd_t& cmd, const usercmd_t* basis, uint8_t*& stream)
basis = &blank;
}
++stream; // Make room for the flags.
AdvanceStream(stream, 1); // Make room for the flags.
uint32_t buttons_changed = cmd.buttons ^ basis->buttons;
if (buttons_changed != 0)
{
@ -284,56 +506,54 @@ int PackUserCmd(const usercmd_t& cmd, const usercmd_t* basis, uint8_t*& stream)
bytes[2] |= MoreButtons;
}
}
WriteInt8(bytes[0], &stream);
WriteInt8(bytes[0], stream);
if (bytes[0] & MoreButtons)
{
WriteInt8(bytes[1], &stream);
WriteInt8(bytes[1], stream);
if (bytes[1] & MoreButtons)
{
WriteInt8(bytes[2], &stream);
WriteInt8(bytes[2], stream);
if (bytes[2] & MoreButtons)
WriteInt8(bytes[3], &stream);
WriteInt8(bytes[3], stream);
}
}
}
if (cmd.pitch != basis->pitch)
{
flags |= UCMDF_PITCH;
WriteInt16(cmd.pitch, &stream);
WriteInt16(cmd.pitch, stream);
}
if (cmd.yaw != basis->yaw)
{
flags |= UCMDF_YAW;
WriteInt16 (cmd.yaw, &stream);
WriteInt16 (cmd.yaw, stream);
}
if (cmd.forwardmove != basis->forwardmove)
{
flags |= UCMDF_FORWARDMOVE;
WriteInt16 (cmd.forwardmove, &stream);
WriteInt16 (cmd.forwardmove, stream);
}
if (cmd.sidemove != basis->sidemove)
{
flags |= UCMDF_SIDEMOVE;
WriteInt16(cmd.sidemove, &stream);
WriteInt16(cmd.sidemove, stream);
}
if (cmd.upmove != basis->upmove)
{
flags |= UCMDF_UPMOVE;
WriteInt16(cmd.upmove, &stream);
WriteInt16(cmd.upmove, stream);
}
if (cmd.roll != basis->roll)
{
flags |= UCMDF_ROLL;
WriteInt16(cmd.roll, &stream);
WriteInt16(cmd.roll, stream);
}
// Write the packing bits
WriteInt8(flags, &start);
return int(stream - start);
WriteInt8(flags, flagsPosition);
}
int WriteUserCmdMessage(const usercmd_t& cmd, const usercmd_t* basis, uint8_t*& stream)
void WriteUserCmdMessage(const usercmd_t& cmd, const usercmd_t* basis, TArrayView<uint8_t>& stream)
{
if (basis == nullptr)
{
@ -341,58 +561,60 @@ int WriteUserCmdMessage(const usercmd_t& cmd, const usercmd_t* basis, uint8_t*&
|| cmd.pitch || cmd.yaw || cmd.roll
|| cmd.forwardmove || cmd.sidemove || cmd.upmove)
{
WriteInt8(DEM_USERCMD, &stream);
return PackUserCmd(cmd, basis, stream) + 1;
WriteInt8(DEM_USERCMD, stream);
PackUserCmd(cmd, basis, stream);
return;
}
}
else if (cmd.buttons != basis->buttons
|| cmd.yaw != basis->yaw || cmd.pitch != basis->pitch || cmd.roll != basis->roll
|| cmd.forwardmove != basis->forwardmove || cmd.sidemove != basis->sidemove || cmd.upmove != basis->upmove)
{
WriteInt8(DEM_USERCMD, &stream);
return PackUserCmd(cmd, basis, stream) + 1;
WriteInt8(DEM_USERCMD, stream);
PackUserCmd(cmd, basis, stream);
return;
}
WriteInt8(DEM_EMPTYUSERCMD, &stream);
return 1;
WriteInt8(DEM_EMPTYUSERCMD, stream);
}
// Reads through the user command without actually setting any of its info. Used to get the size
// of the command when getting the length of the stream.
int SkipUserCmdMessage(uint8_t*& stream)
void SkipUserCmdMessage(TArrayView<uint8_t>& stream)
{
const uint8_t* start = stream;
while (true)
{
const uint8_t type = *stream++;
const uint8_t type = ReadInt8(stream);
if (type == DEM_USERCMD)
{
int skip = 1;
if (*stream & UCMDF_PITCH)
if (stream[0] & UCMDF_PITCH)
skip += 2;
if (*stream & UCMDF_YAW)
if (stream[0] & UCMDF_YAW)
skip += 2;
if (*stream & UCMDF_FORWARDMOVE)
if (stream[0] & UCMDF_FORWARDMOVE)
skip += 2;
if (*stream & UCMDF_SIDEMOVE)
if (stream[0] & UCMDF_SIDEMOVE)
skip += 2;
if (*stream & UCMDF_UPMOVE)
if (stream[0] & UCMDF_UPMOVE)
skip += 2;
if (*stream & UCMDF_ROLL)
if (stream[0] & UCMDF_ROLL)
skip += 2;
if (*stream & UCMDF_BUTTONS)
if (stream[0] & UCMDF_BUTTONS)
{
if (*++stream & MoreButtons)
AdvanceStream(stream, 1);
if (stream[0] & MoreButtons)
{
if (*++stream & MoreButtons)
AdvanceStream(stream, 1);
if (stream[0] & MoreButtons)
{
if (*++stream & MoreButtons)
++stream;
AdvanceStream(stream, 1);
if (stream[0] & MoreButtons)
AdvanceStream(stream, 1);
}
}
}
stream += skip;
AdvanceStream(stream, skip);
break;
}
else if (type == DEM_EMPTYUSERCMD)
@ -401,31 +623,29 @@ int SkipUserCmdMessage(uint8_t*& stream)
}
else
{
Net_SkipCommand(type, &stream);
Net_SkipCommand(type, stream);
}
}
return int(stream - start);
}
int ReadUserCmdMessage(uint8_t*& stream, int player, int tic)
void ReadUserCmdMessage(TArrayView<uint8_t>& stream, int player, int tic)
{
const int ticMod = tic % BACKUPTICS;
auto& curTic = ClientStates[player].Tics[ticMod];
usercmd_t& ticCmd = curTic.Command;
const uint8_t* start = stream;
const uint8_t* start = stream.Data();
// Skip until we reach the player command. Event data will get read off once the
// tick is actually executed.
int type;
while ((type = ReadInt8(&stream)) != DEM_USERCMD && type != DEM_EMPTYUSERCMD)
Net_SkipCommand(type, &stream);
while ((type = ReadInt8(stream)) != DEM_USERCMD && type != DEM_EMPTYUSERCMD)
Net_SkipCommand(type, stream);
// Subtract a byte to account for the fact the stream head is now sitting on the
// user command.
curTic.Data.SetData(start, int(stream - start - 1));
curTic.Data.SetData(start, int(stream.Data() - start - 1));
if (type == DEM_USERCMD)
{
@ -439,8 +659,6 @@ int ReadUserCmdMessage(uint8_t*& stream, int player, int tic)
else
memset(&ticCmd, 0, sizeof(ticCmd));
}
return int(stream - start);
}
void RunPlayerCommands(int player, int tic)
@ -449,14 +667,12 @@ void RunPlayerCommands(int player, int tic)
if (gametic % TicDup)
return;
int len;
auto& data = ClientStates[player].Tics[tic % BACKUPTICS].Data;
uint8_t* stream = data.GetData(&len);
if (stream != nullptr)
auto stream = data.GetTArrayView();
if (stream.Size())
{
uint8_t* end = stream + len;
while (stream < end)
Net_DoCommand(ReadInt8(&stream), &stream, player);
while (stream.Size() > 0)
Net_DoCommand(ReadInt8(stream), stream, player);
if (!demorecording)
data.SetData(nullptr, 0);
@ -469,22 +685,23 @@ uint8_t* streamPos = nullptr;
// Write the header of an IFF chunk and leave space
// for the length field.
void StartChunk(int id, uint8_t **stream)
void StartChunk(int id, TArrayView<uint8_t>& stream)
{
WriteInt32(id, stream);
streamPos = *stream;
*stream += 4;
streamPos = stream.Data();
AdvanceStream(stream, 4);
}
// Write the length field for the chunk and insert
// pad byte if the chunk is odd-sized.
void FinishChunk(uint8_t **stream)
void FinishChunk(TArrayView<uint8_t>& stream)
{
if (streamPos == nullptr)
return;
int len = int(*stream - streamPos - 4);
WriteInt32(len, &streamPos);
int len = int(stream.Data() - streamPos - 4);
auto streamPosView = TArrayView<uint8_t>(streamPos, 4);
WriteInt32(len, streamPosView);
if (len & 1)
WriteInt8(0, stream);
@ -493,8 +710,8 @@ void FinishChunk(uint8_t **stream)
// Skip past an unknown chunk. *stream should be
// pointing to the chunk's length field.
void SkipChunk(uint8_t **stream)
void SkipChunk(TArrayView<uint8_t>& stream)
{
int len = ReadInt32(stream);
*stream += len + (len & 1);
AdvanceStream(stream, len + (len & 1));
}

View file

@ -229,33 +229,53 @@ enum ECheatCommand
CHT_MASSACRE2
};
void StartChunk (int id, uint8_t **stream);
void FinishChunk (uint8_t **stream);
void SkipChunk (uint8_t **stream);
void StartChunk (int id, TArrayView<uint8_t>& stream);
void FinishChunk (TArrayView<uint8_t>& stream);
void SkipChunk (TArrayView<uint8_t>& stream);
// Returns the number of bytes written to the stream
int UnpackUserCmd(usercmd_t& ucmd, const usercmd_t* basis, uint8_t*& stream);
int PackUserCmd(const usercmd_t& ucmd, const usercmd_t* basis, uint8_t*& stream);
int WriteUserCmdMessage(const usercmd_t& ucmd, const usercmd_t *basis, uint8_t*& stream);
void UnpackUserCmd(usercmd_t& ucmd, const usercmd_t* basis, TArrayView<uint8_t>& stream);
void PackUserCmd(const usercmd_t& ucmd, const usercmd_t* basis, TArrayView<uint8_t>& stream);
void WriteUserCmdMessage(const usercmd_t& ucmd, const usercmd_t *basis, TArrayView<uint8_t>& stream);
int SkipUserCmdMessage(uint8_t*& stream);
int ReadUserCmdMessage(uint8_t*& stream, int player, int tic);
void SkipUserCmdMessage(TArrayView<uint8_t>& stream);
void ReadUserCmdMessage(TArrayView<uint8_t>& stream, int player, int tic);
void RunPlayerCommands(int player, int tic);
uint8_t ReadInt8 (uint8_t **stream);
int16_t ReadInt16 (uint8_t **stream);
int32_t ReadInt32 (uint8_t **stream);
int64_t ReadInt64(uint8_t** stream);
float ReadFloat (uint8_t **stream);
double ReadDouble(uint8_t** stream);
char *ReadString (uint8_t **stream);
const char *ReadStringConst(uint8_t **stream);
void WriteInt8 (uint8_t val, uint8_t **stream);
void WriteInt16 (int16_t val, uint8_t **stream);
void WriteInt32 (int32_t val, uint8_t **stream);
void WriteInt64(int64_t val, uint8_t** stream);
void WriteFloat (float val, uint8_t **stream);
void WriteDouble(double val, uint8_t** stream);
void WriteString (const char *string, uint8_t **stream);
uint8_t UncheckedReadInt8(uint8_t** stream);
int16_t UncheckedReadInt16(uint8_t** stream);
int32_t UncheckedReadInt32(uint8_t** stream);
int64_t UncheckedReadInt64(uint8_t** stream);
float UncheckedReadFloat(uint8_t** stream);
double UncheckedReadDouble(uint8_t** stream);
char* UncheckedReadString(uint8_t** stream);
const char* UncheckedReadStringConst(uint8_t** stream);
void UncheckedWriteInt8(uint8_t val, uint8_t** stream);
void UncheckedWriteInt16(int16_t val, uint8_t** stream);
void UncheckedWriteInt32(int32_t val, uint8_t** stream);
void UncheckedWriteInt64(int64_t val, uint8_t** stream);
void UncheckedWriteFloat(float val, uint8_t** stream);
void UncheckedWriteDouble(double val, uint8_t** stream);
void UncheckedWriteString(const char* string, uint8_t** stream);
void AdvanceStream(TArrayView<uint8_t>& stream, size_t bytes);
uint8_t ReadInt8(TArrayView<uint8_t>& stream);
int16_t ReadInt16(TArrayView<uint8_t>& stream);
int32_t ReadInt32(TArrayView<uint8_t>& stream);
int64_t ReadInt64(TArrayView<uint8_t>& stream);
float ReadFloat(TArrayView<uint8_t>& stream);
double ReadDouble(TArrayView<uint8_t>& stream);
char* ReadString(TArrayView<uint8_t>& stream);
const char* ReadStringConst(TArrayView<uint8_t>& stream);
void ReadBytes(TArrayView<uint8_t>& dst, TArrayView<uint8_t>& stream);
void WriteInt8(uint8_t val, TArrayView<uint8_t>& stream);
void WriteInt16(int16_t val, TArrayView<uint8_t>& stream);
void WriteInt32(int32_t val, TArrayView<uint8_t>& stream);
void WriteInt64(int64_t val, TArrayView<uint8_t>& stream);
void WriteFloat(float val, TArrayView<uint8_t>& stream);
void WriteDouble(double val, TArrayView<uint8_t>& stream);
void WriteString(const char* string, TArrayView<uint8_t>& stream);
void WriteBytes(const TArrayView<uint8_t>& source, TArrayView<uint8_t>& stream);
void WriteFString(FString& string, TArrayView<uint8_t>& stream);
#endif //__D_PROTOCOL_H__

View file

@ -178,8 +178,8 @@ bool demorecording;
bool demoplayback;
bool demonew; // [RH] Only used around G_InitNew for demos
int demover;
uint8_t* demobuffer;
uint8_t* demo_p;
TArray<uint8_t> demobuffer;
TArrayView<uint8_t> demo_p;
uint8_t* democompspot;
uint8_t* demobodyspot;
size_t maxdemosize;
@ -2089,8 +2089,8 @@ void G_DoLoadGame ()
arc("importantcvars", cvar);
if (!cvar.IsEmpty())
{
uint8_t *vars_p = (uint8_t *)cvar.GetChars();
C_ReadCVars(&vars_p);
auto vars_p = cvar.GetTArrayView();
C_ReadCVars(vars_p);
}
else
{
@ -2488,14 +2488,14 @@ void G_ReadDemoTiccmd (usercmd_t *cmd, int player)
while (id != DEM_USERCMD && id != DEM_EMPTYUSERCMD)
{
if (!demorecording && demo_p >= zdembodyend)
if (!demorecording && demo_p.Data() >= zdembodyend)
{
// nothing left in the BODY chunk, so end playback.
G_CheckDemoStatus ();
break;
}
id = ReadInt8 (&demo_p);
id = ReadInt8 (demo_p);
switch (id)
{
@ -2514,7 +2514,7 @@ void G_ReadDemoTiccmd (usercmd_t *cmd, int player)
case DEM_DROPPLAYER:
{
uint8_t i = ReadInt8 (&demo_p);
uint8_t i = ReadInt8 (demo_p);
if (i < MAXPLAYERS)
{
playeringame[i] = false;
@ -2523,7 +2523,7 @@ void G_ReadDemoTiccmd (usercmd_t *cmd, int player)
break;
default:
Net_DoCommand (id, &demo_p, player);
Net_DoCommand (id, demo_p, player);
break;
}
}
@ -2556,8 +2556,7 @@ void G_WriteDemoTiccmd (usercmd_t *cmd, int player, int buf)
// [RH] Write any special "ticcmds" for this player to the demo
if ((specdata = ClientStates[player].Tics[buf % BACKUPTICS].Data.GetData (&speclen)) && !(gametic % TicDup))
{
memcpy (demo_p, specdata, speclen);
demo_p += speclen;
WriteBytes(TArrayView(specdata, speclen), demo_p);
ClientStates[player].Tics[buf % BACKUPTICS].Data.SetData(nullptr, 0);
}
@ -2566,19 +2565,19 @@ void G_WriteDemoTiccmd (usercmd_t *cmd, int player, int buf)
WriteUserCmdMessage (*cmd, &players[player].cmd, demo_p);
// [RH] Bigger safety margin
if (demo_p > demobuffer + maxdemosize - 64)
if (demo_p.Data() > demobuffer.Data() + demobuffer.Size() - 64)
{
ptrdiff_t pos = demo_p - demobuffer;
ptrdiff_t spot = streamPos - demobuffer;
ptrdiff_t comp = democompspot - demobuffer;
ptrdiff_t body = demobodyspot - demobuffer;
ptrdiff_t pos = demo_p.Data() - demobuffer.Data();
ptrdiff_t spot = streamPos - demobuffer.Data();
ptrdiff_t comp = democompspot - demobuffer.Data();
ptrdiff_t body = demobodyspot - demobuffer.Data();
// [RH] Allocate more space for the demo
maxdemosize += 0x20000;
demobuffer = (uint8_t *)M_Realloc (demobuffer, maxdemosize);
demo_p = demobuffer + pos;
streamPos = demobuffer + spot;
democompspot = demobuffer + comp;
demobodyspot = demobuffer + body;
demobuffer.Resize(maxdemosize);
demo_p = TArrayView(demobuffer.Data() + pos, demobuffer.Size() - pos);
streamPos = demobuffer.Data() + spot;
democompspot = demobuffer.Data() + comp;
demobodyspot = demobuffer.Data() + body;
}
}
@ -2594,7 +2593,7 @@ void G_RecordDemo (const char* name)
FixPathSeperator (demoname);
DefaultExtension (demoname, ".lmp");
maxdemosize = 0x20000;
demobuffer = (uint8_t *)M_Malloc (maxdemosize);
demobuffer.Resize(maxdemosize);
demorecording = true;
}
@ -2611,35 +2610,33 @@ void G_BeginRecording (const char *startmap)
{
startmap = primaryLevel->MapName.GetChars();
}
demo_p = demobuffer;
demo_p = TArrayView(demobuffer.Data(), demobuffer.Size());
WriteInt32 (FORM_ID, &demo_p); // Write FORM ID
demo_p += 4; // Leave space for len
WriteInt32 (ZDEM_ID, &demo_p); // Write ZDEM ID
WriteInt32 (FORM_ID, demo_p); // Write FORM ID
AdvanceStream(demo_p, 4); // Leave space for len
WriteInt32 (ZDEM_ID, demo_p); // Write ZDEM ID
// Write header chunk
StartChunk (ZDHD_ID, &demo_p);
WriteInt16 (DEMOGAMEVERSION, &demo_p); // Write ZDoom version
*demo_p++ = 2; // Write minimum version needed to use this demo.
*demo_p++ = 3; // (Useful?)
StartChunk (ZDHD_ID, demo_p);
WriteInt16 (DEMOGAMEVERSION, demo_p); // Write ZDoom version
WriteInt8(2, demo_p); // Write minimum version needed to use this demo.
WriteInt8(3, demo_p); // (Useful?)
strcpy((char*)demo_p, startmap); // Write name of map demo was recorded on.
demo_p += strlen(startmap) + 1;
WriteInt32(rngseed, &demo_p); // Write RNG seed
*demo_p++ = consoleplayer;
FinishChunk (&demo_p);
WriteString(startmap, demo_p); // Write name of map demo was recorded on.
WriteInt32(rngseed, demo_p); // Write RNG seed
WriteInt8(consoleplayer, demo_p);
FinishChunk (demo_p);
// Write player info chunks
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
StartChunk(UINF_ID, &demo_p);
WriteInt8((uint8_t)i, &demo_p);
StartChunk(UINF_ID, demo_p);
WriteInt8((uint8_t)i, demo_p);
auto str = D_GetUserInfoStrings(i);
memcpy(demo_p, str.GetChars(), str.Len() + 1);
demo_p += str.Len() + 1;
FinishChunk(&demo_p);
WriteFString(str, demo_p);
FinishChunk(demo_p);
}
}
@ -2648,29 +2645,29 @@ void G_BeginRecording (const char *startmap)
// enough.
if (multiplayer)
{
StartChunk (NETD_ID, &demo_p);
FinishChunk (&demo_p);
StartChunk (NETD_ID, demo_p);
FinishChunk (demo_p);
}
// Write cvars chunk
StartChunk (VARS_ID, &demo_p);
C_WriteCVars (&demo_p, CVAR_SERVERINFO|CVAR_DEMOSAVE);
FinishChunk (&demo_p);
StartChunk (VARS_ID, demo_p);
C_WriteCVars (demo_p, CVAR_SERVERINFO|CVAR_DEMOSAVE);
FinishChunk (demo_p);
// Write weapon ordering chunk
StartChunk (WEAP_ID, &demo_p);
P_WriteDemoWeaponsChunk(&demo_p);
FinishChunk (&demo_p);
StartChunk (WEAP_ID, demo_p);
P_WriteDemoWeaponsChunk(demo_p);
FinishChunk (demo_p);
// Indicate body is compressed
StartChunk (COMP_ID, &demo_p);
democompspot = demo_p;
WriteInt32 (0, &demo_p);
FinishChunk (&demo_p);
StartChunk (COMP_ID, demo_p);
democompspot = demo_p.Data();
WriteInt32 (0, demo_p);
FinishChunk (demo_p);
// Begin BODY chunk
StartChunk (BODY_ID, &demo_p);
demobodyspot = demo_p;
StartChunk (BODY_ID, demo_p);
demobodyspot = demo_p.Data();
}
@ -2730,13 +2727,13 @@ bool G_ProcessIFFDemo (FString &mapname)
for (i = 0; i < MAXPLAYERS; i++)
playeringame[i] = 0;
len = ReadInt32 (&demo_p);
zdemformend = demo_p + len + (len & 1);
len = ReadInt32 (demo_p);
zdemformend = demo_p.Data() + len + (len & 1);
// Check to make sure this is a ZDEM chunk file.
// TODO: Support multiple FORM ZDEMs in a CAT. Might be useful.
id = ReadInt32 (&demo_p);
id = ReadInt32 (demo_p);
if (id != ZDEM_ID)
{
Printf ("Not a " GAMENAME " demo file!\n");
@ -2745,11 +2742,11 @@ bool G_ProcessIFFDemo (FString &mapname)
// Process all chunks until a BODY chunk is encountered.
while (demo_p < zdemformend && !bodyHit)
while (demo_p.Data() < zdemformend && !bodyHit)
{
id = ReadInt32 (&demo_p);
len = ReadInt32 (&demo_p);
nextchunk = demo_p + len + (len & 1);
id = ReadInt32 (demo_p);
len = ReadInt32 (demo_p);
nextchunk = demo_p.Data() + len + (len & 1);
if (nextchunk > zdemformend)
{
Printf ("Demo is mangled!\n");
@ -2761,48 +2758,47 @@ bool G_ProcessIFFDemo (FString &mapname)
case ZDHD_ID:
headerHit = true;
demover = ReadInt16 (&demo_p); // ZDoom version demo was created with
demover = ReadInt16 (demo_p); // ZDoom version demo was created with
if (demover < MINDEMOVERSION)
{
Printf ("Demo requires an older version of " GAMENAME "!\n");
//return true;
}
if (ReadInt16 (&demo_p) > DEMOGAMEVERSION) // Minimum ZDoom version
if (ReadInt16 (demo_p) > DEMOGAMEVERSION) // Minimum ZDoom version
{
Printf ("Demo requires a newer version of " GAMENAME "!\n");
return true;
}
if (demover >= 0x21a)
{
mapname = (char*)demo_p;
demo_p += mapname.Len() + 1;
mapname = ReadStringConst(demo_p);
}
else
{
mapname = FString((char*)demo_p, 8);
demo_p += 8;
mapname = FString((char*)demo_p.Data(), 8);
AdvanceStream(demo_p, 8);
}
rngseed = ReadInt32 (&demo_p);
rngseed = ReadInt32 (demo_p);
// Only reset the RNG if this demo is not in conjunction with a savegame.
if (mapname[0] != 0)
{
FRandom::StaticClearRandom ();
}
consoleplayer = *demo_p++;
consoleplayer = ReadInt8(demo_p);
break;
case VARS_ID:
C_ReadCVars (&demo_p);
C_ReadCVars (demo_p);
break;
case UINF_ID:
i = ReadInt8 (&demo_p);
i = ReadInt8 (demo_p);
if (!playeringame[i])
{
playeringame[i] = 1;
numPlayers++;
}
D_ReadUserInfoStrings (i, &demo_p, false);
D_ReadUserInfoStrings (i, demo_p, false);
break;
case NETD_ID:
@ -2810,21 +2806,21 @@ bool G_ProcessIFFDemo (FString &mapname)
break;
case WEAP_ID:
P_ReadDemoWeaponsChunk(&demo_p);
P_ReadDemoWeaponsChunk(demo_p);
break;
case BODY_ID:
bodyHit = true;
zdembodyend = demo_p + len;
zdembodyend = demo_p.Data() + len;
break;
case COMP_ID:
uncompSize = ReadInt32 (&demo_p);
uncompSize = ReadInt32 (demo_p);
break;
}
if (!bodyHit)
demo_p = nextchunk;
demo_p = TArrayView(nextchunk, demo_p.Size() + demo_p.Data() - nextchunk);
}
if (!headerHit)
@ -2851,17 +2847,16 @@ bool G_ProcessIFFDemo (FString &mapname)
if (uncompSize > 0)
{
uint8_t *uncompressed = (uint8_t*)M_Malloc(uncompSize);
int r = uncompress (uncompressed, &uncompSize, demo_p, uLong(zdembodyend - demo_p));
auto uncompressed = TArray<uint8_t>(uncompSize, true);
int r = uncompress (uncompressed.Data(), &uncompSize, demo_p.Data(), uLong(zdembodyend - demo_p.Data()));
if (r != Z_OK)
{
Printf ("Could not decompress demo! %s\n", M_ZLibError(r).GetChars());
M_Free(uncompressed);
return true;
}
M_Free (demobuffer);
zdembodyend = uncompressed + uncompSize;
demobuffer = demo_p = uncompressed;
zdembodyend = uncompressed.Data() + uncompSize;
demobuffer = std::move(uncompressed);
demo_p = TArrayView(demobuffer.Data(), uncompSize);
}
return false;
@ -2878,9 +2873,9 @@ void G_DoPlayDemo (void)
demolump = fileSystem.CheckNumForFullName (defdemoname.GetChars(), true);
if (demolump >= 0)
{
int demolen = fileSystem.FileLength (demolump);
demobuffer = (uint8_t *)M_Malloc(demolen);
fileSystem.ReadFile (demolump, demobuffer);
size_t demolen = fileSystem.FileLength (demolump);
demobuffer.Resize(demolen);
fileSystem.ReadFile (demolump, demobuffer.Data());
}
else
{
@ -2891,26 +2886,26 @@ void G_DoPlayDemo (void)
{
I_Error("Unable to open demo '%s'", defdemoname.GetChars());
}
auto len = fr.GetLength();
demobuffer = (uint8_t*)M_Malloc(len);
if (fr.Read(demobuffer, len) != len)
size_t demolen = fr.GetLength();
demobuffer.Resize(demolen);
if (fr.Read(demobuffer.Data(), demolen) != demolen)
{
I_Error("Unable to read demo '%s'", defdemoname.GetChars());
}
}
demo_p = demobuffer;
demo_p = TArrayView(demobuffer.Data(), demobuffer.Size());
if (singledemo) Printf ("Playing demo %s\n", defdemoname.GetChars());
C_BackupCVars (); // [RH] Save cvars that might be affected by demo
if (ReadInt32 (&demo_p) != FORM_ID)
if (ReadInt32 (demo_p) != FORM_ID)
{
const char *eek = "Cannot play non-" GAMENAME " demos.\n";
C_ForgetCVars();
M_Free(demobuffer);
demo_p = demobuffer = NULL;
demobuffer.Reset();
demo_p = NULL;
if (singledemo)
{
I_Error ("%s", eek);
@ -2990,8 +2985,7 @@ bool G_CheckDemoStatus (void)
endtime = I_GetTime () - starttime;
C_RestoreCVars (); // [RH] Restore cvars demo might have changed
M_Free (demobuffer);
demobuffer = NULL;
demobuffer.Reset();
P_SetupWeapons_ntohton();
demoplayback = false;
@ -3034,9 +3028,7 @@ bool G_CheckDemoStatus (void)
if (demorecording)
{
uint8_t *formlen;
WriteInt8 (DEM_STOP, &demo_p);
WriteInt8 (DEM_STOP, demo_p);
if (demo_compress)
{
@ -3044,32 +3036,31 @@ bool G_CheckDemoStatus (void)
// a compressed version. If the BODY successfully compresses, the
// contents of the COMP chunk will be changed to indicate the
// uncompressed size of the BODY.
uLong len = uLong(demo_p - demobodyspot);
uLong len = uLong(demo_p.Data() - demobodyspot);
uLong outlen = (len + len/100 + 12);
TArray<Byte> compressed(outlen, true);
int r = compress2 (compressed.Data(), &outlen, demobodyspot, len, 9);
if (r == Z_OK && outlen < len)
{
formlen = democompspot;
WriteInt32 (len, &democompspot);
UncheckedWriteInt32 (len, &democompspot);
memcpy (demobodyspot, compressed.Data(), outlen);
demo_p = demobodyspot + outlen;
demo_p = TArrayView(demobodyspot + outlen, demo_p.Size() + len - outlen);
}
}
FinishChunk (&demo_p);
formlen = demobuffer + 4;
WriteInt32 (int(demo_p - demobuffer - 8), &formlen);
FinishChunk (demo_p);
uint8_t* formlen = demobuffer.Data() + 4;
UncheckedWriteInt32 (int(demo_p.Data() - demobuffer.Data() - 8), &formlen);
auto fw = FileWriter::Open(demoname.GetChars());
bool saved = false;
if (fw != nullptr)
{
const size_t size = demo_p - demobuffer;
saved = fw->Write(demobuffer, size) == size;
const size_t size = demo_p.Data() - demobuffer.Data();
saved = fw->Write(demobuffer.Data(), size) == size;
delete fw;
if (!saved) RemoveFile(demoname.GetChars());
}
M_Free (demobuffer);
demobuffer.Reset();
demorecording = false;
stoprecording = false;
if (saved)

View file

@ -855,7 +855,7 @@ static int ntoh_cmp(const void *a, const void *b)
//
//===========================================================================
void P_WriteDemoWeaponsChunk(uint8_t **demo)
void P_WriteDemoWeaponsChunk(TArrayView<uint8_t>& demo)
{
WriteInt16(Weapons_ntoh.Size(), demo);
for (unsigned int i = 1; i < Weapons_ntoh.Size(); ++i)
@ -873,7 +873,7 @@ void P_WriteDemoWeaponsChunk(uint8_t **demo)
//
//===========================================================================
void P_ReadDemoWeaponsChunk(uint8_t **demo)
void P_ReadDemoWeaponsChunk(TArrayView<uint8_t>& demo)
{
int count, i;
PClassActor *type;
@ -938,7 +938,7 @@ void Net_WriteWeapon(PClassActor *type)
//
//===========================================================================
PClassActor *Net_ReadWeapon(uint8_t **stream)
PClassActor *Net_ReadWeapon(TArrayView<uint8_t>& stream)
{
int index;

View file

@ -120,11 +120,11 @@ public:
void P_PlaybackKeyConfWeapons(FWeaponSlots *slots);
void Net_WriteWeapon(PClassActor *type);
PClassActor *Net_ReadWeapon(uint8_t **stream);
PClassActor *Net_ReadWeapon(TArrayView<uint8_t>& stream);
void P_SetupWeapons_ntohton();
void P_WriteDemoWeaponsChunk(uint8_t **demo);
void P_ReadDemoWeaponsChunk(uint8_t **demo);
void P_WriteDemoWeaponsChunk(TArrayView<uint8_t>& demo);
void P_ReadDemoWeaponsChunk(TArrayView<uint8_t>& demo);
enum class EBobStyle

View file

@ -667,7 +667,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
//
//============================================================================
void P_ConversationCommand (int netcode, int pnum, uint8_t **stream)
void P_ConversationCommand (int netcode, int pnum, TArrayView<uint8_t>& stream)
{
player_t *player = &players[pnum];

View file

@ -70,7 +70,7 @@ void P_FreeStrifeConversations ();
void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveangle);
void P_ResumeConversation ();
void P_ConversationCommand (int netcode, int player, uint8_t **stream);
void P_ConversationCommand (int netcode, int player, TArrayView<uint8_t>& stream);
#endif

View file

@ -120,7 +120,7 @@ public:
void Init ();
void End();
bool SpawnBot (const char *name, int color = NOCOLOR);
void TryAddBot (FLevelLocals *Level, uint8_t **stream, int player);
void TryAddBot (FLevelLocals *Level, TArrayView<uint8_t>& stream, int player);
void RemoveAllBots (FLevelLocals *Level, bool fromlist);
bool LoadBots ();
void ForgetBots ();
@ -154,7 +154,7 @@ public:
private:
//(b_game.cpp)
bool DoAddBot (FLevelLocals *Level, uint8_t *info, botskill_t skill);
bool DoAddBot (FLevelLocals *Level, TArrayView<uint8_t> info, botskill_t skill);
protected:
bool ctf;

View file

@ -319,7 +319,7 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
return true;
}
void FCajunMaster::TryAddBot (FLevelLocals *Level, uint8_t **stream, int player)
void FCajunMaster::TryAddBot (FLevelLocals *Level, TArrayView<uint8_t>& stream, int player)
{
int botshift = ReadInt8 (stream);
char *info = ReadString (stream);
@ -342,7 +342,7 @@ void FCajunMaster::TryAddBot (FLevelLocals *Level, uint8_t **stream, int player)
}
}
if (DoAddBot (Level,(uint8_t *)info, skill))
if (DoAddBot (Level, TArrayView((uint8_t*)info, strlen(info)+1), skill))
{
//Increment this.
botnum++;
@ -363,7 +363,7 @@ void FCajunMaster::TryAddBot (FLevelLocals *Level, uint8_t **stream, int player)
delete[] info;
}
bool FCajunMaster::DoAddBot (FLevelLocals *Level, uint8_t *info, botskill_t skill)
bool FCajunMaster::DoAddBot (FLevelLocals *Level, TArrayView<uint8_t> info, botskill_t skill)
{
int bnum;
@ -381,7 +381,7 @@ bool FCajunMaster::DoAddBot (FLevelLocals *Level, uint8_t *info, botskill_t skil
return false;
}
D_ReadUserInfoStrings (bnum, &info, false);
D_ReadUserInfoStrings (bnum, info, false);
multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost).
players[bnum].Bot = Level->CreateThinker<DBot>();