From c2b4522b8f2df776b996633de0af75b58070a401 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 24 Feb 2009 05:58:59 +0000 Subject: [PATCH] - Changed DEM_ADDSLOTDEFAULT, DEM_ADDSLOT, and DEM_SETSLOT to be more space- efficient. SVN r1445 (trunk) --- docs/rh-log.txt | 2 + src/d_main.cpp | 1 + src/d_net.cpp | 40 +++--- src/d_protocol.h | 1 + src/g_game.cpp | 20 ++- src/g_level.cpp | 2 +- src/g_shared/a_pickups.h | 13 +- src/g_shared/a_weapons.cpp | 243 ++++++++++++++++++++++++++++------- src/g_shared/shared_sbar.cpp | 2 +- src/tarray.h | 2 +- src/version.h | 6 +- 11 files changed, 247 insertions(+), 85 deletions(-) diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 1f05c1f13..9500c0edd 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,6 @@ February 23, 2009 +- Changed DEM_ADDSLOTDEFAULT, DEM_ADDSLOT, and DEM_SETSLOT to be more space- + efficient. - Fixed: player->oldbuttons was copied from the current button set at the end of P_PlayerThink(), well before ACS could ever get at it with GetPlayerInput. diff --git a/src/d_main.cpp b/src/d_main.cpp index 34d9eb4ac..372510e0e 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2605,6 +2605,7 @@ void D_DoomMain (void) StartScreen->LoadingStatus ("Init game engine", 0x3f); P_Init (); + P_SetupWeapons_ntohton(); //SBarInfo support. SBarInfo::Load(); diff --git a/src/d_net.cpp b/src/d_net.cpp index ce16d48d2..8c45a5b33 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2351,35 +2351,33 @@ void Net_DoCommand (int type, BYTE **stream, int player) case DEM_SETSLOT: { - BYTE playernum = ReadByte(stream); - BYTE slot = ReadByte(stream); - BYTE count = ReadByte(stream); - TArray weapons; - - weapons.Resize(count); - for(int i = 0; i < count; i++) + unsigned int slot = ReadByte(stream); + int count = ReadByte(stream); + if (slot < NUM_WEAPON_SLOTS) { - weapons[i] = ReadStringConst(stream); + players[player].weapons.Slots[slot].Clear(); + } + for(int i = 0; i < count; ++i) + { + const PClass *wpn = Net_ReadWeapon(stream); + players[player].weapons.AddSlot(slot, wpn, player == consoleplayer); } - players[playernum].weapons.SetSlot(slot, weapons); } break; case DEM_ADDSLOT: { - BYTE playernum = ReadByte(stream); - BYTE slot = ReadByte(stream); - const char *weap = ReadStringConst(stream); - players[playernum].weapons.AddSlot(slot, weap); + int slot = ReadByte(stream); + const PClass *wpn = Net_ReadWeapon(stream); + players[player].weapons.AddSlot(slot, wpn, player == consoleplayer); } break; case DEM_ADDSLOTDEFAULT: { - BYTE playernum = ReadByte(stream); - BYTE slot = ReadByte(stream); - const char *weap = ReadStringConst(stream); - players[playernum].weapons.AddSlotDefault(slot, weap); + int slot = ReadByte(stream); + const PClass *wpn = Net_ReadWeapon(stream); + players[player].weapons.AddSlotDefault(slot, wpn, player == consoleplayer); } break; @@ -2503,17 +2501,17 @@ void Net_SkipCommand (int type, BYTE **stream) case DEM_SETSLOT: { - skip = 3; - for(int numweapons = *(*stream + 2); numweapons > 0; numweapons--) + skip = 2; + for(int numweapons = (*stream)[1]; numweapons > 0; numweapons--) { - skip += strlen ((char *)(*stream + skip)) + 1; + skip += 1 + ((*stream)[skip] >> 7); } } break; case DEM_ADDSLOT: case DEM_ADDSLOTDEFAULT: - skip = strlen ((char *)(*stream + 2)) + 3; + skip = 2 + ((*stream)[1] >> 7); break; diff --git a/src/d_protocol.h b/src/d_protocol.h index 9812ca3f2..19de6b287 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -48,6 +48,7 @@ #define COMP_ID BIGE_ID('C','O','M','P') #define BODY_ID BIGE_ID('B','O','D','Y') #define NETD_ID BIGE_ID('N','E','T','D') +#define WEAP_ID BIGE_ID('W','E','A','P') #define ANGLE2SHORT(x) ((((x)/360) & 65535) #define SHORT2ANGLE(x) ((x)*360) diff --git a/src/g_game.cpp b/src/g_game.cpp index 1273e023e..f2fc66875 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -2175,6 +2175,11 @@ void G_BeginRecording (const char *startmap) 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); + // Indicate body is compressed StartChunk (COMP_ID, &demo_p); democompspot = demo_p; @@ -2301,6 +2306,10 @@ bool G_ProcessIFFDemo (char *mapname) multiplayer = true; break; + case WEAP_ID: + P_ReadDemoWeaponsChunk(&demo_p); + break; + case BODY_ID: bodyHit = true; zdembodyend = demo_p + len; @@ -2456,19 +2465,16 @@ bool G_CheckDemoStatus (void) endtime = I_GetTime (false) - starttime; C_RestoreCVars (); // [RH] Restore cvars demo might have changed - M_Free (demobuffer); + + P_SetupWeapons_ntohton(); demoplayback = false; netdemo = false; netgame = false; multiplayer = false; singletics = false; - { - int i; - - for (i = 1; i < MAXPLAYERS; i++) - playeringame[i] = 0; - } + for (int i = 1; i < MAXPLAYERS; i++) + playeringame[i] = 0; consoleplayer = 0; players[0].camera = NULL; StatusBar->AttachToPlayer (&players[0]); diff --git a/src/g_level.cpp b/src/g_level.cpp index 92e992e26..dbdb97c44 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -922,7 +922,7 @@ void G_DoLoadLevel (int position, bool autosave) } P_SetupLevel (level.mapname, position); - P_CompleteWeaponSetup(consoleplayer, players[consoleplayer].mo->GetClass()); + P_CompleteWeaponSetup(); AM_LevelInit(); diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 50c65a6bc..d95e04dd1 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -73,14 +73,19 @@ struct FWeaponSlots int RestoreSlots (FConfigFile *config, const char *section); void PrintSettings(); - void SetSlot(int slot, TArray argv); - void AddSlot(int slot, const char *name); - void AddSlotDefault(int slot, const char *name); + void AddSlot(int slot, const PClass *type, bool feedback); + void AddSlotDefault(int slot, const PClass *type, bool feedback); }; void P_PlaybackKeyConfWeapons(); -void P_CompleteWeaponSetup(int playernum, const PClass *type); +void P_CompleteWeaponSetup(); +void Net_WriteWeapon(const PClass *type); +const PClass *Net_ReadWeapon(BYTE **stream); + +void P_SetupWeapons_ntohton(); +void P_WriteDemoWeaponsChunk(BYTE **demo); +void P_ReadDemoWeaponsChunk(BYTE **demo); /************************************************************************/ /* Class definitions */ diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index 26927916c..a125cc555 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -30,6 +30,11 @@ FString WeaponSection; TArray KeyConfWeapons; bool PlayingKeyConf; +TArray Weapons_ntoh; +TMap Weapons_hton; + +static int STACK_ARGS ntoh_cmp(const void *a, const void *b); + //=========================================================================== // // AWeapon :: Serialize @@ -1132,21 +1137,22 @@ void FWeaponSlots::CompleteSetup(const PClass *type) } } -void P_CompleteWeaponSetup(int playernum, const PClass *type) +void P_CompleteWeaponSetup() { // Set up the weapon slots locally - LocalWeapons.CompleteSetup(type); + LocalWeapons.CompleteSetup(players[consoleplayer].mo->GetClass()); // Now transmit them across the network for (int i = 0; i < NUM_WEAPON_SLOTS; ++i) { - Net_WriteByte(DEM_SETSLOT); - Net_WriteByte(playernum); - Net_WriteByte(i); - Net_WriteByte(LocalWeapons.Slots[i].Size()); - for(int j = 0; j < LocalWeapons.Slots[i].Size(); j++) + if (LocalWeapons.Slots[i].Size() > 0) { - const PClass *cls = LocalWeapons.Slots[i].GetWeapon(j); - if (cls != NULL) Net_WriteString(cls->TypeName.GetChars()); + Net_WriteByte(DEM_SETSLOT); + Net_WriteByte(i); + Net_WriteByte(LocalWeapons.Slots[i].Size()); + for(int j = 0; j < LocalWeapons.Slots[i].Size(); j++) + { + Net_WriteWeapon(LocalWeapons.Slots[i].GetWeapon(j)); + } } } } @@ -1214,18 +1220,6 @@ int FWeaponSlots::RestoreSlots(FConfigFile *config, const char *section) // CCMD setslot // //=========================================================================== -void FWeaponSlots::SetSlot(int slot, TArray argv) -{ - Slots[slot].Clear(); - for (int i = 0; i < argv.Size(); ++i) - { - if (!Slots[slot].AddWeapon (argv[i])) - { - Printf ("Could not add %s to slot %d\n", argv[i], slot); - } - } -} - void FWeaponSlots::PrintSettings() { @@ -1272,15 +1266,11 @@ CCMD (setslot) } else if (PlayingKeyConf) { - BYTE count = argv.argc()-2; - TArray weapons; - - weapons.Resize(count); - for(int i = 0; i < count; i++) + LocalWeapons.Slots[slot].Clear(); + for (int i = 2; i < argv.argc(); ++i) { - weapons[i] = argv[i+2]; + LocalWeapons.Slots[slot].AddWeapon(argv[i]); } - LocalWeapons.SetSlot(slot, weapons); } else { @@ -1290,12 +1280,11 @@ CCMD (setslot) } Net_WriteByte(DEM_SETSLOT); - Net_WriteByte(consoleplayer); Net_WriteByte(slot); Net_WriteByte(argv.argc()-2); for (int i = 2; i < argv.argc(); i++) { - Net_WriteString(argv[i]); + Net_WriteWeapon(PClass::FindClass(argv[i])); } } } @@ -1306,11 +1295,11 @@ CCMD (setslot) // //=========================================================================== -void FWeaponSlots::AddSlot(int slot, const char *name) +void FWeaponSlots::AddSlot(int slot, const PClass *type, bool feedback) { - if (!Slots[slot].AddWeapon (name)) + if (type != NULL && !Slots[slot].AddWeapon(type) && feedback) { - Printf ("Could not add %s to slot %zu\n", name, slot); + Printf ("Could not add %s to slot %zu\n", type->TypeName.GetChars(), slot); } } @@ -1330,14 +1319,13 @@ CCMD (addslot) } else if (PlayingKeyConf) { - LocalWeapons.AddSlot(int(slot), argv[2]); + LocalWeapons.AddSlot(int(slot), PClass::FindClass(argv[2]), false); } else { Net_WriteByte(DEM_ADDSLOT); - Net_WriteByte(consoleplayer); Net_WriteByte(slot); - Net_WriteString(argv[2]); + Net_WriteWeapon(PClass::FindClass(argv[2])); } } @@ -1360,15 +1348,17 @@ CCMD (weaponsection) // CCMD addslotdefault // //=========================================================================== -void FWeaponSlots::AddSlotDefault(int slot, const char *name) +void FWeaponSlots::AddSlotDefault(int slot, const PClass *type, bool feedback) { - const PClass *type = PClass::FindClass (name); - if (type != NULL && type->IsDescendantOf (RUNTIME_CLASS(AWeapon))) + if (type != NULL && type->IsDescendantOf(RUNTIME_CLASS(AWeapon))) { - switch (AddDefaultWeapon (slot, type)) + switch (AddDefaultWeapon(slot, type)) { case SLOTDEF_Full: - Printf ("Could not add %s to slot %d\n", name, slot); + if (feedback) + { + Printf ("Could not add %s to slot %d\n", type->TypeName.GetChars(), slot); + } break; default: @@ -1405,14 +1395,13 @@ CCMD (addslotdefault) } else if (PlayingKeyConf) { - LocalWeapons.AddSlotDefault(int(slot), argv[2]); + LocalWeapons.AddSlotDefault(int(slot), PClass::FindClass(argv[2]), false); } else { Net_WriteByte(DEM_ADDSLOTDEFAULT); - Net_WriteByte(consoleplayer); Net_WriteByte(slot); - Net_WriteString(argv[2]); + Net_WriteWeapon(PClass::FindClass(argv[2])); } } @@ -1437,12 +1426,172 @@ void P_PlaybackKeyConfWeapons() //=========================================================================== // -// CCMD dumpslots +// P_SetupWeapons_ntohton // -// Dumps a config-friendly listing of the current slot assignments. +// Initializes the ntoh and hton maps for weapon types. To populate the ntoh +// array, weapons are sorted first by game, then lexicographically. Weapons +// from the current game are sorted first, followed by weapons for all other +// games, and within each block, they are sorted by name. // //=========================================================================== -CCMD (dumpslots) +void P_SetupWeapons_ntohton() { + unsigned int i; + const PClass *cls; + + Weapons_ntoh.Clear(); + Weapons_hton.Clear(); + + cls = NULL; + Weapons_ntoh.Push(cls); // Index 0 is always NULL. + for (i = 0; i < PClass::m_Types.Size(); ++i) + { + PClass *cls = PClass::m_Types[i]; + + if (cls->ActorInfo != NULL && cls->IsDescendantOf(RUNTIME_CLASS(AWeapon))) + { + Weapons_ntoh.Push(cls); + } + } + qsort(&Weapons_ntoh[1], Weapons_ntoh.Size() - 1, sizeof(Weapons_ntoh[0]), ntoh_cmp); + for (i = 0; i < Weapons_ntoh.Size(); ++i) + { + Weapons_hton[Weapons_ntoh[i]] = i; + } +} + +//=========================================================================== +// +// ntoh_cmp +// +// Sorting comparison function used by P_SetupWeapons_ntohton(). +// +// Weapons that filter for the current game appear first, weapons that filter +// for any game appear second, and weapons that filter for some other game +// appear last. The idea here is to try to keep all the weapons that are +// most likely to be used at the start of the list so that they only need +// one byte to transmit across the network. +// +//=========================================================================== + +static int STACK_ARGS ntoh_cmp(const void *a, const void *b) +{ + const PClass *c1 = *(const PClass **)a; + const PClass *c2 = *(const PClass **)b; + int g1 = c1->ActorInfo->GameFilter == GAME_Any ? 1 : (c1->ActorInfo->GameFilter & gameinfo.gametype) ? 0 : 2; + int g2 = c2->ActorInfo->GameFilter == GAME_Any ? 1 : (c2->ActorInfo->GameFilter & gameinfo.gametype) ? 0 : 2; + if (g1 != g2) + { + return g1 - g2; + } + return stricmp(c1->TypeName.GetChars(), c2->TypeName.GetChars()); +} + +//=========================================================================== +// +// P_WriteDemoWeaponsChunk +// +// Store the list of weapons so that adding new ones does not automatically +// break demos. +// +//=========================================================================== + +void P_WriteDemoWeaponsChunk(BYTE **demo) +{ + WriteWord(Weapons_ntoh.Size(), demo); + for (unsigned int i = 1; i < Weapons_ntoh.Size(); ++i) + { + WriteString(Weapons_ntoh[i]->TypeName.GetChars(), demo); + } +} + +//=========================================================================== +// +// P_ReadDemoWeaponsChunk +// +// Restore the list of weapons that was current at the time the demo was +// recorded. +// +//=========================================================================== + +void P_ReadDemoWeaponsChunk(BYTE **demo) +{ + int count, i; + const PClass *type; + const char *s; + + count = ReadWord(demo); + Weapons_ntoh.Resize(count); + Weapons_hton.Clear(count); + + Weapons_ntoh[0] = type = NULL; + Weapons_hton[type] = 0; + + for (i = 1; i < count; ++i) + { + s = ReadStringConst(demo); + type = PClass::FindClass(s); + // If a demo was recorded with a weapon that is no longer present, + // should we report it? + Weapons_ntoh[i] = type; + if (type != NULL) + { + Weapons_hton[type] = i; + } + } +} + +//=========================================================================== +// +// Net_WriteWeapon +// +//=========================================================================== + +void Net_WriteWeapon(const PClass *type) +{ + int index, *index_p; + + index_p = Weapons_hton.CheckKey(type); + if (index_p == NULL) + { + index = 0; + } + else + { + index = *index_p; + } + // 32767 weapons better be enough for anybody. + assert(index >= 0 && index <= 32767); + if (index < 128) + { + Net_WriteByte(index); + } + else + { + Net_WriteByte(0x80 | index); + Net_WriteByte(index >> 7); + } +} + +//=========================================================================== +// +// Net_ReadWeapon +// +//=========================================================================== + +const PClass *Net_ReadWeapon(BYTE **stream) +{ + int index; + + index = ReadByte(stream); + if (index & 0x80) + { + index = (index & 0x7F) | (ReadByte(stream) << 7); + } + if (index >= Weapons_ntoh.Size()) + { + return NULL; + } + return Weapons_ntoh[index]; } diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 46737d9d4..60fd77914 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -1180,7 +1180,7 @@ void DBaseStatusBar::Draw (EHudState state) } else if (automapactive) { - int y, i, time = level.time / TICRATE, height; + int y, time = level.time / TICRATE, height; int totaltime = level.totaltime / TICRATE; EColorRange highlight = (gameinfo.gametype & GAME_DoomChex) ? CR_UNTRANSLATED : CR_YELLOW; diff --git a/src/tarray.h b/src/tarray.h index 59d6273c3..1075f8e6a 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -390,7 +390,7 @@ typedef unsigned int hash_t; template struct THashTraits { // Returns the hash value for a key. - hash_t Hash(const KT key) { return hash_t(key); } + hash_t Hash(const KT key) { return (hash_t)(intptr_t)key; } // Compares two keys, returning zero if they are the same. int Compare(const KT left, const KT right) { return left != right; } diff --git a/src/version.h b/src/version.h index b8ff05a07..b11e719a2 100644 --- a/src/version.h +++ b/src/version.h @@ -54,7 +54,7 @@ // Version identifier for network games. // Bump it every time you do a release unless you're certain you // didn't change anything that will affect sync. -#define NETGAMEVERSION 219 +#define NETGAMEVERSION 220 // Version stored in the ini's [LastRun] section. // Bump it if you made some configuration change that you want to @@ -64,11 +64,11 @@ // Protocol version used in demos. // Bump it if you change existing DEM_ commands or add new ones. // Otherwise, it should be safe to leave it alone. -#define DEMOGAMEVERSION 0x20F +#define DEMOGAMEVERSION 0x210 // Minimum demo version we can play. // Bump it whenever you change or remove existing DEM_ commands. -#define MINDEMOVERSION 0x20F +#define MINDEMOVERSION 0x210 // SAVEVER is the version of the information stored in level snapshots. // Note that SAVEVER is not directly comparable to VERSION.