summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Android/jni/Android.mk24
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnProjectileHitBlock.lua24
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnProjectileHitEntity.lua25
-rw-r--r--src/Bindings/LuaState.cpp12
-rw-r--r--src/Bindings/LuaState.h2
-rw-r--r--src/Bindings/Plugin.h2
-rw-r--r--src/Bindings/PluginLua.cpp40
-rw-r--r--src/Bindings/PluginLua.h2
-rw-r--r--src/Bindings/PluginManager.cpp42
-rw-r--r--src/Bindings/PluginManager.h7
-rw-r--r--src/Bindings/lua51.dll (renamed from src/Bindings/lua5.1.dll)bin167424 -> 167424 bytes
-rw-r--r--src/ClientHandle.cpp6
-rw-r--r--src/Entities/Player.cpp1
-rw-r--r--src/Entities/ProjectileEntity.cpp12
-rw-r--r--src/Generating/Prefab.cpp103
-rw-r--r--src/Generating/Prefab.h9
-rw-r--r--src/Root.cpp1
17 files changed, 285 insertions, 27 deletions
diff --git a/Android/jni/Android.mk b/Android/jni/Android.mk
index 3542e588b..ce142d446 100644
--- a/Android/jni/Android.mk
+++ b/Android/jni/Android.mk
@@ -5,7 +5,7 @@ LOCAL_MODULE := mcserver
-LOCAL_SRC_FILES := $(shell find ../CryptoPP ../lua ../jsoncpp ../zlib ../src ../tolua++ ../iniFile ../expat ../md5 ../sqlite ../luaexpat '(' -name '*.cpp' -o -name '*.c' ')')
+LOCAL_SRC_FILES := $(shell find ../lib/polarssl ../lib/lua ../lib/jsoncpp ../lib/zlib ../src ../lib/tolua++ ../lib/iniFile ../lib/expat ../lib/md5 ../lib/sqlite ../lib/luaexpat '(' -name '*.cpp' -o -name '*.c' ')')
LOCAL_SRC_FILES := $(filter-out %SquirrelFunctions.cpp %SquirrelBindings.cpp %cPlugin_Squirrel.cpp %cSquirrelCommandBinder.cpp %minigzip.c %lua.c %tolua.c %toluabind.c %LeakFinder.cpp %StackWalker.cpp %example.c,$(LOCAL_SRC_FILES))
LOCAL_SRC_FILES := $(patsubst %.cpp,../%.cpp,$(LOCAL_SRC_FILES))
LOCAL_SRC_FILES := $(patsubst %.c,../%.c,$(LOCAL_SRC_FILES))
@@ -24,17 +24,17 @@ LOCAL_C_INCLUDES := ../src \
../src/packets \
../src/items \
../src/blocks \
- ../tolua++/src/lib \
- ../lua/src \
- ../zlib-1.2.7 \
- ../iniFile \
- ../tolua++/include \
- ../jsoncpp/include \
- ../jsoncpp/src/lib_json \
- ../expat/ \
- ../md5/ \
- ../sqlite/ \
- ../luaexpat/ \
+ ../lib/tolua++/src/lib \
+ ../lib/lua/src \
+ ../lib/zlib-1.2.7 \
+ ../lib/iniFile \
+ ../lib/tolua++/include \
+ ../lib/jsoncpp/include \
+ ../lib/jsoncpp/src/lib_json \
+ ../lib/expat/ \
+ ../lib/md5/ \
+ ../lib/sqlite/ \
+ ../lib/luaexpat/ \
.. \
diff --git a/MCServer/Plugins/APIDump/Hooks/OnProjectileHitBlock.lua b/MCServer/Plugins/APIDump/Hooks/OnProjectileHitBlock.lua
new file mode 100644
index 000000000..1588d420c
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Hooks/OnProjectileHitBlock.lua
@@ -0,0 +1,24 @@
+return
+{
+ HOOK_PROJECTILE_HIT_BLOCK =
+ {
+ CalledWhen = "A projectile hits a solid block.",
+ DefaultFnName = "OnProjectileHitBlock", -- also used as pagename
+ Desc = [[
+ This hook is called when a {{cProjectileEntity|projectile}} hits a solid block..
+ ]],
+ Params =
+ {
+ { Name = "ProjectileEntity", Type = "{{cProjectileEntity}}", Notes = "The projectile that hit an entity." },
+ },
+ 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 projectile flies through block..
+ ]],
+ }, -- HOOK_PROJECTILE_HIT_BLOCK
+}
+
+
+
+
+
diff --git a/MCServer/Plugins/APIDump/Hooks/OnProjectileHitEntity.lua b/MCServer/Plugins/APIDump/Hooks/OnProjectileHitEntity.lua
new file mode 100644
index 000000000..dd949fb46
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Hooks/OnProjectileHitEntity.lua
@@ -0,0 +1,25 @@
+return
+{
+ HOOK_PROJECTILE_HIT_ENTITY =
+ {
+ CalledWhen = "A projectile hits another entity.",
+ DefaultFnName = "OnProjectileHitEntity", -- also used as pagename
+ Desc = [[
+ This hook is called when a {{cProjectileEntity|projectile}} hits another entity.
+ ]],
+ Params =
+ {
+ { Name = "ProjectileEntity", Type = "{{cProjectileEntity}}", Notes = "The projectile that hit an entity." },
+ { Name = "Entity", Type = "{{cEntity}}", Notes = "The entity wich was hit." },
+ },
+ 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 projectile flies through the entity.
+ ]],
+ }, -- HOOK_PROJECTILE_HIT_ENTITY
+}
+
+
+
+
+
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index 47380b8a7..13eb17f7d 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -479,6 +479,18 @@ void cLuaState::Push(cEntity * a_Entity)
+void cLuaState::Push(cProjectileEntity * a_ProjectileEntity)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_ProjectileEntity, "cProjectileEntity");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
void cLuaState::Push(cMonster * a_Monster)
{
ASSERT(IsValid());
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index f0047b362..b9ca2f29b 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -38,6 +38,7 @@ extern "C"
class cWorld;
class cPlayer;
class cEntity;
+class cProjectileEntity;
class cMonster;
class cItem;
class cItems;
@@ -183,6 +184,7 @@ public:
void Push(cPlayer * a_Player);
void Push(const cPlayer * a_Player);
void Push(cEntity * a_Entity);
+ void Push(cProjectileEntity * a_ProjectileEntity);
void Push(cMonster * a_Monster);
void Push(cItem * a_Item);
void Push(cItems * a_Items);
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 057fa0304..df0bd4dcc 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -90,6 +90,8 @@ public:
virtual bool OnPluginsLoaded (void) = 0;
virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
+ virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile) = 0;
+ virtual bool OnProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity) = 0;
virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) = 0;
virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) = 0;
virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) = 0;
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 4f0e13f12..7e69e0f4b 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -1108,6 +1108,46 @@ bool cPluginLua::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a
+bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_BLOCK];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Projectile, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity & a_HitEntity)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_ENTITY];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Projectile, &a_HitEntity, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity)
{
cCSLock Lock(m_CriticalSection);
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index f51056186..59542d23a 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -113,6 +113,8 @@ public:
virtual bool OnPluginsLoaded (void) override;
virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile) override;
+ virtual bool OnProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity) override;
virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) override;
virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) override;
virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) override;
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 7d346c522..6a5356c0b 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -1154,6 +1154,48 @@ bool cPluginManager::CallHookPreCrafting(const cPlayer * a_Player, const cCrafti
+bool cPluginManager::CallHookProjectileHitBlock(cProjectileEntity & a_Projectile)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PROJECTILE_HIT_BLOCK);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnProjectileHitBlock(a_Projectile))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity & a_HitEntity)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PROJECTILE_HIT_ENTITY);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnProjectileHitEntity(a_Projectile, a_HitEntity))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginManager::CallHookSpawnedEntity(cWorld & a_World, cEntity & a_Entity)
{
HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNED_ENTITY);
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index 7895be959..512bc1351 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -18,6 +18,9 @@ class cChunkDesc;
// fwd: Entities/Entity.h
class cEntity;
+// fwd: Entities/ProjectileEntity.h
+class cProjectileEntity;
+
// fwd: Mobs/Monster.h
class cMonster;
@@ -102,6 +105,8 @@ public: // tolua_export
HOOK_PLUGINS_LOADED,
HOOK_POST_CRAFTING,
HOOK_PRE_CRAFTING,
+ HOOK_PROJECTILE_HIT_BLOCK,
+ HOOK_PROJECTILE_HIT_ENTITY,
HOOK_SPAWNED_ENTITY,
HOOK_SPAWNED_MONSTER,
HOOK_SPAWNING_ENTITY,
@@ -201,6 +206,8 @@ public: // tolua_export
bool CallHookPluginsLoaded (void);
bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
+ bool CallHookProjectileHitBlock (cProjectileEntity & a_Projectile);
+ bool CallHookProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity);
bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity);
bool CallHookSpawnedMonster (cWorld & a_World, cMonster & a_Monster);
bool CallHookSpawningEntity (cWorld & a_World, cEntity & a_Entity);
diff --git a/src/Bindings/lua5.1.dll b/src/Bindings/lua51.dll
index 515cf8b30..515cf8b30 100644
--- a/src/Bindings/lua5.1.dll
+++ b/src/Bindings/lua51.dll
Binary files differ
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 46c10ae82..eb05ebdb7 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -1606,10 +1606,8 @@ void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket)
m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
} // for itr - Chunks[]
- // Do NOT stream new chunks, the new world runs its own tick thread and may deadlock
- // Instead, the chunks will be streamed when the client is moved to the new world's Tick list,
- // by setting state to csAuthenticated
- m_State = csAuthenticated;
+ // StreamChunks() called in cPlayer::MoveToWorld() after new world has been set
+ // Meanwhile here, we set last streamed values to bogus ones so everything is resent
m_LastStreamedChunkX = 0x7fffffff;
m_LastStreamedChunkZ = 0x7fffffff;
m_HasSentPlayerChunk = false;
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 863aaa799..646aad50f 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -1489,6 +1489,7 @@ bool cPlayer::MoveToWorld(const char * a_WorldName)
// Add player to all the necessary parts of the new world
SetWorld(World);
+ m_ClientHandle->StreamChunks();
World->AddEntity(this);
World->AddPlayer(this);
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index f4ab825f2..a9735a53c 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -4,6 +4,7 @@
// Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types
#include "Globals.h"
+#include "../Bindings/PluginManager.h"
#include "ProjectileEntity.h"
#include "../ClientHandle.h"
#include "Player.h"
@@ -66,6 +67,11 @@ protected:
eBlockFace Face;
if (bb.CalcLineIntersection(Line1, Line2, LineCoeff, Face))
{
+ if (cPluginManager::Get()->CallHookProjectileHitBlock(*m_Projectile))
+ {
+ return false;
+ }
+
Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff;
m_Projectile->OnHitSolidBlock(Intersection, Face);
return true;
@@ -147,7 +153,11 @@ public:
}
// TODO: Some entities don't interact with the projectiles (pickups, falling blocks)
- // TODO: Allow plugins to interfere about which entities can be hit
+ if (cPluginManager::Get()->CallHookProjectileHitEntity(*m_Projectile, *a_Entity))
+ {
+ // A plugin disagreed.
+ return false;
+ }
if (LineCoeff < m_MinCoeff)
{
diff --git a/src/Generating/Prefab.cpp b/src/Generating/Prefab.cpp
index 1af1faec1..a9e0a839d 100644
--- a/src/Generating/Prefab.cpp
+++ b/src/Generating/Prefab.cpp
@@ -15,6 +15,92 @@ uses a prefabricate in a cBlockArea for drawing itself.
+#ifdef SELF_TEST
+
+// Create one static prefab to test the parser:
+static const cPrefab::sDef g_TestPrefabDef =
+{
+ // Size:
+ 7, 6, 7, // SizeX = 7, SizeY = 6, SizeZ = 7
+
+ // Block definitions:
+ ".: 0: 0\n" /* 0 */
+ "a:112: 0\n" /* netherbrick */
+ "b:113: 0\n" /* netherbrickfence */,
+
+ // Block data:
+ // Level 1
+ "aaaaaaa"
+ "aaaaaaa"
+ "aaaaaaa"
+ "aaaaaaa"
+ "aaaaaaa"
+ "aaaaaaa"
+ "aaaaaaa"
+
+ // Level 2
+ "aa...aa"
+ "a.....a"
+ "......."
+ "......."
+ "......."
+ "a.....a"
+ "aa...aa"
+
+ // Level 3
+ "aa...aa"
+ "a.....a"
+ "......."
+ "......."
+ "......."
+ "a.....a"
+ "aa...aa"
+
+ // Level 4
+ "aa...aa"
+ "a.....a"
+ "......."
+ "......."
+ "......."
+ "a.....a"
+ "aa...aa"
+
+ // Level 5
+ "aabbbaa"
+ "a.....a"
+ "b.....b"
+ "b.....b"
+ "b.....b"
+ "a.....a"
+ "aabbbaa"
+
+ // Level 6
+ "aaaaaaa"
+ "a.....a"
+ "a.....a"
+ "a.....a"
+ "a.....a"
+ "a.....a"
+ "aaaaaaa",
+
+ // Connections:
+ "0: 0, 3, 2: 4\n"
+ "0: 2, 3, 0: 2\n",
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotations */
+
+ // Merge strategy:
+ cBlockArea::msImprint
+};
+
+static cPrefab g_TestPrefab(g_TestPrefabDef);
+#endif
+
+
+
+
+
cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
m_Size(a_Def.m_SizeX, a_Def.m_SizeY, a_Def.m_SizeZ),
m_HitBox(0, 0, 0, a_Def.m_SizeX - 1, a_Def.m_SizeY - 1, a_Def.m_SizeZ - 1),
@@ -89,7 +175,8 @@ void cPrefab::ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef)
// Initialize the charmap to all-invalid values:
for (size_t i = 0; i < ARRAYCOUNT(a_CharMapOut); i++)
{
- a_CharMapOut[i] = -1;
+ a_CharMapOut[i].m_BlockType = 0;
+ a_CharMapOut[i].m_BlockMeta = 16; // Mark unassigned entries with a meta that is impossible otherwise
}
// Process the lines in the definition:
@@ -104,15 +191,15 @@ void cPrefab::ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef)
continue;
}
unsigned char Src = (unsigned char)CharDef[0][0];
- BLOCKTYPE BlockType = (BLOCKTYPE)atoi(CharDef[1].c_str());
+ ASSERT(a_CharMapOut[Src].m_BlockMeta == 16); // This letter has not been assigned yet?
+ a_CharMapOut[Src].m_BlockType = (BLOCKTYPE)atoi(CharDef[1].c_str());
NIBBLETYPE BlockMeta = 0;
if ((NumElements >= 3) && !CharDef[2].empty())
{
BlockMeta = (NIBBLETYPE)atoi(CharDef[2].c_str());
ASSERT((BlockMeta >= 0) && (BlockMeta <= 15));
}
- ASSERT(a_CharMapOut[Src] == -1); // Any duplicates letter-wise?
- a_CharMapOut[Src] = (BlockType << 4) | BlockMeta;
+ a_CharMapOut[Src].m_BlockMeta = BlockMeta;
} // for itr - Lines[]
}
@@ -130,11 +217,9 @@ void cPrefab::ParseBlockImage(const CharMap & a_CharMap, const char * a_BlockIma
const unsigned char * BlockImage = (const unsigned char *)a_BlockImage + y * m_Size.x * m_Size.z + z * m_Size.x;
for (int x = 0; x < m_Size.x; x++)
{
- int MappedValue = a_CharMap[BlockImage[x]];
- ASSERT(MappedValue != -1); // Using a letter not defined in the CharMap?
- BLOCKTYPE BlockType = MappedValue >> 4;
- NIBBLETYPE BlockMeta = MappedValue & 0x0f;
- m_BlockArea[0].SetRelBlockTypeMeta(x, y, z, BlockType, BlockMeta);
+ const sBlockTypeDef & MappedValue = a_CharMap[BlockImage[x]];
+ ASSERT(MappedValue.m_BlockMeta != 16); // Using a letter not defined in the CharMap?
+ m_BlockArea[0].SetRelBlockTypeMeta(x, y, z, MappedValue.m_BlockType, MappedValue.m_BlockMeta);
}
}
}
diff --git a/src/Generating/Prefab.h b/src/Generating/Prefab.h
index 0b254c03b..04c4f09da 100644
--- a/src/Generating/Prefab.h
+++ b/src/Generating/Prefab.h
@@ -53,8 +53,15 @@ public:
bool HasConnectorType(int a_ConnectorType) const;
protected:
+ /** Packs complete definition of a single block, for per-letter assignment. */
+ struct sBlockTypeDef
+ {
+ BLOCKTYPE m_BlockType;
+ NIBBLETYPE m_BlockMeta;
+ };
+
/** Maps letters in the sDef::m_Image onto a number, BlockType * 16 | BlockMeta */
- typedef int CharMap[256];
+ typedef sBlockTypeDef CharMap[256];
/** The cBlockArea that contains the block definitions for the prefab.
diff --git a/src/Root.cpp b/src/Root.cpp
index 3555afb45..ba4398b35 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -304,6 +304,7 @@ void cRoot::LoadWorlds(cIniFile & IniFile)
{
if (IniFile.GetKeyComment("Worlds", 0) != " World=secondworld")
{
+ IniFile.DeleteKeyComment("Worlds", 0);
IniFile.AddKeyComment("Worlds", " World=secondworld");
}
}