From 5c99f3fadafa03664da4a179af28ed4db4980048 Mon Sep 17 00:00:00 2001 From: Howaner Date: Wed, 17 Dec 2014 17:28:24 +0100 Subject: Fixed eMonsterType lua bugs. --- src/Mobs/MonsterTypes.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h index dc6dd3992..3175f0606 100644 --- a/src/Mobs/MonsterTypes.h +++ b/src/Mobs/MonsterTypes.h @@ -37,6 +37,7 @@ enum eMonsterType mtWolf = E_META_SPAWN_EGG_WOLF, mtZombie = E_META_SPAWN_EGG_ZOMBIE, mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, + mtMax = 120, } ; // tolua_end -- cgit v1.2.3 From fff108f20a8962f6690a9dff45176c7f31c8cf01 Mon Sep 17 00:00:00 2001 From: Howaner Date: Wed, 17 Dec 2014 19:16:35 +0100 Subject: Added comment. --- src/Mobs/MonsterTypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h index 3175f0606..6886bd51a 100644 --- a/src/Mobs/MonsterTypes.h +++ b/src/Mobs/MonsterTypes.h @@ -37,7 +37,7 @@ enum eMonsterType mtWolf = E_META_SPAWN_EGG_WOLF, mtZombie = E_META_SPAWN_EGG_ZOMBIE, mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, - mtMax = 120, + mtMax = 120, // This is just a hotfix for #http://forum.mc-server.org/showthread.php?tid=1616. Tolua is too bad to find the highest value, so this is needed. } ; // tolua_end -- cgit v1.2.3 From c2926f8de1709d660cdca130f95a24623e2007c2 Mon Sep 17 00:00:00 2001 From: Howaner Date: Wed, 17 Dec 2014 20:08:55 +0100 Subject: derp --- src/Mobs/MonsterTypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h index 6886bd51a..bbe9e0471 100644 --- a/src/Mobs/MonsterTypes.h +++ b/src/Mobs/MonsterTypes.h @@ -37,7 +37,7 @@ enum eMonsterType mtWolf = E_META_SPAWN_EGG_WOLF, mtZombie = E_META_SPAWN_EGG_ZOMBIE, mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, - mtMax = 120, // This is just a hotfix for #http://forum.mc-server.org/showthread.php?tid=1616. Tolua is too bad to find the highest value, so this is needed. + mtMax = 120, // This is just a hotfix for http://forum.mc-server.org/showthread.php?tid=1616. Tolua is too bad to find the highest value, so this is needed. } ; // tolua_end -- cgit v1.2.3 From dd1df3b6f740fad699855de26d3f7f512008671b Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Thu, 18 Dec 2014 00:22:46 +0000 Subject: Fix repeater unpowering --- src/Simulator/IncrementalRedstoneSimulator.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 8a56287a8..544a0689d 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -2074,9 +2074,11 @@ void cIncrementalRedstoneSimulator::SetSourceUnpowered(int a_RelSourceX, int a_R for (const auto & itr : BlocksPotentiallyUnpowered) { - if (!AreCoordsPowered(itr.x, itr.y, itr.z)) + auto Neighbour = a_Chunk->GetRelNeighborChunk(itr.x, itr.z); + if (!AreCoordsPowered(itr.x, itr.y, itr.z) && (Neighbour->GetBlock(itr) != E_BLOCK_REDSTONE_REPEATER_ON)) { - SetSourceUnpowered(itr.x, itr.y, itr.z, a_Chunk->GetRelNeighborChunk(itr.x, itr.z)); + // Repeaters time themselves with regards to unpowering; ensure we don't do it for them + SetSourceUnpowered(itr.x, itr.y, itr.z, Neighbour); } } } -- cgit v1.2.3 From c836b52dd1b8d6a2999721f235e6c2b6079b266c Mon Sep 17 00:00:00 2001 From: Masy98 Date: Thu, 18 Dec 2014 19:30:32 +0100 Subject: Added Entity Guardian --- src/BlockID.h | 1 + src/Items/ItemSpawnEgg.h | 1 + src/MobSpawner.cpp | 6 +++ src/Mobs/CMakeLists.txt | 2 + src/Mobs/Guardian.cpp | 65 +++++++++++++++++++++++++++++++++ src/Mobs/Guardian.h | 31 ++++++++++++++++ src/Mobs/IncludeAllMonsters.h | 1 + src/Mobs/Monster.cpp | 4 ++ src/Mobs/Monster.h | 2 +- src/Mobs/MonsterTypes.h | 1 + src/World.cpp | 2 +- src/WorldStorage/NBTChunkSerializer.cpp | 2 + src/WorldStorage/WSSAnvil.cpp | 24 ++++++++++++ src/WorldStorage/WSSAnvil.h | 1 + 14 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 src/Mobs/Guardian.cpp create mode 100644 src/Mobs/Guardian.h (limited to 'src') diff --git a/src/BlockID.h b/src/BlockID.h index 8f2cee02e..bfb6b8d6d 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -959,6 +959,7 @@ enum E_META_SPAWN_EGG_WITHER = 64, E_META_SPAWN_EGG_BAT = 65, E_META_SPAWN_EGG_WITCH = 66, + E_META_SPAWN_EGG_GUARDIAN = 68, E_META_SPAWN_EGG_PIG = 90, E_META_SPAWN_EGG_SHEEP = 91, E_META_SPAWN_EGG_COW = 92, diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h index 617ecd808..2f28ec6eb 100644 --- a/src/Items/ItemSpawnEgg.h +++ b/src/Items/ItemSpawnEgg.h @@ -64,6 +64,7 @@ public: case E_META_SPAWN_EGG_CREEPER: return mtCreeper; case E_META_SPAWN_EGG_ENDERMAN: return mtEnderman; case E_META_SPAWN_EGG_GHAST: return mtGhast; + case E_META_SPAWN_EGG_GUARDIAN: return mtGuardian; case E_META_SPAWN_EGG_HORSE: return mtHorse; case E_META_SPAWN_EGG_MAGMA_CUBE: return mtMagmaCube; case E_META_SPAWN_EGG_MOOSHROOM: return mtMooshroom; diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index ee9e569a7..bd34d8fcd 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -83,6 +83,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) addIfAllowed(mtSkeleton, allowedMobs); addIfAllowed(mtCreeper, allowedMobs); addIfAllowed(mtSquid, allowedMobs); + addIfAllowed(mtGuardian, allowedMobs); if ((a_Biome != biDesert) && (a_Biome != biBeach) && (a_Biome != biOcean)) { @@ -144,6 +145,11 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R switch (a_MobType) { + case mtGuardian: + { + return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62); + } + case mtSquid: { return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62); diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt index bbbb9287a..c1ac2de32 100644 --- a/src/Mobs/CMakeLists.txt +++ b/src/Mobs/CMakeLists.txt @@ -16,6 +16,7 @@ SET (SRCS Enderman.cpp Ghast.cpp Giant.cpp + Guardian.cpp Horse.cpp IronGolem.cpp MagmaCube.cpp @@ -49,6 +50,7 @@ SET (HDRS Enderman.h Ghast.h Giant.h + Guardian.h Horse.h IncludeAllMonsters.h IronGolem.h diff --git a/src/Mobs/Guardian.cpp b/src/Mobs/Guardian.cpp new file mode 100644 index 000000000..166057865 --- /dev/null +++ b/src/Mobs/Guardian.cpp @@ -0,0 +1,65 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Guardian.h" +#include "../Vector3.h" +#include "../Chunk.h" + + + + + +cGuardian::cGuardian(void) : + super("Guardian", mtGuardian, "mob.guardian.idle", "mob.guardian.death", 0.95, 0.95) +{ +} + + + + + +void cGuardian::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + // Drops 0-3 Ink Sacs + int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_PRISMARINE_SHARD); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_RAW_FISH); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_PRISMARINE_CRYSTALS); // ToDo: Prismarine Crystals only drop if the raw fish drop is 0 +} + + + + + +void cGuardian::Tick(float a_Dt, cChunk & a_Chunk) +{ + // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk + // that is not where the entity currently resides (FS #411) + + Vector3d Pos = GetPosition(); + + // TODO: Not a real behavior, but cool :D + int RelY = (int)floor(Pos.y); + if ((RelY < 0) || (RelY >= cChunkDef::Height)) + { + return; + } + int RelX = (int)floor(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelZ = (int)floor(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width; + BLOCKTYPE BlockType; + if (a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockType) && !IsBlockWater(BlockType) && !IsOnFire()) + { + // Burn for 10 ticks, then decide again + StartBurning(10); + } + + super::Tick(a_Dt, a_Chunk); +} + + + + diff --git a/src/Mobs/Guardian.h b/src/Mobs/Guardian.h new file mode 100644 index 000000000..50c034036 --- /dev/null +++ b/src/Mobs/Guardian.h @@ -0,0 +1,31 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cGuardian : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cGuardian(); + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + CLASS_PROTODEF(cGuardian) + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + + // Guardians do not drown (or float) + virtual void HandleAir(void) override {} + virtual void SetSwimState(cChunk & a_Chunk) override {} +} ; + + + + diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h index 3460db993..f5eb9dcc3 100644 --- a/src/Mobs/IncludeAllMonsters.h +++ b/src/Mobs/IncludeAllMonsters.h @@ -8,6 +8,7 @@ #include "EnderDragon.h" #include "Ghast.h" #include "Giant.h" +#include "Guardian.h" #include "Horse.h" #include "IronGolem.h" #include "MagmaCube.h" diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 7b8f763af..963ca628c 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -38,6 +38,7 @@ static const struct {mtEnderman, "enderman", "Enderman"}, {mtEnderDragon, "enderdragon", "EnderDragon"}, {mtGhast, "ghast", "Ghast"}, + {mtGuardian, "guardian", "Guardian"}, {mtHorse, "horse", "EntityHorse"}, {mtIronGolem, "irongolem", "VillagerGolem"}, {mtMagmaCube, "magmacube", "LavaSlime"}, @@ -513,6 +514,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) case mtCreeper: case mtEnderman: case mtGhast: + case mtGuardian: case mtSilverfish: case mtSkeleton: case mtSpider: @@ -842,6 +844,7 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) case mtEnderman: return mfHostile; case mtGhast: return mfHostile; case mtGiant: return mfNoSpawn; + case mtGuardian: return mfNoSpawn; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! case mtHorse: return mfPassive; case mtIronGolem: return mfPassive; case mtMagmaCube: return mfHostile; @@ -955,6 +958,7 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) case mtEnderman: toReturn = new cEnderman(); break; case mtGhast: toReturn = new cGhast(); break; case mtGiant: toReturn = new cGiant(); break; + case mtGuardian: toReturn = new cGuardian(); break; case mtIronGolem: toReturn = new cIronGolem(); break; case mtMooshroom: toReturn = new cMooshroom(); break; case mtOcelot: toReturn = new cOcelot(); break; diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index f04e45ac6..fb1bc550d 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -30,7 +30,7 @@ public: mfHostile = 0, // Spider, Zombies ... mfPassive = 1, // Cows, Pigs mfAmbient = 2, // Bats - mfWater = 3, // Squid + mfWater = 3, // Squid, Guardian mfNoSpawn, mfUnhandled, // Nothing. Be sure this is the last and the others are in order diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h index dc6dd3992..50fcf971a 100644 --- a/src/Mobs/MonsterTypes.h +++ b/src/Mobs/MonsterTypes.h @@ -18,6 +18,7 @@ enum eMonsterType mtEnderman = E_META_SPAWN_EGG_ENDERMAN, mtGhast = E_META_SPAWN_EGG_GHAST, mtGiant = E_META_SPAWN_EGG_GIANT, + mtGuardian = E_META_SPAWN_EGG_GUARDIAN, mtHorse = E_META_SPAWN_EGG_HORSE, mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM, mtMagmaCube = E_META_SPAWN_EGG_MAGMA_CUBE, diff --git a/src/World.cpp b/src/World.cpp index 1bee6e344..99701e11b 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -776,7 +776,7 @@ void cWorld::InitialiseAndLoadMobSpawningValues(cIniFile & a_IniFile) AString DefaultMonsters; switch (m_Dimension) { - case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; + case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, guardian, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break; case dimEnd: DefaultMonsters = "enderman"; break; case dimNotSet: ASSERT(!"Dimension not set"); break; diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 432e122b5..9d8e20b0b 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -495,6 +495,7 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) case mtEnderman: EntityClass = "Enderman"; break; case mtGhast: EntityClass = "Ghast"; break; case mtGiant: EntityClass = "Giant"; break; + case mtGuardian: EntityClass = "Guardian"; break; case mtHorse: EntityClass = "Horse"; break; case mtIronGolem: EntityClass = "VillagerGolem"; break; case mtMagmaCube: EntityClass = "LavaSlime"; break; @@ -627,6 +628,7 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) case mtEnderDragon: case mtGhast: case mtGiant: + case mtGuardian: case mtIronGolem: case mtMooshroom: case mtOcelot: diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index af65db700..a045497bc 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1410,6 +1410,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a { LoadGiantFromNBT(a_Entities, a_NBT, a_EntityTagIdx); } + else if (strncmp(a_IDTag, "Guardian", a_IDTagLength) == 0) + { + LoadGuardianFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } else if (strncmp(a_IDTag, "Horse", a_IDTagLength) == 0) { LoadHorseFromNBT(a_Entities, a_NBT, a_EntityTagIdx); @@ -2197,6 +2201,26 @@ void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ +void cWSSAnvil::LoadGuardianFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::unique_ptr Monster(new cGuardian()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Type"); diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 974ba932e..7bcdd259e 100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -191,6 +191,7 @@ protected: void LoadEndermanFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadGhastFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadGiantFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadGuardianFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadHorseFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadIronGolemFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMagmaCubeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); -- cgit v1.2.3 From f09c6701eb9126b627503bc301084ffb4039fabd Mon Sep 17 00:00:00 2001 From: Masy98 Date: Thu, 18 Dec 2014 20:44:39 +0100 Subject: Guardian can now spawn if wanted!? --- src/Mobs/Monster.cpp | 2 +- src/World.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 963ca628c..3d174677c 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -844,7 +844,7 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) case mtEnderman: return mfHostile; case mtGhast: return mfHostile; case mtGiant: return mfNoSpawn; - case mtGuardian: return mfNoSpawn; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! + case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! case mtHorse: return mfPassive; case mtIronGolem: return mfPassive; case mtMagmaCube: return mfHostile; diff --git a/src/World.cpp b/src/World.cpp index b3d34b48c..69b39f831 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -776,7 +776,7 @@ void cWorld::InitialiseAndLoadMobSpawningValues(cIniFile & a_IniFile) AString DefaultMonsters; switch (m_Dimension) { - case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, guardian, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; + case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break; case dimEnd: DefaultMonsters = "enderman"; break; case dimNotSet: ASSERT(!"Dimension not set"); break; -- cgit v1.2.3 From 5cfb6063c37243477ea79319706dae7cdde27ab2 Mon Sep 17 00:00:00 2001 From: Masy98 Date: Fri, 19 Dec 2014 16:06:43 +0100 Subject: Fixed Guardians size and health --- src/Mobs/Guardian.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Mobs/Guardian.cpp b/src/Mobs/Guardian.cpp index 166057865..d69ee1683 100644 --- a/src/Mobs/Guardian.cpp +++ b/src/Mobs/Guardian.cpp @@ -10,7 +10,7 @@ cGuardian::cGuardian(void) : - super("Guardian", mtGuardian, "mob.guardian.idle", "mob.guardian.death", 0.95, 0.95) + super("Guardian", mtGuardian, "mob.guardian.idle", "mob.guardian.death", 0.875, 0.8) { } -- cgit v1.2.3 From 6e8e1c6d8da3d5a0f5ca39f6f18d88c2ca6c1e71 Mon Sep 17 00:00:00 2001 From: Masy98 Date: Sat, 20 Dec 2014 10:31:34 +0100 Subject: Added Rabbits --- src/BlockID.h | 1 + src/Items/ItemSpawnEgg.h | 1 + src/MobSpawner.cpp | 2 ++ src/Mobs/IncludeAllMonsters.h | 1 + src/Mobs/Monster.cpp | 4 ++++ src/Mobs/MonsterTypes.h | 1 + src/Mobs/Rabbit.cpp | 38 +++++++++++++++++++++++++++++++++ src/Mobs/Rabbit.h | 24 +++++++++++++++++++++ src/WorldStorage/NBTChunkSerializer.cpp | 2 ++ src/WorldStorage/WSSAnvil.cpp | 24 +++++++++++++++++++++ src/WorldStorage/WSSAnvil.h | 1 + 11 files changed, 99 insertions(+) create mode 100644 src/Mobs/Rabbit.cpp create mode 100644 src/Mobs/Rabbit.h (limited to 'src') diff --git a/src/BlockID.h b/src/BlockID.h index bfb6b8d6d..41ccf90b5 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -971,6 +971,7 @@ enum E_META_SPAWN_EGG_OCELOT = 98, E_META_SPAWN_EGG_IRON_GOLEM = 99, E_META_SPAWN_EGG_HORSE = 100, + E_META_SPAWN_EGG_RABBIT = 101, E_META_SPAWN_EGG_VILLAGER = 120, E_META_SPAWN_EGG_ENDER_CRYSTAL = 200, } ; diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h index 2f28ec6eb..dee8a9057 100644 --- a/src/Items/ItemSpawnEgg.h +++ b/src/Items/ItemSpawnEgg.h @@ -70,6 +70,7 @@ public: case E_META_SPAWN_EGG_MOOSHROOM: return mtMooshroom; case E_META_SPAWN_EGG_OCELOT: return mtOcelot; case E_META_SPAWN_EGG_PIG: return mtPig; + case E_META_SPAWN_EGG_RABBIT: return mtRabbit; case E_META_SPAWN_EGG_SHEEP: return mtSheep; case E_META_SPAWN_EGG_SILVERFISH: return mtSilverfish; case E_META_SPAWN_EGG_SKELETON: return mtSkeleton; diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index bd34d8fcd..0a32d17ef 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -92,6 +92,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) addIfAllowed(mtCow, allowedMobs); addIfAllowed(mtChicken, allowedMobs); addIfAllowed(mtEnderman, allowedMobs); + addIfAllowed(mtRabbit, allowedMobs); addIfAllowed(mtSlime, allowedMobs); // MG TODO : much more complicated rule if ((a_Biome == biForest) || (a_Biome == biForestHills) || (a_Biome == biTaiga) || (a_Biome == biTaigaHills)) @@ -164,6 +165,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R case mtCow: case mtPig: case mtHorse: + case mtRabbit: case mtSheep: { return ( diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h index f5eb9dcc3..53c709c2b 100644 --- a/src/Mobs/IncludeAllMonsters.h +++ b/src/Mobs/IncludeAllMonsters.h @@ -15,6 +15,7 @@ #include "Mooshroom.h" #include "Ocelot.h" #include "Pig.h" +#include "Rabbit.h" #include "Sheep.h" #include "Silverfish.h" #include "Skeleton.h" diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 3d174677c..a02ea357e 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -45,6 +45,7 @@ static const struct {mtMooshroom, "mooshroom", "MushroomCow"}, {mtOcelot, "ocelot", "Ozelot"}, {mtPig, "pig", "Pig"}, + {mtRabbit, "rabbit", "Rabbit"}, {mtSheep, "sheep", "Sheep"}, {mtSilverfish, "silverfish", "Silverfish"}, {mtSkeleton, "skeleton", "Skeleton"}, @@ -499,6 +500,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) case mtCow: case mtHorse: case mtPig: + case mtRabbit: case mtSheep: case mtSquid: case mtMooshroom: @@ -851,6 +853,7 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) case mtMooshroom: return mfHostile; case mtOcelot: return mfPassive; case mtPig: return mfPassive; + case mtRabbit: return mfPassive; case mtSheep: return mfPassive; case mtSilverfish: return mfHostile; case mtSkeleton: return mfHostile; @@ -963,6 +966,7 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) case mtMooshroom: toReturn = new cMooshroom(); break; case mtOcelot: toReturn = new cOcelot(); break; case mtPig: toReturn = new cPig(); break; + case mtRabbit: toReturn = new cRabbit(); break; case mtSheep: toReturn = new cSheep(); break; case mtSilverfish: toReturn = new cSilverfish(); break; case mtSnowGolem: toReturn = new cSnowGolem(); break; diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h index 0d716cca3..02bec267b 100644 --- a/src/Mobs/MonsterTypes.h +++ b/src/Mobs/MonsterTypes.h @@ -25,6 +25,7 @@ enum eMonsterType mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM, mtOcelot = E_META_SPAWN_EGG_OCELOT, mtPig = E_META_SPAWN_EGG_PIG, + mtRabbit = E_META_SPAWN_EGG_RABBIT, mtSheep = E_META_SPAWN_EGG_SHEEP, mtSilverfish = E_META_SPAWN_EGG_SILVERFISH, mtSkeleton = E_META_SPAWN_EGG_SKELETON, diff --git a/src/Mobs/Rabbit.cpp b/src/Mobs/Rabbit.cpp new file mode 100644 index 000000000..1c7d810b7 --- /dev/null +++ b/src/Mobs/Rabbit.cpp @@ -0,0 +1,38 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Rabbit.h" +#include "../Entities/Player.h" +#include "../World.h" + + + + + +cRabbit::cRabbit(void) : + super("Rabbit", mtRabbit, "mob.rabbit.idle", "mob.rabbit.death", 0.9, 0.9) +{ +} + + + + + +void cRabbit::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_RABBIT : E_ITEM_RAW_RABBIT); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_RABBIT_HIDE); + cItems RareDrops; + RareDrops.Add(cItem(E_ITEM_RABBITS_FOOT)); + AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); +} + + + + + diff --git a/src/Mobs/Rabbit.h b/src/Mobs/Rabbit.h new file mode 100644 index 000000000..5ea03d09b --- /dev/null +++ b/src/Mobs/Rabbit.h @@ -0,0 +1,24 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cRabbit : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cRabbit(); + + CLASS_PROTODEF(cRabbit) + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + + virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); } + +} ; \ No newline at end of file diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 9d8e20b0b..c87397542 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -502,6 +502,7 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) case mtMooshroom: EntityClass = "MushroomCow"; break; case mtOcelot: EntityClass = "Ozelot"; break; case mtPig: EntityClass = "Pig"; break; + case mtRabbit: EntityClass = "Rabbit"; break; case mtSheep: EntityClass = "Sheep"; break; case mtSilverfish: EntityClass = "Silverfish"; break; case mtSkeleton: EntityClass = "Skeleton"; break; @@ -633,6 +634,7 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) case mtMooshroom: case mtOcelot: case mtPig: + case mtRabbit: case mtSilverfish: case mtSnowGolem: case mtSpider: diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index a045497bc..cf51ab19c 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1442,6 +1442,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a { LoadPigFromNBT(a_Entities, a_NBT, a_EntityTagIdx); } + else if (strncmp(a_IDTag, "Rabbit", a_IDTagLength) == 0) + { + LoadRabbitFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } else if (strncmp(a_IDTag, "Sheep", a_IDTagLength) == 0) { LoadSheepFromNBT(a_Entities, a_NBT, a_EntityTagIdx); @@ -2363,6 +2367,26 @@ void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB +void cWSSAnvil::LoadRabbitFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::unique_ptr Monster(new cRabbit()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { int ColorIdx = a_NBT.FindChildByName(a_TagIdx, "Color"); diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 7bcdd259e..362796614 100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -198,6 +198,7 @@ protected: void LoadMooshroomFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadOcelotFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadPigFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadRabbitFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadSheepFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadSilverfishFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadSkeletonFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); -- cgit v1.2.3 From 3d2b1875a196a6aa39414381d580bdf5b2135bff Mon Sep 17 00:00:00 2001 From: Masy98 Date: Sat, 20 Dec 2014 10:38:56 +0100 Subject: Fixed Rabbit size --- src/Mobs/Rabbit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Mobs/Rabbit.cpp b/src/Mobs/Rabbit.cpp index 1c7d810b7..7b873da33 100644 --- a/src/Mobs/Rabbit.cpp +++ b/src/Mobs/Rabbit.cpp @@ -10,7 +10,7 @@ cRabbit::cRabbit(void) : - super("Rabbit", mtRabbit, "mob.rabbit.idle", "mob.rabbit.death", 0.9, 0.9) + super("Rabbit", mtRabbit, "mob.rabbit.idle", "mob.rabbit.death", 0.82, 0.68) { } -- cgit v1.2.3 From 7903ee485ee58b67c6bc4a6b7a182c5d855d5418 Mon Sep 17 00:00:00 2001 From: Masy98 Date: Sat, 20 Dec 2014 11:41:23 +0100 Subject: Added Rabbit.h and Rabbit.cpp to the CMakeList --- src/Mobs/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt index c1ac2de32..7a291dcf2 100644 --- a/src/Mobs/CMakeLists.txt +++ b/src/Mobs/CMakeLists.txt @@ -25,6 +25,7 @@ SET (SRCS PassiveAggressiveMonster.cpp PassiveMonster.cpp Pig.cpp + Rabbit.cpp Sheep.cpp Skeleton.cpp Slime.cpp @@ -62,6 +63,7 @@ SET (HDRS PassiveAggressiveMonster.h PassiveMonster.h Pig.h + Rabbit.h Sheep.h Silverfish.h Skeleton.h -- cgit v1.2.3 From 5695649bb94276a7465c6c1df5a271889650abc0 Mon Sep 17 00:00:00 2001 From: Masy98 Date: Sat, 20 Dec 2014 13:04:42 +0100 Subject: Fixed damn empty line with no use --- src/Mobs/Rabbit.cpp | 4 ---- src/Mobs/Rabbit.h | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'src') diff --git a/src/Mobs/Rabbit.cpp b/src/Mobs/Rabbit.cpp index 7b873da33..cf49d2744 100644 --- a/src/Mobs/Rabbit.cpp +++ b/src/Mobs/Rabbit.cpp @@ -32,7 +32,3 @@ void cRabbit::GetDrops(cItems & a_Drops, cEntity * a_Killer) AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); } - - - - diff --git a/src/Mobs/Rabbit.h b/src/Mobs/Rabbit.h index 5ea03d09b..e86c85579 100644 --- a/src/Mobs/Rabbit.h +++ b/src/Mobs/Rabbit.h @@ -21,4 +21,4 @@ public: virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); } -} ; \ No newline at end of file +} ; -- cgit v1.2.3 From 0d6672bf5d976f86f2746a120acd3c19c9d4c8a2 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sun, 21 Dec 2014 14:31:20 +0000 Subject: Fixed crash on restart --- src/Root.cpp | 15 ++++++++++++--- src/Root.h | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/Root.cpp b/src/Root.cpp index 9f8ffeeff..9725502ee 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -135,8 +135,9 @@ void cRoot::Start(void) } 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 + 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)) { IniFile.WriteFile("settings.ini"); @@ -149,7 +150,7 @@ void cRoot::Start(void) LOGD("Loading settings..."); m_RankManager.reset(new cRankManager()); - m_RankManager->Initialize(m_MojangAPI); + m_RankManager->Initialize(*m_MojangAPI); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); @@ -196,7 +197,7 @@ void cRoot::Start(void) } #endif - LOG("Startup complete, took %ld ms!", static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - BeginTime).count())); + LOG("Startup complete, took %ldms!", static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - BeginTime).count())); #ifdef _WIN32 EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button #endif @@ -213,21 +214,28 @@ void cRoot::Start(void) // Stop the server: m_WebAdmin->Stop(); + LOG("Shutting down server..."); m_Server->Shutdown(); + delete m_MojangAPI; m_MojangAPI = nullptr; + LOGD("Shutting down deadlock detector..."); dd.Stop(); + LOGD("Stopping world threads..."); StopWorlds(); + LOGD("Stopping authenticator..."); m_Authenticator.Stop(); LOGD("Freeing MonsterConfig..."); delete m_MonsterConfig; m_MonsterConfig = nullptr; delete m_WebAdmin; m_WebAdmin = nullptr; + LOGD("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = nullptr; delete m_CraftingRecipes; m_CraftingRecipes = nullptr; + LOGD("Unloading worlds..."); UnloadWorlds(); @@ -238,6 +246,7 @@ void cRoot::Start(void) LOG("Cleaning up..."); delete m_Server; m_Server = nullptr; + LOG("Shutdown successful!"); } diff --git a/src/Root.h b/src/Root.h index 2c512a5df..fdaf444bd 100644 --- a/src/Root.h +++ b/src/Root.h @@ -86,7 +86,7 @@ public: cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } - cMojangAPI & GetMojangAPI (void) { return m_MojangAPI; } + cMojangAPI & GetMojangAPI (void) { return *m_MojangAPI; } cRankManager * GetRankManager (void) { return m_RankManager.get(); } /** Queues a console command for execution through the cServer class. @@ -191,7 +191,7 @@ private: cWebAdmin * m_WebAdmin; cPluginManager * m_PluginManager; cAuthenticator m_Authenticator; - cMojangAPI m_MojangAPI; + cMojangAPI * m_MojangAPI; std::unique_ptr m_RankManager; -- cgit v1.2.3 From 9e9459a36735b768f647a4cb5013c2928464a6eb Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 21 Dec 2014 19:48:29 +0100 Subject: Fixed a possible division by zero. --- src/Enchantments.cpp | 14 +++++++++++--- src/Enchantments.h | 6 ++++-- src/ItemGrid.cpp | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp index 36c451b81..e72ec668a 100644 --- a/src/Enchantments.cpp +++ b/src/Enchantments.cpp @@ -1021,26 +1021,34 @@ cEnchantments cEnchantments::GetRandomEnchantmentFromVector(cWeightedEnchantment -cEnchantments cEnchantments::GenerateEnchantmentFromVector(cWeightedEnchantments & a_Enchantments, int a_Seed) +cEnchantments cEnchantments::SelectEnchantmentFromVector(const cWeightedEnchantments & a_Enchantments, int a_Seed) { + // Sum up all the enchantments' weights: int AllWeights = 0; for (const auto Enchantment : a_Enchantments) { AllWeights += Enchantment.m_Weight; } + // If there's no weight for any of the enchantments, return an empty enchantment + if (AllWeights <= 0) + { + return cEnchantments(); + } + + // Pick a random enchantment: cNoise Noise(a_Seed); int RandomNumber = Noise.IntNoise1DInt(AllWeights) / 7 % AllWeights; - for (const auto Enchantment : a_Enchantments) { RandomNumber -= Enchantment.m_Weight; - if (RandomNumber < 0) + if (RandomNumber <= 0) { return Enchantment.m_Enchantments; } } + // No enchantment picked, return an empty one (we probably shouldn't ever get here): return cEnchantments(); } diff --git a/src/Enchantments.h b/src/Enchantments.h index e4390a5f2..31226b5c2 100644 --- a/src/Enchantments.h +++ b/src/Enchantments.h @@ -128,8 +128,10 @@ public: /** Gets random enchantment from Vector and returns it */ static cEnchantments GetRandomEnchantmentFromVector(cWeightedEnchantments & a_Enchantments); - /** Returns an enchantment from a Vector using cNoise. Mostly used for generators.*/ - static cEnchantments GenerateEnchantmentFromVector(cWeightedEnchantments & a_Enchantments, int a_Seed); + /** Selects one enchantment from a Vector using cNoise. Mostly used for generators. + Uses the enchantments' weights for the random distribution. + If a_Enchantments is empty, returns an empty enchantment. */ + static cEnchantments SelectEnchantmentFromVector(const cWeightedEnchantments & a_Enchantments, int a_Seed); /** Returns true if a_Other doesn't contain exactly the same enchantments and levels */ bool operator !=(const cEnchantments & a_Other) const; diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp index d49ea9df1..06971a1ac 100644 --- a/src/ItemGrid.cpp +++ b/src/ItemGrid.cpp @@ -646,7 +646,7 @@ void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, s for (int j = 0; j <= NumEnchantments; j++) { - cEnchantments Enchantment = cEnchantments::GenerateEnchantmentFromVector(Enchantments, Noise.IntNoise2DInt(NumEnchantments, i)); + cEnchantments Enchantment = cEnchantments::SelectEnchantmentFromVector(Enchantments, Noise.IntNoise2DInt(NumEnchantments, i)); CurrentLoot.m_Enchantments.Add(Enchantment); cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment); cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment); -- cgit v1.2.3 From d4c3821eca6249399a2d690c3c8735526d1d5a4c Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 21 Dec 2014 20:01:42 +0100 Subject: Fixed coverity issues in protocols. Fixes CID 73099, CID 66411. --- src/Protocol/Protocol17x.cpp | 7 +++++-- src/Protocol/Protocol18x.cpp | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 1e5fe5586..1e33ec433 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -1524,7 +1524,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size) AString PacketData; bb.ReadAll(PacketData); bb.ResetRead(); - bb.ReadVarInt(PacketType); + bb.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again. ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read PacketData.resize(PacketData.size() - 1); AString PacketDataHex; @@ -1753,7 +1753,10 @@ void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffe { return; } - a_ByteBuffer.ReadBEShort(EncNonceLength); + if (!a_ByteBuffer.ReadBEShort(EncNonceLength)) + { + return; + } AString EncNonce; if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength)) { diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index ce580d73e..1a13f4f7c 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -1723,7 +1723,11 @@ void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size) { // Decompress the data: AString CompressedData; - m_ReceivedData.ReadString(CompressedData, CompressedSize); + if (!m_ReceivedData.ReadString(CompressedData, CompressedSize)) + { + m_Client->Kick("Compression failure"); + return; + } InflateString(CompressedData.data(), CompressedSize, UncompressedData); PacketLen = UncompressedData.size(); } @@ -1765,7 +1769,7 @@ void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size) AString PacketData; bb.ReadAll(PacketData); bb.ResetRead(); - bb.ReadVarInt(PacketType); + bb.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read PacketData.resize(PacketData.size() - 1); AString PacketDataHex; -- cgit v1.2.3 From c9697083e57e7062a68f61b5516b73c412e83004 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 21 Dec 2014 21:56:34 +0100 Subject: cNoise3DComposable: Fixed unitialized member variables. Fixes CID 43665. --- src/Generating/Noise3DGenerator.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index b43a1a6de..29471f936 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -379,7 +379,9 @@ cNoise3DComposable::cNoise3DComposable(int a_Seed) : m_ChoiceNoise(a_Seed), m_DensityNoiseA(a_Seed + 1), m_DensityNoiseB(a_Seed + 2), - m_BaseNoise(a_Seed + 3) + m_BaseNoise(a_Seed + 3), + m_LastChunkX(0x7fffffff), // Use dummy coords that won't ever be used by real chunks + m_LastChunkZ(0x7fffffff) { } -- cgit v1.2.3 From fe00c99c9518be022f8faaa52899e68fc0738ce4 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 21 Dec 2014 21:59:44 +0100 Subject: cEndGen: Fixed unitialized member variables. Fixes CID 43671. --- src/Generating/EndGen.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp index 89d6117bb..bc26edb20 100644 --- a/src/Generating/EndGen.cpp +++ b/src/Generating/EndGen.cpp @@ -39,7 +39,9 @@ cEndGen::cEndGen(int a_Seed) : m_IslandSizeZ(256), m_FrequencyX(80), m_FrequencyY(80), - m_FrequencyZ(80) + m_FrequencyZ(80), + m_LastChunkX(0x7fffffff), // Use dummy coords that won't ever be used by real chunks + m_LastChunkZ(0x7fffffff) { m_Perlin.AddOctave(1, 1); m_Perlin.AddOctave(2, 0.5); -- cgit v1.2.3 From ecf778bbec2794562bf5e5b8645e2171f7cd081c Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 21 Dec 2014 22:19:22 +0100 Subject: cWorld: Moved initialization into constructor. Fixes CID 71781. --- src/World.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/World.cpp b/src/World.cpp index 69b39f831..cd17cde08 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -279,6 +279,8 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin m_WorldAge(0), m_TimeOfDay(0), m_LastTimeUpdate(0), + m_LastSave(0), + m_LastUnload(0), m_SkyDarkness(0), m_GameMode(gmNotSet), m_bEnabledPVP(false), @@ -620,9 +622,6 @@ void cWorld::Start(void) m_ChunkMap = make_unique(this); - m_LastSave = 0; - m_LastUnload = 0; - // preallocate some memory for ticking blocks so we don't need to allocate that often m_BlockTickQueue.reserve(1000); m_BlockTickQueueCopy.reserve(1000); -- cgit v1.2.3 From afdd53729c2a51a3720800e1b82aa5c40840dffe Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 21 Dec 2014 22:19:53 +0100 Subject: cChunk: Fixed missing initialization. Fixes CID 72670. --- src/Chunk.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 39e97f5cf..555e85015 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -73,6 +73,7 @@ cChunk::cChunk( cAllocationPool & a_Pool ) : m_Presence(cpInvalid), + m_ShouldGenerateIfLoadFailed(false), m_IsLightValid(false), m_IsDirty(false), m_IsSaving(false), @@ -93,6 +94,7 @@ cChunk::cChunk( m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData()), m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()), m_RedstoneSimulatorData(a_World->GetRedstoneSimulator()->CreateChunkData()), + m_IsRedstoneDirty(false), m_AlwaysTicked(0) { if (a_NeighborXM != nullptr) -- cgit v1.2.3 From e192da5316e5617a5fd597d4de9181a36b5e4c2e Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 21 Dec 2014 22:25:01 +0100 Subject: FastNBT: Added a sanity check for number of list items. Fixes CID 55812. --- src/WorldStorage/FastNBT.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp index ed8e8bb14..aaef2fdfe 100644 --- a/src/WorldStorage/FastNBT.cpp +++ b/src/WorldStorage/FastNBT.cpp @@ -10,6 +10,13 @@ +/** If a list being loaded has more than this number of items, it's considered corrupted. */ +static const int MAX_LIST_ITEMS = 10000; + + + + + // The number of NBT tags that are reserved when an NBT parsing is started. // You can override this by using a cmdline define #ifndef NBT_RESERVE_SIZE @@ -142,7 +149,7 @@ bool cParsedNBT::ReadList(eTagType a_ChildrenType) NEEDBYTES(4); int Count = GetBEInt(m_Data + m_Pos); m_Pos += 4; - if (Count < 0) + if ((Count < 0) || (Count > MAX_LIST_ITEMS)) { return false; } -- cgit v1.2.3 From f2327042037383abc2773710dac78af527bc5500 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 21 Dec 2014 22:37:48 +0100 Subject: WSSAnvil: Added clamping to entity coords. Fixes CID 72854. --- src/WorldStorage/WSSAnvil.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index cf51ab19c..a76e9461a 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -2945,9 +2945,9 @@ bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int { return false; } - a_X = a_NBT.GetInt(x); - a_Y = a_NBT.GetInt(y); - a_Z = a_NBT.GetInt(z); + a_X = Clamp(a_NBT.GetInt(x), -40000000, 40000000); // World is limited to 30M blocks in XZ, we clamp to 40M + a_Y = Clamp(a_NBT.GetInt(y), -10000, 10000); // Y is limited to 0 .. 255, we clamp to 10K + a_Z = Clamp(a_NBT.GetInt(z), -40000000, 40000000); return true; } -- cgit v1.2.3 From ae8c871565e7a13159eb2a0839053f1b5b6bcfc4 Mon Sep 17 00:00:00 2001 From: Jonathan Fabian Date: Sun, 21 Dec 2014 19:29:34 -0500 Subject: Added Depth Strider enchantment --- src/Enchantments.cpp | 15 +++++++++++++++ src/Enchantments.h | 1 + 2 files changed, 16 insertions(+) (limited to 'src') diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp index e72ec668a..5ed18de6b 100644 --- a/src/Enchantments.cpp +++ b/src/Enchantments.cpp @@ -183,6 +183,7 @@ int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName) { enchRespiration, "Respiration"}, { enchAquaAffinity, "AquaAffinity"}, { enchThorns, "Thorns"}, + { enchDepthStrider, "DepthStrider"}, { enchSharpness, "Sharpness"}, { enchSmite, "Smite"}, { enchBaneOfArthropods, "BaneOfArthropods"}, @@ -506,6 +507,20 @@ void cEnchantments::AddItemEnchantmentWeights(cWeightedEnchantments & a_Enchantm { AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 1); } + + // Depth Strider + if ((a_EnchantmentLevel >= 30) && (a_EnchantmentLevel <= 45)) + { + AddEnchantmentWeightToVector(a_Enchantments, 2, enchDepthStrider, 3); + } + else if ((a_EnchantmentLevel >= 20) && (a_EnchantmentLevel <= 35)) + { + AddEnchantmentWeightToVector(a_Enchantments, 2, enchDepthStrider, 2); + } + else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 25)) + { + AddEnchantmentWeightToVector(a_Enchantments, 2, enchDepthStrider, 1); + } } } diff --git a/src/Enchantments.h b/src/Enchantments.h index 31226b5c2..e8e84d43c 100644 --- a/src/Enchantments.h +++ b/src/Enchantments.h @@ -53,6 +53,7 @@ public: enchRespiration = 5, enchAquaAffinity = 6, enchThorns = 7, + enchDepthStrider = 8, enchSharpness = 16, enchSmite = 17, enchBaneOfArthropods = 18, -- cgit v1.2.3 From fbd0cf74bdc3a6c3a2524691da07e4ac4ad7940d Mon Sep 17 00:00:00 2001 From: Jonathan Fabian Date: Sun, 21 Dec 2014 23:02:02 -0500 Subject: Fix compile error on OS X introduced by commit ecf778bbec2794562bf5e5b8645e2171f7cd081c The following error occurs on OS X with the order reversed: `MCServer/src/World.cpp:282:2: error: field 'm_LastSave' will be initialized after field 'm_LastUnload' [-Werror,-Wreorder] m_LastSave(0),`. Reversing the order of initialization fixes this. --- src/World.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/World.cpp b/src/World.cpp index cd17cde08..ae739a2c3 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -279,8 +279,8 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin m_WorldAge(0), m_TimeOfDay(0), m_LastTimeUpdate(0), - m_LastSave(0), m_LastUnload(0), + m_LastSave(0), m_SkyDarkness(0), m_GameMode(gmNotSet), m_bEnabledPVP(false), -- cgit v1.2.3 From 9fde173142921339f6b65dccb8fe6b82427c4d04 Mon Sep 17 00:00:00 2001 From: worktycho Date: Tue, 23 Dec 2014 00:41:46 +0000 Subject: Init Mojang API pointer Fixes CID 90583 --- src/Root.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/Root.cpp b/src/Root.cpp index 9725502ee..8951bafe6 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -47,6 +47,7 @@ cRoot::cRoot(void) : m_FurnaceRecipe(nullptr), m_WebAdmin(nullptr), m_PluginManager(nullptr), + m_MojangAPI(nullptr), m_bStop(false), m_bRestart(false) { -- cgit v1.2.3 From 075b19c7cbf5d6e45dbd14e8fab5740d7000bf0f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 24 Dec 2014 06:43:28 +0100 Subject: Added Vector3::TurnCW() and Vector3::TurnCCW() --- src/Vector3.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src') diff --git a/src/Vector3.h b/src/Vector3.h index 1e4a1f5d9..1f3f6b955 100644 --- a/src/Vector3.h +++ b/src/Vector3.h @@ -307,6 +307,22 @@ public: return (a_X - x) / (a_OtherEnd.x - x); } + /** Rotates the vector 90 degrees clockwise around the vertical axis. + Note that this is specific to minecraft's axis ordering, which is X+ left, Z+ down. */ + inline void TurnCW(void) + { + std::swap(x, z); + x = -x; + } + + /** Rotates the vector 90 degrees counterclockwise around the vertical axis. + Note that this is specific to minecraft's axis ordering, which is X+ left, Z+ down. */ + inline void TurnCCW(void) + { + std::swap(x, z); + z = -z; + } + /** The max difference between two coords for which the coords are assumed equal. */ static const double EPS; -- cgit v1.2.3 From ccdf03daaf880dd0c89a03b50c11eb083ee1cfb0 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 24 Dec 2014 07:20:17 +0100 Subject: Refactored all player block placing to go through hooks. Fixes #1618. --- src/Bindings/Plugin.h | 4 +- src/Bindings/PluginLua.cpp | 18 ++- src/Bindings/PluginLua.h | 4 +- src/Bindings/PluginManager.cpp | 8 +- src/Bindings/PluginManager.h | 6 +- src/Blocks/BlockBed.cpp | 20 +--- src/Blocks/BlockBed.h | 1 - src/Blocks/BlockBigFlower.h | 12 -- src/Blocks/BlockChest.h | 65 ++-------- src/Blocks/BlockDoor.cpp | 40 ++----- src/Blocks/BlockDoor.h | 39 ++++-- src/Blocks/BlockHandler.cpp | 2 +- src/Blocks/BlockHandler.h | 21 ++-- src/Blocks/BlockMobHead.h | 204 +------------------------------- src/Blocks/BlockPumpkin.h | 64 ---------- src/Blocks/BlockSignPost.h | 11 -- src/Blocks/BlockWallSign.h | 11 -- src/Chunk.cpp | 16 +-- src/ChunkDef.h | 48 ++++++-- src/ChunkMap.cpp | 103 ++++++++++------ src/ChunkMap.h | 23 +++- src/ClientHandle.cpp | 158 ++----------------------- src/ClientHandle.h | 3 - src/Entities/Player.cpp | 96 +++++++++++++++ src/Entities/Player.h | 18 +++ src/Generating/StructGen.cpp | 14 +-- src/Generating/Trees.cpp | 14 +-- src/Items/CMakeLists.txt | 7 +- src/Items/ItemBed.h | 28 +++-- src/Items/ItemBigFlower.h | 56 +++++++++ src/Items/ItemChest.h | 167 ++++++++++++++++++++++++++ src/Items/ItemDoor.h | 96 +++++++++++---- src/Items/ItemDye.h | 25 ++-- src/Items/ItemHandler.cpp | 199 ++++++++++++++++++++++++------- src/Items/ItemHandler.h | 39 ++++-- src/Items/ItemMobHead.h | 261 +++++++++++++++++++++++++++++++++++++++++ src/Items/ItemPumpkin.h | 156 ++++++++++++++++++++++++ src/Items/ItemSign.h | 22 +++- src/Items/ItemSlab.h | 93 +++++++++++++++ src/Protocol/Protocol17x.cpp | 4 +- src/Protocol/Protocol18x.cpp | 4 +- src/World.cpp | 13 +- src/World.h | 5 + 43 files changed, 1423 insertions(+), 775 deletions(-) create mode 100644 src/Items/ItemBigFlower.h create mode 100644 src/Items/ItemChest.h create mode 100644 src/Items/ItemPumpkin.h create mode 100644 src/Items/ItemSlab.h (limited to 'src') diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 08677553c..b5944e9cb 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -76,8 +76,8 @@ public: virtual bool OnPlayerJoined (cPlayer & a_Player) = 0; virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0; virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0; - virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; - virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0; + virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0; virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) = 0; virtual bool OnPlayerShooting (cPlayer & a_Player) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index ea782ea3f..cc3146610 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -857,14 +857,19 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi -bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { cCSLock Lock(m_CriticalSection); bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + m_LuaState.Call((int)(**itr), &a_Player, + a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), + a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta, + cLuaState::Return, + res + ); if (res) { return true; @@ -877,14 +882,19 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_Blo -bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { cCSLock Lock(m_CriticalSection); bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + m_LuaState.Call((int)(**itr), &a_Player, + a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), + a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta, + cLuaState::Return, + res + ); if (res) { return true; diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index 7de5ffec4..ad3f82b42 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -100,8 +100,8 @@ public: virtual bool OnPlayerJoined (cPlayer & a_Player) override; virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override; virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override; - virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override; + virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override; virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override; virtual bool OnPlayerShooting (cPlayer & a_Player) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 406a540f4..fad0a36d2 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -866,14 +866,14 @@ bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d a_O -bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { FIND_HOOK(HOOK_PLAYER_PLACED_BLOCK); VERIFY_HOOK; for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) { - if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockChange)) { return true; } @@ -885,14 +885,14 @@ bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, -bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { FIND_HOOK(HOOK_PLAYER_PLACING_BLOCK); VERIFY_HOOK; for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) { - if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockChange)) { return true; } diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 3a2aecc92..d4b82376a 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -207,10 +207,10 @@ public: bool CallHookPlayerFishing (cPlayer & a_Player, cItems a_Reward); bool CallHookPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel); bool CallHookPlayerJoined (cPlayer & a_Player); - bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition); bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); - bool CallHookPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - bool CallHookPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition); + bool CallHookPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange); + bool CallHookPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange); bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); bool CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity); bool CallHookPlayerShooting (cPlayer & a_Player); diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp index 3b6328b38..57b9855d0 100644 --- a/src/Blocks/BlockBed.cpp +++ b/src/Blocks/BlockBed.cpp @@ -14,24 +14,6 @@ -void cBlockBedHandler::OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta -) -{ - if (a_BlockMeta < 8) - { - Vector3i Direction = MetaDataToDirection(a_BlockMeta); - a_ChunkInterface.SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8); - } -} - - - - - void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) { NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); @@ -151,7 +133,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface cPlayerBedStateUnsetter Unsetter(Vector3i(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z), a_WorldInterface); a_WorldInterface.ForEachPlayer(Unsetter); a_WorldInterface.SetTimeOfDay(0); - a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0xB); // Where 0xB = 1011, and zero is to make sure 'occupied' bit is always unset + a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x0b); // Clear the "occupied" bit of the bed's block } } } diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h index a8b5be899..5b746110a 100644 --- a/src/Blocks/BlockBed.h +++ b/src/Blocks/BlockBed.h @@ -23,7 +23,6 @@ public: } - virtual void OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override; virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; diff --git a/src/Blocks/BlockBigFlower.h b/src/Blocks/BlockBigFlower.h index 3577bdd40..5240ddf53 100644 --- a/src/Blocks/BlockBigFlower.h +++ b/src/Blocks/BlockBigFlower.h @@ -85,18 +85,6 @@ public: } - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - int Meta = (((int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 0.5) & 0x3) + 2) % 4; - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, 0x8 | Meta); - } - - virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override { NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h index 201f2309b..01fec7f8b 100644 --- a/src/Blocks/BlockChest.h +++ b/src/Blocks/BlockChest.h @@ -62,50 +62,11 @@ public: } // Single chest, get meta from rotation only - a_BlockMeta = RotationToMetaData(yaw); + a_BlockMeta = PlayerYawToMetaData(yaw); return true; } - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - // Check if this forms a doublechest, if so, need to adjust the meta: - cBlockArea Area; - if (!Area.Read(&a_ChunkInterface, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) - { - return; - } - - double rot = a_Player->GetYaw(); // FIXME: Rename rot to yaw - // Choose meta from player rotation, choose only between 2 or 3 - NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; - if ( - CheckAndAdjustNeighbor(a_ChunkInterface, Area, 0, 1, NewMeta) || - CheckAndAdjustNeighbor(a_ChunkInterface, Area, 2, 1, NewMeta) - ) - { - // Forming a double chest in the X direction - return; - } - // Choose meta from player rotation, choose only between 4 or 5 - NewMeta = (rot < 0) ? 4 : 5; - if ( - CheckAndAdjustNeighbor(a_ChunkInterface, Area, 1, 0, NewMeta) || - CheckAndAdjustNeighbor(a_ChunkInterface, Area, 2, 2, NewMeta) - ) - { - // Forming a double chest in the Z direction - return; - } - - // Single chest, no further processing needed - } - virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; @@ -180,30 +141,30 @@ public: } - /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only - static NIBBLETYPE RotationToMetaData(double a_Rotation) + /** Translates player yaw when placing a chest into the chest block metadata. Valid for single chests only */ + static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) { - a_Rotation += 90 + 45; // So its not aligned with axis + a_Yaw += 90 + 45; // So its not aligned with axis - if (a_Rotation > 360.f) + if (a_Yaw > 360.f) { - a_Rotation -= 360.f; + a_Yaw -= 360.f; } - if ((a_Rotation >= 0.f) && (a_Rotation < 90.f)) + if ((a_Yaw >= 0.f) && (a_Yaw < 90.f)) { - return 0x4; + return 0x04; } - else if ((a_Rotation >= 180) && (a_Rotation < 270)) + else if ((a_Yaw >= 180) && (a_Yaw < 270)) { - return 0x5; + return 0x05; } - else if ((a_Rotation >= 90) && (a_Rotation < 180)) + else if ((a_Yaw >= 90) && (a_Yaw < 180)) { - return 0x2; + return 0x02; } else { - return 0x3; + return 0x03; } } diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp index 90b7b15c2..d2bf180be 100644 --- a/src/Blocks/BlockDoor.cpp +++ b/src/Blocks/BlockDoor.cpp @@ -23,7 +23,7 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn if (OldMeta & 8) { // Was upper part of door - if (IsDoor(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + if (IsDoorBlockType(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) { a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); } @@ -31,7 +31,7 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn else { // Was lower part - if (IsDoor(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) + if (IsDoorBlockType(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) { a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0); } @@ -84,52 +84,34 @@ void cBlockDoorHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, c -void cBlockDoorHandler::OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta -) -{ - NIBBLETYPE a_TopBlockMeta = 8; - if ( - ((a_BlockMeta == 0) && (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType)) || - ((a_BlockMeta == 1) && (a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType)) || - ((a_BlockMeta == 2) && (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType)) || - ((a_BlockMeta == 3) && (a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType)) - ) - { - a_TopBlockMeta = 9; - } - a_ChunkInterface.SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta); -} - - - - - NIBBLETYPE cBlockDoorHandler::MetaRotateCCW(NIBBLETYPE a_Meta) { if (a_Meta & 0x08) { + // The meta doesn't change for the top block return a_Meta; } else { + // Rotate the bottom block return super::MetaRotateCCW(a_Meta); } } + + NIBBLETYPE cBlockDoorHandler::MetaRotateCW(NIBBLETYPE a_Meta) { if (a_Meta & 0x08) { + // The meta doesn't change for the top block return a_Meta; } else { + // Rotate the bottom block return super::MetaRotateCW(a_Meta); } } @@ -138,8 +120,10 @@ NIBBLETYPE cBlockDoorHandler::MetaRotateCW(NIBBLETYPE a_Meta) NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta) { - // Top bit (0x08) contains door panel type (Top/Bottom panel) Only Bottom panels contain position data - // Return a_Meta if panel is a top panel (0x08 bit is set to 1) + /* + Top bit (0x08) contains door block position (Top / Bottom). Only Bottom blocks contain position data + Return a_Meta if panel is a top panel (0x08 bit is set to 1) + */ // Note: Currently, you can not properly mirror the hinges on a double door. The orientation of the door is stored // in only the bottom tile while the hinge position is in the top tile. This function only operates on one tile at a time, diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h index 92ad8da12..334519077 100644 --- a/src/Blocks/BlockDoor.h +++ b/src/Blocks/BlockDoor.h @@ -101,14 +101,6 @@ public: } - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override; - - virtual bool IsUseable(void) override { return true; @@ -117,11 +109,19 @@ public: virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + return ((a_RelY > 0) && CanBeOn(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))); + } + + + /** Returns true if door can be placed on the specified block type. */ + static bool CanBeOn(BLOCKTYPE a_BlockType) + { + // Vanilla refuses to place doors on transparent blocks + return !cBlockInfo::IsTransparent(a_BlockType); } - bool CanReplaceBlock(BLOCKTYPE a_BlockType) + static bool CanReplaceBlock(BLOCKTYPE a_BlockType) { switch (a_BlockType) { @@ -170,8 +170,21 @@ public: } + /** Returns a vector pointing one block in the direction the door is facing (where the outside is). */ + inline static Vector3i GetRelativeDirectionToOutside(NIBBLETYPE a_BlockMeta) + { + switch (a_BlockMeta & 0x03) + { + case 0: return Vector3i(-1, 0, 0); // Facing West / XM + case 1: return Vector3i( 0, 0, -1); // Facing North / ZM + case 2: return Vector3i( 1, 0, 0); // Facing East / XP + default: return Vector3i( 0, 0, 1); // Facing South / ZP + } + } + + /** Returns true if the specified blocktype is any kind of door */ - inline static bool IsDoor(BLOCKTYPE a_Block) + inline static bool IsDoorBlockType(BLOCKTYPE a_Block) { switch (a_Block) { @@ -193,6 +206,8 @@ public: } + /** Returns true iff the door at the specified coords is open. + The coords may point to either the top part or the bottom part of the door. */ static NIBBLETYPE IsOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) { NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); @@ -237,7 +252,7 @@ public: static void SetOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open) { BLOCKTYPE Block = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ); - if (!IsDoor(Block)) + if (!IsDoorBlockType(Block)) { return; } diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index d532aa1dc..2de4a3e4c 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -369,7 +369,7 @@ void cBlockHandler::OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface -void cBlockHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +void cBlockHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange) { } diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h index f2298afb5..4dec0dc95 100644 --- a/src/Blocks/BlockHandler.h +++ b/src/Blocks/BlockHandler.h @@ -42,15 +42,12 @@ public: BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta ); - /// Called by cWorld::SetBlock() after the block has been set + /** Called by cWorld::SetBlock() after the block has been set */ virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced(). + /** Called by cPlayer::PlaceBlocks() for each block after it has been set to the world. Called after OnPlaced(). */ virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange ); /// Called before the player has destroyed a block @@ -96,7 +93,8 @@ public: */ // virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); - /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called + /** Called to check whether this block supports a rclk action. + If it returns true, OnUse() is called */ virtual bool IsUseable(void); /** Indicates whether the client will click through this block. @@ -109,20 +107,21 @@ public: */ virtual bool DoesIgnoreBuildCollision(void); - /// Similar to DoesIgnoreBuildCollision(void), but is used for cases where block meta/player item-in-hand is needed to determine collision (thin snow) + /** Similar to DoesIgnoreBuildCollision(void), but is used for cases where block's meta or + player's item-in-hand is needed to determine collision (thin snow) */ virtual bool DoesIgnoreBuildCollision(cPlayer *, NIBBLETYPE a_Meta) { UNUSED(a_Meta); return DoesIgnoreBuildCollision(); } - /// Returns if this block drops if it gets destroyed by an unsuitable situation. Default: true + /** Returns if this block drops if it gets destroyed by an unsuitable situation. + Default: true */ virtual bool DoesDropOnUnsuitable(void); /** Called when one of the neighbors gets set; equivalent to MC block update. By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()), - and wakes up all simulators on the block. - */ + and wakes up all simulators on the block. */ virtual void Check(cChunkInterface & ChunkInterface, cBlockPluginInterface & a_PluginInterface, int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk); /// Rotates a given block meta counter-clockwise. Default: no change diff --git a/src/Blocks/BlockMobHead.h b/src/Blocks/BlockMobHead.h index e21e42334..cb8143749 100644 --- a/src/Blocks/BlockMobHead.h +++ b/src/Blocks/BlockMobHead.h @@ -12,16 +12,18 @@ class cBlockMobHeadHandler : public cBlockEntityHandler { public: - cBlockMobHeadHandler(BLOCKTYPE a_BlockType) - : cBlockEntityHandler(a_BlockType) + cBlockMobHeadHandler(BLOCKTYPE a_BlockType): + cBlockEntityHandler(a_BlockType) { } + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { - // The drop spawn is in OnDestroyed method + // The drop spawn is in the OnDestroyedByPlayer method } + virtual void OnDestroyedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override { if (a_Player->IsGameModeCreative()) @@ -61,202 +63,6 @@ public: a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback); } - - bool TrySpawnWither(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) - { - if (a_BlockY < 2) - { - return false; - } - - class cCallback : public cBlockEntityCallback - { - bool m_IsWither; - - virtual bool Item(cBlockEntity * a_BlockEntity) - { - if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD) - { - return false; - } - cMobHeadEntity * MobHeadEntity = static_cast(a_BlockEntity); - - m_IsWither = (MobHeadEntity->GetType() == SKULL_TYPE_WITHER); - return false; - } - - public: - cCallback () : m_IsWither(false) {} - - bool IsWither(void) const { return m_IsWither; } - - void Reset(void) { m_IsWither = false; } - - } CallbackA, CallbackB; - - class cPlayerCallback : public cPlayerListCallback - { - Vector3f m_Pos; - - virtual bool Item(cPlayer * a_Player) - { - // TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one - double Dist = (a_Player->GetPosition() - m_Pos).Length(); - if (Dist < 50.0) - { - // If player is close, award achievement - a_Player->AwardAchievement(achSpawnWither); - } - return false; - } - - public: - cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {} - - } PlayerCallback(Vector3f((float)a_BlockX, (float)a_BlockY, (float)a_BlockZ)); - - a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, CallbackA); - - if (!CallbackA.IsWither()) - { - return false; - } - - CallbackA.Reset(); - - BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); - - if ((BlockY1 != E_BLOCK_SOULSAND) || (BlockY2 != E_BLOCK_SOULSAND)) - { - return false; - } - - a_WorldInterface.DoWithBlockEntityAt(a_BlockX - 1, a_BlockY, a_BlockZ, CallbackA); - a_WorldInterface.DoWithBlockEntityAt(a_BlockX + 1, a_BlockY, a_BlockZ, CallbackB); - - BLOCKTYPE Block1 = a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ); - BLOCKTYPE Block2 = a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ); - - if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither()) - { - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - - // Block entities - a_ChunkInterface.SetBlock(a_BlockX + 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.SetBlock(a_BlockX - 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - - // Spawn the wither: - a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtWither); - - // Award Achievement - a_WorldInterface.ForEachPlayer(PlayerCallback); - - return true; - } - - CallbackA.Reset(); - CallbackB.Reset(); - - a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ - 1, CallbackA); - a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ + 1, CallbackB); - - Block1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1); - Block2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1); - - if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither()) - { - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - - // Block entities - a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ + 1, E_BLOCK_AIR, 0); - a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ - 1, E_BLOCK_AIR, 0); - - // Spawn the wither: - a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtWither); - - // Award Achievement - a_WorldInterface.ForEachPlayer(PlayerCallback); - - return true; - } - - return false; - } - - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - class cCallback : public cBlockEntityCallback - { - cPlayer * m_Player; - NIBBLETYPE m_OldBlockMeta; - NIBBLETYPE m_NewBlockMeta; - - virtual bool Item(cBlockEntity * a_BlockEntity) - { - if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD) - { - return false; - } - cMobHeadEntity * MobHeadEntity = static_cast(a_BlockEntity); - - int Rotation = 0; - if (m_NewBlockMeta == 1) - { - Rotation = (int) floor(m_Player->GetYaw() * 16.0F / 360.0F + 0.5) & 0xF; - } - - MobHeadEntity->SetType(static_cast(m_OldBlockMeta)); - MobHeadEntity->SetRotation(static_cast(Rotation)); - MobHeadEntity->GetWorld()->BroadcastBlockEntity(MobHeadEntity->GetPosX(), MobHeadEntity->GetPosY(), MobHeadEntity->GetPosZ()); - return false; - } - - public: - cCallback (cPlayer * a_CBPlayer, NIBBLETYPE a_OldBlockMeta, NIBBLETYPE a_NewBlockMeta) : - m_Player(a_CBPlayer), - m_OldBlockMeta(a_OldBlockMeta), - m_NewBlockMeta(a_NewBlockMeta) - {} - }; - cCallback Callback(a_Player, a_BlockMeta, static_cast(a_BlockFace)); - - a_BlockMeta = (NIBBLETYPE)a_BlockFace; - a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback); - a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta); - - if (a_BlockMeta == SKULL_TYPE_WITHER) - { - static const Vector3i Coords[] = - { - Vector3i( 0, 0, 0), - Vector3i( 1, 0, 0), - Vector3i(-1, 0, 0), - Vector3i( 0, 0, 1), - Vector3i( 0, 0, -1), - }; - for (size_t i = 0; i < ARRAYCOUNT(Coords); ++i) - { - if (TrySpawnWither(a_ChunkInterface, a_WorldInterface, a_BlockX + Coords[i].x, a_BlockY, a_BlockZ + Coords[i].z)) - { - break; - } - } // for i - Coords[] - } - } } ; diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 275d1422a..af00fbe8e 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -17,70 +17,6 @@ public: } - virtual void OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override - { - // Check whether the pumpkin is a part of a golem or a snowman - - if (a_BlockY < 2) - { - // The pumpkin is too low for a golem / snowman - return; - } - - BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); - - // Check for a snow golem: - if ((BlockY1 == E_BLOCK_SNOW_BLOCK) && (BlockY2 == E_BLOCK_SNOW_BLOCK)) - { - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtSnowGolem); - return; - } - - // Check for an iron golem. First check only the body and legs, since those are the same for both orientations: - if ((BlockY1 != E_BLOCK_IRON_BLOCK) || (BlockY2 != E_BLOCK_IRON_BLOCK)) - { - // One of the blocks is not an iron, no chance of a golem here - return; - } - - // Now check both orientations for hands: - if ( - (a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && - (a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) - ) - { - // Remove the iron blocks: - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - - // Spawn the golem: - a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtIronGolem); - } - else if ( - (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && - (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) - ) - { - // Remove the iron blocks: - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - - // Spawn the golem: - a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtIronGolem); - } - } - - virtual bool GetPlacementBlockTypeMeta( cChunkInterface & a_ChunkInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, diff --git a/src/Blocks/BlockSignPost.h b/src/Blocks/BlockSignPost.h index d97501651..99c000633 100644 --- a/src/Blocks/BlockSignPost.h +++ b/src/Blocks/BlockSignPost.h @@ -53,17 +53,6 @@ public: } - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); - } - - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override { return (a_Meta + 4) & 0x0f; diff --git a/src/Blocks/BlockWallSign.h b/src/Blocks/BlockWallSign.h index 0abe9c52c..b6599d033 100644 --- a/src/Blocks/BlockWallSign.h +++ b/src/Blocks/BlockWallSign.h @@ -27,17 +27,6 @@ public: } - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); - } - - virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { int BlockX = (a_Chunk.GetPosX() * cChunkDef::Width) + a_RelX; diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 555e85015..b06eed316 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -49,14 +49,14 @@ //////////////////////////////////////////////////////////////////////////////// // sSetBlock: -sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) // absolute block position - : x( a_BlockX) - , y( a_BlockY) - , z( a_BlockZ) - , BlockType( a_BlockType) - , BlockMeta( a_BlockMeta) -{ - cChunkDef::AbsoluteToRelative(x, y, z, ChunkX, ChunkZ); +sSetBlock::sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta): + m_RelX(a_BlockX), + m_RelY(a_BlockY), + m_RelZ(a_BlockZ), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta) +{ + cChunkDef::AbsoluteToRelative(m_RelX, m_RelY, m_RelZ, m_ChunkX, m_ChunkZ); } diff --git a/src/ChunkDef.h b/src/ChunkDef.h index 8f1d416ad..b62656a58 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -347,18 +347,32 @@ public: struct sSetBlock { - int x, y, z; - int ChunkX, ChunkZ; - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - - sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // absolute block position - sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : - x(a_X), y(a_Y), z(a_Z), - ChunkX(a_ChunkX), ChunkZ(a_ChunkZ), - BlockType(a_BlockType), - BlockMeta(a_BlockMeta) - {} + int m_RelX, m_RelY, m_RelZ; + int m_ChunkX, m_ChunkZ; + BLOCKTYPE m_BlockType; + NIBBLETYPE m_BlockMeta; + + sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + sSetBlock(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : + m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ), + m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta) + { + ASSERT((a_RelX >= 0) && (a_RelX < cChunkDef::Width)); + ASSERT((a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)); + } + + /** Returns the absolute X coord of the stored block. */ + int GetX(void) const { return m_RelX + cChunkDef::Width * m_ChunkX; } + + /** Returns the absolute Y coord of the stored block. + Is the same as relative Y coords, because there's no Y relativization. */ + int GetY(void) const { return m_RelY; } + + /** Returns the absolute Z coord of the stored block. */ + int GetZ(void) const { return m_RelZ + cChunkDef::Width * m_ChunkZ; } }; typedef std::list sSetBlockList; @@ -385,6 +399,16 @@ public: typedef std::list cChunkCoordsList; typedef std::vector cChunkCoordsVector; +/** A simple hash function for chunk coords, we assume that chunk coords won't use more than 16 bits, so the hash is almost an identity. +Used for std::unordered_map */ +template<> struct std::hash +{ + size_t operator ()(const cChunkCoords & a_Coords) + { + return (static_cast(a_Coords.m_ChunkX) << 16) ^ static_cast(a_Coords.m_ChunkZ); + } +}; + diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 6aff3a754..c8f5aa673 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -1101,17 +1101,17 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) // Process all items from a_BlockList, either successfully or by placing into Failed while (!a_BlockList.empty()) { - int ChunkX = a_BlockList.front().ChunkX; - int ChunkZ = a_BlockList.front().ChunkZ; + int ChunkX = a_BlockList.front().m_ChunkX; + int ChunkZ = a_BlockList.front().m_ChunkZ; cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk != nullptr) && Chunk->IsValid()) { for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) { - if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ)) { - Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + Chunk->FastSetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); itr = a_BlockList.erase(itr); } else @@ -1125,7 +1125,7 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) // The chunk is not valid, move all blocks within this chunk to Failed for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) { - if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ)) { Failed.push_back(*itr); itr = a_BlockList.erase(itr); @@ -1146,6 +1146,34 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) +void cChunkMap::SetBlocks(const sSetBlockVector & a_Blocks) +{ + cCSLock lock(m_CSLayers); + cChunkPtr chunk = nullptr; + int lastChunkX = 0x7fffffff; // Bogus coords so that chunk is updated on first pass + int lastChunkZ = 0x7fffffff; + for (auto block: a_Blocks) + { + // Update the chunk, if different from last time: + if ((block.m_ChunkX != lastChunkX) || (block.m_ChunkZ != lastChunkZ)) + { + lastChunkX = block.m_ChunkX; + lastChunkZ = block.m_ChunkZ; + chunk = GetChunk(lastChunkX, lastChunkZ); + } + + // If the chunk is valid, set the block: + if (chunk != nullptr) + { + chunk->SetBlock(block.m_RelX, block.m_RelY, block.m_RelZ, block.m_BlockType, block.m_BlockMeta); + } + } // for block - a_Blocks[] +} + + + + + void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player) { int BlockX = (int)(a_Player.GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway @@ -1175,28 +1203,28 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player) BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) { + int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); + // First check if it isn't queued in the m_FastSetBlockQueue: { - int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); cCSLock Lock(m_CSFastSetBlock); for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) { - if ((itr->x == X) && (itr->y == Y) && (itr->z == Z) && (itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + if ((itr->m_RelX == X) && (itr->m_RelY == Y) && (itr->m_RelZ == Z) && (itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ)) { - return itr->BlockType; + return itr->m_BlockType; } } // for itr - m_FastSetBlockQueue[] } - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - + + // Not in the queue, query the chunk, if loaded: cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if ((Chunk != nullptr) && Chunk->IsValid()) { - return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + return Chunk->GetBlock(X, Y, Z); } return 0; } @@ -1207,25 +1235,28 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) { + int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); + // First check if it isn't queued in the m_FastSetBlockQueue: { cCSLock Lock(m_CSFastSetBlock); for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) { - if ((itr->x == a_BlockX) && (itr->y == a_BlockY) && (itr->z == a_BlockZ)) + if ((itr->m_RelX == X) && (itr->m_RelY == Y) && (itr->m_RelZ == Z) && (itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ)) { - return itr->BlockMeta; + return itr->m_BlockMeta; } } // for itr - m_FastSetBlockQueue[] } - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - + + // Not in the queue, query the chunk, if loaded: cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if ((Chunk != nullptr) && Chunk->IsValid()) { - return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ); + return Chunk->GetMeta(X, Y, Z); } return 0; } @@ -1373,14 +1404,14 @@ void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_Filt cCSLock Lock(m_CSLayers); for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) { - cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ); + cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ); if ((Chunk == nullptr) || !Chunk->IsValid()) { continue; } - if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType) + if (Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ) == a_FilterBlockType) { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); } } } @@ -1394,24 +1425,24 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks) cCSLock Lock(m_CSLayers); for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) { - cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ); + cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ); if ((Chunk == nullptr) || !Chunk->IsValid()) { continue; } - switch (Chunk->GetBlock(itr->x, itr->y, itr->z)) + switch (Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ)) { CASE_TREE_OVERWRITTEN_BLOCKS: { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); break; } case E_BLOCK_LEAVES: case E_BLOCK_NEW_LEAVES: { - if ((itr->BlockType == E_BLOCK_LOG) || (itr->BlockType == E_BLOCK_NEW_LOG)) + if ((itr->m_BlockType == E_BLOCK_LOG) || (itr->m_BlockType == E_BLOCK_NEW_LOG)) { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); } break; } @@ -1507,7 +1538,7 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) cCSLock Lock(m_CSLayers); for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) { - cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ); + cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ); if ((Chunk == nullptr) || !Chunk->IsValid()) { if (!a_ContinueOnFailure) @@ -1517,8 +1548,8 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) res = false; continue; } - itr->BlockType = Chunk->GetBlock(itr->x, itr->y, itr->z); - itr->BlockMeta = Chunk->GetMeta(itr->x, itr->y, itr->z); + itr->m_BlockType = Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ); + itr->m_BlockMeta = Chunk->GetMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ); } return res; } @@ -1527,11 +1558,11 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) -bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z) +bool cChunkMap::DigBlock(int a_BlockX, int a_BlockY, int a_BlockZ) { - int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ; + int PosX = a_BlockX, PosY = a_BlockY, PosZ = a_BlockZ, ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ); + cChunkDef::AbsoluteToRelative(PosX, PosY, PosZ, ChunkX, ChunkZ); { cCSLock Lock(m_CSLayers); @@ -1542,7 +1573,7 @@ bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z) } DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0); - m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z, DestChunk); + m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, DestChunk); } return true; diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 6a858b06d..fe0bb3733 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -143,7 +143,13 @@ public: void FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); void FastSetQueuedBlocks(); - void FastSetBlocks (sSetBlockList & a_BlockList); + void FastSetBlocks(sSetBlockList & a_BlockList); + + /** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item. + Is more efficient than calling SetBlock() multiple times. + If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */ + void SetBlocks(const sSetBlockVector & a_Blocks); + void CollectPickupsByPlayer(cPlayer & a_Player); BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ); @@ -173,11 +179,20 @@ public: (Re)sends the chunks to their relevant clients if successful. */ bool SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMCSBiome a_Biome); - /** Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. */ + /** Retrieves block types and metas of the specified blocks. + If a chunk is not loaded, doesn't modify the block and consults a_ContinueOnFailure whether to process the rest of the array. + Returns true if all blocks were read, false if any one failed. */ bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); - bool DigBlock (int a_X, int a_Y, int a_Z); - void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player); + /** Removes the block at the specified coords and wakes up simulators. + Returns false if the chunk is not loaded (and the block is not dug). + Returns true if successful. */ + bool DigBlock(int a_BlockX, int a_BlockY, int a_BlockZ); + + /** Sends the block at the specified coords to the specified player. + Uses a blockchange packet to send the block. + If the relevant chunk isn't loaded, doesn't do anything. */ + void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer * a_Player); /** Compares clients of two chunks, calls the callback accordingly */ void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback); diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index cb9d34c84..6fe6e99fa 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1346,12 +1346,19 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e if (ItemHandler->IsPlaceable() && (a_BlockFace != BLOCK_FACE_NONE)) { - HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); + if (!ItemHandler->OnPlayerPlace(*World, *m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + // Placement failed, bail out + return; + } } else if ((ItemHandler->IsFood() || ItemHandler->IsDrinkable(EquippedDamage))) { - if ((m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && - ItemHandler->IsFood() && (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE)) + if ( + (m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && // Only creative or hungry players can eat + ItemHandler->IsFood() && + (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE) // Golden apple is a special case, it is used instead of eaten + ) { // The player is satiated or in creative, and trying to eat return; @@ -1379,151 +1386,6 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e -void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler) -{ - BLOCKTYPE EquippedBlock = (BLOCKTYPE)(m_Player->GetEquippedItem().m_ItemType); - if (a_BlockFace < 0) - { - // Clicked in air - return; - } - - cWorld * World = m_Player->GetWorld(); - - BLOCKTYPE ClickedBlock; - NIBBLETYPE ClickedBlockMeta; - NIBBLETYPE EquippedBlockDamage = (NIBBLETYPE)(m_Player->GetEquippedItem().m_ItemDamage); - - if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) - { - // The block is being placed outside the world, ignore this packet altogether (#128) - return; - } - - World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); - - // Special slab handling - placing a slab onto another slab produces a dblslab instead: - if ( - cBlockSlabHandler::IsAnySlabType(ClickedBlock) && // Is there a slab already? - cBlockSlabHandler::IsAnySlabType(EquippedBlock) && // Is the player placing another slab? - ((ClickedBlockMeta & 0x07) == EquippedBlockDamage) && // Is it the same slab type? - ( - (a_BlockFace == BLOCK_FACE_TOP) || // Clicking the top of a bottom slab - (a_BlockFace == BLOCK_FACE_BOTTOM) // Clicking the bottom of a top slab - ) - ) - { - // Coordinates at clicked block, which was an eligible slab, and either top or bottom faces were clicked - // If clicked top face and slab occupies the top voxel, we want a slab to be placed above it (therefore increment Y) - // Else if clicked bottom face and slab occupies the bottom voxel, decrement Y for the same reason - // Don't touch coordinates if anything else because a dblslab opportunity is present - if ((ClickedBlockMeta & 0x08) && (a_BlockFace == BLOCK_FACE_TOP)) - { - ++a_BlockY; - } - else if (!(ClickedBlockMeta & 0x08) && (a_BlockFace == BLOCK_FACE_BOTTOM)) - { - --a_BlockY; - } - World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); - } - else - { - // Check if the block ignores build collision (water, grass etc.): - if ( - BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() || - BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(m_Player, ClickedBlockMeta) - ) - { - cChunkInterface ChunkInterface(World->GetChunkMap()); - BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ); - } - else - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - - if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) - { - // The block is being placed outside the world, ignore this packet altogether (#128) - return; - } - - NIBBLETYPE PlaceMeta; - BLOCKTYPE PlaceBlock; - World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta); - - // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. - // No need to do combinability (dblslab) checks, client will do that here. - if (cBlockSlabHandler::IsAnySlabType(PlaceBlock)) - { - // It's a slab, don't do checks and proceed to double-slabbing - } - else - { - if ( - !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() && - !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(m_Player, PlaceMeta) - ) - { - // Tried to place a block *into* another? - // Happens when you place a block aiming at side of block with a torch on it or stem beside it - return; - } - } - } - } - - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!a_ItemHandler.GetPlacementBlockTypeMeta(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) - { - // Handler refused the placement, send that information back to the client: - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - m_Player->GetInventory().SendEquippedSlot(); - return; - } - - cBlockHandler * NewBlock = BlockHandler(BlockType); - - if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) - { - // A plugin doesn't agree with placing the block, revert the block on the client: - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - m_Player->GetInventory().SendEquippedSlot(); - return; - } - - // The actual block placement: - World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - if (!m_Player->IsGameModeCreative()) - { - m_Player->GetInventory().RemoveOneEquippedItem(); - } - - cChunkInterface ChunkInterface(World->GetChunkMap()); - NewBlock->OnPlacedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); - - AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType); - float Volume = 1.0f, Pitch = 0.8f; - if (PlaceSound == "dig.metal") - { - Pitch = 1.2f; - PlaceSound = "dig.stone"; - } - else if (PlaceSound == "random.anvil_land") - { - Volume = 0.65f; - } - - World->BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); - - cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); -} - - - - - void cClientHandle::HandleChat(const AString & a_Message) { // We no longer need to postpone message processing, because the messages already arrive in the Tick thread diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 25dd250d9..5e10bb52f 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -459,9 +459,6 @@ private: UInt32 m_ProtocolVersion; - /** Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) */ - void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler); - /** Returns true if the rate block interactions is within a reasonable limit (bot protection) */ bool CheckBlockInteractionsRate(void); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 15920d6cf..7bdd1c6f7 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -2,6 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Player.h" +#include #include "../ChatColor.h" #include "../Server.h" #include "../UI/Window.h" @@ -19,6 +20,10 @@ #include "../WorldStorage/StatSerializer.h" #include "../CompositeChat.h" +#include "../Blocks/BlockHandler.h" +#include "../Blocks/BlockSlab.h" +#include "../Blocks/ChunkInterface.h" + #include "../IniFile.h" #include "json/json.h" @@ -2168,6 +2173,97 @@ void cPlayer::LoadRank(void) +bool cPlayer::PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + sSetBlockVector blk{{a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta}}; + return PlaceBlocks(blk); +} + + + + + +void cPlayer::SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range) +{ + // Collect the coords of all the blocks to send: + sSetBlockVector blks; + for (int y = a_BlockY - a_Range + 1; y < a_BlockY + a_Range; y++) + { + for (int z = a_BlockZ - a_Range + 1; z < a_BlockZ + a_Range; z++) + { + for (int x = a_BlockX - a_Range + 1; x < a_BlockX + a_Range; x++) + { + blks.emplace_back(x, y, z, E_BLOCK_AIR, 0); // Use fake blocktype, it will get set later on. + }; + }; + } // for y + + // Get the values of all the blocks: + if (!m_World->GetBlocks(blks, false)) + { + LOGD("%s: Cannot query all blocks, not sending an update", __FUNCTION__); + return; + } + + // Divide the block changes by their respective chunks: + std::unordered_map Changes; + for (const auto & blk: blks) + { + Changes[cChunkCoords(blk.m_ChunkX, blk.m_ChunkZ)].push_back(blk); + } // for blk - blks[] + blks.clear(); + + // Send the blocks for each affected chunk: + for (auto itr = Changes.cbegin(), end = Changes.cend(); itr != end; ++itr) + { + m_ClientHandle->SendBlockChanges(itr->first.m_ChunkX, itr->first.m_ChunkZ, itr->second); + } +} + + + + + +bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks) +{ + // Call the "placing" hooks; if any fail, abort: + cPluginManager * pm = cPluginManager::Get(); + for (auto blk: a_Blocks) + { + if (pm->CallHookPlayerPlacingBlock(*this, blk)) + { + // Abort - re-send all the current blocks in the a_Blocks' coords to the client: + for (auto blk2: a_Blocks) + { + m_World->SendBlockTo(blk2.GetX(), blk2.GetY(), blk2.GetZ(), this); + } + return false; + } + } // for blk - a_Blocks[] + + // Set the blocks: + m_World->SetBlocks(a_Blocks); + + // Notify the blockhandlers: + cChunkInterface ChunkInterface(m_World->GetChunkMap()); + for (auto blk: a_Blocks) + { + cBlockHandler * newBlock = BlockHandler(blk.m_BlockType); + newBlock->OnPlacedByPlayer(ChunkInterface, *m_World, this, blk); + } + + // Call the "placed" hooks: + for (auto blk: a_Blocks) + { + pm->CallHookPlayerPlacedBlock(*this, blk); + } + return true; +} + + + + + void cPlayer::Detach() { super::Detach(); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index c643aaa8e..33ab5293c 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -440,8 +440,26 @@ public: Loads the m_Rank, m_Permissions, m_MsgPrefix, m_MsgSuffix and m_MsgNameColorCode members. */ void LoadRank(void); + /** Calls the block-placement hook and places the block in the world, unless refused by the hook. + If the hook prevents the placement, sends the current block at the specified coords back to the client. + Assumes that all the blocks are in currently loaded chunks. */ + bool PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** Sends the block in the specified range around the specified coord to the client + as a block change packet. + The blocks in range (a_BlockX - a_Range, a_BlockX + a_Range) are sent (NY-metric). */ + void SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range = 1); + // tolua_end + /** Calls the block placement hooks and places the blocks in the world. + First the "placing" hooks for all the blocks are called, then the blocks are placed, and finally + the "placed" hooks are called. + If the any of the "placing" hooks aborts, none of the blocks are placed and the function returns false. + Returns true if all the blocks are placed. + Assumes that all the blocks are in currently loaded chunks. */ + bool PlaceBlocks(const sSetBlockVector & a_Blocks); + // cEntity overrides: virtual bool IsCrouched (void) const { return m_IsCrouched; } virtual bool IsSprinting(void) const { return m_IsSprinting; } diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp index 2f685c808..2d5a73739 100644 --- a/src/Generating/StructGen.cpp +++ b/src/Generating/StructGen.cpp @@ -123,18 +123,18 @@ void cStructGenTrees::GenerateSingleTree( // Check if the generated image fits the terrain. Only the logs are checked: for (sSetBlockVector::const_iterator itr = TreeLogs.begin(); itr != TreeLogs.end(); ++itr) { - if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ)) + if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkZ != a_ChunkZ)) { // Outside the chunk continue; } - if (itr->y >= cChunkDef::Height) + if (itr->m_RelY >= cChunkDef::Height) { // Above the chunk, cut off (this shouldn't happen too often, we're limiting trees to y < 230) continue; } - BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z); + BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->m_RelX, itr->m_RelY, itr->m_RelZ); switch (Block) { CASE_TREE_ALLOWED_BLOCKS: @@ -167,14 +167,14 @@ void cStructGenTrees::ApplyTreeImage( // Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr) { - if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ) && (itr->y < cChunkDef::Height)) + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_RelY < cChunkDef::Height)) { // Inside this chunk, integrate into a_ChunkDesc: - switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z)) + switch (a_ChunkDesc.GetBlockType(itr->m_RelX, itr->m_RelY, itr->m_RelZ)) { case E_BLOCK_LEAVES: { - if (itr->BlockType != E_BLOCK_LOG) + if (itr->m_BlockType != E_BLOCK_LOG) { break; } @@ -182,7 +182,7 @@ void cStructGenTrees::ApplyTreeImage( } CASE_TREE_OVERWRITTEN_BLOCKS: { - a_ChunkDesc.SetBlockTypeMeta(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + a_ChunkDesc.SetBlockTypeMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); break; } diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp index be8b0cd6b..a10e0f4f1 100644 --- a/src/Generating/Trees.cpp +++ b/src/Generating/Trees.cpp @@ -403,17 +403,17 @@ void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a for (auto itr : a_LogBlocks) { // Get the log's X and Z coordinates - int X = itr.ChunkX * 16 + itr.x; - int Z = itr.ChunkZ * 16 + itr.z; + int X = itr.GetX(); + int Z = itr.GetZ(); - a_OtherBlocks.push_back(sSetBlock(X, itr.y - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); - PushCoordBlocks(X, itr.y - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_OtherBlocks.push_back(sSetBlock(X, itr.m_RelY - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); + PushCoordBlocks(X, itr.m_RelY - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); for (int y = -1; y <= 1; y++) { - PushCoordBlocks (X, itr.y + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + PushCoordBlocks (X, itr.m_RelY + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); } - PushCoordBlocks(X, itr.y + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - a_OtherBlocks.push_back(sSetBlock(X, itr.y + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); + PushCoordBlocks(X, itr.m_RelY + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_OtherBlocks.push_back(sSetBlock(X, itr.m_RelY + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); } // Trunk: diff --git a/src/Items/CMakeLists.txt b/src/Items/CMakeLists.txt index 12a467672..c50ddb372 100644 --- a/src/Items/CMakeLists.txt +++ b/src/Items/CMakeLists.txt @@ -10,12 +10,14 @@ SET (SRCS SET (HDRS ItemArmor.h ItemBed.h + ItemBigFlower.h ItemBoat.h ItemBow.h ItemBrewingStand.h ItemBucket.h ItemCake.h ItemCauldron.h + ItemChest.h ItemCloth.h ItemComparator.h ItemDoor.h @@ -38,18 +40,21 @@ SET (HDRS ItemPainting.h ItemPickaxe.h ItemPotion.h + ItemPumpkin.h ItemRedstoneDust.h ItemRedstoneRepeater.h ItemSapling.h ItemSeeds.h ItemShears.h ItemShovel.h + ItemSlab.h ItemSign.h ItemSpawnEgg.h ItemString.h ItemSugarcane.h ItemSword.h - ItemThrowable.h) + ItemThrowable.h +) if(NOT MSVC) add_library(Items ${SRCS} ${HDRS}) diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h index 94a14cf16..77d51d744 100644 --- a/src/Items/ItemBed.h +++ b/src/Items/ItemBed.h @@ -24,30 +24,36 @@ public: return true; } - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, + + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + int a_CursorX, int a_CursorY, int a_CursorZ ) override { + // Can only be placed on the floor: if (a_BlockFace != BLOCK_FACE_TOP) { - // Can only be placed on the floor return false; } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetYaw()); + // The "foot" block: + sSetBlockVector blks; + NIBBLETYPE BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player.GetYaw()); + blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BED, BlockMeta); - // Check if there is empty space for the foot section: - Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta); - if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR) + // Check if there is empty space for the "head" block: + // (Vanilla only allows beds to be placed into air) + Vector3i Direction = cBlockBedHandler::MetaDataToDirection(BlockMeta); + if (a_World.GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR) { return false; } + blks.emplace_back(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, BlockMeta | 0x08); - a_BlockType = E_BLOCK_BED; - return true; + // Place both bed blocks: + return a_Player.PlaceBlocks(blks); } } ; diff --git a/src/Items/ItemBigFlower.h b/src/Items/ItemBigFlower.h new file mode 100644 index 000000000..4341a1a17 --- /dev/null +++ b/src/Items/ItemBigFlower.h @@ -0,0 +1,56 @@ + +// ItemBigFlower.h + +// Declares the cItemBigFlower class representing the cItemHandler for big flowers + + + + + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemBigFlowerHandler: + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemBigFlowerHandler(void): + super(E_BLOCK_BIG_FLOWER) + { + } + + + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + // Can only be placed on the floor: + if (a_BlockFace != BLOCK_FACE_TOP) + { + return false; + } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + // Place both blocks atomically: + sSetBlockVector blks; + blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07); + if (a_BlockY < cChunkDef::Height - 1) + { + blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_BIG_FLOWER, (a_EquippedItem.m_ItemDamage & 0x07) | 0x08); + } + return a_Player.PlaceBlocks(blks); + } +}; + + + + diff --git a/src/Items/ItemChest.h b/src/Items/ItemChest.h new file mode 100644 index 000000000..b6579c423 --- /dev/null +++ b/src/Items/ItemChest.h @@ -0,0 +1,167 @@ + +// ItemChest.h + +// Declares the cItemChestHandler class representing the cItemHandler descendant responsible for chests + + + + + +#pragma once + +#include "ItemHandler.h" +#include "../Blocks/BlockChest.h" + + + + + +class cItemChestHandler: + public cItemHandler +{ + typedef cItemHandler super; +public: + cItemChestHandler(int a_ItemType): + super(a_ItemType) + { + } + + + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + if (a_BlockFace < 0) + { + // Clicked in air + return false; + } + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The clicked block is outside the world, ignore this call altogether (#128) + return false; + } + + // Check if the block ignores build collision (water, grass etc.): + BLOCKTYPE ClickedBlock; + NIBBLETYPE ClickedBlockMeta; + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); + if ( + BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() || + BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta) + ) + { + cChunkInterface ChunkInterface(a_World.GetChunkMap()); + BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + else + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The block is being placed outside the world, ignore this packet altogether (#128) + return false; + } + + NIBBLETYPE PlaceMeta; + BLOCKTYPE PlaceBlock; + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta); + + // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. + // No need to do combinability (dblslab) checks, client will do that here. + if ( + !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() && + !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta) + ) + { + // Tried to place a block *into* another? + // Happens when you place a block aiming at side of block with a torch on it or stem beside it + return false; + } + } + + // Check that there is at most one single neighbor of the same chest type: + static const Vector3i CrossCoords[] = + { + {-1, 0, 0}, + { 0, 0, -1}, + { 1, 0, 0}, + { 0, 0, 1}, + }; + int NeighborIdx = -1; + for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++) + { + if (a_World.GetBlock(a_BlockX + CrossCoords[i].x, a_BlockY, a_BlockZ + CrossCoords[i].z) != m_ItemType) + { + continue; + } + if (NeighborIdx >= 0) + { + // Can't place here, there are already two neighbors, this would form a 3-block chest + return false; + } + NeighborIdx = static_cast(i); + + // Check that this neighbor is a single chest: + int bx = a_BlockX + CrossCoords[i].x; + int bz = a_BlockZ + CrossCoords[i].z; + for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++) + { + if (a_World.GetBlock(bx + CrossCoords[j].x, a_BlockY, bz + CrossCoords[j].z) == m_ItemType) + { + return false; + } + } // for j + } // for i + + // If there's no chest neighbor, place the single block chest and bail out: + BLOCKTYPE ChestBlockType = static_cast(m_ItemType); + if (NeighborIdx < 0) + { + NIBBLETYPE Meta = cBlockChestHandler::PlayerYawToMetaData(a_Player.GetYaw()); + return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta); + } + + // There is a neighbor to which we need to adjust + double yaw = a_Player.GetYaw(); + if ((NeighborIdx == 0) || (NeighborIdx == 2)) + { + // The neighbor is in the X axis, form a X-axis-aligned dblchest: + NIBBLETYPE Meta = ((yaw >= -90) && (yaw < 90)) ? E_META_CHEST_FACING_ZM : E_META_CHEST_FACING_ZP; + + // Place the new chest: + if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta)) + { + return false; + } + + // Adjust the existing chest: + a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta); + return true; + } + + // The neighbor is in the Z axis, form a Z-axis-aligned dblchest: + NIBBLETYPE Meta = (yaw < 0) ? E_META_CHEST_FACING_XM : E_META_CHEST_FACING_XP; + + // Place the new chest: + if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta)) + { + return false; + } + + // Adjust the existing chest: + a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta); + return true; + } + +private: + cItemChestHandler(const cItemChestHandler &) = delete; +}; + + + + diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h index cd5baf44f..dbba26728 100644 --- a/src/Items/ItemDoor.h +++ b/src/Items/ItemDoor.h @@ -3,6 +3,7 @@ #include "ItemHandler.h" #include "../World.h" +#include "../Blocks/BlockDoor.h" @@ -18,27 +19,43 @@ public: } - virtual bool IsPlaceable(void) override - { - return true; - } - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + int a_CursorX, int a_CursorY, int a_CursorZ ) override { + // Vanilla only allows door placement while clicking on the top face of the block below the door: + if (a_BlockFace != BLOCK_FACE_NONE) + { + return false; + } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + // Door (bottom block) can be placed in Y range of [1, 254]: + if ((a_BlockY < 1) || (a_BlockY + 2 >= cChunkDef::Height)) + { + return false; + } + + // The door needs a compatible block below it: + if ((a_BlockY > 0) && cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + { + return false; + } + + // Get the block type of the door to place: + BLOCKTYPE BlockType; switch (m_ItemType) { - case E_ITEM_WOODEN_DOOR: a_BlockType = E_BLOCK_WOODEN_DOOR; break; - case E_ITEM_IRON_DOOR: a_BlockType = E_BLOCK_IRON_DOOR; break; - case E_ITEM_SPRUCE_DOOR: a_BlockType = E_BLOCK_SPRUCE_DOOR; break; - case E_ITEM_BIRCH_DOOR: a_BlockType = E_BLOCK_BIRCH_DOOR; break; - case E_ITEM_JUNGLE_DOOR: a_BlockType = E_BLOCK_JUNGLE_DOOR; break; - case E_ITEM_DARK_OAK_DOOR: a_BlockType = E_BLOCK_DARK_OAK_DOOR; break; - case E_ITEM_ACACIA_DOOR: a_BlockType = E_BLOCK_ACACIA_DOOR; break; + case E_ITEM_WOODEN_DOOR: BlockType = E_BLOCK_WOODEN_DOOR; break; + case E_ITEM_IRON_DOOR: BlockType = E_BLOCK_IRON_DOOR; break; + case E_ITEM_SPRUCE_DOOR: BlockType = E_BLOCK_SPRUCE_DOOR; break; + case E_ITEM_BIRCH_DOOR: BlockType = E_BLOCK_BIRCH_DOOR; break; + case E_ITEM_JUNGLE_DOOR: BlockType = E_BLOCK_JUNGLE_DOOR; break; + case E_ITEM_DARK_OAK_DOOR: BlockType = E_BLOCK_DARK_OAK_DOOR; break; + case E_ITEM_ACACIA_DOOR: BlockType = E_BLOCK_ACACIA_DOOR; break; default: { ASSERT(!"Unhandled door type"); @@ -46,14 +63,47 @@ public: } } - cChunkInterface ChunkInterface(a_World->GetChunkMap()); - bool Meta = BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta( - ChunkInterface, a_Player, - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, - a_CursorX, a_CursorY, a_CursorZ, - a_BlockType, a_BlockMeta - ); - return Meta; + // Check the two blocks that will get replaced by the door: + BLOCKTYPE LowerBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); + BLOCKTYPE UpperBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 2, a_BlockZ); + if ( + !cBlockDoorHandler::CanReplaceBlock(LowerBlockType) || + !cBlockDoorHandler::CanReplaceBlock(UpperBlockType)) + { + return false; + } + + // Get the coords of the neighboring blocks: + NIBBLETYPE LowerBlockMeta = cBlockDoorHandler::PlayerYawToMetaData(a_Player.GetYaw()); + Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta); + Vector3i LeftNeighborPos = RelDirToOutside; + LeftNeighborPos.TurnCCW(); + LeftNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ); + Vector3i RightNeighborPos = RelDirToOutside; + RightNeighborPos.TurnCW(); + RightNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ); + + // Decide whether the hinge is on the left (default) or on the right: + NIBBLETYPE UpperBlockMeta = 0x08; + if ( + cBlockDoorHandler::IsDoorBlockType(a_World.GetBlock(LeftNeighborPos)) || // The block to the left is a door block + cBlockInfo::IsSolid(a_World.GetBlock(RightNeighborPos)) // The block to the right is solid + ) + { + UpperBlockMeta = 0x09; // Upper block | hinge on right + } + + // Set the blocks: + sSetBlockVector blks; + blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta); + blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta); + return a_Player.PlaceBlocks(blks); + } + + + virtual bool IsPlaceable(void) override + { + return true; } } ; diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h index da978040d..bfcd0bac4 100644 --- a/src/Items/ItemDye.h +++ b/src/Items/ItemDye.h @@ -55,25 +55,16 @@ public: return false; } - // Check plugins - if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta)) + // Place the cocoa pod: + if (a_Player->PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta)) { - a_World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); - a_Player->GetInventory().SendEquippedSlot(); - return false; - } - - // Set block and broadcast place sound - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta); - a_World->BroadcastSoundEffect("dig.stone", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f); - - // Remove one cocoa pod from the inventory - if (!a_Player->IsGameModeCreative()) - { - a_Player->GetInventory().RemoveOneEquippedItem(); + a_World->BroadcastSoundEffect("dig.stone", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f); + if (a_Player->IsGameModeSurvival()) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + return true; } - cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta); - return true; } return false; } diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index 9272a723d..92c55ec62 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -6,39 +6,43 @@ #include "../Entities/Player.h" #include "../FastRandom.h" #include "../BlockInServerPluginInterface.h" +#include "../Chunk.h" // Handlers: #include "ItemArmor.h" #include "ItemBed.h" +#include "ItemBigFlower.h" #include "ItemBoat.h" #include "ItemBow.h" #include "ItemBrewingStand.h" #include "ItemBucket.h" #include "ItemCake.h" #include "ItemCauldron.h" +#include "ItemChest.h" #include "ItemCloth.h" #include "ItemComparator.h" #include "ItemDoor.h" -#include "ItemMilk.h" #include "ItemDye.h" #include "ItemEmptyMap.h" #include "ItemFishingRod.h" #include "ItemFlowerPot.h" #include "ItemFood.h" #include "ItemGoldenApple.h" -#include "ItemItemFrame.h" #include "ItemHoe.h" +#include "ItemItemFrame.h" #include "ItemLeaves.h" #include "ItemLighter.h" #include "ItemLilypad.h" #include "ItemMap.h" +#include "ItemMilk.h" #include "ItemMinecart.h" +#include "ItemMobHead.h" #include "ItemMushroomSoup.h" #include "ItemNetherWart.h" #include "ItemPainting.h" #include "ItemPickaxe.h" #include "ItemPotion.h" -#include "ItemThrowable.h" +#include "ItemPumpkin.h" #include "ItemRedstoneDust.h" #include "ItemRedstoneRepeater.h" #include "ItemSapling.h" @@ -46,11 +50,12 @@ #include "ItemShears.h" #include "ItemShovel.h" #include "ItemSign.h" -#include "ItemMobHead.h" +#include "ItemSlab.h" #include "ItemSpawnEgg.h" #include "ItemString.h" #include "ItemSugarcane.h" #include "ItemSword.h" +#include "ItemThrowable.h" #include "../Blocks/BlockHandler.h" @@ -94,52 +99,58 @@ cItemHandler * cItemHandler::GetItemHandler(int a_ItemType) -cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) +cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType) { switch (a_ItemType) { default: return new cItemHandler(a_ItemType); // Single item per handler, alphabetically sorted: - case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); - case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType); - case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); - case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); - case E_ITEM_BED: return new cItemBedHandler(a_ItemType); - case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType); + case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType); + case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); + case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType); + case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType); + case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler; + case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); + case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(E_BLOCK_STONE_SLAB, E_BLOCK_DOUBLE_STONE_SLAB); + case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType); + case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(E_BLOCK_WOODEN_SLAB, E_BLOCK_DOUBLE_WOODEN_SLAB); + case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); + case E_ITEM_BED: return new cItemBedHandler(a_ItemType); + case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType); case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler(); - case E_ITEM_BOW: return new cItemBowHandler(); - case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType); - case E_ITEM_CAKE: return new cItemCakeHandler(a_ItemType); - case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType); - case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType); - case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); - case E_ITEM_EGG: return new cItemEggHandler(); - case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler(); - case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler(); - case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType); - case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler(); - case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType); - case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); - case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); - case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler(); - case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType); - case E_ITEM_MAP: return new cItemMapHandler(); - case E_ITEM_MILK: return new cItemMilkHandler(); - case E_ITEM_MUSHROOM_SOUP: return new cItemMushroomSoupHandler(a_ItemType); - case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType); - case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType); - case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType); - case E_ITEM_POTIONS: return new cItemPotionHandler(); - case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); - case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); - case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); - case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); - case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType); - case E_ITEM_SNOWBALL: return new cItemSnowballHandler(); - case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); - case E_ITEM_STRING: return new cItemStringHandler(a_ItemType); - case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); + case E_ITEM_BOW: return new cItemBowHandler(); + case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType); + case E_ITEM_CAKE: return new cItemCakeHandler(a_ItemType); + case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType); + case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType); + case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); + case E_ITEM_EGG: return new cItemEggHandler(); + case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler(); + case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler(); + case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType); + case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler(); + case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType); + case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); + case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); + case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler(); + case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType); + case E_ITEM_MAP: return new cItemMapHandler(); + case E_ITEM_MILK: return new cItemMilkHandler(); + case E_ITEM_MUSHROOM_SOUP: return new cItemMushroomSoupHandler(a_ItemType); + case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType); + case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType); + case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType); + case E_ITEM_POTIONS: return new cItemPotionHandler(); + case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); + case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); + case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); + case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); + case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType); + case E_ITEM_SNOWBALL: return new cItemSnowballHandler(); + case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); + case E_ITEM_STRING: return new cItemStringHandler(a_ItemType); + case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); case E_ITEM_WOODEN_HOE: case E_ITEM_STONE_HOE: @@ -297,6 +308,108 @@ cItemHandler::cItemHandler(int a_ItemType) +bool cItemHandler::OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ +) +{ + if (a_BlockFace < 0) + { + // Clicked in air + return false; + } + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The clicked block is outside the world, ignore this call altogether (#128) + return false; + } + + BLOCKTYPE ClickedBlock; + NIBBLETYPE ClickedBlockMeta; + + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); + + // Check if the block ignores build collision (water, grass etc.): + if ( + BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() || + BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta) + ) + { + cChunkInterface ChunkInterface(a_World.GetChunkMap()); + BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + else + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The block is being placed outside the world, ignore this packet altogether (#128) + return false; + } + + NIBBLETYPE PlaceMeta; + BLOCKTYPE PlaceBlock; + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta); + + // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. + // No need to do combinability (dblslab) checks, client will do that here. + if ( + !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() && + !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta) + ) + { + // Tried to place a block *into* another? + // Happens when you place a block aiming at side of block with a torch on it or stem beside it + return false; + } + } + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) + { + // Handler refused the placement, send that information back to the client: + a_World.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, &a_Player); + a_Player.GetInventory().SendEquippedSlot(); + return false; + } + + if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta)) + { + // The placement failed, the block has already been re-sent, re-send inventory: + a_Player.GetInventory().SendEquippedSlot(); + return false; + } + + AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType); + float Volume = 1.0f, Pitch = 0.8f; + if (PlaceSound == "dig.metal") + { + Pitch = 1.2f; + PlaceSound = "dig.stone"; + } + else if (PlaceSound == "random.anvil_land") + { + Volume = 0.65f; + } + + a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); + + // Remove the "placed" item: + if (a_Player.IsGameModeSurvival()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + return true; +} + + + + + bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) { UNUSED(a_World); diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h index 67c250a97..3ac664798 100644 --- a/src/Items/ItemHandler.h +++ b/src/Items/ItemHandler.h @@ -31,10 +31,35 @@ public: /** Force virtual destructor */ virtual ~cItemHandler() {} + + + /** Called when the player tries to place the item (right mouse button, IsPlaceable() == true). + The default handler uses GetPlacementBlockTypeMeta and places the returned block. + Override this function for advanced behavior such as placing multiple blocks. + If the block placement is refused inside this call, it will automatically revert the client-side changes. + Returns true if the placement succeeded, false if the placement was aborted for any reason. */ + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ); + + /** Called when the player right-clicks with this item and IsPlaceable() == true, and OnPlace() is not overridden. + This function should provide the block type and meta for the placed block, or refuse the placement. + Returns true to allow placement, false to refuse. */ + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ); + + /** Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False */ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir); + /** Called when the client sends the SHOOT status in the lclk packet */ virtual void OnItemShoot(cPlayer *, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) { @@ -106,18 +131,8 @@ public: /** Can the anvil repair this item, when a_Item is the second input? */ virtual bool CanRepairWithRawMaterial(short a_ItemType); - /** Called before a block is placed into a world. - The handler should return true to allow placement, false to refuse. - Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. - */ - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ); - - /** Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can't) DEFAULT: False */ + /** Returns whether this tool / item can harvest a specific block (e.g. iron pickaxe can harvest diamond ore, but wooden one can't). + Defaults to false unless overridden. */ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType); static cItemHandler * GetItemHandler(int a_ItemType); diff --git a/src/Items/ItemMobHead.h b/src/Items/ItemMobHead.h index 4c36fe8d8..d962dabae 100644 --- a/src/Items/ItemMobHead.h +++ b/src/Items/ItemMobHead.h @@ -3,6 +3,7 @@ #include "ItemHandler.h" #include "../World.h" +#include "../BlockEntities/MobHeadEntity.h" @@ -18,6 +19,266 @@ public: } + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + // Cannot place a head at "no face" and from the bottom: + if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_BOTTOM)) + { + return true; + } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + // If the placed head is a wither, try to spawn the wither first: + if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER) + { + if (TrySpawnWitherAround(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ)) + { + return true; + } + // Wither not created, proceed with regular head placement + } + + return PlaceRegularHead(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + } + + + /** Places a regular head block with no mob spawning checking. */ + bool PlaceRegularHead( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) + { + // Place the block: + if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_HEAD, static_cast(a_EquippedItem.m_ItemType))) + { + return false; + } + + // Use a callback to set the properties of the mob head block entity: + class cCallback : public cBlockEntityCallback + { + cPlayer & m_Player; + eMobHeadType m_HeadType; + NIBBLETYPE m_BlockMeta; + + virtual bool Item(cBlockEntity * a_BlockEntity) + { + if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD) + { + return false; + } + cMobHeadEntity * MobHeadEntity = static_cast(a_BlockEntity); + + int Rotation = 0; + if (m_BlockMeta == 1) + { + Rotation = FloorC(m_Player.GetYaw() * 16.0f / 360.0f + 0.5f) & 0x0f; + } + + MobHeadEntity->SetType(m_HeadType); + MobHeadEntity->SetRotation(static_cast(Rotation)); + MobHeadEntity->GetWorld()->BroadcastBlockEntity(MobHeadEntity->GetPosX(), MobHeadEntity->GetPosY(), MobHeadEntity->GetPosZ()); + return false; + } + + public: + cCallback (cPlayer & a_CBPlayer, eMobHeadType a_HeadType, NIBBLETYPE a_BlockMeta) : + m_Player(a_CBPlayer), + m_HeadType(a_HeadType), + m_BlockMeta(a_BlockMeta) + {} + }; + cCallback Callback(a_Player, static_cast(a_EquippedItem.m_ItemType), static_cast(a_BlockFace)); + a_World.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback); + return true; + } + + + /** Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formula. + Returns true if the wither was created. */ + bool TrySpawnWitherAround( + cWorld & a_World, cPlayer & a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ + ) + { + // No wither can be created at Y < 2 - not enough space for the formula: + if (a_BlockY < 2) + { + return false; + } + + // Check for all relevant wither locations: + static const Vector3i RelCoords[] = + { + { 0, 0, 0}, + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + }; + for (size_t i = 0; i < ARRAYCOUNT(RelCoords); ++i) + { + if (TrySpawnWitherAt( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, + RelCoords[i].x, RelCoords[i].z + )) + { + return true; + } + } // for i - Coords[] + + return false; + } + + + /** Tries to spawn a wither at the specified offset from the placed head block. + PlacedHead coords are used to override the block query - at those coords the block is not queried from the world, + but assumed to be a head instead. + Offset is used to shift the image around the X and Z axis. + Returns true iff the wither was created successfully. */ + bool TrySpawnWitherAt( + cWorld & a_World, cPlayer & a_Player, + int a_PlacedHeadX, int a_PlacedHeadY, int a_PlacedHeadZ, + int a_OffsetX, int a_OffsetZ + ) + { + // Image for the wither at the X axis: + static const sSetBlock ImageWitherX[] = + { + {-1, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER}, + { 0, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER}, + { 1, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER}, + {-1, -1, 0, E_BLOCK_SOULSAND, 0}, + { 0, -1, 0, E_BLOCK_SOULSAND, 0}, + { 1, -1, 0, E_BLOCK_SOULSAND, 0}, + {-1, -2, 0, E_BLOCK_AIR, 0}, + { 0, -2, 0, E_BLOCK_SOULSAND, 0}, + { 1, -2, 0, E_BLOCK_AIR, 0}, + }; + + // Image for the wither at the Z axis: + static const sSetBlock ImageWitherZ[] = + { + { 0, 0, -1, E_BLOCK_HEAD, E_META_HEAD_WITHER}, + { 0, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER}, + { 0, 0, 1, E_BLOCK_HEAD, E_META_HEAD_WITHER}, + { 0, -1, -1, E_BLOCK_SOULSAND, 0}, + { 0, -1, 0, E_BLOCK_SOULSAND, 0}, + { 0, -1, 1, E_BLOCK_SOULSAND, 0}, + { 0, -2, -1, E_BLOCK_AIR, 0}, + { 0, -2, 0, E_BLOCK_SOULSAND, 0}, + { 0, -2, 1, E_BLOCK_AIR, 0}, + }; + + // Try to spawn the wither from each image: + return ( + TrySpawnWitherFromImage( + a_World, a_Player, ImageWitherX, ARRAYCOUNT(ImageWitherX), + a_PlacedHeadX, a_PlacedHeadY, a_PlacedHeadZ, + a_OffsetX, a_OffsetZ + ) || + TrySpawnWitherFromImage( + a_World, a_Player, ImageWitherZ, ARRAYCOUNT(ImageWitherZ), + a_PlacedHeadX, a_PlacedHeadY, a_PlacedHeadZ, + a_OffsetX, a_OffsetZ + ) + ); + } + + + /** Tries to spawn a wither from the specified image at the specified offset from the placed head block. + PlacedHead coords are used to override the block query - at those coords the block is not queried from the world, + but assumed to be a head instead. + Offset is used to shift the image around the X and Z axis. + Returns true iff the wither was created successfully. */ + bool TrySpawnWitherFromImage( + cWorld & a_World, cPlayer & a_Player, const sSetBlock * a_Image, size_t a_ImageCount, + int a_PlacedHeadX, int a_PlacedHeadY, int a_PlacedHeadZ, + int a_OffsetX, int a_OffsetZ + ) + { + // Check each block individually; simultaneously build the SetBlockVector for clearing the blocks: + sSetBlockVector AirBlocks; + AirBlocks.reserve(a_ImageCount); + for (size_t i = 0; i < a_ImageCount; i++) + { + // Get the absolute coords of the image: + int BlockX = a_PlacedHeadX + a_OffsetX + a_Image[i].m_RelX; + int BlockY = a_PlacedHeadY + a_Image[i].m_RelY; + int BlockZ = a_PlacedHeadZ + a_OffsetZ + a_Image[i].m_RelZ; + + // If the query is for the placed head, short-circuit-evaluate it: + if ((BlockX == a_PlacedHeadX) && (BlockY == a_PlacedHeadY) && (BlockZ == a_PlacedHeadZ)) + { + if ((a_Image[i].m_BlockType != E_BLOCK_HEAD) || (a_Image[i].m_BlockMeta != E_META_HEAD_WITHER)) + { + return false; // Didn't match + } + continue; // Matched, continue checking the rest of the image + } + + // Query the world block: + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_World.GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta)) + { + // Cannot query block, assume unloaded chunk, fail to spawn the wither + return false; + } + + // Compare the world block: + if ((BlockType != a_Image[i].m_BlockType) || (BlockMeta != a_Image[i].m_BlockMeta)) + { + return false; // Didn't match + } + // Matched, continue checking + } // for i - a_Image + + // All image blocks matched, try place the wither: + if (!a_Player.PlaceBlocks(AirBlocks)) + { + return false; + } + + // Spawn the wither: + int BlockX = a_PlacedHeadX + a_OffsetX; + int BlockZ = a_PlacedHeadZ + a_OffsetZ; + a_World.SpawnMob(static_cast(BlockX) + 0.5, a_PlacedHeadY - 2, static_cast(BlockZ) + 0.5, mtWither); + AwardSpawnWitherAchievement(a_World, BlockX, a_PlacedHeadY - 2, BlockZ); + return true; + } + + + /** Awards the achievement to all players close to the specified point. */ + void AwardSpawnWitherAchievement(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + class cPlayerCallback : public cPlayerListCallback + { + Vector3f m_Pos; + + virtual bool Item(cPlayer * a_Player) + { + // If player is close, award achievement: + double Dist = (a_Player->GetPosition() - m_Pos).Length(); + if (Dist < 50.0) + { + a_Player->AwardAchievement(achSpawnWither); + } + return false; + } + + public: + cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {} + } PlayerCallback(Vector3f(static_cast(a_BlockX), static_cast(a_BlockY), static_cast(a_BlockZ))); + a_World.ForEachPlayer(PlayerCallback); + } + + virtual bool IsPlaceable(void) override { return true; diff --git a/src/Items/ItemPumpkin.h b/src/Items/ItemPumpkin.h new file mode 100644 index 000000000..fa00179d3 --- /dev/null +++ b/src/Items/ItemPumpkin.h @@ -0,0 +1,156 @@ + +// ItemPumpkin.h + +// Declares the cItemPumpkinHandler class representing the pumpkin block in its item form + + + + + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemPumpkinHandler: + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemPumpkinHandler(void): + super(E_BLOCK_PUMPKIN) + { + } + + + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + // First try spawning a snow golem or an iron golem: + int PlacedBlockX = a_BlockX; + int PlacedBlockY = a_BlockY; + int PlacedBlockZ = a_BlockZ; + AddFaceDirection(PlacedBlockX, PlacedBlockY, PlacedBlockZ, a_BlockFace); + if (TrySpawnGolem(a_World, a_Player, PlacedBlockX, PlacedBlockY, PlacedBlockZ)) + { + // The client thinks that they placed the pumpkin, let them know it's been replaced: + a_Player.SendBlocksAround(PlacedBlockX, PlacedBlockY, PlacedBlockZ); + return true; + } + + // No golem at these coords, place the block normally: + return super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + } + + + /** Spawns a snow / iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. + Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. */ + bool TrySpawnGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + // A golem can't form with a pumpkin below level 2 or above level 255 + if ((a_BlockY < 2) || (a_BlockY >= cChunkDef::Height)) + { + return false; + } + + // Decide which golem to try spawning based on the block below the placed pumpkin: + switch (a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)) + { + case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + default: + { + // No golem here + return false; + } + } + } + + + /** Spawns a snow golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. + Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. + Assumes that the block below the specified block has already been checked and is a snow block. */ + bool TrySpawnSnowGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + // Need one more snow block 2 blocks below the pumpkin: + if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_SNOW_BLOCK) + { + return false; + } + + // Try to place air blocks where the original recipe blocks were: + sSetBlockVector AirBlocks; + AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head + AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso + AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs + if (!a_Player.PlaceBlocks(AirBlocks)) + { + return false; + } + + // Spawn the golem: + a_World.SpawnMob(static_cast(a_BlockX) + 0.5, a_BlockY - 2, static_cast(a_BlockZ) + 0.5, mtSnowGolem); + return true; + } + + + /** Spawns an iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. + Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. + Assumes that the block below the specified block has already been checked and is an iron block. */ + bool TrySpawnIronGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + // Need one more iron block 2 blocks below the pumpkin: + if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_IRON_BLOCK) + { + return false; + } + + // Check the two arm directions (X, Z) using a loop over two sets of offset vectors: + static const Vector3i ArmOffsets[] = + { + {1, 0, 0}, + {0, 0, 1}, + }; + for (size_t i = 0; i < ARRAYCOUNT(ArmOffsets); i++) + { + // If the arm blocks don't match, bail out of this loop repetition: + if ( + (a_World.GetBlock(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) || + (a_World.GetBlock(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) + ) + { + continue; + } + + // Try to place air blocks where the original recipe blocks were: + sSetBlockVector AirBlocks; + AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head + AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso + AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs + AirBlocks.emplace_back(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm + AirBlocks.emplace_back(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm + if (!a_Player.PlaceBlocks(AirBlocks)) + { + return false; + } + + // Spawn the golem: + a_World.SpawnMob(static_cast(a_BlockX) + 0.5, a_BlockY - 2, static_cast(a_BlockZ) + 0.5, mtIronGolem); + return true; + } // for i - ArmOffsets[] + + // Neither arm offset matched, this thing is not a complete golem + return false; + } +}; + + + + diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h index 0fa0fa0be..dabbdbba1 100644 --- a/src/Items/ItemSign.h +++ b/src/Items/ItemSign.h @@ -13,13 +13,33 @@ class cItemSignHandler : public cItemHandler { + typedef cItemHandler super; public: cItemSignHandler(int a_ItemType) : - cItemHandler(a_ItemType) + super(a_ItemType) { } + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) + { + // If the regular placement doesn't work, do no further processing: + if (!super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + return false; + } + + // After successfully placing the sign, open the sign editor for the player: + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + a_Player.GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); + return true; + } + + virtual bool IsPlaceable(void) override { return true; diff --git a/src/Items/ItemSlab.h b/src/Items/ItemSlab.h new file mode 100644 index 000000000..1b68b9d0c --- /dev/null +++ b/src/Items/ItemSlab.h @@ -0,0 +1,93 @@ + +// ItemSlab.h + +// Declares the cItemSlabHandler responsible for handling slabs, when in their item form. + + + + + +#pragma once + +#include "ItemHandler.h" +#include "../Blocks/BlockSlab.h" + + + + + +class cItemSlabHandler: + public cItemHandler +{ + typedef cItemHandler super; + +public: + + /** Creates a new handler for the specified slab item type. + Sets the handler to use the specified doubleslab block type for combining self into doubleslabs. */ + cItemSlabHandler(int a_ItemType, BLOCKTYPE a_DoubleSlabBlockType): + super(a_ItemType), + m_DoubleSlabBlockType(a_DoubleSlabBlockType) + { + } + + + // cItemHandler overrides: + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + // Special slab handling - placing a slab onto another slab produces a dblslab instead: + BLOCKTYPE ClickedBlockType; + NIBBLETYPE ClickedBlockMeta; + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlockType, ClickedBlockMeta); + if ( + (ClickedBlockType == m_ItemType) && // Placing the same slab material + (ClickedBlockMeta == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single) + ) + { + // If clicking the top side of a bottom-half slab, combine into a doubleslab: + if ( + (a_BlockFace == BLOCK_FACE_TOP) && + ((ClickedBlockMeta & 0x08) == 0) + ) + { + return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07); + } + + // If clicking the bottom side of a top-half slab, combine into a doubleslab: + if ( + (a_BlockFace == BLOCK_FACE_BOTTOM) && + ((ClickedBlockMeta & 0x08) != 0) + ) + { + return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07); + } + } + + // The slabs didn't combine, use the default handler to place the slab: + bool res = super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + + /* + The client has a bug when a slab replaces snow and there's a slab above it. + The client then combines the slab above, rather than replacing the snow. + We send the block above the currently placed block back to the client to fix the bug. + Ref.: http://forum.mc-server.org/showthread.php?tid=434&pid=17388#pid17388 + */ + if ((a_BlockFace == BLOCK_FACE_TOP) && (a_BlockY < cChunkDef::Height - 1)) + { + a_Player.SendBlocksAround(a_BlockX, a_BlockY + 1, a_BlockZ, 1); + } + return res; + } + +protected: + /** The block type to use when the slab combines into a doubleslab block. */ + BLOCKTYPE m_DoubleSlabBlockType; +}; + + + + diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 1e33ec433..34103ea5a 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -228,8 +228,8 @@ void cProtocol172::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV Pkt.WriteInt((int)a_Changes.size() * 4); for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) { - unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); - unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); + unsigned int Coords = itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12); + unsigned int Blocks = itr->m_BlockMeta | (itr->m_BlockType << 4); Pkt.WriteInt((Coords << 16) | Blocks); } // for itr - a_Changes[] } diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 1a13f4f7c..72827ac47 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -207,9 +207,9 @@ void cProtocol180::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV Pkt.WriteVarInt((UInt32)a_Changes.size()); for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) { - short Coords = (short) (itr->y | (itr->z << 8) | (itr->x << 12)); + short Coords = (short) (itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12)); Pkt.WriteShort(Coords); - Pkt.WriteVarInt((itr->BlockType & 0xFFF) << 4 | (itr->BlockMeta & 0xF)); + Pkt.WriteVarInt((itr->m_BlockType & 0xFFF) << 4 | (itr->m_BlockMeta & 0xF)); } // for itr - a_Changes[] } diff --git a/src/World.cpp b/src/World.cpp index ae739a2c3..c08b44f77 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1463,7 +1463,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) sSetBlockVector b2; for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) { - if (itr->BlockType == E_BLOCK_LOG) + if (itr->m_BlockType == E_BLOCK_LOG) { b2.push_back(*itr); } @@ -1478,7 +1478,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) // Check that at each log's coord there's an block allowed to be overwritten: for (sSetBlockVector::const_iterator itr = b2.begin(); itr != b2.end(); ++itr) { - switch (itr->BlockType) + switch (itr->m_BlockType) { CASE_TREE_ALLOWED_BLOCKS: { @@ -1926,6 +1926,15 @@ void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTicks, +void cWorld::SetBlocks(const sSetBlockVector & a_Blocks) +{ + m_ChunkMap->SetBlocks(a_Blocks); +} + + + + + void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) { m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType); diff --git a/src/World.h b/src/World.h index 4f24280a4..2ee33375f 100644 --- a/src/World.h +++ b/src/World.h @@ -491,6 +491,11 @@ public: // tolua_end + /** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item. + Is more efficient than calling SetBlock() multiple times. + If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */ + void SetBlocks(const sSetBlockVector & a_Blocks); + /** Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType */ void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); -- cgit v1.2.3 From 9c5463be1e090ff65acdc0c0571788f60ad76478 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 24 Dec 2014 07:32:31 +0100 Subject: gcc compilation fix. --- src/ChunkDef.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ChunkDef.h b/src/ChunkDef.h index b62656a58..959841ecc 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -399,9 +399,12 @@ public: typedef std::list cChunkCoordsList; typedef std::vector cChunkCoordsVector; +namespace std +{ + /** A simple hash function for chunk coords, we assume that chunk coords won't use more than 16 bits, so the hash is almost an identity. Used for std::unordered_map */ -template<> struct std::hash +template<> struct hash { size_t operator ()(const cChunkCoords & a_Coords) { @@ -409,6 +412,8 @@ template<> struct std::hash } }; +} // namespace std + -- cgit v1.2.3 From 63de5f8a555e3bd4b9309792e3a9c0dcb9e8f4b6 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 24 Dec 2014 08:38:37 +0100 Subject: Replaced a std::hash specialization with explicit type. std::hash is problematic in gcc / clang, one has a class, the other a struct. --- src/ChunkDef.h | 10 +++------- src/Entities/Player.cpp | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/ChunkDef.h b/src/ChunkDef.h index 959841ecc..2bfa2949c 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -399,21 +399,17 @@ public: typedef std::list cChunkCoordsList; typedef std::vector cChunkCoordsVector; -namespace std -{ - /** A simple hash function for chunk coords, we assume that chunk coords won't use more than 16 bits, so the hash is almost an identity. Used for std::unordered_map */ -template<> struct hash +class cChunkCoordsHash { - size_t operator ()(const cChunkCoords & a_Coords) +public: + size_t operator () (const cChunkCoords & a_Coords) const { return (static_cast(a_Coords.m_ChunkX) << 16) ^ static_cast(a_Coords.m_ChunkZ); } }; -} // namespace std - diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 7bdd1c6f7..1d5cc6554 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -2206,7 +2206,7 @@ void cPlayer::SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_R } // Divide the block changes by their respective chunks: - std::unordered_map Changes; + std::unordered_map Changes; for (const auto & blk: blks) { Changes[cChunkCoords(blk.m_ChunkX, blk.m_ChunkZ)].push_back(blk); -- cgit v1.2.3 From e09348c05d188979c490a9a22d39a1203cacc797 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 24 Dec 2014 09:13:58 +0100 Subject: ByteBuffer: SingleThreadAccessChecker is request-only. It slows the server down way too much, so it can't be turned on by default. --- src/ByteBuffer.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp index 080176dcd..b441d61ca 100644 --- a/src/ByteBuffer.cpp +++ b/src/ByteBuffer.cpp @@ -13,6 +13,15 @@ +/** When defined, each access to a cByteBuffer object is checked whether it's done in the same thread. +cByteBuffer assumes that it is not used by multiple threads at once, this macro adds a runtime check for that. +Unfortunately it is very slow, so it is disabled even for regular DEBUG builds. */ +// #define DEBUG_SINGLE_THREAD_ACCESS + + + + + // Try to determine endianness: #if ( \ defined(__i386__) || defined(__alpha__) || \ @@ -109,7 +118,7 @@ public: -#ifdef _DEBUG +#ifdef DEBUG_SINGLE_THREAD_ACCESS /** Simple RAII class that is used for checking that no two threads are using an object simultanously. It requires the monitored object to provide the storage for a thread ID. -- cgit v1.2.3 From f07784b92f9e402706afce148ecc50430832ae16 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 24 Dec 2014 19:44:15 +0100 Subject: Fixed redstone dust placement on upside-down slabs. --- src/Items/ItemRedstoneDust.h | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Items/ItemRedstoneDust.h b/src/Items/ItemRedstoneDust.h index a2289239c..6d5fb521f 100644 --- a/src/Items/ItemRedstoneDust.h +++ b/src/Items/ItemRedstoneDust.h @@ -27,7 +27,20 @@ public: BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta ) override { - if (!cBlockInfo::FullyOccupiesVoxel(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) // Some solid blocks, such as cocoa beans, are not suitable for dust + // Check if coords are out of range: + if ((a_BlockY <= 0) || (a_BlockY >= cChunkDef::Height)) + { + return false; + } + + // Check the block below, if it supports dust on top of it: + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_World->GetBlockTypeMeta(a_BlockX, a_BlockY - 1, a_BlockZ, BlockType, BlockMeta)) + { + return false; + } + if (!IsBlockTypeUnderSuitable(BlockType, BlockMeta)) { return false; } @@ -36,6 +49,28 @@ public: a_BlockMeta = 0; return true; } + + + /** Returns true if the specified block type / meta is suitable to have redstone dust on top of it. */ + static bool IsBlockTypeUnderSuitable(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) + { + if (cBlockInfo::FullyOccupiesVoxel(a_BlockType)) + { + return true; + } + + switch (a_BlockType) + { + case E_BLOCK_NEW_STONE_SLAB: + case E_BLOCK_WOODEN_SLAB: + case E_BLOCK_STONE_SLAB: + { + // Slabs can support redstone if they're upside down: + return ((a_BlockMeta & 0x08) != 0); + } + } + return false; + } } ; -- cgit v1.2.3 From 5609d76ed7d8026b3bcaeb02fb42bd9ba2f27c96 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 24 Dec 2014 20:02:51 +0100 Subject: APIDump: Updated the player block placement documentation. The hooks now have fewer parameters but are called on all player-placed blocks (#1618). --- src/Entities/Player.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 33ab5293c..b94d2659e 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -442,7 +442,8 @@ public: /** Calls the block-placement hook and places the block in the world, unless refused by the hook. If the hook prevents the placement, sends the current block at the specified coords back to the client. - Assumes that all the blocks are in currently loaded chunks. */ + Assumes that the block is in a currently loaded chunk. + Returns true if the block is successfully placed. */ bool PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); /** Sends the block in the specified range around the specified coord to the client -- cgit v1.2.3 From 081e7ddd028d9382bd52c2b117dae6b6f84225e5 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 25 Dec 2014 00:34:54 +0100 Subject: cIsThread: Fixed a race condition on thread start. --- src/OSSupport/IsThread.cpp | 18 ++++++++++++++++-- src/OSSupport/IsThread.h | 19 ++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp index 94bed1f56..55e96b622 100644 --- a/src/OSSupport/IsThread.cpp +++ b/src/OSSupport/IsThread.cpp @@ -68,11 +68,22 @@ cIsThread::~cIsThread() +void cIsThread::DoExecute(void) +{ + m_evtStart.Wait(); + Execute(); +} + + + + + bool cIsThread::Start(void) { try { - m_Thread = std::thread(&cIsThread::Execute, this); + // Initialize the thread: + m_Thread = std::thread(&cIsThread::DoExecute, this); #if defined (_MSC_VER) && defined(_DEBUG) if (!m_ThreadName.empty()) @@ -81,9 +92,12 @@ bool cIsThread::Start(void) } #endif + // Notify the thread that initialization is complete and it can run its code safely: + m_evtStart.Set(); + return true; } - catch (std::system_error & a_Exception) + catch (const std::system_error & a_Exception) { LOGERROR("cIsThread::Start error %i: could not construct thread %s; %s", a_Exception.code().value(), m_ThreadName.c_str(), a_Exception.code().message().c_str()); return false; diff --git a/src/OSSupport/IsThread.h b/src/OSSupport/IsThread.h index 131c6950e..f642c8724 100644 --- a/src/OSSupport/IsThread.h +++ b/src/OSSupport/IsThread.h @@ -25,23 +25,28 @@ In the descending class' constructor call the Start() method to start the thread class cIsThread { protected: - /// This is the main thread entrypoint + /** This is the main thread entrypoint. + This function, overloaded by the descendants, is called in the new thread. */ virtual void Execute(void) = 0; - /// The overriden Execute() method should check this value periodically and terminate if this is true + /** The overriden Execute() method should check this value periodically and terminate if this is true. */ volatile bool m_ShouldTerminate; +private: + /** Wrapper for Execute() that waits for the initialization event, to prevent race conditions in thread initialization. */ + void DoExecute(void); + public: cIsThread(const AString & a_ThreadName); virtual ~cIsThread(); - /// Starts the thread; returns without waiting for the actual start + /** Starts the thread; returns without waiting for the actual start. */ bool Start(void); - /// Signals the thread to terminate and waits until it's finished + /** Signals the thread to terminate and waits until it's finished. */ void Stop(void); - /// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag + /** Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag. */ bool Wait(void); /** Returns true if the thread calling this function is the thread contained within this object. */ @@ -50,6 +55,10 @@ public: protected: AString m_ThreadName; std::thread m_Thread; + + /** The event that is used to wait with the thread's execution until the thread object is fully initialized. + This prevents the IsCurrentThread() call to fail because of a race-condition. */ + cEvent m_evtStart; } ; -- cgit v1.2.3 From 3da6e4374e72e86930a24441c2b26f8eb582c723 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 25 Dec 2014 00:44:09 +0100 Subject: Fixed basic style. --- src/Mobs/Guardian.cpp | 2 +- src/Mobs/Monster.cpp | 2 +- src/Simulator/FloodyFluidSimulator.cpp | 2 +- src/Simulator/IncrementalRedstoneSimulator.cpp | 88 ++++++++++++++++++++------ 4 files changed, 71 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/Mobs/Guardian.cpp b/src/Mobs/Guardian.cpp index d69ee1683..15908d801 100644 --- a/src/Mobs/Guardian.cpp +++ b/src/Mobs/Guardian.cpp @@ -28,7 +28,7 @@ void cGuardian::GetDrops(cItems & a_Drops, cEntity * a_Killer) } AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_PRISMARINE_SHARD); AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_RAW_FISH); - AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_PRISMARINE_CRYSTALS); // ToDo: Prismarine Crystals only drop if the raw fish drop is 0 + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_PRISMARINE_CRYSTALS); // TODO: Prismarine Crystals only drop if the raw fish drop is 0 } diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index a02ea357e..425c80bf4 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -846,7 +846,7 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) case mtEnderman: return mfHostile; case mtGhast: return mfHostile; case mtGiant: return mfNoSpawn; - case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! + case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! case mtHorse: return mfPassive; case mtIronGolem: return mfPassive; case mtMagmaCube: return mfHostile; diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp index 37d58307b..bcd083294 100644 --- a/src/Simulator/FloodyFluidSimulator.cpp +++ b/src/Simulator/FloodyFluidSimulator.cpp @@ -105,7 +105,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re } // Spread to the neighbors: if (SpreadFurther && (NewMeta < 8)) - { + { SpreadXZ(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta); } // If source creation is on, check for it here: diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 544a0689d..7ff499eb5 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -93,7 +93,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, ), SimulatedPlayerToggleableBlocks.end()); - auto & RepeatersDelayList = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_RepeatersDelayList; + auto & RepeatersDelayList = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_RepeatersDelayList; RepeatersDelayList.erase(std::remove_if(RepeatersDelayList.begin(), RepeatersDelayList.end(), [RelX, a_BlockY, RelZ, Block](const sRepeatersDelayList & itr) { return itr.a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ)) && (Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF); @@ -1488,62 +1488,92 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel for (const auto & itr : *m_PoweredBlocks) { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } switch (a_Meta & 0x3) { case 0x0: { // Flip the coords to check the back of the repeater - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) { return true; } + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) + { + return true; + } break; } case 0x1: { - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) + { + return true; + } break; } case 0x2: { - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) { return true; } + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) + { + return true; + } break; } case 0x3: { - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) + { + return true; + } break; } } - } + } // for itr - m_PoweredBlocks[] for (const auto & itr : *m_LinkedPoweredBlocks) { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } switch (a_Meta & 0x3) { case 0x0: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) { return true; } + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) + { + return true; + } break; } case 0x1: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) + { + return true; + } break; } case 0x2: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) { return true; } + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) + { + return true; + } break; } case 0x3: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) + { + return true; + } break; } } - } + } // for itr - m_LinkedPoweredBlocks[] return false; // Couldn't find power source behind repeater } @@ -1559,10 +1589,13 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB case 0x0: case 0x2: { - // Check if eastern(right) neighbor is a powered on repeater who is facing us + // Check if eastern (right) neighbor is a powered on repeater who is facing us BLOCKTYPE Block = 0; NIBBLETYPE OtherRepeaterDir = 0; - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) // Is right neighbor a powered repeater? + if ( + m_Chunk->UnboundedRelGetBlock(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && + (Block == E_BLOCK_REDSTONE_REPEATER_ON) + ) { if ((OtherRepeaterDir & 0x03) == 0x3) { @@ -1571,7 +1604,10 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB } // Check if western(left) neighbor is a powered on repeater who is facing us - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) + if ( + m_Chunk->UnboundedRelGetBlock(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && + (Block == E_BLOCK_REDSTONE_REPEATER_ON) + ) { if ((OtherRepeaterDir & 0x03) == 0x1) { @@ -1590,7 +1626,10 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB BLOCKTYPE Block = 0; NIBBLETYPE OtherRepeaterDir = 0; - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) + if ( + m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, Block, OtherRepeaterDir) && + (Block == E_BLOCK_REDSTONE_REPEATER_ON) + ) { if ((OtherRepeaterDir & 0x30) == 0x00) { @@ -1599,7 +1638,10 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB } // Check if northern(up) neighbor is a powered on repeater who is facing us - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) + if ( + m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, Block, OtherRepeaterDir) && + (Block == E_BLOCK_REDSTONE_REPEATER_ON) + ) { if ((OtherRepeaterDir & 0x03) == 0x02) { @@ -1625,7 +1667,10 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBl for (const auto & itr : *m_PoweredBlocks) { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } int X = a_RelBlockX, Z = a_RelBlockZ; AddFaceDirection(X, a_RelBlockY, Z, Face); @@ -1638,7 +1683,10 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBl for (const auto & itr : *m_LinkedPoweredBlocks) { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } int X = a_RelBlockX, Z = a_RelBlockZ; AddFaceDirection(X, a_RelBlockY, Z, Face); -- cgit v1.2.3 From 1af621046652b4b79672342a2c40748cac8137cb Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 25 Dec 2014 00:51:24 +0100 Subject: Fixed forgotten big flower handler. --- src/Items/ItemHandler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index 92c55ec62..621cf9501 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -106,8 +106,10 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType) default: return new cItemHandler(a_ItemType); // Single item per handler, alphabetically sorted: + case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler; case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType); case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); + case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType); case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType); case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType); case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler; @@ -134,7 +136,6 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType) case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler(); - case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType); case E_ITEM_MAP: return new cItemMapHandler(); case E_ITEM_MILK: return new cItemMilkHandler(); case E_ITEM_MUSHROOM_SOUP: return new cItemMushroomSoupHandler(a_ItemType); -- cgit v1.2.3 From 9e478c6f29702002c227dc06c0a1f49d1568de53 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 25 Dec 2014 01:00:51 +0100 Subject: Fixed door placement. --- src/Blocks/BlockDoor.h | 3 ++- src/Items/ItemDoor.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h index 334519077..53f84b553 100644 --- a/src/Blocks/BlockDoor.h +++ b/src/Blocks/BlockDoor.h @@ -117,7 +117,8 @@ public: static bool CanBeOn(BLOCKTYPE a_BlockType) { // Vanilla refuses to place doors on transparent blocks - return !cBlockInfo::IsTransparent(a_BlockType); + // We need to keep the door compatible with itself, otherwise the top half drops while the bottom half stays + return !cBlockInfo::IsTransparent(a_BlockType) || IsDoorBlockType(a_BlockType); } diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h index dbba26728..dacf286e5 100644 --- a/src/Items/ItemDoor.h +++ b/src/Items/ItemDoor.h @@ -27,7 +27,7 @@ public: ) override { // Vanilla only allows door placement while clicking on the top face of the block below the door: - if (a_BlockFace != BLOCK_FACE_NONE) + if (a_BlockFace != BLOCK_FACE_TOP) { return false; } @@ -40,7 +40,7 @@ public: } // The door needs a compatible block below it: - if ((a_BlockY > 0) && cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + if ((a_BlockY > 0) && !cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) { return false; } -- cgit v1.2.3 From eddbce64be8af048581afb0db85a27dd50af26a6 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 25 Dec 2014 17:15:19 +0100 Subject: MobHeads: fixed regular head placement. --- src/Items/ItemMobHead.h | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Items/ItemMobHead.h b/src/Items/ItemMobHead.h index d962dabae..ac905275a 100644 --- a/src/Items/ItemMobHead.h +++ b/src/Items/ItemMobHead.h @@ -53,7 +53,7 @@ public: ) { // Place the block: - if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_HEAD, static_cast(a_EquippedItem.m_ItemType))) + if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_HEAD, BlockFaceToBlockMeta(a_BlockFace))) { return false; } @@ -92,7 +92,7 @@ public: m_BlockMeta(a_BlockMeta) {} }; - cCallback Callback(a_Player, static_cast(a_EquippedItem.m_ItemType), static_cast(a_BlockFace)); + cCallback Callback(a_Player, static_cast(a_EquippedItem.m_ItemDamage), static_cast(a_BlockFace)); a_World.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback); return true; } @@ -279,6 +279,26 @@ public: } + /** Converts the block face of the placement (which face of the block was clicked to place the head) + into the block's metadata value. */ + static NIBBLETYPE BlockFaceToBlockMeta(int a_BlockFace) + { + switch (a_BlockFace) + { + case BLOCK_FACE_TOP: return 0x01; // On ground (rotation provided in block entity) + case BLOCK_FACE_XM: return 0x04; // west wall, facing east + case BLOCK_FACE_XP: return 0x05; // east wall, facing west + case BLOCK_FACE_ZM: return 0x02; // north wall, facing south + case BLOCK_FACE_ZP: return 0x03; // south wall, facing north + default: + { + ASSERT(!"Unhandled block face"); + return 0; + } + } + } + + virtual bool IsPlaceable(void) override { return true; -- cgit v1.2.3 From 19ff14752eca260e2dfcbf4e8aa3cb11a044383d Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 25 Dec 2014 20:41:27 +0100 Subject: MobHead: Fixed wither spawning. --- src/Items/ItemMobHead.h | 53 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/Items/ItemMobHead.h b/src/Items/ItemMobHead.h index ac905275a..8780f7e4b 100644 --- a/src/Items/ItemMobHead.h +++ b/src/Items/ItemMobHead.h @@ -150,9 +150,9 @@ public: // Image for the wither at the X axis: static const sSetBlock ImageWitherX[] = { - {-1, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER}, - { 0, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER}, - { 1, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER}, + {-1, 0, 0, E_BLOCK_HEAD, 0}, + { 0, 0, 0, E_BLOCK_HEAD, 0}, + { 1, 0, 0, E_BLOCK_HEAD, 0}, {-1, -1, 0, E_BLOCK_SOULSAND, 0}, { 0, -1, 0, E_BLOCK_SOULSAND, 0}, { 1, -1, 0, E_BLOCK_SOULSAND, 0}, @@ -164,9 +164,9 @@ public: // Image for the wither at the Z axis: static const sSetBlock ImageWitherZ[] = { - { 0, 0, -1, E_BLOCK_HEAD, E_META_HEAD_WITHER}, - { 0, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER}, - { 0, 0, 1, E_BLOCK_HEAD, E_META_HEAD_WITHER}, + { 0, 0, -1, E_BLOCK_HEAD, 0}, + { 0, 0, 0, E_BLOCK_HEAD, 0}, + { 0, 0, 1, E_BLOCK_HEAD, 0}, { 0, -1, -1, E_BLOCK_SOULSAND, 0}, { 0, -1, 0, E_BLOCK_SOULSAND, 0}, { 0, -1, 1, E_BLOCK_SOULSAND, 0}, @@ -208,14 +208,14 @@ public: for (size_t i = 0; i < a_ImageCount; i++) { // Get the absolute coords of the image: - int BlockX = a_PlacedHeadX + a_OffsetX + a_Image[i].m_RelX; - int BlockY = a_PlacedHeadY + a_Image[i].m_RelY; - int BlockZ = a_PlacedHeadZ + a_OffsetZ + a_Image[i].m_RelZ; + int BlockX = a_PlacedHeadX + a_OffsetX + a_Image[i].GetX(); + int BlockY = a_PlacedHeadY + a_Image[i].GetY(); + int BlockZ = a_PlacedHeadZ + a_OffsetZ + a_Image[i].GetZ(); // If the query is for the placed head, short-circuit-evaluate it: if ((BlockX == a_PlacedHeadX) && (BlockY == a_PlacedHeadY) && (BlockZ == a_PlacedHeadZ)) { - if ((a_Image[i].m_BlockType != E_BLOCK_HEAD) || (a_Image[i].m_BlockMeta != E_META_HEAD_WITHER)) + if (a_Image[i].m_BlockType != E_BLOCK_HEAD) { return false; // Didn't match } @@ -232,14 +232,41 @@ public: } // Compare the world block: - if ((BlockType != a_Image[i].m_BlockType) || (BlockMeta != a_Image[i].m_BlockMeta)) + if (BlockType != a_Image[i].m_BlockType) { - return false; // Didn't match + return false; + } + + // If it is a mob head, check the correct head type using the block entity: + if (BlockType == E_BLOCK_HEAD) + { + class cHeadCallback: public cBlockEntityCallback + { + virtual bool Item(cBlockEntity * a_Entity) override + { + ASSERT(a_Entity->GetBlockType() == E_BLOCK_HEAD); + cMobHeadEntity * MobHead = static_cast(a_Entity); + m_IsWitherHead = (MobHead->GetType() == SKULL_TYPE_WITHER); + return true; + } + public: + cHeadCallback(void): + m_IsWitherHead(false) + { + } + bool m_IsWitherHead; + } callback; + a_World.DoWithBlockEntityAt(BlockX, BlockY, BlockZ, callback); + if (!callback.m_IsWitherHead) + { + return false; + } } // Matched, continue checking + AirBlocks.emplace_back(BlockX, BlockY, BlockZ, E_BLOCK_AIR, 0); } // for i - a_Image - // All image blocks matched, try place the wither: + // All image blocks matched, try replace the image with air blocks: if (!a_Player.PlaceBlocks(AirBlocks)) { return false; -- cgit v1.2.3 From 35a3a1b9f4fd52b37106271964157fc3b7a9ee84 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 3 Jan 2015 22:20:30 +0100 Subject: cByteBuffer: Improved SingleThreadAccessChecker performance. But it's still poor and unusable for regular testing. --- src/ByteBuffer.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp index b441d61ca..e1e5867a9 100644 --- a/src/ByteBuffer.cpp +++ b/src/ByteBuffer.cpp @@ -131,7 +131,7 @@ public: { ASSERT( (*a_ThreadID == std::this_thread::get_id()) || // Either the object is used by current thread... - (*a_ThreadID == std::thread::id()) // ... or by no thread at all + (*a_ThreadID == m_EmptyThreadID) // ... or by no thread at all ); // Mark as being used by this thread: @@ -147,8 +147,13 @@ public: protected: /** Points to the storage used for ID of the thread using the object. */ std::thread::id * m_ThreadID; + + /** The value of an unassigned thread ID, used to speed up checking. */ + static std::thread::id m_EmptyThreadID; }; + std::thread::id cSingleThreadAccessChecker::m_EmptyThreadID; + #define CHECK_THREAD cSingleThreadAccessChecker Checker(&m_ThreadID); #else -- cgit v1.2.3 From 06c2669cf6b03feb15990004087e882ac846b061 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 3 Jan 2015 22:23:49 +0100 Subject: Protocols: Ignore garbage data at the end of PluginMessage packets. Fixes #1692. --- src/Protocol/Protocol17x.cpp | 11 +++++++++++ src/Protocol/Protocol18x.cpp | 10 ++++++++++ 2 files changed, 21 insertions(+) (limited to 'src') diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 34103ea5a..4e985355a 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -2242,6 +2242,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const { if (a_Channel == "MC|AdvCdm") { + size_t BeginningSpace = a_ByteBuffer.GetReadableSpace(); HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Mode); switch (Mode) { @@ -2265,6 +2266,16 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const return; } } // switch (Mode) + + // Read the remainder of the packet (Vanilla sometimes sends bogus data at the end of the packet; #1692): + size_t BytesRead = BeginningSpace - a_ByteBuffer.GetReadableSpace(); + if (BytesRead < static_cast(a_PayloadLength)) + { + LOGD("Protocol 1.7: Skipping garbage data at the end of a vanilla MC|AdvCdm packet, %u bytes", + a_PayloadLength - BytesRead + ); + a_ByteBuffer.SkipRead(a_PayloadLength - BytesRead); + } return; } else if (a_Channel == "MC|Brand") diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 72827ac47..21b098735 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -2331,6 +2331,16 @@ void cProtocol180::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) if (Channel.substr(0, 3) == "MC|") { HandleVanillaPluginMessage(a_ByteBuffer, Channel); + + // Skip any unread data (vanilla sometimes sends garbage at the end of a packet; #1692): + if (a_ByteBuffer.GetReadableSpace() > 1) + { + LOGD("Protocol 1.8: Skipping garbage data at the end of a vanilla PluginMessage packet, %u bytes", + a_ByteBuffer.GetReadableSpace() - 1 + ); + a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); + } + return; } -- cgit v1.2.3 From 7d13a2a77a030a4f458ac01a51f8a4216a4a44b4 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 3 Jan 2015 22:39:55 +0100 Subject: Fixed Linux compilation. --- src/Protocol/Protocol17x.cpp | 2 +- src/Protocol/Protocol18x.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 4e985355a..dac1ebde8 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -2272,7 +2272,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const if (BytesRead < static_cast(a_PayloadLength)) { LOGD("Protocol 1.7: Skipping garbage data at the end of a vanilla MC|AdvCdm packet, %u bytes", - a_PayloadLength - BytesRead + static_cast(a_PayloadLength - BytesRead) ); a_ByteBuffer.SkipRead(a_PayloadLength - BytesRead); } diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 21b098735..3c4e049bd 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -2336,7 +2336,7 @@ void cProtocol180::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) if (a_ByteBuffer.GetReadableSpace() > 1) { LOGD("Protocol 1.8: Skipping garbage data at the end of a vanilla PluginMessage packet, %u bytes", - a_ByteBuffer.GetReadableSpace() - 1 + static_cast(a_ByteBuffer.GetReadableSpace() - 1) ); a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); } -- cgit v1.2.3 From 4f75b94c99eeb1642a5ec46144542bfcd18af934 Mon Sep 17 00:00:00 2001 From: Tycho Date: Sun, 11 Jan 2015 01:54:18 +0000 Subject: Created new type cTickTime and rewrote cWorld::TickThread to use it --- src/Globals.h | 3 +++ src/World.cpp | 11 +++++------ 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/Globals.h b/src/Globals.h index 61f500db9..b8e9ec7ed 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -419,6 +419,8 @@ std::unique_ptr make_unique(Args&&... args) return std::unique_ptr(new T(args...)); } +// a tick is 50 ms +using cTickTime = std::chrono::duration>>; #ifndef TOLUA_TEMPLATE_BIND #define TOLUA_TEMPLATE_BIND(x) @@ -436,3 +438,4 @@ std::unique_ptr make_unique(Args&&... args) + diff --git a/src/World.cpp b/src/World.cpp index c08b44f77..2dd03497b 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -233,8 +233,7 @@ cWorld::cTickThread::cTickThread(cWorld & a_World) : void cWorld::cTickThread::Execute(void) { auto LastTime = std::chrono::steady_clock::now(); - static const auto msPerTick = std::chrono::milliseconds(50); - auto TickTime = std::chrono::steady_clock::duration(50); + auto TickTime = std::chrono::duration_cast(cTickTime(1)); while (!m_ShouldTerminate) { @@ -242,12 +241,12 @@ void cWorld::cTickThread::Execute(void) auto msec = std::chrono::duration_cast(NowTime - LastTime).count(); auto LastTickMsec = std::chrono::duration_cast>(TickTime).count(); m_World.Tick(static_cast(msec), LastTickMsec); - TickTime = std::chrono::steady_clock::now() - NowTime; + TickTime = std::chrono::duration_cast(std::chrono::steady_clock::now() - NowTime); - if (TickTime < msPerTick) + if (TickTime < cTickTime(1)) { - // Stretch tick time until it's at least msPerTick - std::this_thread::sleep_for(msPerTick - TickTime); + // Stretch tick time until it's at least 1 tick + std::this_thread::sleep_for(cTickTime(1) - TickTime); } LastTime = NowTime; -- cgit v1.2.3 From 2a9664d6ca8aa9eb4f554301e4d9b0ec33b465ce Mon Sep 17 00:00:00 2001 From: Tycho Date: Sun, 11 Jan 2015 21:12:26 +0000 Subject: Initial convertion of a_Dt to std::chrono also refactored cWorld::m_WorldAge and cWorld::m_TimeOfDay --- src/Bindings/LuaState.cpp | 7 +++ src/Bindings/LuaState.h | 1 + src/Bindings/Plugin.h | 2 +- src/Bindings/PluginLua.cpp | 2 +- src/Bindings/PluginLua.h | 2 +- src/Bindings/PluginManager.cpp | 2 +- src/Bindings/PluginManager.h | 2 +- src/BlockEntities/BeaconEntity.cpp | 2 +- src/BlockEntities/BeaconEntity.h | 2 +- src/BlockEntities/BlockEntity.h | 2 +- src/BlockEntities/CommandBlockEntity.cpp | 2 +- src/BlockEntities/CommandBlockEntity.h | 2 +- src/BlockEntities/DropSpenserEntity.cpp | 2 +- src/BlockEntities/DropSpenserEntity.h | 2 +- src/BlockEntities/FurnaceEntity.cpp | 2 +- src/BlockEntities/FurnaceEntity.h | 2 +- src/BlockEntities/HopperEntity.cpp | 2 +- src/BlockEntities/HopperEntity.h | 2 +- src/BlockEntities/MobSpawnerEntity.cpp | 2 +- src/BlockEntities/MobSpawnerEntity.h | 2 +- src/Chunk.cpp | 2 +- src/Chunk.h | 2 +- src/ChunkMap.cpp | 4 +- src/ChunkMap.h | 4 +- src/Entities/ArrowEntity.cpp | 6 +- src/Entities/ArrowEntity.h | 2 +- src/Entities/Boat.cpp | 2 +- src/Entities/Boat.h | 2 +- src/Entities/EnderCrystal.cpp | 2 +- src/Entities/EnderCrystal.h | 2 +- src/Entities/Entity.cpp | 26 ++++----- src/Entities/Entity.h | 4 +- src/Entities/ExpOrb.cpp | 4 +- src/Entities/ExpOrb.h | 2 +- src/Entities/FallingBlock.cpp | 4 +- src/Entities/FallingBlock.h | 2 +- src/Entities/FireworkEntity.cpp | 8 +-- src/Entities/FireworkEntity.h | 4 +- src/Entities/Floater.cpp | 2 +- src/Entities/Floater.h | 2 +- src/Entities/HangingEntity.h | 2 +- src/Entities/Minecart.cpp | 10 ++-- src/Entities/Minecart.h | 4 +- src/Entities/Painting.cpp | 2 +- src/Entities/Painting.h | 2 +- src/Entities/Pawn.cpp | 2 +- src/Entities/Pawn.h | 2 +- src/Entities/Pickup.cpp | 6 +- src/Entities/Pickup.h | 2 +- src/Entities/Player.cpp | 2 +- src/Entities/Player.h | 4 +- src/Entities/ProjectileEntity.cpp | 4 +- src/Entities/ProjectileEntity.h | 4 +- src/Entities/SplashPotionEntity.h | 2 +- src/Entities/TNTEntity.cpp | 2 +- src/Entities/TNTEntity.h | 2 +- src/Entities/ThrownEggEntity.cpp | 2 +- src/Entities/ThrownEggEntity.h | 2 +- src/Entities/ThrownEnderPearlEntity.cpp | 2 +- src/Entities/ThrownEnderPearlEntity.h | 2 +- src/Entities/ThrownSnowballEntity.cpp | 2 +- src/Entities/ThrownSnowballEntity.h | 2 +- src/Globals.h | 1 + src/Mobs/AggressiveMonster.cpp | 6 +- src/Mobs/AggressiveMonster.h | 4 +- src/Mobs/Blaze.cpp | 4 +- src/Mobs/Blaze.h | 2 +- src/Mobs/CaveSpider.cpp | 4 +- src/Mobs/CaveSpider.h | 4 +- src/Mobs/Chicken.cpp | 2 +- src/Mobs/Chicken.h | 2 +- src/Mobs/Creeper.cpp | 4 +- src/Mobs/Creeper.h | 4 +- src/Mobs/Enderman.cpp | 2 +- src/Mobs/Enderman.h | 2 +- src/Mobs/Ghast.cpp | 4 +- src/Mobs/Ghast.h | 2 +- src/Mobs/Guardian.cpp | 2 +- src/Mobs/Guardian.h | 2 +- src/Mobs/Horse.cpp | 2 +- src/Mobs/Horse.h | 2 +- src/Mobs/Monster.cpp | 12 ++-- src/Mobs/Monster.h | 2 +- src/Mobs/PassiveMonster.cpp | 2 +- src/Mobs/PassiveMonster.h | 2 +- src/Mobs/Pig.cpp | 2 +- src/Mobs/Pig.h | 2 +- src/Mobs/Sheep.cpp | 2 +- src/Mobs/Sheep.h | 2 +- src/Mobs/Skeleton.cpp | 4 +- src/Mobs/Skeleton.h | 2 +- src/Mobs/Slime.cpp | 2 +- src/Mobs/Slime.h | 2 +- src/Mobs/SnowGolem.cpp | 2 +- src/Mobs/SnowGolem.h | 2 +- src/Mobs/Squid.cpp | 2 +- src/Mobs/Squid.h | 2 +- src/Mobs/Villager.cpp | 2 +- src/Mobs/Villager.h | 2 +- src/Mobs/Wither.cpp | 2 +- src/Mobs/Wither.h | 2 +- src/Mobs/Wolf.cpp | 4 +- src/Mobs/Wolf.h | 4 +- src/Simulator/DelayedFluidSimulator.cpp | 2 +- src/Simulator/DelayedFluidSimulator.h | 2 +- src/Simulator/FireSimulator.cpp | 4 +- src/Simulator/FireSimulator.h | 2 +- src/Simulator/IncrementalRedstoneSimulator.cpp | 2 +- src/Simulator/IncrementalRedstoneSimulator.h | 2 +- src/Simulator/NoopRedstoneSimulator.h | 2 +- src/Simulator/SandSimulator.cpp | 2 +- src/Simulator/SandSimulator.h | 2 +- src/Simulator/Simulator.h | 2 +- src/Simulator/SimulatorManager.cpp | 2 +- src/Simulator/SimulatorManager.h | 2 +- src/World.cpp | 79 ++++++++++++-------------- src/World.h | 28 +++++---- 117 files changed, 221 insertions(+), 221 deletions(-) (limited to 'src') diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 2f5d173fd..4bc9906ee 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -839,6 +839,13 @@ void cLuaState::Push(void * a_Ptr) m_NumCurrentFunctionArgs += 1; } +void cLuaState::Push(std::chrono::milliseconds a_Value) +{ + ASSERT(IsValid()); + + tolua_pushnumber(m_LuaState, a_Value.count()); + m_NumCurrentFunctionArgs += 1; +} diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index c13e36188..7ac4120e1 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -217,6 +217,7 @@ public: void Push(Vector3d * a_Vector); void Push(Vector3i * a_Vector); void Push(void * a_Ptr); + void Push(std::chrono::milliseconds a_time); /** Retrieve value at a_StackPos, if it is a valid bool. If not, a_Value is unchanged */ void GetStackValue(int a_StackPos, bool & a_Value); diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index b5944e9cb..6210dbed4 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -104,7 +104,7 @@ public: virtual bool OnWeatherChanged (cWorld & a_World) = 0; virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) = 0; virtual bool OnWorldStarted (cWorld & a_World) = 0; - virtual bool OnWorldTick (cWorld & a_World, float a_Dt, int a_LastTickDurationMSec) = 0; + virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) = 0; /** Handles the command split into a_Split, issued by player a_Player. Command permissions have already been checked. diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index cc3146610..500913e76 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -1430,7 +1430,7 @@ bool cPluginLua::OnWorldStarted(cWorld & a_World) -bool cPluginLua::OnWorldTick(cWorld & a_World, float a_Dt, int a_LastTickDurationMSec) +bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { cCSLock Lock(m_CriticalSection); cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK]; diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index ad3f82b42..f443f5fc0 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -128,7 +128,7 @@ public: virtual bool OnWeatherChanged (cWorld & a_World) override; virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) override; virtual bool OnWorldStarted (cWorld & a_World) override; - virtual bool OnWorldTick (cWorld & a_World, float a_Dt, int a_LastTickDurationMSec) override; + virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) override; virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index fad0a36d2..58faf54d3 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -1394,7 +1394,7 @@ bool cPluginManager::CallHookWorldStarted(cWorld & a_World) -bool cPluginManager::CallHookWorldTick(cWorld & a_World, float a_Dt, int a_LastTickDurationMSec) +bool cPluginManager::CallHookWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { FIND_HOOK(HOOK_WORLD_TICK); VERIFY_HOOK; diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index d4b82376a..7b35e92c4 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -237,7 +237,7 @@ public: bool CallHookWeatherChanged (cWorld & a_World); bool CallHookWeatherChanging (cWorld & a_World, eWeather & a_NewWeather); bool CallHookWorldStarted (cWorld & a_World); - bool CallHookWorldTick (cWorld & a_World, float a_Dt, int a_LastTickDurationMSec); + bool CallHookWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec); bool DisablePlugin(const AString & a_PluginName); // tolua_export bool LoadPlugin (const AString & a_PluginName); // tolua_export diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp index 409f2937c..37ce7a8ab 100644 --- a/src/BlockEntities/BeaconEntity.cpp +++ b/src/BlockEntities/BeaconEntity.cpp @@ -266,7 +266,7 @@ void cBeaconEntity::GiveEffects(void) -bool cBeaconEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cBeaconEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Update the beacon every 4 seconds if ((GetWorld()->GetWorldAge() % 80) == 0) diff --git a/src/BlockEntities/BeaconEntity.h b/src/BlockEntities/BeaconEntity.h index bc27e92b0..4f723c617 100644 --- a/src/BlockEntities/BeaconEntity.h +++ b/src/BlockEntities/BeaconEntity.h @@ -30,7 +30,7 @@ public: // cBlockEntity overrides: virtual void SendTo(cClientHandle & a_Client) override; - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void UsedBy(cPlayer * a_Player) override; /** Modify the beacon level. (It is needed to load the beacon corectly) */ diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h index 056a88721..85f75a523 100644 --- a/src/BlockEntities/BlockEntity.h +++ b/src/BlockEntities/BlockEntity.h @@ -110,7 +110,7 @@ public: virtual void SendTo(cClientHandle & a_Client) = 0; /// Ticks the entity; returns true if the chunk should be marked as dirty as a result of this ticking. By default does nothing. - virtual bool Tick(float a_Dt, cChunk & a_Chunk) + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); return false; diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp index b7f938ffd..13c2637dc 100644 --- a/src/BlockEntities/CommandBlockEntity.cpp +++ b/src/BlockEntities/CommandBlockEntity.cpp @@ -125,7 +125,7 @@ void cCommandBlockEntity::SetRedstonePower(bool a_IsPowered) -bool cCommandBlockEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cCommandBlockEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); UNUSED(a_Chunk); diff --git a/src/BlockEntities/CommandBlockEntity.h b/src/BlockEntities/CommandBlockEntity.h index d8ac054f0..c525ebc26 100644 --- a/src/BlockEntities/CommandBlockEntity.h +++ b/src/BlockEntities/CommandBlockEntity.h @@ -33,7 +33,7 @@ public: /// Creates a new empty command block entity cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SendTo(cClientHandle & a_Client) override; virtual void UsedBy(cPlayer * a_Player) override; diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp index 3f98836e7..5e98506f1 100644 --- a/src/BlockEntities/DropSpenserEntity.cpp +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -125,7 +125,7 @@ void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered) -bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cDropSpenserEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); if (!m_ShouldDropSpense) diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h index 6c23a402f..83500addb 100644 --- a/src/BlockEntities/DropSpenserEntity.h +++ b/src/BlockEntities/DropSpenserEntity.h @@ -47,7 +47,7 @@ public: virtual ~cDropSpenserEntity(); // cBlockEntity overrides: - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SendTo(cClientHandle & a_Client) override; virtual void UsedBy(cPlayer * a_Player) override; diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index d2ec2bd1e..cc5a00af7 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -90,7 +90,7 @@ bool cFurnaceEntity::ContinueCooking(void) -bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cFurnaceEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h index fbe9d6c75..8b3ba3e36 100644 --- a/src/BlockEntities/FurnaceEntity.h +++ b/src/BlockEntities/FurnaceEntity.h @@ -42,7 +42,7 @@ public: // cBlockEntity overrides: virtual void SendTo(cClientHandle & a_Client) override; - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void UsedBy(cPlayer * a_Player) override; virtual void Destroy() override { diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index fe3fb85c4..c2d6cabbb 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -54,7 +54,7 @@ bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, i -bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cHopperEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h index da65aa671..af99c526d 100644 --- a/src/BlockEntities/HopperEntity.h +++ b/src/BlockEntities/HopperEntity.h @@ -48,7 +48,7 @@ protected: Int64 m_LastMoveItemsOutTick; // cBlockEntity overrides: - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SendTo(cClientHandle & a_Client) override; virtual void UsedBy(cPlayer * a_Player) override; diff --git a/src/BlockEntities/MobSpawnerEntity.cpp b/src/BlockEntities/MobSpawnerEntity.cpp index a7d29638a..9b3f605f9 100644 --- a/src/BlockEntities/MobSpawnerEntity.cpp +++ b/src/BlockEntities/MobSpawnerEntity.cpp @@ -73,7 +73,7 @@ void cMobSpawnerEntity::UpdateActiveState(void) -bool cMobSpawnerEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Update the active flag every 5 seconds if ((m_World->GetWorldAge() % 100) == 0) diff --git a/src/BlockEntities/MobSpawnerEntity.h b/src/BlockEntities/MobSpawnerEntity.h index 594b5301e..b572e6657 100644 --- a/src/BlockEntities/MobSpawnerEntity.h +++ b/src/BlockEntities/MobSpawnerEntity.h @@ -29,7 +29,7 @@ public: virtual void SendTo(cClientHandle & a_Client) override; virtual void UsedBy(cPlayer * a_Player) override; - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // tolua_begin diff --git a/src/Chunk.cpp b/src/Chunk.cpp index b06eed316..979492b46 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -601,7 +601,7 @@ void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) -void cChunk::Tick(float a_Dt) +void cChunk::Tick(std::chrono::milliseconds a_Dt) { BroadcastPendingBlockChanges(); diff --git a/src/Chunk.h b/src/Chunk.h index 6f4ac5cac..1ce862371 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -160,7 +160,7 @@ public: /** Try to Spawn Monsters inside chunk */ void SpawnMobs(cMobSpawner& a_MobSpawner); - void Tick(float a_Dt); + void Tick(std::chrono::milliseconds a_Dt); /** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */ void TickBlock(int a_RelX, int a_RelY, int a_RelZ); diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index c8f5aa673..2dd04f801 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -2720,7 +2720,7 @@ void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner) -void cChunkMap::Tick(float a_Dt) +void cChunkMap::Tick(std::chrono::milliseconds a_Dt) { cCSLock Lock(m_CSLayers); for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) @@ -2948,7 +2948,7 @@ void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) -void cChunkMap::cChunkLayer::Tick(float a_Dt) +void cChunkMap::cChunkLayer::Tick(std::chrono::milliseconds a_Dt) { for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++) { diff --git a/src/ChunkMap.h b/src/ChunkMap.h index fe0bb3733..f08d02337 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -348,7 +348,7 @@ public: /** Try to Spawn Monsters inside all Chunks */ void SpawnMobs(cMobSpawner& a_MobSpawner); - void Tick(float a_Dt); + void Tick(std::chrono::milliseconds a_Dt); /** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */ void TickBlock(int a_BlockX, int a_BlockY, int a_BlockZ); @@ -415,7 +415,7 @@ private: /** Try to Spawn Monsters inside all Chunks */ void SpawnMobs(cMobSpawner& a_MobSpawner); - void Tick(float a_Dt); + void Tick(std::chrono::milliseconds a_Dt); void RemoveClient(cClientHandle * a_Client); diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index 30f18f677..2ec825ddb 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -174,10 +174,10 @@ void cArrowEntity::CollectedBy(cPlayer & a_Dest) -void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - m_Timer += a_Dt; + m_Timer += a_Dt.count(); if (m_bIsCollected) { @@ -209,7 +209,7 @@ void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk) } else { - m_HitGroundTimer += a_Dt; + m_HitGroundTimer += a_Dt.count(); } } diff --git a/src/Entities/ArrowEntity.h b/src/Entities/ArrowEntity.h index 436ec0293..8c92049b0 100644 --- a/src/Entities/ArrowEntity.h +++ b/src/Entities/ArrowEntity.h @@ -103,6 +103,6 @@ protected: virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; virtual void CollectedBy(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; }; // tolua_export diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp index 953213bca..6d8b4ef31 100644 --- a/src/Entities/Boat.cpp +++ b/src/Entities/Boat.cpp @@ -91,7 +91,7 @@ void cBoat::OnRightClicked(cPlayer & a_Player) -void cBoat::Tick(float a_Dt, cChunk & a_Chunk) +void cBoat::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); BroadcastMovementUpdate(); diff --git a/src/Entities/Boat.h b/src/Entities/Boat.h index 8de88d165..a873ff822 100644 --- a/src/Entities/Boat.h +++ b/src/Entities/Boat.h @@ -27,7 +27,7 @@ public: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void OnRightClicked(cPlayer & a_Player) override; virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override; cBoat(double a_X, double a_Y, double a_Z); diff --git a/src/Entities/EnderCrystal.cpp b/src/Entities/EnderCrystal.cpp index 30df2c110..7a911d4db 100644 --- a/src/Entities/EnderCrystal.cpp +++ b/src/Entities/EnderCrystal.cpp @@ -29,7 +29,7 @@ void cEnderCrystal::SpawnOn(cClientHandle & a_ClientHandle) -void cEnderCrystal::Tick(float a_Dt, cChunk & a_Chunk) +void cEnderCrystal::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); // No further processing (physics e.t.c.) is needed diff --git a/src/Entities/EnderCrystal.h b/src/Entities/EnderCrystal.h index c98c3b681..8f7e2e9b9 100644 --- a/src/Entities/EnderCrystal.h +++ b/src/Entities/EnderCrystal.h @@ -23,7 +23,7 @@ private: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override; }; // tolua_export diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 54b9f2a20..c64d94528 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -772,7 +772,7 @@ void cEntity::SetHealth(int a_Health) -void cEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { m_TicksAlive++; @@ -841,7 +841,7 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk) -void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { int BlockX = POSX_TOINT; int BlockY = POSY_TOINT; @@ -851,15 +851,15 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, BlockX, BlockZ) // TODO Add collision detection with entities. - a_Dt /= 1000; // Convert from msec to sec + auto DtSec = std::chrono::duration_cast>(a_Dt); Vector3d NextPos = Vector3d(GetPosX(), GetPosY(), GetPosZ()); Vector3d NextSpeed = Vector3d(GetSpeedX(), GetSpeedY(), GetSpeedZ()); if ((BlockY >= cChunkDef::Height) || (BlockY < 0)) { // Outside of the world - AddSpeedY(m_Gravity * a_Dt); - AddPosition(GetSpeed() * a_Dt); + AddSpeedY(m_Gravity * DtSec.count()); + AddPosition(GetSpeed() * DtSec.count()); return; } @@ -930,8 +930,8 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) float fallspeed; if (IsBlockWater(BlockIn)) { - fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water - ApplyFriction(NextSpeed, 0.7, a_Dt); + fallspeed = m_Gravity * DtSec.count() / 3; // Fall 3x slower in water + ApplyFriction(NextSpeed, 0.7, DtSec.count()); } else if (BlockIn == E_BLOCK_COBWEB) { @@ -941,13 +941,13 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) else { // Normal gravity - fallspeed = m_Gravity * a_Dt; + fallspeed = m_Gravity * DtSec.count(); } NextSpeed.y += fallspeed; } else { - ApplyFriction(NextSpeed, 0.7, a_Dt); + ApplyFriction(NextSpeed, 0.7, DtSec.count()); } // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we @@ -1002,14 +1002,14 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) { cTracer Tracer(GetWorld()); // Distance traced is an integer, so we round up from the distance we should go (Speed * Delta), else we will encounter collision detection failurse - int DistanceToTrace = (int)(ceil((NextSpeed * a_Dt).SqrLength()) * 2); + int DistanceToTrace = (int)(ceil((NextSpeed * DtSec.count()).SqrLength()) * 2); bool HasHit = Tracer.Trace(NextPos, NextSpeed, DistanceToTrace); if (HasHit) { // Oh noez! We hit something: verify that the (hit position - current) was smaller or equal to the (position that we should travel without obstacles - current) // This is because previously, we traced with a length that was rounded up (due to integer limitations), and in the case that something was hit, we don't want to overshoot our projected movement - if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * a_Dt).SqrLength()) + if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * DtSec.count()).SqrLength()) { // Block hit was within our projected path // Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1. @@ -1044,13 +1044,13 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) // and that this piece of software will come to be hailed as the epitome of performance and functionality in C++, never before seen, and of such a like that will never // be henceforth seen again in the time of programmers and man alike // - NextPos += (NextSpeed * a_Dt); + NextPos += (NextSpeed * DtSec.count()); } } else { // We didn't hit anything, so move =] - NextPos += (NextSpeed * a_Dt); + NextPos += (NextSpeed * DtSec.count()); } } diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index af545fe4a..de9b88dfb 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -326,10 +326,10 @@ public: // tolua_end - virtual void Tick(float a_Dt, cChunk & a_Chunk); + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); /// Handles the physics of the entity - updates position based on speed, updates speed based on environment - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk); + virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); /// Updates the state related to this entity being on fire virtual void TickBurning(cChunk & a_Chunk); diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp index 751308661..9767f96ca 100644 --- a/src/Entities/ExpOrb.cpp +++ b/src/Entities/ExpOrb.cpp @@ -42,7 +42,7 @@ void cExpOrb::SpawnOn(cClientHandle & a_Client) -void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) +void cExpOrb::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 5)); if (a_ClosestPlayer != nullptr) @@ -69,7 +69,7 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) } HandlePhysics(a_Dt, a_Chunk); - m_Timer += a_Dt; + m_Timer += a_Dt.count(); if (m_Timer >= 1000 * 60 * 5) // 5 minutes { Destroy(true); diff --git a/src/Entities/ExpOrb.h b/src/Entities/ExpOrb.h index bdb9a5b19..e12e3c504 100644 --- a/src/Entities/ExpOrb.h +++ b/src/Entities/ExpOrb.h @@ -22,7 +22,7 @@ public: cExpOrb(const Vector3d & a_Pos, int a_Reward); // Override functions - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SpawnOn(cClientHandle & a_Client) override; /** Returns the number of ticks that this entity has existed */ diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp index 111c5fa84..75105a0cd 100644 --- a/src/Entities/FallingBlock.cpp +++ b/src/Entities/FallingBlock.cpp @@ -31,7 +31,7 @@ void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle) -void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) +void cFallingBlock::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // GetWorld()->BroadcastTeleportEntity(*this); // Test position @@ -82,7 +82,7 @@ void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) return; } - float MilliDt = a_Dt * 0.001f; + float MilliDt = a_Dt.count() * 0.001f; AddSpeedY(MilliDt * -9.8f); AddPosition(GetSpeed() * MilliDt); diff --git a/src/Entities/FallingBlock.h b/src/Entities/FallingBlock.h index c20fe8eb9..884938f4d 100644 --- a/src/Entities/FallingBlock.h +++ b/src/Entities/FallingBlock.h @@ -30,7 +30,7 @@ public: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; private: BLOCKTYPE m_BlockType; diff --git a/src/Entities/FireworkEntity.cpp b/src/Entities/FireworkEntity.cpp index 68d02640a..9dc7850a7 100644 --- a/src/Entities/FireworkEntity.cpp +++ b/src/Entities/FireworkEntity.cpp @@ -19,7 +19,7 @@ cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, do -void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +void cFireworkEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width; int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width; @@ -28,7 +28,7 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) if ((PosY < 0) || (PosY >= cChunkDef::Height)) { AddSpeedY(1); - AddPosition(GetSpeed() * (a_Dt / 1000)); + AddPosition(GetSpeed() * (a_Dt.count() / 1000)); return; } @@ -53,14 +53,14 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) } AddSpeedY(1); - AddPosition(GetSpeed() * (a_Dt / 1000)); + AddPosition(GetSpeed() * (a_Dt.count() / 1000)); } -void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cFireworkEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Entities/FireworkEntity.h b/src/Entities/FireworkEntity.h index 300ec571e..c0a38a943 100644 --- a/src/Entities/FireworkEntity.h +++ b/src/Entities/FireworkEntity.h @@ -49,8 +49,8 @@ public: protected: // cProjectileEntity overrides: - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; private: diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index 5fe6a1238..cf8dd6c6f 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -125,7 +125,7 @@ void cFloater::SpawnOn(cClientHandle & a_Client) -void cFloater::Tick(float a_Dt, cChunk & a_Chunk) +void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { HandlePhysics(a_Dt, a_Chunk); if (IsBlockWater(m_World->GetBlock((int) GetPosX(), (int) GetPosY(), (int) GetPosZ())) && m_World->GetBlockMeta((int) GetPosX(), (int) GetPosY(), (int) GetPosZ()) == 0) diff --git a/src/Entities/Floater.h b/src/Entities/Floater.h index 96d77ac82..d5715f89e 100644 --- a/src/Entities/Floater.h +++ b/src/Entities/Floater.h @@ -21,7 +21,7 @@ public: cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID, int a_CountDownTime); virtual void SpawnOn(cClientHandle & a_Client) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // tolua_begin bool CanPickup(void) const { return m_CanPickupItem; } diff --git a/src/Entities/HangingEntity.h b/src/Entities/HangingEntity.h index 67146a20b..d1ef79a68 100644 --- a/src/Entities/HangingEntity.h +++ b/src/Entities/HangingEntity.h @@ -43,7 +43,7 @@ public: private: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override {} + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override {} eBlockFace m_Facing; diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index fac4f0714..552d70de7 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -111,7 +111,7 @@ void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) -void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) +void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (IsDestroyed()) // Mainly to stop detector rails triggering again after minecart is dead { @@ -165,19 +165,19 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) switch (InsideType) { - case E_BLOCK_RAIL: HandleRailPhysics(InsideMeta, a_Dt); break; + case E_BLOCK_RAIL: HandleRailPhysics(InsideMeta, a_Dt.count()); break; case E_BLOCK_ACTIVATOR_RAIL: break; case E_BLOCK_POWERED_RAIL: HandlePoweredRailPhysics(InsideMeta); break; case E_BLOCK_DETECTOR_RAIL: { - HandleDetectorRailPhysics(InsideMeta, a_Dt); + HandleDetectorRailPhysics(InsideMeta, a_Dt.count()); WasDetectorRail = true; break; } default: VERIFY(!"Unhandled rail type despite checking if block was rail!"); break; } - AddPosition(GetSpeed() * (a_Dt / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp + AddPosition(GetSpeed() * (a_Dt.count() / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp } else { @@ -1213,7 +1213,7 @@ void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) -void cMinecartWithFurnace::Tick(float a_Dt, cChunk & a_Chunk) +void cMinecartWithFurnace::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h index f7d0d5dda..1c6f4a6c4 100644 --- a/src/Entities/Minecart.h +++ b/src/Entities/Minecart.h @@ -38,7 +38,7 @@ public: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; virtual void Destroyed() override; @@ -171,7 +171,7 @@ public: // cEntity overrides: virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // Set functions. void SetIsFueled(bool a_IsFueled, int a_FueledTimeLeft = -1) {m_IsFueled = a_IsFueled; m_FueledTimeLeft = a_FueledTimeLeft;} diff --git a/src/Entities/Painting.cpp b/src/Entities/Painting.cpp index 1aa6da0a1..6f6277f28 100644 --- a/src/Entities/Painting.cpp +++ b/src/Entities/Painting.cpp @@ -31,7 +31,7 @@ void cPainting::SpawnOn(cClientHandle & a_Client) -void cPainting::Tick(float a_Dt, cChunk & a_Chunk) +void cPainting::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); UNUSED(a_Chunk); diff --git a/src/Entities/Painting.h b/src/Entities/Painting.h index 078270b42..6e8a382fc 100644 --- a/src/Entities/Painting.h +++ b/src/Entities/Painting.h @@ -31,7 +31,7 @@ public: private: virtual void SpawnOn(cClientHandle & a_Client) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override { diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp index fc8ca3d47..baf8a2f3b 100644 --- a/src/Entities/Pawn.cpp +++ b/src/Entities/Pawn.cpp @@ -19,7 +19,7 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) : -void cPawn::Tick(float a_Dt, cChunk & a_Chunk) +void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Iterate through this entity's applied effects for (tEffectMap::iterator iter = m_EntityEffects.begin(); iter != m_EntityEffects.end();) diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h index d50bcd8af..e3e99651d 100644 --- a/src/Entities/Pawn.h +++ b/src/Entities/Pawn.h @@ -20,7 +20,7 @@ public: cPawn(eEntityType a_EntityType, double a_Width, double a_Height); - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override; // tolua_begin diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index e5e28446d..accb42a63 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -110,12 +110,12 @@ void cPickup::SpawnOn(cClientHandle & a_Client) -void cPickup::Tick(float a_Dt, cChunk & a_Chunk) +void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); BroadcastMovementUpdate(); // Notify clients of position - m_Timer += a_Dt; + m_Timer += a_Dt.count(); if (!m_bCollected) { @@ -142,7 +142,7 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk) { m_bCollected = true; m_Timer = 0; // We have to reset the timer. - m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick. + m_Timer += a_Dt.count(); // In case we have to destroy the pickup in the same tick. if (m_Timer > 500.f) { Destroy(true); diff --git a/src/Entities/Pickup.h b/src/Entities/Pickup.h index d1176a7cf..67f2756ca 100644 --- a/src/Entities/Pickup.h +++ b/src/Entities/Pickup.h @@ -34,7 +34,7 @@ public: bool CollectedBy(cPlayer & a_Dest); // tolua_export - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; /** Returns the number of ticks that this entity has existed */ int GetAge(void) const { return static_cast(m_Timer / 50); } // tolua_export diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 1d5cc6554..1ca131375 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -191,7 +191,7 @@ void cPlayer::SpawnOn(cClientHandle & a_Client) -void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) +void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_ClientHandle != nullptr) { diff --git a/src/Entities/Player.h b/src/Entities/Player.h index b94d2659e..d3ed46db6 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -46,9 +46,9 @@ public: virtual void SpawnOn(cClientHandle & a_Client) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void HandlePhysics(float a_Dt, cChunk &) override { UNUSED(a_Dt); } + virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &) override { UNUSED(a_Dt); } /** Returns the curently equipped weapon; empty item if none */ virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index 1768714f8..4f20bfae6 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -331,7 +331,7 @@ AString cProjectileEntity::GetMCAClassName(void) const -void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cProjectileEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); @@ -346,7 +346,7 @@ void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk) -void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_IsInGround) { diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h index 2a98e31c7..93e442d8c 100644 --- a/src/Entities/ProjectileEntity.h +++ b/src/Entities/ProjectileEntity.h @@ -118,8 +118,8 @@ protected: bool m_IsInGround; // cEntity overrides: - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SpawnOn(cClientHandle & a_Client) override; } ; // tolua_export diff --git a/src/Entities/SplashPotionEntity.h b/src/Entities/SplashPotionEntity.h index 9302d8292..264dc0eb9 100644 --- a/src/Entities/SplashPotionEntity.h +++ b/src/Entities/SplashPotionEntity.h @@ -58,7 +58,7 @@ protected: // cProjectileEntity overrides: virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void Tick (float a_Dt, cChunk & a_Chunk) override + virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override { if (m_DestroyTimer > 0) { diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp index 53af446cc..a89d2f300 100644 --- a/src/Entities/TNTEntity.cpp +++ b/src/Entities/TNTEntity.cpp @@ -50,7 +50,7 @@ void cTNTEntity::Explode(void) -void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cTNTEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); BroadcastMovementUpdate(); diff --git a/src/Entities/TNTEntity.h b/src/Entities/TNTEntity.h index 48503cf76..9f894338e 100644 --- a/src/Entities/TNTEntity.h +++ b/src/Entities/TNTEntity.h @@ -21,7 +21,7 @@ public: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // tolua_begin diff --git a/src/Entities/ThrownEggEntity.cpp b/src/Entities/ThrownEggEntity.cpp index 24c946a9c..e9ef29239 100644 --- a/src/Entities/ThrownEggEntity.cpp +++ b/src/Entities/ThrownEggEntity.cpp @@ -44,7 +44,7 @@ void cThrownEggEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_Hit -void cThrownEggEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cThrownEggEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_DestroyTimer > 0) { diff --git a/src/Entities/ThrownEggEntity.h b/src/Entities/ThrownEggEntity.h index 6ffedf5b5..620927c5d 100644 --- a/src/Entities/ThrownEggEntity.h +++ b/src/Entities/ThrownEggEntity.h @@ -35,7 +35,7 @@ protected: // cProjectileEntity overrides: virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // Randomly decides whether to spawn a chicken where the egg lands. void TrySpawnChicken(const Vector3d & a_HitPos); diff --git a/src/Entities/ThrownEnderPearlEntity.cpp b/src/Entities/ThrownEnderPearlEntity.cpp index 8f1b62934..f01cdc18c 100644 --- a/src/Entities/ThrownEnderPearlEntity.cpp +++ b/src/Entities/ThrownEnderPearlEntity.cpp @@ -46,7 +46,7 @@ void cThrownEnderPearlEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d -void cThrownEnderPearlEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cThrownEnderPearlEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_DestroyTimer > 0) { diff --git a/src/Entities/ThrownEnderPearlEntity.h b/src/Entities/ThrownEnderPearlEntity.h index 475ebde87..94f3ab5cb 100644 --- a/src/Entities/ThrownEnderPearlEntity.h +++ b/src/Entities/ThrownEnderPearlEntity.h @@ -35,7 +35,7 @@ protected: // cProjectileEntity overrides: virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; /** Teleports the creator where the ender pearl lands */ void TeleportCreator(const Vector3d & a_HitPos); diff --git a/src/Entities/ThrownSnowballEntity.cpp b/src/Entities/ThrownSnowballEntity.cpp index 88e39d22e..24db1e7ee 100644 --- a/src/Entities/ThrownSnowballEntity.cpp +++ b/src/Entities/ThrownSnowballEntity.cpp @@ -48,7 +48,7 @@ void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & -void cThrownSnowballEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cThrownSnowballEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_DestroyTimer > 0) { diff --git a/src/Entities/ThrownSnowballEntity.h b/src/Entities/ThrownSnowballEntity.h index f806996cc..391b0c40b 100644 --- a/src/Entities/ThrownSnowballEntity.h +++ b/src/Entities/ThrownSnowballEntity.h @@ -35,7 +35,7 @@ protected: // cProjectileEntity overrides: virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; private: diff --git a/src/Globals.h b/src/Globals.h index b8e9ec7ed..f0726454b 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -421,6 +421,7 @@ std::unique_ptr make_unique(Args&&... args) // a tick is 50 ms using cTickTime = std::chrono::duration>>; +using cTickTimeLong = std::chrono::duration; #ifndef TOLUA_TEMPLATE_BIND #define TOLUA_TEMPLATE_BIND(x) diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 7ca7a9d66..b9e80c01d 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -61,7 +61,7 @@ void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity) -void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) +void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); @@ -93,9 +93,9 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) -void cAggressiveMonster::Attack(float a_Dt) +void cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt * m_AttackRate; + m_AttackInterval += a_Dt.count() * m_AttackRate; if ((m_Target == nullptr) || (m_AttackInterval < 3.0)) { diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h index 2549ba2d3..932915055 100644 --- a/src/Mobs/AggressiveMonster.h +++ b/src/Mobs/AggressiveMonster.h @@ -16,11 +16,11 @@ public: cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual void Tick (float a_Dt, cChunk & a_Chunk) override; + virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void InStateChasing(float a_Dt) override; virtual void EventSeePlayer(cEntity *) override; - virtual void Attack(float a_Dt); + virtual void Attack(std::chrono::milliseconds a_Dt); protected: /** Whether this mob's destination is the same as its target's position. */ diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index 1fa9d2c37..172ccd071 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -30,9 +30,9 @@ void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cBlaze::Attack(float a_Dt) +void cBlaze::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt * m_AttackRate; + m_AttackInterval += a_Dt.count() * m_AttackRate; if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h index e2a4ad9f1..493953a14 100644 --- a/src/Mobs/Blaze.h +++ b/src/Mobs/Blaze.h @@ -18,5 +18,5 @@ public: CLASS_PROTODEF(cBlaze) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; } ; diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp index 045b47e73..fa530db82 100644 --- a/src/Mobs/CaveSpider.cpp +++ b/src/Mobs/CaveSpider.cpp @@ -16,7 +16,7 @@ cCaveSpider::cCaveSpider(void) : -void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk) +void cCaveSpider::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); @@ -27,7 +27,7 @@ void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk) -void cCaveSpider::Attack(float a_Dt) +void cCaveSpider::Attack(std::chrono::milliseconds a_Dt) { super::Attack(a_Dt); diff --git a/src/Mobs/CaveSpider.h b/src/Mobs/CaveSpider.h index 494ba1360..d3e56fd2b 100644 --- a/src/Mobs/CaveSpider.h +++ b/src/Mobs/CaveSpider.h @@ -16,8 +16,8 @@ public: CLASS_PROTODEF(cCaveSpider) - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void Attack(float a_Dt) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; } ; diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp index 634867cfa..b2b21d4ae 100644 --- a/src/Mobs/Chicken.cpp +++ b/src/Mobs/Chicken.cpp @@ -18,7 +18,7 @@ cChicken::cChicken(void) : -void cChicken::Tick(float a_Dt, cChunk & a_Chunk) +void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h index 07b921884..9349187c6 100644 --- a/src/Mobs/Chicken.h +++ b/src/Mobs/Chicken.h @@ -17,7 +17,7 @@ public: CLASS_PROTODEF(cChicken) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_SEEDS); } diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp index a073224cf..c4ae47f2f 100644 --- a/src/Mobs/Creeper.cpp +++ b/src/Mobs/Creeper.cpp @@ -23,7 +23,7 @@ cCreeper::cCreeper(void) : -void cCreeper::Tick(float a_Dt, cChunk & a_Chunk) +void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); @@ -119,7 +119,7 @@ bool cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) -void cCreeper::Attack(float a_Dt) +void cCreeper::Attack(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h index bf3272e22..1827c416e 100644 --- a/src/Mobs/Creeper.h +++ b/src/Mobs/Creeper.h @@ -19,8 +19,8 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void Attack(float a_Dt) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void OnRightClicked(cPlayer & a_Player) override; bool IsBlowing(void) const {return m_bIsBlowing; } diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp index 56ea10245..42c33884a 100644 --- a/src/Mobs/Enderman.cpp +++ b/src/Mobs/Enderman.cpp @@ -186,7 +186,7 @@ bool cEnderman::CheckLight() -void cEnderman::Tick(float a_Dt, cChunk & a_Chunk) +void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h index 28bbceb84..8ccbf2ce7 100644 --- a/src/Mobs/Enderman.h +++ b/src/Mobs/Enderman.h @@ -21,7 +21,7 @@ public: virtual void CheckEventSeePlayer(void) override; virtual void CheckEventLostPlayer(void) override; virtual void EventLosePlayer(void) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; bool IsScreaming(void) const {return m_bIsScreaming; } BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; } diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index fc8de8362..ea0295102 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -32,9 +32,9 @@ void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cGhast::Attack(float a_Dt) +void cGhast::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt * m_AttackRate; + m_AttackInterval += a_Dt.count() * m_AttackRate; if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h index a28940a01..431edaf6d 100644 --- a/src/Mobs/Ghast.h +++ b/src/Mobs/Ghast.h @@ -18,7 +18,7 @@ public: CLASS_PROTODEF(cGhast) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; bool IsCharging(void) const {return false; } } ; diff --git a/src/Mobs/Guardian.cpp b/src/Mobs/Guardian.cpp index 15908d801..5eb30785b 100644 --- a/src/Mobs/Guardian.cpp +++ b/src/Mobs/Guardian.cpp @@ -35,7 +35,7 @@ void cGuardian::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cGuardian::Tick(float a_Dt, cChunk & a_Chunk) +void cGuardian::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk // that is not where the entity currently resides (FS #411) diff --git a/src/Mobs/Guardian.h b/src/Mobs/Guardian.h index 50c034036..6bc17947c 100644 --- a/src/Mobs/Guardian.h +++ b/src/Mobs/Guardian.h @@ -15,7 +15,7 @@ class cGuardian : public: cGuardian(); - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; CLASS_PROTODEF(cGuardian) diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp index d92f0d023..5b4c78bfc 100644 --- a/src/Mobs/Horse.cpp +++ b/src/Mobs/Horse.cpp @@ -30,7 +30,7 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : -void cHorse::Tick(float a_Dt, cChunk & a_Chunk) +void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h index 4c644e512..be283705e 100644 --- a/src/Mobs/Horse.h +++ b/src/Mobs/Horse.h @@ -18,7 +18,7 @@ public: CLASS_PROTODEF(cHorse) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void OnRightClicked(cPlayer & a_Player) override; bool IsSaddled (void) const {return m_bIsSaddled; } diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 425c80bf4..30bbd0ff2 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -252,14 +252,14 @@ bool cMonster::ReachedFinalDestination() -void cMonster::Tick(float a_Dt, cChunk & a_Chunk) +void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); if (m_Health <= 0) { // The mob is dead, but we're still animating the "puff" they leave when they die - m_DestroyTimer += a_Dt / 1000; + m_DestroyTimer += a_Dt.count() / 1000; if (m_DestroyTimer > 1) { Destroy(true); @@ -275,8 +275,6 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) // Burning in daylight HandleDaylightBurning(a_Chunk); - a_Dt /= 1000; - if (m_bMovingToDestination) { if (m_bOnGround) @@ -347,18 +345,18 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) case IDLE: { // If enemy passive we ignore checks for player visibility - InStateIdle(a_Dt); + InStateIdle(std::chrono::duration_cast(a_Dt).count()); break; } case CHASING: { // If we do not see a player anymore skip chasing action - InStateChasing(a_Dt); + InStateChasing(std::chrono::duration_cast(a_Dt).count()); break; } case ESCAPING: { - InStateEscaping(a_Dt); + InStateEscaping(std::chrono::duration_cast(a_Dt).count()); break; } diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index fb1bc550d..5752a2040 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -52,7 +52,7 @@ public: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp index 1048616d0..012ca9949 100644 --- a/src/Mobs/PassiveMonster.cpp +++ b/src/Mobs/PassiveMonster.cpp @@ -35,7 +35,7 @@ bool cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) -void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk) +void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h index 9221d9a6e..2ed2cd21d 100644 --- a/src/Mobs/PassiveMonster.h +++ b/src/Mobs/PassiveMonster.h @@ -15,7 +15,7 @@ class cPassiveMonster : public: cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; /// When hit by someone, run away virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp index 1e4c35acd..edd4d9de4 100644 --- a/src/Mobs/Pig.cpp +++ b/src/Mobs/Pig.cpp @@ -80,7 +80,7 @@ void cPig::OnRightClicked(cPlayer & a_Player) -void cPig::Tick(float a_Dt, cChunk & a_Chunk) +void cPig::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h index 0e026933a..0fe4b4fed 100644 --- a/src/Mobs/Pig.h +++ b/src/Mobs/Pig.h @@ -22,7 +22,7 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); } diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index c46404391..e4d1760e0 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -84,7 +84,7 @@ void cSheep::OnRightClicked(cPlayer & a_Player) -void cSheep::Tick(float a_Dt, cChunk & a_Chunk) +void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); int PosX = POSX_TOINT; diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h index 16d5fddd3..b6c99ac2a 100644 --- a/src/Mobs/Sheep.h +++ b/src/Mobs/Sheep.h @@ -24,7 +24,7 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); } diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index da5ddc670..dd59d6454 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -67,9 +67,9 @@ void cSkeleton::MoveToPosition(const Vector3d & a_Position) -void cSkeleton::Attack(float a_Dt) +void cSkeleton::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt * m_AttackRate; + m_AttackInterval += a_Dt.count() * m_AttackRate; if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h index cd1c6c3f0..9c49c52fb 100644 --- a/src/Mobs/Skeleton.h +++ b/src/Mobs/Skeleton.h @@ -19,7 +19,7 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual void MoveToPosition(const Vector3d & a_Position) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual bool IsUndead(void) override { return true; } diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp index 1c68c5189..e42501e47 100644 --- a/src/Mobs/Slime.cpp +++ b/src/Mobs/Slime.cpp @@ -46,7 +46,7 @@ void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cSlime::Attack(float a_Dt) +void cSlime::Attack(std::chrono::milliseconds a_Dt) { if (m_Size > 1) { diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h index a177a279c..29605992d 100644 --- a/src/Mobs/Slime.h +++ b/src/Mobs/Slime.h @@ -20,7 +20,7 @@ public: // cAggressiveMonster overrides: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override; int GetSize(void) const { return m_Size; } diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp index 8c4178beb..e1510b203 100644 --- a/src/Mobs/SnowGolem.cpp +++ b/src/Mobs/SnowGolem.cpp @@ -27,7 +27,7 @@ void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cSnowGolem::Tick(float a_Dt, cChunk & a_Chunk) +void cSnowGolem::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); if (IsBiomeNoDownfall(m_World->GetBiomeAt((int) floor(GetPosX()), (int) floor(GetPosZ())))) diff --git a/src/Mobs/SnowGolem.h b/src/Mobs/SnowGolem.h index f036b1867..9c95e21c5 100644 --- a/src/Mobs/SnowGolem.h +++ b/src/Mobs/SnowGolem.h @@ -17,7 +17,7 @@ public: CLASS_PROTODEF(cSnowGolem) - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; } ; diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp index 59ee963ae..3c508b65f 100644 --- a/src/Mobs/Squid.cpp +++ b/src/Mobs/Squid.cpp @@ -33,7 +33,7 @@ void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cSquid::Tick(float a_Dt, cChunk & a_Chunk) +void cSquid::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk // that is not where the entity currently resides (FS #411) diff --git a/src/Mobs/Squid.h b/src/Mobs/Squid.h index a46d738c6..7e944a17e 100644 --- a/src/Mobs/Squid.h +++ b/src/Mobs/Squid.h @@ -15,7 +15,7 @@ class cSquid : public: cSquid(); - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; CLASS_PROTODEF(cSquid) diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp index 963595347..6f647ac18 100644 --- a/src/Mobs/Villager.cpp +++ b/src/Mobs/Villager.cpp @@ -51,7 +51,7 @@ bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) -void cVillager::Tick(float a_Dt, cChunk & a_Chunk) +void cVillager::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h index d3a38dbf0..2de79295c 100644 --- a/src/Mobs/Villager.h +++ b/src/Mobs/Villager.h @@ -31,7 +31,7 @@ public: // cEntity overrides virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void Tick (float a_Dt, cChunk & a_Chunk) override; + virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // cVillager functions /** return true if the given blocktype are: crops, potatoes or carrots.*/ diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp index 578b47995..6ef81ce1b 100644 --- a/src/Mobs/Wither.cpp +++ b/src/Mobs/Wither.cpp @@ -66,7 +66,7 @@ bool cWither::DoTakeDamage(TakeDamageInfo & a_TDI) -void cWither::Tick(float a_Dt, cChunk & a_Chunk) +void cWither::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h index a20fed3d3..9e333c7fa 100644 --- a/src/Mobs/Wither.h +++ b/src/Mobs/Wither.h @@ -28,7 +28,7 @@ public: virtual bool Initialize(cWorld & a_World) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual bool IsUndead(void) override { return true; } diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index 4711d5a7a..b3eefdf79 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -43,7 +43,7 @@ bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) -void cWolf::Attack(float a_Dt) +void cWolf::Attack(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); @@ -145,7 +145,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player) -void cWolf::Tick(float a_Dt, cChunk & a_Chunk) +void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (!IsAngry()) { diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h index 7500854f8..73ffb55c2 100644 --- a/src/Mobs/Wolf.h +++ b/src/Mobs/Wolf.h @@ -20,9 +20,9 @@ public: virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void TickFollowPlayer(); - virtual void Attack(float a_Dt) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; // Get functions bool IsSitting (void) const { return m_IsSitting; } diff --git a/src/Simulator/DelayedFluidSimulator.cpp b/src/Simulator/DelayedFluidSimulator.cpp index dc8dffe2d..0973962b6 100644 --- a/src/Simulator/DelayedFluidSimulator.cpp +++ b/src/Simulator/DelayedFluidSimulator.cpp @@ -130,7 +130,7 @@ void cDelayedFluidSimulator::Simulate(float a_Dt) -void cDelayedFluidSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +void cDelayedFluidSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData(); cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw; diff --git a/src/Simulator/DelayedFluidSimulator.h b/src/Simulator/DelayedFluidSimulator.h index 8a6c26c7a..e3182812d 100644 --- a/src/Simulator/DelayedFluidSimulator.h +++ b/src/Simulator/DelayedFluidSimulator.h @@ -56,7 +56,7 @@ public: // cSimulator overrides: virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; virtual void Simulate(float a_Dt) override; - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; virtual cFluidSimulatorData * CreateChunkData(void) override { return new cDelayedFluidSimulatorChunkData(m_TickDelay); } protected: diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp index 75ebefcf7..0439ebdca 100644 --- a/src/Simulator/FireSimulator.cpp +++ b/src/Simulator/FireSimulator.cpp @@ -88,11 +88,11 @@ cFireSimulator::~cFireSimulator() -void cFireSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { cCoordWithIntList & Data = a_Chunk->GetFireSimulatorData(); - int NumMSecs = (int)a_Dt; + int NumMSecs = a_Dt.count(); for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();) { int x = itr->x; diff --git a/src/Simulator/FireSimulator.h b/src/Simulator/FireSimulator.h index 9ccc3ef4f..a40e29565 100644 --- a/src/Simulator/FireSimulator.h +++ b/src/Simulator/FireSimulator.h @@ -23,7 +23,7 @@ public: ~cFireSimulator(); virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override; diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 7ff499eb5..1cc5340dd 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -145,7 +145,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, -void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +void cIncrementalRedstoneSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { m_RedstoneSimulatorChunkData = (cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData(); if (m_RedstoneSimulatorChunkData == nullptr) diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h index 3d2962c08..43f0e89d0 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.h +++ b/src/Simulator/IncrementalRedstoneSimulator.h @@ -32,7 +32,7 @@ public: } virtual void Simulate(float a_Dt) override { UNUSED(a_Dt); } // not used - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override { return IsRedstone(a_BlockType); } virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; diff --git a/src/Simulator/NoopRedstoneSimulator.h b/src/Simulator/NoopRedstoneSimulator.h index 4e05529f5..b8c797472 100644 --- a/src/Simulator/NoopRedstoneSimulator.h +++ b/src/Simulator/NoopRedstoneSimulator.h @@ -21,7 +21,7 @@ public: // ~cRedstoneNoopSimulator(); virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override { UNUSED(a_Dt); UNUSED(a_ChunkX); diff --git a/src/Simulator/SandSimulator.cpp b/src/Simulator/SandSimulator.cpp index dfbd3e458..497f81999 100644 --- a/src/Simulator/SandSimulator.cpp +++ b/src/Simulator/SandSimulator.cpp @@ -24,7 +24,7 @@ cSandSimulator::cSandSimulator(cWorld & a_World, cIniFile & a_IniFile) : -void cSandSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +void cSandSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData(); if (ChunkData.empty()) diff --git a/src/Simulator/SandSimulator.h b/src/Simulator/SandSimulator.h index 93b1de8e2..8fff659ed 100644 --- a/src/Simulator/SandSimulator.h +++ b/src/Simulator/SandSimulator.h @@ -18,7 +18,7 @@ public: // cSimulator overrides: virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override; /// Returns true if a falling-able block can start falling through the specified block type diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h index f28a07e35..c8066edfd 100644 --- a/src/Simulator/Simulator.h +++ b/src/Simulator/Simulator.h @@ -24,7 +24,7 @@ public: virtual void Simulate(float a_Dt) = 0; /// Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct access to chunk data available - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { UNUSED(a_Dt); UNUSED(a_ChunkX); diff --git a/src/Simulator/SimulatorManager.cpp b/src/Simulator/SimulatorManager.cpp index 918bac7a1..e74642fc0 100644 --- a/src/Simulator/SimulatorManager.cpp +++ b/src/Simulator/SimulatorManager.cpp @@ -42,7 +42,7 @@ void cSimulatorManager::Simulate(float a_Dt) -void cSimulatorManager::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +void cSimulatorManager::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { // m_Ticks has already been increased in Simulate() for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr) diff --git a/src/Simulator/SimulatorManager.h b/src/Simulator/SimulatorManager.h index 31a709316..b96f6ca84 100644 --- a/src/Simulator/SimulatorManager.h +++ b/src/Simulator/SimulatorManager.h @@ -33,7 +33,7 @@ public: void Simulate(float a_Dt); - void SimulateChunk(float a_DT, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk); + void SimulateChunk(std::chrono::milliseconds a_DT, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk); void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk); diff --git a/src/World.cpp b/src/World.cpp index 2dd03497b..cd99f2c2d 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -238,9 +238,8 @@ void cWorld::cTickThread::Execute(void) while (!m_ShouldTerminate) { auto NowTime = std::chrono::steady_clock::now(); - auto msec = std::chrono::duration_cast(NowTime - LastTime).count(); - auto LastTickMsec = std::chrono::duration_cast>(TickTime).count(); - m_World.Tick(static_cast(msec), LastTickMsec); + auto WaitTime = std::chrono::duration_cast(NowTime - LastTime); + m_World.Tick(WaitTime, TickTime); TickTime = std::chrono::duration_cast(std::chrono::steady_clock::now() - NowTime); if (TickTime < cTickTime(1)) @@ -273,8 +272,6 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin m_Dimension(a_Dimension), m_IsSpawnExplicitlySet(false), m_IsDaylightCycleEnabled(true), - m_WorldAgeSecs(0), - m_TimeOfDaySecs(0), m_WorldAge(0), m_TimeOfDay(0), m_LastTimeUpdate(0), @@ -617,7 +614,7 @@ void cWorld::Start(void) InitialiseGeneratorDefaults(IniFile); InitialiseAndLoadMobSpawningValues(IniFile); - SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", m_TimeOfDay)); + SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay())); m_ChunkMap = make_unique(this); @@ -644,10 +641,10 @@ void cWorld::Start(void) m_TickThread.Start(); // Init of the spawn monster time (as they are supposed to have different spawn rate) - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfHostile, 0)); - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfPassive, 0)); - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfAmbient, 0)); - m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfWater, 0)); + m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfHostile, cTickTimeLong(0))); + m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfPassive, cTickTimeLong(0))); + m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfAmbient, cTickTimeLong(0))); + m_LastSpawnMonster.insert(std::map::value_type(cMonster::mfWater, cTickTimeLong(0))); m_MapManager.LoadMapData(); @@ -838,7 +835,7 @@ void cWorld::Stop(void) IniFile.SetValueB("Mechanics", "UseChatPrefixes", m_bUseChatPrefixes); IniFile.SetValueB("General", "IsDaylightCycleEnabled", m_IsDaylightCycleEnabled); IniFile.SetValueI("General", "Weather", (int)m_Weather); - IniFile.SetValueI("General", "TimeInTicks", m_TimeOfDay); + IniFile.SetValueI("General", "TimeInTicks", GetTimeOfDay()); IniFile.WriteFile(m_IniFileName); m_TickThread.Stop(); @@ -852,7 +849,7 @@ void cWorld::Stop(void) -void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec) +void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { // Call the plugins cPluginManager::Get()->CallHookWorldTick(*this, a_Dt, a_LastTickDurationMSec); @@ -868,30 +865,27 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec) SetChunkData(**itr); } // for itr - SetChunkDataQueue[] - m_WorldAgeSecs += (double)a_Dt / 1000.0; - m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0); + m_WorldAge += a_Dt; if (m_IsDaylightCycleEnabled) { // We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it - m_TimeOfDaySecs += (double)a_Dt / 1000.0; + m_TimeOfDay += a_Dt; // Wrap time of day each 20 minutes (1200 seconds) - if (m_TimeOfDaySecs > 1200.0) + if (m_TimeOfDay > std::chrono::minutes(20)) { - m_TimeOfDaySecs -= 1200.0; + m_TimeOfDay -= std::chrono::minutes(20); } - m_TimeOfDay = static_cast(m_TimeOfDaySecs * 20.0); - // Updates the sky darkness based on current time of day UpdateSkyDarkness(); // Broadcast time update every 40 ticks (2 seconds) - if (m_LastTimeUpdate < m_WorldAge - 40) + if (m_LastTimeUpdate < m_WorldAge - cTickTime(40)) { BroadcastTimeUpdate(); - m_LastTimeUpdate = m_WorldAge; + m_LastTimeUpdate = std::chrono::duration_cast(m_WorldAge); } } @@ -911,23 +905,23 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec) m_ChunkMap->Tick(a_Dt); - TickClients(a_Dt); + TickClients(a_Dt.count()); TickQueuedBlocks(); TickQueuedTasks(); TickScheduledTasks(); - GetSimulatorManager()->Simulate(a_Dt); + GetSimulatorManager()->Simulate(a_Dt.count()); - TickWeather(a_Dt); + TickWeather(a_Dt.count()); m_ChunkMap->FastSetQueuedBlocks(); - if (m_WorldAge - m_LastSave > 60 * 5 * 20) // Save each 5 minutes + if (m_WorldAge - m_LastSave > std::chrono::minutes(5)) // Save each 5 minutes { SaveAllChunks(); } - if (m_WorldAge - m_LastUnload > 10 * 20) // Unload every 10 seconds + if (m_WorldAge - m_LastUnload > std::chrono::minutes(5)) // Unload every 10 seconds { UnloadUnusedChunks(); } @@ -973,7 +967,7 @@ void cWorld::TickWeather(float a_Dt) -void cWorld::TickMobs(float a_Dt) +void cWorld::TickMobs(std::chrono::milliseconds a_Dt) { // _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs cWorld::cLock Lock(*this); @@ -994,7 +988,7 @@ void cWorld::TickMobs(float a_Dt) for (size_t i = 0; i < ARRAYCOUNT(AllFamilies); i++) { cMonster::eFamily Family = AllFamilies[i]; - int SpawnDelay = cMonster::GetSpawnDelay(Family); + cTickTime SpawnDelay = cTickTime(cMonster::GetSpawnDelay(Family)); if ( (m_LastSpawnMonster[Family] > m_WorldAge - SpawnDelay) || // Not reached the needed ticks before the next round MobCensus.IsCapped(Family) @@ -1002,7 +996,7 @@ void cWorld::TickMobs(float a_Dt) { continue; } - m_LastSpawnMonster[Family] = m_WorldAge; + m_LastSpawnMonster[Family] = std::chrono::duration_cast(m_WorldAge); cMobSpawner Spawner(Family, m_AllowedMobs); if (Spawner.CanSpawnAnything()) { @@ -1066,7 +1060,7 @@ void cWorld::TickScheduledTasks(void) // Move all the due tasks from m_ScheduledTasks into Tasks: for (auto itr = m_ScheduledTasks.begin(); itr != m_ScheduledTasks.end();) // Cannot use range-basd for, we're modifying the container { - if ((*itr)->m_TargetTick < WorldAge) + if ((*itr)->m_TargetTick < std::chrono::duration_cast(WorldAge).count()) { auto next = itr; ++next; @@ -1141,7 +1135,7 @@ void cWorld::TickClients(float a_Dt) void cWorld::UpdateSkyDarkness(void) { - int TempTime = (int)m_TimeOfDay; + int TempTime = std::chrono::duration_cast(m_TimeOfDay).count(); if (TempTime <= TIME_SUNSET) { m_SkyDarkness = 0; @@ -1422,14 +1416,15 @@ void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_Sapling { cNoise Noise(m_Generator.GetSeed()); sSetBlockVector Logs, Other; + auto WorldAge = (int)(std::chrono::duration_cast(m_WorldAge).count() & 0xffffffff); switch (a_SaplingMeta & 0x07) { - case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_ACACIA: GetAcaciaTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_DARK_OAK: GetDarkoakTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_ACACIA: GetAcaciaTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_DARK_OAK: GetDarkoakTreeImage(a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; } Other.insert(Other.begin(), Logs.begin(), Logs.end()); Logs.clear(); @@ -1444,7 +1439,7 @@ void cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z) { cNoise Noise(m_Generator.GetSeed()); sSetBlockVector Logs, Other; - GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), GetBiomeAt(a_X, a_Z), Logs, Other); + GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(std::chrono::duration_cast(m_WorldAge).count() & 0xffffffff), GetBiomeAt(a_X, a_Z), Logs, Other); Other.insert(Other.begin(), Logs.begin(), Logs.end()); Logs.clear(); GrowTreeImage(Other); @@ -2405,7 +2400,7 @@ void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude) { continue; } - ch->SendTimeUpdate(m_WorldAge, m_TimeOfDay, m_IsDaylightCycleEnabled); + ch->SendTimeUpdate(std::chrono::duration_cast(m_WorldAge).count(), std::chrono::duration_cast(m_TimeOfDay).count(), m_IsDaylightCycleEnabled); } } @@ -2607,7 +2602,7 @@ bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const void cWorld::UnloadUnusedChunks(void) { - m_LastUnload = m_WorldAge; + m_LastUnload = std::chrono::duration_cast(m_WorldAge); m_ChunkMap->UnloadUnusedChunks(); } @@ -3083,7 +3078,7 @@ bool cWorld::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunk void cWorld::SaveAllChunks(void) { - m_LastSave = m_WorldAge; + m_LastSave = std::chrono::duration_cast(m_WorldAge); m_ChunkMap->SaveAllChunks(); } @@ -3112,7 +3107,7 @@ void cWorld::QueueTask(std::unique_ptr a_Task) void cWorld::ScheduleTask(int a_DelayTicks, cTask * a_Task) { - Int64 TargetTick = a_DelayTicks + m_WorldAge; + Int64 TargetTick = a_DelayTicks + std::chrono::duration_cast(m_WorldAge).count(); // Insert the task into the list of scheduled tasks, ordered by its target tick cCSLock Lock(m_CSScheduledTasks); diff --git a/src/World.h b/src/World.h index 2ee33375f..4e5c9bceb 100644 --- a/src/World.h +++ b/src/World.h @@ -157,8 +157,8 @@ public: BroadcastTimeUpdate(); } - virtual Int64 GetWorldAge (void) const override { return m_WorldAge; } - virtual int GetTimeOfDay(void) const override { return m_TimeOfDay; } + virtual Int64 GetWorldAge (void) const override { return std::chrono::duration_cast(m_WorldAge).count(); } + virtual int GetTimeOfDay(void) const override { return std::chrono::duration_cast(m_TimeOfDay).count(); } void SetTicksUntilWeatherChange(int a_WeatherInterval) { @@ -167,8 +167,7 @@ public: virtual void SetTimeOfDay(int a_TimeOfDay) override { - m_TimeOfDay = a_TimeOfDay; - m_TimeOfDaySecs = (double)a_TimeOfDay / 20.0; + m_TimeOfDay = cTickTime(a_TimeOfDay); UpdateSkyDarkness(); BroadcastTimeUpdate(); } @@ -913,15 +912,14 @@ private: bool m_BroadcastDeathMessages; bool m_BroadcastAchievementMessages; - bool m_IsDaylightCycleEnabled; - double m_WorldAgeSecs; // World age, in seconds. Is only incremented, cannot be set by plugins. - double m_TimeOfDaySecs; // Time of day in seconds. Can be adjusted. Is wrapped to zero each day. - Int64 m_WorldAge; // World age in ticks, calculated off of m_WorldAgeSecs - int m_TimeOfDay; // Time in ticks, calculated off of m_TimeOfDaySecs - Int64 m_LastTimeUpdate; // The tick in which the last time update has been sent. - Int64 m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred - Int64 m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred - std::map m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed) + bool m_IsDaylightCycleEnabled; + // std::chrono::milliseconds is guaranteed to be good for 292 years by the standard. + std::chrono::milliseconds m_WorldAge; + std::chrono::milliseconds m_TimeOfDay; + cTickTimeLong m_LastTimeUpdate; // The tick in which the last time update has been sent. + cTickTimeLong m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred + cTickTimeLong m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred + std::map m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed) NIBBLETYPE m_SkyDarkness; @@ -1051,13 +1049,13 @@ private: cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = ""); virtual ~cWorld(); - void Tick(float a_Dt, int a_LastTickDurationMSec); + void Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec); /** Handles the weather in each tick */ void TickWeather(float a_Dt); /** Handles the mob spawning/moving/destroying each tick */ - void TickMobs(float a_Dt); + void TickMobs(std::chrono::milliseconds a_Dt); /** Executes all tasks queued onto the tick thread */ void TickQueuedTasks(void); -- cgit v1.2.3 From acc2dcbbc3e08ddae654b8c64d735f93207c297a Mon Sep 17 00:00:00 2001 From: Tycho Date: Sun, 11 Jan 2015 21:53:20 +0000 Subject: Fix trailing whitespace --- src/World.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/World.h b/src/World.h index 4e5c9bceb..1f58ddbb7 100644 --- a/src/World.h +++ b/src/World.h @@ -912,7 +912,7 @@ private: bool m_BroadcastDeathMessages; bool m_BroadcastAchievementMessages; - bool m_IsDaylightCycleEnabled; + bool m_IsDaylightCycleEnabled; // std::chrono::milliseconds is guaranteed to be good for 292 years by the standard. std::chrono::milliseconds m_WorldAge; std::chrono::milliseconds m_TimeOfDay; -- cgit v1.2.3 From d6f042da4a4d296ab9be18884996f0d88dd23c39 Mon Sep 17 00:00:00 2001 From: Tycho Date: Fri, 16 Jan 2015 13:13:23 +0000 Subject: Converted ArrowEntityTiers to std::chrono --- src/Entities/ArrowEntity.cpp | 10 +++++----- src/Entities/ArrowEntity.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index 2ec825ddb..0fbbfb681 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -177,17 +177,17 @@ void cArrowEntity::CollectedBy(cPlayer & a_Dest) void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - m_Timer += a_Dt.count(); + m_Timer += a_Dt; if (m_bIsCollected) { - if (m_Timer > 500.f) // 0.5 seconds + if (m_Timer > std::chrono::milliseconds(500)) { Destroy(); return; } } - else if (m_Timer > 1000 * 60 * 5) // 5 minutes + else if (m_Timer > std::chrono::minutes(5)) { Destroy(); return; @@ -202,14 +202,14 @@ void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (!m_HasTeleported) // Sent a teleport already, don't do again { - if (m_HitGroundTimer > 500.f) // Send after half a second, could be less, but just in case + if (m_HitGroundTimer > std::chrono::milliseconds(500)) { m_World->BroadcastTeleportEntity(*this); m_HasTeleported = true; } else { - m_HitGroundTimer += a_Dt.count(); + m_HitGroundTimer += a_Dt; } } diff --git a/src/Entities/ArrowEntity.h b/src/Entities/ArrowEntity.h index 8c92049b0..2ecc16d1c 100644 --- a/src/Entities/ArrowEntity.h +++ b/src/Entities/ArrowEntity.h @@ -85,10 +85,10 @@ protected: bool m_IsCritical; /** Timer for pickup collection animation or five minute timeout */ - float m_Timer; + std::chrono::milliseconds m_Timer; /** Timer for client arrow position confirmation via TeleportEntity */ - float m_HitGroundTimer; + std::chrono::milliseconds m_HitGroundTimer; // Whether the arrow has already been teleported into the proper position in the ground. bool m_HasTeleported; -- cgit v1.2.3 From 7562a381c004470dfa8f0f94cd00f92d3c413f74 Mon Sep 17 00:00:00 2001 From: Tycho Date: Fri, 16 Jan 2015 13:27:10 +0000 Subject: Converted cExpOrbEntity to std::chrono --- src/Entities/ExpOrb.cpp | 8 ++++---- src/Entities/ExpOrb.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp index 9767f96ca..db7f6f2c8 100644 --- a/src/Entities/ExpOrb.cpp +++ b/src/Entities/ExpOrb.cpp @@ -8,7 +8,7 @@ cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward) : cEntity(etExpOrb, a_X, a_Y, a_Z, 0.98, 0.98) , m_Reward(a_Reward) - , m_Timer(0.f) + , m_Timer(0) { SetMaxHealth(5); SetHealth(5); @@ -21,7 +21,7 @@ cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward) cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward) : cEntity(etExpOrb, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98) , m_Reward(a_Reward) - , m_Timer(0.f) + , m_Timer(0) { SetMaxHealth(5); SetHealth(5); @@ -69,8 +69,8 @@ void cExpOrb::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } HandlePhysics(a_Dt, a_Chunk); - m_Timer += a_Dt.count(); - if (m_Timer >= 1000 * 60 * 5) // 5 minutes + m_Timer += a_Dt; + if (m_Timer >= std::chrono::minutes(5)) { Destroy(true); } diff --git a/src/Entities/ExpOrb.h b/src/Entities/ExpOrb.h index e12e3c504..9aac4f748 100644 --- a/src/Entities/ExpOrb.h +++ b/src/Entities/ExpOrb.h @@ -26,10 +26,10 @@ public: virtual void SpawnOn(cClientHandle & a_Client) override; /** Returns the number of ticks that this entity has existed */ - int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export + int GetAge(void) const { return std::chrono::duration_cast(m_Timer).count(); } // tolua_export /** Set the number of ticks that this entity has existed */ - void SetAge(int a_Age) { m_Timer = (float)(a_Age * 50); } // tolua_export + void SetAge(int a_Age) { m_Timer = cTickTime(a_Age); } // tolua_export /** Get the exp amount */ int GetReward(void) const { return m_Reward; } // tolua_export @@ -41,5 +41,5 @@ protected: int m_Reward; /** The number of ticks that the entity has existed / timer between collect and destroy; in msec */ - float m_Timer; + std::chrono::milliseconds m_Timer; } ; // tolua_export -- cgit v1.2.3 From 8dc9cf0c769b46dd3a97440f76cae4d83b52348e Mon Sep 17 00:00:00 2001 From: Tycho Date: Fri, 16 Jan 2015 13:42:44 +0000 Subject: Converted MinecartEntity to std::chrono --- src/Entities/Minecart.cpp | 10 +++++----- src/Entities/Minecart.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index 552d70de7..a906c9767 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -165,12 +165,12 @@ void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) switch (InsideType) { - case E_BLOCK_RAIL: HandleRailPhysics(InsideMeta, a_Dt.count()); break; + case E_BLOCK_RAIL: HandleRailPhysics(InsideMeta, a_Dt); break; case E_BLOCK_ACTIVATOR_RAIL: break; case E_BLOCK_POWERED_RAIL: HandlePoweredRailPhysics(InsideMeta); break; case E_BLOCK_DETECTOR_RAIL: { - HandleDetectorRailPhysics(InsideMeta, a_Dt.count()); + HandleDetectorRailPhysics(InsideMeta, a_Dt); WasDetectorRail = true; break; } @@ -205,7 +205,7 @@ void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) +void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt) { /* NOTE: Please bear in mind that taking away from negatives make them even more negative, @@ -565,7 +565,7 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta) -void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) +void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt) { m_World->SetBlockMeta(m_DetectorRailPosition, a_RailMeta | 0x08); @@ -576,7 +576,7 @@ void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) -void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) +void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt) { HandleRailPhysics(a_RailMeta & 0x07, a_Dt); } diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h index 1c6f4a6c4..898776e71 100644 --- a/src/Entities/Minecart.h +++ b/src/Entities/Minecart.h @@ -56,7 +56,7 @@ protected: /** Handles physics on normal rails For each tick, slow down on flat rails, speed up or slow down on ascending/descending rails (depending on direction), and turn on curved rails */ - void HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt); + void HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt); /** Handles powered rail physics Each tick, speed up or slow down cart, depending on metadata of rail (powered or not) @@ -66,10 +66,10 @@ protected: /** Handles detector rail activation Activates detector rails when a minecart is on them. Calls HandleRailPhysics() for physics simulations */ - void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt); + void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt); /** Handles activator rails - placeholder for future implementation */ - void HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt); + void HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt); /** Snaps a mincecart to a rail's axis, resetting its speed For curved rails, it changes the cart's direction as well as snapping it to axis */ -- cgit v1.2.3 From 05c40db060809612e6d0bb3bee596c4c95ce758d Mon Sep 17 00:00:00 2001 From: Tycho Date: Fri, 16 Jan 2015 13:49:22 +0000 Subject: Converted cPickupEntity to std::chrono --- src/Entities/Pickup.cpp | 18 +++++++++--------- src/Entities/Pickup.h | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index accb42a63..9f2609894 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -86,7 +86,7 @@ protected: cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */) : cEntity(etPickup, a_PosX, a_PosY, a_PosZ, 0.2, 0.2) - , m_Timer(0.f) + , m_Timer(0) , m_Item(a_Item) , m_bCollected(false) , m_bIsPlayerCreated(IsPlayerCreated) @@ -115,7 +115,7 @@ void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) super::Tick(a_Dt, a_Chunk); BroadcastMovementUpdate(); // Notify clients of position - m_Timer += a_Dt.count(); + m_Timer += a_Dt; if (!m_bCollected) { @@ -141,9 +141,9 @@ void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) ) { m_bCollected = true; - m_Timer = 0; // We have to reset the timer. - m_Timer += a_Dt.count(); // In case we have to destroy the pickup in the same tick. - if (m_Timer > 500.f) + m_Timer = std::chrono::milliseconds(0); // We have to reset the timer. + m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick. + if (m_Timer > std::chrono::milliseconds(500)) { Destroy(true); return; @@ -167,14 +167,14 @@ void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } else { - if (m_Timer > 500.f) // 0.5 second + if (m_Timer > std::chrono::milliseconds(500)) // 0.5 second { Destroy(true); return; } } - if (m_Timer > 1000 * 60 * 5) // 5 minutes + if (m_Timer > std::chrono::minutes(5)) // 5 minutes { Destroy(true); return; @@ -200,7 +200,7 @@ bool cPickup::CollectedBy(cPlayer & a_Dest) } // Two seconds if player created the pickup (vomiting), half a second if anything else - if (m_Timer < (m_bIsPlayerCreated ? 2000.f : 500.f)) + if (m_Timer < (m_bIsPlayerCreated ? std::chrono::seconds(2) : std::chrono::milliseconds(500))) { // LOG("Pickup %d cannot be collected by \"%s\", because it is not old enough.", m_UniqueID, a_Dest->GetName().c_str()); return false; // Not old enough @@ -234,7 +234,7 @@ bool cPickup::CollectedBy(cPlayer & a_Dest) // All of the pickup has been collected, schedule the pickup for destroying m_bCollected = true; } - m_Timer = 0; + m_Timer = std::chrono::milliseconds(0); return true; } diff --git a/src/Entities/Pickup.h b/src/Entities/Pickup.h index 67f2756ca..ed5949f37 100644 --- a/src/Entities/Pickup.h +++ b/src/Entities/Pickup.h @@ -37,10 +37,10 @@ public: virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; /** Returns the number of ticks that this entity has existed */ - int GetAge(void) const { return static_cast(m_Timer / 50); } // tolua_export + int GetAge(void) const { return std::chrono::duration_cast(m_Timer).count(); } // tolua_export /** Set the number of ticks that this entity has existed */ - void SetAge(int a_Age) { m_Timer = static_cast(a_Age * 50); } // tolua_export + void SetAge(int a_Age) { m_Timer = cTickTime(a_Age); } // tolua_export /** Returns true if the pickup has already been collected */ bool IsCollected(void) const { return m_bCollected; } // tolua_export @@ -51,7 +51,7 @@ public: private: /** The number of ticks that the entity has existed / timer between collect and destroy; in msec */ - float m_Timer; + std::chrono::milliseconds m_Timer; cItem m_Item; -- cgit v1.2.3 From bfe1960191e902013b5ac75e8a3a487496d63bb9 Mon Sep 17 00:00:00 2001 From: Tycho Date: Fri, 16 Jan 2015 14:38:21 +0000 Subject: Converted Monster to std::chrono --- src/Mobs/AggressiveMonster.cpp | 2 +- src/Mobs/AggressiveMonster.h | 2 +- src/Mobs/Monster.cpp | 22 +++++++++++----------- src/Mobs/Monster.h | 10 +++++----- 4 files changed, 18 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index b9e80c01d..72317d66b 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -22,7 +22,7 @@ cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eMonsterTyp // What to do if in Chasing State -void cAggressiveMonster::InStateChasing(float a_Dt) +void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt) { super::InStateChasing(a_Dt); diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h index 932915055..f64c1103f 100644 --- a/src/Mobs/AggressiveMonster.h +++ b/src/Mobs/AggressiveMonster.h @@ -17,7 +17,7 @@ public: cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void InStateChasing(float a_Dt) override; + virtual void InStateChasing(std::chrono::milliseconds a_Dt) override; virtual void EventSeePlayer(cEntity *) override; virtual void Attack(std::chrono::milliseconds a_Dt); diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 30bbd0ff2..6e07bfbb6 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -259,8 +259,8 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (m_Health <= 0) { // The mob is dead, but we're still animating the "puff" they leave when they die - m_DestroyTimer += a_Dt.count() / 1000; - if (m_DestroyTimer > 1) + m_DestroyTimer += a_Dt; + if (m_DestroyTimer > std::chrono::seconds(1)) { Destroy(true); } @@ -345,18 +345,18 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) case IDLE: { // If enemy passive we ignore checks for player visibility - InStateIdle(std::chrono::duration_cast(a_Dt).count()); + InStateIdle(a_Dt); break; } case CHASING: { // If we do not see a player anymore skip chasing action - InStateChasing(std::chrono::duration_cast(a_Dt).count()); + InStateChasing(a_Dt); break; } case ESCAPING: { - InStateEscaping(std::chrono::duration_cast(a_Dt).count()); + InStateEscaping(a_Dt); break; } @@ -555,7 +555,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) { m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); } - m_DestroyTimer = 0; + m_DestroyTimer = std::chrono::milliseconds(0); } @@ -638,7 +638,7 @@ void cMonster::EventLosePlayer(void) -void cMonster::InStateIdle(float a_Dt) +void cMonster::InStateIdle(std::chrono::milliseconds a_Dt) { if (m_bMovingToDestination) { @@ -647,11 +647,11 @@ void cMonster::InStateIdle(float a_Dt) m_IdleInterval += a_Dt; - if (m_IdleInterval > 1) + if (m_IdleInterval > std::chrono::seconds(1)) { // At this interval the results are predictable int rem = m_World->GetTickRandomNumber(6) + 1; - m_IdleInterval -= 1; // So nothing gets dropped when the server hangs for a few seconds + m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds Vector3d Dist; Dist.x = (double)m_World->GetTickRandomNumber(10) - 5; @@ -678,7 +678,7 @@ void cMonster::InStateIdle(float a_Dt) // What to do if in Chasing State // This state should always be defined in each child class -void cMonster::InStateChasing(float a_Dt) +void cMonster::InStateChasing(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); } @@ -688,7 +688,7 @@ void cMonster::InStateChasing(float a_Dt) // What to do if in Escaping State -void cMonster::InStateEscaping(float a_Dt) +void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 5752a2040..21ed0c25a 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -80,9 +80,9 @@ public: virtual void EventLosePlayer(void); virtual void CheckEventLostPlayer(void); - virtual void InStateIdle (float a_Dt); - virtual void InStateChasing (float a_Dt); - virtual void InStateEscaping(float a_Dt); + virtual void InStateIdle (std::chrono::milliseconds a_Dt); + virtual void InStateChasing (std::chrono::milliseconds a_Dt); + virtual void InStateEscaping(std::chrono::milliseconds a_Dt); int GetAttackRate() { return static_cast(m_AttackRate); } void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } @@ -217,8 +217,8 @@ protected: /* =========================== */ - float m_IdleInterval; - float m_DestroyTimer; + std::chrono::milliseconds m_IdleInterval; + std::chrono::milliseconds m_DestroyTimer; eMonsterType m_MobType; AString m_CustomName; -- cgit v1.2.3 From 2ce2741968e861d10acfe1e7b012ec2448accdc7 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Fri, 16 Jan 2015 18:42:19 +0300 Subject: Fixed CppCheck: (performance) Function parameter should be passed by reference. --- src/Bindings/PluginManager.cpp | 4 ++-- src/Bindings/PluginManager.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index fad0a36d2..6fb9a5acb 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -771,7 +771,7 @@ bool cPluginManager::CallHookPlayerFoodLevelChange(cPlayer & a_Player, int a_New -bool cPluginManager::CallHookPlayerFished(cPlayer & a_Player, const cItems a_Reward) +bool cPluginManager::CallHookPlayerFished(cPlayer & a_Player, const cItems & a_Reward) { FIND_HOOK(HOOK_PLAYER_FISHED); VERIFY_HOOK; @@ -847,7 +847,7 @@ bool cPluginManager::CallHookPlayerLeftClick(cPlayer & a_Player, int a_BlockX, i -bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition) +bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { FIND_HOOK(HOOK_PLAYER_MOVING); VERIFY_HOOK; diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index d4b82376a..576430964 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -203,12 +203,12 @@ public: bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); bool CallHookPlayerDestroyed (cPlayer & a_Player); bool CallHookPlayerEating (cPlayer & a_Player); - bool CallHookPlayerFished (cPlayer & a_Player, const cItems a_Reward); + bool CallHookPlayerFished (cPlayer & a_Player, const cItems & a_Reward); bool CallHookPlayerFishing (cPlayer & a_Player, cItems a_Reward); bool CallHookPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel); bool CallHookPlayerJoined (cPlayer & a_Player); bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); - bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition); + bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition); bool CallHookPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange); bool CallHookPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange); bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); -- cgit v1.2.3 From 3dc994f9dca109c0d039d6ba39480eaf67d7cc57 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Sat, 17 Jan 2015 15:12:14 +0300 Subject: Fixed CppCheck: (performance) Possible inefficient checking for emptiness. --- src/LightingThread.cpp | 2 +- src/MobProximityCounter.cpp | 2 +- src/OSSupport/Queue.h | 4 ++-- src/Server.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp index ced95d4e1..effde03d0 100644 --- a/src/LightingThread.cpp +++ b/src/LightingThread.cpp @@ -197,7 +197,7 @@ void cLightingThread::Execute(void) { { cCSLock Lock(m_CS); - if (m_Queue.size() == 0) + if (m_Queue.empty()) { cCSUnlock Unlock(Lock); m_evtItemAdded.Wait(); diff --git a/src/MobProximityCounter.cpp b/src/MobProximityCounter.cpp index 82ba771ff..cfd52440b 100644 --- a/src/MobProximityCounter.cpp +++ b/src/MobProximityCounter.cpp @@ -50,7 +50,7 @@ cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDist a_DistanceMin *= a_DistanceMin;// this is because is use square distance a_DistanceMax *= a_DistanceMax; - if (m_DistanceToMonster.size() <= 0) + if (m_DistanceToMonster.empty()) { convertMaps(); } diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h index 82f221453..afbde1b0d 100644 --- a/src/OSSupport/Queue.h +++ b/src/OSSupport/Queue.h @@ -86,7 +86,7 @@ public: bool TryDequeueItem(ItemType & item) { cCSLock Lock(m_CS); - if (m_Contents.size() == 0) + if (m_Contents.empty()) { return false; } @@ -101,7 +101,7 @@ public: ItemType DequeueItem(void) { cCSLock Lock(m_CS); - while (m_Contents.size() == 0) + while (m_Contents.empty()) { cCSUnlock Unlock(Lock); m_evtAdded.Wait(); diff --git a/src/Server.cpp b/src/Server.cpp index 3eaf6e096..4dbe59ac6 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -764,7 +764,7 @@ void cServer::cNotifyWriteThread::Execute(void) while (!m_ShouldTerminate) { cCSLock Lock(m_CS); - while (m_Clients.size() == 0) + while (m_Clients.empty()) { cCSUnlock Unlock(Lock); m_Event.Wait(); -- cgit v1.2.3 From 41f30edcf77521630b7f69d3e94338e6ab2f821f Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Sat, 17 Jan 2015 16:00:12 +0300 Subject: Fixed CppCheck: (performance) Prefer prefix ++/-- operators for non-primitive types. --- src/Root.cpp | 2 +- src/SetChunkData.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Root.cpp b/src/Root.cpp index 8951bafe6..eaacf3608 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -639,7 +639,7 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac bool cRoot::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback) { - for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end();itr++) + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) { if (itr->second->DoWithPlayerByUUID(a_PlayerUUID, a_Callback)) { diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp index e335176a8..5a0bea980 100644 --- a/src/SetChunkData.cpp +++ b/src/SetChunkData.cpp @@ -132,7 +132,7 @@ void cSetChunkData::RemoveInvalidBlockEntities(void) ItemTypeToString(WorldBlockType).c_str(), WorldBlockType ); cBlockEntityList::iterator itr2 = itr; - itr2++; + ++itr2; delete *itr; m_BlockEntities.erase(itr); itr = itr2; -- cgit v1.2.3 From 6758c1d2a15c789e232c8a3f7b51f23b66efd6e1 Mon Sep 17 00:00:00 2001 From: worktycho Date: Sat, 17 Jan 2015 22:24:25 +0000 Subject: correct comment to say milliseconds --- src/World.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/World.cpp b/src/World.cpp index cd99f2c2d..46488d58b 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -869,7 +869,7 @@ void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_La if (m_IsDaylightCycleEnabled) { - // We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it + // We need sub-tick precision here, that's why we store the time in milliseconds and calculate ticks off of it m_TimeOfDay += a_Dt; // Wrap time of day each 20 minutes (1200 seconds) -- cgit v1.2.3 From e211aafaa45b0e4a12e9c50ee445377077ea8172 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 18 Jan 2015 11:02:17 +0100 Subject: Fixed type-conversion warnings. --- src/Bindings/LuaState.cpp | 2 +- src/Entities/Entity.cpp | 8 ++++---- src/Entities/FireworkEntity.cpp | 4 ++-- src/Entities/Minecart.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 4bc9906ee..01d3ac687 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -843,7 +843,7 @@ void cLuaState::Push(std::chrono::milliseconds a_Value) { ASSERT(IsValid()); - tolua_pushnumber(m_LuaState, a_Value.count()); + tolua_pushnumber(m_LuaState, static_cast(a_Value.count())); m_NumCurrentFunctionArgs += 1; } diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index c64d94528..c51a27961 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -927,11 +927,11 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (!m_bOnGround) { - float fallspeed; + double fallspeed; if (IsBlockWater(BlockIn)) { fallspeed = m_Gravity * DtSec.count() / 3; // Fall 3x slower in water - ApplyFriction(NextSpeed, 0.7, DtSec.count()); + ApplyFriction(NextSpeed, 0.7, static_cast(DtSec.count())); } else if (BlockIn == E_BLOCK_COBWEB) { @@ -943,11 +943,11 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // Normal gravity fallspeed = m_Gravity * DtSec.count(); } - NextSpeed.y += fallspeed; + NextSpeed.y += static_cast(fallspeed); } else { - ApplyFriction(NextSpeed, 0.7, DtSec.count()); + ApplyFriction(NextSpeed, 0.7, static_cast(DtSec.count())); } // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we diff --git a/src/Entities/FireworkEntity.cpp b/src/Entities/FireworkEntity.cpp index 9dc7850a7..32eaf669a 100644 --- a/src/Entities/FireworkEntity.cpp +++ b/src/Entities/FireworkEntity.cpp @@ -28,7 +28,7 @@ void cFireworkEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_C if ((PosY < 0) || (PosY >= cChunkDef::Height)) { AddSpeedY(1); - AddPosition(GetSpeed() * (a_Dt.count() / 1000)); + AddPosition(GetSpeed() * (static_cast(a_Dt.count()) / 1000)); return; } @@ -53,7 +53,7 @@ void cFireworkEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_C } AddSpeedY(1); - AddPosition(GetSpeed() * (a_Dt.count() / 1000)); + AddPosition(GetSpeed() * (static_cast(a_Dt.count()) / 1000)); } diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index a906c9767..776f957f4 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -177,7 +177,7 @@ void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) default: VERIFY(!"Unhandled rail type despite checking if block was rail!"); break; } - AddPosition(GetSpeed() * (a_Dt.count() / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp + AddPosition(GetSpeed() * (static_cast(a_Dt.count()) / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp } else { -- cgit v1.2.3 From 83ed6a2c1b4da2e4a3467090e56eebb2d3e48a30 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 18 Jan 2015 11:25:16 +0100 Subject: Fixed type conversion warnings. --- src/Simulator/FireSimulator.cpp | 2 +- src/World.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp index 0439ebdca..8456ed11d 100644 --- a/src/Simulator/FireSimulator.cpp +++ b/src/Simulator/FireSimulator.cpp @@ -92,7 +92,7 @@ void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, { cCoordWithIntList & Data = a_Chunk->GetFireSimulatorData(); - int NumMSecs = a_Dt.count(); + int NumMSecs = static_cast(a_Dt.count()); for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();) { int x = itr->x; diff --git a/src/World.cpp b/src/World.cpp index 46488d58b..eb76abc2c 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -905,14 +905,14 @@ void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_La m_ChunkMap->Tick(a_Dt); - TickClients(a_Dt.count()); + TickClients(static_cast(a_Dt.count())); TickQueuedBlocks(); TickQueuedTasks(); TickScheduledTasks(); - GetSimulatorManager()->Simulate(a_Dt.count()); + GetSimulatorManager()->Simulate(static_cast(a_Dt.count())); - TickWeather(a_Dt.count()); + TickWeather(static_cast(a_Dt.count())); m_ChunkMap->FastSetQueuedBlocks(); -- cgit v1.2.3 From b1c58b7b5282ac22287be81d7600bc9580cfdf39 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 18 Jan 2015 15:10:05 +0100 Subject: cWorld: Fixed a type warning. --- src/World.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/World.h b/src/World.h index 1f58ddbb7..e7519dab8 100644 --- a/src/World.h +++ b/src/World.h @@ -803,7 +803,7 @@ public: int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem * a_Item, const Vector3d * a_Speed = nullptr); // tolua_export /** Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! */ - int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); } + int GetTickRandomNumber(int a_Range) { return (int)(m_TickRand.randInt(a_Range)); } /** Appends all usernames starting with a_Text (case-insensitive) into Results */ void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results); -- cgit v1.2.3 From 45b1d5ff78e23b3de9cbee1e247e2f275b77e9d9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 18 Jan 2015 18:01:24 +0100 Subject: Fixed various warnings. --- src/HTTPServer/HTTPMessage.cpp | 2 +- src/Items/ItemBucket.h | 12 ++++++------ src/OSSupport/StackTrace.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp index f6c0204ae..d59ca438e 100644 --- a/src/HTTPServer/HTTPMessage.cpp +++ b/src/HTTPServer/HTTPMessage.cpp @@ -55,7 +55,7 @@ void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value) } else if (Key == "content-length") { - m_ContentLength = atoi(m_Headers[Key].c_str()); + m_ContentLength = static_cast(atol(m_Headers[Key].c_str())); } } diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h index 3a533958f..871db821c 100644 --- a/src/Items/ItemBucket.h +++ b/src/Items/ItemBucket.h @@ -199,16 +199,16 @@ public: Vector3i m_Pos; BLOCKTYPE m_ReplacedBlock; - virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override + virtual bool OnNextBlock(int a_CBBlockX, int a_CBBlockY, int a_CBBlockZ, BLOCKTYPE a_CBBlockType, NIBBLETYPE a_CBBlockMeta, char a_CBEntryFace) override { - if (a_BlockType != E_BLOCK_AIR) + if (a_CBBlockType != E_BLOCK_AIR) { - m_ReplacedBlock = a_BlockType; - if (!cFluidSimulator::CanWashAway(a_BlockType) && !IsBlockLiquid(a_BlockType)) + m_ReplacedBlock = a_CBBlockType; + if (!cFluidSimulator::CanWashAway(a_CBBlockType) && !IsBlockLiquid(a_CBBlockType)) { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, (eBlockFace)a_EntryFace); // Was an unwashawayable block, can't overwrite it! + AddFaceDirection(a_CBBlockX, a_CBBlockY, a_CBBlockZ, (eBlockFace)a_CBEntryFace); // Was an unwashawayable block, can't overwrite it! } - m_Pos.Set(a_BlockX, a_BlockY, a_BlockZ); // (Block could be washed away, replace it) + m_Pos.Set(a_CBBlockX, a_CBBlockY, a_CBBlockZ); // (Block could be washed away, replace it) return true; // Abort tracing } return false; diff --git a/src/OSSupport/StackTrace.cpp b/src/OSSupport/StackTrace.cpp index a56568457..015a53ba0 100644 --- a/src/OSSupport/StackTrace.cpp +++ b/src/OSSupport/StackTrace.cpp @@ -34,7 +34,7 @@ void PrintStackTrace(void) // Use the backtrace() function to get and output the stackTrace: // Code adapted from http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes void * stackTrace[30]; - size_t numItems = backtrace(stackTrace, ARRAYCOUNT(stackTrace)); + int numItems = backtrace(stackTrace, ARRAYCOUNT(stackTrace)); backtrace_symbols_fd(stackTrace, numItems, STDERR_FILENO); #endif } -- cgit v1.2.3 From 5ac3a23586d746eae27fb2375cfd0696d512704b Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 18 Jan 2015 22:43:35 +0100 Subject: Fixed warnings in 1.7 protocol. --- src/ClientHandle.cpp | 4 +- src/ClientHandle.h | 4 +- src/Protocol/Protocol17x.cpp | 233 +++++++++++++++++++++++-------------------- 3 files changed, 131 insertions(+), 110 deletions(-) (limited to 'src') diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 6fe6e99fa..387cc4628 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1445,7 +1445,7 @@ void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_ -void cClientHandle::HandleAnimation(char a_Animation) +void cClientHandle::HandleAnimation(int a_Animation) { if (cPluginManager::Get()->CallHookPlayerAnimation(*m_Player, a_Animation)) { @@ -2832,7 +2832,7 @@ void cClientHandle::PacketUnknown(UInt32 a_PacketType) -void cClientHandle::PacketError(unsigned char a_PacketType) +void cClientHandle::PacketError(UInt32 a_PacketType) { LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username.c_str()); SendDisconnect("Protocol error"); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 5e10bb52f..03ae38cfd 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -251,10 +251,10 @@ public: // Calls that cProtocol descendants use to report state: void PacketBufferFull(void); void PacketUnknown(UInt32 a_PacketType); - void PacketError(unsigned char a_PacketType); + void PacketError(UInt32 a_PacketType); // Calls that cProtocol descendants use for handling packets: - void HandleAnimation(char a_Animation); + void HandleAnimation(int a_Animation); /** Called when the protocol receives a MC|ItemName plugin message, indicating that the player named an item in the anvil UI. */ diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index dac1ebde8..169367949 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -174,10 +174,10 @@ void cProtocol172::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, cha cPacketizer Pkt(*this, 0x24); // Block Action packet Pkt.WriteInt(a_BlockX); - Pkt.WriteShort(a_BlockY); + Pkt.WriteShort(static_cast(a_BlockY)); Pkt.WriteInt(a_BlockZ); - Pkt.WriteByte(a_Byte1); - Pkt.WriteByte(a_Byte2); + Pkt.WriteByte(static_cast(a_Byte1)); + Pkt.WriteByte(static_cast(a_Byte2)); Pkt.WriteVarInt(a_BlockType); } @@ -190,7 +190,7 @@ void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x25); // Block Break Animation packet - Pkt.WriteVarInt(a_EntityID); + Pkt.WriteVarInt(static_cast(a_EntityID)); Pkt.WriteInt(a_BlockX); Pkt.WriteInt(a_BlockY); Pkt.WriteInt(a_BlockZ); @@ -204,10 +204,11 @@ void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY void cProtocol172::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_BlockY >= 0) && (a_BlockY < 256)); cPacketizer Pkt(*this, 0x23); // Block Change packet Pkt.WriteInt(a_BlockX); - Pkt.WriteByte(a_BlockY); + Pkt.WriteByte(static_cast(a_BlockY)); Pkt.WriteInt(a_BlockZ); Pkt.WriteVarInt(a_BlockType); Pkt.WriteByte(a_BlockMeta); @@ -228,8 +229,8 @@ void cProtocol172::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV Pkt.WriteInt((int)a_Changes.size() * 4); for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) { - unsigned int Coords = itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12); - unsigned int Blocks = itr->m_BlockMeta | (itr->m_BlockType << 4); + int Coords = itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12); + int Blocks = static_cast(itr->m_BlockMeta | (itr->m_BlockType << 4)); Pkt.WriteInt((Coords << 16) | Blocks); } // for itr - a_Changes[] } @@ -352,11 +353,13 @@ void cProtocol172::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) void cProtocol172::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_EffectID >= 0) && (a_EffectID < 256)); + ASSERT((a_Amplifier >= 0) && (a_Amplifier < 256)); cPacketizer Pkt(*this, 0x1D); // Entity Effect packet Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_EffectID); - Pkt.WriteByte(a_Amplifier); + Pkt.WriteByte(static_cast(a_EffectID)); + Pkt.WriteByte(static_cast(a_Amplifier)); Pkt.WriteShort(a_Duration); } @@ -438,9 +441,9 @@ void cProtocol172::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char cPacketizer Pkt(*this, 0x15); // Entity Relative Move packet Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_RelX); - Pkt.WriteByte(a_RelY); - Pkt.WriteByte(a_RelZ); + Pkt.WriteChar(a_RelX); + Pkt.WriteChar(a_RelY); + Pkt.WriteChar(a_RelZ); } @@ -453,9 +456,9 @@ void cProtocol172::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, cPacketizer Pkt(*this, 0x17); // Entity Look And Relative Move packet Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_RelX); - Pkt.WriteByte(a_RelY); - Pkt.WriteByte(a_RelZ); + Pkt.WriteChar(a_RelX); + Pkt.WriteChar(a_RelY); + Pkt.WriteChar(a_RelZ); Pkt.WriteByteAngle(a_Entity.GetYaw()); Pkt.WriteByteAngle(a_Entity.GetPitch()); } @@ -537,9 +540,9 @@ void cProtocol172::SendHealth(void) cPacketizer Pkt(*this, 0x06); // Update Health packet cPlayer * Player = m_Client->GetPlayer(); - Pkt.WriteFloat((float)Player->GetHealth()); - Pkt.WriteShort(Player->GetFoodLevel()); - Pkt.WriteFloat((float)Player->GetFoodSaturationLevel()); + Pkt.WriteFloat(static_cast(Player->GetHealth())); + Pkt.WriteShort(static_cast(Player->GetFoodLevel())); + Pkt.WriteFloat(static_cast(Player->GetFoodSaturationLevel())); } @@ -584,10 +587,10 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World) cServer * Server = cRoot::Get()->GetServer(); cPacketizer Pkt(*this, 0x01); // Join Game packet Pkt.WriteInt(a_Player.GetUniqueID()); - Pkt.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4 - Pkt.WriteChar((char)a_World.GetDimension()); + Pkt.WriteByte(static_cast(a_Player.GetEffectiveGameMode()) | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4 + Pkt.WriteChar(static_cast(a_World.GetDimension())); Pkt.WriteByte(2); // TODO: Difficulty (set to Normal) - Pkt.WriteByte(std::min(Server->GetMaxPlayers(), 60)); + Pkt.WriteByte(static_cast(std::min(Server->GetMaxPlayers(), 60))); Pkt.WriteString("default"); // Level type - wtf? } m_LastSentDimension = a_World.GetDimension(); @@ -595,9 +598,9 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World) // Send the spawn position: { cPacketizer Pkt(*this, 0x05); // Spawn Position packet - Pkt.WriteInt((int)a_World.GetSpawnX()); - Pkt.WriteInt((int)a_World.GetSpawnY()); - Pkt.WriteInt((int)a_World.GetSpawnZ()); + Pkt.WriteInt(static_cast(a_World.GetSpawnX())); + Pkt.WriteInt(static_cast(a_World.GetSpawnY())); + Pkt.WriteInt(static_cast(a_World.GetSpawnZ())); } // Send player abilities: @@ -629,11 +632,11 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting) ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x10); // Spawn Painting packet - Pkt.WriteVarInt(a_Painting.GetUniqueID()); + Pkt.WriteVarInt(static_cast(a_Painting.GetUniqueID())); Pkt.WriteString(a_Painting.GetName().c_str()); - Pkt.WriteInt((int)a_Painting.GetPosX()); - Pkt.WriteInt((int)a_Painting.GetPosY()); - Pkt.WriteInt((int)a_Painting.GetPosZ()); + Pkt.WriteInt(static_cast(a_Painting.GetPosX())); + Pkt.WriteInt(static_cast(a_Painting.GetPosY())); + Pkt.WriteInt(static_cast(a_Painting.GetPosZ())); Pkt.WriteInt(a_Painting.GetDirection()); } @@ -644,19 +647,19 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting) void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale) { ASSERT(m_State == 3); // In game mode? + ASSERT(a_Length + 3 <= USHRT_MAX); + ASSERT((a_X >= 0) && (a_X < 256)); + ASSERT((a_Y >= 0) && (a_Y < 256)); cPacketizer Pkt(*this, 0x34); - Pkt.WriteVarInt(a_ID); - Pkt.WriteShort (3 + a_Length); + Pkt.WriteVarInt(static_cast(a_ID)); + Pkt.WriteShort (static_cast(3 + a_Length)); Pkt.WriteByte(0); - Pkt.WriteByte(a_X); - Pkt.WriteByte(a_Y); + Pkt.WriteByte(static_cast(a_X)); + Pkt.WriteByte(static_cast(a_Y)); - for (unsigned int i = 0; i < a_Length; ++i) - { - Pkt.WriteByte(a_Colors[i]); - } + Pkt.WriteBuf(reinterpret_cast(a_Colors), a_Length); } @@ -666,18 +669,21 @@ void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colo void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale) { ASSERT(m_State == 3); // In game mode? + ASSERT(1 + 3 * a_Decorators.size() < USHRT_MAX); cPacketizer Pkt(*this, 0x34); - Pkt.WriteVarInt(a_ID); - Pkt.WriteShort ((short)(1 + (3 * a_Decorators.size()))); + Pkt.WriteVarInt(static_cast(a_ID)); + Pkt.WriteShort (static_cast(1 + (3 * a_Decorators.size()))); Pkt.WriteByte(1); for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it) { - Pkt.WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf)); - Pkt.WriteByte(it->GetPixelX()); - Pkt.WriteByte(it->GetPixelZ()); + ASSERT((it->GetPixelX() >= 0) && (it->GetPixelX() < 256)); + ASSERT((it->GetPixelZ() >= 0) && (it->GetPixelZ() < 256)); + Pkt.WriteByte(static_cast((it->GetType() << 4) | static_cast(it->GetRot() & 0xf))); + Pkt.WriteByte(static_cast(it->GetPixelX())); + Pkt.WriteByte(static_cast(it->GetPixelZ())); } } @@ -688,13 +694,14 @@ void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decor void cProtocol172::SendMapInfo(int a_ID, unsigned int a_Scale) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_Scale >= 0) && (a_Scale < 256)); cPacketizer Pkt(*this, 0x34); - Pkt.WriteVarInt(a_ID); + Pkt.WriteVarInt(static_cast(a_ID)); Pkt.WriteShort (2); Pkt.WriteByte(2); - Pkt.WriteByte(a_Scale); + Pkt.WriteByte(static_cast(a_Scale)); } @@ -707,7 +714,7 @@ void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup) { cPacketizer Pkt(*this, 0x0e); // Spawn Object packet - Pkt.WriteVarInt(a_Pickup.GetUniqueID()); + Pkt.WriteVarInt(static_cast(a_Pickup.GetUniqueID())); Pkt.WriteByte(2); // Type = Pickup Pkt.WriteFPInt(a_Pickup.GetPosX()); Pkt.WriteFPInt(a_Pickup.GetPosY()); @@ -763,7 +770,7 @@ void cProtocol172::SendEntityAnimation(const cEntity & a_Entity, char a_Animatio ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x0b); // Animation packet - Pkt.WriteVarInt(a_Entity.GetUniqueID()); + Pkt.WriteVarInt(static_cast(a_Entity.GetUniqueID())); Pkt.WriteChar(a_Animation); } @@ -865,7 +872,7 @@ void cProtocol172::SendPlayerMaxSpeed(void) { Pkt.WriteShort(1); // Modifier count Pkt.WriteInt64(0x662a6b8dda3e4c1c); - Pkt.WriteInt64(0x881396ea6097278d); // UUID of the modifier + Pkt.WriteInt64(static_cast(0x881396ea6097278d)); // UUID of the modifier Pkt.WriteDouble(Player->GetSprintingMaxSpeed() - Player->GetNormalMaxSpeed()); Pkt.WriteByte(2); } @@ -960,10 +967,11 @@ void cProtocol172::SendPluginMessage(const AString & a_Channel, const AString & void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_EffectID >= 0) && (a_EffectID < 256)); cPacketizer Pkt(*this, 0x1e); Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_EffectID); + Pkt.WriteByte(static_cast(a_EffectID)); } @@ -1009,13 +1017,14 @@ void cProtocol172::SendExperience (void) void cProtocol172::SendExperienceOrb(const cExpOrb & a_ExpOrb) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_ExpOrb.GetReward() >= 0) && (a_ExpOrb.GetReward() < SHRT_MAX)); cPacketizer Pkt(*this, 0x11); - Pkt.WriteVarInt(a_ExpOrb.GetUniqueID()); + Pkt.WriteVarInt(static_cast(a_ExpOrb.GetUniqueID())); Pkt.WriteFPInt(a_ExpOrb.GetPosX()); Pkt.WriteFPInt(a_ExpOrb.GetPosY()); Pkt.WriteFPInt(a_ExpOrb.GetPosZ()); - Pkt.WriteShort(a_ExpOrb.GetReward()); + Pkt.WriteShort(static_cast(a_ExpOrb.GetReward())); } @@ -1060,7 +1069,7 @@ void cProtocol172::SendDisplayObjective(const AString & a_Objective, cScoreboard ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x3d); - Pkt.WriteByte((int) a_Display); + Pkt.WriteByte(static_cast(a_Display)); Pkt.WriteString(a_Objective); } @@ -1074,11 +1083,11 @@ void cProtocol172::SendSoundEffect(const AString & a_SoundName, double a_X, doub cPacketizer Pkt(*this, 0x29); // Sound Effect packet Pkt.WriteString(a_SoundName); - Pkt.WriteInt((int)(a_X * 8.0)); - Pkt.WriteInt((int)(a_Y * 8.0)); - Pkt.WriteInt((int)(a_Z * 8.0)); + Pkt.WriteInt(static_cast(a_X * 8.0)); + Pkt.WriteInt(static_cast(a_Y * 8.0)); + Pkt.WriteInt(static_cast(a_Z * 8.0)); Pkt.WriteFloat(a_Volume); - Pkt.WriteByte((Byte)(a_Pitch * 63)); + Pkt.WriteByte(static_cast(a_Pitch * 63)); } @@ -1088,11 +1097,12 @@ void cProtocol172::SendSoundEffect(const AString & a_SoundName, double a_X, doub void cProtocol172::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_SrcY >= 0) && (a_SrcY < 256)); cPacketizer Pkt(*this, 0x28); // Effect packet Pkt.WriteInt(a_EffectID); Pkt.WriteInt(a_SrcX); - Pkt.WriteByte(a_SrcY); + Pkt.WriteByte(static_cast(a_SrcY)); Pkt.WriteInt(a_SrcZ); Pkt.WriteInt(a_Data); Pkt.WriteBool(false); @@ -1107,17 +1117,17 @@ void cProtocol172::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x0e); // Spawn Object packet - Pkt.WriteVarInt(a_FallingBlock.GetUniqueID()); + Pkt.WriteVarInt(static_cast(a_FallingBlock.GetUniqueID())); Pkt.WriteByte(70); // Falling block Pkt.WriteFPInt(a_FallingBlock.GetPosX()); Pkt.WriteFPInt(a_FallingBlock.GetPosY()); Pkt.WriteFPInt(a_FallingBlock.GetPosZ()); Pkt.WriteByteAngle(a_FallingBlock.GetYaw()); Pkt.WriteByteAngle(a_FallingBlock.GetPitch()); - Pkt.WriteInt(((int)a_FallingBlock.GetBlockType()) | (((int)a_FallingBlock.GetBlockMeta()) << 16)); // Or 0x10 - Pkt.WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); + Pkt.WriteInt((static_cast(a_FallingBlock.GetBlockType()) | ((static_cast(a_FallingBlock.GetBlockMeta()) << 16)))); + Pkt.WriteShort(static_cast(a_FallingBlock.GetSpeedX() * 400)); + Pkt.WriteShort(static_cast(a_FallingBlock.GetSpeedY() * 400)); + Pkt.WriteShort(static_cast(a_FallingBlock.GetSpeedZ() * 400)); } @@ -1129,7 +1139,7 @@ void cProtocol172::SendSpawnMob(const cMonster & a_Mob) ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x0f); // Spawn Mob packet - Pkt.WriteVarInt(a_Mob.GetUniqueID()); + Pkt.WriteVarInt(static_cast(a_Mob.GetUniqueID())); Pkt.WriteByte((Byte)a_Mob.GetMobType()); Pkt.WriteFPInt(a_Mob.GetPosX()); Pkt.WriteFPInt(a_Mob.GetPosY()); @@ -1137,9 +1147,9 @@ void cProtocol172::SendSpawnMob(const cMonster & a_Mob) Pkt.WriteByteAngle(a_Mob.GetPitch()); Pkt.WriteByteAngle(a_Mob.GetHeadYaw()); Pkt.WriteByteAngle(a_Mob.GetYaw()); - Pkt.WriteShort((short)(a_Mob.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_Mob.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_Mob.GetSpeedZ() * 400)); + Pkt.WriteShort(static_cast(a_Mob.GetSpeedX() * 400)); + Pkt.WriteShort(static_cast(a_Mob.GetSpeedY() * 400)); + Pkt.WriteShort(static_cast(a_Mob.GetSpeedZ() * 400)); Pkt.WriteEntityMetadata(a_Mob); Pkt.WriteByte(0x7f); // Metadata terminator } @@ -1153,8 +1163,8 @@ void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0xe); // Spawn Object packet - Pkt.WriteVarInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_ObjectType); + Pkt.WriteVarInt(static_cast(a_Entity.GetUniqueID())); + Pkt.WriteChar(a_ObjectType); Pkt.WriteFPInt(a_Entity.GetPosX()); Pkt.WriteFPInt(a_Entity.GetPosY()); Pkt.WriteFPInt(a_Entity.GetPosZ()); @@ -1163,9 +1173,9 @@ void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, Pkt.WriteInt(a_ObjectData); if (a_ObjectData != 0) { - Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + Pkt.WriteShort(static_cast(a_Entity.GetSpeedX() * 400)); + Pkt.WriteShort(static_cast(a_Entity.GetSpeedY() * 400)); + Pkt.WriteShort(static_cast(a_Entity.GetSpeedZ() * 400)); } } @@ -1178,8 +1188,8 @@ void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0xe); // Spawn Object packet - Pkt.WriteVarInt(a_Vehicle.GetUniqueID()); - Pkt.WriteByte(a_VehicleType); + Pkt.WriteVarInt(static_cast(a_Vehicle.GetUniqueID())); + Pkt.WriteChar(a_VehicleType); Pkt.WriteFPInt(a_Vehicle.GetPosX()); Pkt.WriteFPInt(a_Vehicle.GetPosY()); Pkt.WriteFPInt(a_Vehicle.GetPosZ()); @@ -1188,9 +1198,9 @@ void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp Pkt.WriteInt(a_VehicleSubType); if (a_VehicleSubType != 0) { - Pkt.WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + Pkt.WriteShort(static_cast(a_Vehicle.GetSpeedX() * 400)); + Pkt.WriteShort(static_cast(a_Vehicle.GetSpeedY() * 400)); + Pkt.WriteShort(static_cast(a_Vehicle.GetSpeedZ() * 400)); } } @@ -1211,7 +1221,7 @@ void cProtocol172::SendStatistics(const cStatManager & a_Manager) const AString & StatName = cStatInfo::GetName((eStatistic) i); Pkt.WriteString(StatName); - Pkt.WriteVarInt(Value); + Pkt.WriteVarInt(static_cast(Value)); } } @@ -1224,7 +1234,7 @@ void cProtocol172::SendTabCompletionResults(const AStringVector & a_Results) ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x3a); // Tab-Complete packet - Pkt.WriteVarInt((int)a_Results.size()); + Pkt.WriteVarInt(static_cast(a_Results.size())); for (AStringVector::const_iterator itr = a_Results.begin(), end = a_Results.end(); itr != end; ++itr) { @@ -1309,7 +1319,7 @@ void cProtocol172::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity) cPacketizer Pkt(*this, 0x35); // Update tile entity packet Pkt.WriteInt(a_BlockEntity.GetPosX()); - Pkt.WriteShort(a_BlockEntity.GetPosY()); + Pkt.WriteShort(static_cast(a_BlockEntity.GetPosY())); Pkt.WriteInt(a_BlockEntity.GetPosZ()); Byte Action = 0; @@ -1389,7 +1399,7 @@ void cProtocol172::SendWholeInventory(const cWindow & a_Window) cPacketizer Pkt(*this, 0x30); // Window Items packet Pkt.WriteChar(a_Window.GetWindowID()); - Pkt.WriteShort(a_Window.GetNumSlots()); + Pkt.WriteShort(static_cast(a_Window.GetNumSlots())); cItems Slots; a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); for (cItems::const_iterator itr = Slots.begin(), end = Slots.end(); itr != end; ++itr) @@ -1426,9 +1436,9 @@ void cProtocol172::SendWindowOpen(const cWindow & a_Window) cPacketizer Pkt(*this, 0x2d); Pkt.WriteChar(a_Window.GetWindowID()); - Pkt.WriteChar(a_Window.GetWindowType()); + Pkt.WriteChar(static_cast(a_Window.GetWindowType())); Pkt.WriteString(a_Window.GetWindowTitle()); - Pkt.WriteChar(a_Window.GetNumNonInventorySlots()); + Pkt.WriteChar(static_cast(a_Window.GetNumNonInventorySlots())); Pkt.WriteBool(true); if (a_Window.GetWindowType() == cWindow::wtAnimalChest) { @@ -1505,7 +1515,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size) break; } cByteBuffer bb(PacketLen + 1); - VERIFY(m_ReceivedData.ReadToByteBuffer(bb, (int)PacketLen)); + VERIFY(m_ReceivedData.ReadToByteBuffer(bb, static_cast(PacketLen))); m_ReceivedData.CommitRead(); UInt32 PacketType; @@ -1748,8 +1758,14 @@ void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffe { short EncKeyLength, EncNonceLength; a_ByteBuffer.ReadBEShort(EncKeyLength); + if ((EncKeyLength < 0) || (EncKeyLength > MAX_ENC_LEN)) + { + LOGD("Invalid Encryption Key length: %d. Kicking client.", EncKeyLength); + m_Client->Kick("Invalid EncKeyLength"); + return; + } AString EncKey; - if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength)) + if (!a_ByteBuffer.ReadString(EncKey, static_cast(EncKeyLength))) { return; } @@ -1757,15 +1773,15 @@ void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffe { return; } - AString EncNonce; - if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength)) + if ((EncNonceLength < 0) || (EncNonceLength > MAX_ENC_LEN)) { + LOGD("Invalid Encryption Nonce length: %d. Kicking client.", EncNonceLength); + m_Client->Kick("Invalid EncNonceLength"); return; } - if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) + AString EncNonce; + if (!a_ByteBuffer.ReadString(EncNonce, static_cast(EncNonceLength))) { - LOGD("Too long encryption"); - m_Client->Kick("Hacked client"); return; } @@ -1854,7 +1870,7 @@ void cProtocol172::HandlePacketAnimation(cByteBuffer & a_ByteBuffer) void cProtocol172::HandlePacketBlockDig(cByteBuffer & a_ByteBuffer) { - HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Status); + HANDLE_READ(a_ByteBuffer, ReadChar, char, Status); HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockX); HANDLE_READ(a_ByteBuffer, ReadByte, Byte, BlockY); HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockZ); @@ -2072,7 +2088,7 @@ void cProtocol172::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) if (Length + 1 != (int)a_ByteBuffer.GetReadableSpace()) { LOGD("Invalid plugin message packet, payload length doesn't match packet length (exp %d, got %d)", - (int)a_ByteBuffer.GetReadableSpace() - 1, Length + static_cast(a_ByteBuffer.GetReadableSpace()) - 1, Length ); return; } @@ -2086,7 +2102,7 @@ void cProtocol172::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) // Read the plugin message and relay to clienthandle: AString Data; - if (!a_ByteBuffer.ReadString(Data, Length)) + if (!a_ByteBuffer.ReadString(Data, static_cast(Length))) { return; } @@ -2274,7 +2290,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const LOGD("Protocol 1.7: Skipping garbage data at the end of a vanilla MC|AdvCdm packet, %u bytes", static_cast(a_PayloadLength - BytesRead) ); - a_ByteBuffer.SkipRead(a_PayloadLength - BytesRead); + a_ByteBuffer.SkipRead(static_cast(a_PayloadLength) - BytesRead); } return; } @@ -2282,7 +2298,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const { // Read the client's brand: AString Brand; - if (a_ByteBuffer.ReadString(Brand, a_PayloadLength)) + if (a_ByteBuffer.ReadString(Brand, static_cast(a_PayloadLength))) { m_Client->SetClientBrand(Brand); } @@ -2301,7 +2317,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const else if (a_Channel == "MC|ItemName") { AString ItemName; - if (a_ByteBuffer.ReadString(ItemName, a_PayloadLength)) + if (a_ByteBuffer.ReadString(ItemName, static_cast(a_PayloadLength))) { m_Client->HandleAnvilItemName(ItemName); } @@ -2317,7 +2333,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const // Read the payload and send it through to the clienthandle: AString Message; - VERIFY(a_ByteBuffer.ReadString(Message, a_PayloadLength)); + VERIFY(a_ByteBuffer.ReadString(Message, static_cast(a_PayloadLength))); m_Client->HandlePluginMessage(a_Channel, Message); } @@ -2377,7 +2393,7 @@ bool cProtocol172::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item) // Read the metadata AString Metadata; - if (!a_ByteBuffer.ReadString(Metadata, MetadataLength)) + if (!a_ByteBuffer.ReadString(Metadata, static_cast(MetadataLength))) { return false; } @@ -2546,7 +2562,7 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item) } WriteShort(ItemType); - WriteByte (a_Item.m_ItemCount); + WriteChar (a_Item.m_ItemCount); WriteShort(a_Item.m_ItemDamage); if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR)) @@ -2706,7 +2722,7 @@ void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEnt void cProtocol172::cPacketizer::WriteByteAngle(double a_Angle) { - WriteByte((char)(255 * a_Angle / 360)); + WriteChar(static_cast(255 * a_Angle / 360)); } @@ -2849,7 +2865,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) case mtCreeper: { WriteByte(0x10); - WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); + WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : 0); WriteByte(0x11); WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); break; @@ -2918,15 +2934,14 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) WriteByte(0x13); WriteByte(Wolf.IsBegging() ? 1 : 0); WriteByte(0x14); - WriteByte(Wolf.GetCollarColor()); + WriteByte(static_cast(Wolf.GetCollarColor())); break; } case mtSheep: { WriteByte(0x10); - Byte SheepMetadata = 0; - SheepMetadata = ((const cSheep &)a_Mob).GetFurColor(); + Byte SheepMetadata = static_cast(((const cSheep &)a_Mob).GetFurColor() & 0x0f); if (((const cSheep &)a_Mob).IsSheared()) { SheepMetadata |= 0x10; @@ -2963,7 +2978,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) case mtWither: { WriteByte(0x54); // Int at index 20 - WriteInt(((const cWither &)a_Mob).GetWitherInvulnerableTicks()); + WriteInt(static_cast(((const cWither &)a_Mob).GetWitherInvulnerableTicks())); WriteByte(0x66); // Float at index 6 WriteFloat((float)(a_Mob.GetHealth())); break; @@ -2972,14 +2987,14 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) case mtSlime: { WriteByte(0x10); - WriteByte(((const cSlime &)a_Mob).GetSize()); + WriteByte(static_cast(((const cSlime &)a_Mob).GetSize())); break; } case mtMagmaCube: { WriteByte(0x10); - WriteByte(((const cMagmaCube &)a_Mob).GetSize()); + WriteByte(static_cast(((const cMagmaCube &)a_Mob).GetSize())); break; } @@ -3018,7 +3033,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) WriteByte(0x50); // Int at index 16 WriteInt(Flags); WriteByte(0x13); // Byte at index 19 - WriteByte(Horse.GetHorseType()); + WriteByte(static_cast(Horse.GetHorseType())); WriteByte(0x54); // Int at index 20 int Appearance = 0; Appearance = Horse.GetHorseColor(); @@ -3028,6 +3043,12 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) WriteInt(Horse.GetHorseArmour()); break; } + + default: + { + // No data to send for this mob + break; + } } // switch (a_Mob.GetType()) // Custom name: @@ -3080,7 +3101,7 @@ void cProtocol176::SendPlayerSpawn(const cPlayer & a_Player) { // Called to spawn another player for the client cPacketizer Pkt(*this, 0x0c); // Spawn Player packet - Pkt.WriteVarInt(a_Player.GetUniqueID()); + Pkt.WriteVarInt(static_cast(a_Player.GetUniqueID())); Pkt.WriteString(cMojangAPI::MakeUUIDDashed(a_Player.GetClientHandle()->GetUUID())); if (a_Player.HasCustomName()) { -- cgit v1.2.3 From 2df8e4863f2047862700c0f3096c03859caff2d9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 20 Jan 2015 23:52:53 +0100 Subject: Fixed warnings in FastNBT.cpp. --- src/WorldStorage/FastNBT.cpp | 79 ++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp index aaef2fdfe..033a07601 100644 --- a/src/WorldStorage/FastNBT.cpp +++ b/src/WorldStorage/FastNBT.cpp @@ -109,7 +109,7 @@ bool cParsedNBT::ReadCompound(void) ASSERT(m_Tags.size() > 0); // Reads the latest tag as a compound - int ParentIdx = (int)m_Tags.size() - 1; + size_t ParentIdx = m_Tags.size() - 1; int PrevSibling = -1; for (;;) { @@ -120,10 +120,10 @@ bool cParsedNBT::ReadCompound(void) { break; } - m_Tags.push_back(cFastNBTTag(TagType, ParentIdx, PrevSibling)); + m_Tags.push_back(cFastNBTTag(TagType, static_cast(ParentIdx), PrevSibling)); if (PrevSibling >= 0) { - m_Tags[PrevSibling].m_NextSibling = (int)m_Tags.size() - 1; + m_Tags[static_cast(PrevSibling)].m_NextSibling = (int)m_Tags.size() - 1; } else { @@ -155,20 +155,21 @@ bool cParsedNBT::ReadList(eTagType a_ChildrenType) } // Read items: - int ParentIdx = (int)m_Tags.size() - 1; + ASSERT(m_Tags.size() > 0); + size_t ParentIdx = m_Tags.size() - 1; int PrevSibling = -1; for (int i = 0; i < Count; i++) { - m_Tags.push_back(cFastNBTTag(a_ChildrenType, ParentIdx, PrevSibling)); + m_Tags.push_back(cFastNBTTag(a_ChildrenType, static_cast(ParentIdx), PrevSibling)); if (PrevSibling >= 0) { - m_Tags[PrevSibling].m_NextSibling = (int)m_Tags.size() - 1; + m_Tags[static_cast(PrevSibling)].m_NextSibling = static_cast(m_Tags.size()) - 1; } else { - m_Tags[ParentIdx].m_FirstChild = (int)m_Tags.size() - 1; + m_Tags[ParentIdx].m_FirstChild = static_cast(m_Tags.size()) - 1; } - PrevSibling = (int)m_Tags.size() - 1; + PrevSibling = static_cast(m_Tags.size()) - 1; RETURN_FALSE_IF_FALSE(ReadTag()); } // for (i) m_Tags[ParentIdx].m_LastChild = PrevSibling; @@ -217,16 +218,16 @@ bool cParsedNBT::ReadTag(void) return false; } NEEDBYTES(len); - Tag.m_DataLength = len; + Tag.m_DataLength = static_cast(len); Tag.m_DataStart = m_Pos; - m_Pos += len; + m_Pos += static_cast(len); return true; } case TAG_List: { NEEDBYTES(1); - eTagType ItemType = (eTagType)m_Data[m_Pos]; + eTagType ItemType = static_cast(m_Data[m_Pos]); m_Pos++; RETURN_FALSE_IF_FALSE(ReadList(ItemType)); return true; @@ -250,9 +251,9 @@ bool cParsedNBT::ReadTag(void) } len *= 4; NEEDBYTES(len); - Tag.m_DataLength = len; + Tag.m_DataLength = static_cast(len); Tag.m_DataStart = m_Pos; - m_Pos += len; + m_Pos += static_cast(len); return true; } @@ -276,7 +277,7 @@ int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLen { return -1; } - if (m_Tags[a_Tag].m_Type != TAG_Compound) + if (m_Tags[static_cast(a_Tag)].m_Type != TAG_Compound) { return -1; } @@ -285,11 +286,11 @@ int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLen { a_NameLength = strlen(a_Name); } - for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling) + for (int Child = m_Tags[static_cast(a_Tag)].m_FirstChild; Child != -1; Child = m_Tags[static_cast(Child)].m_NextSibling) { if ( - (m_Tags[Child].m_NameLength == a_NameLength) && - (memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0) + (m_Tags[static_cast(Child)].m_NameLength == a_NameLength) && + (memcmp(m_Data + m_Tags[static_cast(Child)].m_NameStart, a_Name, a_NameLength) == 0) ) { return Child; @@ -413,7 +414,7 @@ void cFastNBTWriter::EndList(void) ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List); // Update the list count: - SetBEInt((char *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos), m_Stack[m_CurrentStack].m_Count); + SetBEInt(const_cast(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos), m_Stack[m_CurrentStack].m_Count); --m_CurrentStack; } @@ -425,7 +426,7 @@ void cFastNBTWriter::EndList(void) void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value) { TagCommon(a_Name, TAG_Byte); - m_Result.push_back(a_Value); + m_Result.push_back(static_cast(a_Value)); } @@ -435,8 +436,8 @@ void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value) void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value) { TagCommon(a_Name, TAG_Short); - Int16 Value = htons(a_Value); - m_Result.append((const char *)&Value, 2); + UInt16 Value = htons(a_Value); + m_Result.append(reinterpret_cast(&Value), 2); } @@ -446,8 +447,8 @@ void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value) void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value) { TagCommon(a_Name, TAG_Int); - Int32 Value = htonl(a_Value); - m_Result.append((const char *)&Value, 4); + UInt32 Value = htonl(a_Value); + m_Result.append(reinterpret_cast(&Value), 4); } @@ -457,8 +458,8 @@ void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value) void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value) { TagCommon(a_Name, TAG_Long); - Int64 Value = HostToNetwork8(&a_Value); - m_Result.append((const char *)&Value, 8); + UInt64 Value = HostToNetwork8(&a_Value); + m_Result.append(reinterpret_cast(&Value), 8); } @@ -468,8 +469,8 @@ void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value) void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value) { TagCommon(a_Name, TAG_Float); - Int32 Value = HostToNetwork4(&a_Value); - m_Result.append((const char *)&Value, 4); + UInt32 Value = HostToNetwork4(&a_Value); + m_Result.append(reinterpret_cast(&Value), 4); } @@ -479,8 +480,8 @@ void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value) void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value) { TagCommon(a_Name, TAG_Double); - Int64 Value = HostToNetwork8(&a_Value); - m_Result.append((const char *)&Value, 8); + UInt64 Value = HostToNetwork8(&a_Value); + m_Result.append(reinterpret_cast(&Value), 8); } @@ -490,8 +491,8 @@ void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value) void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value) { TagCommon(a_Name, TAG_String); - Int16 len = htons((short)(a_Value.size())); - m_Result.append((const char *)&len, 2); + UInt16 len = htons(static_cast(a_Value.size())); + m_Result.append(reinterpret_cast(&len), 2); m_Result.append(a_Value.c_str(), a_Value.size()); } @@ -502,8 +503,8 @@ void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value) void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements) { TagCommon(a_Name, TAG_ByteArray); - u_long len = htonl((u_long)a_NumElements); - m_Result.append((const char *)&len, 4); + u_long len = htonl(static_cast(a_NumElements)); + m_Result.append(reinterpret_cast(&len), 4); m_Result.append(a_Value, a_NumElements); } @@ -514,18 +515,18 @@ void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, size_t a_NumElements) { TagCommon(a_Name, TAG_IntArray); - u_long len = htonl((u_long)a_NumElements); + u_long len = htonl(static_cast(a_NumElements)); size_t cap = m_Result.capacity(); size_t size = m_Result.length(); if ((cap - size) < (4 + a_NumElements * 4)) { m_Result.reserve(size + 4 + (a_NumElements * 4)); } - m_Result.append((const char *)&len, 4); + m_Result.append(reinterpret_cast(&len), 4); for (size_t i = 0; i < a_NumElements; i++) { - int Element = htonl(a_Value[i]); - m_Result.append((const char *)&Element, 4); + UInt32 Element = htonl(a_Value[i]); + m_Result.append(reinterpret_cast(&Element), 4); } } @@ -545,8 +546,8 @@ void cFastNBTWriter::Finish(void) void cFastNBTWriter::WriteString(const char * a_Data, UInt16 a_Length) { - Int16 Len = htons(a_Length); - m_Result.append((const char *)&Len, 2); + UInt16 Len = htons(a_Length); + m_Result.append(reinterpret_cast(&Len), 2); m_Result.append(a_Data, a_Length); } -- cgit v1.2.3 From adf3b3a56944dfa3a0204707e2ccc94c3a0d043c Mon Sep 17 00:00:00 2001 From: Matyas Dolak Date: Wed, 21 Jan 2015 12:12:22 +0100 Subject: ByteBuffer: Added support for reading unsigned shorts and ints. --- src/ByteBuffer.cpp | 32 +++++++++++++++++++++++++++++++- src/ByteBuffer.h | 2 ++ 2 files changed, 33 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp index e1e5867a9..f3dc44d91 100644 --- a/src/ByteBuffer.cpp +++ b/src/ByteBuffer.cpp @@ -344,12 +344,28 @@ bool cByteBuffer::ReadByte(unsigned char & a_Value) bool cByteBuffer::ReadBEShort(short & a_Value) +{ + CHECK_THREAD + CheckValid(); + NEEDBYTES(2); + Int16 val; + ReadBuf(&val, 2); + val = ntohs(val); + a_Value = *(reinterpret_cast(&val)); + return true; +} + + + + + +bool cByteBuffer::ReadBEUInt16(UInt16 & a_Value) { CHECK_THREAD CheckValid(); NEEDBYTES(2); ReadBuf(&a_Value, 2); - a_Value = (short)ntohs((u_short)a_Value); + a_Value = ntohs(a_Value); return true; } @@ -371,6 +387,20 @@ bool cByteBuffer::ReadBEInt(int & a_Value) +bool cByteBuffer::ReadBEUInt32(UInt32 & a_Value) +{ + CHECK_THREAD + CheckValid(); + NEEDBYTES(4); + ReadBuf(&a_Value, 4); + a_Value = ntohl(a_Value); + return true; +} + + + + + bool cByteBuffer::ReadBEInt64(Int64 & a_Value) { CHECK_THREAD diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h index 2a316fa32..f480ad557 100644 --- a/src/ByteBuffer.h +++ b/src/ByteBuffer.h @@ -55,7 +55,9 @@ public: bool ReadChar (char & a_Value); bool ReadByte (unsigned char & a_Value); bool ReadBEShort (short & a_Value); + bool ReadBEUInt16 (UInt16 & a_Value); bool ReadBEInt (int & a_Value); + bool ReadBEUInt32 (UInt32 & a_Value); bool ReadBEInt64 (Int64 & a_Value); bool ReadBEFloat (float & a_Value); bool ReadBEDouble (double & a_Value); -- cgit v1.2.3 From 9429cdcb5309c8f3734eedac1c0c6cfbb421dc1f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 21 Jan 2015 20:39:34 +0100 Subject: Fixed warnings in StringUtils. --- src/StringUtils.cpp | 57 ++++++++++++++++++++++++++--------------------------- src/StringUtils.h | 51 ++++++++++++++++++++++++++++------------------- 2 files changed, 59 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index 5febf5d6c..a63525356 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -23,7 +23,7 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args) ASSERT(format != nullptr); char buffer[2048]; - size_t len; + int len; #ifdef va_copy va_list argsCopy; va_copy(argsCopy, args); @@ -34,14 +34,14 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args) // MS CRT provides secure printf that doesn't behave like in the C99 standard if ((len = _vsnprintf_s(buffer, ARRAYCOUNT(buffer), _TRUNCATE, format, argsCopy)) != -1) #else // _MSC_VER - if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, argsCopy)) < ARRAYCOUNT(buffer)) + if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, argsCopy)) < static_cast(ARRAYCOUNT(buffer))) #endif // else _MSC_VER { // The result did fit into the static buffer #ifdef va_copy va_end(argsCopy); #endif - str.append(buffer, len); + str.append(buffer, static_cast(len)); return str; } #ifdef va_copy @@ -51,7 +51,6 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args) // The result did not fit into the static buffer, use a dynamic buffer: #ifdef _MSC_VER // for MS CRT, we need to calculate the result length - // MS doesn't have va_copy() and does nod need it at all len = _vscprintf(format, args); if (len == -1) { @@ -63,11 +62,11 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args) #ifdef va_copy va_copy(argsCopy, args); #endif - std::vector Buffer(len + 1); + std::vector Buffer(static_cast(len) + 1); #ifdef _MSC_VER - vsprintf_s((char *)&(Buffer.front()), Buffer.size(), format, argsCopy); + vsprintf_s(&(Buffer.front()), Buffer.size(), format, argsCopy); #else // _MSC_VER - vsnprintf((char *)&(Buffer.front()), Buffer.size(), format, argsCopy); + vsnprintf(&(Buffer.front()), Buffer.size(), format, argsCopy); #endif // else _MSC_VER str.append(&(Buffer.front()), Buffer.size() - 1); #ifdef va_copy @@ -85,7 +84,7 @@ AString & Printf(AString & str, const char * format, ...) str.clear(); va_list args; va_start(args, format); - std::string &retval = AppendVPrintf(str, format, args); + std::string & retval = AppendVPrintf(str, format, args); va_end(args); return retval; } @@ -108,11 +107,11 @@ AString Printf(const char * format, ...) -AString & AppendPrintf(AString &str, const char * format, ...) +AString & AppendPrintf(AString & dst, const char * format, ...) { va_list args; va_start(args, format); - std::string &retval = AppendVPrintf(str, format, args); + std::string & retval = AppendVPrintf(dst, format, args); va_end(args); return retval; } @@ -297,7 +296,6 @@ void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & -// Converts a stream of BE shorts into UTF-8 string; returns a ref to a_UTF8 AString & RawBEToUTF8(const char * a_RawData, size_t a_NumShorts, AString & a_UTF8) { a_UTF8.clear(); @@ -314,22 +312,22 @@ AString & RawBEToUTF8(const char * a_RawData, size_t a_NumShorts, AString & a_UT a_UTF8.push_back((char)(192 + c / 64)); a_UTF8.push_back((char)(128 + c % 64)); } - else if (c - 0xd800u < 0x800) + else if (c - 0xd800 < 0x800) { // Error, silently drop } else if (c < 0x10000) { - a_UTF8.push_back((char)(224 + c / 4096)); - a_UTF8.push_back((char)(128 + c / 64 % 64)); - a_UTF8.push_back((char)(128 + c % 64)); + a_UTF8.push_back(static_cast(224 + c / 4096)); + a_UTF8.push_back(static_cast(128 + (c / 64) % 64)); + a_UTF8.push_back(static_cast(128 + c % 64)); } else if (c < 0x110000) { - a_UTF8.push_back((char)(240 + c / 262144)); - a_UTF8.push_back((char)(128 + c / 4096 % 64)); - a_UTF8.push_back((char)(128 + c / 64 % 64)); - a_UTF8.push_back((char)(128 + c % 64)); + a_UTF8.push_back(static_cast(240 + c / 262144)); + a_UTF8.push_back(static_cast(128 + (c / 4096) % 64)); + a_UTF8.push_back(static_cast(128 + (c / 64) % 64)); + a_UTF8.push_back(static_cast(128 + c % 64)); } else { @@ -382,7 +380,7 @@ Notice from the original file: -static const char trailingBytesForUTF8[256] = +static const Byte trailingBytesForUTF8[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -572,18 +570,18 @@ AString & CreateHexDump(AString & a_Out, const void * a_Data, size_t a_Size, siz int Count = sprintf(line, "%08x:", (unsigned)i); #endif // Remove the terminating nullptr / leftover garbage in line, after the sprintf-ed value - memset(line + Count, 32, sizeof(line) - Count); + memset(line + Count, 32, sizeof(line) - static_cast(Count)); p = line + 10; q = p + 2 + a_BytesPerLine * 3 + 1; for (size_t j = 0; j < k; j++) { - unsigned char c = ((unsigned char *)a_Data)[i + j]; + Byte c = (reinterpret_cast(a_Data))[i + j]; p[0] = HEX(c >> 4); p[1] = HEX(c & 0xf); p[2] = ' '; if (c >= ' ') { - q[0] = (char)c; + q[0] = static_cast(c); } else { @@ -708,7 +706,7 @@ AString URLDecode(const AString & a_String) res.push_back(ch); continue; } - res.push_back((hi << 4) | lo); + res.push_back(static_cast((hi << 4) | lo)); i += 2; } // for i - a_String[] return res; @@ -767,7 +765,8 @@ AString Base64Decode(const AString & a_Base64String) { AString res; size_t i, len = a_Base64String.size(); - int o, c; + size_t o; + int c; res.resize((len * 4) / 3 + 5, 0); // Approximate the upper bound on the result length for (o = 0, i = 0; i < len; i++) { @@ -850,7 +849,7 @@ AString Base64Encode(const AString & a_Input) short GetBEShort(const char * a_Mem) { const Byte * Bytes = (const Byte *)a_Mem; - return (Bytes[0] << 8) | Bytes[1]; + return static_cast((Bytes[0] << 8) | Bytes[1]); } @@ -870,9 +869,9 @@ int GetBEInt(const char * a_Mem) void SetBEInt(char * a_Mem, Int32 a_Value) { a_Mem[0] = a_Value >> 24; - a_Mem[1] = (a_Value >> 16) & 0xff; - a_Mem[2] = (a_Value >> 8) & 0xff; - a_Mem[3] = a_Value & 0xff; + a_Mem[1] = static_cast((a_Value >> 16) & 0xff); + a_Mem[2] = static_cast((a_Value >> 8) & 0xff); + a_Mem[3] = static_cast(a_Value & 0xff); } diff --git a/src/StringUtils.h b/src/StringUtils.h index 159e8ecac..bfe2a41fa 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -21,31 +21,40 @@ typedef std::list AStringList; -/** Add the formated string to the existing data in the string */ -extern AString & AppendVPrintf(AString & str, const char * format, va_list args) FORMATSTRING(2, 0); +/** Add the formated string to the existing data in the string. +Returns a_Dst. */ +extern AString & AppendVPrintf(AString & a_Dst, const char * format, va_list args) FORMATSTRING(2, 0); -/// Output the formatted text into the string -extern AString & Printf (AString & str, const char * format, ...) FORMATSTRING(2, 3); +/** Output the formatted text into the string. +Returns a_Dst. */ +extern AString & Printf (AString & a_Dst, const char * format, ...) FORMATSTRING(2, 3); -/// Output the formatted text into string, return string by value +/** Output the formatted text into string +Returns the formatted string by value. */ extern AString Printf(const char * format, ...) FORMATSTRING(1, 2); -/// Add the formatted string to the existing data in the string -extern AString & AppendPrintf (AString & str, const char * format, ...) FORMATSTRING(2, 3); +/** Add the formatted string to the existing data in the string. +Returns a_Dst */ +extern AString & AppendPrintf (AString & a_Dst, const char * format, ...) FORMATSTRING(2, 3); -/// Split the string at any of the listed delimiters, return as a stringvector +/** Split the string at any of the listed delimiters. +Return the splitted strings as a stringvector. */ extern AStringVector StringSplit(const AString & str, const AString & delim); -/// Split the string at any of the listed delimiters and trim each value, return as a stringvector +/** Split the string at any of the listed delimiters and trim each value. +Returns the splitted strings as a stringvector. */ extern AStringVector StringSplitAndTrim(const AString & str, const AString & delim); -/// Trime whitespace at both ends of the string +/** Trims whitespace at both ends of the string. +Returns a trimmed copy of the original string. */ extern AString TrimString(const AString & str); // tolua_export -/// In-place string conversion to uppercase; returns the same string +/** In-place string conversion to uppercase. +Returns the same string object. */ extern AString & InPlaceUppercase(AString & s); -/// In-place string conversion to lowercase; returns the same string +/** In-place string conversion to lowercase. +Returns the same string object. */ extern AString & InPlaceLowercase(AString & s); /** Returns an upper-cased copy of the string */ @@ -54,28 +63,30 @@ extern AString StrToUpper(const AString & s); /** Returns a lower-cased copy of the string */ extern AString StrToLower(const AString & s); -/// Case-insensitive string comparison; returns 0 if the strings are the same +/** Case-insensitive string comparison. +Returns 0 if the strings are the same, <0 if s1 < s2 and >0 if s1 > s2. */ extern int NoCaseCompare(const AString & s1, const AString & s2); // tolua_export -/// Case-insensitive string comparison that returns a rating of equal-ness between [0 - s1.length()] +/** Case-insensitive string comparison that returns a rating of equal-ness between [0 - s1.length()]. */ extern size_t RateCompareString(const AString & s1, const AString & s2); -/// Replaces *each* occurence of iNeedle in iHayStack with iReplaceWith +/** Replaces *each* occurence of iNeedle in iHayStack with iReplaceWith */ extern void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith); // tolua_export -/// Converts a stream of BE shorts into UTF-8 string; returns a ref to a_UTF8 +/** Converts a stream of BE shorts into UTF-8 string; returns a_UTF8. */ extern AString & RawBEToUTF8(const char * a_RawData, size_t a_NumShorts, AString & a_UTF8); -/// Converts a UTF-8 string into a UTF-16 BE string; returns a ref to a_UTF16 +/** Converts a UTF-8 string into a UTF-16 BE string. */ extern AString UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length); -/// Creates a nicely formatted HEX dump of the given memory block. Max a_BytesPerLine is 120 +/** Creates a nicely formatted HEX dump of the given memory block. +Max a_BytesPerLine is 120. */ extern AString & CreateHexDump(AString & a_Out, const void * a_Data, size_t a_Size, size_t a_BytesPerLine); -/// Returns a copy of a_Message with all quotes and backslashes escaped by a backslash +/** Returns a copy of a_Message with all quotes and backslashes escaped by a backslash. */ extern AString EscapeString(const AString & a_Message); // tolua_export -/// Removes all control codes used by MC for colors and styles +/** Removes all control codes used by MC for colors and styles. */ extern AString StripColorCodes(const AString & a_Message); // tolua_export /// URL-Decodes the given string, replacing all "%HH" into the correct characters. Invalid % sequences are left intact -- cgit v1.2.3 From a8a7488b54b06b5768ae66036adea937e519b242 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 5 Jan 2015 22:14:48 +0100 Subject: Added the libevent library. --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 997326cc7..50bdb0e8e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -323,4 +323,4 @@ endif () if (WIN32) target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib) endif() -target_link_libraries(${EXECUTABLE} luaexpat jsoncpp polarssl zlib sqlite lua SQLiteCpp) +target_link_libraries(${EXECUTABLE} luaexpat jsoncpp polarssl zlib sqlite lua SQLiteCpp event_core event_extra) -- cgit v1.2.3 From 6f5e267d5881db22843cedb9a752e92cce9f01ee Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 9 Jan 2015 11:07:43 +0100 Subject: Implemented LibEvent-based client connections. --- src/CMakeLists.txt | 1 + src/OSSupport/CMakeLists.txt | 2 + src/OSSupport/Network.cpp | 837 +++++++++++++++++++++++++++++++++++++++++++ src/OSSupport/Network.h | 188 ++++++++++ 4 files changed, 1028 insertions(+) create mode 100644 src/OSSupport/Network.cpp create mode 100644 src/OSSupport/Network.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 50bdb0e8e..c39f5f6e6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ project (MCServer) include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/") include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/jsoncpp/include") include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include") +include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/libevent/include") set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index e943ceb18..8454279fe 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -12,6 +12,7 @@ SET (SRCS GZipFile.cpp IsThread.cpp ListenThread.cpp + Network.cpp Semaphore.cpp Socket.cpp SocketThreads.cpp @@ -26,6 +27,7 @@ SET (HDRS GZipFile.h IsThread.h ListenThread.h + Network.h Queue.h Semaphore.h Socket.h diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp new file mode 100644 index 000000000..d947bfa90 --- /dev/null +++ b/src/OSSupport/Network.cpp @@ -0,0 +1,837 @@ + +// Network.cpp + +// Implements the classes used for the Network API + +#include "Globals.h" +#include "Network.h" +#include +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// Self-test: + +class cNetworkTest +{ +public: + /** cTCPLink callbacks that dump everything it received to the log. */ + class cDumpCallbacks: + public cTCPLink::cCallbacks + { + cEvent & m_Event; + + virtual void OnReceivedData(cTCPLink & a_Link, const char * a_Data, size_t a_Size) override + { + // Log the incoming data size: + AString Hex; + CreateHexDump(Hex, a_Data, a_Size, 16); + LOGD("Incoming data: %u bytes:\n%s", static_cast(a_Size), Hex.c_str()); + } + + virtual void OnRemoteClosed(cTCPLink & a_Link) override + { + LOGD("Remote has closed the connection."); + m_Event.Set(); + } + + virtual void OnError(cTCPLink & a_Link, int a_ErrorCode) override + { + LOGD("Error in the cDumpCallbacks."); + m_Event.Set(); + } + + public: + cDumpCallbacks(cEvent & a_Event): + m_Event(a_Event) + { + } + }; + + + /** cTCPLink callbacks that echo everything they receive back to the remote peer. */ + class cEchoCallbacks: + public cTCPLink::cCallbacks + { + cEvent & m_Event; + + virtual void OnReceivedData(cTCPLink & a_Link, const char * a_Data, size_t a_Size) override + { + // Echo the incoming data back to outgoing data: + LOGD("Data received (%u bytes), echoing back.", static_cast(a_Size)); + a_Link.Send(a_Data, a_Size); + LOGD("Echo queued"); + } + + virtual void OnRemoteClosed(cTCPLink & a_Link) override + { + LOGD("Remote has closed the connection."); + m_Event.Set(); + } + + virtual void OnError(cTCPLink & a_Link, int a_ErrorCode) override + { + LOGD("Error in the cEchoCallbacks."); + m_Event.Set(); + } + + public: + cEchoCallbacks(cEvent & a_Event): + m_Event(a_Event) + { + } + }; + + + /** Connect callbacks that send a simple test message when connected. */ + class cConnectCallbacks: + public cNetwork::cConnectCallbacks + { + cEvent & m_Event; + + virtual void OnSuccess(cTCPLink & a_Link) override + { + LOGD("Connected, sending test message"); + a_Link.Send("test message"); + LOGD("Message queued."); + } + + virtual void OnError(int a_ErrorCode) override + { + LOGD("Error while connecting: %d", a_ErrorCode); + m_Event.Set(); + } + + public: + cConnectCallbacks(cEvent & a_Event): + m_Event(a_Event) + { + } + }; + + + /** Connect callbacks that send a HTTP GET request for google.com when connected. */ + class cHTTPConnectCallbacks: + public cNetwork::cConnectCallbacks + { + cEvent & m_Event; + virtual void OnSuccess(cTCPLink & a_Link) override + { + LOGD("Connected, sending HTTP GET"); + if (!a_Link.Send("GET / HTTP/1.0\r\nHost:google.com\r\n\r\n")) + { + LOGWARNING("Sending HTTP GET failed"); + } + LOGD("HTTP GET queued."); + } + + virtual void OnError(int a_ErrorCode) override + { + LOGD("Error while connecting HTTP: %d", a_ErrorCode); + m_Event.Set(); + } + + public: + cHTTPConnectCallbacks(cEvent & a_Event): + m_Event(a_Event) + { + } + }; + + + /** Listen callbacks that send a simple welcome message to all connecting peers. */ + class cListenCallbacks: + public cNetwork::cListenCallbacks + { + virtual void OnAccepted(cTCPLink & a_Link) override + { + // Send some trivial data: + a_Link.Send("Welcome to echo server\n"); + } + }; + + cNetworkTest(void) + { + cEvent evtFinish; + + LOGD("Network test: Connecting to google.com:80, reading front page via HTTP."); + if (!cNetwork::Connect("google.com", 80, std::make_shared(evtFinish), std::make_shared(evtFinish))) + { + LOGWARNING("Cannot queue connection to google.com"); + abort(); + } + LOGD("Connect request has been queued."); + + /* + LOGD("Creating a server on port 33033"); + auto Server = cNetwork::Listen(33033, std::make_shared(), std::make_shared()); + LOGD("Test server created."); + + LOGD("Connecting to test server"); + cNetwork::Connect("localhost", 33033, std::make_shared(), std::make_shared()); + + LOGD("Waiting for network operations to finish."); + evtFinish.Wait(); + + LOGD("Terminating test server."); + Server->Close(); + */ + + evtFinish.Wait(); + LOGD("Network test finished"); + } +} g_NetworkTest; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// Class definitions: + +/** Implements the cTCPLink details so that it can represent the single connection between two endpoints. */ +class cTCPLinkImpl: + public cTCPLink +{ + typedef cTCPLink super; +public: + /** Creates a new link to be queued to connect to a specified host:port. + Used for outgoing connections created using cNetwork::Connect(). + Call Connect() first, before using the link. */ + cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks); + + /** Creates a new link based on the given socket. + Used for connections accepted in a server using cNetwork::Listen(). */ + cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks); + + /** Queues a connection request to the specified host. + a_ConnectCallbacks must be valid. + The object must have been constructed by the right constructor (without the Socket param). */ + bool Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks); + + // cTCPLink overrides: + virtual bool Send(const void * a_Data, size_t a_Length) override; + virtual AString GetLocalIP(void) const override; + virtual UInt16 GetLocalPort(void) const override; + virtual AString GetRemoteIP(void) const override; + virtual UInt16 GetRemotePort(void) const override; + virtual void Close(void) override; + virtual void Drop(void) override; + +protected: + + /** Callbacks to call when the connection is established. + May be NULL if not used. Only used for outgoing connections (cNetwork::Connect()). */ + cNetwork::cConnectCallbacksPtr m_ConnectCallbacks; + + /** The LibEvent handle representing this connection. */ + bufferevent * m_BufferEvent; + + + /** Callback that LibEvent calls when there's data available from the remote peer. */ + static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self); + + /** Callback that LibEvent calls when there's a non-data-related event on the socket. */ + static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self); +}; +typedef SharedPtr cTCPLinkImplPtr; +typedef std::vector cTCPLinkImplPtrs; + + + + + +/** Implements the cServerHandle details so that it can represent a real server socket, with a list of clients. */ +class cServerHandleImpl: + public cServerHandle +{ +public: + /** Creates a new instance with the specified callbacks. + Initializes the internals, but doesn't start listening yet. */ + cServerHandleImpl( + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks + ); + + /** Starts listening on the specified port. + Both IPv4 and IPv6 interfaces are used, if possible. */ + bool Listen(UInt16 a_Port); + + // cServerHandle overrides: + virtual void Close(void) override; + virtual bool IsListening(void) const override { return m_IsListening; } + +protected: + /** The callbacks used to notify about incoming connections. */ + cNetwork::cListenCallbacksPtr m_ListenCallbacks; + + /** The callbacks used to create new cTCPLink instances for incoming connections. */ + cTCPLink::cCallbacksPtr m_LinkCallbacks; + + /** Set to true when the server is initialized successfully and is listening for incoming connections. */ + bool m_IsListening; +}; +typedef SharedPtr cServerHandleImplPtr; +typedef std::vector cServerHandleImplPtrs; + + + + + +class cNetworkSingleton +{ + friend class cTCPLinkImpl; + +public: + /** Returns the singleton instance of this class */ + static cNetworkSingleton & Get(void); + + + // The following functions are implementations for the cNetwork class + + /** Queues a TCP connection to be made to the specified host. + Calls one the connection callbacks (success, error) when the connection is successfully established, or upon failure. + The a_LinkCallbacks is passed to the newly created cTCPLink. + Returns true if queueing was successful, false on failure to queue. + Note that the return value doesn't report the success of the actual connection; the connection is established asynchronously in the background. */ + bool Connect( + const AString & a_Host, + const UInt16 a_Port, + cNetwork::cConnectCallbacksPtr a_ConnectCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks + ); + + + /** Opens up the specified port for incoming connections. + Calls an OnAccepted callback for each incoming connection. + A cTCPLink with the specified link callbacks is created for each connection. + Returns a cServerHandle that can be used to query the operation status and close the server. */ + cServerHandlePtr Listen( + const UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks + ); + + + /** Queues a DNS query to resolve the specified hostname to IP address. + Calls one of the callbacks when the resolving succeeds, or when it fails. + Returns true if queueing was successful, false if not. + Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */ + bool HostnameToIP( + const AString & a_Hostname, + cNetwork::cResolveNameCallbacksPtr a_Callbacks + ); + + + /** Queues a DNS query to resolve the specified IP address to a hostname. + Calls one of the callbacks when the resolving succeeds, or when it fails. + Returns true if queueing was successful, false if not. + Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */ + bool IPToHostName( + const AString & a_IP, + cNetwork::cResolveNameCallbacksPtr a_Callbacks + ); + +protected: + + /** The main LibEvent container for driving the event loop. */ + event_base * m_EventBase; + + /** The LibEvent handle for doing DNS lookups. */ + evdns_base * m_DNSBase; + + /** Container for all client connections, including ones with pending-connect. */ + cTCPLinkImplPtrs m_Connections; + + /** Container for all servers that are currently active. */ + cServerHandleImplPtrs m_Servers; + + + /** Initializes the LibEvent internals. */ + cNetworkSingleton(void); + + /** Converts LibEvent-generated log events into log messages in MCS log. */ + static void LogCallback(int a_Severity, const char * a_Msg); + + /** Runs the thread that LibEvent uses to dispatch event. */ + static void EventLoopThread(cNetworkSingleton * a_Self); +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cTCPLinkImpl: + +cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): + super(a_LinkCallbacks), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, -1, BEV_OPT_CLOSE_ON_FREE)) +{ + bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); + bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); +} + + + + + +cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks): + super(a_LinkCallbacks), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, a_Socket, BEV_OPT_CLOSE_ON_FREE)) +{ + bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); + bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); +} + + + + + +/** Schedules the actual connection request. +Returns true on success, false on failure. */ +bool cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) +{ + ASSERT(bufferevent_getfd(m_BufferEvent) == -1); // Did you create this object using the right constructor (the one without the Socket param)? + ASSERT(a_ConnectCallbacks != nullptr); + + m_ConnectCallbacks = a_ConnectCallbacks; + + // If a_Host is an IP address, schedule a connection immediately: + sockaddr_storage sa; + int salen = static_cast(sizeof(sa)); + if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast(&sa), &salen) == 0) + { + // Insert the correct port: + if (sa.ss_family == AF_INET6) + { + reinterpret_cast(&sa)->sin6_port = htons(a_Port); + } + else + { + reinterpret_cast(&sa)->sin_port = htons(a_Port); + } + + if (bufferevent_socket_connect(m_BufferEvent, reinterpret_cast(&sa), salen) == 0) + { + // Success + return true; + } + // Failure + return false; + } + + // a_Host is a hostname, connect after a lookup: + if (bufferevent_socket_connect_hostname(m_BufferEvent, cNetworkSingleton::Get().m_DNSBase, AF_UNSPEC, a_Host.c_str(), a_Port) == 0) + { + // Success + return true; + } + // Failure + return false; +} + + + + + +bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) +{ + return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0); +} + + + + + +AString cTCPLinkImpl::GetLocalIP(void) const +{ + // TODO + ASSERT(!"cTCPLinkImpl::GetLocalIP: Not implemented yet"); + return ""; +} + + + + + +UInt16 cTCPLinkImpl::GetLocalPort(void) const +{ + // TODO + ASSERT(!"cTCPLinkImpl::GetLocalPort(): Not implemented yet"); + return 0; +} + + + + + +AString cTCPLinkImpl::GetRemoteIP(void) const +{ + // TODO + ASSERT(!"cTCPLinkImpl::GetRemoteIP(): Not implemented yet"); + return ""; +} + + + + + +UInt16 cTCPLinkImpl::GetRemotePort(void) const +{ + // TODO + ASSERT(!"cTCPLinkImpl::GetRemotePort(): Not implemented yet"); + return 0; +} + + + + + +void cTCPLinkImpl::Close(void) +{ + // TODO + ASSERT(!"cTCPLinkImpl::Close(): Not implemented yet"); +} + + + + + +void cTCPLinkImpl::Drop(void) +{ + // TODO + ASSERT(!"cTCPLinkImpl::Drop(): Not implemented yet"); +} + + + + + + +void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self) +{ + ASSERT(a_Self != nullptr); + cTCPLinkImpl * Self = static_cast(a_Self); + ASSERT(Self->m_Callbacks != nullptr); + + // Read all the incoming data, in 1024-byte chunks: + char data[1024]; + size_t length; + while ((length = bufferevent_read(a_BufferEvent, data, sizeof(data))) > 0) + { + Self->m_Callbacks->OnReceivedData(*Self, data, length); + } +} + + + + + +void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self) +{ + ASSERT(a_Self != nullptr); + cTCPLinkImpl * Self = static_cast(a_Self); + + // If an error is reported, call the error callback: + if (a_What & BEV_EVENT_ERROR) + { + // Choose the proper callback to call based on whether we were waiting for connection or not: + if (Self->m_ConnectCallbacks != nullptr) + { + Self->m_ConnectCallbacks->OnError(EVUTIL_SOCKET_ERROR()); + } + else + { + Self->m_Callbacks->OnError(*Self, EVUTIL_SOCKET_ERROR()); + } + return; + } + + // Pending connection succeeded, call the connection callback: + if (a_What & BEV_EVENT_CONNECTED) + { + if (Self->m_ConnectCallbacks != nullptr) + { + Self->m_ConnectCallbacks->OnSuccess(*Self); + // Reset the connect callbacks so that later errors get reported through the link callbacks: + Self->m_ConnectCallbacks.reset(); + return; + } + } + + // If the connectino has been closed, call the link callback: + if (a_What & BEV_EVENT_EOF) + { + Self->m_Callbacks->OnRemoteClosed(*Self); + return; + } + + // Unknown event, report it: + LOGWARNING("cTCPLinkImpl: Unhandled LibEvent event %d (0x%x)", a_What, a_What); + ASSERT(!"cTCPLinkImpl: Unhandled LibEvent event"); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cServerHandleImpl: + +cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks, cTCPLink::cCallbacksPtr a_LinkCallbacks): + m_ListenCallbacks(a_ListenCallbacks), + m_LinkCallbacks(a_LinkCallbacks) +{ + // TODO + ASSERT(!"Not implemented yet!"); +} + + + + + +void cServerHandleImpl::Close(void) +{ + // TODO + ASSERT(!"Not implemented yet!"); +} + + + + + +bool cServerHandleImpl::Listen(UInt16 a_Port) +{ + // TODO + ASSERT(!"Not implemented yet!"); + return false; +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork: + +bool cNetwork::Connect( + const AString & a_Host, + const UInt16 a_Port, + cNetwork::cConnectCallbacksPtr a_ConnectCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +) +{ + return cNetworkSingleton::Get().Connect(a_Host, a_Port, a_ConnectCallbacks, a_LinkCallbacks); +} + + + + + +cServerHandlePtr cNetwork::Listen( + const UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +) +{ + return cNetworkSingleton::Get().Listen(a_Port, a_ListenCallbacks, a_LinkCallbacks); +} + + + + + +bool cNetwork::HostnameToIP( + const AString & a_Hostname, + cNetwork::cResolveNameCallbacksPtr a_Callbacks +) +{ + return cNetworkSingleton::Get().HostnameToIP(a_Hostname, a_Callbacks); +} + + + + + +bool cNetwork::IPToHostName( + const AString & a_IP, + cNetwork::cResolveNameCallbacksPtr a_Callbacks +) +{ + return cNetworkSingleton::Get().IPToHostName(a_IP, a_Callbacks); +} + + + + +//////////////////////////////////////////////////////////////////////////////// +// cTCPLink: + +cTCPLink::cTCPLink(cCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks) +{ +} + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetworkSingleton: + +cNetworkSingleton::cNetworkSingleton(void) +{ + // Windows: initialize networking: + #ifdef _WIN32 + WSADATA wsaData; + memset(&wsaData, 0, sizeof(wsaData)); + WSAStartup (MAKEWORD(2, 2), &wsaData); + #endif // _WIN32 + + // Initialize LibEvent logging: + event_set_log_callback(LogCallback); + + // Initialize threading: + #if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED) + evthread_use_windows_threads(); + #elif defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) + evthread_use_pthreads(); + #else + #error No threading implemented for EVTHREAD + #endif + + // Create the main event_base: + m_EventBase = event_base_new(); + if (m_EventBase == nullptr) + { + LOGERROR("Failed to initialize LibEvent. The server will now terminate."); + abort(); + } + + // Create the DNS lookup helper: + m_DNSBase = evdns_base_new(m_EventBase, 1); + if (m_DNSBase == nullptr) + { + LOGERROR("Failed to initialize LibEvent's DNS subsystem. The server will now terminate."); + abort(); + } + + // Create the event loop thread: + std::thread::thread(EventLoopThread, this).detach(); +} + + + + + +cNetworkSingleton & cNetworkSingleton::Get(void) +{ + static cNetworkSingleton Instance; + return Instance; +} + + +bool cNetworkSingleton::Connect( + const AString & a_Host, + const UInt16 a_Port, + cNetwork::cConnectCallbacksPtr a_ConnectCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +) +{ + // Add a connection request to the queue: + cTCPLinkImplPtr ConnRequest = std::make_shared(a_LinkCallbacks); + m_Connections.push_back(ConnRequest); + + // Queue the connection: + if (!ConnRequest->Connect(a_Host, a_Port, a_ConnectCallbacks)) + { + // TODO: m_Connections.remove(ConnRequest); + return false; + } + + // Everything successful, return success: + return true; +} + + + + + +cServerHandlePtr cNetworkSingleton::Listen( + const UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +) +{ + cServerHandleImplPtr res = std::make_shared(a_ListenCallbacks, a_LinkCallbacks); + if (!res->Listen(a_Port)) + { + return res; + } + m_Servers.push_back(res); + return res; +} + + + + + +bool cNetworkSingleton::HostnameToIP( + const AString & a_Hostname, + cNetwork::cResolveNameCallbacksPtr a_Callbacks +) +{ + // TODO + ASSERT(!"Not implemented yet!"); + return false; +} + + + + +bool cNetworkSingleton::IPToHostName( + const AString & a_IP, + cNetwork::cResolveNameCallbacksPtr a_Callbacks +) +{ + // TODO + ASSERT(!"Not implemented yet!"); + return false; +} + + + + +void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) +{ + switch (a_Severity) + { + case _EVENT_LOG_DEBUG: LOGD ("LibEvent: %s", a_Msg); break; + case _EVENT_LOG_MSG: LOG ("LibEvent: %s", a_Msg); break; + case _EVENT_LOG_WARN: LOGWARNING("LibEvent: %s", a_Msg); break; + case _EVENT_LOG_ERR: LOGERROR ("LibEvent: %s", a_Msg); break; + default: + { + printf("LibEvent: %s", a_Msg); + break; + } + } +} + + + + + +void cNetworkSingleton::EventLoopThread(cNetworkSingleton * a_Self) +{ + event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY); +} + + + + diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h new file mode 100644 index 000000000..447cd457b --- /dev/null +++ b/src/OSSupport/Network.h @@ -0,0 +1,188 @@ + +// Network.h + +// Declares the classes used for the Network API + + + + + +#pragma once + + + + + +/** Interface that provides the methods available on a single TCP connection. */ +class cTCPLink +{ + friend class cNetwork; + +public: + class cCallbacks + { + public: + /** Called when there's data incoming from the remote peer. */ + virtual void OnReceivedData(cTCPLink & a_Link, const char * a_Data, size_t a_Length) = 0; + + /** Called when the remote end closes the connection. + The link is still available for connection information query (IP / port). + Sending data on the link is not an error, but the data won't be delivered. */ + virtual void OnRemoteClosed(cTCPLink & a_Link) = 0; + + /** Called when an error is detected on the connection. */ + virtual void OnError(cTCPLink & a_Link, int a_ErrorCode) = 0; + }; + typedef SharedPtr cCallbacksPtr; + + + /** Queues the specified data for sending to the remote peer. + Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */ + virtual bool Send(const void * a_Data, size_t a_Length) = 0; + + /** Queues the specified data for sending to the remote peer. + Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */ + bool Send(const AString & a_Data) + { + return Send(a_Data.data(), a_Data.size()); + } + + /** Returns the IP address of the local endpoint of the connection. */ + virtual AString GetLocalIP(void) const = 0; + + /** Returns the port used by the local endpoint of the connection. */ + virtual UInt16 GetLocalPort(void) const = 0; + + /** Returns the IP address of the remote endpoint of the connection. */ + virtual AString GetRemoteIP(void) const = 0; + + /** Returns the port used by the remote endpoint of the connection. */ + virtual UInt16 GetRemotePort(void) const = 0; + + /** Closes the link gracefully. + The link will keep trying to send the queued data, then it will send the FIN packet. */ + virtual void Close(void) = 0; + + /** Drops the connection without any more processing. + Sends the RST packet, queued outgoing and incoming data is lost. */ + virtual void Drop(void) = 0; + +protected: + /** Callbacks to be used for the various situations. */ + cCallbacksPtr m_Callbacks; + + + /** Creates a new link, with the specified callbacks. */ + cTCPLink(cCallbacksPtr a_Callbacks); +}; + + + + + +/** Interface that provides the methods available on a listening server socket. */ +class cServerHandle +{ + friend class cNetwork; +public: + + /** Stops the server, no more incoming connections will be accepted. */ + virtual void Close(void) = 0; + + /** Returns true if the server has been started correctly and is currently listening for incoming connections. */ + virtual bool IsListening(void) const = 0; +}; +typedef SharedPtr cServerHandlePtr; + + + + + +class cNetwork +{ +public: + /** Callbacks used for connecting to other servers as a client. */ + class cConnectCallbacks + { + public: + /** Called when the Connect call succeeds. + Provides the newly created link that can be used for communication. */ + virtual void OnSuccess(cTCPLink & a_Link) = 0; + + /** Called when the Connect call fails. */ + virtual void OnError(int a_ErrorCode) = 0; + }; + typedef SharedPtr cConnectCallbacksPtr; + + + /** Callbacks used when listening for incoming connections as a server. */ + class cListenCallbacks + { + public: + /** Called when the TCP server created with Listen() accepts an incoming connection. + Provides the newly created Link that can be used for communication. */ + virtual void OnAccepted(cTCPLink & a_Link) = 0; + }; + typedef SharedPtr cListenCallbacksPtr; + + + /** Callbacks used when resolving names to IPs. */ + class cResolveNameCallbacks + { + public: + /** Called when the hostname is successfully resolved into an IP address. */ + virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) = 0; + + /** Called when an error is encountered while resolving. */ + virtual void OnError(int a_ErrorCode) = 0; + }; + typedef SharedPtr cResolveNameCallbacksPtr; + + + /** Queues a TCP connection to be made to the specified host. + Calls one the connection callbacks (success, error) when the connection is successfully established, or upon failure. + The a_LinkCallbacks is passed to the newly created cTCPLink. + Returns true if queueing was successful, false on failure to queue. + Note that the return value doesn't report the success of the actual connection; the connection is established asynchronously in the background. */ + static bool Connect( + const AString & a_Host, + const UInt16 a_Port, + cConnectCallbacksPtr a_ConnectCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks + ); + + + /** Opens up the specified port for incoming connections. + Calls an OnAccepted callback for each incoming connection. + A cTCPLink with the specified link callbacks is created for each connection. + Returns a cServerHandle that can be used to query the operation status and close the server. */ + static cServerHandlePtr Listen( + const UInt16 a_Port, + cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks + ); + + + /** Queues a DNS query to resolve the specified hostname to IP address. + Calls one of the callbacks when the resolving succeeds, or when it fails. + Returns true if queueing was successful, false if not. + Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */ + static bool HostnameToIP( + const AString & a_Hostname, + cResolveNameCallbacksPtr a_Callbacks + ); + + + /** Queues a DNS query to resolve the specified IP address to a hostname. + Calls one of the callbacks when the resolving succeeds, or when it fails. + Returns true if queueing was successful, false if not. + Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */ + static bool IPToHostName( + const AString & a_IP, + cResolveNameCallbacksPtr a_Callbacks + ); +}; + + + + -- cgit v1.2.3 From b8b3409b74e93dd7d1e87f60f498c724e5374f26 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 9 Jan 2015 11:20:19 +0100 Subject: cNetwork: Linux compilation fix. --- src/OSSupport/Network.cpp | 9 +++++---- src/OSSupport/Network.h | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index d947bfa90..0e19e40de 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -358,8 +358,8 @@ protected: /** Converts LibEvent-generated log events into log messages in MCS log. */ static void LogCallback(int a_Severity, const char * a_Msg); - /** Runs the thread that LibEvent uses to dispatch event. */ - static void EventLoopThread(cNetworkSingleton * a_Self); + /** Implements the thread that runs LibEvent's event dispatcher loop. */ + static void RunEventLoop(cNetworkSingleton * a_Self); }; @@ -722,7 +722,8 @@ cNetworkSingleton::cNetworkSingleton(void) } // Create the event loop thread: - std::thread::thread(EventLoopThread, this).detach(); + std::thread EventLoopThread(RunEventLoop, this); + EventLoopThread.detach(); } @@ -827,7 +828,7 @@ void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) -void cNetworkSingleton::EventLoopThread(cNetworkSingleton * a_Self) +void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) { event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY); } diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 447cd457b..5cca511dc 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -22,6 +22,9 @@ public: class cCallbacks { public: + // Force a virtual destructor for all descendants: + virtual ~cCallbacks() {} + /** Called when there's data incoming from the remote peer. */ virtual void OnReceivedData(cTCPLink & a_Link, const char * a_Data, size_t a_Length) = 0; @@ -36,6 +39,9 @@ public: typedef SharedPtr cCallbacksPtr; + // Force a virtual destructor for all descendants: + virtual ~cTCPLink() {} + /** Queues the specified data for sending to the remote peer. Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */ virtual bool Send(const void * a_Data, size_t a_Length) = 0; @@ -86,6 +92,9 @@ class cServerHandle friend class cNetwork; public: + // Force a virtual destructor for all descendants: + virtual ~cServerHandle() {} + /** Stops the server, no more incoming connections will be accepted. */ virtual void Close(void) = 0; @@ -105,6 +114,9 @@ public: class cConnectCallbacks { public: + // Force a virtual destructor for all descendants: + virtual ~cConnectCallbacks() {} + /** Called when the Connect call succeeds. Provides the newly created link that can be used for communication. */ virtual void OnSuccess(cTCPLink & a_Link) = 0; @@ -119,6 +131,9 @@ public: class cListenCallbacks { public: + // Force a virtual destructor for all descendants: + virtual ~cListenCallbacks() {} + /** Called when the TCP server created with Listen() accepts an incoming connection. Provides the newly created Link that can be used for communication. */ virtual void OnAccepted(cTCPLink & a_Link) = 0; @@ -130,6 +145,9 @@ public: class cResolveNameCallbacks { public: + // Force a virtual destructor for all descendants: + virtual ~cResolveNameCallbacks() {} + /** Called when the hostname is successfully resolved into an IP address. */ virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) = 0; -- cgit v1.2.3 From a9b8a530b1e082d2ab108947ef28e0a58c8fd838 Mon Sep 17 00:00:00 2001 From: Tycho Date: Fri, 9 Jan 2015 14:07:59 +0000 Subject: Extracted Google connection test --- src/Globals.h | 10 ++++++ src/OSSupport/Network.cpp | 77 ++--------------------------------------------- 2 files changed, 13 insertions(+), 74 deletions(-) (limited to 'src') diff --git a/src/Globals.h b/src/Globals.h index f0726454b..51ddc4e79 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -298,6 +298,16 @@ void inline LOGD(const char* a_Format, ...) va_end(argList); } +void inline LOG(const char* a_Format, ...) FORMATSTRING(1, 2); + +void inline LOG(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + vprintf(a_Format, argList); + va_end(argList); +} + #endif diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 0e19e40de..0f532624d 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -5,12 +5,13 @@ #include "Globals.h" #include "Network.h" +#include "Event.h" #include #include #include #include - +#include @@ -20,38 +21,6 @@ class cNetworkTest { public: - /** cTCPLink callbacks that dump everything it received to the log. */ - class cDumpCallbacks: - public cTCPLink::cCallbacks - { - cEvent & m_Event; - - virtual void OnReceivedData(cTCPLink & a_Link, const char * a_Data, size_t a_Size) override - { - // Log the incoming data size: - AString Hex; - CreateHexDump(Hex, a_Data, a_Size, 16); - LOGD("Incoming data: %u bytes:\n%s", static_cast(a_Size), Hex.c_str()); - } - - virtual void OnRemoteClosed(cTCPLink & a_Link) override - { - LOGD("Remote has closed the connection."); - m_Event.Set(); - } - - virtual void OnError(cTCPLink & a_Link, int a_ErrorCode) override - { - LOGD("Error in the cDumpCallbacks."); - m_Event.Set(); - } - - public: - cDumpCallbacks(cEvent & a_Event): - m_Event(a_Event) - { - } - }; /** cTCPLink callbacks that echo everything they receive back to the remote peer. */ @@ -114,36 +83,6 @@ public: } }; - - /** Connect callbacks that send a HTTP GET request for google.com when connected. */ - class cHTTPConnectCallbacks: - public cNetwork::cConnectCallbacks - { - cEvent & m_Event; - virtual void OnSuccess(cTCPLink & a_Link) override - { - LOGD("Connected, sending HTTP GET"); - if (!a_Link.Send("GET / HTTP/1.0\r\nHost:google.com\r\n\r\n")) - { - LOGWARNING("Sending HTTP GET failed"); - } - LOGD("HTTP GET queued."); - } - - virtual void OnError(int a_ErrorCode) override - { - LOGD("Error while connecting HTTP: %d", a_ErrorCode); - m_Event.Set(); - } - - public: - cHTTPConnectCallbacks(cEvent & a_Event): - m_Event(a_Event) - { - } - }; - - /** Listen callbacks that send a simple welcome message to all connecting peers. */ class cListenCallbacks: public cNetwork::cListenCallbacks @@ -157,15 +96,6 @@ public: cNetworkTest(void) { - cEvent evtFinish; - - LOGD("Network test: Connecting to google.com:80, reading front page via HTTP."); - if (!cNetwork::Connect("google.com", 80, std::make_shared(evtFinish), std::make_shared(evtFinish))) - { - LOGWARNING("Cannot queue connection to google.com"); - abort(); - } - LOGD("Connect request has been queued."); /* LOGD("Creating a server on port 33033"); @@ -182,8 +112,7 @@ public: Server->Close(); */ - evtFinish.Wait(); - LOGD("Network test finished"); + } } g_NetworkTest; -- cgit v1.2.3 From bef957ef14fa6b215bd9d649c119a6cb16221248 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 11 Jan 2015 10:34:50 +0100 Subject: Added newline to logged messages in the tests. --- src/Globals.h | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/Globals.h b/src/Globals.h index 51ddc4e79..654ede95f 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -268,43 +268,47 @@ template class SizeChecker; #include "OSSupport/StackTrace.h" #else // Logging functions -void inline LOGERROR(const char* a_Format, ...) FORMATSTRING(1, 2); +void inline LOGERROR(const char * a_Format, ...) FORMATSTRING(1, 2); -void inline LOGERROR(const char* a_Format, ...) +void inline LOGERROR(const char * a_Format, ...) { va_list argList; va_start(argList, a_Format); vprintf(a_Format, argList); + putchar('\n'); va_end(argList); } -void inline LOGWARNING(const char* a_Format, ...) FORMATSTRING(1, 2); +void inline LOGWARNING(const char * a_Format, ...) FORMATSTRING(1, 2); -void inline LOGWARNING(const char* a_Format, ...) +void inline LOGWARNING(const char * a_Format, ...) { va_list argList; va_start(argList, a_Format); vprintf(a_Format, argList); + putchar('\n'); va_end(argList); } -void inline LOGD(const char* a_Format, ...) FORMATSTRING(1, 2); +void inline LOGD(const char * a_Format, ...) FORMATSTRING(1, 2); -void inline LOGD(const char* a_Format, ...) +void inline LOGD(const char * a_Format, ...) { va_list argList; va_start(argList, a_Format); vprintf(a_Format, argList); + putchar('\n'); va_end(argList); } -void inline LOG(const char* a_Format, ...) FORMATSTRING(1, 2); +void inline LOG(const char * a_Format, ...) FORMATSTRING(1, 2); -void inline LOG(const char* a_Format, ...) +void inline LOG(const char * a_Format, ...) { va_list argList; va_start(argList, a_Format); vprintf(a_Format, argList); + putchar('\n'); va_end(argList); } -- cgit v1.2.3 From fde44cba0815f626253c0d352cd0d782eec94328 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 11 Jan 2015 11:21:18 +0100 Subject: cNetwork: Implemented HostnameToIP lookups. --- src/OSSupport/Network.cpp | 136 ++++++++++++++++++++++++++++++++++++++++++++-- src/OSSupport/Network.h | 11 +++- 2 files changed, 141 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 0f532624d..8e2d060b4 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -123,6 +123,27 @@ public: //////////////////////////////////////////////////////////////////////////////// // Class definitions: +/** Holds information about an in-progress hostname lookup. */ +class cHostnameLookup +{ + /** The callbacks to call for resolved names / errors. */ + cNetwork::cResolveNameCallbacksPtr m_Callbacks; + + /** The hostname that was queried (needed for the callbacks). */ + AString m_Hostname; + + static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self); + +public: + cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks); +}; +typedef SharedPtr cHostnameLookupPtr; +typedef std::vector cHostnameLookupPtrs; + + + + + /** Implements the cTCPLink details so that it can represent the single connection between two endpoints. */ class cTCPLinkImpl: public cTCPLink @@ -214,7 +235,8 @@ typedef std::vector cServerHandleImplPtrs; class cNetworkSingleton { - friend class cTCPLinkImpl; + friend class cHostnameLookup; // Needs access to m_DNSBase + friend class cTCPLinkImpl; // Needs access to m_EventBase and m_DNSBase public: /** Returns the singleton instance of this class */ @@ -280,6 +302,9 @@ protected: /** Container for all servers that are currently active. */ cServerHandleImplPtrs m_Servers; + /** Container for all pending hostname lookups. */ + cHostnameLookupPtrs m_HostnameLookups; + /** Initializes the LibEvent internals. */ cNetworkSingleton(void); @@ -289,12 +314,94 @@ protected: /** Implements the thread that runs LibEvent's event dispatcher loop. */ static void RunEventLoop(cNetworkSingleton * a_Self); + + /** Removes the specified hostname lookup from m_HostnameLookups. */ + void RemoveHostnameLookup(cHostnameLookup * a_HostnameLookup); }; +//////////////////////////////////////////////////////////////////////////////// +// cHostnameLookup: + +cHostnameLookup::cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks), + m_Hostname(a_Hostname) +{ + evutil_addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = EVUTIL_AI_CANONNAME; + evdns_getaddrinfo(cNetworkSingleton::Get().m_DNSBase, a_Hostname.c_str(), nullptr, &hints, Callback, this); +} + + + + + +void cHostnameLookup::Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self) +{ + // Get the Self class: + cHostnameLookup * Self = reinterpret_cast(a_Self); + ASSERT(Self != nullptr); + + // If an error has occurred, notify the error callback: + if (a_ErrCode != 0) + { + Self->m_Callbacks->OnError(a_ErrCode); + cNetworkSingleton::Get().RemoveHostnameLookup(Self); + return; + } + + // Call the success handler for each entry received: + bool HasResolved = false; + for (;a_Addr != nullptr; a_Addr = a_Addr->ai_next) + { + char IP[128]; + switch (a_Addr->ai_family) + { + case AF_INET: // IPv4 + { + sockaddr_in * sin = reinterpret_cast(a_Addr->ai_addr); + evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); + break; + } + case AF_INET6: // IPv6 + { + sockaddr_in6 * sin = reinterpret_cast(a_Addr->ai_addr); + evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); + break; + } + default: + { + // Unknown address family, handle as error + continue; + } + } + Self->m_Callbacks->OnNameResolved(Self->m_Hostname, IP); + HasResolved = true; + } + + // If only unsupported families were reported, call the Error handler: + if (!HasResolved) + { + Self->m_Callbacks->OnError(1); + } + else + { + Self->m_Callbacks->OnFinished(); + } + cNetworkSingleton::Get().RemoveHostnameLookup(Self); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cTCPLinkImpl: @@ -716,9 +823,15 @@ bool cNetworkSingleton::HostnameToIP( cNetwork::cResolveNameCallbacksPtr a_Callbacks ) { - // TODO - ASSERT(!"Not implemented yet!"); - return false; + try + { + m_HostnameLookups.push_back(std::make_shared(a_Hostname, a_Callbacks)); + } + catch (...) + { + return false; + } + return true; } @@ -765,3 +878,18 @@ void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) +void cNetworkSingleton::RemoveHostnameLookup(cHostnameLookup * a_HostnameLookup) +{ + for (auto itr = m_HostnameLookups.begin(), end = m_HostnameLookups.end(); itr != end; ++itr) + { + if (itr->get() == a_HostnameLookup) + { + m_HostnameLookups.erase(itr); + return; + } + } // for itr - m_HostnameLookups[] +} + + + + diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 5cca511dc..3f60b03a7 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -148,11 +148,18 @@ public: // Force a virtual destructor for all descendants: virtual ~cResolveNameCallbacks() {} - /** Called when the hostname is successfully resolved into an IP address. */ + /** Called when the hostname is successfully resolved into an IP address. + May be called multiple times if an address resolves to multiple addresses. + a_IP may be either an IPv4 or an IPv6 address with their proper formatting. */ virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) = 0; - /** Called when an error is encountered while resolving. */ + /** Called when an error is encountered while resolving. + If an error is reported, the OnFinished() callback is not called. */ virtual void OnError(int a_ErrorCode) = 0; + + /** Called when all the addresses resolved have been reported via the OnNameResolved() callback. + Only called if there was no error reported. */ + virtual void OnFinished(void) = 0; }; typedef SharedPtr cResolveNameCallbacksPtr; -- cgit v1.2.3 From 251c96952bb80c57f24d243def2677e3ee94efe1 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 11 Jan 2015 12:59:07 +0100 Subject: cNetwork: Implemented IP-to-hostname lookup. --- src/OSSupport/Network.cpp | 122 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 8e2d060b4..392fb586e 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -123,7 +123,7 @@ public: //////////////////////////////////////////////////////////////////////////////// // Class definitions: -/** Holds information about an in-progress hostname lookup. */ +/** Holds information about an in-progress Hostname-to-IP lookup. */ class cHostnameLookup { /** The callbacks to call for resolved names / errors. */ @@ -144,6 +144,27 @@ typedef std::vector cHostnameLookupPtrs; +/** Holds information about an in-progress IP-to-Hostname lookup. */ +class cIPLookup +{ + /** The callbacks to call for resolved names / errors. */ + cNetwork::cResolveNameCallbacksPtr m_Callbacks; + + /** The IP that was queried (needed for the callbacks). */ + AString m_IP; + + static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self); + +public: + cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks); +}; +typedef SharedPtr cIPLookupPtr; +typedef std::vector cIPLookupPtrs; + + + + + /** Implements the cTCPLink details so that it can represent the single connection between two endpoints. */ class cTCPLinkImpl: public cTCPLink @@ -236,6 +257,7 @@ typedef std::vector cServerHandleImplPtrs; class cNetworkSingleton { friend class cHostnameLookup; // Needs access to m_DNSBase + friend class cIPLookup; // Needs access to m_DNSBase friend class cTCPLinkImpl; // Needs access to m_EventBase and m_DNSBase public: @@ -305,6 +327,9 @@ protected: /** Container for all pending hostname lookups. */ cHostnameLookupPtrs m_HostnameLookups; + /** Container for all pending IP lookups. */ + cIPLookupPtrs m_IPLookups; + /** Initializes the LibEvent internals. */ cNetworkSingleton(void); @@ -315,8 +340,13 @@ protected: /** Implements the thread that runs LibEvent's event dispatcher loop. */ static void RunEventLoop(cNetworkSingleton * a_Self); - /** Removes the specified hostname lookup from m_HostnameLookups. */ - void RemoveHostnameLookup(cHostnameLookup * a_HostnameLookup); + /** Removes the specified hostname lookup from m_HostnameLookups. + Used by the underlying lookup implementation when the lookup is finished. */ + void RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup); + + /** Removes the specified IP lookup from m_IPLookups. + Used by the underlying lookup implementation when the lookup is finished. */ + void RemoveIPLookup(const cIPLookup * a_IPLookup); }; @@ -343,7 +373,7 @@ cHostnameLookup::cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveN -void cHostnameLookup::Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self) +void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a_Self) { // Get the Self class: cHostnameLookup * Self = reinterpret_cast(a_Self); @@ -359,6 +389,7 @@ void cHostnameLookup::Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, v // Call the success handler for each entry received: bool HasResolved = false; + evutil_addrinfo * OrigAddr = a_Addr; for (;a_Addr != nullptr; a_Addr = a_Addr->ai_next) { char IP[128]; @@ -395,6 +426,7 @@ void cHostnameLookup::Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, v { Self->m_Callbacks->OnFinished(); } + evutil_freeaddrinfo(OrigAddr); cNetworkSingleton::Get().RemoveHostnameLookup(Self); } @@ -402,6 +434,57 @@ void cHostnameLookup::Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, v +//////////////////////////////////////////////////////////////////////////////// +// cIPLookup: + +cIPLookup::cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks), + m_IP(a_IP) +{ + sockaddr_storage sa; + int salen = static_cast(sizeof(sa)); + evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast(&sa), &salen); + switch (sa.ss_family) + { + case AF_INET: + { + sockaddr_in * sa4 = reinterpret_cast(&sa); + evdns_base_resolve_reverse(cNetworkSingleton::Get().m_DNSBase, &(sa4->sin_addr), 0, Callback, this); + } + case AF_INET6: + { + } + } +} + + + + + +void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self) +{ + // Get the Self class: + cIPLookup * Self = reinterpret_cast(a_Self); + ASSERT(Self != nullptr); + + if ((a_Result != 0) || (a_Addresses == nullptr)) + { + // An error has occurred, notify the error callback: + Self->m_Callbacks->OnError(a_Result); + } + else + { + // Call the success handler:: + Self->m_Callbacks->OnNameResolved(*(reinterpret_cast(a_Addresses)), Self->m_IP); + Self->m_Callbacks->OnFinished(); + } + cNetworkSingleton::Get().RemoveIPLookup(Self); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cTCPLinkImpl: @@ -842,9 +925,15 @@ bool cNetworkSingleton::IPToHostName( cNetwork::cResolveNameCallbacksPtr a_Callbacks ) { - // TODO - ASSERT(!"Not implemented yet!"); - return false; + try + { + m_IPLookups.push_back(std::make_shared(a_IP, a_Callbacks)); + } + catch (...) + { + return false; + } + return true; } @@ -878,7 +967,8 @@ void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) -void cNetworkSingleton::RemoveHostnameLookup(cHostnameLookup * a_HostnameLookup) + +void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup) { for (auto itr = m_HostnameLookups.begin(), end = m_HostnameLookups.end(); itr != end; ++itr) { @@ -893,3 +983,19 @@ void cNetworkSingleton::RemoveHostnameLookup(cHostnameLookup * a_HostnameLookup) + +void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) +{ + for (auto itr = m_IPLookups.begin(), end = m_IPLookups.end(); itr != end; ++itr) + { + if (itr->get() == a_IPLookup) + { + m_IPLookups.erase(itr); + return; + } + } // for itr - m_HostnameLookups[] +} + + + + -- cgit v1.2.3 From 28e97d54684a246ce2c46197fe229cb9cb152562 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 12 Jan 2015 09:38:00 +0100 Subject: cNetwork: Implemented basic server functionality. --- src/OSSupport/Network.cpp | 337 +++++++++++++++++++++++++++++----------------- 1 file changed, 215 insertions(+), 122 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 392fb586e..399bffe50 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -5,116 +5,13 @@ #include "Globals.h" #include "Network.h" -#include "Event.h" #include #include #include #include - +#include #include - - - -//////////////////////////////////////////////////////////////////////////////// -// Self-test: - -class cNetworkTest -{ -public: - - - /** cTCPLink callbacks that echo everything they receive back to the remote peer. */ - class cEchoCallbacks: - public cTCPLink::cCallbacks - { - cEvent & m_Event; - - virtual void OnReceivedData(cTCPLink & a_Link, const char * a_Data, size_t a_Size) override - { - // Echo the incoming data back to outgoing data: - LOGD("Data received (%u bytes), echoing back.", static_cast(a_Size)); - a_Link.Send(a_Data, a_Size); - LOGD("Echo queued"); - } - - virtual void OnRemoteClosed(cTCPLink & a_Link) override - { - LOGD("Remote has closed the connection."); - m_Event.Set(); - } - - virtual void OnError(cTCPLink & a_Link, int a_ErrorCode) override - { - LOGD("Error in the cEchoCallbacks."); - m_Event.Set(); - } - - public: - cEchoCallbacks(cEvent & a_Event): - m_Event(a_Event) - { - } - }; - - - /** Connect callbacks that send a simple test message when connected. */ - class cConnectCallbacks: - public cNetwork::cConnectCallbacks - { - cEvent & m_Event; - - virtual void OnSuccess(cTCPLink & a_Link) override - { - LOGD("Connected, sending test message"); - a_Link.Send("test message"); - LOGD("Message queued."); - } - - virtual void OnError(int a_ErrorCode) override - { - LOGD("Error while connecting: %d", a_ErrorCode); - m_Event.Set(); - } - - public: - cConnectCallbacks(cEvent & a_Event): - m_Event(a_Event) - { - } - }; - - /** Listen callbacks that send a simple welcome message to all connecting peers. */ - class cListenCallbacks: - public cNetwork::cListenCallbacks - { - virtual void OnAccepted(cTCPLink & a_Link) override - { - // Send some trivial data: - a_Link.Send("Welcome to echo server\n"); - } - }; - - cNetworkTest(void) - { - - /* - LOGD("Creating a server on port 33033"); - auto Server = cNetwork::Listen(33033, std::make_shared(), std::make_shared()); - LOGD("Test server created."); - - LOGD("Connecting to test server"); - cNetwork::Connect("localhost", 33033, std::make_shared(), std::make_shared()); - - LOGD("Waiting for network operations to finish."); - evtFinish.Wait(); - - LOGD("Terminating test server."); - Server->Close(); - */ - - - } -} g_NetworkTest; +#include "Event.h" @@ -170,6 +67,8 @@ class cTCPLinkImpl: public cTCPLink { typedef cTCPLink super; + friend class cServerHandleImpl; + public: /** Creates a new link to be queued to connect to a specified host:port. Used for outgoing connections created using cNetwork::Connect(). @@ -178,7 +77,7 @@ public: /** Creates a new link based on the given socket. Used for connections accepted in a server using cNetwork::Listen(). */ - cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks); + cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server); /** Queues a connection request to the specified host. a_ConnectCallbacks must be valid. @@ -203,6 +102,10 @@ protected: /** The LibEvent handle representing this connection. */ bufferevent * m_BufferEvent; + /** The server handle that has created this link. + Only valid for incoming connections, NULL for outgoing connections. */ + cServerHandleImpl * m_Server; + /** Callback that LibEvent calls when there's data available from the remote peer. */ static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self); @@ -221,6 +124,9 @@ typedef std::vector cTCPLinkImplPtrs; class cServerHandleImpl: public cServerHandle { + typedef cServerHandle super; + friend class cTCPLinkImpl; + public: /** Creates a new instance with the specified callbacks. Initializes the internals, but doesn't start listening yet. */ @@ -244,8 +150,25 @@ protected: /** The callbacks used to create new cTCPLink instances for incoming connections. */ cTCPLink::cCallbacksPtr m_LinkCallbacks; + /** The LibEvent handle representing the main listening socket. */ + evconnlistener * m_ConnListener; + + /** The LibEvent handle representing the secondary listening socket (only when side-by-side listening is needed, such as WinXP). */ + evconnlistener * m_SecondaryConnListener; + /** Set to true when the server is initialized successfully and is listening for incoming connections. */ bool m_IsListening; + + /** Container for all currently active connections on this server. */ + cTCPLinkImplPtrs m_Connections; + + + /** The callback called by LibEvent upon incoming connection. */ + static void Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self); + + /** Removes the specified link from m_Connections. + Called by cTCPLinkImpl when the link is terminated. */ + void RemoveLink(const cTCPLinkImpl * a_Link); }; typedef SharedPtr cServerHandleImplPtr; typedef std::vector cServerHandleImplPtrs; @@ -256,9 +179,10 @@ typedef std::vector cServerHandleImplPtrs; class cNetworkSingleton { - friend class cHostnameLookup; // Needs access to m_DNSBase - friend class cIPLookup; // Needs access to m_DNSBase - friend class cTCPLinkImpl; // Needs access to m_EventBase and m_DNSBase + friend class cHostnameLookup; // Needs access to m_DNSBase + friend class cIPLookup; // Needs access to m_DNSBase + friend class cTCPLinkImpl; // Needs access to m_EventBase and m_DNSBase + friend class cServerHandleImpl; // Needs access to m_EventBase public: /** Returns the singleton instance of this class */ @@ -347,6 +271,10 @@ protected: /** Removes the specified IP lookup from m_IPLookups. Used by the underlying lookup implementation when the lookup is finished. */ void RemoveIPLookup(const cIPLookup * a_IPLookup); + + /** Removes the specified link from m_Connections. + Used by the underlying link implementation when the link is closed / errored. */ + void RemoveLink(const cTCPLinkImpl * a_Link); }; @@ -490,7 +418,8 @@ void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): super(a_LinkCallbacks), - m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, -1, BEV_OPT_CLOSE_ON_FREE)) + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, -1, BEV_OPT_CLOSE_ON_FREE)), + m_Server(nullptr) { bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); @@ -500,9 +429,10 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): -cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks): +cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server): super(a_LinkCallbacks), - m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, a_Socket, BEV_OPT_CLOSE_ON_FREE)) + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, a_Socket, BEV_OPT_CLOSE_ON_FREE)), + m_Server(a_Server) { bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); @@ -517,6 +447,7 @@ Returns true on success, false on failure. */ bool cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) { ASSERT(bufferevent_getfd(m_BufferEvent) == -1); // Did you create this object using the right constructor (the one without the Socket param)? + ASSERT(m_Server == nullptr); ASSERT(a_ConnectCallbacks != nullptr); m_ConnectCallbacks = a_ConnectCallbacks; @@ -668,6 +599,14 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void else { Self->m_Callbacks->OnError(*Self, EVUTIL_SOCKET_ERROR()); + if (Self->m_Server == nullptr) + { + cNetworkSingleton::Get().RemoveLink(Self); + } + else + { + Self->m_Server->RemoveLink(Self); + } } return; } @@ -684,10 +623,18 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void } } - // If the connectino has been closed, call the link callback: + // If the connection has been closed, call the link callback and remove the connection: if (a_What & BEV_EVENT_EOF) { Self->m_Callbacks->OnRemoteClosed(*Self); + if (Self->m_Server != nullptr) + { + Self->m_Server->RemoveLink(Self); + } + else + { + cNetworkSingleton::Get().RemoveLink(Self); + } return; } @@ -705,10 +652,11 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks, cTCPLink::cCallbacksPtr a_LinkCallbacks): m_ListenCallbacks(a_ListenCallbacks), - m_LinkCallbacks(a_LinkCallbacks) + m_LinkCallbacks(a_LinkCallbacks), + m_ConnListener(nullptr), + m_SecondaryConnListener(nullptr), + m_IsListening(false) { - // TODO - ASSERT(!"Not implemented yet!"); } @@ -717,8 +665,23 @@ cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallb void cServerHandleImpl::Close(void) { - // TODO - ASSERT(!"Not implemented yet!"); + // Stop the listener sockets: + evconnlistener_free(m_ConnListener); + m_ConnListener = nullptr; + if (m_SecondaryConnListener != nullptr) + { + evconnlistener_free(m_SecondaryConnListener); + m_SecondaryConnListener = nullptr; + } + m_IsListening = false; + + // Close all connections: + cTCPLinkImplPtrs Conns; + std::swap(Conns, m_Connections); + for (auto conn: Conns) + { + conn->Close(); + } } @@ -727,9 +690,123 @@ void cServerHandleImpl::Close(void) bool cServerHandleImpl::Listen(UInt16 a_Port) { - // TODO - ASSERT(!"Not implemented yet!"); - return false; + ASSERT(!m_IsListening); + + // Set up the main socket: + // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available. + bool NeedsTwoSockets = false; + evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (MainSock == SOCKET_ERROR) + { + // Failed to create IPv6 socket, create an IPv4 one instead: + MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (MainSock == SOCKET_ERROR) + { + return false; + } + + // Bind to all interfaces: + sockaddr_in name; + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = ntohs(a_Port); + if (bind(MainSock, reinterpret_cast(&name), sizeof(name)) != 0) + { + int err = EVUTIL_SOCKET_ERROR(); + LOGWARNING("Cannot bind to IPv4 port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(MainSock); + return false; + } + } + else + { + // IPv6 socket created, switch it into "dualstack" mode: + UInt32 Zero = 0; + #ifdef _WIN32 + // WinXP doesn't support this feature, so if the setting fails, create another socket later on: + NeedsTwoSockets = ( + (setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)) == SOCKET_ERROR) && + (EVUTIL_SOCKET_ERROR() == WSAENOPROTOOPT) + ); + #else + setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); + #endif + + // Bind to all interfaces: + sockaddr_in6 name; + memset(&name, 0, sizeof(name)); + name.sin6_family = AF_INET6; + name.sin6_port = ntohs(a_Port); + if (bind(MainSock, reinterpret_cast(&name), sizeof(name)) != 0) + { + int err = EVUTIL_SOCKET_ERROR(); + LOGWARNING("Cannot bind to IPv6 port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(MainSock); + return false; + } + } + if (evutil_make_socket_nonblocking(MainSock) != 0) + { + int err = EVUTIL_SOCKET_ERROR(); + LOGWARNING("Cannot make socket for port %d non-blocking: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(MainSock); + return false; + } + if (listen(MainSock, 0) != 0) + { + int err = EVUTIL_SOCKET_ERROR(); + LOGWARNING("Cannot listen on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(MainSock); + return false; + } + m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().m_EventBase, Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock); + + // If a secondary socket is required (WinXP dual-stack), create it here: + if (NeedsTwoSockets) + { + evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (SecondSock != SOCKET_ERROR) + { + evutil_make_socket_nonblocking(SecondSock); + m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().m_EventBase, Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); + } + } + m_IsListening = true; + return true; +} + + + + + +void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self) +{ + // Cast to true self: + cServerHandleImpl * Self = reinterpret_cast(a_Self); + ASSERT(Self != nullptr); + + // Create a new cTCPLink for the incoming connection: + cTCPLinkImplPtr Link = std::make_shared(a_Socket, Self->m_LinkCallbacks, Self); + Self->m_Connections.push_back(Link); + + // Call the OnAccepted callback: + Self->m_ListenCallbacks->OnAccepted(*Link); +} + + + + + +void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link) +{ + for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + if (itr->get() == a_Link) + { + m_Connections.erase(itr); + return; + } + } // for itr - m_Connections[] } @@ -993,7 +1070,23 @@ void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) m_IPLookups.erase(itr); return; } - } // for itr - m_HostnameLookups[] + } // for itr - m_IPLookups[] +} + + + + + +void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link) +{ + for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + if (itr->get() == a_Link) + { + m_Connections.erase(itr); + return; + } + } // for itr - m_Connections[] } -- cgit v1.2.3 From a2aa37bdc505c72edd5c9d41bac6e7679cea13c3 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 12 Jan 2015 10:17:11 +0100 Subject: cNetwork: Implemented link address getting. --- src/OSSupport/Network.cpp | 174 +++++++++++++++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 399bffe50..2c9b2ef37 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -62,6 +62,13 @@ typedef std::vector cIPLookupPtrs; +// fwd: +class cServerHandleImpl; + + + + + /** Implements the cTCPLink details so that it can represent the single connection between two endpoints. */ class cTCPLinkImpl: public cTCPLink @@ -76,8 +83,9 @@ public: cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks); /** Creates a new link based on the given socket. - Used for connections accepted in a server using cNetwork::Listen(). */ - cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server); + Used for connections accepted in a server using cNetwork::Listen(). + a_Address and a_AddrLen describe the remote peer that has connected. */ + cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen); /** Queues a connection request to the specified host. a_ConnectCallbacks must be valid. @@ -86,10 +94,10 @@ public: // cTCPLink overrides: virtual bool Send(const void * a_Data, size_t a_Length) override; - virtual AString GetLocalIP(void) const override; - virtual UInt16 GetLocalPort(void) const override; - virtual AString GetRemoteIP(void) const override; - virtual UInt16 GetRemotePort(void) const override; + virtual AString GetLocalIP(void) const override { return m_LocalIP; } + virtual UInt16 GetLocalPort(void) const override { return m_LocalPort; } + virtual AString GetRemoteIP(void) const override { return m_RemoteIP; } + virtual UInt16 GetRemotePort(void) const override { return m_RemotePort; } virtual void Close(void) override; virtual void Drop(void) override; @@ -106,12 +114,33 @@ protected: Only valid for incoming connections, NULL for outgoing connections. */ cServerHandleImpl * m_Server; + /** The IP address of the local endpoint. Valid only after the socket has been connected. */ + AString m_LocalIP; + + /** The port of the local endpoint. Valid only after the socket has been connected. */ + UInt16 m_LocalPort; + + /** The IP address of the remote endpoint. Valid only after the socket has been connected. */ + AString m_RemoteIP; + + /** The port of the remote endpoint. Valid only after the socket has been connected. */ + UInt16 m_RemotePort; + /** Callback that LibEvent calls when there's data available from the remote peer. */ static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self); /** Callback that LibEvent calls when there's a non-data-related event on the socket. */ static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self); + + /** Sets a_IP and a_Port to values read from a_Address, based on the correct address family. */ + static void UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AString & a_IP, UInt16 & a_Port); + + /** Updates m_LocalIP and m_LocalPort based on the metadata read from the socket. */ + void UpdateLocalAddress(void); + + /** Updates m_RemoteIP and m_RemotePort based on the metadata read from the socket. */ + void UpdateRemoteAddress(void); }; typedef SharedPtr cTCPLinkImplPtr; typedef std::vector cTCPLinkImplPtrs; @@ -337,13 +366,13 @@ void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a } default: { - // Unknown address family, handle as error - continue; + // Unknown address family, handle as if this entry wasn't received + continue; // for (a_Addr) } } Self->m_Callbacks->OnNameResolved(Self->m_Hostname, IP); HasResolved = true; - } + } // for (a_Addr) // If only unsupported families were reported, call the Error handler: if (!HasResolved) @@ -378,11 +407,21 @@ cIPLookup::cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_ { sockaddr_in * sa4 = reinterpret_cast(&sa); evdns_base_resolve_reverse(cNetworkSingleton::Get().m_DNSBase, &(sa4->sin_addr), 0, Callback, this); + break; } case AF_INET6: { + sockaddr_in6 * sa6 = reinterpret_cast(&sa); + evdns_base_resolve_reverse_ipv6(cNetworkSingleton::Get().m_DNSBase, &(sa6->sin6_addr), 0, Callback, this); + break; } - } + default: + { + LOGWARNING("%s: Unknown address family: %d", __FUNCTION__, sa.ss_family); + ASSERT(!"Unknown address family"); + break; + } + } // switch (address family) } @@ -429,11 +468,13 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): -cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server): +cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen): super(a_LinkCallbacks), m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, a_Socket, BEV_OPT_CLOSE_ON_FREE)), m_Server(a_Server) { + UpdateLocalAddress(); + UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort); bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); } @@ -499,50 +540,6 @@ bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) -AString cTCPLinkImpl::GetLocalIP(void) const -{ - // TODO - ASSERT(!"cTCPLinkImpl::GetLocalIP: Not implemented yet"); - return ""; -} - - - - - -UInt16 cTCPLinkImpl::GetLocalPort(void) const -{ - // TODO - ASSERT(!"cTCPLinkImpl::GetLocalPort(): Not implemented yet"); - return 0; -} - - - - - -AString cTCPLinkImpl::GetRemoteIP(void) const -{ - // TODO - ASSERT(!"cTCPLinkImpl::GetRemoteIP(): Not implemented yet"); - return ""; -} - - - - - -UInt16 cTCPLinkImpl::GetRemotePort(void) const -{ - // TODO - ASSERT(!"cTCPLinkImpl::GetRemotePort(): Not implemented yet"); - return 0; -} - - - - - void cTCPLinkImpl::Close(void) { // TODO @@ -621,6 +618,8 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void Self->m_ConnectCallbacks.reset(); return; } + Self->UpdateLocalAddress(); + Self->UpdateRemoteAddress(); } // If the connection has been closed, call the link callback and remove the connection: @@ -647,6 +646,64 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void +void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AString & a_IP, UInt16 & a_Port) +{ + char IP[128]; + switch (a_Address->sa_family) + { + case AF_INET: // IPv4: + { + const sockaddr_in * sin = reinterpret_cast(a_Address); + evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); + a_Port = ntohs(sin->sin_port); + break; + } + case AF_INET6: // IPv6 + { + const sockaddr_in6 * sin = reinterpret_cast(a_Address); + evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); + a_Port = ntohs(sin->sin6_port); + break; + } + + default: + { + LOGWARNING("%s: Unknown socket address family: %d", __FUNCTION__, a_Address->sa_family); + ASSERT(!"Unknown socket address family"); + break; + } + } + a_IP.assign(IP); +} + + + + + +void cTCPLinkImpl::UpdateLocalAddress(void) +{ + sockaddr_storage sa; + int salen = static_cast(sizeof(sa)); + getsockname(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); + UpdateAddress(reinterpret_cast(&sa), salen, m_LocalIP, m_LocalPort); +} + + + + + +void cTCPLinkImpl::UpdateRemoteAddress(void) +{ + sockaddr_storage sa; + int salen = static_cast(sizeof(sa)); + getpeername(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); + UpdateAddress(reinterpret_cast(&sa), salen, m_LocalIP, m_LocalPort); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cServerHandleImpl: @@ -786,7 +843,7 @@ void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_ ASSERT(Self != nullptr); // Create a new cTCPLink for the incoming connection: - cTCPLinkImplPtr Link = std::make_shared(a_Socket, Self->m_LinkCallbacks, Self); + cTCPLinkImplPtr Link = std::make_shared(a_Socket, Self->m_LinkCallbacks, Self, a_Addr, a_Len); Self->m_Connections.push_back(Link); // Call the OnAccepted callback: @@ -933,6 +990,9 @@ cNetworkSingleton & cNetworkSingleton::Get(void) } + + + bool cNetworkSingleton::Connect( const AString & a_Host, const UInt16 a_Port, @@ -947,7 +1007,7 @@ bool cNetworkSingleton::Connect( // Queue the connection: if (!ConnRequest->Connect(a_Host, a_Port, a_ConnectCallbacks)) { - // TODO: m_Connections.remove(ConnRequest); + RemoveLink(ConnRequest.get()); return false; } -- cgit v1.2.3 From 9ffca127090e98f473a3a6b686804672fa0c40b0 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 12 Jan 2015 10:54:28 +0100 Subject: cNetwork: Fixed Linux compilation. --- src/OSSupport/Network.cpp | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 2c9b2ef37..a34f7ebca 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -310,6 +310,22 @@ protected: +//////////////////////////////////////////////////////////////////////////////// +// Globals: + +bool IsValidSocket(evutil_socket_t a_Socket) +{ + #ifdef _WIN32 + return (a_Socket != INVALID_SOCKET); + #else // _WIN32 + return (a_Socket >= 0); + #endif // else _WIN32 +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cHostnameLookup: @@ -683,7 +699,7 @@ void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AStr void cTCPLinkImpl::UpdateLocalAddress(void) { sockaddr_storage sa; - int salen = static_cast(sizeof(sa)); + socklen_t salen = static_cast(sizeof(sa)); getsockname(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); UpdateAddress(reinterpret_cast(&sa), salen, m_LocalIP, m_LocalPort); } @@ -695,7 +711,7 @@ void cTCPLinkImpl::UpdateLocalAddress(void) void cTCPLinkImpl::UpdateRemoteAddress(void) { sockaddr_storage sa; - int salen = static_cast(sizeof(sa)); + socklen_t salen = static_cast(sizeof(sa)); getpeername(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); UpdateAddress(reinterpret_cast(&sa), salen, m_LocalIP, m_LocalPort); } @@ -753,12 +769,14 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available. bool NeedsTwoSockets = false; evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - if (MainSock == SOCKET_ERROR) + if (!IsValidSocket(MainSock)) { // Failed to create IPv6 socket, create an IPv4 one instead: MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (MainSock == SOCKET_ERROR) + if (!IsValidSocket(MainSock)) { + int err = EVUTIL_SOCKET_ERROR(); + LOGWARNING("%s: Cannot create a socket for neither IPv6 nor IPv4: %d (%s)", __FUNCTION__, err, evutil_socket_error_to_string(err)); return false; } @@ -822,7 +840,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) if (NeedsTwoSockets) { evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (SecondSock != SOCKET_ERROR) + if (!IsValidSocket(SecondSock)) { evutil_make_socket_nonblocking(SecondSock); m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().m_EventBase, Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); -- cgit v1.2.3 From d8ac99a0374b528caca66ad8ecb5fb36f6334e77 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 12 Jan 2015 14:58:52 +0100 Subject: cNetwork: Implemented connection shutdown and close. --- src/OSSupport/Network.cpp | 66 +++++++++++++++++++++++++++++++++++++---------- src/OSSupport/Network.h | 10 ++++--- 2 files changed, 59 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index a34f7ebca..2ef6ace3c 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -87,6 +87,9 @@ public: a_Address and a_AddrLen describe the remote peer that has connected. */ cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen); + /** Destroys the LibEvent handle representing the link. */ + ~cTCPLinkImpl(); + /** Queues a connection request to the specified host. a_ConnectCallbacks must be valid. The object must have been constructed by the right constructor (without the Socket param). */ @@ -98,8 +101,8 @@ public: virtual UInt16 GetLocalPort(void) const override { return m_LocalPort; } virtual AString GetRemoteIP(void) const override { return m_RemoteIP; } virtual UInt16 GetRemotePort(void) const override { return m_RemotePort; } + virtual void Shutdown(void) override; virtual void Close(void) override; - virtual void Drop(void) override; protected: @@ -164,6 +167,9 @@ public: cTCPLink::cCallbacksPtr a_LinkCallbacks ); + /** Closes the server, dropping all the connections. */ + ~cServerHandleImpl(); + /** Starts listening on the specified port. Both IPv4 and IPv6 interfaces are used, if possible. */ bool Listen(UInt16 a_Port); @@ -499,6 +505,15 @@ cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_L +cTCPLinkImpl::~cTCPLinkImpl() +{ + bufferevent_free(m_BufferEvent); +} + + + + + /** Schedules the actual connection request. Returns true on success, false on failure. */ bool cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) @@ -556,20 +571,31 @@ bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) -void cTCPLinkImpl::Close(void) +void cTCPLinkImpl::Shutdown(void) { - // TODO - ASSERT(!"cTCPLinkImpl::Close(): Not implemented yet"); + #ifdef _WIN32 + shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND); + #else + shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR); + #endif + bufferevent_disable(m_BufferEvent, EV_WRITE); } -void cTCPLinkImpl::Drop(void) +void cTCPLinkImpl::Close(void) { - // TODO - ASSERT(!"cTCPLinkImpl::Drop(): Not implemented yet"); + bufferevent_disable(m_BufferEvent, EV_READ | EV_WRITE); + if (m_Server == nullptr) + { + cNetworkSingleton::Get().RemoveLink(this); + } + else + { + m_Server->RemoveLink(this); + } } @@ -736,24 +762,38 @@ cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallb +cServerHandleImpl::~cServerHandleImpl() +{ + if (m_ConnListener != nullptr) + { + evconnlistener_free(m_ConnListener); + } + if (m_SecondaryConnListener != nullptr) + { + evconnlistener_free(m_SecondaryConnListener); + } +} + + + + + void cServerHandleImpl::Close(void) { // Stop the listener sockets: - evconnlistener_free(m_ConnListener); - m_ConnListener = nullptr; + evconnlistener_disable(m_ConnListener); if (m_SecondaryConnListener != nullptr) { - evconnlistener_free(m_SecondaryConnListener); - m_SecondaryConnListener = nullptr; + evconnlistener_disable(m_SecondaryConnListener); } m_IsListening = false; - // Close all connections: + // Shutdown all connections: cTCPLinkImplPtrs Conns; std::swap(Conns, m_Connections); for (auto conn: Conns) { - conn->Close(); + conn->Shutdown(); } } diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 3f60b03a7..11c45b152 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -66,12 +66,13 @@ public: virtual UInt16 GetRemotePort(void) const = 0; /** Closes the link gracefully. - The link will keep trying to send the queued data, then it will send the FIN packet. */ - virtual void Close(void) = 0; + The link will send any queued outgoing data, then it will send the FIN packet. + The link will still receive incoming data from remote until the remote closes the connection. */ + virtual void Shutdown(void) = 0; /** Drops the connection without any more processing. Sends the RST packet, queued outgoing and incoming data is lost. */ - virtual void Drop(void) = 0; + virtual void Close(void) = 0; protected: /** Callbacks to be used for the various situations. */ @@ -95,7 +96,8 @@ public: // Force a virtual destructor for all descendants: virtual ~cServerHandle() {} - /** Stops the server, no more incoming connections will be accepted. */ + /** Stops the server, no more incoming connections will be accepted. + All current connections will be shut down (cTCPLink::Shutdown()). */ virtual void Close(void) = 0; /** Returns true if the server has been started correctly and is currently listening for incoming connections. */ -- cgit v1.2.3 From ddb1818dd54d82e19e7f629e6a729bccb68bfcf1 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 12 Jan 2015 15:45:30 +0100 Subject: cNetwork: Added multithreading protection. --- src/OSSupport/CriticalSection.cpp | 1 + src/OSSupport/Network.cpp | 30 +++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/OSSupport/CriticalSection.cpp b/src/OSSupport/CriticalSection.cpp index 13a3e4d9f..5248356c5 100644 --- a/src/OSSupport/CriticalSection.cpp +++ b/src/OSSupport/CriticalSection.cpp @@ -1,5 +1,6 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "CriticalSection.h" diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 2ef6ace3c..bee5bc09f 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -12,6 +12,7 @@ #include #include #include "Event.h" +#include "CriticalSection.h" @@ -197,6 +198,9 @@ protected: /** Container for all currently active connections on this server. */ cTCPLinkImplPtrs m_Connections; + /** Mutex protecting m_Connections againt multithreaded access. */ + cCriticalSection m_CS; + /** The callback called by LibEvent upon incoming connection. */ static void Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self); @@ -289,6 +293,9 @@ protected: /** Container for all pending IP lookups. */ cIPLookupPtrs m_IPLookups; + /** Mutex protecting all containers against multithreaded access. */ + cCriticalSection m_CS; + /** Initializes the LibEvent internals. */ cNetworkSingleton(void); @@ -587,6 +594,7 @@ void cTCPLinkImpl::Shutdown(void) void cTCPLinkImpl::Close(void) { + // Disable all events on the socket, but keep it alive (multithreading): bufferevent_disable(m_BufferEvent, EV_READ | EV_WRITE); if (m_Server == nullptr) { @@ -790,7 +798,10 @@ void cServerHandleImpl::Close(void) // Shutdown all connections: cTCPLinkImplPtrs Conns; - std::swap(Conns, m_Connections); + { + cCSLock Lock(m_CS); + std::swap(Conns, m_Connections); + } for (auto conn: Conns) { conn->Shutdown(); @@ -902,7 +913,10 @@ void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_ // Create a new cTCPLink for the incoming connection: cTCPLinkImplPtr Link = std::make_shared(a_Socket, Self->m_LinkCallbacks, Self, a_Addr, a_Len); - Self->m_Connections.push_back(Link); + { + cCSLock Lock(Self->m_CS); + Self->m_Connections.push_back(Link); + } // Lock(m_CS) // Call the OnAccepted callback: Self->m_ListenCallbacks->OnAccepted(*Link); @@ -914,6 +928,7 @@ void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_ void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link) { + cCSLock Lock(m_CS); for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) { if (itr->get() == a_Link) @@ -1060,7 +1075,10 @@ bool cNetworkSingleton::Connect( { // Add a connection request to the queue: cTCPLinkImplPtr ConnRequest = std::make_shared(a_LinkCallbacks); - m_Connections.push_back(ConnRequest); + { + cCSLock Lock(m_CS); + m_Connections.push_back(ConnRequest); + } // Lock(m_CS) // Queue the connection: if (!ConnRequest->Connect(a_Host, a_Port, a_ConnectCallbacks)) @@ -1088,6 +1106,7 @@ cServerHandlePtr cNetworkSingleton::Listen( { return res; } + cCSLock Lock(m_CS); m_Servers.push_back(res); return res; } @@ -1103,6 +1122,7 @@ bool cNetworkSingleton::HostnameToIP( { try { + cCSLock Lock(m_CS); m_HostnameLookups.push_back(std::make_shared(a_Hostname, a_Callbacks)); } catch (...) @@ -1122,6 +1142,7 @@ bool cNetworkSingleton::IPToHostName( { try { + cCSLock Lock(m_CS); m_IPLookups.push_back(std::make_shared(a_IP, a_Callbacks)); } catch (...) @@ -1165,6 +1186,7 @@ void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup) { + cCSLock Lock(m_CS); for (auto itr = m_HostnameLookups.begin(), end = m_HostnameLookups.end(); itr != end; ++itr) { if (itr->get() == a_HostnameLookup) @@ -1181,6 +1203,7 @@ void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameL void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) { + cCSLock Lock(m_CS); for (auto itr = m_IPLookups.begin(), end = m_IPLookups.end(); itr != end; ++itr) { if (itr->get() == a_IPLookup) @@ -1197,6 +1220,7 @@ void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link) { + cCSLock Lock(m_CS); for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) { if (itr->get() == a_Link) -- cgit v1.2.3 From 235b8f1f6b84dbea8ea18da50a8d87bf5ae8c319 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 12 Jan 2015 16:19:24 +0100 Subject: cNetwork: Added error logging to server socket creation. This is mainly for WinXP and RasPi testing. --- src/OSSupport/Network.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index bee5bc09f..b916fd804 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -823,6 +823,8 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) if (!IsValidSocket(MainSock)) { // Failed to create IPv6 socket, create an IPv4 one instead: + int err = EVUTIL_SOCKET_ERROR(); + LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err)); MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (!IsValidSocket(MainSock)) { @@ -850,9 +852,12 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) UInt32 Zero = 0; #ifdef _WIN32 // WinXP doesn't support this feature, so if the setting fails, create another socket later on: - NeedsTwoSockets = ( - (setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)) == SOCKET_ERROR) && - (EVUTIL_SOCKET_ERROR() == WSAENOPROTOOPT) + int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); + int err = EVUTIL_SOCKET_ERROR(); + NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT)); + LOGD("setsockopt(IPV6_V6ONLY) returned %d, err is %d (%s). %s", + res, err, evutil_socket_error_to_string(err), + NeedsTwoSockets ? "Second socket will be created" : "Second socket not needed" ); #else setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); @@ -890,11 +895,24 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) // If a secondary socket is required (WinXP dual-stack), create it here: if (NeedsTwoSockets) { + LOGD("Creating a second socket for IPv4"); evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (!IsValidSocket(SecondSock)) + if (IsValidSocket(SecondSock)) { - evutil_make_socket_nonblocking(SecondSock); - m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().m_EventBase, Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); + if (evutil_make_socket_nonblocking(SecondSock) == 0) + { + m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().m_EventBase, Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); + } + else + { + int err = EVUTIL_SOCKET_ERROR(); + LOGD("evutil_make_socket_nonblocking() failed: %d, %s", err, evutil_socket_error_to_string(err)); + } + } + else + { + int err = EVUTIL_SOCKET_ERROR(); + LOGD("socket(AF_INET, ...) failed: %d, %s", err, evutil_socket_error_to_string(err)); } } m_IsListening = true; -- cgit v1.2.3 From 19121d2962694468aa32cdfe839af7b0ec48acff Mon Sep 17 00:00:00 2001 From: worktycho Date: Mon, 12 Jan 2015 16:28:56 +0000 Subject: Wrap debug variable in ifdef avoidss unused variable error in clang --- src/OSSupport/Network.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index b916fd804..7a4e92610 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -823,7 +823,9 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) if (!IsValidSocket(MainSock)) { // Failed to create IPv6 socket, create an IPv4 one instead: + #ifdef DEBUG int err = EVUTIL_SOCKET_ERROR(); + #endif LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err)); MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (!IsValidSocket(MainSock)) -- cgit v1.2.3 From b59206207c924b0d7034ffcc239b657f5dfb4dd0 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 13 Jan 2015 12:55:10 +0100 Subject: cNetwork: Fixed compilation. --- src/OSSupport/Network.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 7a4e92610..036e48d53 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -819,13 +819,12 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) // Set up the main socket: // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available. bool NeedsTwoSockets = false; + int err; evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (!IsValidSocket(MainSock)) { // Failed to create IPv6 socket, create an IPv4 one instead: - #ifdef DEBUG - int err = EVUTIL_SOCKET_ERROR(); - #endif + err = EVUTIL_SOCKET_ERROR(); LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err)); MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (!IsValidSocket(MainSock)) @@ -842,7 +841,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) name.sin_port = ntohs(a_Port); if (bind(MainSock, reinterpret_cast(&name), sizeof(name)) != 0) { - int err = EVUTIL_SOCKET_ERROR(); + err = EVUTIL_SOCKET_ERROR(); LOGWARNING("Cannot bind to IPv4 port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); evutil_closesocket(MainSock); return false; @@ -855,7 +854,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) #ifdef _WIN32 // WinXP doesn't support this feature, so if the setting fails, create another socket later on: int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); - int err = EVUTIL_SOCKET_ERROR(); + err = EVUTIL_SOCKET_ERROR(); NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT)); LOGD("setsockopt(IPV6_V6ONLY) returned %d, err is %d (%s). %s", res, err, evutil_socket_error_to_string(err), @@ -880,14 +879,14 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) } if (evutil_make_socket_nonblocking(MainSock) != 0) { - int err = EVUTIL_SOCKET_ERROR(); + err = EVUTIL_SOCKET_ERROR(); LOGWARNING("Cannot make socket for port %d non-blocking: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); evutil_closesocket(MainSock); return false; } if (listen(MainSock, 0) != 0) { - int err = EVUTIL_SOCKET_ERROR(); + err = EVUTIL_SOCKET_ERROR(); LOGWARNING("Cannot listen on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); evutil_closesocket(MainSock); return false; @@ -907,13 +906,13 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) } else { - int err = EVUTIL_SOCKET_ERROR(); + err = EVUTIL_SOCKET_ERROR(); LOGD("evutil_make_socket_nonblocking() failed: %d, %s", err, evutil_socket_error_to_string(err)); } } else { - int err = EVUTIL_SOCKET_ERROR(); + err = EVUTIL_SOCKET_ERROR(); LOGD("socket(AF_INET, ...) failed: %d, %s", err, evutil_socket_error_to_string(err)); } } -- cgit v1.2.3 From 6f29cfe084a77a55b4bfc4288ac2c7bd1d9559e9 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 13 Jan 2015 17:48:25 +0100 Subject: cNetwork: Cosmetics. --- src/OSSupport/Network.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 036e48d53..e08109ad0 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -463,6 +463,7 @@ void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void cIPLookup * Self = reinterpret_cast(a_Self); ASSERT(Self != nullptr); + // Call the proper callback based on the event received: if ((a_Result != 0) || (a_Addresses == nullptr)) { // An error has occurred, notify the error callback: @@ -470,7 +471,7 @@ void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void } else { - // Call the success handler:: + // Call the success handler: Self->m_Callbacks->OnNameResolved(*(reinterpret_cast(a_Addresses)), Self->m_IP); Self->m_Callbacks->OnFinished(); } @@ -489,6 +490,7 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, -1, BEV_OPT_CLOSE_ON_FREE)), m_Server(nullptr) { + // Create the LibEvent handle, but don't assign a socket to it yet (will be assigned within Connect() method): bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); } @@ -502,8 +504,11 @@ cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_L m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, a_Socket, BEV_OPT_CLOSE_ON_FREE)), m_Server(a_Server) { + // Update the endpoint addresses: UpdateLocalAddress(); UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort); + + // Create the LibEvent handle: bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); } @@ -521,8 +526,6 @@ cTCPLinkImpl::~cTCPLinkImpl() -/** Schedules the actual connection request. -Returns true on success, false on failure. */ bool cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) { ASSERT(bufferevent_getfd(m_BufferEvent) == -1); // Did you create this object using the right constructor (the one without the Socket param)? @@ -594,7 +597,7 @@ void cTCPLinkImpl::Shutdown(void) void cTCPLinkImpl::Close(void) { - // Disable all events on the socket, but keep it alive (multithreading): + // Disable all events on the socket, but keep it alive: bufferevent_disable(m_BufferEvent, EV_READ | EV_WRITE); if (m_Server == nullptr) { @@ -698,6 +701,7 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AString & a_IP, UInt16 & a_Port) { + // Based on the family specified in the address, use the correct datastructure to convert to IP string: char IP[128]; switch (a_Address->sa_family) { @@ -747,7 +751,7 @@ void cTCPLinkImpl::UpdateRemoteAddress(void) sockaddr_storage sa; socklen_t salen = static_cast(sizeof(sa)); getpeername(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); - UpdateAddress(reinterpret_cast(&sa), salen, m_LocalIP, m_LocalPort); + UpdateAddress(reinterpret_cast(&sa), salen, m_RemoteIP, m_RemotePort); } @@ -1184,7 +1188,7 @@ void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) case _EVENT_LOG_ERR: LOGERROR ("LibEvent: %s", a_Msg); break; default: { - printf("LibEvent: %s", a_Msg); + LOGWARNING("LibEvent: Unknown log severity (%d): %s", a_Severity, a_Msg); break; } } -- cgit v1.2.3 From 728a624e48fd3f2cc16baa2bd317a52c92c92f59 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 14 Jan 2015 14:04:52 +0100 Subject: cNetwork: Refactored cTCPLinkImpl::Connect into a factory. --- src/OSSupport/Network.cpp | 89 +++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index e08109ad0..f29a3da17 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -18,6 +18,16 @@ +// fwd: +class cServerHandleImpl; +class cTCPLinkImpl; +typedef SharedPtr cTCPLinkImplPtr; +typedef std::vector cTCPLinkImplPtrs; + + + + + //////////////////////////////////////////////////////////////////////////////// // Class definitions: @@ -63,13 +73,6 @@ typedef std::vector cIPLookupPtrs; -// fwd: -class cServerHandleImpl; - - - - - /** Implements the cTCPLink details so that it can represent the single connection between two endpoints. */ class cTCPLinkImpl: public cTCPLink @@ -78,11 +81,6 @@ class cTCPLinkImpl: friend class cServerHandleImpl; public: - /** Creates a new link to be queued to connect to a specified host:port. - Used for outgoing connections created using cNetwork::Connect(). - Call Connect() first, before using the link. */ - cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks); - /** Creates a new link based on the given socket. Used for connections accepted in a server using cNetwork::Listen(). a_Address and a_AddrLen describe the remote peer that has connected. */ @@ -93,8 +91,8 @@ public: /** Queues a connection request to the specified host. a_ConnectCallbacks must be valid. - The object must have been constructed by the right constructor (without the Socket param). */ - bool Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks); + Returns a link that has the connection request queued, or NULL for failure. */ + static cTCPLinkImplPtr Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks); // cTCPLink overrides: virtual bool Send(const void * a_Data, size_t a_Length) override; @@ -131,6 +129,11 @@ protected: UInt16 m_RemotePort; + /** Creates a new link to be queued to connect to a specified host:port. + Used for outgoing connections created using cNetwork::Connect(). + To be used only by the Connect() factory function. */ + cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks); + /** Callback that LibEvent calls when there's data available from the remote peer. */ static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self); @@ -146,8 +149,6 @@ protected: /** Updates m_RemoteIP and m_RemotePort based on the metadata read from the socket. */ void UpdateRemoteAddress(void); }; -typedef SharedPtr cTCPLinkImplPtr; -typedef std::vector cTCPLinkImplPtrs; @@ -314,6 +315,10 @@ protected: Used by the underlying lookup implementation when the lookup is finished. */ void RemoveIPLookup(const cIPLookup * a_IPLookup); + /** Adds the specified link to m_Connections. + Used by the underlying link implementation when a new link is created. */ + void AddLink(cTCPLinkImplPtr a_Link); + /** Removes the specified link from m_Connections. Used by the underlying link implementation when the link is closed / errored. */ void RemoveLink(const cTCPLinkImpl * a_Link); @@ -526,13 +531,15 @@ cTCPLinkImpl::~cTCPLinkImpl() -bool cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) +cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) { - ASSERT(bufferevent_getfd(m_BufferEvent) == -1); // Did you create this object using the right constructor (the one without the Socket param)? - ASSERT(m_Server == nullptr); + ASSERT(a_LinkCallbacks != nullptr); ASSERT(a_ConnectCallbacks != nullptr); - m_ConnectCallbacks = a_ConnectCallbacks; + // Create a new link: + cTCPLinkImplPtr res{new cTCPLinkImpl(a_LinkCallbacks)}; // Cannot use std::make_shared here, constructor is not accessible + res->m_ConnectCallbacks = a_ConnectCallbacks; + cNetworkSingleton::Get().AddLink(res); // If a_Host is an IP address, schedule a connection immediately: sockaddr_storage sa; @@ -549,23 +556,26 @@ bool cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cCon reinterpret_cast(&sa)->sin_port = htons(a_Port); } - if (bufferevent_socket_connect(m_BufferEvent, reinterpret_cast(&sa), salen) == 0) + // Queue the connect request: + if (bufferevent_socket_connect(res->m_BufferEvent, reinterpret_cast(&sa), salen) == 0) { // Success - return true; + return res; } // Failure - return false; + cNetworkSingleton::Get().RemoveLink(res.get()); + return nullptr; } // a_Host is a hostname, connect after a lookup: - if (bufferevent_socket_connect_hostname(m_BufferEvent, cNetworkSingleton::Get().m_DNSBase, AF_UNSPEC, a_Host.c_str(), a_Port) == 0) + if (bufferevent_socket_connect_hostname(res->m_BufferEvent, cNetworkSingleton::Get().m_DNSBase, AF_UNSPEC, a_Host.c_str(), a_Port) == 0) { // Success - return true; + return res; } // Failure - return false; + cNetworkSingleton::Get().RemoveLink(res.get()); + return nullptr; } @@ -1097,21 +1107,8 @@ bool cNetworkSingleton::Connect( ) { // Add a connection request to the queue: - cTCPLinkImplPtr ConnRequest = std::make_shared(a_LinkCallbacks); - { - cCSLock Lock(m_CS); - m_Connections.push_back(ConnRequest); - } // Lock(m_CS) - - // Queue the connection: - if (!ConnRequest->Connect(a_Host, a_Port, a_ConnectCallbacks)) - { - RemoveLink(ConnRequest.get()); - return false; - } - - // Everything successful, return success: - return true; + cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks); + return (Conn != nullptr); } @@ -1241,6 +1238,16 @@ void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) +void cNetworkSingleton::AddLink(cTCPLinkImplPtr a_Link) +{ + cCSLock Lock(m_CS); + m_Connections.push_back(a_Link); +} + + + + + void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link) { cCSLock Lock(m_CS); -- cgit v1.2.3 From 4647e6ad716eccde67059ffde95d00ff06fc891f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 14 Jan 2015 14:06:14 +0100 Subject: cNetwork: Removed unneeded dependency. --- src/OSSupport/Network.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index f29a3da17..1eb635333 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -78,7 +78,6 @@ class cTCPLinkImpl: public cTCPLink { typedef cTCPLink super; - friend class cServerHandleImpl; public: /** Creates a new link based on the given socket. -- cgit v1.2.3 From 646e636933e8a4a048ac62a82d1bcf3cb9790f56 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 15 Jan 2015 16:52:06 +0100 Subject: cNetwork: Rewritten server listen into a factory method. --- src/OSSupport/Network.cpp | 190 +++++++++++++++++++++++++--------------------- 1 file changed, 102 insertions(+), 88 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 1eb635333..af63c75d0 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -23,6 +23,8 @@ class cServerHandleImpl; class cTCPLinkImpl; typedef SharedPtr cTCPLinkImplPtr; typedef std::vector cTCPLinkImplPtrs; +typedef SharedPtr cServerHandleImplPtr; +typedef std::vector cServerHandleImplPtrs; @@ -161,19 +163,17 @@ class cServerHandleImpl: friend class cTCPLinkImpl; public: - /** Creates a new instance with the specified callbacks. - Initializes the internals, but doesn't start listening yet. */ - cServerHandleImpl( - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks - ); - /** Closes the server, dropping all the connections. */ ~cServerHandleImpl(); - /** Starts listening on the specified port. - Both IPv4 and IPv6 interfaces are used, if possible. */ - bool Listen(UInt16 a_Port); + /** Creates a new server instance listening on the specified port. + Both IPv4 and IPv6 interfaces are used, if possible. + Always returns a server instance; in the event of a failure, the instance holds the error details. Use IsListening() to query success. */ + static cServerHandleImplPtr Listen( + UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks + ); // cServerHandle overrides: virtual void Close(void) override; @@ -201,6 +201,24 @@ protected: /** Mutex protecting m_Connections againt multithreaded access. */ cCriticalSection m_CS; + /** Contains the error code for the failure to listen. Only valid for non-listening instances. */ + int m_ErrorCode; + + /** Contains the error message for the failure to listen. Only valid for non-listening instances. */ + AString m_ErrorMsg; + + + + /** Creates a new instance with the specified callbacks. + Initializes the internals, but doesn't start listening yet. */ + cServerHandleImpl( + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks + ); + + /** Starts listening on the specified port. + Returns true if successful, false on failure. On failure, sets m_ErrorCode and m_ErrorMsg. */ + bool Listen(UInt16 a_Port); /** The callback called by LibEvent upon incoming connection. */ static void Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self); @@ -209,8 +227,6 @@ protected: Called by cTCPLinkImpl when the link is terminated. */ void RemoveLink(const cTCPLinkImpl * a_Link); }; -typedef SharedPtr cServerHandleImplPtr; -typedef std::vector cServerHandleImplPtrs; @@ -230,30 +246,6 @@ public: // The following functions are implementations for the cNetwork class - /** Queues a TCP connection to be made to the specified host. - Calls one the connection callbacks (success, error) when the connection is successfully established, or upon failure. - The a_LinkCallbacks is passed to the newly created cTCPLink. - Returns true if queueing was successful, false on failure to queue. - Note that the return value doesn't report the success of the actual connection; the connection is established asynchronously in the background. */ - bool Connect( - const AString & a_Host, - const UInt16 a_Port, - cNetwork::cConnectCallbacksPtr a_ConnectCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks - ); - - - /** Opens up the specified port for incoming connections. - Calls an OnAccepted callback for each incoming connection. - A cTCPLink with the specified link callbacks is created for each connection. - Returns a cServerHandle that can be used to query the operation status and close the server. */ - cServerHandlePtr Listen( - const UInt16 a_Port, - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks - ); - - /** Queues a DNS query to resolve the specified hostname to IP address. Calls one of the callbacks when the resolving succeeds, or when it fails. Returns true if queueing was successful, false if not. @@ -321,6 +313,15 @@ protected: /** Removes the specified link from m_Connections. Used by the underlying link implementation when the link is closed / errored. */ void RemoveLink(const cTCPLinkImpl * a_Link); + + /** Adds the specified link to m_Servers. + Used by the underlying server handle implementation when a new listening server is created. + Only servers that succeed in listening are added. */ + void AddServer(cServerHandleImplPtr a_Server); + + /** Removes the specified server from m_Servers. + Used by the underlying server handle implementation when the server is closed. */ + void RemoveServer(const cServerHandleImpl * a_Server); }; @@ -770,12 +771,16 @@ void cTCPLinkImpl::UpdateRemoteAddress(void) //////////////////////////////////////////////////////////////////////////////// // cServerHandleImpl: -cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks, cTCPLink::cCallbacksPtr a_LinkCallbacks): +cServerHandleImpl::cServerHandleImpl( + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +): m_ListenCallbacks(a_ListenCallbacks), m_LinkCallbacks(a_LinkCallbacks), m_ConnListener(nullptr), m_SecondaryConnListener(nullptr), - m_IsListening(false) + m_IsListening(false), + m_ErrorCode(0) { } @@ -825,10 +830,26 @@ void cServerHandleImpl::Close(void) -bool cServerHandleImpl::Listen(UInt16 a_Port) +cServerHandleImplPtr cServerHandleImpl::Listen( + UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +) { - ASSERT(!m_IsListening); + cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks, a_LinkCallbacks)}; + if (res->Listen(a_Port)) + { + cNetworkSingleton::Get().AddServer(res); + } + return res; +} + + + + +bool cServerHandleImpl::Listen(UInt16 a_Port) +{ // Set up the main socket: // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available. bool NeedsTwoSockets = false; @@ -842,8 +863,8 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (!IsValidSocket(MainSock)) { - int err = EVUTIL_SOCKET_ERROR(); - LOGWARNING("%s: Cannot create a socket for neither IPv6 nor IPv4: %d (%s)", __FUNCTION__, err, evutil_socket_error_to_string(err)); + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); return false; } @@ -854,8 +875,8 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) name.sin_port = ntohs(a_Port); if (bind(MainSock, reinterpret_cast(&name), sizeof(name)) != 0) { - err = EVUTIL_SOCKET_ERROR(); - LOGWARNING("Cannot bind to IPv4 port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); evutil_closesocket(MainSock); return false; } @@ -884,23 +905,23 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) name.sin6_port = ntohs(a_Port); if (bind(MainSock, reinterpret_cast(&name), sizeof(name)) != 0) { - int err = EVUTIL_SOCKET_ERROR(); - LOGWARNING("Cannot bind to IPv6 port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); evutil_closesocket(MainSock); return false; } } if (evutil_make_socket_nonblocking(MainSock) != 0) { - err = EVUTIL_SOCKET_ERROR(); - LOGWARNING("Cannot make socket for port %d non-blocking: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); evutil_closesocket(MainSock); return false; } if (listen(MainSock, 0) != 0) { - err = EVUTIL_SOCKET_ERROR(); - LOGWARNING("Cannot listen on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot listen on port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); evutil_closesocket(MainSock); return false; } @@ -985,7 +1006,9 @@ bool cNetwork::Connect( cTCPLink::cCallbacksPtr a_LinkCallbacks ) { - return cNetworkSingleton::Get().Connect(a_Host, a_Port, a_ConnectCallbacks, a_LinkCallbacks); + // Add a connection request to the queue: + cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks); + return (Conn != nullptr); } @@ -998,7 +1021,7 @@ cServerHandlePtr cNetwork::Listen( cTCPLink::cCallbacksPtr a_LinkCallbacks ) { - return cNetworkSingleton::Get().Listen(a_Port, a_ListenCallbacks, a_LinkCallbacks); + return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks, a_LinkCallbacks); } @@ -1098,42 +1121,6 @@ cNetworkSingleton & cNetworkSingleton::Get(void) -bool cNetworkSingleton::Connect( - const AString & a_Host, - const UInt16 a_Port, - cNetwork::cConnectCallbacksPtr a_ConnectCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks -) -{ - // Add a connection request to the queue: - cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks); - return (Conn != nullptr); -} - - - - - -cServerHandlePtr cNetworkSingleton::Listen( - const UInt16 a_Port, - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks -) -{ - cServerHandleImplPtr res = std::make_shared(a_ListenCallbacks, a_LinkCallbacks); - if (!res->Listen(a_Port)) - { - return res; - } - cCSLock Lock(m_CS); - m_Servers.push_back(res); - return res; -} - - - - - bool cNetworkSingleton::HostnameToIP( const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks @@ -1263,3 +1250,30 @@ void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link) + +void cNetworkSingleton::AddServer(cServerHandleImplPtr a_Server) +{ + cCSLock Lock(m_CS); + m_Servers.push_back(a_Server); +} + + + + + +void cNetworkSingleton::RemoveServer(const cServerHandleImpl * a_Server) +{ + cCSLock Lock(m_CS); + for (auto itr = m_Servers.begin(), end = m_Servers.end(); itr != end; ++itr) + { + if (itr->get() == a_Server) + { + m_Servers.erase(itr); + return; + } + } // for itr - m_Servers[] +} + + + + -- cgit v1.2.3 From 7cddb6237418f2d7ec984cd0d4cbdac7140330b0 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 15 Jan 2015 21:10:14 +0100 Subject: cNetwork: Added an OnError callback for listening servers. The callback receives the error details. --- src/OSSupport/Network.cpp | 5 +++++ src/OSSupport/Network.h | 3 +++ 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index af63c75d0..4dc8d1df1 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -841,6 +841,10 @@ cServerHandleImplPtr cServerHandleImpl::Listen( { cNetworkSingleton::Get().AddServer(res); } + else + { + a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg); + } return res; } @@ -1062,6 +1066,7 @@ cTCPLink::cTCPLink(cCallbacksPtr a_Callbacks): + //////////////////////////////////////////////////////////////////////////////// // cNetworkSingleton: diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 11c45b152..cb5badaeb 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -139,6 +139,9 @@ public: /** Called when the TCP server created with Listen() accepts an incoming connection. Provides the newly created Link that can be used for communication. */ virtual void OnAccepted(cTCPLink & a_Link) = 0; + + /** Called when the socket fails to listen on the specified port. */ + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; }; typedef SharedPtr cListenCallbacksPtr; -- cgit v1.2.3 From 60807adfb577552bc462660920f42fac0c3c60f3 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 15 Jan 2015 21:11:36 +0100 Subject: cNetwork: Handle WSA initialization failures. --- src/OSSupport/Network.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 4dc8d1df1..c26a8e17d 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -1076,7 +1076,13 @@ cNetworkSingleton::cNetworkSingleton(void) #ifdef _WIN32 WSADATA wsaData; memset(&wsaData, 0, sizeof(wsaData)); - WSAStartup (MAKEWORD(2, 2), &wsaData); + int res = WSAStartup (MAKEWORD(2, 2), &wsaData); + if (res != 0) + { + int err = WSAGetLastError(); + LOGWARNING("WSAStartup failed: %d, WSAGLE = %d (%s)", res, err, evutil_socket_error_to_string(err)); + exit(1); + } #endif // _WIN32 // Initialize LibEvent logging: -- cgit v1.2.3 From ae29a82ebae4742e81901f900010ea5cda2b9c4b Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 16 Jan 2015 16:13:02 +0100 Subject: cNetwork: Fixed WSAStartup()-not-called error in Listen(). --- src/OSSupport/Network.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index c26a8e17d..decd111ea 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -854,6 +854,9 @@ cServerHandleImplPtr cServerHandleImpl::Listen( bool cServerHandleImpl::Listen(UInt16 a_Port) { + // Make sure the cNetwork internals are innitialized: + cNetworkSingleton::Get(); + // Set up the main socket: // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available. bool NeedsTwoSockets = false; -- cgit v1.2.3 From d3076a3e1664c6a6e7c0d4cf24ac4335bb0a3899 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 17 Jan 2015 23:33:59 +0100 Subject: cNetwork: Split cNetworkSingleton to a separate file. --- src/OSSupport/CMakeLists.txt | 2 + src/OSSupport/Network.cpp | 334 +------------------------------------ src/OSSupport/NetworkSingleton.cpp | 281 +++++++++++++++++++++++++++++++ src/OSSupport/NetworkSingleton.h | 138 +++++++++++++++ 4 files changed, 430 insertions(+), 325 deletions(-) create mode 100644 src/OSSupport/NetworkSingleton.cpp create mode 100644 src/OSSupport/NetworkSingleton.h (limited to 'src') diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index 8454279fe..167afb784 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -13,6 +13,7 @@ SET (SRCS IsThread.cpp ListenThread.cpp Network.cpp + NetworkSingleton.cpp Semaphore.cpp Socket.cpp SocketThreads.cpp @@ -28,6 +29,7 @@ SET (HDRS IsThread.h ListenThread.h Network.h + NetworkSingleton.h Queue.h Semaphore.h Socket.h diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index decd111ea..359206632 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -13,6 +13,7 @@ #include #include "Event.h" #include "CriticalSection.h" +#include "NetworkSingleton.h" @@ -232,102 +233,6 @@ protected: -class cNetworkSingleton -{ - friend class cHostnameLookup; // Needs access to m_DNSBase - friend class cIPLookup; // Needs access to m_DNSBase - friend class cTCPLinkImpl; // Needs access to m_EventBase and m_DNSBase - friend class cServerHandleImpl; // Needs access to m_EventBase - -public: - /** Returns the singleton instance of this class */ - static cNetworkSingleton & Get(void); - - - // The following functions are implementations for the cNetwork class - - /** Queues a DNS query to resolve the specified hostname to IP address. - Calls one of the callbacks when the resolving succeeds, or when it fails. - Returns true if queueing was successful, false if not. - Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */ - bool HostnameToIP( - const AString & a_Hostname, - cNetwork::cResolveNameCallbacksPtr a_Callbacks - ); - - - /** Queues a DNS query to resolve the specified IP address to a hostname. - Calls one of the callbacks when the resolving succeeds, or when it fails. - Returns true if queueing was successful, false if not. - Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */ - bool IPToHostName( - const AString & a_IP, - cNetwork::cResolveNameCallbacksPtr a_Callbacks - ); - -protected: - - /** The main LibEvent container for driving the event loop. */ - event_base * m_EventBase; - - /** The LibEvent handle for doing DNS lookups. */ - evdns_base * m_DNSBase; - - /** Container for all client connections, including ones with pending-connect. */ - cTCPLinkImplPtrs m_Connections; - - /** Container for all servers that are currently active. */ - cServerHandleImplPtrs m_Servers; - - /** Container for all pending hostname lookups. */ - cHostnameLookupPtrs m_HostnameLookups; - - /** Container for all pending IP lookups. */ - cIPLookupPtrs m_IPLookups; - - /** Mutex protecting all containers against multithreaded access. */ - cCriticalSection m_CS; - - - /** Initializes the LibEvent internals. */ - cNetworkSingleton(void); - - /** Converts LibEvent-generated log events into log messages in MCS log. */ - static void LogCallback(int a_Severity, const char * a_Msg); - - /** Implements the thread that runs LibEvent's event dispatcher loop. */ - static void RunEventLoop(cNetworkSingleton * a_Self); - - /** Removes the specified hostname lookup from m_HostnameLookups. - Used by the underlying lookup implementation when the lookup is finished. */ - void RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup); - - /** Removes the specified IP lookup from m_IPLookups. - Used by the underlying lookup implementation when the lookup is finished. */ - void RemoveIPLookup(const cIPLookup * a_IPLookup); - - /** Adds the specified link to m_Connections. - Used by the underlying link implementation when a new link is created. */ - void AddLink(cTCPLinkImplPtr a_Link); - - /** Removes the specified link from m_Connections. - Used by the underlying link implementation when the link is closed / errored. */ - void RemoveLink(const cTCPLinkImpl * a_Link); - - /** Adds the specified link to m_Servers. - Used by the underlying server handle implementation when a new listening server is created. - Only servers that succeed in listening are added. */ - void AddServer(cServerHandleImplPtr a_Server); - - /** Removes the specified server from m_Servers. - Used by the underlying server handle implementation when the server is closed. */ - void RemoveServer(const cServerHandleImpl * a_Server); -}; - - - - - //////////////////////////////////////////////////////////////////////////////// // Globals: @@ -357,7 +262,7 @@ cHostnameLookup::cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveN hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; hints.ai_flags = EVUTIL_AI_CANONNAME; - evdns_getaddrinfo(cNetworkSingleton::Get().m_DNSBase, a_Hostname.c_str(), nullptr, &hints, Callback, this); + evdns_getaddrinfo(cNetworkSingleton::Get().GetDNSBase(), a_Hostname.c_str(), nullptr, &hints, Callback, this); } @@ -440,13 +345,13 @@ cIPLookup::cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_ case AF_INET: { sockaddr_in * sa4 = reinterpret_cast(&sa); - evdns_base_resolve_reverse(cNetworkSingleton::Get().m_DNSBase, &(sa4->sin_addr), 0, Callback, this); + evdns_base_resolve_reverse(cNetworkSingleton::Get().GetDNSBase(), &(sa4->sin_addr), 0, Callback, this); break; } case AF_INET6: { sockaddr_in6 * sa6 = reinterpret_cast(&sa); - evdns_base_resolve_reverse_ipv6(cNetworkSingleton::Get().m_DNSBase, &(sa6->sin6_addr), 0, Callback, this); + evdns_base_resolve_reverse_ipv6(cNetworkSingleton::Get().GetDNSBase(), &(sa6->sin6_addr), 0, Callback, this); break; } default: @@ -492,7 +397,7 @@ void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): super(a_LinkCallbacks), - m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, -1, BEV_OPT_CLOSE_ON_FREE)), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE)), m_Server(nullptr) { // Create the LibEvent handle, but don't assign a socket to it yet (will be assigned within Connect() method): @@ -506,7 +411,7 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen): super(a_LinkCallbacks), - m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, a_Socket, BEV_OPT_CLOSE_ON_FREE)), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE)), m_Server(a_Server) { // Update the endpoint addresses: @@ -568,7 +473,7 @@ cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTC } // a_Host is a hostname, connect after a lookup: - if (bufferevent_socket_connect_hostname(res->m_BufferEvent, cNetworkSingleton::Get().m_DNSBase, AF_UNSPEC, a_Host.c_str(), a_Port) == 0) + if (bufferevent_socket_connect_hostname(res->m_BufferEvent, cNetworkSingleton::Get().GetDNSBase(), AF_UNSPEC, a_Host.c_str(), a_Port) == 0) { // Success return res; @@ -932,7 +837,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) evutil_closesocket(MainSock); return false; } - m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().m_EventBase, Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock); + m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock); // If a secondary socket is required (WinXP dual-stack), create it here: if (NeedsTwoSockets) @@ -943,7 +848,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) { if (evutil_make_socket_nonblocking(SecondSock) == 0) { - m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().m_EventBase, Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); + m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); } else { @@ -1070,224 +975,3 @@ cTCPLink::cTCPLink(cCallbacksPtr a_Callbacks): -//////////////////////////////////////////////////////////////////////////////// -// cNetworkSingleton: - -cNetworkSingleton::cNetworkSingleton(void) -{ - // Windows: initialize networking: - #ifdef _WIN32 - WSADATA wsaData; - memset(&wsaData, 0, sizeof(wsaData)); - int res = WSAStartup (MAKEWORD(2, 2), &wsaData); - if (res != 0) - { - int err = WSAGetLastError(); - LOGWARNING("WSAStartup failed: %d, WSAGLE = %d (%s)", res, err, evutil_socket_error_to_string(err)); - exit(1); - } - #endif // _WIN32 - - // Initialize LibEvent logging: - event_set_log_callback(LogCallback); - - // Initialize threading: - #if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED) - evthread_use_windows_threads(); - #elif defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) - evthread_use_pthreads(); - #else - #error No threading implemented for EVTHREAD - #endif - - // Create the main event_base: - m_EventBase = event_base_new(); - if (m_EventBase == nullptr) - { - LOGERROR("Failed to initialize LibEvent. The server will now terminate."); - abort(); - } - - // Create the DNS lookup helper: - m_DNSBase = evdns_base_new(m_EventBase, 1); - if (m_DNSBase == nullptr) - { - LOGERROR("Failed to initialize LibEvent's DNS subsystem. The server will now terminate."); - abort(); - } - - // Create the event loop thread: - std::thread EventLoopThread(RunEventLoop, this); - EventLoopThread.detach(); -} - - - - - -cNetworkSingleton & cNetworkSingleton::Get(void) -{ - static cNetworkSingleton Instance; - return Instance; -} - - - - - -bool cNetworkSingleton::HostnameToIP( - const AString & a_Hostname, - cNetwork::cResolveNameCallbacksPtr a_Callbacks -) -{ - try - { - cCSLock Lock(m_CS); - m_HostnameLookups.push_back(std::make_shared(a_Hostname, a_Callbacks)); - } - catch (...) - { - return false; - } - return true; -} - - - - -bool cNetworkSingleton::IPToHostName( - const AString & a_IP, - cNetwork::cResolveNameCallbacksPtr a_Callbacks -) -{ - try - { - cCSLock Lock(m_CS); - m_IPLookups.push_back(std::make_shared(a_IP, a_Callbacks)); - } - catch (...) - { - return false; - } - return true; -} - - - - -void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) -{ - switch (a_Severity) - { - case _EVENT_LOG_DEBUG: LOGD ("LibEvent: %s", a_Msg); break; - case _EVENT_LOG_MSG: LOG ("LibEvent: %s", a_Msg); break; - case _EVENT_LOG_WARN: LOGWARNING("LibEvent: %s", a_Msg); break; - case _EVENT_LOG_ERR: LOGERROR ("LibEvent: %s", a_Msg); break; - default: - { - LOGWARNING("LibEvent: Unknown log severity (%d): %s", a_Severity, a_Msg); - break; - } - } -} - - - - - -void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) -{ - event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY); -} - - - - - -void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup) -{ - cCSLock Lock(m_CS); - for (auto itr = m_HostnameLookups.begin(), end = m_HostnameLookups.end(); itr != end; ++itr) - { - if (itr->get() == a_HostnameLookup) - { - m_HostnameLookups.erase(itr); - return; - } - } // for itr - m_HostnameLookups[] -} - - - - - -void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) -{ - cCSLock Lock(m_CS); - for (auto itr = m_IPLookups.begin(), end = m_IPLookups.end(); itr != end; ++itr) - { - if (itr->get() == a_IPLookup) - { - m_IPLookups.erase(itr); - return; - } - } // for itr - m_IPLookups[] -} - - - - - -void cNetworkSingleton::AddLink(cTCPLinkImplPtr a_Link) -{ - cCSLock Lock(m_CS); - m_Connections.push_back(a_Link); -} - - - - - -void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link) -{ - cCSLock Lock(m_CS); - for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) - { - if (itr->get() == a_Link) - { - m_Connections.erase(itr); - return; - } - } // for itr - m_Connections[] -} - - - - - -void cNetworkSingleton::AddServer(cServerHandleImplPtr a_Server) -{ - cCSLock Lock(m_CS); - m_Servers.push_back(a_Server); -} - - - - - -void cNetworkSingleton::RemoveServer(const cServerHandleImpl * a_Server) -{ - cCSLock Lock(m_CS); - for (auto itr = m_Servers.begin(), end = m_Servers.end(); itr != end; ++itr) - { - if (itr->get() == a_Server) - { - m_Servers.erase(itr); - return; - } - } // for itr - m_Servers[] -} - - - - diff --git a/src/OSSupport/NetworkSingleton.cpp b/src/OSSupport/NetworkSingleton.cpp new file mode 100644 index 000000000..552abad64 --- /dev/null +++ b/src/OSSupport/NetworkSingleton.cpp @@ -0,0 +1,281 @@ + +// NetworkSingleton.cpp + +// Implements the cNetworkSingleton class representing the storage for global data pertaining to network API +// such as a list of all connections, all listening sockets and the LibEvent dispatch thread. + +#include "Globals.h" +#include "NetworkSingleton.h" +#include +#include +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// Class definitions: + +/** Holds information about an in-progress Hostname-to-IP lookup. */ +class cHostnameLookup +{ + /** The callbacks to call for resolved names / errors. */ + cNetwork::cResolveNameCallbacksPtr m_Callbacks; + + /** The hostname that was queried (needed for the callbacks). */ + AString m_Hostname; + + static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self); + +public: + cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks); +}; +typedef SharedPtr cHostnameLookupPtr; +typedef std::vector cHostnameLookupPtrs; + + + + + +/** Holds information about an in-progress IP-to-Hostname lookup. */ +class cIPLookup +{ + /** The callbacks to call for resolved names / errors. */ + cNetwork::cResolveNameCallbacksPtr m_Callbacks; + + /** The IP that was queried (needed for the callbacks). */ + AString m_IP; + + static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self); + +public: + cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks); +}; +typedef SharedPtr cIPLookupPtr; +typedef std::vector cIPLookupPtrs; + + + + + +cNetworkSingleton::cNetworkSingleton(void) +{ + // Windows: initialize networking: + #ifdef _WIN32 + WSADATA wsaData; + memset(&wsaData, 0, sizeof(wsaData)); + int res = WSAStartup (MAKEWORD(2, 2), &wsaData); + if (res != 0) + { + int err = WSAGetLastError(); + LOGWARNING("WSAStartup failed: %d, WSAGLE = %d (%s)", res, err, evutil_socket_error_to_string(err)); + exit(1); + } + #endif // _WIN32 + + // Initialize LibEvent logging: + event_set_log_callback(LogCallback); + + // Initialize threading: + #if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED) + evthread_use_windows_threads(); + #elif defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) + evthread_use_pthreads(); + #else + #error No threading implemented for EVTHREAD + #endif + + // Create the main event_base: + m_EventBase = event_base_new(); + if (m_EventBase == nullptr) + { + LOGERROR("Failed to initialize LibEvent. The server will now terminate."); + abort(); + } + + // Create the DNS lookup helper: + m_DNSBase = evdns_base_new(m_EventBase, 1); + if (m_DNSBase == nullptr) + { + LOGERROR("Failed to initialize LibEvent's DNS subsystem. The server will now terminate."); + abort(); + } + + // Create the event loop thread: + std::thread EventLoopThread(RunEventLoop, this); + EventLoopThread.detach(); +} + + + + + +cNetworkSingleton & cNetworkSingleton::Get(void) +{ + static cNetworkSingleton Instance; + return Instance; +} + + + + + +bool cNetworkSingleton::HostnameToIP( + const AString & a_Hostname, + cNetwork::cResolveNameCallbacksPtr a_Callbacks +) +{ + try + { + cCSLock Lock(m_CS); + m_HostnameLookups.push_back(std::make_shared(a_Hostname, a_Callbacks)); + } + catch (...) + { + return false; + } + return true; +} + + + + +bool cNetworkSingleton::IPToHostName( + const AString & a_IP, + cNetwork::cResolveNameCallbacksPtr a_Callbacks +) +{ + try + { + cCSLock Lock(m_CS); + m_IPLookups.push_back(std::make_shared(a_IP, a_Callbacks)); + } + catch (...) + { + return false; + } + return true; +} + + + + +void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) +{ + switch (a_Severity) + { + case _EVENT_LOG_DEBUG: LOGD ("LibEvent: %s", a_Msg); break; + case _EVENT_LOG_MSG: LOG ("LibEvent: %s", a_Msg); break; + case _EVENT_LOG_WARN: LOGWARNING("LibEvent: %s", a_Msg); break; + case _EVENT_LOG_ERR: LOGERROR ("LibEvent: %s", a_Msg); break; + default: + { + LOGWARNING("LibEvent: Unknown log severity (%d): %s", a_Severity, a_Msg); + break; + } + } +} + + + + + +void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) +{ + event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY); +} + + + + + +void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup) +{ + cCSLock Lock(m_CS); + for (auto itr = m_HostnameLookups.begin(), end = m_HostnameLookups.end(); itr != end; ++itr) + { + if (itr->get() == a_HostnameLookup) + { + m_HostnameLookups.erase(itr); + return; + } + } // for itr - m_HostnameLookups[] +} + + + + + +void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) +{ + cCSLock Lock(m_CS); + for (auto itr = m_IPLookups.begin(), end = m_IPLookups.end(); itr != end; ++itr) + { + if (itr->get() == a_IPLookup) + { + m_IPLookups.erase(itr); + return; + } + } // for itr - m_IPLookups[] +} + + + + + +void cNetworkSingleton::AddLink(cTCPLinkImplPtr a_Link) +{ + cCSLock Lock(m_CS); + m_Connections.push_back(a_Link); +} + + + + + +void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link) +{ + cCSLock Lock(m_CS); + for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + if (itr->get() == a_Link) + { + m_Connections.erase(itr); + return; + } + } // for itr - m_Connections[] +} + + + + + +void cNetworkSingleton::AddServer(cServerHandleImplPtr a_Server) +{ + cCSLock Lock(m_CS); + m_Servers.push_back(a_Server); +} + + + + + +void cNetworkSingleton::RemoveServer(const cServerHandleImpl * a_Server) +{ + cCSLock Lock(m_CS); + for (auto itr = m_Servers.begin(), end = m_Servers.end(); itr != end; ++itr) + { + if (itr->get() == a_Server) + { + m_Servers.erase(itr); + return; + } + } // for itr - m_Servers[] +} + + + + diff --git a/src/OSSupport/NetworkSingleton.h b/src/OSSupport/NetworkSingleton.h new file mode 100644 index 000000000..d5a4ec279 --- /dev/null +++ b/src/OSSupport/NetworkSingleton.h @@ -0,0 +1,138 @@ + +// NetworkSingleton.h + +// Declares the cNetworkSingleton class representing the storage for global data pertaining to network API +// such as a list of all connections, all listening sockets and the LibEvent dispatch thread. + +#pragma once + +#include "Network.h" +#include "CriticalSection.h" + + + + + +// fwd: +struct event_base; +struct evdns_base; +class cServerHandleImpl; +class cTCPLinkImpl; +class cHostnameLookup; +class cIPLookup; +typedef SharedPtr cTCPLinkImplPtr; +typedef std::vector cTCPLinkImplPtrs; +typedef SharedPtr cServerHandleImplPtr; +typedef std::vector cServerHandleImplPtrs; +typedef SharedPtr cHostnameLookupPtr; +typedef std::vector cHostnameLookupPtrs; +typedef SharedPtr cIPLookupPtr; +typedef std::vector cIPLookupPtrs; + + + + + +class cNetworkSingleton +{ +public: + /** Returns the singleton instance of this class */ + static cNetworkSingleton & Get(void); + + + // The following functions are implementations for the cNetwork class + + /** Queues a DNS query to resolve the specified hostname to IP address. + Calls one of the callbacks when the resolving succeeds, or when it fails. + Returns true if queueing was successful, false if not. + Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. + TODO: Move this out into a separate file with cHostnameLookup. */ + bool HostnameToIP( + const AString & a_Hostname, + cNetwork::cResolveNameCallbacksPtr a_Callbacks + ); + + + /** Queues a DNS query to resolve the specified IP address to a hostname. + Calls one of the callbacks when the resolving succeeds, or when it fails. + Returns true if queueing was successful, false if not. + Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. + TODO: Move this out into a separate file with cIPLookup. */ + bool IPToHostName( + const AString & a_IP, + cNetwork::cResolveNameCallbacksPtr a_Callbacks + ); + + /** Returns the main LibEvent handle for event registering. */ + event_base * GetEventBase(void) { return m_EventBase; } + + /** Returns the LibEvent handle for DNS lookups. */ + evdns_base * GetDNSBase(void) { return m_DNSBase; } + + /** Removes the specified hostname lookup from m_HostnameLookups. + Used by the underlying lookup implementation when the lookup is finished. */ + void RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup); + + /** Removes the specified IP lookup from m_IPLookups. + Used by the underlying lookup implementation when the lookup is finished. */ + void RemoveIPLookup(const cIPLookup * a_IPLookup); + + /** Adds the specified link to m_Connections. + Used by the underlying link implementation when a new link is created. */ + void AddLink(cTCPLinkImplPtr a_Link); + + /** Removes the specified link from m_Connections. + Used by the underlying link implementation when the link is closed / errored. */ + void RemoveLink(const cTCPLinkImpl * a_Link); + + /** Adds the specified link to m_Servers. + Used by the underlying server handle implementation when a new listening server is created. + Only servers that succeed in listening are added. */ + void AddServer(cServerHandleImplPtr a_Server); + + /** Removes the specified server from m_Servers. + Used by the underlying server handle implementation when the server is closed. */ + void RemoveServer(const cServerHandleImpl * a_Server); + +protected: + + /** The main LibEvent container for driving the event loop. */ + event_base * m_EventBase; + + /** The LibEvent handle for doing DNS lookups. */ + evdns_base * m_DNSBase; + + /** Container for all client connections, including ones with pending-connect. */ + cTCPLinkImplPtrs m_Connections; + + /** Container for all servers that are currently active. */ + cServerHandleImplPtrs m_Servers; + + /** Container for all pending hostname lookups. */ + cHostnameLookupPtrs m_HostnameLookups; + + /** Container for all pending IP lookups. */ + cIPLookupPtrs m_IPLookups; + + /** Mutex protecting all containers against multithreaded access. */ + cCriticalSection m_CS; + + + /** Initializes the LibEvent internals. */ + cNetworkSingleton(void); + + /** Converts LibEvent-generated log events into log messages in MCS log. */ + static void LogCallback(int a_Severity, const char * a_Msg); + + /** Implements the thread that runs LibEvent's event dispatcher loop. */ + static void RunEventLoop(cNetworkSingleton * a_Self); +}; + + + + + + + + + -- cgit v1.2.3 From 1e5aedaceaee55438c7e1681f7e221d6c8689800 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 17 Jan 2015 23:47:28 +0100 Subject: cNetwork: Fixed WinXP dualstack listening. --- src/OSSupport/Network.cpp | 63 +++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp index 359206632..02bdfa39c 100644 --- a/src/OSSupport/Network.cpp +++ b/src/OSSupport/Network.cpp @@ -838,31 +838,52 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) return false; } m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock); + m_IsListening = true; + if (!NeedsTwoSockets) + { + return true; + } // If a secondary socket is required (WinXP dual-stack), create it here: - if (NeedsTwoSockets) + LOGD("Creating a second socket for IPv4"); + evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (!IsValidSocket(SecondSock)) { - LOGD("Creating a second socket for IPv4"); - evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (IsValidSocket(SecondSock)) - { - if (evutil_make_socket_nonblocking(SecondSock) == 0) - { - m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); - } - else - { - err = EVUTIL_SOCKET_ERROR(); - LOGD("evutil_make_socket_nonblocking() failed: %d, %s", err, evutil_socket_error_to_string(err)); - } - } - else - { - err = EVUTIL_SOCKET_ERROR(); - LOGD("socket(AF_INET, ...) failed: %d, %s", err, evutil_socket_error_to_string(err)); - } + err = EVUTIL_SOCKET_ERROR(); + LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err)); + return true; // Report as success, the primary socket is working } - m_IsListening = true; + + // Make the secondary socket nonblocking: + if (evutil_make_socket_nonblocking(SecondSock) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err)); + evutil_closesocket(SecondSock); + } + + // Bind to all IPv4 interfaces: + sockaddr_in name; + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = ntohs(a_Port); + if (bind(SecondSock, reinterpret_cast(&name), sizeof(name)) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(SecondSock); + return true; + } + + if (listen(SecondSock, 0) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(SecondSock); + return false; + } + + m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); return true; } -- cgit v1.2.3 From c0cb787c101725a649d26de68fca2632c82830ba Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 18 Jan 2015 11:57:16 +0100 Subject: cNetwork: Split the main cpp file into several files. --- src/OSSupport/CMakeLists.txt | 9 +- src/OSSupport/HostnameLookup.cpp | 109 ++++ src/OSSupport/HostnameLookup.h | 41 ++ src/OSSupport/IPLookup.cpp | 91 ++++ src/OSSupport/IPLookup.h | 40 ++ src/OSSupport/Network.cpp | 998 ------------------------------------- src/OSSupport/Network.h | 19 +- src/OSSupport/NetworkSingleton.cpp | 51 +- src/OSSupport/NetworkSingleton.h | 6 + src/OSSupport/ServerHandleImpl.cpp | 302 +++++++++++ src/OSSupport/ServerHandleImpl.h | 109 ++++ src/OSSupport/TCPLinkImpl.cpp | 314 ++++++++++++ src/OSSupport/TCPLinkImpl.h | 109 ++++ 13 files changed, 1148 insertions(+), 1050 deletions(-) create mode 100644 src/OSSupport/HostnameLookup.cpp create mode 100644 src/OSSupport/HostnameLookup.h create mode 100644 src/OSSupport/IPLookup.cpp create mode 100644 src/OSSupport/IPLookup.h delete mode 100644 src/OSSupport/Network.cpp create mode 100644 src/OSSupport/ServerHandleImpl.cpp create mode 100644 src/OSSupport/ServerHandleImpl.h create mode 100644 src/OSSupport/TCPLinkImpl.cpp create mode 100644 src/OSSupport/TCPLinkImpl.h (limited to 'src') diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index 167afb784..9424b63da 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -10,14 +10,17 @@ SET (SRCS Event.cpp File.cpp GZipFile.cpp + HostnameLookup.cpp + IPLookup.cpp IsThread.cpp ListenThread.cpp - Network.cpp NetworkSingleton.cpp Semaphore.cpp + ServerHandleImpl.cpp Socket.cpp SocketThreads.cpp StackTrace.cpp + TCPLinkImpl.cpp ) SET (HDRS @@ -26,15 +29,19 @@ SET (HDRS Event.h File.h GZipFile.h + HostnameLookup.h + IPLookup.h IsThread.h ListenThread.h Network.h NetworkSingleton.h Queue.h Semaphore.h + ServerHandleImpl.h Socket.h SocketThreads.h StackTrace.h + TCPLinkImpl.h ) if(NOT MSVC) diff --git a/src/OSSupport/HostnameLookup.cpp b/src/OSSupport/HostnameLookup.cpp new file mode 100644 index 000000000..9e35f7163 --- /dev/null +++ b/src/OSSupport/HostnameLookup.cpp @@ -0,0 +1,109 @@ + +// HostnameLookup.cpp + +// Implements the cHostnameLookup class representing an in-progress hostname-to-IP lookup + +#include "Globals.h" +#include "HostnameLookup.h" +#include +#include "NetworkSingleton.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cHostnameLookup: + +cHostnameLookup::cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks), + m_Hostname(a_Hostname) +{ + evutil_addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = EVUTIL_AI_CANONNAME; + evdns_getaddrinfo(cNetworkSingleton::Get().GetDNSBase(), a_Hostname.c_str(), nullptr, &hints, Callback, this); +} + + + + + +void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a_Self) +{ + // Get the Self class: + cHostnameLookup * Self = reinterpret_cast(a_Self); + ASSERT(Self != nullptr); + + // If an error has occurred, notify the error callback: + if (a_ErrCode != 0) + { + Self->m_Callbacks->OnError(a_ErrCode); + cNetworkSingleton::Get().RemoveHostnameLookup(Self); + return; + } + + // Call the success handler for each entry received: + bool HasResolved = false; + evutil_addrinfo * OrigAddr = a_Addr; + for (;a_Addr != nullptr; a_Addr = a_Addr->ai_next) + { + char IP[128]; + switch (a_Addr->ai_family) + { + case AF_INET: // IPv4 + { + sockaddr_in * sin = reinterpret_cast(a_Addr->ai_addr); + evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); + break; + } + case AF_INET6: // IPv6 + { + sockaddr_in6 * sin = reinterpret_cast(a_Addr->ai_addr); + evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); + break; + } + default: + { + // Unknown address family, handle as if this entry wasn't received + continue; // for (a_Addr) + } + } + Self->m_Callbacks->OnNameResolved(Self->m_Hostname, IP); + HasResolved = true; + } // for (a_Addr) + + // If only unsupported families were reported, call the Error handler: + if (!HasResolved) + { + Self->m_Callbacks->OnError(1); + } + else + { + Self->m_Callbacks->OnFinished(); + } + evutil_freeaddrinfo(OrigAddr); + cNetworkSingleton::Get().RemoveHostnameLookup(Self); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API: + +bool cNetwork::HostnameToIP( + const AString & a_Hostname, + cNetwork::cResolveNameCallbacksPtr a_Callbacks +) +{ + return cNetworkSingleton::Get().HostnameToIP(a_Hostname, a_Callbacks); +} + + + + diff --git a/src/OSSupport/HostnameLookup.h b/src/OSSupport/HostnameLookup.h new file mode 100644 index 000000000..98f48b933 --- /dev/null +++ b/src/OSSupport/HostnameLookup.h @@ -0,0 +1,41 @@ + +// HostnameLookup.h + +// Declares the cHostnameLookup class representing an in-progress hostname-to-IP lookup + +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead + + + + + +#pragma once + +#include "Network.h" +#include + + + + + +/** Holds information about an in-progress Hostname-to-IP lookup. */ +class cHostnameLookup +{ + /** The callbacks to call for resolved names / errors. */ + cNetwork::cResolveNameCallbacksPtr m_Callbacks; + + /** The hostname that was queried (needed for the callbacks). */ + AString m_Hostname; + + static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self); + +public: + cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks); +}; +typedef SharedPtr cHostnameLookupPtr; +typedef std::vector cHostnameLookupPtrs; + + + + + diff --git a/src/OSSupport/IPLookup.cpp b/src/OSSupport/IPLookup.cpp new file mode 100644 index 000000000..bbcfbfe40 --- /dev/null +++ b/src/OSSupport/IPLookup.cpp @@ -0,0 +1,91 @@ + +// IPLookup.cpp + +// Implements the cIPLookup class representing an IP-to-hostname lookup in progress. + +#include "Globals.h" +#include "IPLookup.h" +#include +#include "NetworkSingleton.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cIPLookup: + +cIPLookup::cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks), + m_IP(a_IP) +{ + sockaddr_storage sa; + int salen = static_cast(sizeof(sa)); + memset(&sa, 0, sizeof(sa)); + evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast(&sa), &salen); + switch (sa.ss_family) + { + case AF_INET: + { + sockaddr_in * sa4 = reinterpret_cast(&sa); + evdns_base_resolve_reverse(cNetworkSingleton::Get().GetDNSBase(), &(sa4->sin_addr), 0, Callback, this); + break; + } + case AF_INET6: + { + sockaddr_in6 * sa6 = reinterpret_cast(&sa); + evdns_base_resolve_reverse_ipv6(cNetworkSingleton::Get().GetDNSBase(), &(sa6->sin6_addr), 0, Callback, this); + break; + } + default: + { + LOGWARNING("%s: Unknown address family: %d", __FUNCTION__, sa.ss_family); + ASSERT(!"Unknown address family"); + break; + } + } // switch (address family) +} + + + + + +void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self) +{ + // Get the Self class: + cIPLookup * Self = reinterpret_cast(a_Self); + ASSERT(Self != nullptr); + + // Call the proper callback based on the event received: + if ((a_Result != 0) || (a_Addresses == nullptr)) + { + // An error has occurred, notify the error callback: + Self->m_Callbacks->OnError(a_Result); + } + else + { + // Call the success handler: + Self->m_Callbacks->OnNameResolved(*(reinterpret_cast(a_Addresses)), Self->m_IP); + Self->m_Callbacks->OnFinished(); + } + cNetworkSingleton::Get().RemoveIPLookup(Self); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API: + +bool cNetwork::IPToHostName( + const AString & a_IP, + cNetwork::cResolveNameCallbacksPtr a_Callbacks +) +{ + return cNetworkSingleton::Get().IPToHostName(a_IP, a_Callbacks); +} + + + + diff --git a/src/OSSupport/IPLookup.h b/src/OSSupport/IPLookup.h new file mode 100644 index 000000000..f39b955aa --- /dev/null +++ b/src/OSSupport/IPLookup.h @@ -0,0 +1,40 @@ + +// IPLookup.h + +// Declares the cIPLookup class representing an IP-to-hostname lookup in progress. + +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead + + + + + +#pragma once + +#include "Network.h" + + + + + +/** Holds information about an in-progress IP-to-Hostname lookup. */ +class cIPLookup +{ + /** The callbacks to call for resolved names / errors. */ + cNetwork::cResolveNameCallbacksPtr m_Callbacks; + + /** The IP that was queried (needed for the callbacks). */ + AString m_IP; + + static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self); + +public: + cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks); +}; +typedef SharedPtr cIPLookupPtr; +typedef std::vector cIPLookupPtrs; + + + + + diff --git a/src/OSSupport/Network.cpp b/src/OSSupport/Network.cpp deleted file mode 100644 index 02bdfa39c..000000000 --- a/src/OSSupport/Network.cpp +++ /dev/null @@ -1,998 +0,0 @@ - -// Network.cpp - -// Implements the classes used for the Network API - -#include "Globals.h" -#include "Network.h" -#include -#include -#include -#include -#include -#include -#include "Event.h" -#include "CriticalSection.h" -#include "NetworkSingleton.h" - - - - - -// fwd: -class cServerHandleImpl; -class cTCPLinkImpl; -typedef SharedPtr cTCPLinkImplPtr; -typedef std::vector cTCPLinkImplPtrs; -typedef SharedPtr cServerHandleImplPtr; -typedef std::vector cServerHandleImplPtrs; - - - - - -//////////////////////////////////////////////////////////////////////////////// -// Class definitions: - -/** Holds information about an in-progress Hostname-to-IP lookup. */ -class cHostnameLookup -{ - /** The callbacks to call for resolved names / errors. */ - cNetwork::cResolveNameCallbacksPtr m_Callbacks; - - /** The hostname that was queried (needed for the callbacks). */ - AString m_Hostname; - - static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self); - -public: - cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks); -}; -typedef SharedPtr cHostnameLookupPtr; -typedef std::vector cHostnameLookupPtrs; - - - - - -/** Holds information about an in-progress IP-to-Hostname lookup. */ -class cIPLookup -{ - /** The callbacks to call for resolved names / errors. */ - cNetwork::cResolveNameCallbacksPtr m_Callbacks; - - /** The IP that was queried (needed for the callbacks). */ - AString m_IP; - - static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self); - -public: - cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks); -}; -typedef SharedPtr cIPLookupPtr; -typedef std::vector cIPLookupPtrs; - - - - - -/** Implements the cTCPLink details so that it can represent the single connection between two endpoints. */ -class cTCPLinkImpl: - public cTCPLink -{ - typedef cTCPLink super; - -public: - /** Creates a new link based on the given socket. - Used for connections accepted in a server using cNetwork::Listen(). - a_Address and a_AddrLen describe the remote peer that has connected. */ - cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen); - - /** Destroys the LibEvent handle representing the link. */ - ~cTCPLinkImpl(); - - /** Queues a connection request to the specified host. - a_ConnectCallbacks must be valid. - Returns a link that has the connection request queued, or NULL for failure. */ - static cTCPLinkImplPtr Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks); - - // cTCPLink overrides: - virtual bool Send(const void * a_Data, size_t a_Length) override; - virtual AString GetLocalIP(void) const override { return m_LocalIP; } - virtual UInt16 GetLocalPort(void) const override { return m_LocalPort; } - virtual AString GetRemoteIP(void) const override { return m_RemoteIP; } - virtual UInt16 GetRemotePort(void) const override { return m_RemotePort; } - virtual void Shutdown(void) override; - virtual void Close(void) override; - -protected: - - /** Callbacks to call when the connection is established. - May be NULL if not used. Only used for outgoing connections (cNetwork::Connect()). */ - cNetwork::cConnectCallbacksPtr m_ConnectCallbacks; - - /** The LibEvent handle representing this connection. */ - bufferevent * m_BufferEvent; - - /** The server handle that has created this link. - Only valid for incoming connections, NULL for outgoing connections. */ - cServerHandleImpl * m_Server; - - /** The IP address of the local endpoint. Valid only after the socket has been connected. */ - AString m_LocalIP; - - /** The port of the local endpoint. Valid only after the socket has been connected. */ - UInt16 m_LocalPort; - - /** The IP address of the remote endpoint. Valid only after the socket has been connected. */ - AString m_RemoteIP; - - /** The port of the remote endpoint. Valid only after the socket has been connected. */ - UInt16 m_RemotePort; - - - /** Creates a new link to be queued to connect to a specified host:port. - Used for outgoing connections created using cNetwork::Connect(). - To be used only by the Connect() factory function. */ - cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks); - - /** Callback that LibEvent calls when there's data available from the remote peer. */ - static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self); - - /** Callback that LibEvent calls when there's a non-data-related event on the socket. */ - static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self); - - /** Sets a_IP and a_Port to values read from a_Address, based on the correct address family. */ - static void UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AString & a_IP, UInt16 & a_Port); - - /** Updates m_LocalIP and m_LocalPort based on the metadata read from the socket. */ - void UpdateLocalAddress(void); - - /** Updates m_RemoteIP and m_RemotePort based on the metadata read from the socket. */ - void UpdateRemoteAddress(void); -}; - - - - - -/** Implements the cServerHandle details so that it can represent a real server socket, with a list of clients. */ -class cServerHandleImpl: - public cServerHandle -{ - typedef cServerHandle super; - friend class cTCPLinkImpl; - -public: - /** Closes the server, dropping all the connections. */ - ~cServerHandleImpl(); - - /** Creates a new server instance listening on the specified port. - Both IPv4 and IPv6 interfaces are used, if possible. - Always returns a server instance; in the event of a failure, the instance holds the error details. Use IsListening() to query success. */ - static cServerHandleImplPtr Listen( - UInt16 a_Port, - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks - ); - - // cServerHandle overrides: - virtual void Close(void) override; - virtual bool IsListening(void) const override { return m_IsListening; } - -protected: - /** The callbacks used to notify about incoming connections. */ - cNetwork::cListenCallbacksPtr m_ListenCallbacks; - - /** The callbacks used to create new cTCPLink instances for incoming connections. */ - cTCPLink::cCallbacksPtr m_LinkCallbacks; - - /** The LibEvent handle representing the main listening socket. */ - evconnlistener * m_ConnListener; - - /** The LibEvent handle representing the secondary listening socket (only when side-by-side listening is needed, such as WinXP). */ - evconnlistener * m_SecondaryConnListener; - - /** Set to true when the server is initialized successfully and is listening for incoming connections. */ - bool m_IsListening; - - /** Container for all currently active connections on this server. */ - cTCPLinkImplPtrs m_Connections; - - /** Mutex protecting m_Connections againt multithreaded access. */ - cCriticalSection m_CS; - - /** Contains the error code for the failure to listen. Only valid for non-listening instances. */ - int m_ErrorCode; - - /** Contains the error message for the failure to listen. Only valid for non-listening instances. */ - AString m_ErrorMsg; - - - - /** Creates a new instance with the specified callbacks. - Initializes the internals, but doesn't start listening yet. */ - cServerHandleImpl( - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks - ); - - /** Starts listening on the specified port. - Returns true if successful, false on failure. On failure, sets m_ErrorCode and m_ErrorMsg. */ - bool Listen(UInt16 a_Port); - - /** The callback called by LibEvent upon incoming connection. */ - static void Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self); - - /** Removes the specified link from m_Connections. - Called by cTCPLinkImpl when the link is terminated. */ - void RemoveLink(const cTCPLinkImpl * a_Link); -}; - - - - - -//////////////////////////////////////////////////////////////////////////////// -// Globals: - -bool IsValidSocket(evutil_socket_t a_Socket) -{ - #ifdef _WIN32 - return (a_Socket != INVALID_SOCKET); - #else // _WIN32 - return (a_Socket >= 0); - #endif // else _WIN32 -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cHostnameLookup: - -cHostnameLookup::cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks): - m_Callbacks(a_Callbacks), - m_Hostname(a_Hostname) -{ - evutil_addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_protocol = IPPROTO_TCP; - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = AF_UNSPEC; - hints.ai_flags = EVUTIL_AI_CANONNAME; - evdns_getaddrinfo(cNetworkSingleton::Get().GetDNSBase(), a_Hostname.c_str(), nullptr, &hints, Callback, this); -} - - - - - -void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a_Self) -{ - // Get the Self class: - cHostnameLookup * Self = reinterpret_cast(a_Self); - ASSERT(Self != nullptr); - - // If an error has occurred, notify the error callback: - if (a_ErrCode != 0) - { - Self->m_Callbacks->OnError(a_ErrCode); - cNetworkSingleton::Get().RemoveHostnameLookup(Self); - return; - } - - // Call the success handler for each entry received: - bool HasResolved = false; - evutil_addrinfo * OrigAddr = a_Addr; - for (;a_Addr != nullptr; a_Addr = a_Addr->ai_next) - { - char IP[128]; - switch (a_Addr->ai_family) - { - case AF_INET: // IPv4 - { - sockaddr_in * sin = reinterpret_cast(a_Addr->ai_addr); - evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); - break; - } - case AF_INET6: // IPv6 - { - sockaddr_in6 * sin = reinterpret_cast(a_Addr->ai_addr); - evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); - break; - } - default: - { - // Unknown address family, handle as if this entry wasn't received - continue; // for (a_Addr) - } - } - Self->m_Callbacks->OnNameResolved(Self->m_Hostname, IP); - HasResolved = true; - } // for (a_Addr) - - // If only unsupported families were reported, call the Error handler: - if (!HasResolved) - { - Self->m_Callbacks->OnError(1); - } - else - { - Self->m_Callbacks->OnFinished(); - } - evutil_freeaddrinfo(OrigAddr); - cNetworkSingleton::Get().RemoveHostnameLookup(Self); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cIPLookup: - -cIPLookup::cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks): - m_Callbacks(a_Callbacks), - m_IP(a_IP) -{ - sockaddr_storage sa; - int salen = static_cast(sizeof(sa)); - evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast(&sa), &salen); - switch (sa.ss_family) - { - case AF_INET: - { - sockaddr_in * sa4 = reinterpret_cast(&sa); - evdns_base_resolve_reverse(cNetworkSingleton::Get().GetDNSBase(), &(sa4->sin_addr), 0, Callback, this); - break; - } - case AF_INET6: - { - sockaddr_in6 * sa6 = reinterpret_cast(&sa); - evdns_base_resolve_reverse_ipv6(cNetworkSingleton::Get().GetDNSBase(), &(sa6->sin6_addr), 0, Callback, this); - break; - } - default: - { - LOGWARNING("%s: Unknown address family: %d", __FUNCTION__, sa.ss_family); - ASSERT(!"Unknown address family"); - break; - } - } // switch (address family) -} - - - - - -void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self) -{ - // Get the Self class: - cIPLookup * Self = reinterpret_cast(a_Self); - ASSERT(Self != nullptr); - - // Call the proper callback based on the event received: - if ((a_Result != 0) || (a_Addresses == nullptr)) - { - // An error has occurred, notify the error callback: - Self->m_Callbacks->OnError(a_Result); - } - else - { - // Call the success handler: - Self->m_Callbacks->OnNameResolved(*(reinterpret_cast(a_Addresses)), Self->m_IP); - Self->m_Callbacks->OnFinished(); - } - cNetworkSingleton::Get().RemoveIPLookup(Self); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cTCPLinkImpl: - -cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): - super(a_LinkCallbacks), - m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE)), - m_Server(nullptr) -{ - // Create the LibEvent handle, but don't assign a socket to it yet (will be assigned within Connect() method): - bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); - bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); -} - - - - - -cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen): - super(a_LinkCallbacks), - m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE)), - m_Server(a_Server) -{ - // Update the endpoint addresses: - UpdateLocalAddress(); - UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort); - - // Create the LibEvent handle: - bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); - bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); -} - - - - - -cTCPLinkImpl::~cTCPLinkImpl() -{ - bufferevent_free(m_BufferEvent); -} - - - - - -cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) -{ - ASSERT(a_LinkCallbacks != nullptr); - ASSERT(a_ConnectCallbacks != nullptr); - - // Create a new link: - cTCPLinkImplPtr res{new cTCPLinkImpl(a_LinkCallbacks)}; // Cannot use std::make_shared here, constructor is not accessible - res->m_ConnectCallbacks = a_ConnectCallbacks; - cNetworkSingleton::Get().AddLink(res); - - // If a_Host is an IP address, schedule a connection immediately: - sockaddr_storage sa; - int salen = static_cast(sizeof(sa)); - if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast(&sa), &salen) == 0) - { - // Insert the correct port: - if (sa.ss_family == AF_INET6) - { - reinterpret_cast(&sa)->sin6_port = htons(a_Port); - } - else - { - reinterpret_cast(&sa)->sin_port = htons(a_Port); - } - - // Queue the connect request: - if (bufferevent_socket_connect(res->m_BufferEvent, reinterpret_cast(&sa), salen) == 0) - { - // Success - return res; - } - // Failure - cNetworkSingleton::Get().RemoveLink(res.get()); - return nullptr; - } - - // a_Host is a hostname, connect after a lookup: - if (bufferevent_socket_connect_hostname(res->m_BufferEvent, cNetworkSingleton::Get().GetDNSBase(), AF_UNSPEC, a_Host.c_str(), a_Port) == 0) - { - // Success - return res; - } - // Failure - cNetworkSingleton::Get().RemoveLink(res.get()); - return nullptr; -} - - - - - -bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) -{ - return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0); -} - - - - - -void cTCPLinkImpl::Shutdown(void) -{ - #ifdef _WIN32 - shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND); - #else - shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR); - #endif - bufferevent_disable(m_BufferEvent, EV_WRITE); -} - - - - - -void cTCPLinkImpl::Close(void) -{ - // Disable all events on the socket, but keep it alive: - bufferevent_disable(m_BufferEvent, EV_READ | EV_WRITE); - if (m_Server == nullptr) - { - cNetworkSingleton::Get().RemoveLink(this); - } - else - { - m_Server->RemoveLink(this); - } -} - - - - - - -void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self) -{ - ASSERT(a_Self != nullptr); - cTCPLinkImpl * Self = static_cast(a_Self); - ASSERT(Self->m_Callbacks != nullptr); - - // Read all the incoming data, in 1024-byte chunks: - char data[1024]; - size_t length; - while ((length = bufferevent_read(a_BufferEvent, data, sizeof(data))) > 0) - { - Self->m_Callbacks->OnReceivedData(*Self, data, length); - } -} - - - - - -void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self) -{ - ASSERT(a_Self != nullptr); - cTCPLinkImpl * Self = static_cast(a_Self); - - // If an error is reported, call the error callback: - if (a_What & BEV_EVENT_ERROR) - { - // Choose the proper callback to call based on whether we were waiting for connection or not: - if (Self->m_ConnectCallbacks != nullptr) - { - Self->m_ConnectCallbacks->OnError(EVUTIL_SOCKET_ERROR()); - } - else - { - Self->m_Callbacks->OnError(*Self, EVUTIL_SOCKET_ERROR()); - if (Self->m_Server == nullptr) - { - cNetworkSingleton::Get().RemoveLink(Self); - } - else - { - Self->m_Server->RemoveLink(Self); - } - } - return; - } - - // Pending connection succeeded, call the connection callback: - if (a_What & BEV_EVENT_CONNECTED) - { - if (Self->m_ConnectCallbacks != nullptr) - { - Self->m_ConnectCallbacks->OnSuccess(*Self); - // Reset the connect callbacks so that later errors get reported through the link callbacks: - Self->m_ConnectCallbacks.reset(); - return; - } - Self->UpdateLocalAddress(); - Self->UpdateRemoteAddress(); - } - - // If the connection has been closed, call the link callback and remove the connection: - if (a_What & BEV_EVENT_EOF) - { - Self->m_Callbacks->OnRemoteClosed(*Self); - if (Self->m_Server != nullptr) - { - Self->m_Server->RemoveLink(Self); - } - else - { - cNetworkSingleton::Get().RemoveLink(Self); - } - return; - } - - // Unknown event, report it: - LOGWARNING("cTCPLinkImpl: Unhandled LibEvent event %d (0x%x)", a_What, a_What); - ASSERT(!"cTCPLinkImpl: Unhandled LibEvent event"); -} - - - - - -void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AString & a_IP, UInt16 & a_Port) -{ - // Based on the family specified in the address, use the correct datastructure to convert to IP string: - char IP[128]; - switch (a_Address->sa_family) - { - case AF_INET: // IPv4: - { - const sockaddr_in * sin = reinterpret_cast(a_Address); - evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); - a_Port = ntohs(sin->sin_port); - break; - } - case AF_INET6: // IPv6 - { - const sockaddr_in6 * sin = reinterpret_cast(a_Address); - evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); - a_Port = ntohs(sin->sin6_port); - break; - } - - default: - { - LOGWARNING("%s: Unknown socket address family: %d", __FUNCTION__, a_Address->sa_family); - ASSERT(!"Unknown socket address family"); - break; - } - } - a_IP.assign(IP); -} - - - - - -void cTCPLinkImpl::UpdateLocalAddress(void) -{ - sockaddr_storage sa; - socklen_t salen = static_cast(sizeof(sa)); - getsockname(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); - UpdateAddress(reinterpret_cast(&sa), salen, m_LocalIP, m_LocalPort); -} - - - - - -void cTCPLinkImpl::UpdateRemoteAddress(void) -{ - sockaddr_storage sa; - socklen_t salen = static_cast(sizeof(sa)); - getpeername(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); - UpdateAddress(reinterpret_cast(&sa), salen, m_RemoteIP, m_RemotePort); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cServerHandleImpl: - -cServerHandleImpl::cServerHandleImpl( - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks -): - m_ListenCallbacks(a_ListenCallbacks), - m_LinkCallbacks(a_LinkCallbacks), - m_ConnListener(nullptr), - m_SecondaryConnListener(nullptr), - m_IsListening(false), - m_ErrorCode(0) -{ -} - - - - - -cServerHandleImpl::~cServerHandleImpl() -{ - if (m_ConnListener != nullptr) - { - evconnlistener_free(m_ConnListener); - } - if (m_SecondaryConnListener != nullptr) - { - evconnlistener_free(m_SecondaryConnListener); - } -} - - - - - -void cServerHandleImpl::Close(void) -{ - // Stop the listener sockets: - evconnlistener_disable(m_ConnListener); - if (m_SecondaryConnListener != nullptr) - { - evconnlistener_disable(m_SecondaryConnListener); - } - m_IsListening = false; - - // Shutdown all connections: - cTCPLinkImplPtrs Conns; - { - cCSLock Lock(m_CS); - std::swap(Conns, m_Connections); - } - for (auto conn: Conns) - { - conn->Shutdown(); - } -} - - - - - -cServerHandleImplPtr cServerHandleImpl::Listen( - UInt16 a_Port, - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks -) -{ - cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks, a_LinkCallbacks)}; - if (res->Listen(a_Port)) - { - cNetworkSingleton::Get().AddServer(res); - } - else - { - a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg); - } - return res; -} - - - - - -bool cServerHandleImpl::Listen(UInt16 a_Port) -{ - // Make sure the cNetwork internals are innitialized: - cNetworkSingleton::Get(); - - // Set up the main socket: - // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available. - bool NeedsTwoSockets = false; - int err; - evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - if (!IsValidSocket(MainSock)) - { - // Failed to create IPv6 socket, create an IPv4 one instead: - err = EVUTIL_SOCKET_ERROR(); - LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err)); - MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (!IsValidSocket(MainSock)) - { - m_ErrorCode = EVUTIL_SOCKET_ERROR(); - Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); - return false; - } - - // Bind to all interfaces: - sockaddr_in name; - memset(&name, 0, sizeof(name)); - name.sin_family = AF_INET; - name.sin_port = ntohs(a_Port); - if (bind(MainSock, reinterpret_cast(&name), sizeof(name)) != 0) - { - m_ErrorCode = EVUTIL_SOCKET_ERROR(); - Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); - evutil_closesocket(MainSock); - return false; - } - } - else - { - // IPv6 socket created, switch it into "dualstack" mode: - UInt32 Zero = 0; - #ifdef _WIN32 - // WinXP doesn't support this feature, so if the setting fails, create another socket later on: - int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); - err = EVUTIL_SOCKET_ERROR(); - NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT)); - LOGD("setsockopt(IPV6_V6ONLY) returned %d, err is %d (%s). %s", - res, err, evutil_socket_error_to_string(err), - NeedsTwoSockets ? "Second socket will be created" : "Second socket not needed" - ); - #else - setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); - #endif - - // Bind to all interfaces: - sockaddr_in6 name; - memset(&name, 0, sizeof(name)); - name.sin6_family = AF_INET6; - name.sin6_port = ntohs(a_Port); - if (bind(MainSock, reinterpret_cast(&name), sizeof(name)) != 0) - { - m_ErrorCode = EVUTIL_SOCKET_ERROR(); - Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); - evutil_closesocket(MainSock); - return false; - } - } - if (evutil_make_socket_nonblocking(MainSock) != 0) - { - m_ErrorCode = EVUTIL_SOCKET_ERROR(); - Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); - evutil_closesocket(MainSock); - return false; - } - if (listen(MainSock, 0) != 0) - { - m_ErrorCode = EVUTIL_SOCKET_ERROR(); - Printf(m_ErrorMsg, "Cannot listen on port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); - evutil_closesocket(MainSock); - return false; - } - m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock); - m_IsListening = true; - if (!NeedsTwoSockets) - { - return true; - } - - // If a secondary socket is required (WinXP dual-stack), create it here: - LOGD("Creating a second socket for IPv4"); - evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (!IsValidSocket(SecondSock)) - { - err = EVUTIL_SOCKET_ERROR(); - LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err)); - return true; // Report as success, the primary socket is working - } - - // Make the secondary socket nonblocking: - if (evutil_make_socket_nonblocking(SecondSock) != 0) - { - err = EVUTIL_SOCKET_ERROR(); - LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err)); - evutil_closesocket(SecondSock); - } - - // Bind to all IPv4 interfaces: - sockaddr_in name; - memset(&name, 0, sizeof(name)); - name.sin_family = AF_INET; - name.sin_port = ntohs(a_Port); - if (bind(SecondSock, reinterpret_cast(&name), sizeof(name)) != 0) - { - err = EVUTIL_SOCKET_ERROR(); - LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); - evutil_closesocket(SecondSock); - return true; - } - - if (listen(SecondSock, 0) != 0) - { - err = EVUTIL_SOCKET_ERROR(); - LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); - evutil_closesocket(SecondSock); - return false; - } - - m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); - return true; -} - - - - - -void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self) -{ - // Cast to true self: - cServerHandleImpl * Self = reinterpret_cast(a_Self); - ASSERT(Self != nullptr); - - // Create a new cTCPLink for the incoming connection: - cTCPLinkImplPtr Link = std::make_shared(a_Socket, Self->m_LinkCallbacks, Self, a_Addr, a_Len); - { - cCSLock Lock(Self->m_CS); - Self->m_Connections.push_back(Link); - } // Lock(m_CS) - - // Call the OnAccepted callback: - Self->m_ListenCallbacks->OnAccepted(*Link); -} - - - - - -void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link) -{ - cCSLock Lock(m_CS); - for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) - { - if (itr->get() == a_Link) - { - m_Connections.erase(itr); - return; - } - } // for itr - m_Connections[] -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cNetwork: - -bool cNetwork::Connect( - const AString & a_Host, - const UInt16 a_Port, - cNetwork::cConnectCallbacksPtr a_ConnectCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks -) -{ - // Add a connection request to the queue: - cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks); - return (Conn != nullptr); -} - - - - - -cServerHandlePtr cNetwork::Listen( - const UInt16 a_Port, - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks -) -{ - return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks, a_LinkCallbacks); -} - - - - - -bool cNetwork::HostnameToIP( - const AString & a_Hostname, - cNetwork::cResolveNameCallbacksPtr a_Callbacks -) -{ - return cNetworkSingleton::Get().HostnameToIP(a_Hostname, a_Callbacks); -} - - - - - -bool cNetwork::IPToHostName( - const AString & a_IP, - cNetwork::cResolveNameCallbacksPtr a_Callbacks -) -{ - return cNetworkSingleton::Get().IPToHostName(a_IP, a_Callbacks); -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// cTCPLink: - -cTCPLink::cTCPLink(cCallbacksPtr a_Callbacks): - m_Callbacks(a_Callbacks) -{ -} - - - - - diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index cb5badaeb..0452c3b6a 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -80,7 +80,10 @@ protected: /** Creates a new link, with the specified callbacks. */ - cTCPLink(cCallbacksPtr a_Callbacks); + cTCPLink(cCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks) + { + } }; @@ -154,7 +157,7 @@ public: virtual ~cResolveNameCallbacks() {} /** Called when the hostname is successfully resolved into an IP address. - May be called multiple times if an address resolves to multiple addresses. + May be called multiple times if a name resolves to multiple addresses. a_IP may be either an IPv4 or an IPv6 address with their proper formatting. */ virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) = 0; @@ -173,7 +176,8 @@ public: Calls one the connection callbacks (success, error) when the connection is successfully established, or upon failure. The a_LinkCallbacks is passed to the newly created cTCPLink. Returns true if queueing was successful, false on failure to queue. - Note that the return value doesn't report the success of the actual connection; the connection is established asynchronously in the background. */ + Note that the return value doesn't report the success of the actual connection; the connection is established asynchronously in the background. + Implemented in TCPLinkImpl.cpp. */ static bool Connect( const AString & a_Host, const UInt16 a_Port, @@ -185,7 +189,8 @@ public: /** Opens up the specified port for incoming connections. Calls an OnAccepted callback for each incoming connection. A cTCPLink with the specified link callbacks is created for each connection. - Returns a cServerHandle that can be used to query the operation status and close the server. */ + Returns a cServerHandle that can be used to query the operation status and close the server. + Implemented in ServerHandleImpl.cpp. */ static cServerHandlePtr Listen( const UInt16 a_Port, cListenCallbacksPtr a_ListenCallbacks, @@ -196,7 +201,8 @@ public: /** Queues a DNS query to resolve the specified hostname to IP address. Calls one of the callbacks when the resolving succeeds, or when it fails. Returns true if queueing was successful, false if not. - Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */ + Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. + Implemented in HostnameLookup.cpp. */ static bool HostnameToIP( const AString & a_Hostname, cResolveNameCallbacksPtr a_Callbacks @@ -206,7 +212,8 @@ public: /** Queues a DNS query to resolve the specified IP address to a hostname. Calls one of the callbacks when the resolving succeeds, or when it fails. Returns true if queueing was successful, false if not. - Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */ + Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. + Implemented in IPLookup.cpp. */ static bool IPToHostName( const AString & a_IP, cResolveNameCallbacksPtr a_Callbacks diff --git a/src/OSSupport/NetworkSingleton.cpp b/src/OSSupport/NetworkSingleton.cpp index 552abad64..c9d9b1d81 100644 --- a/src/OSSupport/NetworkSingleton.cpp +++ b/src/OSSupport/NetworkSingleton.cpp @@ -11,51 +11,8 @@ #include #include #include - - - - - -//////////////////////////////////////////////////////////////////////////////// -// Class definitions: - -/** Holds information about an in-progress Hostname-to-IP lookup. */ -class cHostnameLookup -{ - /** The callbacks to call for resolved names / errors. */ - cNetwork::cResolveNameCallbacksPtr m_Callbacks; - - /** The hostname that was queried (needed for the callbacks). */ - AString m_Hostname; - - static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self); - -public: - cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks); -}; -typedef SharedPtr cHostnameLookupPtr; -typedef std::vector cHostnameLookupPtrs; - - - - - -/** Holds information about an in-progress IP-to-Hostname lookup. */ -class cIPLookup -{ - /** The callbacks to call for resolved names / errors. */ - cNetwork::cResolveNameCallbacksPtr m_Callbacks; - - /** The IP that was queried (needed for the callbacks). */ - AString m_IP; - - static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self); - -public: - cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks); -}; -typedef SharedPtr cIPLookupPtr; -typedef std::vector cIPLookupPtrs; +#include "IPLookup.h" +#include "HostnameLookup.h" @@ -130,6 +87,8 @@ bool cNetworkSingleton::HostnameToIP( { try { + // TODO: This has a race condition with possible memory leak: + // If a lookup finishes immediately, the constructor calls the removal before this addition cCSLock Lock(m_CS); m_HostnameLookups.push_back(std::make_shared(a_Hostname, a_Callbacks)); } @@ -150,6 +109,8 @@ bool cNetworkSingleton::IPToHostName( { try { + // TODO: This has a race condition with possible memory leak: + // If a lookup finishes immediately, the constructor calls the removal before this addition cCSLock Lock(m_CS); m_IPLookups.push_back(std::make_shared(a_IP, a_Callbacks)); } diff --git a/src/OSSupport/NetworkSingleton.h b/src/OSSupport/NetworkSingleton.h index d5a4ec279..064e075fe 100644 --- a/src/OSSupport/NetworkSingleton.h +++ b/src/OSSupport/NetworkSingleton.h @@ -4,6 +4,12 @@ // Declares the cNetworkSingleton class representing the storage for global data pertaining to network API // such as a list of all connections, all listening sockets and the LibEvent dispatch thread. +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead + + + + + #pragma once #include "Network.h" diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp new file mode 100644 index 000000000..c399c2279 --- /dev/null +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -0,0 +1,302 @@ + +// ServerHandleImpl.cpp + +// Implements the cServerHandleImpl class implementing the TCP server functionality + +#include "Globals.h" +#include "ServerHandleImpl.h" +#include "TCPLinkImpl.h" +#include "NetworkSingleton.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// Globals: + +static bool IsValidSocket(evutil_socket_t a_Socket) +{ + #ifdef _WIN32 + return (a_Socket != INVALID_SOCKET); + #else // _WIN32 + return (a_Socket >= 0); + #endif // else _WIN32 +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cServerHandleImpl: + +cServerHandleImpl::cServerHandleImpl( + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +): + m_ListenCallbacks(a_ListenCallbacks), + m_LinkCallbacks(a_LinkCallbacks), + m_ConnListener(nullptr), + m_SecondaryConnListener(nullptr), + m_IsListening(false), + m_ErrorCode(0) +{ +} + + + + + +cServerHandleImpl::~cServerHandleImpl() +{ + if (m_ConnListener != nullptr) + { + evconnlistener_free(m_ConnListener); + } + if (m_SecondaryConnListener != nullptr) + { + evconnlistener_free(m_SecondaryConnListener); + } +} + + + + + +void cServerHandleImpl::Close(void) +{ + // Stop the listener sockets: + evconnlistener_disable(m_ConnListener); + if (m_SecondaryConnListener != nullptr) + { + evconnlistener_disable(m_SecondaryConnListener); + } + m_IsListening = false; + + // Shutdown all connections: + cTCPLinkImplPtrs Conns; + { + cCSLock Lock(m_CS); + std::swap(Conns, m_Connections); + } + for (auto conn: Conns) + { + conn->Shutdown(); + } +} + + + + + +cServerHandleImplPtr cServerHandleImpl::Listen( + UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +) +{ + cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks, a_LinkCallbacks)}; + if (res->Listen(a_Port)) + { + cNetworkSingleton::Get().AddServer(res); + } + else + { + a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg); + } + return res; +} + + + + + +bool cServerHandleImpl::Listen(UInt16 a_Port) +{ + // Make sure the cNetwork internals are innitialized: + cNetworkSingleton::Get(); + + // Set up the main socket: + // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available. + bool NeedsTwoSockets = false; + int err; + evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (!IsValidSocket(MainSock)) + { + // Failed to create IPv6 socket, create an IPv4 one instead: + err = EVUTIL_SOCKET_ERROR(); + LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err)); + MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (!IsValidSocket(MainSock)) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); + return false; + } + + // Bind to all interfaces: + sockaddr_in name; + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = ntohs(a_Port); + if (bind(MainSock, reinterpret_cast(&name), sizeof(name)) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); + evutil_closesocket(MainSock); + return false; + } + } + else + { + // IPv6 socket created, switch it into "dualstack" mode: + UInt32 Zero = 0; + #ifdef _WIN32 + // WinXP doesn't support this feature, so if the setting fails, create another socket later on: + int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); + err = EVUTIL_SOCKET_ERROR(); + NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT)); + LOGD("setsockopt(IPV6_V6ONLY) returned %d, err is %d (%s). %s", + res, err, evutil_socket_error_to_string(err), + NeedsTwoSockets ? "Second socket will be created" : "Second socket not needed" + ); + #else + setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&Zero), sizeof(Zero)); + #endif + + // Bind to all interfaces: + sockaddr_in6 name; + memset(&name, 0, sizeof(name)); + name.sin6_family = AF_INET6; + name.sin6_port = ntohs(a_Port); + if (bind(MainSock, reinterpret_cast(&name), sizeof(name)) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); + evutil_closesocket(MainSock); + return false; + } + } + if (evutil_make_socket_nonblocking(MainSock) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); + evutil_closesocket(MainSock); + return false; + } + if (listen(MainSock, 0) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot listen on port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); + evutil_closesocket(MainSock); + return false; + } + m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock); + m_IsListening = true; + if (!NeedsTwoSockets) + { + return true; + } + + // If a secondary socket is required (WinXP dual-stack), create it here: + LOGD("Creating a second socket for IPv4"); + evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (!IsValidSocket(SecondSock)) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err)); + return true; // Report as success, the primary socket is working + } + + // Make the secondary socket nonblocking: + if (evutil_make_socket_nonblocking(SecondSock) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err)); + evutil_closesocket(SecondSock); + } + + // Bind to all IPv4 interfaces: + sockaddr_in name; + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = ntohs(a_Port); + if (bind(SecondSock, reinterpret_cast(&name), sizeof(name)) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(SecondSock); + return true; + } + + if (listen(SecondSock, 0) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(SecondSock); + return false; + } + + m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); + return true; +} + + + + + +void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self) +{ + // Cast to true self: + cServerHandleImpl * Self = reinterpret_cast(a_Self); + ASSERT(Self != nullptr); + + // Create a new cTCPLink for the incoming connection: + cTCPLinkImplPtr Link = std::make_shared(a_Socket, Self->m_LinkCallbacks, Self, a_Addr, a_Len); + { + cCSLock Lock(Self->m_CS); + Self->m_Connections.push_back(Link); + } // Lock(m_CS) + + // Call the OnAccepted callback: + Self->m_ListenCallbacks->OnAccepted(*Link); +} + + + + + +void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link) +{ + cCSLock Lock(m_CS); + for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + if (itr->get() == a_Link) + { + m_Connections.erase(itr); + return; + } + } // for itr - m_Connections[] +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API: + +cServerHandlePtr cNetwork::Listen( + const UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +) +{ + return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks, a_LinkCallbacks); +} + + + + + diff --git a/src/OSSupport/ServerHandleImpl.h b/src/OSSupport/ServerHandleImpl.h new file mode 100644 index 000000000..b325a0f37 --- /dev/null +++ b/src/OSSupport/ServerHandleImpl.h @@ -0,0 +1,109 @@ + +// ServerHandleImpl.h + +// Declares the cServerHandleImpl class implementing the TCP server functionality + +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead + + + + + +#pragma once + +#include "Network.h" +#include +#include "CriticalSection.h" + + + + + +// fwd: +class cTCPLinkImpl; +typedef SharedPtr cTCPLinkImplPtr; +typedef std::vector cTCPLinkImplPtrs; +class cServerHandleImpl; +typedef SharedPtr cServerHandleImplPtr; +typedef std::vector cServerHandleImplPtrs; + + + + + +class cServerHandleImpl: + public cServerHandle +{ + typedef cServerHandle super; + friend class cTCPLinkImpl; + +public: + /** Closes the server, dropping all the connections. */ + ~cServerHandleImpl(); + + /** Creates a new server instance listening on the specified port. + Both IPv4 and IPv6 interfaces are used, if possible. + Always returns a server instance; in the event of a failure, the instance holds the error details. Use IsListening() to query success. */ + static cServerHandleImplPtr Listen( + UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks + ); + + // cServerHandle overrides: + virtual void Close(void) override; + virtual bool IsListening(void) const override { return m_IsListening; } + +protected: + /** The callbacks used to notify about incoming connections. */ + cNetwork::cListenCallbacksPtr m_ListenCallbacks; + + /** The callbacks used to create new cTCPLink instances for incoming connections. */ + cTCPLink::cCallbacksPtr m_LinkCallbacks; + + /** The LibEvent handle representing the main listening socket. */ + evconnlistener * m_ConnListener; + + /** The LibEvent handle representing the secondary listening socket (only when side-by-side listening is needed, such as WinXP). */ + evconnlistener * m_SecondaryConnListener; + + /** Set to true when the server is initialized successfully and is listening for incoming connections. */ + bool m_IsListening; + + /** Container for all currently active connections on this server. */ + cTCPLinkImplPtrs m_Connections; + + /** Mutex protecting m_Connections againt multithreaded access. */ + cCriticalSection m_CS; + + /** Contains the error code for the failure to listen. Only valid for non-listening instances. */ + int m_ErrorCode; + + /** Contains the error message for the failure to listen. Only valid for non-listening instances. */ + AString m_ErrorMsg; + + + + /** Creates a new instance with the specified callbacks. + Initializes the internals, but doesn't start listening yet. */ + cServerHandleImpl( + cNetwork::cListenCallbacksPtr a_ListenCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks + ); + + /** Starts listening on the specified port. + Returns true if successful, false on failure. On failure, sets m_ErrorCode and m_ErrorMsg. */ + bool Listen(UInt16 a_Port); + + /** The callback called by LibEvent upon incoming connection. */ + static void Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self); + + /** Removes the specified link from m_Connections. + Called by cTCPLinkImpl when the link is terminated. */ + void RemoveLink(const cTCPLinkImpl * a_Link); +}; + + + + + diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp new file mode 100644 index 000000000..bcacc0569 --- /dev/null +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -0,0 +1,314 @@ + +// TCPLinkImpl.cpp + +// Implements the cTCPLinkImpl class implementing the TCP link functionality + +#include "Globals.h" +#include "TCPLinkImpl.h" +#include "NetworkSingleton.h" +#include "ServerHandleImpl.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cTCPLinkImpl: + +cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): + super(a_LinkCallbacks), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE)), + m_Server(nullptr) +{ + // Create the LibEvent handle, but don't assign a socket to it yet (will be assigned within Connect() method): + bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); + bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); +} + + + + + +cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen): + super(a_LinkCallbacks), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE)), + m_Server(a_Server) +{ + // Update the endpoint addresses: + UpdateLocalAddress(); + UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort); + + // Create the LibEvent handle: + bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); + bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); +} + + + + + +cTCPLinkImpl::~cTCPLinkImpl() +{ + bufferevent_free(m_BufferEvent); +} + + + + + +cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) +{ + ASSERT(a_LinkCallbacks != nullptr); + ASSERT(a_ConnectCallbacks != nullptr); + + // Create a new link: + cTCPLinkImplPtr res{new cTCPLinkImpl(a_LinkCallbacks)}; // Cannot use std::make_shared here, constructor is not accessible + res->m_ConnectCallbacks = a_ConnectCallbacks; + cNetworkSingleton::Get().AddLink(res); + + // If a_Host is an IP address, schedule a connection immediately: + sockaddr_storage sa; + int salen = static_cast(sizeof(sa)); + if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast(&sa), &salen) == 0) + { + // Insert the correct port: + if (sa.ss_family == AF_INET6) + { + reinterpret_cast(&sa)->sin6_port = htons(a_Port); + } + else + { + reinterpret_cast(&sa)->sin_port = htons(a_Port); + } + + // Queue the connect request: + if (bufferevent_socket_connect(res->m_BufferEvent, reinterpret_cast(&sa), salen) == 0) + { + // Success + return res; + } + // Failure + cNetworkSingleton::Get().RemoveLink(res.get()); + return nullptr; + } + + // a_Host is a hostname, connect after a lookup: + if (bufferevent_socket_connect_hostname(res->m_BufferEvent, cNetworkSingleton::Get().GetDNSBase(), AF_UNSPEC, a_Host.c_str(), a_Port) == 0) + { + // Success + return res; + } + // Failure + cNetworkSingleton::Get().RemoveLink(res.get()); + return nullptr; +} + + + + + +bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) +{ + return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0); +} + + + + + +void cTCPLinkImpl::Shutdown(void) +{ + #ifdef _WIN32 + shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND); + #else + shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR); + #endif + bufferevent_disable(m_BufferEvent, EV_WRITE); +} + + + + + +void cTCPLinkImpl::Close(void) +{ + // Disable all events on the socket, but keep it alive: + bufferevent_disable(m_BufferEvent, EV_READ | EV_WRITE); + if (m_Server == nullptr) + { + cNetworkSingleton::Get().RemoveLink(this); + } + else + { + m_Server->RemoveLink(this); + } +} + + + + + + +void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self) +{ + ASSERT(a_Self != nullptr); + cTCPLinkImpl * Self = static_cast(a_Self); + ASSERT(Self->m_Callbacks != nullptr); + + // Read all the incoming data, in 1024-byte chunks: + char data[1024]; + size_t length; + while ((length = bufferevent_read(a_BufferEvent, data, sizeof(data))) > 0) + { + Self->m_Callbacks->OnReceivedData(*Self, data, length); + } +} + + + + + +void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self) +{ + ASSERT(a_Self != nullptr); + cTCPLinkImpl * Self = static_cast(a_Self); + + // If an error is reported, call the error callback: + if (a_What & BEV_EVENT_ERROR) + { + // Choose the proper callback to call based on whether we were waiting for connection or not: + if (Self->m_ConnectCallbacks != nullptr) + { + Self->m_ConnectCallbacks->OnError(EVUTIL_SOCKET_ERROR()); + } + else + { + Self->m_Callbacks->OnError(*Self, EVUTIL_SOCKET_ERROR()); + if (Self->m_Server == nullptr) + { + cNetworkSingleton::Get().RemoveLink(Self); + } + else + { + Self->m_Server->RemoveLink(Self); + } + } + return; + } + + // Pending connection succeeded, call the connection callback: + if (a_What & BEV_EVENT_CONNECTED) + { + if (Self->m_ConnectCallbacks != nullptr) + { + Self->m_ConnectCallbacks->OnSuccess(*Self); + // Reset the connect callbacks so that later errors get reported through the link callbacks: + Self->m_ConnectCallbacks.reset(); + return; + } + Self->UpdateLocalAddress(); + Self->UpdateRemoteAddress(); + } + + // If the connection has been closed, call the link callback and remove the connection: + if (a_What & BEV_EVENT_EOF) + { + Self->m_Callbacks->OnRemoteClosed(*Self); + if (Self->m_Server != nullptr) + { + Self->m_Server->RemoveLink(Self); + } + else + { + cNetworkSingleton::Get().RemoveLink(Self); + } + return; + } + + // Unknown event, report it: + LOGWARNING("cTCPLinkImpl: Unhandled LibEvent event %d (0x%x)", a_What, a_What); + ASSERT(!"cTCPLinkImpl: Unhandled LibEvent event"); +} + + + + + +void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AString & a_IP, UInt16 & a_Port) +{ + // Based on the family specified in the address, use the correct datastructure to convert to IP string: + char IP[128]; + switch (a_Address->sa_family) + { + case AF_INET: // IPv4: + { + const sockaddr_in * sin = reinterpret_cast(a_Address); + evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); + a_Port = ntohs(sin->sin_port); + break; + } + case AF_INET6: // IPv6 + { + const sockaddr_in6 * sin = reinterpret_cast(a_Address); + evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); + a_Port = ntohs(sin->sin6_port); + break; + } + + default: + { + LOGWARNING("%s: Unknown socket address family: %d", __FUNCTION__, a_Address->sa_family); + ASSERT(!"Unknown socket address family"); + break; + } + } + a_IP.assign(IP); +} + + + + + +void cTCPLinkImpl::UpdateLocalAddress(void) +{ + sockaddr_storage sa; + socklen_t salen = static_cast(sizeof(sa)); + getsockname(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); + UpdateAddress(reinterpret_cast(&sa), salen, m_LocalIP, m_LocalPort); +} + + + + + +void cTCPLinkImpl::UpdateRemoteAddress(void) +{ + sockaddr_storage sa; + socklen_t salen = static_cast(sizeof(sa)); + getpeername(bufferevent_getfd(m_BufferEvent), reinterpret_cast(&sa), &salen); + UpdateAddress(reinterpret_cast(&sa), salen, m_RemoteIP, m_RemotePort); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API: + +bool cNetwork::Connect( + const AString & a_Host, + const UInt16 a_Port, + cNetwork::cConnectCallbacksPtr a_ConnectCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +) +{ + // Add a connection request to the queue: + cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks); + return (Conn != nullptr); +} + + + + + diff --git a/src/OSSupport/TCPLinkImpl.h b/src/OSSupport/TCPLinkImpl.h new file mode 100644 index 000000000..3c60b1ad8 --- /dev/null +++ b/src/OSSupport/TCPLinkImpl.h @@ -0,0 +1,109 @@ + +// TCPLinkImpl.h + +// Declares the cTCPLinkImpl class implementing the TCP link functionality + +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead + + + + + +#pragma once + +#include "Network.h" +#include +#include + + + + + +// fwd: +class cServerHandleImpl; +class cTCPLinkImpl; +typedef SharedPtr cTCPLinkImplPtr; +typedef std::vector cTCPLinkImplPtrs; + + + + + +class cTCPLinkImpl: + public cTCPLink +{ + typedef cTCPLink super; + +public: + /** Creates a new link based on the given socket. + Used for connections accepted in a server using cNetwork::Listen(). + a_Address and a_AddrLen describe the remote peer that has connected. */ + cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen); + + /** Destroys the LibEvent handle representing the link. */ + ~cTCPLinkImpl(); + + /** Queues a connection request to the specified host. + a_ConnectCallbacks must be valid. + Returns a link that has the connection request queued, or NULL for failure. */ + static cTCPLinkImplPtr Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks); + + // cTCPLink overrides: + virtual bool Send(const void * a_Data, size_t a_Length) override; + virtual AString GetLocalIP(void) const override { return m_LocalIP; } + virtual UInt16 GetLocalPort(void) const override { return m_LocalPort; } + virtual AString GetRemoteIP(void) const override { return m_RemoteIP; } + virtual UInt16 GetRemotePort(void) const override { return m_RemotePort; } + virtual void Shutdown(void) override; + virtual void Close(void) override; + +protected: + + /** Callbacks to call when the connection is established. + May be NULL if not used. Only used for outgoing connections (cNetwork::Connect()). */ + cNetwork::cConnectCallbacksPtr m_ConnectCallbacks; + + /** The LibEvent handle representing this connection. */ + bufferevent * m_BufferEvent; + + /** The server handle that has created this link. + Only valid for incoming connections, NULL for outgoing connections. */ + cServerHandleImpl * m_Server; + + /** The IP address of the local endpoint. Valid only after the socket has been connected. */ + AString m_LocalIP; + + /** The port of the local endpoint. Valid only after the socket has been connected. */ + UInt16 m_LocalPort; + + /** The IP address of the remote endpoint. Valid only after the socket has been connected. */ + AString m_RemoteIP; + + /** The port of the remote endpoint. Valid only after the socket has been connected. */ + UInt16 m_RemotePort; + + + /** Creates a new link to be queued to connect to a specified host:port. + Used for outgoing connections created using cNetwork::Connect(). + To be used only by the Connect() factory function. */ + cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks); + + /** Callback that LibEvent calls when there's data available from the remote peer. */ + static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self); + + /** Callback that LibEvent calls when there's a non-data-related event on the socket. */ + static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self); + + /** Sets a_IP and a_Port to values read from a_Address, based on the correct address family. */ + static void UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AString & a_IP, UInt16 & a_Port); + + /** Updates m_LocalIP and m_LocalPort based on the metadata read from the socket. */ + void UpdateLocalAddress(void); + + /** Updates m_RemoteIP and m_RemotePort based on the metadata read from the socket. */ + void UpdateRemoteAddress(void); +}; + + + + -- cgit v1.2.3 From d4682463a1d503c349ac95e275b11d67d402268c Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 18 Jan 2015 12:35:02 +0100 Subject: cNetwork: Fixed race conditions with lookups; proper shutdown. --- src/OSSupport/HostnameLookup.cpp | 25 ++++++++++--- src/OSSupport/HostnameLookup.h | 12 ++++-- src/OSSupport/IPLookup.cpp | 32 +++++++++++++--- src/OSSupport/IPLookup.h | 15 ++++++-- src/OSSupport/NetworkSingleton.cpp | 77 +++++++++++++++++++------------------- src/OSSupport/NetworkSingleton.h | 44 +++++++++------------- 6 files changed, 123 insertions(+), 82 deletions(-) (limited to 'src') diff --git a/src/OSSupport/HostnameLookup.cpp b/src/OSSupport/HostnameLookup.cpp index 9e35f7163..860e0d88f 100644 --- a/src/OSSupport/HostnameLookup.cpp +++ b/src/OSSupport/HostnameLookup.cpp @@ -15,10 +15,22 @@ //////////////////////////////////////////////////////////////////////////////// // cHostnameLookup: -cHostnameLookup::cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks): - m_Callbacks(a_Callbacks), - m_Hostname(a_Hostname) +cHostnameLookup::cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks) { +} + + + + + +void cHostnameLookup::Lookup(const AString & a_Hostname) +{ + // Store the hostname for the callback: + m_Hostname = a_Hostname; + + // Start the lookup: + // Note that we don't have to store the LibEvent lookup handle, LibEvent will free it on its own. evutil_addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_protocol = IPPROTO_TCP; @@ -79,7 +91,7 @@ void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a // If only unsupported families were reported, call the Error handler: if (!HasResolved) { - Self->m_Callbacks->OnError(1); + Self->m_Callbacks->OnError(DNS_ERR_NODATA); } else { @@ -101,7 +113,10 @@ bool cNetwork::HostnameToIP( cNetwork::cResolveNameCallbacksPtr a_Callbacks ) { - return cNetworkSingleton::Get().HostnameToIP(a_Hostname, a_Callbacks); + auto Lookup = std::make_shared(a_Callbacks); + cNetworkSingleton::Get().AddHostnameLookup(Lookup); + Lookup->Lookup(a_Hostname); + return true; } diff --git a/src/OSSupport/HostnameLookup.h b/src/OSSupport/HostnameLookup.h index 98f48b933..d69f24707 100644 --- a/src/OSSupport/HostnameLookup.h +++ b/src/OSSupport/HostnameLookup.h @@ -21,6 +21,15 @@ /** Holds information about an in-progress Hostname-to-IP lookup. */ class cHostnameLookup { +public: + /** Creates the lookup object. Doesn't start the lookup yet. */ + cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks); + + /** Starts the lookup. */ + void Lookup(const AString & a_Hostname); + +protected: + /** The callbacks to call for resolved names / errors. */ cNetwork::cResolveNameCallbacksPtr m_Callbacks; @@ -28,9 +37,6 @@ class cHostnameLookup AString m_Hostname; static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self); - -public: - cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks); }; typedef SharedPtr cHostnameLookupPtr; typedef std::vector cHostnameLookupPtrs; diff --git a/src/OSSupport/IPLookup.cpp b/src/OSSupport/IPLookup.cpp index bbcfbfe40..a52b6480b 100644 --- a/src/OSSupport/IPLookup.cpp +++ b/src/OSSupport/IPLookup.cpp @@ -15,14 +15,31 @@ //////////////////////////////////////////////////////////////////////////////// // cIPLookup: -cIPLookup::cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks): - m_Callbacks(a_Callbacks), - m_IP(a_IP) +cIPLookup::cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks) { + ASSERT(a_Callbacks != nullptr); +} + + + + + +bool cIPLookup::Lookup(const AString & a_IP) +{ + // Parse the IP address string into a sockaddr structure: + m_IP = a_IP; sockaddr_storage sa; int salen = static_cast(sizeof(sa)); memset(&sa, 0, sizeof(sa)); - evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast(&sa), &salen); + if (evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast(&sa), &salen) != 0) + { + LOGD("Failed to parse IP address \"%s\".", a_IP.c_str()); + return false; + } + + // Call the proper resolver based on the address family: + // Note that there's no need to store the evdns_request handle returned, LibEvent frees it on its own. switch (sa.ss_family) { case AF_INET: @@ -41,9 +58,10 @@ cIPLookup::cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_ { LOGWARNING("%s: Unknown address family: %d", __FUNCTION__, sa.ss_family); ASSERT(!"Unknown address family"); - break; + return false; } } // switch (address family) + return true; } @@ -83,7 +101,9 @@ bool cNetwork::IPToHostName( cNetwork::cResolveNameCallbacksPtr a_Callbacks ) { - return cNetworkSingleton::Get().IPToHostName(a_IP, a_Callbacks); + auto res = std::make_shared(a_Callbacks); + cNetworkSingleton::Get().AddIPLookup(res); + return res->Lookup(a_IP); } diff --git a/src/OSSupport/IPLookup.h b/src/OSSupport/IPLookup.h index f39b955aa..af878cbf1 100644 --- a/src/OSSupport/IPLookup.h +++ b/src/OSSupport/IPLookup.h @@ -20,16 +20,25 @@ /** Holds information about an in-progress IP-to-Hostname lookup. */ class cIPLookup { +public: + /** Creates the lookup object. Doesn't start the lookup yet. */ + cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks); + + /** Starts the lookup. + Returns true if lookup started successfully, false on failure (invalid IP format etc.) */ + bool Lookup(const AString & a_IP); + +protected: + /** The callbacks to call for resolved names / errors. */ cNetwork::cResolveNameCallbacksPtr m_Callbacks; /** The IP that was queried (needed for the callbacks). */ AString m_IP; - static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self); -public: - cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks); + /** Callback that is called by LibEvent when there's an event for the request. */ + static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self); }; typedef SharedPtr cIPLookupPtr; typedef std::vector cIPLookupPtrs; diff --git a/src/OSSupport/NetworkSingleton.cpp b/src/OSSupport/NetworkSingleton.cpp index c9d9b1d81..8c38fb4eb 100644 --- a/src/OSSupport/NetworkSingleton.cpp +++ b/src/OSSupport/NetworkSingleton.cpp @@ -70,60 +70,40 @@ cNetworkSingleton::cNetworkSingleton(void) -cNetworkSingleton & cNetworkSingleton::Get(void) +cNetworkSingleton::~cNetworkSingleton() { - static cNetworkSingleton Instance; - return Instance; -} - + // Wait for the LibEvent event loop to terminate: + event_base_loopbreak(m_EventBase); + m_EventLoopTerminated.Wait(); - - - -bool cNetworkSingleton::HostnameToIP( - const AString & a_Hostname, - cNetwork::cResolveNameCallbacksPtr a_Callbacks -) -{ - try + // Remove all objects: { - // TODO: This has a race condition with possible memory leak: - // If a lookup finishes immediately, the constructor calls the removal before this addition cCSLock Lock(m_CS); - m_HostnameLookups.push_back(std::make_shared(a_Hostname, a_Callbacks)); + m_Connections.clear(); + m_Servers.clear(); + m_HostnameLookups.clear(); + m_IPLookups.clear(); } - catch (...) - { - return false; - } - return true; + + // Free the underlying LibEvent objects: + evdns_base_free(m_DNSBase, true); + event_base_free(m_EventBase); } -bool cNetworkSingleton::IPToHostName( - const AString & a_IP, - cNetwork::cResolveNameCallbacksPtr a_Callbacks -) + +cNetworkSingleton & cNetworkSingleton::Get(void) { - try - { - // TODO: This has a race condition with possible memory leak: - // If a lookup finishes immediately, the constructor calls the removal before this addition - cCSLock Lock(m_CS); - m_IPLookups.push_back(std::make_shared(a_IP, a_Callbacks)); - } - catch (...) - { - return false; - } - return true; + static cNetworkSingleton Instance; + return Instance; } + void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) { switch (a_Severity) @@ -147,6 +127,17 @@ void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) { event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY); + a_Self->m_EventLoopTerminated.Set(); +} + + + + + +void cNetworkSingleton::AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup) +{ + cCSLock Lock(m_CS); + m_HostnameLookups.push_back(a_HostnameLookup); } @@ -170,6 +161,16 @@ void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameL +void cNetworkSingleton::AddIPLookup(cIPLookupPtr a_IPLookup) +{ + cCSLock Lock(m_CS); + m_IPLookups.push_back(a_IPLookup); +} + + + + + void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) { cCSLock Lock(m_CS); diff --git a/src/OSSupport/NetworkSingleton.h b/src/OSSupport/NetworkSingleton.h index 064e075fe..1d26fc8f4 100644 --- a/src/OSSupport/NetworkSingleton.h +++ b/src/OSSupport/NetworkSingleton.h @@ -14,6 +14,7 @@ #include "Network.h" #include "CriticalSection.h" +#include "Event.h" @@ -22,16 +23,16 @@ // fwd: struct event_base; struct evdns_base; -class cServerHandleImpl; class cTCPLinkImpl; -class cHostnameLookup; -class cIPLookup; typedef SharedPtr cTCPLinkImplPtr; typedef std::vector cTCPLinkImplPtrs; +class cServerHandleImpl; typedef SharedPtr cServerHandleImplPtr; typedef std::vector cServerHandleImplPtrs; +class cHostnameLookup; typedef SharedPtr cHostnameLookupPtr; typedef std::vector cHostnameLookupPtrs; +class cIPLookup; typedef SharedPtr cIPLookupPtr; typedef std::vector cIPLookupPtrs; @@ -42,43 +43,29 @@ typedef std::vector cIPLookupPtrs; class cNetworkSingleton { public: + ~cNetworkSingleton(); + /** Returns the singleton instance of this class */ static cNetworkSingleton & Get(void); - - // The following functions are implementations for the cNetwork class - - /** Queues a DNS query to resolve the specified hostname to IP address. - Calls one of the callbacks when the resolving succeeds, or when it fails. - Returns true if queueing was successful, false if not. - Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. - TODO: Move this out into a separate file with cHostnameLookup. */ - bool HostnameToIP( - const AString & a_Hostname, - cNetwork::cResolveNameCallbacksPtr a_Callbacks - ); - - - /** Queues a DNS query to resolve the specified IP address to a hostname. - Calls one of the callbacks when the resolving succeeds, or when it fails. - Returns true if queueing was successful, false if not. - Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. - TODO: Move this out into a separate file with cIPLookup. */ - bool IPToHostName( - const AString & a_IP, - cNetwork::cResolveNameCallbacksPtr a_Callbacks - ); - /** Returns the main LibEvent handle for event registering. */ event_base * GetEventBase(void) { return m_EventBase; } /** Returns the LibEvent handle for DNS lookups. */ evdns_base * GetDNSBase(void) { return m_DNSBase; } + /** Adds the specified hostname lookup to m_HostnameLookups. + Used by the underlying lookup implementation when a new lookup is initiated. */ + void AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup); + /** Removes the specified hostname lookup from m_HostnameLookups. Used by the underlying lookup implementation when the lookup is finished. */ void RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup); + /** Adds the specified IP lookup to M_IPLookups. + Used by the underlying lookup implementation when a new lookup is initiated. */ + void AddIPLookup(cIPLookupPtr a_IPLookup); + /** Removes the specified IP lookup from m_IPLookups. Used by the underlying lookup implementation when the lookup is finished. */ void RemoveIPLookup(const cIPLookup * a_IPLookup); @@ -123,6 +110,9 @@ protected: /** Mutex protecting all containers against multithreaded access. */ cCriticalSection m_CS; + /** Event that gets signalled when the event loop terminates. */ + cEvent m_EventLoopTerminated; + /** Initializes the LibEvent internals. */ cNetworkSingleton(void); -- cgit v1.2.3 From 00253403b3850911833638a960f3e7d0ea46e1ce Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 18 Jan 2015 15:40:39 +0100 Subject: cTCPLinkImpl: Fixed type conversion warning. --- src/OSSupport/ServerHandleImpl.cpp | 2 +- src/OSSupport/TCPLinkImpl.cpp | 4 ++-- src/OSSupport/TCPLinkImpl.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index c399c2279..866aa01be 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -253,7 +253,7 @@ void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_ ASSERT(Self != nullptr); // Create a new cTCPLink for the incoming connection: - cTCPLinkImplPtr Link = std::make_shared(a_Socket, Self->m_LinkCallbacks, Self, a_Addr, a_Len); + cTCPLinkImplPtr Link = std::make_shared(a_Socket, Self->m_LinkCallbacks, Self, a_Addr, static_cast(a_Len)); { cCSLock Lock(Self->m_CS); Self->m_Connections.push_back(Link); diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index bcacc0569..3c0775367 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -29,7 +29,7 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): -cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen): +cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, socklen_t a_AddrLen): super(a_LinkCallbacks), m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE)), m_Server(a_Server) @@ -234,7 +234,7 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void -void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AString & a_IP, UInt16 & a_Port) +void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, socklen_t a_AddrLen, AString & a_IP, UInt16 & a_Port) { // Based on the family specified in the address, use the correct datastructure to convert to IP string: char IP[128]; diff --git a/src/OSSupport/TCPLinkImpl.h b/src/OSSupport/TCPLinkImpl.h index 3c60b1ad8..edd295fe2 100644 --- a/src/OSSupport/TCPLinkImpl.h +++ b/src/OSSupport/TCPLinkImpl.h @@ -38,7 +38,7 @@ public: /** Creates a new link based on the given socket. Used for connections accepted in a server using cNetwork::Listen(). a_Address and a_AddrLen describe the remote peer that has connected. */ - cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, int a_AddrLen); + cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, socklen_t a_AddrLen); /** Destroys the LibEvent handle representing the link. */ ~cTCPLinkImpl(); @@ -95,7 +95,7 @@ protected: static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self); /** Sets a_IP and a_Port to values read from a_Address, based on the correct address family. */ - static void UpdateAddress(const sockaddr * a_Address, int a_AddrLen, AString & a_IP, UInt16 & a_Port); + static void UpdateAddress(const sockaddr * a_Address, socklen_t a_AddrLen, AString & a_IP, UInt16 & a_Port); /** Updates m_LocalIP and m_LocalPort based on the metadata read from the socket. */ void UpdateLocalAddress(void); -- cgit v1.2.3 From 64855ed340e76779b99f37fbc866a7f5952e11db Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 20 Jan 2015 11:27:05 +0100 Subject: cNetwork: Added error message to error callbacks. --- src/OSSupport/HostnameLookup.cpp | 4 ++-- src/OSSupport/IPLookup.cpp | 2 +- src/OSSupport/Network.h | 6 +++--- src/OSSupport/ServerHandleImpl.cpp | 11 ++++++----- src/OSSupport/TCPLinkImpl.cpp | 10 ++++++++-- 5 files changed, 20 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/OSSupport/HostnameLookup.cpp b/src/OSSupport/HostnameLookup.cpp index 860e0d88f..3a2997ffd 100644 --- a/src/OSSupport/HostnameLookup.cpp +++ b/src/OSSupport/HostnameLookup.cpp @@ -53,7 +53,7 @@ void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a // If an error has occurred, notify the error callback: if (a_ErrCode != 0) { - Self->m_Callbacks->OnError(a_ErrCode); + Self->m_Callbacks->OnError(a_ErrCode, evutil_socket_error_to_string(a_ErrCode)); cNetworkSingleton::Get().RemoveHostnameLookup(Self); return; } @@ -91,7 +91,7 @@ void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a // If only unsupported families were reported, call the Error handler: if (!HasResolved) { - Self->m_Callbacks->OnError(DNS_ERR_NODATA); + Self->m_Callbacks->OnError(DNS_ERR_NODATA, "The name does not resolve to any known address."); } else { diff --git a/src/OSSupport/IPLookup.cpp b/src/OSSupport/IPLookup.cpp index a52b6480b..8cdc5132d 100644 --- a/src/OSSupport/IPLookup.cpp +++ b/src/OSSupport/IPLookup.cpp @@ -78,7 +78,7 @@ void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void if ((a_Result != 0) || (a_Addresses == nullptr)) { // An error has occurred, notify the error callback: - Self->m_Callbacks->OnError(a_Result); + Self->m_Callbacks->OnError(a_Result, evutil_socket_error_to_string(a_Result)); } else { diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 0452c3b6a..3ed9885ec 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -34,7 +34,7 @@ public: virtual void OnRemoteClosed(cTCPLink & a_Link) = 0; /** Called when an error is detected on the connection. */ - virtual void OnError(cTCPLink & a_Link, int a_ErrorCode) = 0; + virtual void OnError(cTCPLink & a_Link, int a_ErrorCode, const AString & a_ErrorMsg) = 0; }; typedef SharedPtr cCallbacksPtr; @@ -127,7 +127,7 @@ public: virtual void OnSuccess(cTCPLink & a_Link) = 0; /** Called when the Connect call fails. */ - virtual void OnError(int a_ErrorCode) = 0; + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; }; typedef SharedPtr cConnectCallbacksPtr; @@ -163,7 +163,7 @@ public: /** Called when an error is encountered while resolving. If an error is reported, the OnFinished() callback is not called. */ - virtual void OnError(int a_ErrorCode) = 0; + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; /** Called when all the addresses resolved have been reported via the OnNameResolved() callback. Only called if there was no error reported. */ diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index 866aa01be..d81d4ba46 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -173,7 +173,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) if (bind(MainSock, reinterpret_cast(&name), sizeof(name)) != 0) { m_ErrorCode = EVUTIL_SOCKET_ERROR(); - Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); + Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)); evutil_closesocket(MainSock); return false; } @@ -181,14 +181,14 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) if (evutil_make_socket_nonblocking(MainSock) != 0) { m_ErrorCode = EVUTIL_SOCKET_ERROR(); - Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); + Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)); evutil_closesocket(MainSock); return false; } if (listen(MainSock, 0) != 0) { m_ErrorCode = EVUTIL_SOCKET_ERROR(); - Printf(m_ErrorMsg, "Cannot listen on port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); + Printf(m_ErrorMsg, "Cannot listen on port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)); evutil_closesocket(MainSock); return false; } @@ -215,6 +215,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) err = EVUTIL_SOCKET_ERROR(); LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err)); evutil_closesocket(SecondSock); + return true; // Report as success, the primary socket is working } // Bind to all IPv4 interfaces: @@ -227,7 +228,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) err = EVUTIL_SOCKET_ERROR(); LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); evutil_closesocket(SecondSock); - return true; + return true; // Report as success, the primary socket is working } if (listen(SecondSock, 0) != 0) @@ -235,7 +236,7 @@ bool cServerHandleImpl::Listen(UInt16 a_Port) err = EVUTIL_SOCKET_ERROR(); LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); evutil_closesocket(SecondSock); - return false; + return true; // Report as success, the primary socket is working } m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index 3c0775367..f87f68280 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -177,13 +177,19 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void if (a_What & BEV_EVENT_ERROR) { // Choose the proper callback to call based on whether we were waiting for connection or not: + int err = EVUTIL_SOCKET_ERROR(); if (Self->m_ConnectCallbacks != nullptr) { - Self->m_ConnectCallbacks->OnError(EVUTIL_SOCKET_ERROR()); + if (err == 0) + { + // This could be a DNS failure + err = bufferevent_socket_get_dns_error(a_BufferEvent); + } + Self->m_ConnectCallbacks->OnError(err, evutil_socket_error_to_string(err)); } else { - Self->m_Callbacks->OnError(*Self, EVUTIL_SOCKET_ERROR()); + Self->m_Callbacks->OnError(*Self, err, evutil_socket_error_to_string(err)); if (Self->m_Server == nullptr) { cNetworkSingleton::Get().RemoveLink(Self); -- cgit v1.2.3 From 5b4c5cf2befebb78ff2b16955c244e79841648a7 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 21 Jan 2015 21:12:11 +0100 Subject: cNetwork: Changed listening API. The link-callbacks for each new accepted link are now received from the OnIncomingConnection listen-callback. --- src/OSSupport/Network.h | 18 +++++++++----- src/OSSupport/ServerHandleImpl.cpp | 49 ++++++++++++++++++++++++++++---------- src/OSSupport/ServerHandleImpl.h | 11 ++------- src/OSSupport/TCPLinkImpl.cpp | 2 +- 4 files changed, 51 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 3ed9885ec..b9ca377cb 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -139,8 +139,15 @@ public: // Force a virtual destructor for all descendants: virtual ~cListenCallbacks() {} - /** Called when the TCP server created with Listen() accepts an incoming connection. - Provides the newly created Link that can be used for communication. */ + /** Called when the TCP server created with Listen() receives a new incoming connection. + Returns the link callbacks that the server should use for the newly created link. + If a nullptr is returned, the connection is dropped immediately; + otherwise a new cTCPLink instance is created and OnAccepted() is called. */ + virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) = 0; + + /** Called when the TCP server created with Listen() creates a new link for an incoming connection. + Provides the newly created Link that can be used for communication. + Called right after a successful OnIncomingConnection(). */ virtual void OnAccepted(cTCPLink & a_Link) = 0; /** Called when the socket fails to listen on the specified port. */ @@ -180,7 +187,7 @@ public: Implemented in TCPLinkImpl.cpp. */ static bool Connect( const AString & a_Host, - const UInt16 a_Port, + UInt16 a_Port, cConnectCallbacksPtr a_ConnectCallbacks, cTCPLink::cCallbacksPtr a_LinkCallbacks ); @@ -192,9 +199,8 @@ public: Returns a cServerHandle that can be used to query the operation status and close the server. Implemented in ServerHandleImpl.cpp. */ static cServerHandlePtr Listen( - const UInt16 a_Port, - cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks + UInt16 a_Port, + cListenCallbacksPtr a_ListenCallbacks ); diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index d81d4ba46..82cbecef2 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -31,12 +31,8 @@ static bool IsValidSocket(evutil_socket_t a_Socket) //////////////////////////////////////////////////////////////////////////////// // cServerHandleImpl: -cServerHandleImpl::cServerHandleImpl( - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks -): +cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks): m_ListenCallbacks(a_ListenCallbacks), - m_LinkCallbacks(a_LinkCallbacks), m_ConnListener(nullptr), m_SecondaryConnListener(nullptr), m_IsListening(false), @@ -92,11 +88,10 @@ void cServerHandleImpl::Close(void) cServerHandleImplPtr cServerHandleImpl::Listen( UInt16 a_Port, - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks + cNetwork::cListenCallbacksPtr a_ListenCallbacks ) { - cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks, a_LinkCallbacks)}; + cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks)}; if (res->Listen(a_Port)) { cNetworkSingleton::Get().AddServer(res); @@ -253,8 +248,37 @@ void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_ cServerHandleImpl * Self = reinterpret_cast(a_Self); ASSERT(Self != nullptr); + // Get the textual IP address and port number out of a_Addr: + char IPAddress[128]; + evutil_inet_ntop(a_Addr->sa_family, a_Addr->sa_data, IPAddress, ARRAYCOUNT(IPAddress)); + UInt16 Port = 0; + switch (a_Addr->sa_family) + { + case AF_INET: + { + sockaddr_in * sin = reinterpret_cast(a_Addr); + Port = ntohs(sin->sin_port); + break; + } + case AF_INET6: + { + sockaddr_in6 * sin6 = reinterpret_cast(a_Addr); + Port = ntohs(sin6->sin6_port); + break; + } + } + + // Call the OnIncomingConnection callback to get the link callbacks to use: + cTCPLink::cCallbacksPtr LinkCallbacks = Self->m_ListenCallbacks->OnIncomingConnection(IPAddress, Port); + if (LinkCallbacks == nullptr) + { + // Drop the connection: + evutil_closesocket(a_Socket); + return; + } + // Create a new cTCPLink for the incoming connection: - cTCPLinkImplPtr Link = std::make_shared(a_Socket, Self->m_LinkCallbacks, Self, a_Addr, static_cast(a_Len)); + cTCPLinkImplPtr Link = std::make_shared(a_Socket, LinkCallbacks, Self, a_Addr, static_cast(a_Len)); { cCSLock Lock(Self->m_CS); Self->m_Connections.push_back(Link); @@ -289,12 +313,11 @@ void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link) // cNetwork API: cServerHandlePtr cNetwork::Listen( - const UInt16 a_Port, - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks + UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks ) { - return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks, a_LinkCallbacks); + return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks); } diff --git a/src/OSSupport/ServerHandleImpl.h b/src/OSSupport/ServerHandleImpl.h index b325a0f37..33ff787f2 100644 --- a/src/OSSupport/ServerHandleImpl.h +++ b/src/OSSupport/ServerHandleImpl.h @@ -46,8 +46,7 @@ public: Always returns a server instance; in the event of a failure, the instance holds the error details. Use IsListening() to query success. */ static cServerHandleImplPtr Listen( UInt16 a_Port, - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks + cNetwork::cListenCallbacksPtr a_ListenCallbacks ); // cServerHandle overrides: @@ -58,9 +57,6 @@ protected: /** The callbacks used to notify about incoming connections. */ cNetwork::cListenCallbacksPtr m_ListenCallbacks; - /** The callbacks used to create new cTCPLink instances for incoming connections. */ - cTCPLink::cCallbacksPtr m_LinkCallbacks; - /** The LibEvent handle representing the main listening socket. */ evconnlistener * m_ConnListener; @@ -86,10 +82,7 @@ protected: /** Creates a new instance with the specified callbacks. Initializes the internals, but doesn't start listening yet. */ - cServerHandleImpl( - cNetwork::cListenCallbacksPtr a_ListenCallbacks, - cTCPLink::cCallbacksPtr a_LinkCallbacks - ); + cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks); /** Starts listening on the specified port. Returns true if successful, false on failure. On failure, sets m_ErrorCode and m_ErrorMsg. */ diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index f87f68280..6f937646f 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -304,7 +304,7 @@ void cTCPLinkImpl::UpdateRemoteAddress(void) bool cNetwork::Connect( const AString & a_Host, - const UInt16 a_Port, + UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks, cTCPLink::cCallbacksPtr a_LinkCallbacks ) -- cgit v1.2.3 From dbf7f13bd414daea5e787da2543df186dc465c34 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 22 Jan 2015 13:00:32 +0100 Subject: cNetwork: Added link creation callback. This allows the callback classes to store the link inside them and use it internally later on, mainly for sending data. --- src/OSSupport/Network.h | 18 +++++++++++++++--- src/OSSupport/ServerHandleImpl.cpp | 2 ++ src/OSSupport/TCPLinkImpl.cpp | 26 ++++++++++++++++---------- src/OSSupport/TCPLinkImpl.h | 11 +++++++++-- 4 files changed, 42 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index b9ca377cb..85c7c5dcb 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -13,6 +13,14 @@ +// fwd: +class cTCPLink; +typedef SharedPtr cTCPLinkPtr; + + + + + /** Interface that provides the methods available on a single TCP connection. */ class cTCPLink { @@ -25,16 +33,20 @@ public: // Force a virtual destructor for all descendants: virtual ~cCallbacks() {} + /** Called when the cTCPLink for the connection is created. + The callback may store the cTCPLink instance for later use, but it should remove it in OnError(), OnRemoteClosed() or right after Close(). */ + virtual void OnLinkCreated(cTCPLinkPtr a_Link) = 0; + /** Called when there's data incoming from the remote peer. */ - virtual void OnReceivedData(cTCPLink & a_Link, const char * a_Data, size_t a_Length) = 0; + virtual void OnReceivedData(const char * a_Data, size_t a_Length) = 0; /** Called when the remote end closes the connection. The link is still available for connection information query (IP / port). Sending data on the link is not an error, but the data won't be delivered. */ - virtual void OnRemoteClosed(cTCPLink & a_Link) = 0; + virtual void OnRemoteClosed(void) = 0; /** Called when an error is detected on the connection. */ - virtual void OnError(cTCPLink & a_Link, int a_ErrorCode, const AString & a_ErrorMsg) = 0; + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; }; typedef SharedPtr cCallbacksPtr; diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index 82cbecef2..026c0fdc1 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -283,6 +283,8 @@ void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_ cCSLock Lock(Self->m_CS); Self->m_Connections.push_back(Link); } // Lock(m_CS) + LinkCallbacks->OnLinkCreated(Link); + Link->Enable(); // Call the OnAccepted callback: Self->m_ListenCallbacks->OnAccepted(*Link); diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index 6f937646f..5d31da22e 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -20,9 +20,6 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE)), m_Server(nullptr) { - // Create the LibEvent handle, but don't assign a socket to it yet (will be assigned within Connect() method): - bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); - bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); } @@ -37,10 +34,6 @@ cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_L // Update the endpoint addresses: UpdateLocalAddress(); UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort); - - // Create the LibEvent handle: - bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); - bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); } @@ -65,6 +58,8 @@ cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTC cTCPLinkImplPtr res{new cTCPLinkImpl(a_LinkCallbacks)}; // Cannot use std::make_shared here, constructor is not accessible res->m_ConnectCallbacks = a_ConnectCallbacks; cNetworkSingleton::Get().AddLink(res); + res->m_Callbacks->OnLinkCreated(res); + res->Enable(); // If a_Host is an IP address, schedule a connection immediately: sockaddr_storage sa; @@ -107,6 +102,17 @@ cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTC +void cTCPLinkImpl::Enable(void) +{ + // Set the LibEvent callbacks and enable processing: + bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); + bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); +} + + + + + bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) { return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0); @@ -160,7 +166,7 @@ void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self) size_t length; while ((length = bufferevent_read(a_BufferEvent, data, sizeof(data))) > 0) { - Self->m_Callbacks->OnReceivedData(*Self, data, length); + Self->m_Callbacks->OnReceivedData(data, length); } } @@ -189,7 +195,7 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void } else { - Self->m_Callbacks->OnError(*Self, err, evutil_socket_error_to_string(err)); + Self->m_Callbacks->OnError(err, evutil_socket_error_to_string(err)); if (Self->m_Server == nullptr) { cNetworkSingleton::Get().RemoveLink(Self); @@ -219,7 +225,7 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void // If the connection has been closed, call the link callback and remove the connection: if (a_What & BEV_EVENT_EOF) { - Self->m_Callbacks->OnRemoteClosed(*Self); + Self->m_Callbacks->OnRemoteClosed(); if (Self->m_Server != nullptr) { Self->m_Server->RemoveLink(Self); diff --git a/src/OSSupport/TCPLinkImpl.h b/src/OSSupport/TCPLinkImpl.h index edd295fe2..66347afe0 100644 --- a/src/OSSupport/TCPLinkImpl.h +++ b/src/OSSupport/TCPLinkImpl.h @@ -37,7 +37,8 @@ class cTCPLinkImpl: public: /** Creates a new link based on the given socket. Used for connections accepted in a server using cNetwork::Listen(). - a_Address and a_AddrLen describe the remote peer that has connected. */ + a_Address and a_AddrLen describe the remote peer that has connected. + The link is created disabled, you need to call Enable() to start the regular communication. */ cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, socklen_t a_AddrLen); /** Destroys the LibEvent handle representing the link. */ @@ -48,6 +49,11 @@ public: Returns a link that has the connection request queued, or NULL for failure. */ static cTCPLinkImplPtr Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks); + /** Enables communication over the link. + Links are created with communication disabled, so that creation callbacks can be called first. + This function then enables the regular communication to be reported. */ + void Enable(void); + // cTCPLink overrides: virtual bool Send(const void * a_Data, size_t a_Length) override; virtual AString GetLocalIP(void) const override { return m_LocalIP; } @@ -85,7 +91,8 @@ protected: /** Creates a new link to be queued to connect to a specified host:port. Used for outgoing connections created using cNetwork::Connect(). - To be used only by the Connect() factory function. */ + To be used only by the Connect() factory function. + The link is created disabled, you need to call Enable() to start the regular communication. */ cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks); /** Callback that LibEvent calls when there's data available from the remote peer. */ -- cgit v1.2.3 From 9014bdfa3233dac70274b27eb40df3739b6f49eb Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 22 Jan 2015 22:49:37 +0100 Subject: cNetwork: Renamed callback to OnConnected() --- src/OSSupport/Network.h | 7 +++++-- src/OSSupport/TCPLinkImpl.cpp | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 85c7c5dcb..cdf6ba0e9 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -16,6 +16,10 @@ // fwd: class cTCPLink; typedef SharedPtr cTCPLinkPtr; +typedef std::vector cTCPLinkPtrs; +class cServerHandle; +typedef SharedPtr cServerHandlePtr; +typedef std::vector cServerHandlePtrs; @@ -118,7 +122,6 @@ public: /** Returns true if the server has been started correctly and is currently listening for incoming connections. */ virtual bool IsListening(void) const = 0; }; -typedef SharedPtr cServerHandlePtr; @@ -136,7 +139,7 @@ public: /** Called when the Connect call succeeds. Provides the newly created link that can be used for communication. */ - virtual void OnSuccess(cTCPLink & a_Link) = 0; + virtual void OnConnected(cTCPLink & a_Link) = 0; /** Called when the Connect call fails. */ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index 5d31da22e..71b3d572d 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -213,7 +213,7 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void { if (Self->m_ConnectCallbacks != nullptr) { - Self->m_ConnectCallbacks->OnSuccess(*Self); + Self->m_ConnectCallbacks->OnConnected(*Self); // Reset the connect callbacks so that later errors get reported through the link callbacks: Self->m_ConnectCallbacks.reset(); return; -- cgit v1.2.3 From 268ea71e4a88293ac6704ce1d8a0fe6caa0d3f0b Mon Sep 17 00:00:00 2001 From: Matyas Dolak Date: Fri, 23 Jan 2015 10:03:39 +0100 Subject: Fixed negative return values in SchematicFileSerializer. Fixes CID 103165. --- src/WorldStorage/SchematicFileSerializer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp index 64f4cb00d..fb881e290 100644 --- a/src/WorldStorage/SchematicFileSerializer.cpp +++ b/src/WorldStorage/SchematicFileSerializer.cpp @@ -184,7 +184,9 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP { LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)", TSizeX, TSizeY, TSizeZ, - a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ) + (TSizeX >= 0) ? a_NBT.GetType(TSizeX) : -1, + (TSizeY >= 0) ? a_NBT.GetType(TSizeY) : -1, + (TSizeZ >= 0) ? a_NBT.GetType(TSizeZ) : -1 ); return false; } -- cgit v1.2.3 From a216413a33dce89288bdf5608ec229c5d5438432 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Fri, 23 Jan 2015 13:51:07 +0300 Subject: Fixed defect #71781 in Coverity list. --- src/World.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/World.cpp b/src/World.cpp index eb76abc2c..24b1a9b40 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -271,6 +271,11 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin #endif m_Dimension(a_Dimension), m_IsSpawnExplicitlySet(false), + m_SpawnX(0), + m_SpawnY(0), + m_SpawnZ(0), + m_BroadcastDeathMessages(true), + m_BroadcastAchievementMessages(true), m_IsDaylightCycleEnabled(true), m_WorldAge(0), m_TimeOfDay(0), -- cgit v1.2.3 From 14ea50cbe7bfe333e6720e62cc6ea55c82e40b15 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Fri, 23 Jan 2015 18:30:38 +0300 Subject: Fixed defect #43661 in Coverity list. --- src/Blocks/BlockFire.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Blocks/BlockFire.h b/src/Blocks/BlockFire.h index 07fcefe16..bafd385ab 100644 --- a/src/Blocks/BlockFire.h +++ b/src/Blocks/BlockFire.h @@ -12,7 +12,8 @@ class cBlockFireHandler : { public: cBlockFireHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) + : cBlockHandler(a_BlockType), + XZP(0), XZM(0), Dir(0) { } -- cgit v1.2.3 From 2557eab95738128eb758f967c52ea977c3e486e0 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Fri, 23 Jan 2015 18:38:23 +0300 Subject: Fixed defect #43671 in Coverity list. --- src/Generating/EndGen.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp index bc26edb20..c559e765f 100644 --- a/src/Generating/EndGen.cpp +++ b/src/Generating/EndGen.cpp @@ -40,6 +40,10 @@ cEndGen::cEndGen(int a_Seed) : m_FrequencyX(80), m_FrequencyY(80), m_FrequencyZ(80), + m_MinChunkX(0), + m_MaxChunkX(0), + m_MinChunkZ(0), + m_MaxChunkZ(0), m_LastChunkX(0x7fffffff), // Use dummy coords that won't ever be used by real chunks m_LastChunkZ(0x7fffffff) { -- cgit v1.2.3 From 0288e90e0be394aa4498c0b97a0f3aad021048ac Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Fri, 23 Jan 2015 18:42:54 +0300 Subject: Fixed defect #73101 in Coverity list. --- src/Generating/BioGen.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 2a4dbe794..2cc810d3b 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -733,7 +733,19 @@ cBioGenTwoLevel::cBioGenTwoLevel(int a_Seed) : m_Noise3(a_Seed + 5003), m_Noise4(a_Seed + 5004), m_Noise5(a_Seed + 5005), - m_Noise6(a_Seed + 5006) + m_Noise6(a_Seed + 5006), + m_FreqX1(0.0), + m_AmpX1(0.0), + m_FreqX2(0.0), + m_AmpX2(0.0), + m_FreqX3(0.0), + m_AmpX3(0.0), + m_FreqZ1(0.0), + m_AmpZ1(0.0), + m_FreqZ2(0.0), + m_AmpZ2(0.0), + m_FreqZ3(0.0), + m_AmpZ3(0.0) { } -- cgit v1.2.3 From ce2d3280cabc486f136380db91ea903e866f9bfe Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Fri, 23 Jan 2015 18:55:04 +0300 Subject: Fixed defect #43665 in Coverity list. --- src/Generating/Noise3DGenerator.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index 29471f936..eadc66a4e 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -380,6 +380,17 @@ cNoise3DComposable::cNoise3DComposable(int a_Seed) : m_DensityNoiseA(a_Seed + 1), m_DensityNoiseB(a_Seed + 2), m_BaseNoise(a_Seed + 3), + m_HeightAmplification(0.0), + m_MidPoint(0.0), + m_FrequencyX(0.0), + m_FrequencyY(0.0), + m_FrequencyZ(0.0), + m_BaseFrequencyX(0.0), + m_BaseFrequencyZ(0.0), + m_ChoiceFrequencyX(0.0), + m_ChoiceFrequencyY(0.0), + m_ChoiceFrequencyZ(0.0), + m_AirThreshold(0.0), m_LastChunkX(0x7fffffff), // Use dummy coords that won't ever be used by real chunks m_LastChunkZ(0x7fffffff) { -- cgit v1.2.3 From 5d257178a08e867817dbe446e4f84aab674598a6 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Fri, 23 Jan 2015 19:56:17 +0300 Subject: Fixed defect #43662 in Coverity list. --- src/LineBlockTracer.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/LineBlockTracer.cpp b/src/LineBlockTracer.cpp index 90f97cd23..e43a79566 100644 --- a/src/LineBlockTracer.cpp +++ b/src/LineBlockTracer.cpp @@ -15,7 +15,23 @@ cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) : - super(a_World, a_Callbacks) + super(a_World, a_Callbacks), + m_StartX(0.0), + m_StartY(0.0), + m_StartZ(0.0), + m_EndX(0.0), + m_EndY(0.0), + m_EndZ(0.0), + m_DiffX(0.0), + m_DiffY(0.0), + m_DiffZ(0.0), + m_DirX(0), + m_DirY(0), + m_DirZ(0), + m_CurrentX(0), + m_CurrentY(0), + m_CurrentZ(0), + m_CurrentFace(0) { } -- cgit v1.2.3 From 10cfa61fbc5d0720f4e4864f50f1298937327348 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 23 Jan 2015 23:01:18 +0100 Subject: cNetwork: Added self pointers to keep objects alive for callbacks. Ref.: http://forum.mc-server.org/showthread.php?tid=1700&pid=17947#pid17947 --- src/OSSupport/NetworkSingleton.cpp | 2 ++ src/OSSupport/ServerHandleImpl.cpp | 10 ++++++++-- src/OSSupport/ServerHandleImpl.h | 3 +++ src/OSSupport/TCPLinkImpl.cpp | 25 +++++++++++++++---------- src/OSSupport/TCPLinkImpl.h | 16 +++++++++++----- 5 files changed, 39 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/OSSupport/NetworkSingleton.cpp b/src/OSSupport/NetworkSingleton.cpp index 8c38fb4eb..92f0604cd 100644 --- a/src/OSSupport/NetworkSingleton.cpp +++ b/src/OSSupport/NetworkSingleton.cpp @@ -88,6 +88,8 @@ cNetworkSingleton::~cNetworkSingleton() // Free the underlying LibEvent objects: evdns_base_free(m_DNSBase, true); event_base_free(m_EventBase); + + libevent_global_shutdown(); } diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp index 026c0fdc1..ba38dbf2e 100644 --- a/src/OSSupport/ServerHandleImpl.cpp +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -80,6 +80,9 @@ void cServerHandleImpl::Close(void) { conn->Shutdown(); } + + // Remove the ptr to self, so that the object may be freed: + m_SelfPtr.reset(); } @@ -92,6 +95,7 @@ cServerHandleImplPtr cServerHandleImpl::Listen( ) { cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks)}; + res->m_SelfPtr = res; if (res->Listen(a_Port)) { cNetworkSingleton::Get().AddServer(res); @@ -99,6 +103,7 @@ cServerHandleImplPtr cServerHandleImpl::Listen( else { a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg); + res->m_SelfPtr.reset(); } return res; } @@ -247,6 +252,7 @@ void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_ // Cast to true self: cServerHandleImpl * Self = reinterpret_cast(a_Self); ASSERT(Self != nullptr); + ASSERT(Self->m_SelfPtr != nullptr); // Get the textual IP address and port number out of a_Addr: char IPAddress[128]; @@ -278,13 +284,13 @@ void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_ } // Create a new cTCPLink for the incoming connection: - cTCPLinkImplPtr Link = std::make_shared(a_Socket, LinkCallbacks, Self, a_Addr, static_cast(a_Len)); + cTCPLinkImplPtr Link = std::make_shared(a_Socket, LinkCallbacks, Self->m_SelfPtr, a_Addr, static_cast(a_Len)); { cCSLock Lock(Self->m_CS); Self->m_Connections.push_back(Link); } // Lock(m_CS) LinkCallbacks->OnLinkCreated(Link); - Link->Enable(); + Link->Enable(Link); // Call the OnAccepted callback: Self->m_ListenCallbacks->OnAccepted(*Link); diff --git a/src/OSSupport/ServerHandleImpl.h b/src/OSSupport/ServerHandleImpl.h index 33ff787f2..dbb18fc6d 100644 --- a/src/OSSupport/ServerHandleImpl.h +++ b/src/OSSupport/ServerHandleImpl.h @@ -78,6 +78,9 @@ protected: /** Contains the error message for the failure to listen. Only valid for non-listening instances. */ AString m_ErrorMsg; + /** The SharedPtr to self, so that it can be passed to created links. */ + cServerHandleImplPtr m_SelfPtr; + /** Creates a new instance with the specified callbacks. diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index 71b3d572d..b4cefa60c 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -17,8 +17,7 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): super(a_LinkCallbacks), - m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE)), - m_Server(nullptr) + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE)) { } @@ -26,7 +25,7 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): -cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, socklen_t a_AddrLen): +cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImplPtr a_Server, const sockaddr * a_Address, socklen_t a_AddrLen): super(a_LinkCallbacks), m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE)), m_Server(a_Server) @@ -59,7 +58,7 @@ cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTC res->m_ConnectCallbacks = a_ConnectCallbacks; cNetworkSingleton::Get().AddLink(res); res->m_Callbacks->OnLinkCreated(res); - res->Enable(); + res->Enable(res); // If a_Host is an IP address, schedule a connection immediately: sockaddr_storage sa; @@ -102,8 +101,11 @@ cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTC -void cTCPLinkImpl::Enable(void) +void cTCPLinkImpl::Enable(cTCPLinkImplPtr a_Self) { + // Take hold of a shared copy of self, to keep as long as the callbacks are coming: + m_Self = a_Self; + // Set the LibEvent callbacks and enable processing: bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); @@ -148,6 +150,7 @@ void cTCPLinkImpl::Close(void) { m_Server->RemoveLink(this); } + m_Self.reset(); } @@ -177,7 +180,7 @@ void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self) void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self) { ASSERT(a_Self != nullptr); - cTCPLinkImpl * Self = static_cast(a_Self); + cTCPLinkImplPtr Self = static_cast(a_Self)->m_Self; // If an error is reported, call the error callback: if (a_What & BEV_EVENT_ERROR) @@ -198,13 +201,14 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void Self->m_Callbacks->OnError(err, evutil_socket_error_to_string(err)); if (Self->m_Server == nullptr) { - cNetworkSingleton::Get().RemoveLink(Self); + cNetworkSingleton::Get().RemoveLink(Self.get()); } else { - Self->m_Server->RemoveLink(Self); + Self->m_Server->RemoveLink(Self.get()); } } + Self->m_Self.reset(); return; } @@ -228,12 +232,13 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void Self->m_Callbacks->OnRemoteClosed(); if (Self->m_Server != nullptr) { - Self->m_Server->RemoveLink(Self); + Self->m_Server->RemoveLink(Self.get()); } else { - cNetworkSingleton::Get().RemoveLink(Self); + cNetworkSingleton::Get().RemoveLink(Self.get()); } + Self->m_Self.reset(); return; } diff --git a/src/OSSupport/TCPLinkImpl.h b/src/OSSupport/TCPLinkImpl.h index 66347afe0..735e8ed9d 100644 --- a/src/OSSupport/TCPLinkImpl.h +++ b/src/OSSupport/TCPLinkImpl.h @@ -21,6 +21,7 @@ // fwd: class cServerHandleImpl; +typedef SharedPtr cServerHandleImplPtr; class cTCPLinkImpl; typedef SharedPtr cTCPLinkImplPtr; typedef std::vector cTCPLinkImplPtrs; @@ -39,7 +40,7 @@ public: Used for connections accepted in a server using cNetwork::Listen(). a_Address and a_AddrLen describe the remote peer that has connected. The link is created disabled, you need to call Enable() to start the regular communication. */ - cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImpl * a_Server, const sockaddr * a_Address, socklen_t a_AddrLen); + cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImplPtr a_Server, const sockaddr * a_Address, socklen_t a_AddrLen); /** Destroys the LibEvent handle representing the link. */ ~cTCPLinkImpl(); @@ -51,8 +52,9 @@ public: /** Enables communication over the link. Links are created with communication disabled, so that creation callbacks can be called first. - This function then enables the regular communication to be reported. */ - void Enable(void); + This function then enables the regular communication to be reported. + The a_Self parameter is used so that the socket can keep itself alive as long as the callbacks are coming. */ + void Enable(cTCPLinkImplPtr a_Self); // cTCPLink overrides: virtual bool Send(const void * a_Data, size_t a_Length) override; @@ -73,8 +75,8 @@ protected: bufferevent * m_BufferEvent; /** The server handle that has created this link. - Only valid for incoming connections, NULL for outgoing connections. */ - cServerHandleImpl * m_Server; + Only valid for incoming connections, nullptr for outgoing connections. */ + cServerHandleImplPtr m_Server; /** The IP address of the local endpoint. Valid only after the socket has been connected. */ AString m_LocalIP; @@ -88,6 +90,10 @@ protected: /** The port of the remote endpoint. Valid only after the socket has been connected. */ UInt16 m_RemotePort; + /** SharedPtr to self, used to keep this object alive as long as the callbacks are coming. + Initialized in Enable(), cleared in Close() and EventCallback(RemoteClosed). */ + cTCPLinkImplPtr m_Self; + /** Creates a new link to be queued to connect to a specified host:port. Used for outgoing connections created using cNetwork::Connect(). -- cgit v1.2.3