forked from Mirrors/openclonk
553 lines
14 KiB
C++
553 lines
14 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2006-2007, 2009 Sven Eberhardt
|
|
* Copyright (c) 2006 Florian Groß
|
|
* Copyright (c) 2006-2007 Peter Wortmann
|
|
* Copyright (c) 2006 Günther Brammer
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
|
*
|
|
* Portions might be copyrighted by other authors who have contributed
|
|
* to OpenClonk.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
* See isc_license.txt for full license and disclaimer.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender.
|
|
* See clonk_trademark_license.txt for full license.
|
|
*/
|
|
#include "C4Include.h"
|
|
#include "C4GameParameters.h"
|
|
|
|
#ifndef BIG_C4INCLUDE
|
|
#include "C4Log.h"
|
|
#include "C4Components.h"
|
|
|
|
#include "C4Wrappers.h"
|
|
#endif
|
|
|
|
// *** C4GameRes
|
|
|
|
|
|
C4GameRes::C4GameRes()
|
|
: eType(NRT_Null), pResCore(NULL), pNetRes(NULL)
|
|
{
|
|
|
|
}
|
|
|
|
C4GameRes::C4GameRes(const C4GameRes &Res)
|
|
: eType(Res.getType()), File(Res.getFile()), pResCore(Res.getResCore()), pNetRes(Res.getNetRes())
|
|
{
|
|
if(pResCore && !pNetRes)
|
|
pResCore = new C4Network2ResCore(*pResCore);
|
|
}
|
|
|
|
|
|
C4GameRes::~C4GameRes()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
C4GameRes &C4GameRes::operator = (const C4GameRes &Res)
|
|
{
|
|
Clear();
|
|
eType = Res.getType();
|
|
File = Res.getFile();
|
|
pResCore = Res.getResCore();
|
|
pNetRes = Res.getNetRes();
|
|
if(pResCore && !pNetRes)
|
|
pResCore = new C4Network2ResCore(*pResCore);
|
|
return *this;
|
|
}
|
|
|
|
void C4GameRes::Clear()
|
|
{
|
|
eType = NRT_Null;
|
|
File.Clear();
|
|
if(pResCore && !pNetRes)
|
|
delete pResCore;
|
|
pResCore = NULL;
|
|
pNetRes = NULL;
|
|
}
|
|
|
|
void C4GameRes::SetFile(C4Network2ResType enType, const char *sznFile)
|
|
{
|
|
assert(!pNetRes && !pResCore);
|
|
eType = enType;
|
|
File = sznFile;
|
|
}
|
|
|
|
void C4GameRes::SetResCore(C4Network2ResCore *pnResCore)
|
|
{
|
|
assert(!pNetRes);
|
|
pResCore = pnResCore;
|
|
eType = pResCore->getType();
|
|
}
|
|
|
|
void C4GameRes::SetNetRes(C4Network2Res::Ref pnNetRes)
|
|
{
|
|
Clear();
|
|
pNetRes = pnNetRes;
|
|
eType = pNetRes->getType();
|
|
File = pNetRes->getFile();
|
|
pResCore = &pNetRes->getCore();
|
|
}
|
|
|
|
void C4GameRes::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
bool fCompiler = pComp->isCompiler();
|
|
// Clear previous data for compiling
|
|
if(fCompiler) Clear();
|
|
// Core is needed to decompile something meaningful
|
|
if(!fCompiler) assert(pResCore);
|
|
// De-/Compile core
|
|
pComp->Value(mkPtrAdaptNoNull(const_cast<C4Network2ResCore * &>(pResCore)));
|
|
// Compile: Set type accordingly
|
|
if(fCompiler)
|
|
eType = pResCore->getType();
|
|
}
|
|
|
|
bool C4GameRes::Publish(C4Network2ResList *pNetResList)
|
|
{
|
|
assert(isPresent());
|
|
// Already present?
|
|
if(pNetRes) return true;
|
|
// determine whether it's loadable
|
|
bool fAllowUnloadable = false;
|
|
if (eType == NRT_Definitions) fAllowUnloadable = true;
|
|
// Add to network resource list
|
|
C4Network2Res::Ref pNetRes = pNetResList->AddByFile(File.getData(), false, eType, -1, NULL, fAllowUnloadable);
|
|
if(!pNetRes) return false;
|
|
// Set resource
|
|
SetNetRes(pNetRes);
|
|
return true;
|
|
}
|
|
|
|
bool C4GameRes::Load(C4Network2ResList *pNetResList)
|
|
{
|
|
assert(pResCore);
|
|
// Already present?
|
|
if(pNetRes) return true;
|
|
// Add to network resource list
|
|
C4Network2Res::Ref pNetRes = pNetResList->AddByCore(*pResCore);
|
|
if(!pNetRes) return false;
|
|
// Set resource
|
|
SetNetRes(pNetRes);
|
|
return true;
|
|
}
|
|
|
|
bool C4GameRes::InitNetwork(C4Network2ResList *pNetResList)
|
|
{
|
|
// Already initialized?
|
|
if(getNetRes())
|
|
return true;
|
|
// Present? [Host]
|
|
if(isPresent())
|
|
{
|
|
// Publish on network
|
|
if(!Publish(pNetResList))
|
|
{
|
|
LogFatal(FormatString(LoadResStr("IDS_NET_NOFILEPUBLISH"), getFile()).getData());
|
|
return false;
|
|
}
|
|
}
|
|
// Got a core? [Client]
|
|
else if(pResCore)
|
|
{
|
|
// Search/Load it
|
|
if(!Load(pNetResList))
|
|
{
|
|
// Give some hints to why this might happen.
|
|
const char *szFilename = pResCore->getFileName();
|
|
if(!pResCore->isLoadable())
|
|
if (pResCore->getType() == NRT_System)
|
|
LogFatal(FormatString(LoadResStr("IDS_NET_NOSAMESYSTEM"), szFilename).getData());
|
|
else
|
|
LogFatal(FormatString(LoadResStr("IDS_NET_NOSAMEANDTOOLARGE"), szFilename).getData());
|
|
// Should not happen
|
|
else
|
|
LogFatal(FormatString(LoadResStr("IDS_NET_NOVALIDCORE"), szFilename).getData());
|
|
return false;
|
|
}
|
|
}
|
|
// Okay
|
|
return true;
|
|
}
|
|
|
|
void C4GameRes::CalcHash()
|
|
{
|
|
if(!pNetRes) return;
|
|
pNetRes->CalculateSHA();
|
|
}
|
|
|
|
// *** C4GameResList
|
|
|
|
C4GameResList &C4GameResList::operator = (const C4GameResList &List)
|
|
{
|
|
Clear();
|
|
// Copy the list
|
|
iResCount = iResCapacity = List.iResCount;
|
|
pResList = new C4GameRes *[iResCapacity];
|
|
for(int i = 0; i < iResCount; i++)
|
|
pResList[i] = new C4GameRes(*List.pResList[i]);
|
|
return *this;
|
|
}
|
|
|
|
C4GameRes *C4GameResList::iterRes(C4GameRes *pLast, C4Network2ResType eType)
|
|
{
|
|
for(int i = 0; i < iResCount; i++)
|
|
if(!pLast)
|
|
{
|
|
if(eType == NRT_Null || pResList[i]->getType() == eType)
|
|
return pResList[i];
|
|
}
|
|
else if(pLast == pResList[i])
|
|
pLast = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
void C4GameResList::Clear()
|
|
{
|
|
// clear them
|
|
for(int32_t i = 0; i < iResCount; i++)
|
|
delete pResList[i];
|
|
delete [] pResList;
|
|
pResList = NULL;
|
|
iResCount = iResCapacity = 0;
|
|
}
|
|
|
|
bool C4GameResList::Load(const char *szDefinitionFilenames)
|
|
{
|
|
// clear any prev
|
|
Clear();
|
|
// no defs to be added? that's OK (LocalOnly)
|
|
if (szDefinitionFilenames && *szDefinitionFilenames)
|
|
{
|
|
// add them
|
|
char szSegment[_MAX_PATH+1];
|
|
for (int32_t cseg=0; SCopySegment(szDefinitionFilenames,cseg,szSegment,';',_MAX_PATH); ++cseg)
|
|
if (*szSegment)
|
|
CreateByFile(NRT_Definitions, szSegment);
|
|
}
|
|
// add System.c4g
|
|
CreateByFile(NRT_System, C4CFN_System);
|
|
// add all instances of Material.c4g, except those inside the scenario file
|
|
C4Group *pMatParentGrp = NULL;
|
|
while(pMatParentGrp = Game.GroupSet.FindGroup(C4GSCnt_Material, pMatParentGrp))
|
|
if(pMatParentGrp != &Game.ScenarioFile)
|
|
{
|
|
StdStrBuf MaterialPath = pMatParentGrp->GetFullName() + DirSep C4CFN_Material;
|
|
CreateByFile(NRT_Material, (pMatParentGrp->GetFullName() + DirSep C4CFN_Material).getData());
|
|
}
|
|
// add global Material.c4g
|
|
CreateByFile(NRT_Material, C4CFN_Material);
|
|
// done; success
|
|
return true;
|
|
}
|
|
|
|
C4GameRes *C4GameResList::CreateByFile(C4Network2ResType eType, const char *szFile)
|
|
{
|
|
// Create & set
|
|
C4GameRes *pRes = new C4GameRes();
|
|
pRes->SetFile(eType, szFile);
|
|
// Add to list
|
|
Add(pRes);
|
|
return pRes;
|
|
}
|
|
|
|
C4GameRes *C4GameResList::CreateByNetRes(C4Network2Res::Ref pNetRes)
|
|
{
|
|
// Create & set
|
|
C4GameRes *pRes = new C4GameRes();
|
|
pRes->SetNetRes(pNetRes);
|
|
// Add to list
|
|
Add(pRes);
|
|
return pRes;
|
|
}
|
|
|
|
bool C4GameResList::InitNetwork(C4Network2ResList *pNetResList)
|
|
{
|
|
// Check all resources without attached network resource object
|
|
for(int i = 0; i < iResCount; i++)
|
|
if(!pResList[i]->InitNetwork(pNetResList))
|
|
return false;
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
void C4GameResList::CalcHashes()
|
|
{
|
|
for (int32_t i = 0; i < iResCount; i++)
|
|
pResList[i]->CalcHash();
|
|
}
|
|
|
|
bool C4GameResList::RetrieveFiles()
|
|
{
|
|
// wait for all resources
|
|
for (int32_t i = 0; i < iResCount; i++)
|
|
{
|
|
const C4Network2ResCore &Core = *pResList[i]->getResCore();
|
|
StdStrBuf ResNameBuf = FormatString("%s: %s", LoadResStr("IDS_DLG_DEFINITION"), GetFilename(Core.getFileName()));
|
|
if (!::Network.RetrieveRes(Core, C4NetResRetrieveTimeout, ResNameBuf.getData()))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void C4GameResList::Add(C4GameRes *pRes)
|
|
{
|
|
// Enlarge
|
|
if(iResCount >= iResCapacity)
|
|
{
|
|
iResCapacity += 10;
|
|
C4GameRes **pnResList = new C4GameRes *[iResCapacity];
|
|
for(int i = 0; i < iResCount; i++)
|
|
pnResList[i] = pResList[i];
|
|
pResList = pnResList;
|
|
}
|
|
// Add
|
|
pResList[iResCount++] = pRes;
|
|
}
|
|
|
|
void C4GameResList::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
bool fCompiler = pComp->isCompiler();
|
|
// Clear previous data
|
|
if(fCompiler) Clear();
|
|
// Compile resource count
|
|
pComp->Value(mkNamingCountAdapt(iResCount, "Resource"));
|
|
// Create list
|
|
if(fCompiler)
|
|
{
|
|
pResList = new C4GameRes *[iResCapacity = iResCount];
|
|
ZeroMem(pResList, sizeof(*pResList) * iResCount);
|
|
}
|
|
// Compile list
|
|
pComp->Value(
|
|
mkNamingAdapt(
|
|
mkArrayAdaptMap(pResList, iResCount, /*(C4GameRes *)NULL, */ mkPtrAdaptNoNull<C4GameRes>),
|
|
"Resource"));
|
|
mkPtrAdaptNoNull<C4GameRes>(*pResList);
|
|
}
|
|
|
|
// *** C4GameParameters
|
|
|
|
C4GameParameters::C4GameParameters()
|
|
{
|
|
|
|
}
|
|
|
|
C4GameParameters::~C4GameParameters()
|
|
{
|
|
|
|
}
|
|
|
|
void C4GameParameters::Clear()
|
|
{
|
|
League.Clear();
|
|
LeagueAddress.Clear();
|
|
Rules.Clear();
|
|
Goals.Clear();
|
|
Scenario.Clear();
|
|
GameRes.Clear();
|
|
Clients.Clear();
|
|
PlayerInfos.Clear();
|
|
RestorePlayerInfos.Clear();
|
|
Teams.Clear();
|
|
}
|
|
|
|
BOOL C4GameParameters::Load(C4Group &hGroup, C4Scenario *pScenario, const char *szGameText, C4LangStringTable *pLang, const char *DefinitionFilenames)
|
|
{
|
|
// Clear previous data
|
|
Clear();
|
|
|
|
// Scenario
|
|
Scenario.SetFile(NRT_Scenario, hGroup.GetFullName().getData());
|
|
|
|
// Additional game resources
|
|
if(!GameRes.Load(DefinitionFilenames))
|
|
return false;
|
|
|
|
// Player infos (replays only)
|
|
if (pScenario->Head.Replay)
|
|
if (hGroup.FindEntry(C4CFN_PlayerInfos))
|
|
PlayerInfos.Load(hGroup, C4CFN_PlayerInfos);
|
|
|
|
// Savegame restore infos: Used for savegames to rejoin joined players
|
|
if (hGroup.FindEntry(C4CFN_SavePlayerInfos))
|
|
{
|
|
// load to savegame info list
|
|
RestorePlayerInfos.Load(hGroup, C4CFN_SavePlayerInfos, pLang);
|
|
// transfer counter to allow for additional player joins in savegame resumes
|
|
PlayerInfos.SetIDCounter(RestorePlayerInfos.GetIDCounter());
|
|
// in network mode, savegame players may be reassigned in the lobby
|
|
// in any mode, the final player restoration will be done in InitPlayers()
|
|
// dropping any players that could not be restored
|
|
}
|
|
else if(pScenario->Head.SaveGame)
|
|
{
|
|
// maybe there should be a player info file? (old-style savegame)
|
|
if (szGameText)
|
|
{
|
|
// then recreate the player infos to be restored from game text
|
|
RestorePlayerInfos.LoadFromGameText(szGameText);
|
|
// transfer counter
|
|
PlayerInfos.SetIDCounter(RestorePlayerInfos.GetIDCounter());
|
|
}
|
|
}
|
|
|
|
// Load teams
|
|
if (!Teams.Load(hGroup, pScenario, pLang))
|
|
{ LogFatal(LoadResStr("IDS_PRC_ERRORLOADINGTEAMS")); return FALSE; }
|
|
|
|
// Compile data
|
|
StdStrBuf Buf;
|
|
if(hGroup.LoadEntryString(C4CFN_Parameters, Buf))
|
|
{
|
|
if(!CompileFromBuf_LogWarn<StdCompilerINIRead>(
|
|
mkNamingAdapt(mkParAdapt(*this, pScenario), "Parameters"),
|
|
Buf,
|
|
C4CFN_Parameters))
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set default values
|
|
StdCompilerNull DefaultCompiler;
|
|
DefaultCompiler.Compile(mkParAdapt(*this, pScenario));
|
|
|
|
// Set control rate default
|
|
if(ControlRate < 0)
|
|
ControlRate = Config.Network.ControlRate;
|
|
|
|
// network game?
|
|
IsNetworkGame = Game.NetworkActive;
|
|
|
|
// FairCrew-flag by command line
|
|
if (!FairCrewForced)
|
|
UseFairCrew = !!Config.General.FairCrew;
|
|
if (!FairCrewStrength && UseFairCrew)
|
|
FairCrewStrength = Config.General.FairCrewStrength;
|
|
|
|
}
|
|
|
|
|
|
// enforce league settings
|
|
if (isLeague()) EnforceLeagueRules(pScenario);
|
|
|
|
// Done
|
|
return TRUE;
|
|
}
|
|
|
|
void C4GameParameters::EnforceLeagueRules(C4Scenario *pScenario)
|
|
{
|
|
Scenario.CalcHash();
|
|
GameRes.CalcHashes();
|
|
Teams.EnforceLeagueRules();
|
|
AllowDebug = false;
|
|
// Fair crew enabled in league, if not explicitely disabled by scenario
|
|
// Fair crew strengt to a moderately high value
|
|
if (!Game.Parameters.FairCrewForced)
|
|
{
|
|
Game.Parameters.UseFairCrew = true;
|
|
Game.Parameters.FairCrewForced = true;
|
|
Game.Parameters.FairCrewStrength = 20000;
|
|
}
|
|
if (pScenario) MaxPlayers = pScenario->Head.MaxPlayerLeague;
|
|
}
|
|
|
|
BOOL C4GameParameters::Save(C4Group &hGroup, C4Scenario *pScenario)
|
|
{
|
|
|
|
// Write Parameters.txt
|
|
StdStrBuf ParData = DecompileToBuf<StdCompilerINIWrite>(
|
|
mkNamingAdapt(mkParAdapt(*this, pScenario), "Parameters"));
|
|
if(!hGroup.Add(C4CFN_Parameters, ParData, FALSE, TRUE))
|
|
return FALSE;
|
|
|
|
// Done
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL C4GameParameters::InitNetwork(C4Network2ResList *pResList)
|
|
{
|
|
|
|
// Scenario & material resource
|
|
if(!Scenario.InitNetwork(pResList))
|
|
return FALSE;
|
|
|
|
// Other game resources
|
|
if(!GameRes.InitNetwork(pResList))
|
|
return FALSE;
|
|
|
|
// Done
|
|
return TRUE;
|
|
}
|
|
|
|
void C4GameParameters::CompileFunc(StdCompiler *pComp, C4Scenario *pScenario)
|
|
{
|
|
pComp->Value(mkNamingAdapt(MaxPlayers, "MaxPlayers", !pScenario ? 0 : pScenario->Head.MaxPlayer));
|
|
pComp->Value(mkNamingAdapt(UseFairCrew, "UseFairCrew", !pScenario ? false : (pScenario->Head.ForcedFairCrew == C4SFairCrew_FairCrew)));
|
|
pComp->Value(mkNamingAdapt(FairCrewForced, "FairCrewForced", !pScenario ? false : (pScenario->Head.ForcedFairCrew != C4SFairCrew_Free)));
|
|
pComp->Value(mkNamingAdapt(FairCrewStrength, "FairCrewStrength", !pScenario ? 0 : pScenario->Head.FairCrewStrength));
|
|
pComp->Value(mkNamingAdapt(AllowDebug, "AllowDebug", true));
|
|
pComp->Value(mkNamingAdapt(IsNetworkGame, "IsNetworkGame", false));
|
|
pComp->Value(mkNamingAdapt(ControlRate, "ControlRate", -1));
|
|
pComp->Value(mkNamingAdapt(Rules, "Rules", !pScenario ? C4IDList() : pScenario->Game.Rules));
|
|
pComp->Value(mkNamingAdapt(Goals, "Goals", !pScenario ? C4IDList() : pScenario->Game.Goals));
|
|
pComp->Value(mkNamingAdapt(League, "League", StdStrBuf()));
|
|
|
|
// These values are either stored seperately (see Load/Save) or
|
|
// don't make sense for savegames.
|
|
if(!pScenario)
|
|
{
|
|
pComp->Value(mkNamingAdapt(LeagueAddress, "LeagueAddress", ""));
|
|
|
|
pComp->Value(mkNamingAdapt(Scenario, "Scenario" ));
|
|
pComp->Value(GameRes);
|
|
|
|
pComp->Value(mkNamingAdapt(PlayerInfos, "PlayerInfos" ));
|
|
pComp->Value(mkNamingAdapt(RestorePlayerInfos,"RestorePlayerInfos"));
|
|
pComp->Value(mkNamingAdapt(Teams, "Teams" ));
|
|
}
|
|
|
|
pComp->Value(Clients);
|
|
|
|
}
|
|
|
|
StdStrBuf C4GameParameters::GetGameGoalString()
|
|
{
|
|
// getting game goals from the ID list
|
|
// unfortunately, names cannot be deduced before object definitions are loaded
|
|
StdStrBuf sResult;
|
|
C4ID idGoal;
|
|
for (int32_t i=0; i<Goals.GetNumberOfIDs(); ++i)
|
|
if (idGoal = Goals.GetID(i)) if (idGoal != C4ID_None)
|
|
{
|
|
if (Game.IsRunning)
|
|
{
|
|
C4Def *pDef = C4Id2Def(idGoal);
|
|
if (pDef)
|
|
{
|
|
if (sResult.getLength()) sResult.Append(", ");
|
|
sResult.Append(pDef->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sResult.getLength()) sResult.Append(", ");
|
|
sResult.Append(C4IdText(idGoal));
|
|
}
|
|
}
|
|
// Max length safety
|
|
if (sResult.getLength() > C4MaxTitle) sResult.SetLength(C4MaxTitle);
|
|
// Compose desc string
|
|
if (sResult.getLength())
|
|
return FormatString("%s: %s", LoadResStr("IDS_MENU_CPGOALS"), sResult.getData());
|
|
else
|
|
return StdCopyStrBuf(LoadResStr("IDS_CTL_NOGOAL"), true);
|
|
}
|