Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
ac1bfd9671
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -28,3 +28,6 @@
|
||||
[submodule "lib/libevent"]
|
||||
path = lib/libevent
|
||||
url = https://github.com/mc-server/libevent.git
|
||||
[submodule "lib/TCLAP"]
|
||||
path = lib/TCLAP
|
||||
url = https://github.com/mc-server/TCLAP.git
|
||||
|
@ -7,6 +7,7 @@ Diusrex
|
||||
Duralex
|
||||
FakeTruth (founder)
|
||||
Howaner
|
||||
jasperarmstrong
|
||||
keyboard
|
||||
Lapayo
|
||||
Luksor
|
||||
|
29
MCServer/Plugins/APIDump/Hooks/OnEntityChangeWorld.lua
Normal file
29
MCServer/Plugins/APIDump/Hooks/OnEntityChangeWorld.lua
Normal file
@ -0,0 +1,29 @@
|
||||
return
|
||||
{
|
||||
HOOK_ENTITY_CHANGE_WORLD =
|
||||
{
|
||||
CalledWhen = "Before a entity is changing the world.",
|
||||
DefaultFnName = "OnEntityChangeWorld", -- also used as pagename
|
||||
Desc = [[
|
||||
This hook is called before the server moves the {{cEntity|entity}} to the given world. Plugins may
|
||||
refuse the changing of the entity to the new world.<p>
|
||||
See also the {{OnEntityChangedWorld|HOOK_ENTITY_CHANGED_WORLD}} hook for a similar hook is called after the
|
||||
entity has been moved to the world.
|
||||
]],
|
||||
Params =
|
||||
{
|
||||
{ Name = "Entity", Type = "{{cEntity}}", Notes = "The entity that wants to change the world" },
|
||||
{ Name = "World", Type = "{{cWorld}}", Notes = "The world to which the entity wants to change" },
|
||||
},
|
||||
Returns = [[
|
||||
If the function returns false or no value, the next plugin's callback is called. If the function
|
||||
returns true, no other callback is called for this event and the change of the entity to the world is
|
||||
cancelled.
|
||||
]],
|
||||
}, -- HOOK_ENTITY_CHANGE_WORLD
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
28
MCServer/Plugins/APIDump/Hooks/OnEntityChangedWorld.lua
Normal file
28
MCServer/Plugins/APIDump/Hooks/OnEntityChangedWorld.lua
Normal file
@ -0,0 +1,28 @@
|
||||
return
|
||||
{
|
||||
HOOK_ENTITY_CHANGED_WORLD =
|
||||
{
|
||||
CalledWhen = "After a entity has changed the world.",
|
||||
DefaultFnName = "OnEntityChangedWorld", -- also used as pagename
|
||||
Desc = [[
|
||||
This hook is called after the server has moved the {{cEntity|entity}} to the given world. This is an information-only
|
||||
callback, the entity is already in the new world.<p>
|
||||
See also the {{OnEntityChangeWorld|HOOK_ENTITY_CHANGE_WORLD}} hook for a similar hook called before the
|
||||
entity is moved to the new world.
|
||||
]],
|
||||
Params =
|
||||
{
|
||||
{ Name = "Entity", Type = "{{cEntity}}", Notes = "The entity that has changed the world" },
|
||||
{ Name = "World", Type = "{{cWorld}}", Notes = "The world from which the entity has come" },
|
||||
},
|
||||
Returns = [[
|
||||
If the function returns false or no value, the next plugin's callback is called. If the function
|
||||
returns true, no other callback is called for this event.
|
||||
]],
|
||||
}, -- HOOK_ENTITY_CHANGED_WORLD
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit ea0ab964d568630fd4f2b52954186f2851a769e1
|
||||
Subproject commit 5171b43807ff699a6b239ad4969520730b3748a2
|
@ -36,9 +36,7 @@ Check out the [CONTRIBUTING.md](https://github.com/mc-server/MCServer/blob/maste
|
||||
Other Stuff
|
||||
-----------
|
||||
|
||||
For other stuff, including plugins and discussion, check the [forums](http://forum.mc-server.org) and [Plugin API](http://mc-server.xoft.cz/LuaAPI/).
|
||||
|
||||
Earn bitcoins for commits or donate to reward the MCServer developers: [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74)
|
||||
For other stuff, including plugins and discussion, check out the [forums](http://forum.mc-server.org) and [Plugin API](http://mc-server.xoft.cz/LuaAPI/).
|
||||
|
||||
Support Us on Gratipay: [![Support via Gratipay](http://img.shields.io/gittip/cuberite_team.svg)](https://www.gratipay.com/cuberite_team)
|
||||
|
||||
|
1
lib/TCLAP
Submodule
1
lib/TCLAP
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 12cee38782897cfe60a1611615c200c45cd99eaf
|
@ -56,6 +56,8 @@ public:
|
||||
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0;
|
||||
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0;
|
||||
virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0;
|
||||
virtual bool OnEntityChangeWorld (cEntity & a_Entity, cWorld & a_World) = 0;
|
||||
virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) = 0;
|
||||
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) = 0;
|
||||
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
|
||||
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
|
||||
|
@ -534,6 +534,54 @@ bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_E
|
||||
|
||||
|
||||
|
||||
bool cPluginLua::OnEntityChangeWorld(cEntity & a_Entity, cWorld & a_World)
|
||||
{
|
||||
cCSLock Lock(m_CriticalSection);
|
||||
if (!m_LuaState.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool res = false;
|
||||
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGE_WORLD];
|
||||
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
|
||||
{
|
||||
m_LuaState.Call((int)(**itr), &a_Entity, &a_World, cLuaState::Return, res);
|
||||
if (res)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World)
|
||||
{
|
||||
cCSLock Lock(m_CriticalSection);
|
||||
if (!m_LuaState.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool res = false;
|
||||
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGED_WORLD];
|
||||
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
|
||||
{
|
||||
m_LuaState.Call((int)(**itr), &a_Entity, &a_World, res);
|
||||
if (res)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result)
|
||||
{
|
||||
cCSLock Lock(m_CriticalSection);
|
||||
@ -1884,6 +1932,8 @@ const char * cPluginLua::GetHookFnName(int a_HookType)
|
||||
case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect";
|
||||
case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation";
|
||||
case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect";
|
||||
case cPluginManager::HOOK_ENTITY_CHANGE_WORLD: return "OnEntityChangeWorld";
|
||||
case cPluginManager::HOOK_ENTITY_CHANGED_WORLD: return "OnEntityChangedWorld";
|
||||
case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport";
|
||||
case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand";
|
||||
case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake";
|
||||
|
@ -115,6 +115,8 @@ public:
|
||||
virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override;
|
||||
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override;
|
||||
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override;
|
||||
virtual bool OnEntityChangeWorld (cEntity & a_Entity, cWorld & a_World) override;
|
||||
virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) override;
|
||||
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) override;
|
||||
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
|
||||
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
|
||||
|
@ -118,7 +118,7 @@ void cPluginManager::ReloadPluginsNow(void)
|
||||
|
||||
|
||||
|
||||
void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
|
||||
void cPluginManager::ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings)
|
||||
{
|
||||
LOG("-- Loading Plugins --");
|
||||
|
||||
@ -130,7 +130,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
|
||||
RefreshPluginList();
|
||||
|
||||
// Load the plugins:
|
||||
AStringVector ToLoad = GetFoldersToLoad(a_SettingsIni);
|
||||
AStringVector ToLoad = GetFoldersToLoad(a_Settings);
|
||||
for (auto & pluginFolder: ToLoad)
|
||||
{
|
||||
LoadPlugin(pluginFolder);
|
||||
@ -157,16 +157,16 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
|
||||
|
||||
|
||||
|
||||
void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni)
|
||||
void cPluginManager::InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings)
|
||||
{
|
||||
a_SettingsIni.AddKeyName("Plugins");
|
||||
a_SettingsIni.AddKeyComment("Plugins", " Plugin=Debuggers");
|
||||
a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify");
|
||||
a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx");
|
||||
a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump");
|
||||
a_SettingsIni.AddValue("Plugins", "Plugin", "Core");
|
||||
a_SettingsIni.AddValue("Plugins", "Plugin", "TransAPI");
|
||||
a_SettingsIni.AddValue("Plugins", "Plugin", "ChatLog");
|
||||
a_Settings.AddKeyName("Plugins");
|
||||
a_Settings.AddKeyComment("Plugins", " Plugin=Debuggers");
|
||||
a_Settings.AddKeyComment("Plugins", " Plugin=HookNotify");
|
||||
a_Settings.AddKeyComment("Plugins", " Plugin=ChunkWorx");
|
||||
a_Settings.AddKeyComment("Plugins", " Plugin=APIDump");
|
||||
a_Settings.AddValue("Plugins", "Plugin", "Core");
|
||||
a_Settings.AddValue("Plugins", "Plugin", "TransAPI");
|
||||
a_Settings.AddValue("Plugins", "Plugin", "ChatLog");
|
||||
}
|
||||
|
||||
|
||||
@ -525,6 +525,42 @@ bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d &
|
||||
|
||||
|
||||
|
||||
bool cPluginManager::CallHookEntityChangeWorld(cEntity & a_Entity, cWorld & a_World)
|
||||
{
|
||||
FIND_HOOK(HOOK_ENTITY_CHANGE_WORLD);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnEntityChangeWorld(a_Entity, a_World))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPluginManager::CallHookEntityChangedWorld(cEntity & a_Entity, cWorld & a_World)
|
||||
{
|
||||
FIND_HOOK(HOOK_ENTITY_CHANGED_WORLD);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnEntityChangedWorld(a_Entity, a_World))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result)
|
||||
{
|
||||
FIND_HOOK(HOOK_EXECUTE_COMMAND);
|
||||
@ -1896,25 +1932,23 @@ size_t cPluginManager::GetNumLoadedPlugins(void) const
|
||||
|
||||
|
||||
|
||||
AStringVector cPluginManager::GetFoldersToLoad(cIniFile & a_SettingsIni)
|
||||
AStringVector cPluginManager::GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings)
|
||||
{
|
||||
// Check if the Plugins section exists.
|
||||
int KeyNum = a_SettingsIni.FindKey("Plugins");
|
||||
if (KeyNum == -1)
|
||||
if (a_Settings.KeyExists("Plugins"))
|
||||
{
|
||||
InsertDefaultPlugins(a_SettingsIni);
|
||||
KeyNum = a_SettingsIni.FindKey("Plugins");
|
||||
InsertDefaultPlugins(a_Settings);
|
||||
}
|
||||
|
||||
// Get the list of plugins to load:
|
||||
AStringVector res;
|
||||
int NumPlugins = a_SettingsIni.GetNumValues(KeyNum);
|
||||
for (int i = 0; i < NumPlugins; i++)
|
||||
auto Values = a_Settings.GetValues("Plugins");
|
||||
for (auto NameValue : Values)
|
||||
{
|
||||
AString ValueName = a_SettingsIni.GetValueName(KeyNum, i);
|
||||
AString ValueName = NameValue.first;
|
||||
if (ValueName.compare("Plugin") == 0)
|
||||
{
|
||||
AString PluginFile = a_SettingsIni.GetValue(KeyNum, i);
|
||||
AString PluginFile = NameValue.second;
|
||||
if (!PluginFile.empty())
|
||||
{
|
||||
res.push_back(PluginFile);
|
||||
|
@ -24,6 +24,7 @@ class cPlayer;
|
||||
class cPlugin;
|
||||
class cProjectileEntity;
|
||||
class cWorld;
|
||||
class cSettingsRepositoryInterface;
|
||||
struct TakeDamageInfo;
|
||||
|
||||
typedef SharedPtr<cPlugin> cPluginPtr;
|
||||
@ -85,6 +86,8 @@ public:
|
||||
HOOK_DISCONNECT,
|
||||
HOOK_PLAYER_ANIMATION,
|
||||
HOOK_ENTITY_ADD_EFFECT,
|
||||
HOOK_ENTITY_CHANGE_WORLD,
|
||||
HOOK_ENTITY_CHANGED_WORLD,
|
||||
HOOK_EXECUTE_COMMAND,
|
||||
HOOK_EXPLODED,
|
||||
HOOK_EXPLODING,
|
||||
@ -200,6 +203,8 @@ public:
|
||||
bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason);
|
||||
bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier);
|
||||
bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition);
|
||||
bool CallHookEntityChangeWorld (cEntity & a_Entity, cWorld & a_World);
|
||||
bool CallHookEntityChangedWorld (cEntity & a_Entity, cWorld & a_World);
|
||||
bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result); // If a_Player == nullptr, it is a console cmd
|
||||
bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
|
||||
bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
|
||||
@ -364,20 +369,20 @@ private:
|
||||
/** Reloads all plugins, defaulting to settings.ini for settings location */
|
||||
void ReloadPluginsNow(void);
|
||||
|
||||
/** Reloads all plugins with a cIniFile object expected to be initialised to settings.ini */
|
||||
void ReloadPluginsNow(cIniFile & a_SettingsIni);
|
||||
/** Reloads all plugins with a settings repo expected to be initialised to settings.ini */
|
||||
void ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings);
|
||||
|
||||
/** Unloads all plugins */
|
||||
void UnloadPluginsNow(void);
|
||||
|
||||
/** Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini */
|
||||
void InsertDefaultPlugins(cIniFile & a_SettingsIni);
|
||||
/** Handles writing default plugins if 'Plugins' key not found using a settings repo expected to be intialised to settings.ini */
|
||||
void InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings);
|
||||
|
||||
/** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns crExecuted if the command is executed. */
|
||||
CommandResult HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions);
|
||||
|
||||
/** Returns the folders that are specified in the settings ini to load plugins from. */
|
||||
AStringVector GetFoldersToLoad(cIniFile & a_SettingsIni);
|
||||
AStringVector GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings);
|
||||
} ; // tolua_export
|
||||
|
||||
|
||||
|
@ -40,29 +40,41 @@ public:
|
||||
{
|
||||
cFastRandom rand;
|
||||
|
||||
// Old leaves - 3 bits contain display; new leaves - 1st bit, shifted left two for saplings to understand
|
||||
if (rand.NextInt(20) == 0)
|
||||
// There is a chance to drop a sapling that varies depending on the type of leaf broken.
|
||||
// TODO: Take into account fortune for sapling drops.
|
||||
int chance;
|
||||
if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE))
|
||||
{
|
||||
// Jungle leaves have a 2.5% chance of dropping a sapling.
|
||||
chance = rand.NextInt(40);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other leaves have a 5% chance of dropping a sapling.
|
||||
chance = rand.NextInt(20);
|
||||
}
|
||||
if (chance == 0)
|
||||
{
|
||||
a_Pickups.push_back(
|
||||
cItem(
|
||||
E_BLOCK_SAPLING,
|
||||
1,
|
||||
(m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (2 << (a_BlockMeta & 0x01))
|
||||
(m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (4 + (a_BlockMeta & 0x01))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 1 % chance of dropping an apple, if the leaves' type is Apple Leaves
|
||||
|
||||
// 0.5 % chance of dropping an apple, if the leaves' type is Apple Leaves
|
||||
if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE))
|
||||
{
|
||||
if (rand.NextInt(101) == 0)
|
||||
if (rand.NextInt(200) == 0)
|
||||
{
|
||||
a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
|
||||
{
|
||||
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
|
@ -48,11 +48,13 @@ SET (SRCS
|
||||
Logger.cpp
|
||||
Map.cpp
|
||||
MapManager.cpp
|
||||
MemorySettingsRepository.cpp
|
||||
MobCensus.cpp
|
||||
MobFamilyCollecter.cpp
|
||||
MobProximityCounter.cpp
|
||||
MobSpawner.cpp
|
||||
MonsterConfig.cpp
|
||||
OverridesSettingsRepository.cpp
|
||||
ProbabDistrib.cpp
|
||||
RankManager.cpp
|
||||
RCONServer.cpp
|
||||
@ -116,11 +118,13 @@ SET (HDRS
|
||||
Map.h
|
||||
MapManager.h
|
||||
Matrix4.h
|
||||
MemorySettingsRepository.h
|
||||
MobCensus.h
|
||||
MobFamilyCollecter.h
|
||||
MobProximityCounter.h
|
||||
MobSpawner.h
|
||||
MonsterConfig.h
|
||||
OverridesSettingsRepository.h
|
||||
ProbabDistrib.h
|
||||
RankManager.h
|
||||
RCONServer.h
|
||||
@ -128,6 +132,7 @@ SET (HDRS
|
||||
Scoreboard.h
|
||||
Server.h
|
||||
SetChunkData.h
|
||||
SettingsRepositoryInterface.h
|
||||
Statistics.h
|
||||
StringCompression.h
|
||||
StringUtils.h
|
||||
@ -142,6 +147,7 @@ SET (HDRS
|
||||
include_directories(".")
|
||||
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite")
|
||||
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/SQLiteCpp/include")
|
||||
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/TCLAP/include")
|
||||
|
||||
configure_file("BuildInfo.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.h")
|
||||
|
||||
|
@ -1403,14 +1403,25 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ask the plugins if the entity is allowed to change the world
|
||||
if (cRoot::Get()->GetPluginManager()->CallHookEntityChangeWorld(*this, *a_World))
|
||||
{
|
||||
// A Plugin doesn't allow the entity to change the world
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove all links to the old world
|
||||
SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal
|
||||
GetWorld()->BroadcastDestroyEntity(*this);
|
||||
|
||||
// Queue add to new world
|
||||
a_World->AddEntity(this);
|
||||
cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
|
||||
SetWorld(a_World);
|
||||
|
||||
// Entity changed the world, call the hook
|
||||
cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1606,6 +1606,12 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cRoot::Get()->GetPluginManager()->CallHookEntityChangeWorld(*this, *a_World))
|
||||
{
|
||||
// A Plugin doesn't allow the player to change the world
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send the respawn packet:
|
||||
if (a_ShouldSendRespawn && (m_ClientHandle != nullptr))
|
||||
{
|
||||
@ -1621,6 +1627,7 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
||||
|
||||
// Queue adding player to the new world, including all the necessary adjustments to the object
|
||||
a_World->AddPlayer(this);
|
||||
cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
|
||||
SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
|
||||
|
||||
// Update the view distance.
|
||||
@ -1635,6 +1642,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
||||
// Broadcast the player into the new world.
|
||||
a_World->BroadcastSpawnEntity(*this);
|
||||
|
||||
// Player changed the world, call the hook
|
||||
cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1274,6 +1274,7 @@ bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX
|
||||
double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5);
|
||||
|
||||
cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn);
|
||||
NewMob->SetHealth(NewMob->GetMaxHealth());
|
||||
NewMob->SetPosition(AnimalX, AnimalY, AnimalZ);
|
||||
a_ChunkDesc.GetEntities().push_back(NewMob);
|
||||
LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ);
|
||||
|
@ -433,10 +433,14 @@ typename std::enable_if<std::is_arithmetic<T>::value, C>::type CeilC(T a_Value)
|
||||
|
||||
|
||||
//temporary replacement for std::make_unique until we get c++14
|
||||
template <class T, class... Args>
|
||||
std::unique_ptr<T> make_unique(Args&&... args)
|
||||
|
||||
namespace cpp14
|
||||
{
|
||||
return std::unique_ptr<T>(new T(args...));
|
||||
template <class T, class... Args>
|
||||
std::unique_ptr<T> make_unique(Args&&... args)
|
||||
{
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
}
|
||||
|
||||
// a tick is 50 ms
|
||||
|
@ -49,6 +49,9 @@ cIniFile::cIniFile(void) :
|
||||
|
||||
bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
|
||||
{
|
||||
|
||||
m_Filename = a_FileName;
|
||||
|
||||
// Normally you would use ifstream, but the SGI CC compiler has
|
||||
// a few bugs with ifstream. So ... fstream used.
|
||||
fstream f;
|
||||
@ -56,7 +59,8 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
|
||||
AString keyname, valuename, value;
|
||||
AString::size_type pLeft, pRight;
|
||||
bool IsFromExampleRedirect = false;
|
||||
|
||||
|
||||
|
||||
f.open((FILE_IO_PREFIX + a_FileName).c_str(), ios::in);
|
||||
if (f.fail())
|
||||
{
|
||||
@ -650,7 +654,7 @@ void cIniFile::Clear(void)
|
||||
|
||||
|
||||
|
||||
bool cIniFile::HasValue(const AString & a_KeyName, const AString & a_ValueName)
|
||||
bool cIniFile::HasValue(const AString & a_KeyName, const AString & a_ValueName) const
|
||||
{
|
||||
// Find the key:
|
||||
int keyID = FindKey(a_KeyName);
|
||||
@ -889,8 +893,36 @@ void cIniFile::RemoveBom(AString & a_line) const
|
||||
|
||||
|
||||
|
||||
bool cIniFile::KeyExists(AString a_keyname) const
|
||||
{
|
||||
return FindKey(a_keyname) != noID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::vector<std::pair<AString, AString>> cIniFile::GetValues(AString a_keyName)
|
||||
{
|
||||
std::vector<std::pair<AString, AString>> ret;
|
||||
int keyID = FindKey(a_keyName);
|
||||
if (keyID == noID)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
for (size_t valueID = 0; valueID < keys[keyID].names.size(); ++valueID)
|
||||
{
|
||||
ret.emplace_back(keys[keyID].names[valueID], keys[keyID].values[valueID]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AStringVector ReadUpgradeIniPorts(
|
||||
cIniFile & a_IniFile,
|
||||
cSettingsRepositoryInterface & a_Settings,
|
||||
const AString & a_KeyName,
|
||||
const AString & a_PortsValueName,
|
||||
const AString & a_OldIPv4ValueName,
|
||||
@ -899,23 +931,34 @@ AStringVector ReadUpgradeIniPorts(
|
||||
)
|
||||
{
|
||||
// Read the regular value, but don't use the default (in order to detect missing value for upgrade):
|
||||
AStringVector Ports = StringSplitAndTrim(a_IniFile.GetValue(a_KeyName, a_PortsValueName), ";,");
|
||||
|
||||
AStringVector Ports;
|
||||
|
||||
for (auto pair : a_Settings.GetValues(a_KeyName))
|
||||
{
|
||||
if (pair.first != a_PortsValueName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AStringVector temp = StringSplitAndTrim(pair.second, ";,");
|
||||
Ports.insert(Ports.end(), temp.begin(), temp.end());
|
||||
}
|
||||
|
||||
if (Ports.empty())
|
||||
{
|
||||
// Historically there were two separate entries for IPv4 and IPv6, merge them and migrate:
|
||||
AString Ports4 = a_IniFile.GetValue(a_KeyName, a_OldIPv4ValueName, a_DefaultValue);
|
||||
AString Ports6 = a_IniFile.GetValue(a_KeyName, a_OldIPv6ValueName);
|
||||
AString Ports4 = a_Settings.GetValue(a_KeyName, a_OldIPv4ValueName, a_DefaultValue);
|
||||
AString Ports6 = a_Settings.GetValue(a_KeyName, a_OldIPv6ValueName);
|
||||
Ports = MergeStringVectors(StringSplitAndTrim(Ports4, ";,"), StringSplitAndTrim(Ports6, ";,"));
|
||||
a_IniFile.DeleteValue(a_KeyName, a_OldIPv4ValueName);
|
||||
a_IniFile.DeleteValue(a_KeyName, a_OldIPv6ValueName);
|
||||
a_Settings.DeleteValue(a_KeyName, a_OldIPv4ValueName);
|
||||
a_Settings.DeleteValue(a_KeyName, a_OldIPv6ValueName);
|
||||
|
||||
// If those weren't present or were empty, use the default:"
|
||||
if (Ports.empty())
|
||||
{
|
||||
Ports = StringSplitAndTrim(a_DefaultValue, ";,");
|
||||
}
|
||||
a_IniFile.SetValue(a_KeyName, a_PortsValueName, StringsConcat(Ports, ','));
|
||||
a_Settings.SetValue(a_KeyName, a_PortsValueName, StringsConcat(Ports, ','));
|
||||
}
|
||||
|
||||
return Ports;
|
||||
@ -923,4 +966,3 @@ AStringVector ReadUpgradeIniPorts(
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#include "SettingsRepositoryInterface.h"
|
||||
|
||||
#define MAX_KEYNAME 128
|
||||
#define MAX_VALUENAME 128
|
||||
@ -30,10 +30,12 @@
|
||||
|
||||
// tolua_begin
|
||||
|
||||
class cIniFile
|
||||
class cIniFile : public cSettingsRepositoryInterface
|
||||
{
|
||||
private:
|
||||
bool m_IsCaseInsensitive;
|
||||
|
||||
AString m_Filename;
|
||||
|
||||
struct key
|
||||
{
|
||||
@ -53,15 +55,19 @@ private:
|
||||
void RemoveBom(AString & a_line) const;
|
||||
|
||||
public:
|
||||
|
||||
enum errors
|
||||
{
|
||||
noID = -1,
|
||||
};
|
||||
|
||||
/// Creates a new instance with no data
|
||||
cIniFile(void);
|
||||
|
||||
// tolua_end
|
||||
virtual ~cIniFile() = default;
|
||||
|
||||
virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override;
|
||||
|
||||
virtual bool KeyExists(const AString a_keyName) const override;
|
||||
|
||||
// tolua_begin
|
||||
|
||||
// Sets whether or not keynames and valuenames should be case sensitive.
|
||||
// The default is case insensitive.
|
||||
void CaseSensitive (void) { m_IsCaseInsensitive = false; }
|
||||
@ -77,11 +83,13 @@ public:
|
||||
/// Writes data stored in class to the specified ini file
|
||||
bool WriteFile(const AString & a_FileName) const;
|
||||
|
||||
virtual bool Flush() override { return WriteFile(m_Filename); }
|
||||
|
||||
/// Deletes all stored ini data (but doesn't touch the file)
|
||||
void Clear(void);
|
||||
|
||||
/** Returns true iff the specified value exists. */
|
||||
bool HasValue(const AString & a_KeyName, const AString & a_ValueName);
|
||||
bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const;
|
||||
|
||||
/// Returns index of specified key, or noID if not found
|
||||
int FindKey(const AString & keyname) const;
|
||||
@ -222,7 +230,7 @@ Reads the list of ports from a_PortsValueName. If that value doesn't exist or is
|
||||
in a_OldIPv4ValueName and a_OldIPv6ValueName; in this case the old values are removed from the INI file.
|
||||
If there is none of the three values or they are all empty, the default is used and stored in the Ports value. */
|
||||
AStringVector ReadUpgradeIniPorts(
|
||||
cIniFile & a_IniFile,
|
||||
cSettingsRepositoryInterface & a_Settings,
|
||||
const AString & a_KeyName,
|
||||
const AString & a_PortsValueName,
|
||||
const AString & a_OldIPv4ValueName,
|
||||
|
312
src/MemorySettingsRepository.cpp
Normal file
312
src/MemorySettingsRepository.cpp
Normal file
@ -0,0 +1,312 @@
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
#include "MemorySettingsRepository.h"
|
||||
|
||||
|
||||
|
||||
|
||||
bool cMemorySettingsRepository::KeyExists(const AString keyname) const
|
||||
{
|
||||
return m_Map.count(keyname) != 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cMemorySettingsRepository::HasValue(const AString & a_KeyName, const AString & a_ValueName) const
|
||||
{
|
||||
auto outerIter = m_Map.find(a_KeyName);
|
||||
if (outerIter == m_Map.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto iter = outerIter->second.find(a_ValueName);
|
||||
if (iter == outerIter->second.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int cMemorySettingsRepository::AddKeyName(const AString & a_keyname)
|
||||
{
|
||||
m_Map.emplace(a_keyname, std::unordered_multimap<AString, sValue>{});
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cMemorySettingsRepository::AddKeyComment(const AString & keyname, const AString & comment)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cMemorySettingsRepository::GetKeyComment(const AString & keyname, const int commentID) const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cMemorySettingsRepository::DeleteKeyComment(const AString & keyname, const int commentID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value)
|
||||
{
|
||||
if (m_Writable)
|
||||
{
|
||||
m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, Int64 a_Value)
|
||||
{
|
||||
if (m_Writable)
|
||||
{
|
||||
m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, bool a_Value)
|
||||
{
|
||||
if (m_Writable)
|
||||
{
|
||||
m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::vector<std::pair<AString, AString>> cMemorySettingsRepository::GetValues(AString a_keyName)
|
||||
{
|
||||
std::vector<std::pair<AString, AString>> ret;
|
||||
for (auto pair : m_Map[a_keyName])
|
||||
{
|
||||
ret.emplace_back(pair.first, pair.second.getStringValue());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cMemorySettingsRepository::GetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) const
|
||||
{
|
||||
auto outerIter = m_Map.find(a_KeyName);
|
||||
if (outerIter == m_Map.end())
|
||||
{
|
||||
return defValue;
|
||||
}
|
||||
auto iter = outerIter->second.find(a_ValueName);
|
||||
if (iter == outerIter->second.end())
|
||||
{
|
||||
return defValue;
|
||||
}
|
||||
return iter->second.getStringValue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cMemorySettingsRepository::GetValueSet (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue)
|
||||
{
|
||||
auto outerIter = m_Map.find(a_KeyName);
|
||||
if (outerIter == m_Map.end())
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, defValue);
|
||||
return defValue;
|
||||
}
|
||||
auto iter = outerIter->second.find(a_ValueName);
|
||||
if (iter == outerIter->second.end())
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, defValue);
|
||||
return defValue;
|
||||
}
|
||||
return iter->second.getStringValue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cMemorySettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const int defValue)
|
||||
{
|
||||
auto outerIter = m_Map.find(a_KeyName);
|
||||
if (outerIter == m_Map.end())
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, static_cast<Int64>(defValue));
|
||||
return defValue;
|
||||
}
|
||||
auto iter = outerIter->second.find(a_ValueName);
|
||||
if (iter == outerIter->second.end())
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, static_cast<Int64>(defValue));
|
||||
return defValue;
|
||||
}
|
||||
return static_cast<int>(iter->second.getIntValue());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Int64 cMemorySettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const Int64 defValue)
|
||||
{
|
||||
auto outerIter = m_Map.find(a_KeyName);
|
||||
if (outerIter == m_Map.end())
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, defValue);
|
||||
return defValue;
|
||||
}
|
||||
auto iter = outerIter->second.find(a_ValueName);
|
||||
if (iter == outerIter->second.end())
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, defValue);
|
||||
return defValue;
|
||||
}
|
||||
return iter->second.getIntValue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool cMemorySettingsRepository::GetValueSetB(const AString & a_KeyName, const AString & a_ValueName, const bool defValue)
|
||||
{
|
||||
auto outerIter = m_Map.find(a_KeyName);
|
||||
if (outerIter == m_Map.end())
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, defValue);
|
||||
return defValue;
|
||||
}
|
||||
auto iter = outerIter->second.find(a_ValueName);
|
||||
if (iter == outerIter->second.end())
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, defValue);
|
||||
return defValue;
|
||||
}
|
||||
return iter->second.getBoolValue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cMemorySettingsRepository::SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists)
|
||||
{
|
||||
if (!m_Writable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto outerIter = m_Map.find(a_KeyName);
|
||||
if (outerIter == m_Map.end())
|
||||
{
|
||||
if (a_CreateIfNotExists)
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, a_Value);
|
||||
}
|
||||
return a_CreateIfNotExists;
|
||||
}
|
||||
auto iter = outerIter->second.find(a_ValueName);
|
||||
if (iter == outerIter->second.end())
|
||||
{
|
||||
if (a_CreateIfNotExists)
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, a_Value);
|
||||
}
|
||||
return a_CreateIfNotExists;
|
||||
}
|
||||
iter->second = sValue(a_Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool cMemorySettingsRepository::SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists)
|
||||
{
|
||||
if (!m_Writable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto outerIter = m_Map.find(a_KeyName);
|
||||
if (outerIter == m_Map.end())
|
||||
{
|
||||
if (a_CreateIfNotExists)
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, static_cast<Int64>(a_Value));
|
||||
}
|
||||
return a_CreateIfNotExists;
|
||||
}
|
||||
auto iter = outerIter->second.find(a_ValueName);
|
||||
if (iter == outerIter->second.end())
|
||||
{
|
||||
if (a_CreateIfNotExists)
|
||||
{
|
||||
AddValue(a_KeyName, a_ValueName, static_cast<Int64>(a_Value));
|
||||
}
|
||||
return a_CreateIfNotExists;
|
||||
}
|
||||
iter->second = sValue(static_cast<Int64>(a_Value));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cMemorySettingsRepository::DeleteValue(const AString & a_KeyName, const AString & a_ValueName)
|
||||
{
|
||||
if (!m_Writable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto outerIter = m_Map.find(a_KeyName);
|
||||
if (outerIter == m_Map.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto iter = outerIter->second.find(a_ValueName);
|
||||
if (iter == outerIter->second.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outerIter->second.erase(iter);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cMemorySettingsRepository::Flush()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
80
src/MemorySettingsRepository.h
Normal file
80
src/MemorySettingsRepository.h
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SettingsRepositoryInterface.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
class cMemorySettingsRepository : public cSettingsRepositoryInterface
|
||||
{
|
||||
public:
|
||||
|
||||
virtual bool KeyExists(const AString keyname) const override;
|
||||
|
||||
virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override;
|
||||
|
||||
virtual int AddKeyName(const AString & keyname) override;
|
||||
|
||||
virtual bool AddKeyComment(const AString & keyname, const AString & comment) override;
|
||||
|
||||
virtual AString GetKeyComment(const AString & keyname, const int commentID) const override;
|
||||
|
||||
virtual bool DeleteKeyComment(const AString & keyname, const int commentID) override;
|
||||
|
||||
virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override;
|
||||
void AddValue (const AString & a_KeyName, const AString & a_ValueName, const Int64 a_Value);
|
||||
void AddValue (const AString & a_KeyName, const AString & a_ValueName, const bool a_Value);
|
||||
|
||||
virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override;
|
||||
|
||||
virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override;
|
||||
|
||||
|
||||
virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override;
|
||||
virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override;
|
||||
virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override;
|
||||
virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override;
|
||||
|
||||
virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override;
|
||||
virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override;
|
||||
|
||||
virtual bool DeleteValue(const AString & keyname, const AString & valuename) override;
|
||||
|
||||
virtual bool Flush() override;
|
||||
|
||||
void SetReadOnly()
|
||||
{
|
||||
m_Writable = false;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool m_Writable = true;
|
||||
|
||||
struct sValue
|
||||
{
|
||||
sValue(AString value) : m_Type(eType::String), m_stringValue (value) {}
|
||||
sValue(Int64 value) : m_Type(eType::Int64), m_intValue(value) {}
|
||||
sValue(bool value) : m_Type(eType::Bool), m_boolValue(value) {}
|
||||
|
||||
AString getStringValue() const { ASSERT(m_Type == eType::String); return m_stringValue; }
|
||||
Int64 getIntValue() const { ASSERT(m_Type == eType::Int64); return m_intValue; }
|
||||
bool getBoolValue() const { ASSERT(m_Type == eType::Bool); return m_boolValue; }
|
||||
private:
|
||||
enum class eType
|
||||
{
|
||||
String,
|
||||
Int64,
|
||||
Bool
|
||||
} m_Type;
|
||||
AString m_stringValue;
|
||||
union
|
||||
{
|
||||
Int64 m_intValue;
|
||||
bool m_boolValue;
|
||||
};
|
||||
};
|
||||
|
||||
std::unordered_map<AString, std::unordered_multimap<AString, sValue>> m_Map{};
|
||||
};
|
||||
|
@ -519,7 +519,7 @@ void cMonster::SetPitchAndYawFromDestination()
|
||||
double HeadRotation, HeadPitch;
|
||||
Distance.Normalize();
|
||||
VectorToEuler(Distance.x, Distance.y, Distance.z, HeadRotation, HeadPitch);
|
||||
if (abs(BodyRotation - HeadRotation) < 120)
|
||||
if (std::abs(BodyRotation - HeadRotation) < 120)
|
||||
{
|
||||
SetHeadYaw(HeadRotation);
|
||||
SetPitch(-HeadPitch);
|
||||
|
273
src/OverridesSettingsRepository.cpp
Normal file
273
src/OverridesSettingsRepository.cpp
Normal file
@ -0,0 +1,273 @@
|
||||
|
||||
#include "Globals.h"
|
||||
#include "OverridesSettingsRepository.h"
|
||||
|
||||
cOverridesSettingsRepository::cOverridesSettingsRepository(std::unique_ptr<cSettingsRepositoryInterface> a_Main, std::unique_ptr<cSettingsRepositoryInterface> a_Overrides) :
|
||||
m_Main(std::move(a_Main)),
|
||||
m_Overrides(std::move(a_Overrides))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cOverridesSettingsRepository::KeyExists(const AString a_keyName) const
|
||||
{
|
||||
return m_Overrides->KeyExists(a_keyName) || m_Main->KeyExists(a_keyName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool cOverridesSettingsRepository::HasValue(const AString & a_KeyName, const AString & a_ValueName) const
|
||||
{
|
||||
return m_Overrides->HasValue(a_KeyName, a_ValueName) || m_Main->HasValue(a_KeyName, a_ValueName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cOverridesSettingsRepository::AddKeyName(const AString & a_keyname)
|
||||
{
|
||||
|
||||
if (m_Overrides->KeyExists(a_keyname))
|
||||
{
|
||||
m_Overrides->AddKeyName(a_keyname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_Main->AddKeyName(a_keyname);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cOverridesSettingsRepository::AddKeyComment(const AString & a_keyname, const AString & a_comment)
|
||||
{
|
||||
if (m_Overrides->KeyExists(a_keyname))
|
||||
{
|
||||
return m_Overrides->AddKeyComment(a_keyname, a_comment);
|
||||
}
|
||||
|
||||
return m_Main->AddKeyComment(a_keyname, a_comment);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cOverridesSettingsRepository::GetKeyComment(const AString & a_keyname, const int a_commentID) const
|
||||
{
|
||||
|
||||
if (m_Overrides->KeyExists(a_keyname))
|
||||
{
|
||||
return m_Overrides->GetKeyComment(a_keyname, a_commentID);
|
||||
}
|
||||
|
||||
return m_Main->GetKeyComment(a_keyname, a_commentID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cOverridesSettingsRepository::DeleteKeyComment(const AString & a_keyname, const int a_commentID)
|
||||
{
|
||||
if (m_Overrides->KeyExists(a_keyname))
|
||||
{
|
||||
return m_Overrides->DeleteKeyComment(a_keyname, a_commentID);
|
||||
}
|
||||
|
||||
return m_Main->DeleteKeyComment(a_keyname, a_commentID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cOverridesSettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value)
|
||||
{
|
||||
if (m_Overrides->HasValue(a_KeyName, a_ValueName))
|
||||
{
|
||||
m_Overrides->AddValue(a_KeyName, a_ValueName, a_Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Main->AddValue(a_KeyName, a_ValueName, a_Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::vector<std::pair<AString, AString>> cOverridesSettingsRepository::GetValues(AString a_keyName)
|
||||
{
|
||||
auto overrides = m_Overrides->GetValues(a_keyName);
|
||||
auto main = m_Main->GetValues(a_keyName);
|
||||
std::sort(overrides.begin(), overrides.end(), [](std::pair<AString, AString> a, std::pair<AString, AString> b) -> bool { return a < b ;});
|
||||
std::sort(main.begin(), main.end(), [](std::pair<AString, AString> a, std::pair<AString, AString> b) -> bool { return a < b ;});
|
||||
|
||||
std::vector<std::pair<AString, AString>> ret;
|
||||
|
||||
|
||||
size_t overridesIndex = 0;
|
||||
for (auto pair : main)
|
||||
{
|
||||
if (overridesIndex >= overrides.size())
|
||||
{
|
||||
ret.push_back(pair);
|
||||
continue;
|
||||
}
|
||||
if (pair.first == overrides[overridesIndex].first)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
while (pair.first > overrides[overridesIndex].first)
|
||||
{
|
||||
ret.push_back(overrides[overridesIndex]);
|
||||
overridesIndex++;
|
||||
}
|
||||
ret.push_back(pair);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cOverridesSettingsRepository::GetValue(const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) const
|
||||
{
|
||||
if (m_Overrides->HasValue(a_KeyName, a_ValueName))
|
||||
{
|
||||
return m_Overrides->GetValue(a_KeyName, a_ValueName, defValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_Main->GetValue(a_KeyName, a_ValueName, defValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cOverridesSettingsRepository::GetValueSet (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue)
|
||||
{
|
||||
if (m_Overrides->HasValue(a_KeyName, a_ValueName))
|
||||
{
|
||||
return m_Overrides->GetValueSet(a_KeyName, a_ValueName, defValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_Main->GetValueSet(a_KeyName, a_ValueName, defValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cOverridesSettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const int defValue)
|
||||
{
|
||||
if (m_Overrides->HasValue(a_KeyName, a_ValueName))
|
||||
{
|
||||
return m_Overrides->GetValueSetI(a_KeyName, a_ValueName, defValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_Main->GetValueSetI(a_KeyName, a_ValueName, defValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Int64 cOverridesSettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const Int64 defValue)
|
||||
{
|
||||
if (m_Overrides->HasValue(a_KeyName, a_ValueName))
|
||||
{
|
||||
return m_Overrides->GetValueSetI(a_KeyName, a_ValueName, defValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_Main->GetValueSetI(a_KeyName, a_ValueName, defValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cOverridesSettingsRepository::GetValueSetB(const AString & a_KeyName, const AString & a_ValueName, const bool defValue)
|
||||
{
|
||||
if (m_Overrides->HasValue(a_KeyName, a_ValueName))
|
||||
{
|
||||
return m_Overrides->GetValueSetB(a_KeyName, a_ValueName, defValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_Main->GetValueSetB(a_KeyName, a_ValueName, defValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cOverridesSettingsRepository::SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists)
|
||||
{
|
||||
if (m_Overrides->HasValue(a_KeyName, a_ValueName))
|
||||
{
|
||||
return m_Overrides->SetValue(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists);
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_Main->SetValue(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cOverridesSettingsRepository::SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists)
|
||||
{
|
||||
if (m_Overrides->HasValue(a_KeyName, a_ValueName))
|
||||
{
|
||||
return m_Overrides->SetValueI(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists);
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_Main->SetValueI(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cOverridesSettingsRepository::DeleteValue(const AString & a_KeyName, const AString & a_ValueName)
|
||||
{
|
||||
if (m_Overrides->HasValue(a_KeyName, a_ValueName))
|
||||
{
|
||||
return m_Overrides->DeleteValue(a_KeyName, a_ValueName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_Overrides->DeleteValue(a_KeyName, a_ValueName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool cOverridesSettingsRepository::Flush()
|
||||
{
|
||||
return m_Overrides->Flush() && m_Main->Flush();
|
||||
}
|
||||
|
52
src/OverridesSettingsRepository.h
Normal file
52
src/OverridesSettingsRepository.h
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SettingsRepositoryInterface.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
class cOverridesSettingsRepository : public cSettingsRepositoryInterface
|
||||
{
|
||||
|
||||
public:
|
||||
cOverridesSettingsRepository(std::unique_ptr<cSettingsRepositoryInterface> a_Main, std::unique_ptr<cSettingsRepositoryInterface> a_Overrides);
|
||||
|
||||
virtual ~cOverridesSettingsRepository() = default;
|
||||
|
||||
virtual bool KeyExists(const AString keyname) const override;
|
||||
|
||||
virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override;
|
||||
|
||||
virtual int AddKeyName(const AString & keyname) override;
|
||||
|
||||
virtual bool AddKeyComment(const AString & keyname, const AString & comment) override;
|
||||
|
||||
virtual AString GetKeyComment(const AString & keyname, const int commentID) const override;
|
||||
|
||||
virtual bool DeleteKeyComment(const AString & keyname, const int commentID) override;
|
||||
|
||||
virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override;
|
||||
|
||||
virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override;
|
||||
|
||||
virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override;
|
||||
|
||||
virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override;
|
||||
virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override;
|
||||
virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override;
|
||||
virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override;
|
||||
|
||||
virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override;
|
||||
virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override;
|
||||
|
||||
virtual bool DeleteValue(const AString & keyname, const AString & valuename) override;
|
||||
|
||||
virtual bool Flush() override;
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<cSettingsRepositoryInterface> m_Main;
|
||||
std::unique_ptr<cSettingsRepositoryInterface> m_Overrides;
|
||||
|
||||
};
|
||||
|
@ -152,7 +152,7 @@ void cSslContext::SetCACerts(const cX509CertPtr & a_CACert, const AString & a_Ex
|
||||
m_CACerts = a_CACert;
|
||||
|
||||
// Set the trusted CA root cert store:
|
||||
ssl_set_authmode(&m_Ssl, SSL_VERIFY_REQUIRED);
|
||||
ssl_set_authmode(&m_Ssl, SSL_VERIFY_OPTIONAL);
|
||||
ssl_set_ca_chain(&m_Ssl, m_CACerts->GetInternal(), nullptr, m_ExpectedPeerName.empty() ? nullptr : m_ExpectedPeerName.c_str());
|
||||
}
|
||||
|
||||
|
@ -40,11 +40,11 @@ cAuthenticator::~cAuthenticator()
|
||||
|
||||
|
||||
|
||||
void cAuthenticator::ReadINI(cIniFile & IniFile)
|
||||
void cAuthenticator::ReadSettings(cSettingsRepositoryInterface & a_Settings)
|
||||
{
|
||||
m_Server = IniFile.GetValueSet ("Authentication", "Server", DEFAULT_AUTH_SERVER);
|
||||
m_Address = IniFile.GetValueSet ("Authentication", "Address", DEFAULT_AUTH_ADDRESS);
|
||||
m_ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true);
|
||||
m_Server = a_Settings.GetValueSet ("Authentication", "Server", DEFAULT_AUTH_SERVER);
|
||||
m_Address = a_Settings.GetValueSet ("Authentication", "Address", DEFAULT_AUTH_ADDRESS);
|
||||
m_ShouldAuthenticate = a_Settings.GetValueSetB("Authentication", "Authenticate", true);
|
||||
}
|
||||
|
||||
|
||||
@ -69,9 +69,9 @@ void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, co
|
||||
|
||||
|
||||
|
||||
void cAuthenticator::Start(cIniFile & IniFile)
|
||||
void cAuthenticator::Start(cSettingsRepositoryInterface & a_Settings)
|
||||
{
|
||||
ReadINI(IniFile);
|
||||
ReadSettings(a_Settings);
|
||||
m_ShouldTerminate = false;
|
||||
super::Start();
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include "../OSSupport/IsThread.h"
|
||||
|
||||
|
||||
class cSettingsRepositoryInterface;
|
||||
|
||||
|
||||
|
||||
@ -40,13 +40,13 @@ public:
|
||||
~cAuthenticator();
|
||||
|
||||
/** (Re-)read server and address from INI: */
|
||||
void ReadINI(cIniFile & IniFile);
|
||||
void ReadSettings(cSettingsRepositoryInterface & a_Settings);
|
||||
|
||||
/** Queues a request for authenticating a user. If the auth fails, the user will be kicked */
|
||||
void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash);
|
||||
|
||||
/** Starts the authenticator thread. The thread may be started and stopped repeatedly */
|
||||
void Start(cIniFile & IniFile);
|
||||
void Start(cSettingsRepositoryInterface & a_Settings);
|
||||
|
||||
/** Stops the authenticator thread. The thread may be started and stopped repeatedly */
|
||||
void Stop(void);
|
||||
|
@ -226,12 +226,12 @@ cMojangAPI::~cMojangAPI()
|
||||
|
||||
|
||||
|
||||
void cMojangAPI::Start(cIniFile & a_SettingsIni, bool a_ShouldAuth)
|
||||
void cMojangAPI::Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth)
|
||||
{
|
||||
m_NameToUUIDServer = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER);
|
||||
m_NameToUUIDAddress = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS);
|
||||
m_UUIDToProfileServer = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER);
|
||||
m_UUIDToProfileAddress = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS);
|
||||
m_NameToUUIDServer = a_Settings.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER);
|
||||
m_NameToUUIDAddress = a_Settings.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS);
|
||||
m_UUIDToProfileServer = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER);
|
||||
m_UUIDToProfileAddress = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS);
|
||||
LoadCachesFromDisk();
|
||||
if (a_ShouldAuth)
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ namespace Json
|
||||
|
||||
|
||||
|
||||
|
||||
class cSettingsRepositoryInterface;
|
||||
|
||||
// tolua_begin
|
||||
class cMojangAPI
|
||||
@ -38,7 +38,7 @@ public:
|
||||
|
||||
/** Initializes the API; reads the settings from the specified ini file.
|
||||
Loads cached results from disk. */
|
||||
void Start(cIniFile & a_SettingsIni, bool a_ShouldAuth);
|
||||
void Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth);
|
||||
|
||||
/** Connects to the specified server using SSL, sends the given request and receives the response.
|
||||
Checks Mojang certificates using the hard-coded Starfield root CA certificate.
|
||||
|
@ -134,15 +134,15 @@ cRCONServer::~cRCONServer()
|
||||
|
||||
|
||||
|
||||
void cRCONServer::Initialize(cIniFile & a_IniFile)
|
||||
void cRCONServer::Initialize(cSettingsRepositoryInterface & a_Settings)
|
||||
{
|
||||
if (!a_IniFile.GetValueSetB("RCON", "Enabled", false))
|
||||
if (!a_Settings.GetValueSetB("RCON", "Enabled", false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the password, don't allow an empty one:
|
||||
m_Password = a_IniFile.GetValueSet("RCON", "Password", "");
|
||||
m_Password = a_Settings.GetValueSet("RCON", "Password", "");
|
||||
if (m_Password.empty())
|
||||
{
|
||||
LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled.");
|
||||
@ -150,7 +150,7 @@ void cRCONServer::Initialize(cIniFile & a_IniFile)
|
||||
}
|
||||
|
||||
// Read the listening ports for RCON from config:
|
||||
AStringVector Ports = ReadUpgradeIniPorts(a_IniFile, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575");
|
||||
AStringVector Ports = ReadUpgradeIniPorts(a_Settings, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575");
|
||||
|
||||
// Start listening on each specified port:
|
||||
for (auto port: Ports)
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
// fwd:
|
||||
class cServer;
|
||||
class cIniFile;
|
||||
class cSettingsRepositoryInterface;
|
||||
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ public:
|
||||
cRCONServer(cServer & a_Server);
|
||||
virtual ~cRCONServer();
|
||||
|
||||
void Initialize(cIniFile & a_IniFile);
|
||||
void Initialize(cSettingsRepositoryInterface & a_Settings);
|
||||
|
||||
protected:
|
||||
friend class cRCONCommandOutput;
|
||||
|
57
src/Root.cpp
57
src/Root.cpp
@ -19,6 +19,8 @@
|
||||
#include "LoggerListeners.h"
|
||||
#include "BuildInfo.h"
|
||||
#include "IniFile.h"
|
||||
#include "SettingsRepositoryInterface.h"
|
||||
#include "OverridesSettingsRepository.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <conio.h>
|
||||
@ -96,7 +98,7 @@ void cRoot::InputThread(cRoot & a_Params)
|
||||
|
||||
|
||||
|
||||
void cRoot::Start(void)
|
||||
void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
HWND hwnd = GetConsoleWindow();
|
||||
@ -130,22 +132,24 @@ void cRoot::Start(void)
|
||||
m_Server = new cServer();
|
||||
|
||||
LOG("Reading server config...");
|
||||
cIniFile IniFile;
|
||||
if (!IniFile.ReadFile("settings.ini"))
|
||||
|
||||
auto IniFile = cpp14::make_unique<cIniFile>();
|
||||
if (!IniFile->ReadFile("settings.ini"))
|
||||
{
|
||||
LOGWARN("Regenerating settings.ini, all settings will be reset");
|
||||
IniFile.AddHeaderComment(" This is the main server configuration");
|
||||
IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
|
||||
IniFile.AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help");
|
||||
IniFile->AddHeaderComment(" This is the main server configuration");
|
||||
IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
|
||||
IniFile->AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help");
|
||||
}
|
||||
auto settingsRepo = cpp14::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(overridesRepo));
|
||||
|
||||
LOG("Starting server...");
|
||||
m_MojangAPI = new cMojangAPI;
|
||||
bool ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true);
|
||||
m_MojangAPI->Start(IniFile, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init
|
||||
if (!m_Server->InitServer(IniFile, ShouldAuthenticate))
|
||||
bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true);
|
||||
m_MojangAPI->Start(*settingsRepo, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init
|
||||
if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate))
|
||||
{
|
||||
IniFile.WriteFile("settings.ini");
|
||||
settingsRepo->Flush();
|
||||
LOGERROR("Failure starting server, aborting...");
|
||||
return;
|
||||
}
|
||||
@ -160,29 +164,29 @@ void cRoot::Start(void)
|
||||
m_FurnaceRecipe = new cFurnaceRecipe();
|
||||
|
||||
LOGD("Loading worlds...");
|
||||
LoadWorlds(IniFile);
|
||||
LoadWorlds(*settingsRepo);
|
||||
|
||||
LOGD("Loading plugin manager...");
|
||||
m_PluginManager = new cPluginManager();
|
||||
m_PluginManager->ReloadPluginsNow(IniFile);
|
||||
m_PluginManager->ReloadPluginsNow(*settingsRepo);
|
||||
|
||||
LOGD("Loading MonsterConfig...");
|
||||
m_MonsterConfig = new cMonsterConfig;
|
||||
|
||||
// This sets stuff in motion
|
||||
LOGD("Starting Authenticator...");
|
||||
m_Authenticator.Start(IniFile);
|
||||
m_Authenticator.Start(*settingsRepo);
|
||||
|
||||
LOGD("Starting worlds...");
|
||||
StartWorlds();
|
||||
|
||||
if (IniFile.GetValueSetB("DeadlockDetect", "Enabled", true))
|
||||
if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true))
|
||||
{
|
||||
LOGD("Starting deadlock detector...");
|
||||
dd.Start(IniFile.GetValueSetI("DeadlockDetect", "IntervalSec", 20));
|
||||
dd.Start(settingsRepo->GetValueSetI("DeadlockDetect", "IntervalSec", 20));
|
||||
}
|
||||
|
||||
IniFile.WriteFile("settings.ini");
|
||||
settingsRepo->Flush();
|
||||
|
||||
LOGD("Finalising startup...");
|
||||
if (m_Server->Start())
|
||||
@ -282,30 +286,29 @@ void cRoot::LoadGlobalSettings()
|
||||
|
||||
|
||||
|
||||
void cRoot::LoadWorlds(cIniFile & IniFile)
|
||||
void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings)
|
||||
{
|
||||
// First get the default world
|
||||
AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world");
|
||||
AString DefaultWorldName = a_Settings.GetValueSet("Worlds", "DefaultWorld", "world");
|
||||
m_pDefaultWorld = new cWorld(DefaultWorldName.c_str());
|
||||
m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld;
|
||||
|
||||
// Then load the other worlds
|
||||
int KeyNum = IniFile.FindKey("Worlds");
|
||||
int NumWorlds = IniFile.GetNumValues(KeyNum);
|
||||
if (NumWorlds <= 0)
|
||||
auto Worlds = a_Settings.GetValues("Worlds");
|
||||
if (Worlds.size() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool FoundAdditionalWorlds = false;
|
||||
for (int i = 0; i < NumWorlds; i++)
|
||||
for (auto WorldNameValue : Worlds)
|
||||
{
|
||||
AString ValueName = IniFile.GetValueName(KeyNum, i);
|
||||
AString ValueName = WorldNameValue.first;
|
||||
if (ValueName.compare("World") != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AString WorldName = IniFile.GetValue(KeyNum, i);
|
||||
AString WorldName = WorldNameValue.second;
|
||||
if (WorldName.empty())
|
||||
{
|
||||
continue;
|
||||
@ -317,10 +320,10 @@ void cRoot::LoadWorlds(cIniFile & IniFile)
|
||||
|
||||
if (!FoundAdditionalWorlds)
|
||||
{
|
||||
if (IniFile.GetKeyComment("Worlds", 0) != " World=secondworld")
|
||||
if (a_Settings.GetKeyComment("Worlds", 0) != " World=secondworld")
|
||||
{
|
||||
IniFile.DeleteKeyComment("Worlds", 0);
|
||||
IniFile.AddKeyComment("Worlds", " World=secondworld");
|
||||
a_Settings.DeleteKeyComment("Worlds", 0);
|
||||
a_Settings.AddKeyComment("Worlds", " World=secondworld");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ class cWorld;
|
||||
class cPlayer;
|
||||
class cCommandOutputCallback;
|
||||
class cCompositeChat;
|
||||
class cSettingsRepositoryInterface;
|
||||
|
||||
typedef cItemCallback<cPlayer> cPlayerListCallback;
|
||||
typedef cItemCallback<cWorld> cWorldListCallback;
|
||||
@ -53,7 +54,7 @@ public:
|
||||
cRoot(void);
|
||||
~cRoot();
|
||||
|
||||
void Start(void);
|
||||
void Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo);
|
||||
|
||||
// tolua_begin
|
||||
cServer * GetServer(void) { return m_Server; }
|
||||
@ -204,7 +205,7 @@ private:
|
||||
void LoadGlobalSettings();
|
||||
|
||||
/// Loads the worlds from settings.ini, creates the worldmap
|
||||
void LoadWorlds(cIniFile & IniFile);
|
||||
void LoadWorlds(cSettingsRepositoryInterface & a_Settings);
|
||||
|
||||
/// Starts each world's life
|
||||
void StartWorlds(void);
|
||||
|
@ -185,12 +185,12 @@ void cServer::PlayerDestroying(const cPlayer * a_Player)
|
||||
|
||||
|
||||
|
||||
bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
|
||||
bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth)
|
||||
{
|
||||
m_Description = a_SettingsIni.GetValueSet("Server", "Description", "MCServer - in C++!");
|
||||
m_MaxPlayers = a_SettingsIni.GetValueSetI("Server", "MaxPlayers", 100);
|
||||
m_bIsHardcore = a_SettingsIni.GetValueSetB("Server", "HardcoreEnabled", false);
|
||||
m_bAllowMultiLogin = a_SettingsIni.GetValueSetB("Server", "AllowMultiLogin", false);
|
||||
m_Description = a_Settings.GetValueSet("Server", "Description", "MCServer - in C++!");
|
||||
m_MaxPlayers = a_Settings.GetValueSetI("Server", "MaxPlayers", 100);
|
||||
m_bIsHardcore = a_Settings.GetValueSetB("Server", "HardcoreEnabled", false);
|
||||
m_bAllowMultiLogin = a_Settings.GetValueSetB("Server", "AllowMultiLogin", false);
|
||||
m_PlayerCount = 0;
|
||||
m_PlayerCountDiff = 0;
|
||||
|
||||
@ -205,9 +205,9 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
|
||||
LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS);
|
||||
LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS);
|
||||
|
||||
m_Ports = ReadUpgradeIniPorts(a_SettingsIni, "Server", "Ports", "Port", "PortsIPv6", "25565");
|
||||
m_Ports = ReadUpgradeIniPorts(a_Settings, "Server", "Ports", "Port", "PortsIPv6", "25565");
|
||||
|
||||
m_RCONServer.Initialize(a_SettingsIni);
|
||||
m_RCONServer.Initialize(a_Settings);
|
||||
|
||||
m_bIsConnected = true;
|
||||
|
||||
@ -226,16 +226,16 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
|
||||
}
|
||||
|
||||
// Check if both BungeeCord and online mode are on, if so, warn the admin:
|
||||
m_ShouldAllowBungeeCord = a_SettingsIni.GetValueSetB("Authentication", "AllowBungeeCord", false);
|
||||
m_ShouldAllowBungeeCord = a_Settings.GetValueSetB("Authentication", "AllowBungeeCord", false);
|
||||
if (m_ShouldAllowBungeeCord && m_ShouldAuthenticate)
|
||||
{
|
||||
LOGWARNING("WARNING: BungeeCord is allowed and server set to online mode. This is unsafe and will not work properly. Disable either authentication or BungeeCord in settings.ini.");
|
||||
}
|
||||
|
||||
m_ShouldLoadOfflinePlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false);
|
||||
m_ShouldLoadNamedPlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadNamedPlayerData", true);
|
||||
m_ShouldLoadOfflinePlayerData = a_Settings.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false);
|
||||
m_ShouldLoadNamedPlayerData = a_Settings.GetValueSetB("PlayerData", "LoadNamedPlayerData", true);
|
||||
|
||||
m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
|
||||
m_ClientViewDistance = a_Settings.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
|
||||
if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE)
|
||||
{
|
||||
m_ClientViewDistance = cClientHandle::MIN_VIEW_DISTANCE;
|
||||
|
@ -38,8 +38,8 @@ class cClientHandle;
|
||||
typedef SharedPtr<cClientHandle> cClientHandlePtr;
|
||||
typedef std::list<cClientHandlePtr> cClientHandlePtrs;
|
||||
typedef std::list<cClientHandle *> cClientHandles;
|
||||
class cIniFile;
|
||||
class cCommandOutputCallback;
|
||||
class cSettingsRepositoryInterface;
|
||||
|
||||
|
||||
namespace Json
|
||||
@ -58,7 +58,7 @@ public:
|
||||
// tolua_end
|
||||
|
||||
virtual ~cServer() {}
|
||||
bool InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth);
|
||||
bool InitServer(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth);
|
||||
|
||||
// tolua_begin
|
||||
|
||||
|
61
src/SettingsRepositoryInterface.h
Normal file
61
src/SettingsRepositoryInterface.h
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
class cSettingsRepositoryInterface
|
||||
{
|
||||
public:
|
||||
|
||||
enum errors
|
||||
{
|
||||
noID = -1,
|
||||
};
|
||||
|
||||
virtual ~cSettingsRepositoryInterface() = default;
|
||||
|
||||
/** Returns true iff the specified key exists */
|
||||
virtual bool KeyExists(const AString keyname) const = 0;
|
||||
|
||||
/** Returns true iff the specified value exists. */
|
||||
virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const = 0;
|
||||
|
||||
/** Add a key name. Return value is not required to mean anything **/
|
||||
virtual int AddKeyName(const AString & keyname) = 0;
|
||||
|
||||
/** Add a key comment, will always fail if the repository does not support comments **/
|
||||
virtual bool AddKeyComment(const AString & keyname, const AString & comment) = 0;
|
||||
|
||||
/** Return a key comment, returns "" for repositories that do not return comments **/
|
||||
virtual AString GetKeyComment(const AString & keyname, const int commentID) const = 0;
|
||||
|
||||
/** Delete a key comment, will always fail if the repository does not support comments **/
|
||||
virtual bool DeleteKeyComment(const AString & keyname, const int commentID) = 0;
|
||||
|
||||
/** Adds a new value to the specified key.
|
||||
If a value of the same name already exists, creates another one **/
|
||||
virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) = 0;
|
||||
|
||||
/** returns a vector containing a name, value pair for each value under the key **/
|
||||
virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) = 0;
|
||||
|
||||
/** Get the value at the specified key and value, returns defValue on failure **/
|
||||
virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const = 0;
|
||||
|
||||
/** Gets the value; if not found, write the default to the repository **/
|
||||
virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") = 0;
|
||||
virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) = 0;
|
||||
virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) = 0;
|
||||
virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) = 0;
|
||||
|
||||
/** Overwrites the value of the key, value pair
|
||||
Specify the optional parameter as false if you do not want the value created if it doesn't exist.
|
||||
Returns true if value set, false otherwise. **/
|
||||
virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) = 0;
|
||||
virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) = 0;
|
||||
|
||||
/** Deletes the specified key, value pair **/
|
||||
virtual bool DeleteValue(const AString & keyname, const AString & valuename) = 0;
|
||||
|
||||
|
||||
/** Writes the changes to the backing store, if the repository has one **/
|
||||
virtual bool Flush() = 0;
|
||||
};
|
@ -621,18 +621,18 @@ void cWorld::Start(void)
|
||||
InitialiseAndLoadMobSpawningValues(IniFile);
|
||||
SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay()));
|
||||
|
||||
m_ChunkMap = make_unique<cChunkMap>(this);
|
||||
m_ChunkMap = cpp14::make_unique<cChunkMap>(this);
|
||||
|
||||
// preallocate some memory for ticking blocks so we don't need to allocate that often
|
||||
m_BlockTickQueue.reserve(1000);
|
||||
m_BlockTickQueueCopy.reserve(1000);
|
||||
|
||||
// Simulators:
|
||||
m_SimulatorManager = make_unique<cSimulatorManager>(*this);
|
||||
m_SimulatorManager = cpp14::make_unique<cSimulatorManager>(*this);
|
||||
m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
|
||||
m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA);
|
||||
m_SandSimulator = make_unique<cSandSimulator>(*this, IniFile);
|
||||
m_FireSimulator = make_unique<cFireSimulator>(*this, IniFile);
|
||||
m_SandSimulator = cpp14::make_unique<cSandSimulator>(*this, IniFile);
|
||||
m_FireSimulator = cpp14::make_unique<cFireSimulator>(*this, IniFile);
|
||||
m_RedstoneSimulator = InitializeRedstoneSimulator(IniFile);
|
||||
|
||||
// Water, Lava and Redstone simulators get registered in their initialize function.
|
||||
@ -2680,7 +2680,7 @@ void cWorld::UnloadUnusedChunks(void)
|
||||
|
||||
void cWorld::QueueUnloadUnusedChunks(void)
|
||||
{
|
||||
QueueTask(make_unique<cWorld::cTaskUnloadUnusedChunks>());
|
||||
QueueTask(cpp14::make_unique<cWorld::cTaskUnloadUnusedChunks>());
|
||||
}
|
||||
|
||||
|
||||
|
60
src/main.cpp
60
src/main.cpp
@ -2,6 +2,7 @@
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Root.h"
|
||||
#include "tclap/CmdLine.h"
|
||||
|
||||
#include <exception>
|
||||
#include <csignal>
|
||||
@ -14,7 +15,7 @@
|
||||
#include "OSSupport/NetworkSingleton.h"
|
||||
#include "BuildInfo.h"
|
||||
|
||||
|
||||
#include "MemorySettingsRepository.h"
|
||||
|
||||
|
||||
|
||||
@ -206,7 +207,7 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// universalMain - Main startup logic for both standard running and as a service
|
||||
|
||||
void universalMain()
|
||||
void universalMain(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
|
||||
@ -226,7 +227,7 @@ void universalMain()
|
||||
#endif
|
||||
{
|
||||
cRoot Root;
|
||||
Root.Start();
|
||||
Root.Start(std::move(overridesRepo));
|
||||
}
|
||||
#if !defined(ANDROID_NDK)
|
||||
catch (std::exception & e)
|
||||
@ -257,7 +258,7 @@ DWORD WINAPI serviceWorkerThread(LPVOID lpParam)
|
||||
UNREFERENCED_PARAMETER(lpParam);
|
||||
|
||||
// Do the normal startup
|
||||
universalMain();
|
||||
universalMain(cpp14::make_unique<cMemorySettingsRepository>());
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
@ -363,14 +364,55 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
|
||||
|
||||
|
||||
|
||||
std::unique_ptr<cMemorySettingsRepository> parseArguments(int argc, char **argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
TCLAP::CmdLine cmd("MCServer");
|
||||
|
||||
TCLAP::ValueArg<int> slotsArg("s", "max-players", "Maximum number of slots for the server to use, overrides setting in setting.ini", false, -1, "number", cmd);
|
||||
|
||||
TCLAP::MultiArg<int> portsArg("p", "port", "The port number the server should listen to", false, "port", cmd);
|
||||
|
||||
cmd.parse(argc, argv);
|
||||
|
||||
auto repo = cpp14::make_unique<cMemorySettingsRepository>();
|
||||
|
||||
if (slotsArg.isSet())
|
||||
{
|
||||
|
||||
int slots = slotsArg.getValue();
|
||||
|
||||
repo->AddValue("Server", "MaxPlayers", static_cast<Int64>(slots));
|
||||
|
||||
}
|
||||
|
||||
if (portsArg.isSet())
|
||||
{
|
||||
std::vector<int> ports = portsArg.getValue();
|
||||
for (auto port : ports)
|
||||
{
|
||||
repo->AddValue("Server", "Port", static_cast<Int64>(port));
|
||||
}
|
||||
}
|
||||
|
||||
repo->SetReadOnly();
|
||||
|
||||
return repo;
|
||||
}
|
||||
catch (TCLAP::ArgException &e)
|
||||
{
|
||||
printf("error reading command line %s for arg %s", e.error().c_str(), e.argId().c_str());
|
||||
return cpp14::make_unique<cMemorySettingsRepository>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// main:
|
||||
|
||||
int main( int argc, char **argv)
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
|
||||
InitLeakFinder();
|
||||
@ -425,6 +467,8 @@ int main( int argc, char **argv)
|
||||
// DEBUG: test the dumpfile creation:
|
||||
// *((int *)0) = 0;
|
||||
|
||||
auto argsRepo = parseArguments(argc, argv);
|
||||
|
||||
// Check if comm logging is to be enabled:
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
@ -483,7 +527,7 @@ int main( int argc, char **argv)
|
||||
#endif
|
||||
{
|
||||
// Not running as a service, do normal startup
|
||||
universalMain();
|
||||
universalMain(std::move(argsRepo));
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
|
||||
|
Loading…
Reference in New Issue
Block a user