diff --git a/src/engine/console.h b/src/engine/console.h index 3ba5cdb08..3d6794c07 100644 --- a/src/engine/console.h +++ b/src/engine/console.h @@ -26,6 +26,9 @@ public: TEMPCMD_PARAMS_LENGTH=16, MAX_PRINT_CB=4, + + CLIENT_ID_GAME=-2, + CLIENT_ID_NO_GAME=-3, }; // TODO: rework this interface to reduce the amount of virtual calls @@ -94,6 +97,8 @@ public: virtual void SetAccessLevel(int AccessLevel) = 0; + virtual void ResetServerGameSettings() = 0; + // DDRace bool m_Cheated; diff --git a/src/engine/map.h b/src/engine/map.h index e9bac7906..a831cd202 100644 --- a/src/engine/map.h +++ b/src/engine/map.h @@ -14,6 +14,7 @@ public: virtual void *GetDataSwapped(int Index) = 0; virtual void UnloadData(int Index) = 0; virtual void *GetItem(int Index, int *Type, int *pID) = 0; + virtual int GetItemSize(int Index) = 0; virtual void GetType(int Type, int *pStart, int *pNum) = 0; virtual void *FindItem(int Type, int ID) = 0; virtual int NumItems() = 0; diff --git a/src/engine/server.h b/src/engine/server.h index de854523b..e2909065c 100644 --- a/src/engine/server.h +++ b/src/engine/server.h @@ -174,6 +174,7 @@ protected: public: virtual void OnInit() = 0; virtual void OnConsoleInit() = 0; + virtual void OnMapChange(char *pNewMapName, int MapNameSize) = 0; virtual void OnShutdown() = 0; virtual void OnTick() = 0; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index f50e24e1b..c3b9f15fa 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -1477,6 +1477,7 @@ int CServer::LoadMap(const char *pMapName) //DATAFILE *df; char aBuf[512]; str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName); + GameServer()->OnMapChange(aBuf, sizeof(aBuf)); /*df = datafile_load(buf); if(!df) diff --git a/src/engine/shared/config.h b/src/engine/shared/config.h index 8c9512aa9..d1fcc1a31 100644 --- a/src/engine/shared/config.h +++ b/src/engine/shared/config.h @@ -30,7 +30,8 @@ enum // DDRace CMDFLAG_TEST=64, - CFGFLAG_CHAT = 128 + CFGFLAG_CHAT=128, + CFGFLAG_GAME=256, }; #endif diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 498f8b65c..b94c87887 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -166,16 +166,16 @@ MACRO_CONFIG_INT(DbgResizable, dbg_resizable, 0, 0, 0, CFGFLAG_CLIENT, "Enables MACRO_CONFIG_STR(SvWelcome, sv_welcome, 64, "", CFGFLAG_SERVER, "Message that will be displayed to players who join the server") MACRO_CONFIG_INT(SvReservedSlots, sv_reserved_slots, 0, 0, 16, CFGFLAG_SERVER, "The number of slots that are reserved for special players") MACRO_CONFIG_STR(SvReservedSlotsPass, sv_reserved_slots_pass, 32, "", CFGFLAG_SERVER, "The password that is required to use a reserved slot") -MACRO_CONFIG_INT(SvHit, sv_hit, 1, 0, 1, CFGFLAG_SERVER, "Whether players can hammer/grenade/laser eachother or not") -MACRO_CONFIG_INT(SvEndlessDrag, sv_endless_drag, 0, 0, 1, CFGFLAG_SERVER, "Turns endless hooking on/off") +MACRO_CONFIG_INT(SvHit, sv_hit, 1, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Whether players can hammer/grenade/laser eachother or not") +MACRO_CONFIG_INT(SvEndlessDrag, sv_endless_drag, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Turns endless hooking on/off") MACRO_CONFIG_INT(SvTestingCommands, sv_test_cmds, 0, 0, 1, CFGFLAG_SERVER, "Turns testing commands aka cheats on/off") -MACRO_CONFIG_INT(SvFreezeDelay, sv_freeze_delay, 3, 1, 30, CFGFLAG_SERVER, "How many seconds the players will remain frozen (applies to all except delayed freeze in switch layer & deepfreeze)") +MACRO_CONFIG_INT(SvFreezeDelay, sv_freeze_delay, 3, 1, 30, CFGFLAG_SERVER|CFGFLAG_GAME, "How many seconds the players will remain frozen (applies to all except delayed freeze in switch layer & deepfreeze)") MACRO_CONFIG_INT(ClDDRaceBinds, cl_race_binds, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Enable Default DDRace builds when pressing the reset binds button") MACRO_CONFIG_INT(ClDDRaceBindsSet, cl_race_binds_set, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Whether the DDRace binds set or not (this is automated you don't need to use this)") MACRO_CONFIG_INT(SvEndlessSuperHook, sv_endless_super_hook, 0, 0, 1, CFGFLAG_SERVER, "Endless hook for super players on/off") MACRO_CONFIG_INT(SvHideScore, sv_hide_score, 0, 0, 1, CFGFLAG_SERVER, "Whether players scores will be announced or not") -MACRO_CONFIG_INT(SvSaveWorseScores, sv_save_worse_scores, 1, 0, 1, CFGFLAG_SERVER, "Whether to save worse scores when you already have a better one") -MACRO_CONFIG_INT(SvPauseable, sv_pauseable, 1, 0, 1, CFGFLAG_SERVER, "Whether players can pause their char or not") +MACRO_CONFIG_INT(SvSaveWorseScores, sv_save_worse_scores, 1, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Whether to save worse scores when you already have a better one") +MACRO_CONFIG_INT(SvPauseable, sv_pauseable, 1, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Whether players can pause their char or not") MACRO_CONFIG_INT(SvPauseMessages, sv_pause_messages, 0, 0, 1, CFGFLAG_SERVER, "Whether to show messages when a player pauses and resumes") MACRO_CONFIG_INT(SvPauseTime, sv_pause_time, 0, 0, 1, CFGFLAG_SERVER, "Whether '/pause' and 'sv_max_dc_restore' pauses the time of player or not") MACRO_CONFIG_INT(SvPauseFrequency, sv_pause_frequency, 1, 0, 9999, CFGFLAG_SERVER, "The minimum allowed delay between pauses") @@ -233,16 +233,16 @@ MACRO_CONFIG_STR(SvRulesLine8, sv_rules_line8, 40, "", CFGFLAG_SERVER, "Rules li MACRO_CONFIG_STR(SvRulesLine9, sv_rules_line9, 40, "", CFGFLAG_SERVER, "Rules line 9") MACRO_CONFIG_STR(SvRulesLine10, sv_rules_line10, 40, "", CFGFLAG_SERVER, "Rules line 10") -MACRO_CONFIG_INT(SvTeam, sv_team, 1, 0, 3, CFGFLAG_SERVER, "Teams configuration (0 = off, 1 = on but optional, 2 = must play only with teams, 3 = forced random team only for you)") -MACRO_CONFIG_INT(SvTeamMaxSize, sv_max_team_size, MAX_CLIENTS, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum team size (from 2 to 16)") -MACRO_CONFIG_INT(SvTeamLock, sv_team_lock, 1, 0, 1, CFGFLAG_SERVER, "Enable team lock") -MACRO_CONFIG_INT(SvMapVote, sv_map_vote, 1, 0, 1, CFGFLAG_SERVER, "Whether to allow /map") +MACRO_CONFIG_INT(SvTeam, sv_team, 1, 0, 3, CFGFLAG_SERVER|CFGFLAG_GAME, "Teams configuration (0 = off, 1 = on but optional, 2 = must play only with teams, 3 = forced random team only for you)") +MACRO_CONFIG_INT(SvTeamMaxSize, sv_max_team_size, MAX_CLIENTS, 1, MAX_CLIENTS, CFGFLAG_SERVER|CFGFLAG_GAME, "Maximum team size (from 2 to 16)") +MACRO_CONFIG_INT(SvTeamLock, sv_team_lock, 1, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Enable team lock") +MACRO_CONFIG_INT(SvMapVote, sv_map_vote, 1, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Whether to allow /map") MACRO_CONFIG_STR(SvAnnouncementFileName, sv_announcement_filename, 24, "announcement.txt", CFGFLAG_SERVER, "file which will have the announcement, each one at a line") MACRO_CONFIG_INT(SvAnnouncementInterval, sv_announcement_interval, 300, 1, 9999, CFGFLAG_SERVER, "time(minutes) in which the announcement will be displayed from the announcement file") MACRO_CONFIG_INT(SvAnnouncementRandom, sv_announcement_random, 1, 0, 1, CFGFLAG_SERVER, "Whether announcements are sequential or random") -MACRO_CONFIG_INT(SvOldLaser, sv_old_laser, 0, 0, 1, CFGFLAG_SERVER, "Whether lasers can hit you if you shot them and that they pull you towards the bounce origin (0 for DDRace Beta) or lasers can't hit you if you shot them, and they pull others towards the shooter") +MACRO_CONFIG_INT(SvOldLaser, sv_old_laser, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Whether lasers can hit you if you shot them and that they pull you towards the bounce origin (0 for DDRace Beta) or lasers can't hit you if you shot them, and they pull others towards the shooter") MACRO_CONFIG_INT(SvSlashMe, sv_slash_me, 0, 0, 1, CFGFLAG_SERVER, "Whether /me is active on the server or not") MACRO_CONFIG_INT(SvRejoinTeam0, sv_rejoin_team_0, 1, 0, 1, CFGFLAG_SERVER, "Make a team automatically rejoin team 0 after finish (only if not locked)") @@ -287,7 +287,7 @@ MACRO_CONFIG_INT(ClRaceShowGhost, cl_race_show_ghost, 1, 0, 1, CFGFLAG_CLIENT|CF MACRO_CONFIG_INT(ClRaceSaveGhost, cl_race_save_ghost, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Save ghost") MACRO_CONFIG_INT(ClDDRaceScoreBoard, cl_ddrace_scoreboard, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Enable DDRace Scoreboard ") MACRO_CONFIG_INT(ClShowDecisecs, cl_show_decisecs, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Show deciseconds in game time") -MACRO_CONFIG_INT(SvResetPickups, sv_reset_pickups, 0, 0, 1, CFGFLAG_SERVER, "Whether the weapons are reset on passing the start tile or not") +MACRO_CONFIG_INT(SvResetPickups, sv_reset_pickups, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Whether the weapons are reset on passing the start tile or not") MACRO_CONFIG_INT(ClShowOthers, cl_show_others, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show players in other teams") MACRO_CONFIG_INT(ClShowOthersAlpha, cl_show_others_alpha, 40, 0, 100, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show players in other teams (alpha value, 0 invisible, 100 fully visible)") MACRO_CONFIG_INT(ClOverlayEntities, cl_overlay_entities, 0, 0, 100, CFGFLAG_CLIENT, "Overlay game tiles with a percentage of opacity") @@ -304,8 +304,8 @@ MACRO_CONFIG_INT(SvShowOthersDefault, sv_show_others_default, 0, 0, 1, CFGFLAG_S MACRO_CONFIG_INT(SvShowAllDefault, sv_show_all_default, 0, 0, 1, CFGFLAG_SERVER, "Whether players see all tees by default") MACRO_CONFIG_INT(SvMaxAfkTime, sv_max_afk_time, 0, 0, 9999, CFGFLAG_SERVER, "The time in seconds a player is allowed to be afk (0 = disabled)") MACRO_CONFIG_INT(SvMaxAfkVoteTime, sv_max_afk_vote_time, 300, 0, 9999, CFGFLAG_SERVER, "The time in seconds a player can be afk and his votes still count (0 = disabled)") -MACRO_CONFIG_INT(SvPlasmaRange, sv_plasma_range, 700, 1, 99999, CFGFLAG_SERVER, "How far will the plasma gun track tees") -MACRO_CONFIG_INT(SvPlasmaPerSec, sv_plasma_per_sec, 3, 0, 50, CFGFLAG_SERVER, "How many shots does the plasma gun fire per seconds") +MACRO_CONFIG_INT(SvPlasmaRange, sv_plasma_range, 700, 1, 99999, CFGFLAG_SERVER|CFGFLAG_GAME, "How far will the plasma gun track tees") +MACRO_CONFIG_INT(SvPlasmaPerSec, sv_plasma_per_sec, 3, 0, 50, CFGFLAG_SERVER|CFGFLAG_GAME, "How many shots does the plasma gun fire per seconds") MACRO_CONFIG_INT(SvVotePause, sv_vote_pause, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to pause players (instead of moving to spectators)") MACRO_CONFIG_INT(SvVotePauseTime, sv_vote_pause_time, 10, 0, 360, CFGFLAG_SERVER, "The time (in seconds) players have to wait in pause when paused by vote") MACRO_CONFIG_INT(SvTuneReset, sv_tune_reset, 1, 0, 1, CFGFLAG_SERVER, "Whether tuning is reset after each map change or not") @@ -326,7 +326,7 @@ MACRO_CONFIG_INT(SvEvents, sv_events, 1, 0, 1, CFGFLAG_SERVER, "Enable triggerin MACRO_CONFIG_INT(SvRankCheats, sv_rank_cheats, 0, 0, 1, CFGFLAG_SERVER, "Enable ranks after cheats have been used (file based server only)") MACRO_CONFIG_INT(SvShutdownWhenEmpty, sv_shutdown_when_empty, 0, 0, 1, CFGFLAG_SERVER, "Shutdown server as soon as noone is on it anymore") MACRO_CONFIG_INT(SvKillProtection, sv_kill_protection, 20, 0, 9999, CFGFLAG_SERVER, "0 - Disable, 1-9999 minutes") -MACRO_CONFIG_INT(SvSoloServer, sv_solo_server, 0, 0, 1, CFGFLAG_SERVER, "Set server to solo mode (no player interactions, has to be set before loading the map)") +MACRO_CONFIG_INT(SvSoloServer, sv_solo_server, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Set server to solo mode (no player interactions, has to be set before loading the map)") MACRO_CONFIG_STR(SvClientSuggestion, sv_client_suggestion, 128, "Get DDNet client from DDNet.tw to use all features on DDNet!", CFGFLAG_SERVER, "Broadcast to display to players without DDNet client") MACRO_CONFIG_STR(SvClientSuggestionOld, sv_client_suggestion_old, 128, "Your DDNet client is old, update it on DDNet.tw!", CFGFLAG_SERVER, "Broadcast to display to players with an old version of DDNet client") MACRO_CONFIG_STR(SvClientSuggestionBot, sv_client_suggestion_bot, 128, "Your client has bots and can be remote controlled!\nPlease use another client like DDNet client from DDNet.tw", CFGFLAG_SERVER, "Broadcast to display to players with a known botting client") diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp index ecc396f2b..42a64b443 100644 --- a/src/engine/shared/console.cpp +++ b/src/engine/shared/console.cpp @@ -323,7 +323,29 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientID) if(pCommand) { - if(pCommand->GetAccessLevel() >= m_AccessLevel) + if(ClientID == IConsole::CLIENT_ID_GAME + && !(pCommand->m_Flags & CFGFLAG_GAME)) + { + if(Stroke) + { + char aBuf[96]; + str_format(aBuf, sizeof(aBuf), "Command '%s' cannot be executed from a map.", Result.m_pCommand); + Print(OUTPUT_LEVEL_STANDARD, "console", aBuf); + } + } + else if(ClientID == IConsole::CLIENT_ID_NO_GAME + && pCommand->m_Flags & CFGFLAG_GAME) + { + if(Stroke) + { + char aBuf[96]; + str_format(aBuf, sizeof(aBuf), "Command '%s' cannot be executed from a non-map config file.", Result.m_pCommand); + Print(OUTPUT_LEVEL_STANDARD, "console", aBuf); + str_format(aBuf, sizeof(aBuf), "Hint: Put the command in '%s.cfg' instead of '%s.map.cfg' ", g_Config.m_SvMap, g_Config.m_SvMap); + Print(OUTPUT_LEVEL_STANDARD, "console", aBuf); + } + } + else if(pCommand->GetAccessLevel() >= m_AccessLevel) { int IsStrokeCommand = 0; if(Result.m_pCommand[0] == '+') @@ -458,7 +480,7 @@ void CConsole::ExecuteFile(const char *pFilename, int ClientID) // exec the file IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_ALL); - char aBuf[8192]; + char aBuf[128]; if(File) { char *pLine; @@ -560,6 +582,7 @@ struct CIntVariableData int *m_pVariable; int m_Min; int m_Max; + int m_OldValue; }; struct CStrVariableData @@ -567,6 +590,7 @@ struct CStrVariableData IConsole *m_pConsole; char *m_pStr; int m_MaxSize; + char *m_pOldValue; }; static void IntVariableCommand(IConsole::IResult *pResult, void *pUserData) @@ -587,10 +611,12 @@ static void IntVariableCommand(IConsole::IResult *pResult, void *pUserData) } *(pData->m_pVariable) = Val; + if(pResult->m_ClientID != IConsole::CLIENT_ID_GAME) + pData->m_OldValue = Val; } else { - char aBuf[1024]; + char aBuf[32]; str_format(aBuf, sizeof(aBuf), "Value: %d", *(pData->m_pVariable)); pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "console", aBuf); } @@ -622,6 +648,9 @@ static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData) } else str_copy(pData->m_pStr, pString, pData->m_MaxSize); + + if(pResult->m_ClientID != IConsole::CLIENT_ID_GAME) + str_copy(pData->m_pOldValue, pData->m_pStr, pData->m_MaxSize); } else { @@ -732,13 +761,14 @@ CConsole::CConsole(int FlagMask) // TODO: this should disappear #define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Flags,Desc) \ { \ - static CIntVariableData Data = { this, &g_Config.m_##Name, Min, Max }; \ + static CIntVariableData Data = { this, &g_Config.m_##Name, Min, Max, Def }; \ Register(#ScriptName, "?i", Flags, IntVariableCommand, &Data, Desc); \ } #define MACRO_CONFIG_STR(Name,ScriptName,Len,Def,Flags,Desc) \ { \ - static CStrVariableData Data = { this, g_Config.m_##Name, Len }; \ + static char OldValue[Len] = Def; \ + static CStrVariableData Data = { this, g_Config.m_##Name, Len, OldValue }; \ Register(#ScriptName, "?r", Flags, StrVariableCommand, &Data, Desc); \ } @@ -1008,6 +1038,50 @@ void CConsole::ConUserCommandStatus(IResult *pResult, void *pUser) pConsole->Print(OUTPUT_LEVEL_STANDARD, "console", aBuf); } +void CConsole::ResetServerGameSettings() +{ + #define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Flags,Desc) \ + { \ + if(((Flags) & (CFGFLAG_SERVER|CFGFLAG_GAME)) == (CFGFLAG_SERVER|CFGFLAG_GAME)) \ + { \ + CCommand *pCommand = FindCommand(#ScriptName, CFGFLAG_SERVER); \ + void *pUserData = pCommand->m_pUserData; \ + FCommandCallback pfnCallback = pCommand->m_pfnCallback; \ + while(pfnCallback == Con_Chain) \ + { \ + CChain *pChainInfo = (CChain *)pUserData; \ + pUserData = pChainInfo->m_pCallbackUserData; \ + pfnCallback = pChainInfo->m_pfnCallback; \ + } \ + CIntVariableData *pData = (CIntVariableData *)pUserData; \ + *pData->m_pVariable = pData->m_OldValue; \ + } \ + } + + #define MACRO_CONFIG_STR(Name,ScriptName,Len,Def,Flags,Desc) \ + { \ + if(((Flags) & (CFGFLAG_SERVER|CFGFLAG_GAME)) == (CFGFLAG_SERVER|CFGFLAG_GAME)) \ + { \ + CCommand *pCommand = FindCommand(#ScriptName, CFGFLAG_SERVER); \ + void *pUserData = pCommand->m_pUserData; \ + FCommandCallback pfnCallback = pCommand->m_pfnCallback; \ + while(pfnCallback == Con_Chain) \ + { \ + CChain *pChainInfo = (CChain *)pUserData; \ + pUserData = pChainInfo->m_pCallbackUserData; \ + pfnCallback = pChainInfo->m_pfnCallback; \ + } \ + CStrVariableData *pData = (CStrVariableData *)pUserData; \ + str_copy(pData->m_pOldValue, pData->m_pStr, pData->m_MaxSize); \ + } \ + } + + #include "config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +} + int CConsole::CResult::GetVictim() { return m_Victim; diff --git a/src/engine/shared/console.h b/src/engine/shared/console.h index 5b0177163..18825c8d8 100644 --- a/src/engine/shared/console.h +++ b/src/engine/shared/console.h @@ -195,7 +195,7 @@ public: virtual void Print(int Level, const char *pFrom, const char *pStr, bool Highlighted = false); void SetAccessLevel(int AccessLevel) { m_AccessLevel = clamp(AccessLevel, (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_USER)); } - + void ResetServerGameSettings(); // DDRace static void ConUserCommandStatus(IConsole::IResult *pResult, void *pUser); diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp index 5f011cc51..1de438511 100644 --- a/src/engine/shared/datafile.cpp +++ b/src/engine/shared/datafile.cpp @@ -452,13 +452,16 @@ CDataFileWriter::~CDataFileWriter() m_pDatas = 0; } -bool CDataFileWriter::Open(class IStorage *pStorage, const char *pFilename) +bool CDataFileWriter::OpenFile(class IStorage *pStorage, const char *pFilename) { dbg_assert(!m_File, "a file already exists"); m_File = pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE); - if(!m_File) - return false; + return m_File != 0; +} +void CDataFileWriter::Init() +{ + dbg_assert(!m_File, "a file already exists"); m_NumItems = 0; m_NumDatas = 0; m_NumItemTypes = 0; @@ -469,14 +472,16 @@ bool CDataFileWriter::Open(class IStorage *pStorage, const char *pFilename) m_pItemTypes[i].m_First = -1; m_pItemTypes[i].m_Last = -1; } +} - return true; +bool CDataFileWriter::Open(class IStorage *pStorage, const char *pFilename) +{ + Init(); + return OpenFile(pStorage, pFilename); } int CDataFileWriter::AddItem(int Type, int ID, int Size, void *pData) { - if(!m_File) return 0; - dbg_assert(Type >= 0 && Type < 0xFFFF, "incorrect type"); dbg_assert(m_NumItems < 1024, "too many items"); dbg_assert(Size%sizeof(int) == 0, "incorrect boundary"); @@ -511,8 +516,6 @@ int CDataFileWriter::AddItem(int Type, int ID, int Size, void *pData) int CDataFileWriter::AddData(int Size, void *pData) { - if(!m_File) return 0; - dbg_assert(m_NumDatas < 1024, "too much data"); CDataInfo *pInfo = &m_pDatas[m_NumDatas]; diff --git a/src/engine/shared/datafile.h b/src/engine/shared/datafile.h index c452c6c9f..0c2e07a97 100644 --- a/src/engine/shared/datafile.h +++ b/src/engine/shared/datafile.h @@ -80,6 +80,8 @@ class CDataFileWriter public: CDataFileWriter(); ~CDataFileWriter(); + void Init(); + bool OpenFile(class IStorage *pStorage, const char *pFilename); bool Open(class IStorage *pStorage, const char *Filename); int AddData(int Size, void *pData); int AddDataSwapped(int Size, void *pData); diff --git a/src/engine/shared/map.cpp b/src/engine/shared/map.cpp index 7bda44643..923dc8308 100644 --- a/src/engine/shared/map.cpp +++ b/src/engine/shared/map.cpp @@ -16,6 +16,7 @@ public: virtual void *GetDataSwapped(int Index) { return m_DataFile.GetDataSwapped(Index); } virtual void UnloadData(int Index) { m_DataFile.UnloadData(Index); } virtual void *GetItem(int Index, int *pType, int *pID) { return m_DataFile.GetItem(Index, pType, pID); } + virtual int GetItemSize(int Index) { return m_DataFile.GetItemSize(Index); } virtual void GetType(int Type, int *pStart, int *pNum) { m_DataFile.GetType(Type, pStart, pNum); } virtual void *FindItem(int Type, int ID) { return m_DataFile.FindItem(Type, ID); } virtual int NumItems() { return m_DataFile.NumItems(); } diff --git a/src/engine/shared/storage.cpp b/src/engine/shared/storage.cpp index aa3857acb..439044aac 100644 --- a/src/engine/shared/storage.cpp +++ b/src/engine/shared/storage.cpp @@ -433,3 +433,18 @@ public: }; IStorage *CreateStorage(const char *pApplicationName, int StorageType, int NumArgs, const char **ppArguments) { return CStorage::Create(pApplicationName, StorageType, NumArgs, ppArguments); } + +IStorage *CreateLocalStorage() +{ + CStorage *pStorage = new CStorage(); + if(pStorage) + { + if(!fs_getcwd(pStorage->m_aCurrentdir, sizeof(pStorage->m_aCurrentdir))) + { + delete pStorage; + return NULL; + } + pStorage->AddPath("$CURRENTDIR"); + } + return pStorage; +} diff --git a/src/engine/storage.h b/src/engine/storage.h index 05a3ac540..9cb0a5a19 100644 --- a/src/engine/storage.h +++ b/src/engine/storage.h @@ -33,6 +33,7 @@ public: }; extern IStorage *CreateStorage(const char *pApplicationName, int StorageType, int NumArgs, const char **ppArguments); +extern IStorage *CreateLocalStorage(); #endif diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index a03126ac5..0c26e0fdf 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -4098,6 +4098,20 @@ void CEditor::RenderStatusbar(CUIRect View) else if(MouseButton == 1) m_ShowEnvelopeEditor = (m_ShowEnvelopeEditor+1)%4; + if(MouseButton) + { + m_ShowServerSettingsEditor = false; + } + + View.VSplitRight(100.0f, &View, &Button); + Button.VSplitRight(10.0f, &Button, 0); + static int s_SettingsButton = 0; + if(DoButton_Editor(&s_SettingsButton, "Server settings", m_ShowServerSettingsEditor, &Button, 0, "Toggles the server settings editor.")) + { + m_ShowEnvelopeEditor = 0; + m_ShowServerSettingsEditor ^= 1; + } + if (g_Config.m_ClEditorUndo) { View.VSplitRight(5.0f, &View, &Button); @@ -4707,6 +4721,126 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) } } +void CEditor::RenderServerSettingsEditor(CUIRect View) +{ + static int s_CommandSelectedIndex = -1; + + CUIRect ToolBar; + View.HSplitTop(20.0f, &ToolBar, &View); + ToolBar.Margin(2.0f, &ToolBar); + + // do the toolbar + { + CUIRect Button; + + // command line + ToolBar.VSplitLeft(5.0f, 0, &Button); + UI()->DoLabel(&Button, "Command:", 12.0f, -1); + + Button.VSplitLeft(70.0f, 0, &Button); + Button.VSplitLeft(180.0f, &Button, 0); + DoEditBox(&m_CommandBox, &Button, m_aSettingsCommand, sizeof(m_aSettingsCommand), 12.0f, &m_CommandBox); + + // buttons + ToolBar.VSplitRight(50.0f, &ToolBar, &Button); + static int s_AddButton = 0; + if(DoButton_Editor(&s_AddButton, "Add", 0, &Button, 0, "Add a command to command list.") + || ((Input()->KeyDown(KEY_RETURN) || Input()->KeyDown(KEY_KP_ENTER)) && UI()->LastActiveItem() == &m_CommandBox)) + { + if(m_aSettingsCommand[0] != 0 && str_find(m_aSettingsCommand, " ")) + { + bool Found = false; + for(int i = 0; i < m_Map.m_lSettings.size(); i++) + if(!str_comp(m_Map.m_lSettings[i].m_aCommand, m_aSettingsCommand)) + { + Found = true; + break; + } + + if(!Found) + { + CEditorMap::CSetting Setting; + str_copy(Setting.m_aCommand, m_aSettingsCommand, sizeof(Setting.m_aCommand)); + m_Map.m_lSettings.add(Setting); + } + } + } + + if(m_Map.m_lSettings.size()) + { + ToolBar.VSplitRight(50.0f, &ToolBar, &Button); + Button.VSplitRight(5.0f, &Button, 0); + static int s_AddButton = 0; + if(DoButton_Editor(&s_AddButton, "Del", 0, &Button, 0, "Delete a command from the command list.") + || Input()->KeyDown(KEY_DELETE)) + if(s_CommandSelectedIndex > -1 && s_CommandSelectedIndex < m_Map.m_lSettings.size()) + m_Map.m_lSettings.remove_index(s_CommandSelectedIndex); + } + } + + View.HSplitTop(2.0f, 0, &View); + RenderBackground(View, ms_CheckerTexture, 32.0f, 0.1f); + + CUIRect ListBox; + View.Margin(1.0f, &ListBox); + + float ListHeight = 17.0f * m_Map.m_lSettings.size(); + static int s_ScrollBar = 0; + static float s_ScrollValue = 0; + + float ScrollDifference = ListHeight - ListBox.h; + + if(ListHeight > ListBox.h) // Do we even need a scrollbar? + { + CUIRect Scroll; + ListBox.VSplitRight(15.0f, &ListBox, &Scroll); + ListBox.VSplitRight(3.0f, &ListBox, 0); // extra spacing + Scroll.HMargin(5.0f, &Scroll); + s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue); + + if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&ListBox)) + { + int ScrollNum = (int)((ListHeight-ListBox.h)/17.0f)+1; + if(ScrollNum > 0) + { + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f); + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f); + } + else + ScrollNum = 0; + } + } + + float ListStartAt = ScrollDifference * s_ScrollValue; + if(ListStartAt < 0.0f) + ListStartAt = 0.0f; + + float ListStopAt = ListHeight - ScrollDifference * (1 - s_ScrollValue); + float ListCur = 0; + + UI()->ClipEnable(&ListBox); + for(int i = 0; i < m_Map.m_lSettings.size(); i++) + { + if(ListCur > ListStopAt) + break; + + if(ListCur >= ListStartAt) + { + CUIRect Button; + ListBox.HSplitTop(15.0f, &Button, &ListBox); + ListBox.HSplitTop(2.0f, 0, &ListBox); + Button.VSplitLeft(5.0f, 0, &Button); + + if(DoButton_MenuItem(&m_Map.m_lSettings[i], m_Map.m_lSettings[i].m_aCommand, s_CommandSelectedIndex == i, &Button, 0, 0)) + s_CommandSelectedIndex = i; + } + ListCur += 17.0f; + } + UI()->ClipDisable(); +} + int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) { static int s_NewMapButton = 0; @@ -4874,8 +5008,8 @@ void CEditor::Render() // render checker RenderBackground(View, ms_CheckerTexture, 32.0f, 1.0f); - CUIRect MenuBar, CModeBar, ToolBar, StatusBar, EnvelopeEditor, UndoList, ToolBox; - m_ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0; + CUIRect MenuBar, CModeBar, ToolBar, StatusBar, ExtraEditor, UndoList, ToolBox; + m_ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && UI()->LastActiveItem() != &m_CommandBox; if(m_GuiActive) { @@ -4892,12 +5026,15 @@ void CEditor::Render() size *= 2.0f; else if(m_ShowEnvelopeEditor == 3) size *= 3.0f; - View.HSplitBottom(size, &View, &EnvelopeEditor); + View.HSplitBottom(size, &View, &ExtraEditor); } if (m_ShowUndo && !m_ShowPicker) { View.HSplitBottom(250.0f, &View, &UndoList); } + + if(m_ShowServerSettingsEditor && !m_ShowPicker) + View.HSplitBottom(250.0f, &View, &ExtraEditor); } // a little hack for now @@ -4963,10 +5100,10 @@ void CEditor::Render() if(m_GuiActive) { - if(m_ShowEnvelopeEditor) + if(m_ShowEnvelopeEditor || m_ShowServerSettingsEditor) { - RenderBackground(EnvelopeEditor, ms_BackgroundTexture, 128.0f, Brightness); - EnvelopeEditor.Margin(2.0f, &EnvelopeEditor); + RenderBackground(ExtraEditor, ms_BackgroundTexture, 128.0f, Brightness); + ExtraEditor.Margin(2.0f, &ExtraEditor); } if(m_ShowUndo) { @@ -4991,9 +5128,11 @@ void CEditor::Render() RenderModebar(CModeBar); if(m_ShowEnvelopeEditor && !m_ShowPicker) - RenderEnvelopeEditor(EnvelopeEditor); + RenderEnvelopeEditor(ExtraEditor); if(m_ShowUndo) RenderUndoList(UndoList); + if(m_ShowServerSettingsEditor) + RenderServerSettingsEditor(ExtraEditor); } if(m_Dialog == DIALOG_FILE) @@ -5215,6 +5354,8 @@ void CEditorMap::Clean() m_MapInfo.Reset(); + m_lSettings.clear(); + m_pGameLayer = 0x0; m_pGameGroup = 0x0; diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 6c0843763..f0162d210 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -376,6 +376,12 @@ public: }; CMapInfo m_MapInfo; + struct CSetting + { + char m_aCommand[64]; + }; + array m_lSettings; + class CLayerGame *m_pGameLayer; CLayerGroup *m_pGameGroup; @@ -678,11 +684,15 @@ public: m_ShowEnvelopeEditor = 0; m_ShowUndo = 0; m_UndoScrollValue = 0.0f; + m_ShowServerSettingsEditor = false; m_ShowEnvelopePreview = 0; m_SelectedQuadEnvelope = -1; m_SelectedEnvelopePoint = -1; + m_CommandBox = 0.0f; + m_aSettingsCommand[0] = 0; + ms_CheckerTexture = 0; ms_BackgroundTexture = 0; ms_CursorTexture = 0; @@ -842,6 +852,7 @@ public: int m_ShowEnvelopeEditor; int m_ShowEnvelopePreview; //Values: 0-Off|1-Selected Envelope|2-All + bool m_ShowServerSettingsEditor; bool m_ShowPicker; int m_SelectedLayer; @@ -871,6 +882,9 @@ public: static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser); + float m_CommandBox; + char m_aSettingsCommand[64]; + void DoMapBorder(); 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); @@ -959,6 +973,7 @@ public: void RenderStatusbar(CUIRect View); void RenderEnvelopeEditor(CUIRect View); void RenderUndoList(CUIRect View); + void RenderServerSettingsEditor(CUIRect View); void RenderMenubar(CUIRect Menubar); void RenderFileDialog(); diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 93fad4b7f..e1797da53 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -238,7 +238,7 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) // save map info { - CMapItemInfo Item; + CMapItemInfoSettings Item; Item.m_Version = 1; if(m_MapInfo.m_aAuthor[0]) @@ -258,6 +258,27 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) else Item.m_License = -1; + Item.m_Settings = -1; + if(m_lSettings.size()) + { + int Size = 0; + for(int i = 0; i < m_lSettings.size(); i++) + { + Size += str_length(m_lSettings[i].m_aCommand) + 1; + } + + char *pSettings = (char *)mem_alloc(Size, 1); + char *pNext = pSettings; + for(int i = 0; i < m_lSettings.size(); i++) + { + int Length = str_length(m_lSettings[i].m_aCommand) + 1; + mem_copy(pNext, m_lSettings[i].m_aCommand, Length); + pNext += Length; + } + Item.m_Settings = df.AddData(Size, pSettings); + mem_free(pSettings); + } + df.AddItem(MAPITEMTYPE_INFO, 0, sizeof(Item), &Item); } @@ -589,9 +610,16 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag // load map info { - CMapItemInfo *pItem = (CMapItemInfo *)DataFile.FindItem(MAPITEMTYPE_INFO, 0); - if(pItem && pItem->m_Version == 1) + int Start, Num; + DataFile.GetType(MAPITEMTYPE_INFO, &Start, &Num); + for(int i = Start; i < Start + Num; i++) { + int ItemSize = DataFile.GetItemSize(Start) - 8; + int ItemID; + CMapItemInfoSettings *pItem = (CMapItemInfoSettings *)DataFile.GetItem(i, 0, &ItemID); + if(!pItem || ItemID != 0) + continue; + if(pItem->m_Author > -1) str_copy(m_MapInfo.m_aAuthor, (char *)DataFile.GetData(pItem->m_Author), sizeof(m_MapInfo.m_aAuthor)); if(pItem->m_MapVersion > -1) @@ -600,6 +628,24 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag str_copy(m_MapInfo.m_aCredits, (char *)DataFile.GetData(pItem->m_Credits), sizeof(m_MapInfo.m_aCredits)); if(pItem->m_License > -1) str_copy(m_MapInfo.m_aLicense, (char *)DataFile.GetData(pItem->m_License), sizeof(m_MapInfo.m_aLicense)); + + if(pItem->m_Version != 1 || ItemSize < (int)sizeof(CMapItemInfoSettings)) + break; + + if(!(pItem->m_Settings > -1)) + break; + + int Size = DataFile.GetUncompressedDataSize(pItem->m_Settings); + char *pSettings = (char *)DataFile.GetData(pItem->m_Settings); + char *pNext = pSettings; + while(pNext < pSettings + Size) + { + int StrSize = str_length(pNext) + 1; + CSetting Setting; + str_copy(Setting.m_aCommand, pNext, sizeof(Setting.m_aCommand)); + m_lSettings.add(Setting); + pNext += StrSize; + } } } diff --git a/src/game/mapitems.h b/src/game/mapitems.h index 3051a1faf..44c6fcbf4 100644 --- a/src/game/mapitems.h +++ b/src/game/mapitems.h @@ -221,6 +221,11 @@ struct CMapItemInfo int m_License; } ; +struct CMapItemInfoSettings : CMapItemInfo +{ + int m_Settings; +} ; + struct CMapItemImage { int m_Version; diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index b87b636a9..021d4f857 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include "gamecontext.h" #include #include @@ -56,6 +59,7 @@ void CGameContext::Construct(int Resetting) m_NumMutes = 0; } m_ChatResponseTargetID = -1; + m_aDeleteTempfile[0] = 0; } CGameContext::CGameContext(int Resetting) @@ -2242,15 +2246,15 @@ void CGameContext::OnConsoleInit() m_ChatPrintCBIndex = Console()->RegisterPrintCallback(0, SendChatResponse, this); - Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, "Tune variable to value"); + Console()->Register("tune", "si", CFGFLAG_SERVER|CFGFLAG_GAME, ConTuneParam, this, "Tune variable to value"); Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "Reset tuning"); Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, "Dump tuning"); - Console()->Register("tune_zone", "isi", CFGFLAG_SERVER, ConTuneZone, this, "Tune in zone a variable to value"); + Console()->Register("tune_zone", "isi", CFGFLAG_SERVER|CFGFLAG_GAME, ConTuneZone, this, "Tune in zone a variable to value"); Console()->Register("tune_zone_dump", "i", CFGFLAG_SERVER, ConTuneDumpZone, this, "Dump zone tuning in zone x"); Console()->Register("tune_zone_reset", "?i", CFGFLAG_SERVER, ConTuneResetZone, this, "reset zone tuning in zone x or in all zones"); - Console()->Register("tune_zone_enter", "is", CFGFLAG_SERVER, ConTuneSetZoneMsgEnter, this, "which message to display on zone enter; use 0 for normal area"); - Console()->Register("tune_zone_leave", "is", CFGFLAG_SERVER, ConTuneSetZoneMsgLeave, this, "which message to display on zone leave; use 0 for normal area"); - Console()->Register("switch_open", "i", CFGFLAG_SERVER, ConSwitchOpen, this, "Whether a switch is open by default (otherwise closed)"); + Console()->Register("tune_zone_enter", "is", CFGFLAG_SERVER|CFGFLAG_GAME, ConTuneSetZoneMsgEnter, this, "which message to display on zone enter; use 0 for normal area"); + Console()->Register("tune_zone_leave", "is", CFGFLAG_SERVER|CFGFLAG_GAME, ConTuneSetZoneMsgLeave, this, "which message to display on zone leave; use 0 for normal area"); + Console()->Register("switch_open", "i", CFGFLAG_SERVER|CFGFLAG_GAME, ConSwitchOpen, this, "Whether a switch is open by default (otherwise closed)"); Console()->Register("pause_game", "", CFGFLAG_SERVER, ConPause, this, "Pause/unpause game"); Console()->Register("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, "Change map"); Console()->Register("random_map", "?i", CFGFLAG_SERVER, ConRandomMap, this, "Random map"); @@ -2285,6 +2289,8 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/) m_World.SetGameServer(this); m_Events.SetGameServer(this); + DeleteTempfile(); + //if(!data) // only load once //data = load_data_from_memory(internal_data); @@ -2347,11 +2353,8 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/) Console()->ExecuteFile(g_Config.m_SvResetFile); - char buf[512]; - str_format(buf, sizeof(buf), "maps/%s.cfg", g_Config.m_SvMap); - Console()->ExecuteFile(buf); - str_format(buf, sizeof(buf), "maps/%s.map.cfg", g_Config.m_SvMap); - Console()->ExecuteFile(buf); + LoadMapSettings(); + /* // select gametype if(str_comp(g_Config.m_SvGametype, "mod") == 0) m_pController = new CGameControllerMOD(this); @@ -2514,8 +2517,132 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/) #endif } +void CGameContext::DeleteTempfile() +{ + if(m_aDeleteTempfile[0] != 0) + { + IStorage *pStorage = Kernel()->RequestInterface(); + pStorage->RemoveFile(m_aDeleteTempfile, IStorage::TYPE_SAVE); + m_aDeleteTempfile[0] = 0; + } +} + +void CGameContext::OnMapChange(char *pNewMapName, int MapNameSize) +{ + IStorage *pStorage = Kernel()->RequestInterface(); + + char aConfig[128]; + char aTemp[128]; + str_format(aConfig, sizeof(aConfig), "maps/%s.cfg", g_Config.m_SvMap); + str_format(aTemp, sizeof(aTemp), "%s.temp.%d", pNewMapName, pid()); + + IOHANDLE File = pStorage->OpenFile(aConfig, IOFLAG_READ, IStorage::TYPE_ALL); + if(!File) + { + // No map-specific config, just return. + return; + } + CLineReader LineReader; + LineReader.Init(File); + + array aLines; + char *pLine; + int TotalLength = 0; + while((pLine = LineReader.Get())) + { + int Length = str_length(pLine) + 1; + char *pCopy = (char *)mem_alloc(Length, 1); + mem_copy(pCopy, pLine, Length); + aLines.add(pCopy); + TotalLength += Length; + } + + char *pSettings = (char *)mem_alloc(TotalLength, 1); + int Offset = 0; + for(int i = 0; i < aLines.size(); i++) + { + int Length = str_length(aLines[i]) + 1; + mem_copy(pSettings + Offset, aLines[i], Length); + Offset += Length; + mem_free(aLines[i]); + } + + CDataFileReader Reader; + Reader.Open(pStorage, pNewMapName, IStorage::TYPE_ALL); + + CDataFileWriter Writer; + Writer.Open(pStorage, aTemp); + + int SettingsIndex = Reader.NumData(); + for(int i = 0; i < Reader.NumItems(); i++) + { + int TypeID; + int ItemID; + int *pData = (int *)Reader.GetItem(i, &TypeID, &ItemID); + // GetItemSize returns item size including header, remove that. + int Size = Reader.GetItemSize(i) - sizeof(int) * 2; + CMapItemInfoSettings MapInfo; + if(TypeID == MAPITEMTYPE_INFO && ItemID == 0) + { + CMapItemInfoSettings *pInfo = (CMapItemInfoSettings *)pData; + if(Size >= (int)sizeof(CMapItemInfoSettings)) + { + if(pInfo->m_Settings > -1) + { + SettingsIndex = pInfo->m_Settings; + char *pMapSettings = (char *)Reader.GetData(SettingsIndex); + int DataSize = Reader.GetUncompressedDataSize(SettingsIndex); + if(DataSize == TotalLength && mem_comp(pSettings, pMapSettings, Size) == 0) + { + // Configs coincide, no need to update map. + return; + } + Reader.UnloadData(pInfo->m_Settings); + } + else + { + MapInfo = *pInfo; + MapInfo.m_Settings = SettingsIndex; + pData = (int *)&MapInfo; + Size = sizeof(MapInfo); + } + } + else + { + *(CMapItemInfo *)&MapInfo = *(CMapItemInfo *)pInfo; + MapInfo.m_Settings = SettingsIndex; + pData = (int *)&MapInfo; + Size = sizeof(MapInfo); + } + } + Writer.AddItem(TypeID, ItemID, Size, pData); + } + + for(int i = 0; i < Reader.NumData() || i == SettingsIndex; i++) + { + if(i == SettingsIndex) + { + Writer.AddData(TotalLength, pSettings); + continue; + } + unsigned char *pData = (unsigned char *)Reader.GetData(i); + int Size = Reader.GetUncompressedDataSize(i); + Writer.AddData(Size, pData); + Reader.UnloadData(i); + } + + dbg_msg("mapchange", "imported settings"); + Reader.Close(); + Writer.Finish(); + + str_copy(pNewMapName, aTemp, MapNameSize); + str_copy(m_aDeleteTempfile, aTemp, sizeof(m_aDeleteTempfile)); +} + void CGameContext::OnShutdown() { + DeleteTempfile(); + Console()->ResetServerGameSettings(); Layers()->Dest(); Collision()->Dest(); delete m_pController; @@ -2523,6 +2650,42 @@ void CGameContext::OnShutdown() Clear(); } +void CGameContext::LoadMapSettings() +{ + IMap *pMap = Kernel()->RequestInterface(); + int Start, Num; + pMap->GetType(MAPITEMTYPE_INFO, &Start, &Num); + for(int i = Start; i < Start + Num; i++) + { + int ItemID; + CMapItemInfoSettings *pItem = (CMapItemInfoSettings *)pMap->GetItem(i, 0, &ItemID); + int ItemSize = pMap->GetItemSize(i) - 8; + if(!pItem || ItemID != 0) + continue; + + if(ItemSize < (int)sizeof(CMapItemInfoSettings)) + break; + if(!(pItem->m_Settings > -1)) + break; + + int Size = pMap->GetUncompressedDataSize(pItem->m_Settings); + char *pSettings = (char *)pMap->GetData(pItem->m_Settings); + char *pNext = pSettings; + while(pNext < pSettings + Size) + { + int StrSize = str_length(pNext) + 1; + Console()->ExecuteLine(pNext, IConsole::CLIENT_ID_GAME); + pNext += StrSize; + } + pMap->UnloadData(pItem->m_Settings); + break; + } + + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "maps/%s.map.cfg", g_Config.m_SvMap); + Console()->ExecuteFile(aBuf, IConsole::CLIENT_ID_NO_GAME); +} + void CGameContext::OnSnap(int ClientID) { // add tuning to demo diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index afc113d39..a306fafdd 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -137,6 +137,9 @@ public: char m_ZoneEnterMsg[NUM_TUNINGZONES][256]; // 0 is used for switching from or to area without tunings char m_ZoneLeaveMsg[NUM_TUNINGZONES][256]; + char m_aDeleteTempfile[128]; + void DeleteTempfile(); + enum { VOTE_ENFORCE_UNKNOWN=0, @@ -188,9 +191,12 @@ public: // //void SwapTeams(); + void LoadMapSettings(); + // engine events virtual void OnInit(); virtual void OnConsoleInit(); + virtual void OnMapChange(char *pNewMapName, int MapNameSize); virtual void OnShutdown(); virtual void OnTick(); diff --git a/src/game/variables.h b/src/game/variables.h index 6c8a7c79f..d5d005841 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -145,10 +145,10 @@ MACRO_CONFIG_INT(SvVoteSpectateRejoindelay, sv_vote_spectate_rejoindelay, 3, 0, MACRO_CONFIG_INT(SvVoteKick, sv_vote_kick, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to kick players") MACRO_CONFIG_INT(SvVoteKickMin, sv_vote_kick_min, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "Minimum number of players required to start a kick vote") MACRO_CONFIG_INT(SvVoteKickBantime, sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time to ban a player if kicked by vote. 0 makes it just use kick") -MACRO_CONFIG_INT(SvOldTeleportWeapons, sv_old_teleport_weapons, 0, 0, 1, CFGFLAG_SERVER, "Teleporting of all weapons (deprecated, use special entities instead)"); -MACRO_CONFIG_INT(SvOldTeleportHook, sv_old_teleport_hook, 0, 0, 1, CFGFLAG_SERVER, "Hook through teleporter (deprecated, use special entities instead)"); -MACRO_CONFIG_INT(SvTeleportHoldHook, sv_teleport_hold_hook, 0, 0, 1, CFGFLAG_SERVER, "Hold hook when teleported"); -MACRO_CONFIG_INT(SvTeleportLoseWeapons, sv_teleport_lose_weapons, 0, 0, 1, CFGFLAG_SERVER, "Lose weapons when teleported (useful for some race maps)"); +MACRO_CONFIG_INT(SvOldTeleportWeapons, sv_old_teleport_weapons, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Teleporting of all weapons (deprecated, use special entities instead)"); +MACRO_CONFIG_INT(SvOldTeleportHook, sv_old_teleport_hook, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Hook through teleporter (deprecated, use special entities instead)"); +MACRO_CONFIG_INT(SvTeleportHoldHook, sv_teleport_hold_hook, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Hold hook when teleported"); +MACRO_CONFIG_INT(SvTeleportLoseWeapons, sv_teleport_lose_weapons, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Lose weapons when teleported (useful for some race maps)"); MACRO_CONFIG_INT(SvMapUpdateRate, sv_mapupdaterate, 5, 1, 100, CFGFLAG_SERVER, "64 player id <-> vanilla id players map update rate") diff --git a/src/tools/config_common.h b/src/tools/config_common.h new file mode 100644 index 000000000..acf735e3c --- /dev/null +++ b/src/tools/config_common.h @@ -0,0 +1,37 @@ +#include +#include + +int main(int argc, const char **argv) +{ + dbg_logger_stdout(); + IStorage *pStorage = CreateLocalStorage(); + if(argc == 1) + { + dbg_msg("Usage", "%s FILE1 [ FILE2... ]", argv[0]); + return -1; + } + for(int i = 1; i < argc; i++) + { + char aConfig[2048]; + + size_t Len = str_length(argv[i]) + 1; // including '\0' + if(Len > sizeof(aConfig)) + { + dbg_msg("config_common", "can't process overlong filename '%s'", argv[i]); + continue; + } + + if(Len < sizeof(".map") || str_comp(argv[i] + Len - sizeof(".map"), ".map") != 0) + { + dbg_msg("config_common", "can't process non-map file '%s'", argv[i]); + continue; + } + + str_copy(aConfig, argv[i], sizeof(aConfig)); + aConfig[Len - sizeof(".map")] = 0; + str_append(aConfig, ".cfg", sizeof(aConfig)); + dbg_msg("config_common", "processing '%s'", argv[i]); + Process(pStorage, argv[i], aConfig); + } + return 0; +} diff --git a/src/tools/config_retrieve.cpp b/src/tools/config_retrieve.cpp new file mode 100644 index 000000000..a5b4db93a --- /dev/null +++ b/src/tools/config_retrieve.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +void Process(IStorage *pStorage, const char *pMapName, const char *pConfigName) +{ + CDataFileReader Map; + if(!Map.Open(pStorage, pMapName, IStorage::TYPE_ALL)) + { + dbg_msg("config_retrieve", "error opening map '%s'", pMapName); + return; + } + bool ConfigFound = false; + int Start, Num; + Map.GetType(MAPITEMTYPE_INFO, &Start, &Num); + for(int i = Start; i < Start + Num; i++) + { + int ItemID; + CMapItemInfoSettings *pItem = (CMapItemInfoSettings *)Map.GetItem(i, 0, &ItemID); + int ItemSize = Map.GetItemSize(i) - 8; + if(!pItem || ItemID != 0) + continue; + + if(ItemSize < (int)sizeof(CMapItemInfoSettings)) + break; + if(!(pItem->m_Settings > -1)) + break; + + ConfigFound = true; + IOHANDLE Config = pStorage->OpenFile(pConfigName, IOFLAG_WRITE, IStorage::TYPE_ALL); + if(!Config) + { + dbg_msg("config_retrieve", "error opening config for writing '%s'", pConfigName); + return; + } + + int Size = Map.GetUncompressedDataSize(pItem->m_Settings); + char *pSettings = (char *)Map.GetData(pItem->m_Settings); + char *pNext = pSettings; + while(pNext < pSettings + Size) + { + int StrSize = str_length(pNext) + 1; + io_write(Config, pNext, StrSize - 1); + io_write_newline(Config); + pNext += StrSize; + } + Map.UnloadData(pItem->m_Settings); + io_close(Config); + break; + } + Map.Close(); + if(!ConfigFound) + { + fs_remove(pConfigName); + } +} +#include "config_common.h" diff --git a/src/tools/config_store.cpp b/src/tools/config_store.cpp new file mode 100644 index 000000000..38a54a786 --- /dev/null +++ b/src/tools/config_store.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include + +void Process(IStorage *pStorage, const char *pMapName, const char *pConfigName) +{ + IOHANDLE File = pStorage->OpenFile(pConfigName, IOFLAG_READ, IStorage::TYPE_ALL); + array aLines; + char *pSettings = NULL; + if(!File) + { + dbg_msg("config_store", "config '%s' not found", pConfigName); + return; + } + + CLineReader LineReader; + LineReader.Init(File); + + char *pLine; + int TotalLength = 0; + while((pLine = LineReader.Get())) + { + int Length = str_length(pLine) + 1; + char *pCopy = (char *)mem_alloc(Length, 1); + mem_copy(pCopy, pLine, Length); + aLines.add(pCopy); + TotalLength += Length; + } + + pSettings = (char *)mem_alloc(TotalLength, 1); + int Offset = 0; + for(int i = 0; i < aLines.size(); i++) + { + int Length = str_length(aLines[i]) + 1; + mem_copy(pSettings + Offset, aLines[i], Length); + Offset += Length; + mem_free(aLines[i]); + } + + CDataFileReader Reader; + Reader.Open(pStorage, pMapName, IStorage::TYPE_ALL); + + CDataFileWriter Writer; + Writer.Init(); + + int SettingsIndex = Reader.NumData(); + for(int i = 0; i < Reader.NumItems(); i++) + { + int TypeID; + int ItemID; + int *pData = (int *)Reader.GetItem(i, &TypeID, &ItemID); + // GetItemSize returns item size including header, remove that. + int Size = Reader.GetItemSize(i) - sizeof(int) * 2; + CMapItemInfoSettings MapInfo; + if(TypeID == MAPITEMTYPE_INFO && ItemID == 0) + { + CMapItemInfoSettings *pInfo = (CMapItemInfoSettings *)pData; + if(Size >= (int)sizeof(CMapItemInfoSettings)) + { + MapInfo = *pInfo; + pData = (int *)&MapInfo; + Size = sizeof(MapInfo); + if(pInfo->m_Settings > -1) + { + SettingsIndex = pInfo->m_Settings; + char *pMapSettings = (char *)Reader.GetData(SettingsIndex); + int DataSize = Reader.GetUncompressedDataSize(SettingsIndex); + if(DataSize == TotalLength && mem_comp(pSettings, pMapSettings, Size) == 0) + { + dbg_msg("config_store", "configs coincide, not updating map"); + return; + } + Reader.UnloadData(pInfo->m_Settings); + } + else + { + MapInfo = *pInfo; + MapInfo.m_Settings = SettingsIndex; + pData = (int *)&MapInfo; + Size = sizeof(MapInfo); + } + } + else + { + *(CMapItemInfo *)&MapInfo = *(CMapItemInfo *)pInfo; + MapInfo.m_Settings = SettingsIndex; + pData = (int *)&MapInfo; + Size = sizeof(MapInfo); + } + } + Writer.AddItem(TypeID, ItemID, Size, pData); + } + + for(int i = 0; i < Reader.NumData() || i == SettingsIndex; i++) + { + if(i == SettingsIndex) + { + Writer.AddData(TotalLength, pSettings); + continue; + } + unsigned char *pData = (unsigned char *)Reader.GetData(i); + int Size = Reader.GetUncompressedDataSize(i); + Writer.AddData(Size, pData); + Reader.UnloadData(i); + } + + Reader.Close(); + if(!Writer.OpenFile(pStorage, pMapName)) + { + dbg_msg("config_store", "couldn't open map file '%s' for writing", pMapName); + return; + } + Writer.Finish(); + dbg_msg("config_store", "imported settings"); +} +#include "config_common.h"