summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AllToLua.bat27
-rw-r--r--src/AllToLua.pkg81
-rwxr-xr-xsrc/AllToLua.sh2
-rw-r--r--src/Authenticator.cpp267
-rw-r--r--src/Authenticator.h93
-rw-r--r--src/Bindings.cpp31618
-rw-r--r--src/Bindings.h8
-rw-r--r--src/BlockArea.cpp2124
-rw-r--r--src/BlockArea.h310
-rw-r--r--src/BlockEntities/BlockEntity.cpp44
-rw-r--r--src/BlockEntities/BlockEntity.h106
-rw-r--r--src/BlockEntities/BlockEntityWithItems.h86
-rw-r--r--src/BlockEntities/ChestEntity.cpp172
-rw-r--r--src/BlockEntities/ChestEntity.h59
-rw-r--r--src/BlockEntities/DispenserEntity.cpp215
-rw-r--r--src/BlockEntities/DispenserEntity.h38
-rw-r--r--src/BlockEntities/DropSpenserEntity.cpp266
-rw-r--r--src/BlockEntities/DropSpenserEntity.h89
-rw-r--r--src/BlockEntities/DropperEntity.cpp32
-rw-r--r--src/BlockEntities/DropperEntity.h46
-rw-r--r--src/BlockEntities/FurnaceEntity.cpp479
-rw-r--r--src/BlockEntities/FurnaceEntity.h164
-rw-r--r--src/BlockEntities/HopperEntity.cpp566
-rw-r--r--src/BlockEntities/HopperEntity.h96
-rw-r--r--src/BlockEntities/JukeboxEntity.cpp125
-rw-r--r--src/BlockEntities/JukeboxEntity.h56
-rw-r--r--src/BlockEntities/NoteEntity.cpp154
-rw-r--r--src/BlockEntities/NoteEntity.h63
-rw-r--r--src/BlockEntities/SignEntity.cpp115
-rw-r--r--src/BlockEntities/SignEntity.h67
-rw-r--r--src/BlockID.cpp958
-rw-r--r--src/BlockID.h907
-rw-r--r--src/BlockTracer.h104
-rw-r--r--src/Blocks/BlockBed.cpp88
-rw-r--r--src/Blocks/BlockBed.h67
-rw-r--r--src/Blocks/BlockBrewingStand.h32
-rw-r--r--src/Blocks/BlockButton.cpp32
-rw-r--r--src/Blocks/BlockButton.h93
-rw-r--r--src/Blocks/BlockCactus.h82
-rw-r--r--src/Blocks/BlockCarpet.h60
-rw-r--r--src/Blocks/BlockCauldron.h59
-rw-r--r--src/Blocks/BlockChest.h223
-rw-r--r--src/Blocks/BlockCloth.h34
-rw-r--r--src/Blocks/BlockCobWeb.h30
-rw-r--r--src/Blocks/BlockComparator.cpp53
-rw-r--r--src/Blocks/BlockComparator.h55
-rw-r--r--src/Blocks/BlockCrops.h114
-rw-r--r--src/Blocks/BlockDeadBush.h35
-rw-r--r--src/Blocks/BlockDirt.h88
-rw-r--r--src/Blocks/BlockDoor.cpp90
-rw-r--r--src/Blocks/BlockDoor.h175
-rw-r--r--src/Blocks/BlockDropSpenser.h41
-rw-r--r--src/Blocks/BlockEnderchest.h28
-rw-r--r--src/Blocks/BlockEntity.h31
-rw-r--r--src/Blocks/BlockFarmland.h107
-rw-r--r--src/Blocks/BlockFenceGate.h88
-rw-r--r--src/Blocks/BlockFire.h228
-rw-r--r--src/Blocks/BlockFlower.h41
-rw-r--r--src/Blocks/BlockFlowerPot.h105
-rw-r--r--src/Blocks/BlockFluid.h56
-rw-r--r--src/Blocks/BlockFurnace.h47
-rw-r--r--src/Blocks/BlockGlass.h26
-rw-r--r--src/Blocks/BlockGlowstone.h30
-rw-r--r--src/Blocks/BlockGravel.h27
-rw-r--r--src/Blocks/BlockHandler.cpp465
-rw-r--r--src/Blocks/BlockHandler.h152
-rw-r--r--src/Blocks/BlockHopper.h46
-rw-r--r--src/Blocks/BlockIce.h37
-rw-r--r--src/Blocks/BlockLadder.h115
-rw-r--r--src/Blocks/BlockLeaves.h184
-rw-r--r--src/Blocks/BlockLever.cpp31
-rw-r--r--src/Blocks/BlockLever.h68
-rw-r--r--src/Blocks/BlockMelon.h35
-rw-r--r--src/Blocks/BlockMushroom.h59
-rw-r--r--src/Blocks/BlockMycelium.h32
-rw-r--r--src/Blocks/BlockNote.h13
-rw-r--r--src/Blocks/BlockOre.h80
-rw-r--r--src/Blocks/BlockPiston.cpp102
-rw-r--r--src/Blocks/BlockPiston.h43
-rw-r--r--src/Blocks/BlockPlanks.h41
-rw-r--r--src/Blocks/BlockPortal.h108
-rw-r--r--src/Blocks/BlockPumpkin.h60
-rw-r--r--src/Blocks/BlockRail.h398
-rw-r--r--src/Blocks/BlockRedstone.cpp27
-rw-r--r--src/Blocks/BlockRedstone.h35
-rw-r--r--src/Blocks/BlockRedstoneRepeater.cpp50
-rw-r--r--src/Blocks/BlockRedstoneRepeater.h82
-rw-r--r--src/Blocks/BlockRedstoneTorch.h36
-rw-r--r--src/Blocks/BlockSand.h28
-rw-r--r--src/Blocks/BlockSapling.h57
-rw-r--r--src/Blocks/BlockSign.h78
-rw-r--r--src/Blocks/BlockSlab.h182
-rw-r--r--src/Blocks/BlockSnow.h72
-rw-r--r--src/Blocks/BlockStairs.h152
-rw-r--r--src/Blocks/BlockStems.h58
-rw-r--r--src/Blocks/BlockStone.h29
-rw-r--r--src/Blocks/BlockSugarcane.h90
-rw-r--r--src/Blocks/BlockTallGrass.h51
-rw-r--r--src/Blocks/BlockTorch.h275
-rw-r--r--src/Blocks/BlockVine.h201
-rw-r--r--src/Blocks/BlockWood.h72
-rw-r--r--src/Blocks/BlockWorkbench.h43
-rw-r--r--src/BoundingBox.cpp331
-rw-r--r--src/BoundingBox.h90
-rw-r--r--src/ByteBuffer.cpp841
-rw-r--r--src/ByteBuffer.h139
-rw-r--r--src/ChatColor.cpp39
-rw-r--r--src/ChatColor.h43
-rw-r--r--src/Chunk.cpp2776
-rw-r--r--src/Chunk.h484
-rw-r--r--src/Chunk.inl.h34
-rw-r--r--src/ChunkDef.h617
-rw-r--r--src/ChunkMap.cpp2701
-rw-r--r--src/ChunkMap.h439
-rw-r--r--src/ChunkSender.cpp295
-rw-r--r--src/ChunkSender.h169
-rw-r--r--src/ClientHandle.cpp2210
-rw-r--r--src/ClientHandle.h332
-rw-r--r--src/CommandOutput.cpp71
-rw-r--r--src/CommandOutput.h82
-rw-r--r--src/CraftingRecipes.cpp770
-rw-r--r--src/CraftingRecipes.h172
-rw-r--r--src/Cuboid.cpp117
-rw-r--r--src/Cuboid.h75
-rw-r--r--src/DeadlockDetect.cpp147
-rw-r--r--src/DeadlockDetect.h65
-rw-r--r--src/Defines.h562
-rw-r--r--src/Enchantments.cpp299
-rw-r--r--src/Enchantments.h115
-rw-r--r--src/Endianness.h70
-rw-r--r--src/Entities/Boat.cpp87
-rw-r--r--src/Entities/Boat.h37
-rw-r--r--src/Entities/Entity.cpp1450
-rw-r--r--src/Entities/Entity.h445
-rw-r--r--src/Entities/FallingBlock.cpp93
-rw-r--r--src/Entities/FallingBlock.h43
-rw-r--r--src/Entities/Minecart.cpp541
-rw-r--r--src/Entities/Minecart.h169
-rw-r--r--src/Entities/Pawn.cpp19
-rw-r--r--src/Entities/Pawn.h28
-rw-r--r--src/Entities/Pickup.cpp166
-rw-r--r--src/Entities/Pickup.h64
-rw-r--r--src/Entities/Player.cpp1765
-rw-r--r--src/Entities/Player.h456
-rw-r--r--src/Entities/ProjectileEntity.cpp855
-rw-r--r--src/Entities/ProjectileEntity.h382
-rw-r--r--src/Entities/TNTEntity.cpp62
-rw-r--r--src/Entities/TNTEntity.h32
-rw-r--r--src/FastRandom.cpp174
-rw-r--r--src/FastRandom.h57
-rw-r--r--src/FurnaceRecipe.cpp255
-rw-r--r--src/FurnaceRecipe.h50
-rw-r--r--src/Generating/BioGen.cpp707
-rw-r--r--src/Generating/BioGen.h230
-rw-r--r--src/Generating/Caves.cpp968
-rw-r--r--src/Generating/Caves.h102
-rw-r--r--src/Generating/ChunkDesc.cpp605
-rw-r--r--src/Generating/ChunkDesc.h217
-rw-r--r--src/Generating/ChunkGenerator.cpp329
-rw-r--r--src/Generating/ChunkGenerator.h113
-rw-r--r--src/Generating/CompoGen.cpp634
-rw-r--r--src/Generating/CompoGen.h182
-rw-r--r--src/Generating/ComposableGenerator.cpp501
-rw-r--r--src/Generating/ComposableGenerator.h181
-rw-r--r--src/Generating/DistortedHeightmap.cpp444
-rw-r--r--src/Generating/DistortedHeightmap.h108
-rw-r--r--src/Generating/EndGen.cpp217
-rw-r--r--src/Generating/EndGen.h69
-rw-r--r--src/Generating/FinishGen.cpp664
-rw-r--r--src/Generating/FinishGen.h185
-rw-r--r--src/Generating/HeiGen.cpp390
-rw-r--r--src/Generating/HeiGen.h145
-rw-r--r--src/Generating/MineShafts.cpp1424
-rw-r--r--src/Generating/MineShafts.h61
-rw-r--r--src/Generating/Noise3DGenerator.cpp576
-rw-r--r--src/Generating/Noise3DGenerator.h106
-rw-r--r--src/Generating/Ravines.cpp531
-rw-r--r--src/Generating/Ravines.h46
-rw-r--r--src/Generating/StructGen.cpp675
-rw-r--r--src/Generating/StructGen.h165
-rw-r--r--src/Generating/Trees.cpp684
-rw-r--r--src/Generating/Trees.h93
-rw-r--r--src/Globals.cpp10
-rw-r--r--src/Globals.h227
-rw-r--r--src/Group.cpp37
-rw-r--r--src/Group.h40
-rw-r--r--src/GroupManager.cpp122
-rw-r--r--src/GroupManager.h30
-rw-r--r--src/HTTPServer/EnvelopeParser.cpp132
-rw-r--r--src/HTTPServer/EnvelopeParser.h69
-rw-r--r--src/HTTPServer/HTTPConnection.cpp247
-rw-r--r--src/HTTPServer/HTTPConnection.h101
-rw-r--r--src/HTTPServer/HTTPFormParser.cpp290
-rw-r--r--src/HTTPServer/HTTPFormParser.h112
-rw-r--r--src/HTTPServer/HTTPMessage.cpp279
-rw-r--r--src/HTTPServer/HTTPMessage.h164
-rw-r--r--src/HTTPServer/HTTPServer.cpp258
-rw-r--r--src/HTTPServer/HTTPServer.h101
-rw-r--r--src/HTTPServer/MultipartParser.cpp256
-rw-r--r--src/HTTPServer/MultipartParser.h76
-rw-r--r--src/HTTPServer/NameValueParser.cpp412
-rw-r--r--src/HTTPServer/NameValueParser.h70
-rw-r--r--src/Inventory.cpp682
-rw-r--r--src/Inventory.h182
-rw-r--r--src/Item.cpp261
-rw-r--r--src/Item.h210
-rw-r--r--src/ItemGrid.cpp665
-rw-r--r--src/ItemGrid.h191
-rw-r--r--src/Items/ItemBed.h56
-rw-r--r--src/Items/ItemBoat.h54
-rw-r--r--src/Items/ItemBow.h87
-rw-r--r--src/Items/ItemBrewingStand.h41
-rw-r--r--src/Items/ItemBucket.h160
-rw-r--r--src/Items/ItemCauldron.h41
-rw-r--r--src/Items/ItemCloth.h23
-rw-r--r--src/Items/ItemComparator.h40
-rw-r--r--src/Items/ItemDoor.h45
-rw-r--r--src/Items/ItemDye.h44
-rw-r--r--src/Items/ItemFlowerPot.h41
-rw-r--r--src/Items/ItemFood.h63
-rw-r--r--src/Items/ItemHandler.cpp511
-rw-r--r--src/Items/ItemHandler.h99
-rw-r--r--src/Items/ItemHoe.h40
-rw-r--r--src/Items/ItemLeaves.h41
-rw-r--r--src/Items/ItemLighter.h59
-rw-r--r--src/Items/ItemMinecart.h82
-rw-r--r--src/Items/ItemPickaxe.h92
-rw-r--r--src/Items/ItemRedstoneDust.h38
-rw-r--r--src/Items/ItemRedstoneRepeater.h40
-rw-r--r--src/Items/ItemSapling.h42
-rw-r--r--src/Items/ItemSeeds.h65
-rw-r--r--src/Items/ItemShears.h62
-rw-r--r--src/Items/ItemShovel.h41
-rw-r--r--src/Items/ItemSign.h51
-rw-r--r--src/Items/ItemSpawnEgg.h52
-rw-r--r--src/Items/ItemSugarcane.h39
-rw-r--r--src/Items/ItemSword.h30
-rw-r--r--src/Items/ItemThrowable.h140
-rw-r--r--src/LeakFinder.cpp1047
-rw-r--r--src/LeakFinder.h156
-rw-r--r--src/LightingThread.cpp562
-rw-r--r--src/LightingThread.h181
-rw-r--r--src/LineBlockTracer.cpp262
-rw-r--r--src/LineBlockTracer.h87
-rw-r--r--src/LinearInterpolation.cpp251
-rw-r--r--src/LinearInterpolation.h60
-rw-r--r--src/LinearUpscale.h244
-rw-r--r--src/Log.cpp169
-rw-r--r--src/Log.h30
-rw-r--r--src/LuaFunctions.h17
-rw-r--r--src/LuaState.cpp1024
-rw-r--r--src/LuaState.h817
-rw-r--r--src/LuaWindow.cpp185
-rw-r--r--src/LuaWindow.h95
-rw-r--r--src/MCLogger.cpp261
-rw-r--r--src/MCLogger.h84
-rw-r--r--src/ManualBindings.cpp2285
-rw-r--r--src/ManualBindings.h8
-rw-r--r--src/Matrix4f.cpp4
-rw-r--r--src/Matrix4f.h225
-rw-r--r--src/MemoryLeak.h19
-rw-r--r--src/MersenneTwister.h456
-rw-r--r--src/MobCensus.cpp92
-rw-r--r--src/MobCensus.h59
-rw-r--r--src/MobFamilyCollecter.cpp26
-rw-r--r--src/MobFamilyCollecter.h39
-rw-r--r--src/MobProximityCounter.cpp83
-rw-r--r--src/MobProximityCounter.h65
-rw-r--r--src/MobSpawner.cpp361
-rw-r--r--src/MobSpawner.h76
-rw-r--r--src/Mobs/AggressiveMonster.cpp97
-rw-r--r--src/Mobs/AggressiveMonster.h30
-rw-r--r--src/Mobs/Bat.cpp15
-rw-r--r--src/Mobs/Bat.h25
-rw-r--r--src/Mobs/Blaze.cpp52
-rw-r--r--src/Mobs/Blaze.h26
-rw-r--r--src/Mobs/Cavespider.cpp40
-rw-r--r--src/Mobs/Cavespider.h26
-rw-r--r--src/Mobs/Chicken.cpp62
-rw-r--r--src/Mobs/Chicken.h29
-rw-r--r--src/Mobs/Cow.cpp45
-rw-r--r--src/Mobs/Cow.h26
-rw-r--r--src/Mobs/Creeper.cpp47
-rw-r--r--src/Mobs/Creeper.h34
-rw-r--r--src/Mobs/EnderDragon.cpp27
-rw-r--r--src/Mobs/EnderDragon.h25
-rw-r--r--src/Mobs/Enderman.cpp29
-rw-r--r--src/Mobs/Enderman.h36
-rw-r--r--src/Mobs/Ghast.cpp54
-rw-r--r--src/Mobs/Ghast.h28
-rw-r--r--src/Mobs/Giant.cpp27
-rw-r--r--src/Mobs/Giant.h25
-rw-r--r--src/Mobs/Horse.cpp152
-rw-r--r--src/Mobs/Horse.h44
-rw-r--r--src/Mobs/IncludeAllMonsters.h29
-rw-r--r--src/Mobs/IronGolem.cpp26
-rw-r--r--src/Mobs/IronGolem.h25
-rw-r--r--src/Mobs/Magmacube.cpp27
-rw-r--r--src/Mobs/Magmacube.h32
-rw-r--r--src/Mobs/Monster.cpp758
-rw-r--r--src/Mobs/Monster.h195
-rw-r--r--src/Mobs/Mooshroom.cpp33
-rw-r--r--src/Mobs/Mooshroom.h25
-rw-r--r--src/Mobs/Ocelot.h26
-rw-r--r--src/Mobs/PassiveAggressiveMonster.cpp38
-rw-r--r--src/Mobs/PassiveAggressiveMonster.h23
-rw-r--r--src/Mobs/PassiveMonster.cpp59
-rw-r--r--src/Mobs/PassiveMonster.h27
-rw-r--r--src/Mobs/Pig.cpp77
-rw-r--r--src/Mobs/Pig.h32
-rw-r--r--src/Mobs/Sheep.cpp62
-rw-r--r--src/Mobs/Sheep.h34
-rw-r--r--src/Mobs/Silverfish.h26
-rw-r--r--src/Mobs/Skeleton.cpp70
-rw-r--r--src/Mobs/Skeleton.h33
-rw-r--r--src/Mobs/Slime.cpp29
-rw-r--r--src/Mobs/Slime.h32
-rw-r--r--src/Mobs/SnowGolem.cpp26
-rw-r--r--src/Mobs/SnowGolem.h25
-rw-r--r--src/Mobs/Spider.cpp27
-rw-r--r--src/Mobs/Spider.h25
-rw-r--r--src/Mobs/Squid.cpp56
-rw-r--r--src/Mobs/Squid.h28
-rw-r--r--src/Mobs/Villager.cpp35
-rw-r--r--src/Mobs/Villager.h43
-rw-r--r--src/Mobs/Witch.cpp32
-rw-r--r--src/Mobs/Witch.h27
-rw-r--r--src/Mobs/Wither.cpp26
-rw-r--r--src/Mobs/Wither.h25
-rw-r--r--src/Mobs/Wolf.cpp189
-rw-r--r--src/Mobs/Wolf.h54
-rw-r--r--src/Mobs/Zombie.cpp47
-rw-r--r--src/Mobs/Zombie.h33
-rw-r--r--src/Mobs/Zombiepigman.cpp45
-rw-r--r--src/Mobs/Zombiepigman.h26
-rw-r--r--src/MonsterConfig.cpp103
-rw-r--r--src/MonsterConfig.h32
-rw-r--r--src/Noise.cpp951
-rw-r--r--src/Noise.h308
-rw-r--r--src/OSSupport/BlockingTCPLink.cpp149
-rw-r--r--src/OSSupport/BlockingTCPLink.h28
-rw-r--r--src/OSSupport/CriticalSection.cpp188
-rw-r--r--src/OSSupport/CriticalSection.h80
-rw-r--r--src/OSSupport/Event.cpp118
-rw-r--r--src/OSSupport/Event.h47
-rw-r--r--src/OSSupport/File.cpp451
-rw-r--r--src/OSSupport/File.h144
-rw-r--r--src/OSSupport/GZipFile.cpp107
-rw-r--r--src/OSSupport/GZipFile.h52
-rw-r--r--src/OSSupport/IsThread.cpp172
-rw-r--r--src/OSSupport/IsThread.h100
-rw-r--r--src/OSSupport/ListenThread.cpp238
-rw-r--r--src/OSSupport/ListenThread.h83
-rw-r--r--src/OSSupport/Semaphore.cpp91
-rw-r--r--src/OSSupport/Semaphore.h17
-rw-r--r--src/OSSupport/Sleep.cpp19
-rw-r--r--src/OSSupport/Sleep.h7
-rw-r--r--src/OSSupport/Socket.cpp393
-rw-r--r--src/OSSupport/Socket.h102
-rw-r--r--src/OSSupport/SocketThreads.cpp675
-rw-r--r--src/OSSupport/SocketThreads.h169
-rw-r--r--src/OSSupport/Thread.cpp128
-rw-r--r--src/OSSupport/Thread.h26
-rw-r--r--src/OSSupport/Timer.cpp37
-rw-r--r--src/OSSupport/Timer.h32
-rw-r--r--src/Piston.cpp291
-rw-r--r--src/Piston.h91
-rw-r--r--src/Plugin.cpp38
-rw-r--r--src/Plugin.h149
-rw-r--r--src/PluginLua.cpp1471
-rw-r--r--src/PluginLua.h202
-rw-r--r--src/PluginManager.cpp1668
-rw-r--r--src/PluginManager.h295
-rw-r--r--src/ProbabDistrib.cpp142
-rw-r--r--src/ProbabDistrib.h74
-rw-r--r--src/Protocol/ChunkDataSerializer.cpp176
-rw-r--r--src/Protocol/ChunkDataSerializer.h48
-rw-r--r--src/Protocol/Protocol.h216
-rw-r--r--src/Protocol/Protocol125.cpp1884
-rw-r--r--src/Protocol/Protocol125.h158
-rw-r--r--src/Protocol/Protocol132.cpp950
-rw-r--r--src/Protocol/Protocol132.h102
-rw-r--r--src/Protocol/Protocol14x.cpp256
-rw-r--r--src/Protocol/Protocol14x.h63
-rw-r--r--src/Protocol/Protocol15x.cpp138
-rw-r--r--src/Protocol/Protocol15x.h38
-rw-r--r--src/Protocol/Protocol16x.cpp268
-rw-r--r--src/Protocol/Protocol16x.h76
-rw-r--r--src/Protocol/Protocol17x.cpp1929
-rw-r--r--src/Protocol/Protocol17x.h259
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp915
-rw-r--r--src/Protocol/ProtocolRecognizer.h153
-rw-r--r--src/RCONServer.cpp333
-rw-r--r--src/RCONServer.h109
-rw-r--r--src/ReferenceManager.cpp43
-rw-r--r--src/ReferenceManager.h34
-rw-r--r--src/Root.cpp754
-rw-r--r--src/Root.h190
-rw-r--r--src/Server.cpp707
-rw-r--r--src/Server.h195
-rw-r--r--src/Simulator/DelayedFluidSimulator.cpp158
-rw-r--r--src/Simulator/DelayedFluidSimulator.h82
-rw-r--r--src/Simulator/FireSimulator.cpp374
-rw-r--r--src/Simulator/FireSimulator.h75
-rw-r--r--src/Simulator/FloodyFluidSimulator.cpp330
-rw-r--r--src/Simulator/FloodyFluidSimulator.h53
-rw-r--r--src/Simulator/FluidSimulator.cpp212
-rw-r--r--src/Simulator/FluidSimulator.h75
-rw-r--r--src/Simulator/NoopFluidSimulator.h36
-rw-r--r--src/Simulator/RedstoneSimulator.cpp1073
-rw-r--r--src/Simulator/RedstoneSimulator.h199
-rw-r--r--src/Simulator/SandSimulator.cpp309
-rw-r--r--src/Simulator/SandSimulator.h63
-rw-r--r--src/Simulator/Simulator.cpp51
-rw-r--r--src/Simulator/Simulator.h46
-rw-r--r--src/Simulator/SimulatorManager.cpp80
-rw-r--r--src/Simulator/SimulatorManager.h52
-rw-r--r--src/Simulator/VaporizeFluidSimulator.cpp53
-rw-r--r--src/Simulator/VaporizeFluidSimulator.h34
-rw-r--r--src/StackWalker.cpp1345
-rw-r--r--src/StackWalker.h214
-rw-r--r--src/StringCompression.cpp180
-rw-r--r--src/StringCompression.h25
-rw-r--r--src/StringUtils.cpp766
-rw-r--r--src/StringUtils.h93
-rw-r--r--src/Tracer.cpp398
-rw-r--r--src/Tracer.h82
-rw-r--r--src/UI/SlotArea.cpp897
-rw-r--r--src/UI/SlotArea.h313
-rw-r--r--src/UI/Window.cpp886
-rw-r--r--src/UI/Window.h300
-rw-r--r--src/UI/WindowOwner.h125
-rw-r--r--src/Vector3d.cpp77
-rw-r--r--src/Vector3d.h81
-rw-r--r--src/Vector3f.cpp34
-rw-r--r--src/Vector3f.h47
-rw-r--r--src/Vector3i.cpp16
-rw-r--r--src/Vector3i.h45
-rw-r--r--src/WebAdmin.cpp527
-rw-r--r--src/WebAdmin.h215
-rw-r--r--src/WebPlugin.cpp113
-rw-r--r--src/WebPlugin.h48
-rw-r--r--src/World.cpp2733
-rw-r--r--src/World.h750
-rw-r--r--src/WorldStorage/FastNBT.cpp547
-rw-r--r--src/WorldStorage/FastNBT.h293
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp533
-rw-r--r--src/WorldStorage/NBTChunkSerializer.h116
-rw-r--r--src/WorldStorage/WSSAnvil.cpp1555
-rw-r--r--src/WorldStorage/WSSAnvil.h184
-rw-r--r--src/WorldStorage/WSSCompact.cpp1009
-rw-r--r--src/WorldStorage/WSSCompact.h144
-rw-r--r--src/WorldStorage/WorldStorage.cpp409
-rw-r--r--src/WorldStorage/WorldStorage.h135
-rw-r--r--src/XMLParser.h701
-rw-r--r--src/lua5.1.dllbin0 -> 167424 bytes
-rw-r--r--src/main.cpp197
-rw-r--r--src/tolua++.exebin0 -> 484864 bytes
-rw-r--r--src/tolua++.h186
-rw-r--r--src/tolua_base.h128
-rw-r--r--src/virtual_method_hooks.lua506
461 files changed, 138187 insertions, 0 deletions
diff --git a/src/AllToLua.bat b/src/AllToLua.bat
new file mode 100644
index 000000000..f7867fadb
--- /dev/null
+++ b/src/AllToLua.bat
@@ -0,0 +1,27 @@
+
+:: AllToLua.bat
+
+:: This scripts updates the automatically-generates Lua bindings in Bindings.cpp / Bindings.h
+
+
+
+
+
+:: If there was a Git conflict, resolve it by resetting to HEAD; we're regenerating the files from scratch anyway
+git checkout --ours Bindings.cpp
+git add -u Bindings.cpp
+git checkout --ours Bindings.h
+git add -u Bindings.h
+
+
+
+
+
+:: Regenerate the files:
+"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+
+
+
+
+
+if %ALLTOLUA_WAIT%N == N pause
diff --git a/src/AllToLua.pkg b/src/AllToLua.pkg
new file mode 100644
index 000000000..ee594be1a
--- /dev/null
+++ b/src/AllToLua.pkg
@@ -0,0 +1,81 @@
+
+$#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+$#include "tolua_base.h"
+
+// Typedefs from Globals.h, so that we don't have to include that file:
+typedef long long Int64;
+typedef int Int32;
+typedef short Int16;
+
+typedef unsigned long long UInt64;
+typedef unsigned int UInt32;
+typedef unsigned short UInt16;
+
+
+$cfile "ChunkDef.h"
+
+$cfile "../iniFile/iniFile.h"
+
+$cfile "OSSupport/File.h"
+
+$cfile "BlockID.h"
+$cfile "StringUtils.h"
+$cfile "Defines.h"
+$cfile "LuaFunctions.h"
+$cfile "ChatColor.h"
+$cfile "ClientHandle.h"
+$cfile "Entities/Entity.h"
+$cfile "Entities/Pawn.h"
+$cfile "Entities/Player.h"
+$cfile "Entities/Pickup.h"
+$cfile "Entities/ProjectileEntity.h"
+$cfile "PluginManager.h"
+$cfile "Plugin.h"
+$cfile "PluginLua.h"
+$cfile "Server.h"
+$cfile "World.h"
+$cfile "Inventory.h"
+$cfile "Enchantments.h"
+$cfile "Item.h"
+$cfile "ItemGrid.h"
+$cfile "BlockEntities/BlockEntity.h"
+$cfile "BlockEntities/BlockEntityWithItems.h"
+$cfile "BlockEntities/ChestEntity.h"
+$cfile "BlockEntities/DropSpenserEntity.h"
+$cfile "BlockEntities/DispenserEntity.h"
+$cfile "BlockEntities/DropperEntity.h"
+$cfile "BlockEntities/FurnaceEntity.h"
+$cfile "BlockEntities/HopperEntity.h"
+$cfile "BlockEntities/JukeboxEntity.h"
+$cfile "BlockEntities/NoteEntity.h"
+$cfile "BlockEntities/SignEntity.h"
+$cfile "WebAdmin.h"
+$cfile "WebPlugin.h"
+$cfile "Root.h"
+$cfile "Vector3f.h"
+$cfile "Vector3d.h"
+$cfile "Vector3i.h"
+$cfile "Matrix4f.h"
+$cfile "Cuboid.h"
+$cfile "BoundingBox.h"
+$cfile "Tracer.h"
+$cfile "Group.h"
+$cfile "BlockArea.h"
+$cfile "Generating/ChunkDesc.h"
+$cfile "CraftingRecipes.h"
+$cfile "UI/Window.h"
+$cfile "LuaWindow.h"
+$cfile "Mobs/Monster.h"
+
+
+
+
+
+// Need to declare this class so that the usertype is properly registered in Bindings.cpp -
+// it seems impossible to register a usertype in ManualBindings.cpp
+class cLineBlockTracer;
+
+
+
+
diff --git a/src/AllToLua.sh b/src/AllToLua.sh
new file mode 100755
index 000000000..887c2490c
--- /dev/null
+++ b/src/AllToLua.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+/usr/bin/tolua++ -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
diff --git a/src/Authenticator.cpp b/src/Authenticator.cpp
new file mode 100644
index 000000000..4d9e90a01
--- /dev/null
+++ b/src/Authenticator.cpp
@@ -0,0 +1,267 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Authenticator.h"
+#include "OSSupport/BlockingTCPLink.h"
+#include "Root.h"
+#include "Server.h"
+
+#include ".inifile/iniFile.h"
+
+#include <sstream>
+
+
+
+
+
+#define DEFAULT_AUTH_SERVER "session.minecraft.net"
+#define DEFAULT_AUTH_ADDRESS "/game/checkserver.jsp?user=%USERNAME%&serverId=%SERVERID%"
+#define MAX_REDIRECTS 10
+
+
+
+
+
+cAuthenticator::cAuthenticator(void) :
+ super("cAuthenticator"),
+ m_Server(DEFAULT_AUTH_SERVER),
+ m_Address(DEFAULT_AUTH_ADDRESS),
+ m_ShouldAuthenticate(true)
+{
+}
+
+
+
+
+
+cAuthenticator::~cAuthenticator()
+{
+ Stop();
+}
+
+
+
+
+
+/// Read custom values from INI
+void cAuthenticator::ReadINI(cIniFile & IniFile)
+{
+ m_Server = IniFile.GetValueSet("Authentication", "Server", DEFAULT_AUTH_SERVER);
+ m_Address = IniFile.GetValueSet("Authentication", "Address", DEFAULT_AUTH_ADDRESS);
+ m_ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true);
+}
+
+
+
+
+
+/// Queues a request for authenticating a user. If the auth fails, the user is kicked
+void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash)
+{
+ if (!m_ShouldAuthenticate)
+ {
+ cRoot::Get()->AuthenticateUser(a_ClientID);
+ return;
+ }
+
+ cCSLock Lock(m_CS);
+ m_Queue.push_back(cUser(a_ClientID, a_UserName, a_ServerHash));
+ m_QueueNonempty.Set();
+}
+
+
+
+
+
+void cAuthenticator::Start(cIniFile & IniFile)
+{
+ ReadINI(IniFile);
+ m_ShouldTerminate = false;
+ super::Start();
+}
+
+
+
+
+
+void cAuthenticator::Stop(void)
+{
+ m_ShouldTerminate = true;
+ m_QueueNonempty.Set();
+ Wait();
+}
+
+
+
+
+
+void cAuthenticator::Execute(void)
+{
+ for (;;)
+ {
+ cCSLock Lock(m_CS);
+ while (!m_ShouldTerminate && (m_Queue.size() == 0))
+ {
+ cCSUnlock Unlock(Lock);
+ m_QueueNonempty.Wait();
+ }
+ if (m_ShouldTerminate)
+ {
+ return;
+ }
+ ASSERT(!m_Queue.empty());
+
+ int ClientID = m_Queue.front().m_ClientID;
+ AString UserName = m_Queue.front().m_Name;
+ AString ActualAddress = m_Address;
+ ReplaceString(ActualAddress, "%USERNAME%", UserName);
+ ReplaceString(ActualAddress, "%SERVERID%", m_Queue.front().m_ServerID);
+ m_Queue.pop_front();
+ Lock.Unlock();
+
+ if (!AuthFromAddress(m_Server, ActualAddress, UserName))
+ {
+ cRoot::Get()->KickUser(ClientID, "Failed to authenticate account!");
+ }
+ else
+ {
+ cRoot::Get()->AuthenticateUser(ClientID);
+ }
+ } // for (-ever)
+}
+
+
+
+
+
+bool cAuthenticator::AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level /* = 1 */)
+{
+ // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep)
+
+ cBlockingTCPLink Link;
+ if (!Link.Connect(a_Server.c_str(), 80))
+ {
+ LOGERROR("cAuthenticator: cannot connect to auth server \"%s\", kicking user \"%s\"", a_Server.c_str(), a_Server.c_str());
+ return false;
+ }
+
+ Link.SendMessage( AString( "GET " + a_Address + " HTTP/1.1\r\n" ).c_str());
+ Link.SendMessage( AString( "User-Agent: MCServer\r\n" ).c_str());
+ Link.SendMessage( AString( "Host: " + a_Server + "\r\n" ).c_str());
+ //Link.SendMessage( AString( "Host: session.minecraft.net\r\n" ).c_str());
+ Link.SendMessage( AString( "Accept: */*\r\n" ).c_str());
+ Link.SendMessage( AString( "Connection: close\r\n" ).c_str()); //Close so we don´t have to mess with the Content-Length :)
+ Link.SendMessage( AString( "\r\n" ).c_str());
+ AString DataRecvd;
+ Link.ReceiveData(DataRecvd);
+ Link.CloseSocket();
+
+ std::stringstream ss(DataRecvd);
+
+ // Parse the data received:
+ std::string temp;
+ ss >> temp;
+ bool bRedirect = false;
+ bool bOK = false;
+ if ((temp.compare("HTTP/1.1") == 0) || (temp.compare("HTTP/1.0") == 0))
+ {
+ int code;
+ ss >> code;
+ if (code == 302)
+ {
+ // redirect blabla
+ LOGINFO("Need to redirect!");
+ if (a_Level > MAX_REDIRECTS)
+ {
+ LOGERROR("cAuthenticator: received too many levels of redirection from auth server \"%s\" for user \"%s\", bailing out and kicking the user", a_Server.c_str(), a_UserName.c_str());
+ return false;
+ }
+ bRedirect = true;
+ }
+ else if (code == 200)
+ {
+ LOGD("cAuthenticator: Received status 200 OK! :D");
+ bOK = true;
+ }
+ }
+ else
+ {
+ LOGERROR("cAuthenticator: cannot parse auth reply from server \"%s\" for user \"%s\", kicking the user.", a_Server.c_str(), a_UserName.c_str());
+ return false;
+ }
+
+ if( bRedirect )
+ {
+ AString Location;
+ // Search for "Location:"
+ bool bFoundLocation = false;
+ while( !bFoundLocation && ss.good() )
+ {
+ char c = 0;
+ while( c != '\n' )
+ {
+ ss.get( c );
+ }
+ AString Name;
+ ss >> Name;
+ if (Name.compare("Location:") == 0)
+ {
+ bFoundLocation = true;
+ ss >> Location;
+ }
+ }
+ if (!bFoundLocation)
+ {
+ LOGERROR("cAuthenticator: received invalid redirection from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str());
+ return false;
+ }
+
+ Location = Location.substr(strlen("http://"), std::string::npos); // Strip http://
+ std::string Server = Location.substr( 0, Location.find( "/" ) ); // Only leave server address
+ Location = Location.substr( Server.length(), std::string::npos);
+ return AuthFromAddress(Server, Location, a_UserName, a_Level + 1);
+ }
+
+ if (!bOK)
+ {
+ LOGERROR("cAuthenticator: received an error from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str());
+ return false;
+ }
+
+ // Header says OK, so receive the rest.
+ // Go past header, double \n means end of headers
+ char c = 0;
+ while (ss.good())
+ {
+ while (c != '\n')
+ {
+ ss.get(c);
+ }
+ ss.get(c);
+ if( c == '\n' || c == '\r' || ss.peek() == '\r' || ss.peek() == '\n' )
+ break;
+ }
+ if (!ss.good())
+ {
+ LOGERROR("cAuthenticator: error while parsing response body from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str());
+ return false;
+ }
+
+ std::string Result;
+ ss >> Result;
+ LOGD("cAuthenticator: Authentication result was %s", Result.c_str());
+
+ if (Result.compare("YES") == 0) //Works well
+ {
+ LOGINFO("Authentication result \"YES\", player authentication success!");
+ return true;
+ }
+
+
+ LOGINFO("Authentication result was \"%s\", player authentication failure!", Result.c_str());
+ return false;
+}
+
+
+
+
diff --git a/src/Authenticator.h b/src/Authenticator.h
new file mode 100644
index 000000000..02cd6f4c5
--- /dev/null
+++ b/src/Authenticator.h
@@ -0,0 +1,93 @@
+
+// cAuthenticator.h
+
+// Interfaces to the cAuthenticator class representing the thread that authenticates users against the official MC server
+// Authentication prevents "hackers" from joining with an arbitrary username (possibly impersonating the server admins)
+// For more info, see http://wiki.vg/Session#Server_operation
+// In MCS, authentication is implemented as a single thread that receives queued auth requests and dispatches them one by one.
+
+
+
+
+
+#pragma once
+#ifndef CAUTHENTICATOR_H_INCLUDED
+#define CAUTHENTICATOR_H_INCLUDED
+
+#include "OSSupport/IsThread.h"
+
+
+
+
+
+// fwd: "cRoot.h"
+class cRoot;
+
+
+
+
+
+class cAuthenticator :
+ public cIsThread
+{
+ typedef cIsThread super;
+
+public:
+ cAuthenticator(void);
+ ~cAuthenticator();
+
+ /// (Re-)read server and address from INI:
+ void ReadINI(cIniFile & IniFile);
+
+ /// Queues a request for authenticating a user. If the auth fails, the user is kicked
+ void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash);
+
+ /// Starts the authenticator thread. The thread may be started and stopped repeatedly
+ void Start(cIniFile & IniFile);
+
+ /// Stops the authenticator thread. The thread may be started and stopped repeatedly
+ void Stop(void);
+
+private:
+
+ class cUser
+ {
+ public:
+ int m_ClientID;
+ AString m_Name;
+ AString m_ServerID;
+
+ cUser(int a_ClientID, const AString & a_Name, const AString & a_ServerID) :
+ m_ClientID(a_ClientID),
+ m_Name(a_Name),
+ m_ServerID(a_ServerID)
+ {
+ }
+ } ;
+
+ typedef std::deque<cUser> cUserList;
+
+ cCriticalSection m_CS;
+ cUserList m_Queue;
+ cEvent m_QueueNonempty;
+
+ AString m_Server;
+ AString m_Address;
+ bool m_ShouldAuthenticate;
+
+ // cIsThread override:
+ virtual void Execute(void) override;
+
+ // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep)
+ bool AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level = 1);
+};
+
+
+
+
+
+#endif // CAUTHENTICATOR_H_INCLUDED
+
+
+
+
diff --git a/src/Bindings.cpp b/src/Bindings.cpp
new file mode 100644
index 000000000..cbb50a321
--- /dev/null
+++ b/src/Bindings.cpp
@@ -0,0 +1,31618 @@
+/*
+** Lua binding: AllToLua
+** Generated automatically by tolua++-1.0.92 on 11/23/13 19:57:30.
+*/
+
+#ifndef __cplusplus
+#include "stdlib.h"
+#endif
+#include "string.h"
+
+#include "tolua++.h"
+
+/* Exported function */
+TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "tolua_base.h"
+#include "ChunkDef.h"
+#include "lib/iniFile/iniFile.h"
+#include "OSSupport/File.h"
+#include "BlockID.h"
+#include "StringUtils.h"
+#include "Defines.h"
+#include "LuaFunctions.h"
+#include "ChatColor.h"
+#include "ClientHandle.h"
+#include "Entities/Entity.h"
+#include "Entities/Pawn.h"
+#include "Entities/Player.h"
+#include "Entities/Pickup.h"
+#include "Entities/ProjectileEntity.h"
+#include "PluginManager.h"
+#include "Plugin.h"
+#include "PluginLua.h"
+#include "Server.h"
+#include "World.h"
+#include "Inventory.h"
+#include "Enchantments.h"
+#include "Item.h"
+#include "ItemGrid.h"
+#include "BlockEntities/BlockEntity.h"
+#include "BlockEntities/BlockEntityWithItems.h"
+#include "BlockEntities/ChestEntity.h"
+#include "BlockEntities/DropSpenserEntity.h"
+#include "BlockEntities/DispenserEntity.h"
+#include "BlockEntities/DropperEntity.h"
+#include "BlockEntities/FurnaceEntity.h"
+#include "BlockEntities/HopperEntity.h"
+#include "BlockEntities/JukeboxEntity.h"
+#include "BlockEntities/NoteEntity.h"
+#include "BlockEntities/SignEntity.h"
+#include "WebAdmin.h"
+#include "WebPlugin.h"
+#include "Root.h"
+#include "Vector3f.h"
+#include "Vector3d.h"
+#include "Vector3i.h"
+#include "Matrix4f.h"
+#include "Cuboid.h"
+#include "BoundingBox.h"
+#include "Tracer.h"
+#include "Group.h"
+#include "BlockArea.h"
+#include "Generating/ChunkDesc.h"
+#include "CraftingRecipes.h"
+#include "UI/Window.h"
+#include "LuaWindow.h"
+#include "Mobs/Monster.h"
+
+/* function to release collected object via destructor */
+#ifdef __cplusplus
+
+static int tolua_collect_sWebAdminPage (lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cBoundingBox (lua_State* tolua_S)
+{
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cItem (lua_State* tolua_S)
+{
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_Vector3f (lua_State* tolua_S)
+{
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cIniFile (lua_State* tolua_S)
+{
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cPickup (lua_State* tolua_S)
+{
+ cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cItems (lua_State* tolua_S)
+{
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cBlockArea (lua_State* tolua_S)
+{
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cTracer (lua_State* tolua_S)
+{
+ cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cCraftingGrid (lua_State* tolua_S)
+{
+ cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cCuboid (lua_State* tolua_S)
+{
+ cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cBlockEntity (lua_State* tolua_S)
+{
+ cBlockEntity* self = (cBlockEntity*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_Vector3i (lua_State* tolua_S)
+{
+ Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cEnchantments (lua_State* tolua_S)
+{
+ cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_cLuaWindow (lua_State* tolua_S)
+{
+ cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
+static int tolua_collect_Vector3d (lua_State* tolua_S)
+{
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+#endif
+
+
+/* function to register type */
+static void tolua_reg_types (lua_State* tolua_S)
+{
+ tolua_usertype(tolua_S,"cThrownEnderPearlEntity");
+ tolua_usertype(tolua_S,"cFurnaceEntity");
+ tolua_usertype(tolua_S,"cEntity");
+ tolua_usertype(tolua_S,"cExpBottleEntity");
+ tolua_usertype(tolua_S,"cEnchantments");
+ tolua_usertype(tolua_S,"cMonster");
+ tolua_usertype(tolua_S,"cPluginLua");
+ tolua_usertype(tolua_S,"cRoot");
+ tolua_usertype(tolua_S,"std::vector<cIniFile::key>");
+ tolua_usertype(tolua_S,"cPickup");
+ tolua_usertype(tolua_S,"sWebAdminPage");
+ tolua_usertype(tolua_S,"cFireChargeEntity");
+ tolua_usertype(tolua_S,"cClientHandle");
+ tolua_usertype(tolua_S,"cChunkDesc");
+ tolua_usertype(tolua_S,"cPluginManager");
+ tolua_usertype(tolua_S,"Vector3f");
+ tolua_usertype(tolua_S,"cCraftingRecipes");
+ tolua_usertype(tolua_S,"cJukeboxEntity");
+ tolua_usertype(tolua_S,"cChestEntity");
+ tolua_usertype(tolua_S,"cDispenserEntity");
+ tolua_usertype(tolua_S,"cGhastFireballEntity");
+ tolua_usertype(tolua_S,"cLineBlockTracer");
+ tolua_usertype(tolua_S,"cListeners");
+ tolua_usertype(tolua_S,"cThrownSnowballEntity");
+ tolua_usertype(tolua_S,"cFireworkEntity");
+ tolua_usertype(tolua_S,"TakeDamageInfo");
+ tolua_usertype(tolua_S,"cCraftingRecipe");
+ tolua_usertype(tolua_S,"cPlugin");
+ tolua_usertype(tolua_S,"cItemGrid");
+ tolua_usertype(tolua_S,"cHTTPServer::cCallbacks");
+ tolua_usertype(tolua_S,"cLuaWindow");
+ tolua_usertype(tolua_S,"cServer");
+ tolua_usertype(tolua_S,"cHopperEntity");
+ tolua_usertype(tolua_S,"std::vector<AString>");
+ tolua_usertype(tolua_S,"cBlockEntityWithItems");
+ tolua_usertype(tolua_S,"cWindow");
+ tolua_usertype(tolua_S,"cCraftingGrid");
+ tolua_usertype(tolua_S,"cWorld");
+ tolua_usertype(tolua_S,"cBlockArea");
+ tolua_usertype(tolua_S,"cItem");
+ tolua_usertype(tolua_S,"cGroup");
+ tolua_usertype(tolua_S,"cArrowEntity");
+ tolua_usertype(tolua_S,"cDropSpenserEntity");
+ tolua_usertype(tolua_S,"cTracer");
+ tolua_usertype(tolua_S,"cBoundingBox");
+ tolua_usertype(tolua_S,"cCuboid");
+ tolua_usertype(tolua_S,"cNoteEntity");
+ tolua_usertype(tolua_S,"Vector3i");
+ tolua_usertype(tolua_S,"cBlockEntity");
+ tolua_usertype(tolua_S,"cCriticalSection");
+ tolua_usertype(tolua_S,"HTTPTemplateRequest");
+ tolua_usertype(tolua_S,"Vector3d");
+ tolua_usertype(tolua_S,"cFile");
+ tolua_usertype(tolua_S,"cItems");
+ tolua_usertype(tolua_S,"cWebPlugin");
+ tolua_usertype(tolua_S,"cWebAdmin");
+ tolua_usertype(tolua_S,"cChatColor");
+ tolua_usertype(tolua_S,"cIniFile");
+ tolua_usertype(tolua_S,"HTTPRequest");
+ tolua_usertype(tolua_S,"HTTPFormData");
+ tolua_usertype(tolua_S,"cPawn");
+ tolua_usertype(tolua_S,"cPlayer");
+ tolua_usertype(tolua_S,"cGroupManager");
+ tolua_usertype(tolua_S,"cSignEntity");
+ tolua_usertype(tolua_S,"cItemGrid::cListener");
+ tolua_usertype(tolua_S,"cProjectileEntity");
+ tolua_usertype(tolua_S,"cDropperEntity");
+ tolua_usertype(tolua_S,"cInventory");
+ tolua_usertype(tolua_S,"cThrownEggEntity");
+}
+
+/* method: new of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new00
+static int tolua_AllToLua_cIniFile_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new00_local
+static int tolua_AllToLua_cIniFile_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CaseSensitive of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_CaseSensitive00
+static int tolua_AllToLua_cIniFile_CaseSensitive00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CaseSensitive'", NULL);
+#endif
+ {
+ self->CaseSensitive();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CaseSensitive'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CaseInsensitive of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_CaseInsensitive00
+static int tolua_AllToLua_cIniFile_CaseInsensitive00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CaseInsensitive'", NULL);
+#endif
+ {
+ self->CaseInsensitive();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CaseInsensitive'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ReadFile of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_ReadFile00
+static int tolua_AllToLua_cIniFile_ReadFile00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,3,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ bool a_AllowExampleRedirect = ((bool) tolua_toboolean(tolua_S,3,true));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReadFile'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->ReadFile(a_FileName,a_AllowExampleRedirect);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ReadFile'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: WriteFile of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_WriteFile00
+static int tolua_AllToLua_cIniFile_WriteFile00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WriteFile'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->WriteFile(a_FileName);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'WriteFile'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Clear of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_Clear00
+static int tolua_AllToLua_cIniFile_Clear00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL);
+#endif
+ {
+ self->Clear();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FindKey of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_FindKey00
+static int tolua_AllToLua_cIniFile_FindKey00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindKey'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->FindKey(keyname);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FindKey'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FindValue of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_FindValue00
+static int tolua_AllToLua_cIniFile_FindValue00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindValue'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->FindValue(keyID,valuename);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FindValue'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumKeys of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeys00
+static int tolua_AllToLua_cIniFile_GetNumKeys00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeys'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNumKeys();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNumKeys'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddKeyName of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyName00
+static int tolua_AllToLua_cIniFile_AddKeyName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyName'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->AddKeyName(keyname);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddKeyName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetKeyName of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyName00
+static int tolua_AllToLua_cIniFile_GetKeyName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyName'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetKeyName(keyID);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetKeyName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumValues of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumValues00
+static int tolua_AllToLua_cIniFile_GetNumValues00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumValues'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNumValues(keyname);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNumValues'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumValues of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumValues01
+static int tolua_AllToLua_cIniFile_GetNumValues01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumValues'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNumValues(keyID);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_GetNumValues00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValueName of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueName00
+static int tolua_AllToLua_cIniFile_GetValueName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueName'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetValueName(keyname,valueID);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetValueName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValueName of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueName01
+static int tolua_AllToLua_cIniFile_GetValueName01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueName'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetValueName(keyID,valueID);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_GetValueName00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValue of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue00
+static int tolua_AllToLua_cIniFile_GetValue00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetValue(keyname,valuename);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetValue'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValue of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue01
+static int tolua_AllToLua_cIniFile_GetValue01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetValue(keyname,valuename,defValue);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ tolua_pushcppstring(tolua_S,(const char*)defValue);
+ }
+ }
+ return 4;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_GetValue00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValue of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue02
+static int tolua_AllToLua_cIniFile_GetValue02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetValue(keyID,valueID);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_GetValue01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValue of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue03
+static int tolua_AllToLua_cIniFile_GetValue03(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
+ const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetValue(keyID,valueID,defValue);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)defValue);
+ }
+ }
+ return 2;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_GetValue02(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValueF of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueF00
+static int tolua_AllToLua_cIniFile_GetValueF00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const double defValue = ((const double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueF'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetValueF(keyname,valuename,defValue);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetValueF'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValueI of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueI00
+static int tolua_AllToLua_cIniFile_GetValueI00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const int defValue = ((const int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueI'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetValueI(keyname,valuename,defValue);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetValueI'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValueB of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueB00
+static int tolua_AllToLua_cIniFile_GetValueB00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const bool defValue = ((const bool) tolua_toboolean(tolua_S,4,false));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueB'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->GetValueB(keyname,valuename,defValue);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetValueB'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValueSet of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSet00
+static int tolua_AllToLua_cIniFile_GetValueSet00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSet'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetValueSet(keyname,valuename);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetValueSet'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValueSet of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSet01
+static int tolua_AllToLua_cIniFile_GetValueSet01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSet'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetValueSet(keyname,valuename,defValue);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ tolua_pushcppstring(tolua_S,(const char*)defValue);
+ }
+ }
+ return 4;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_GetValueSet00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValueSetF of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetF00
+static int tolua_AllToLua_cIniFile_GetValueSetF00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const double defValue = ((const double) tolua_tonumber(tolua_S,4,0.0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetF'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetValueSetF(keyname,valuename,defValue);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetValueSetF'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValueSetI of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetI00
+static int tolua_AllToLua_cIniFile_GetValueSetI00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const int defValue = ((const int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetI'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetValueSetI(keyname,valuename,defValue);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetValueSetI'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetValueSetB of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetB00
+static int tolua_AllToLua_cIniFile_GetValueSetB00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const bool defValue = ((const bool) tolua_toboolean(tolua_S,4,false));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetB'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->GetValueSetB(keyname,valuename,defValue);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetValueSetB'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetValue of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValue00
+static int tolua_AllToLua_cIniFile_SetValue00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
+ const AString value = ((const AString) tolua_tocppstring(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValue'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->SetValue(keyID,valueID,value);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)value);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetValue'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetValue of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValue01
+static int tolua_AllToLua_cIniFile_SetValue01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,4,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,5,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const AString value = ((const AString) tolua_tocppstring(tolua_S,4,0));
+ const bool create = ((const bool) tolua_toboolean(tolua_S,5,true));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValue'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->SetValue(keyname,valuename,value,create);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ tolua_pushcppstring(tolua_S,(const char*)value);
+ }
+ }
+ return 4;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_SetValue00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetValueI of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueI00
+static int tolua_AllToLua_cIniFile_SetValueI00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,5,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const int value = ((const int) tolua_tonumber(tolua_S,4,0));
+ const bool create = ((const bool) tolua_toboolean(tolua_S,5,true));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueI'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->SetValueI(keyname,valuename,value,create);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetValueI'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetValueB of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueB00
+static int tolua_AllToLua_cIniFile_SetValueB00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,4,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,5,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const bool value = ((const bool) tolua_toboolean(tolua_S,4,0));
+ const bool create = ((const bool) tolua_toboolean(tolua_S,5,true));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueB'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->SetValueB(keyname,valuename,value,create);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetValueB'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetValueF of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueF00
+static int tolua_AllToLua_cIniFile_SetValueF00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,5,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const double value = ((const double) tolua_tonumber(tolua_S,4,0));
+ const bool create = ((const bool) tolua_toboolean(tolua_S,5,true));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueF'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->SetValueF(keyname,valuename,value,create);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetValueF'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DeleteValueByID of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteValueByID00
+static int tolua_AllToLua_cIniFile_DeleteValueByID00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int valueID = ((const int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteValueByID'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DeleteValueByID(keyID,valueID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DeleteValueByID'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DeleteValue of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteValue00
+static int tolua_AllToLua_cIniFile_DeleteValue00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteValue'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DeleteValue(keyname,valuename);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)valuename);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DeleteValue'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DeleteKey of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKey00
+static int tolua_AllToLua_cIniFile_DeleteKey00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKey'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DeleteKey(keyname);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DeleteKey'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumHeaderComments of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumHeaderComments00
+static int tolua_AllToLua_cIniFile_GetNumHeaderComments00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumHeaderComments'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNumHeaderComments();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNumHeaderComments'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddHeaderComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddHeaderComment00
+static int tolua_AllToLua_cIniFile_AddHeaderComment00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString comment = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddHeaderComment'", NULL);
+#endif
+ {
+ self->AddHeaderComment(comment);
+ tolua_pushcppstring(tolua_S,(const char*)comment);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddHeaderComment'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHeaderComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetHeaderComment00
+static int tolua_AllToLua_cIniFile_GetHeaderComment00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int commentID = ((const int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeaderComment'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetHeaderComment(commentID);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHeaderComment'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DeleteHeaderComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteHeaderComment00
+static int tolua_AllToLua_cIniFile_DeleteHeaderComment00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ int commentID = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteHeaderComment'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DeleteHeaderComment(commentID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DeleteHeaderComment'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DeleteHeaderComments of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteHeaderComments00
+static int tolua_AllToLua_cIniFile_DeleteHeaderComments00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteHeaderComments'", NULL);
+#endif
+ {
+ self->DeleteHeaderComments();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DeleteHeaderComments'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumKeyComments of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeyComments00
+static int tolua_AllToLua_cIniFile_GetNumKeyComments00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeyComments'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNumKeyComments(keyID);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNumKeyComments'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumKeyComments of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeyComments01
+static int tolua_AllToLua_cIniFile_GetNumKeyComments01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeyComments'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNumKeyComments(keyname);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ }
+ }
+ return 2;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_GetNumKeyComments00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddKeyComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyComment00
+static int tolua_AllToLua_cIniFile_AddKeyComment00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const AString comment = ((const AString) tolua_tocppstring(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyComment'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->AddKeyComment(keyID,comment);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)comment);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddKeyComment'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddKeyComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyComment01
+static int tolua_AllToLua_cIniFile_AddKeyComment01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString comment = ((const AString) tolua_tocppstring(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyComment'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->AddKeyComment(keyname,comment);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ tolua_pushcppstring(tolua_S,(const char*)comment);
+ }
+ }
+ return 3;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_AddKeyComment00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetKeyComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyComment00
+static int tolua_AllToLua_cIniFile_GetKeyComment00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int commentID = ((const int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyComment'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetKeyComment(keyID,commentID);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetKeyComment'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetKeyComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyComment01
+static int tolua_AllToLua_cIniFile_GetKeyComment01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const int commentID = ((const int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyComment'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetKeyComment(keyname,commentID);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ }
+ }
+ return 2;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_GetKeyComment00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DeleteKeyComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComment00
+static int tolua_AllToLua_cIniFile_DeleteKeyComment00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+ const int commentID = ((const int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComment'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DeleteKeyComment(keyID,commentID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DeleteKeyComment'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DeleteKeyComment of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComment01
+static int tolua_AllToLua_cIniFile_DeleteKeyComment01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const int commentID = ((const int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComment'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DeleteKeyComment(keyname,commentID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ }
+ }
+ return 2;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_DeleteKeyComment00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DeleteKeyComments of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComments00
+static int tolua_AllToLua_cIniFile_DeleteKeyComments00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const int keyID = ((const int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComments'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DeleteKeyComments(keyID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DeleteKeyComments'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DeleteKeyComments of class cIniFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComments01
+static int tolua_AllToLua_cIniFile_DeleteKeyComments01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
+ const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComments'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DeleteKeyComments(keyname);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)keyname);
+ }
+ }
+ return 2;
+tolua_lerror:
+ return tolua_AllToLua_cIniFile_DeleteKeyComments00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Exists of class cFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Exists00
+static int tolua_AllToLua_cFile_Exists00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ bool tolua_ret = (bool) cFile::Exists(a_FileName);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Exists'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Delete of class cFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Delete00
+static int tolua_AllToLua_cFile_Delete00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ bool tolua_ret = (bool) cFile::Delete(a_FileName);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Delete'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Rename of class cFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Rename00
+static int tolua_AllToLua_cFile_Rename00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_OrigPath = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString a_NewPath = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ {
+ bool tolua_ret = (bool) cFile::Rename(a_OrigPath,a_NewPath);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_OrigPath);
+ tolua_pushcppstring(tolua_S,(const char*)a_NewPath);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Rename'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Copy of class cFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Copy00
+static int tolua_AllToLua_cFile_Copy00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_SrcFileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString a_DstFileName = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ {
+ bool tolua_ret = (bool) cFile::Copy(a_SrcFileName,a_DstFileName);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_SrcFileName);
+ tolua_pushcppstring(tolua_S,(const char*)a_DstFileName);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Copy'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsFolder of class cFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFolder00
+static int tolua_AllToLua_cFile_IsFolder00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ bool tolua_ret = (bool) cFile::IsFolder(a_Path);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Path);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsFolder'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsFile of class cFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFile00
+static int tolua_AllToLua_cFile_IsFile00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ bool tolua_ret = (bool) cFile::IsFile(a_Path);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Path);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsFile'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSize of class cFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_GetSize00
+static int tolua_AllToLua_cFile_GetSize00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ int tolua_ret = (int) cFile::GetSize(a_FileName);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSize'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CreateFolder of class cFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_CreateFolder00
+static int tolua_AllToLua_cFile_CreateFolder00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_FolderPath = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ bool tolua_ret = (bool) cFile::CreateFolder(a_FolderPath);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FolderPath);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CreateFolder'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ReadWholeFile of class cFile */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_ReadWholeFile00
+static int tolua_AllToLua_cFile_ReadWholeFile00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ AString tolua_ret = (AString) cFile::ReadWholeFile(a_FileName);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ReadWholeFile'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: BlockStringToType */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_BlockStringToType00
+static int tolua_AllToLua_BlockStringToType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_BlockTypeString = ((const AString) tolua_tocppstring(tolua_S,1,0));
+ {
+ unsigned char tolua_ret = ( unsigned char) BlockStringToType(a_BlockTypeString);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_BlockTypeString);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'BlockStringToType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: StringToItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToItem00
+static int tolua_AllToLua_StringToItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_ItemTypeString = ((const AString) tolua_tocppstring(tolua_S,1,0));
+ cItem* a_Item = ((cItem*) tolua_tousertype(tolua_S,2,0));
+ {
+ bool tolua_ret = (bool) StringToItem(a_ItemTypeString,*a_Item);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_ItemTypeString);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StringToItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemToString */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemToString00
+static int tolua_AllToLua_ItemToString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,1,0));
+ {
+ AString tolua_ret = (AString) ItemToString(*a_Item);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ItemToString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemTypeToString */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemTypeToString00
+static int tolua_AllToLua_ItemTypeToString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ AString tolua_ret = (AString) ItemTypeToString(a_ItemType);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ItemTypeToString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemToFullString */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemToFullString00
+static int tolua_AllToLua_ItemToFullString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,1,0));
+ {
+ AString tolua_ret = (AString) ItemToFullString(*a_Item);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ItemToFullString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: StringToBiome */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToBiome00
+static int tolua_AllToLua_StringToBiome00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_BiomeString = ((const AString) tolua_tocppstring(tolua_S,1,0));
+ {
+ EMCSBiome tolua_ret = (EMCSBiome) StringToBiome(a_BiomeString);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_BiomeString);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StringToBiome'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: StringToMobType */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToMobType00
+static int tolua_AllToLua_StringToMobType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_MobString = ((const AString) tolua_tocppstring(tolua_S,1,0));
+ {
+ int tolua_ret = (int) StringToMobType(a_MobString);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_MobString);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StringToMobType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: StringToDimension */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToDimension00
+static int tolua_AllToLua_StringToDimension00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_DimensionString = ((const AString) tolua_tocppstring(tolua_S,1,0));
+ {
+ eDimension tolua_ret = (eDimension) StringToDimension(a_DimensionString);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_DimensionString);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StringToDimension'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: DamageTypeToString */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_DamageTypeToString00
+static int tolua_AllToLua_DamageTypeToString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,1,0));
+ {
+ AString tolua_ret = (AString) DamageTypeToString(a_DamageType);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DamageTypeToString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: StringToDamageType */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToDamageType00
+static int tolua_AllToLua_StringToDamageType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_DamageString = ((const AString) tolua_tocppstring(tolua_S,1,0));
+ {
+ eDamageType tolua_ret = (eDamageType) StringToDamageType(a_DamageString);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_DamageString);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StringToDamageType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: GetIniItemSet */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_GetIniItemSet00
+static int tolua_AllToLua_GetIniItemSet00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err)) ||
+ !tolua_isstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isstring(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cIniFile* a_IniFile = ((cIniFile*) tolua_tousertype(tolua_S,1,0));
+ const char* a_Section = ((const char*) tolua_tostring(tolua_S,2,0));
+ const char* a_Key = ((const char*) tolua_tostring(tolua_S,3,0));
+ const char* a_Default = ((const char*) tolua_tostring(tolua_S,4,0));
+ {
+ cItem tolua_ret = (cItem) GetIniItemSet(*a_IniFile,a_Section,a_Key,a_Default);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((cItem)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetIniItemSet'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: TrimString */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_TrimString00
+static int tolua_AllToLua_TrimString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString str = ((const AString) tolua_tocppstring(tolua_S,1,0));
+ {
+ AString tolua_ret = (AString) TrimString(str);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)str);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'TrimString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: NoCaseCompare */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_NoCaseCompare00
+static int tolua_AllToLua_NoCaseCompare00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString s1 = ((const AString) tolua_tocppstring(tolua_S,1,0));
+ const AString s2 = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ int tolua_ret = (int) NoCaseCompare(s1,s2);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)s1);
+ tolua_pushcppstring(tolua_S,(const char*)s2);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'NoCaseCompare'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ReplaceString */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ReplaceString00
+static int tolua_AllToLua_ReplaceString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ AString iHayStack = ((AString) tolua_tocppstring(tolua_S,1,0));
+ const AString iNeedle = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString iReplaceWith = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ {
+ ReplaceString(iHayStack,iNeedle,iReplaceWith);
+ tolua_pushcppstring(tolua_S,(const char*)iHayStack);
+ tolua_pushcppstring(tolua_S,(const char*)iNeedle);
+ tolua_pushcppstring(tolua_S,(const char*)iReplaceWith);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ReplaceString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: EscapeString */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_EscapeString00
+static int tolua_AllToLua_EscapeString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,1,0));
+ {
+ AString tolua_ret = (AString) EscapeString(a_Message);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Message);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'EscapeString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: StripColorCodes */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_StripColorCodes00
+static int tolua_AllToLua_StripColorCodes00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,1,0));
+ {
+ AString tolua_ret = (AString) StripColorCodes(a_Message);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Message);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StripColorCodes'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: g_BlockLightValue */
+#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockLightValue
+static int tolua_get_AllToLua_g_BlockLightValue(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)g_BlockLightValue[tolua_index]);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: g_BlockLightValue */
+#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockLightValue
+static int tolua_set_AllToLua_g_BlockLightValue(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ g_BlockLightValue[tolua_index] = ((unsigned char) tolua_tonumber(tolua_S,3,0));
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: g_BlockSpreadLightFalloff */
+#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockSpreadLightFalloff
+static int tolua_get_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)g_BlockSpreadLightFalloff[tolua_index]);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: g_BlockSpreadLightFalloff */
+#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockSpreadLightFalloff
+static int tolua_set_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ g_BlockSpreadLightFalloff[tolua_index] = ((unsigned char) tolua_tonumber(tolua_S,3,0));
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: g_BlockTransparent */
+#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockTransparent
+static int tolua_get_AllToLua_g_BlockTransparent(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ tolua_pushboolean(tolua_S,(bool)g_BlockTransparent[tolua_index]);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: g_BlockTransparent */
+#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockTransparent
+static int tolua_set_AllToLua_g_BlockTransparent(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ g_BlockTransparent[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0));
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: g_BlockOneHitDig */
+#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockOneHitDig
+static int tolua_get_AllToLua_g_BlockOneHitDig(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ tolua_pushboolean(tolua_S,(bool)g_BlockOneHitDig[tolua_index]);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: g_BlockOneHitDig */
+#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockOneHitDig
+static int tolua_set_AllToLua_g_BlockOneHitDig(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ g_BlockOneHitDig[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0));
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: g_BlockPistonBreakable */
+#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockPistonBreakable
+static int tolua_get_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0 || tolua_index>=256)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ tolua_pushboolean(tolua_S,(bool)g_BlockPistonBreakable[tolua_index]);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: g_BlockPistonBreakable */
+#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockPistonBreakable
+static int tolua_set_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0 || tolua_index>=256)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ g_BlockPistonBreakable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0));
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: g_BlockIsSnowable */
+#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsSnowable
+static int tolua_get_AllToLua_g_BlockIsSnowable(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0 || tolua_index>=256)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ tolua_pushboolean(tolua_S,(bool)g_BlockIsSnowable[tolua_index]);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: g_BlockIsSnowable */
+#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsSnowable
+static int tolua_set_AllToLua_g_BlockIsSnowable(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0 || tolua_index>=256)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ g_BlockIsSnowable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0));
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: g_BlockRequiresSpecialTool */
+#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockRequiresSpecialTool
+static int tolua_get_AllToLua_g_BlockRequiresSpecialTool(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0 || tolua_index>=256)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ tolua_pushboolean(tolua_S,(bool)g_BlockRequiresSpecialTool[tolua_index]);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: g_BlockRequiresSpecialTool */
+#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockRequiresSpecialTool
+static int tolua_set_AllToLua_g_BlockRequiresSpecialTool(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0 || tolua_index>=256)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ g_BlockRequiresSpecialTool[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0));
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: g_BlockIsSolid */
+#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsSolid
+static int tolua_get_AllToLua_g_BlockIsSolid(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0 || tolua_index>=256)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ tolua_pushboolean(tolua_S,(bool)g_BlockIsSolid[tolua_index]);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: g_BlockIsSolid */
+#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsSolid
+static int tolua_set_AllToLua_g_BlockIsSolid(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0 || tolua_index>=256)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ g_BlockIsSolid[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0));
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: g_BlockIsTorchPlaceable */
+#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsTorchPlaceable
+static int tolua_get_AllToLua_g_BlockIsTorchPlaceable(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0 || tolua_index>=256)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ tolua_pushboolean(tolua_S,(bool)g_BlockIsTorchPlaceable[tolua_index]);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: g_BlockIsTorchPlaceable */
+#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsTorchPlaceable
+static int tolua_set_AllToLua_g_BlockIsTorchPlaceable(lua_State* tolua_S)
+{
+ int tolua_index;
+#ifndef TOLUA_RELEASE
+ {
+ tolua_Error tolua_err;
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err);
+ }
+#endif
+ tolua_index = (int)tolua_tonumber(tolua_S,2,0);
+#ifndef TOLUA_RELEASE
+ if (tolua_index<0 || tolua_index>=256)
+ tolua_error(tolua_S,"array indexing out of range.",NULL);
+#endif
+ g_BlockIsTorchPlaceable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0));
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ClickActionToString */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ClickActionToString00
+static int tolua_AllToLua_ClickActionToString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ eClickAction a_ClickAction = ((eClickAction) (int) tolua_tonumber(tolua_S,1,0));
+ {
+ const char* tolua_ret = (const char*) ClickActionToString(a_ClickAction);
+ tolua_pushstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ClickActionToString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: IsValidBlock */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_IsValidBlock00
+static int tolua_AllToLua_IsValidBlock00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ int a_BlockType = ((int) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) IsValidBlock(a_BlockType);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsValidBlock'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: IsValidItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_IsValidItem00
+static int tolua_AllToLua_IsValidItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ int a_ItemType = ((int) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) IsValidItem(a_ItemType);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsValidItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: AddFaceDirection */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_AddFaceDirection00
+static int tolua_AllToLua_AddFaceDirection00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,5,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,1,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0));
+ char a_BlockFace = ((char) tolua_tonumber(tolua_S,4,0));
+ bool a_bInverse = ((bool) tolua_toboolean(tolua_S,5,false));
+ {
+ AddFaceDirection(a_BlockX,a_BlockY,a_BlockZ,a_BlockFace,a_bInverse);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockX);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockY);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockZ);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddFaceDirection'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsPickaxe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsPickaxe00
+static int tolua_AllToLua_ItemCategory_IsPickaxe00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsPickaxe(a_ItemID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsPickaxe'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsAxe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsAxe00
+static int tolua_AllToLua_ItemCategory_IsAxe00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsAxe(a_ItemID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsAxe'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsSword */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsSword00
+static int tolua_AllToLua_ItemCategory_IsSword00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsSword(a_ItemID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSword'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsHoe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsHoe00
+static int tolua_AllToLua_ItemCategory_IsHoe00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsHoe(a_ItemID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsHoe'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsShovel */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsShovel00
+static int tolua_AllToLua_ItemCategory_IsShovel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsShovel(a_ItemID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsShovel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsTool */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsTool00
+static int tolua_AllToLua_ItemCategory_IsTool00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsTool(a_ItemID);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsTool'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsHelmet */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsHelmet00
+static int tolua_AllToLua_ItemCategory_IsHelmet00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsHelmet(a_ItemType);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsHelmet'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsChestPlate */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsChestPlate00
+static int tolua_AllToLua_ItemCategory_IsChestPlate00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsChestPlate(a_ItemType);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsChestPlate'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsLeggings */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsLeggings00
+static int tolua_AllToLua_ItemCategory_IsLeggings00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsLeggings(a_ItemType);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsLeggings'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsBoots */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsBoots00
+static int tolua_AllToLua_ItemCategory_IsBoots00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsBoots(a_ItemType);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsBoots'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: ItemCategory::IsArmor */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsArmor00
+static int tolua_AllToLua_ItemCategory_IsArmor00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnumber(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0));
+ {
+ bool tolua_ret = (bool) ItemCategory::IsArmor(a_ItemType);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsArmor'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: GetTime */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_GetTime00
+static int tolua_AllToLua_GetTime00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isnoobj(tolua_S,1,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ unsigned int tolua_ret = (unsigned int) GetTime();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetTime'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* function: GetChar */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_GetChar00
+static int tolua_AllToLua_GetChar00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ std::string a_Str = ((std::string) tolua_tocppstring(tolua_S,1,0));
+ unsigned int a_Idx = ((unsigned int) tolua_tonumber(tolua_S,2,0));
+ {
+ std::string tolua_ret = (std::string) GetChar(a_Str,a_Idx);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Str);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetChar'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Color of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Color
+static int tolua_get_cChatColor_Color(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Color);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Delimiter of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Delimiter
+static int tolua_get_cChatColor_Delimiter(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Delimiter);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Black of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Black
+static int tolua_get_cChatColor_Black(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Black);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Navy of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Navy
+static int tolua_get_cChatColor_Navy(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Navy);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Green of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Green
+static int tolua_get_cChatColor_Green(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Green);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Blue of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Blue
+static int tolua_get_cChatColor_Blue(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Blue);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Red of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Red
+static int tolua_get_cChatColor_Red(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Red);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Purple of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Purple
+static int tolua_get_cChatColor_Purple(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Purple);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Gold of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Gold
+static int tolua_get_cChatColor_Gold(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Gold);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: LightGray of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightGray
+static int tolua_get_cChatColor_LightGray(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightGray);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Gray of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Gray
+static int tolua_get_cChatColor_Gray(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Gray);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: DarkPurple of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_DarkPurple
+static int tolua_get_cChatColor_DarkPurple(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::DarkPurple);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: LightGreen of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightGreen
+static int tolua_get_cChatColor_LightGreen(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightGreen);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: LightBlue of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightBlue
+static int tolua_get_cChatColor_LightBlue(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightBlue);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Rose of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Rose
+static int tolua_get_cChatColor_Rose(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Rose);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: LightPurple of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightPurple
+static int tolua_get_cChatColor_LightPurple(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightPurple);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Yellow of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Yellow
+static int tolua_get_cChatColor_Yellow(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Yellow);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: White of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_White
+static int tolua_get_cChatColor_White(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::White);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Random of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Random
+static int tolua_get_cChatColor_Random(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Random);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Bold of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Bold
+static int tolua_get_cChatColor_Bold(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Bold);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Strikethrough of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Strikethrough
+static int tolua_get_cChatColor_Strikethrough(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Strikethrough);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Underlined of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Underlined
+static int tolua_get_cChatColor_Underlined(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Underlined);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Italic of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Italic
+static int tolua_get_cChatColor_Italic(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Italic);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Plain of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Plain
+static int tolua_get_cChatColor_Plain(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Plain);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MakeColor of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChatColor_MakeColor00
+static int tolua_AllToLua_cChatColor_MakeColor00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cChatColor",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ char a_Color = ((char) tolua_tonumber(tolua_S,2,0));
+ {
+ const std::string tolua_ret = (const std::string) cChatColor::MakeColor(a_Color);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MakeColor'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPlayer of class cClientHandle */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetPlayer00
+static int tolua_AllToLua_cClientHandle_GetPlayer00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPlayer'", NULL);
+#endif
+ {
+ cPlayer* tolua_ret = (cPlayer*) self->GetPlayer();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPlayer");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPlayer'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Kick of class cClientHandle */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_Kick00
+static int tolua_AllToLua_cClientHandle_Kick00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Reason = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Kick'", NULL);
+#endif
+ {
+ self->Kick(a_Reason);
+ tolua_pushcppstring(tolua_S,(const char*)a_Reason);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Kick'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SendBlockChange of class cClientHandle */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SendBlockChange00
+static int tolua_AllToLua_cClientHandle_SendBlockChange00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendBlockChange'", NULL);
+#endif
+ {
+ self->SendBlockChange(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SendBlockChange'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetUsername of class cClientHandle */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetUsername00
+static int tolua_AllToLua_cClientHandle_GetUsername00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUsername'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetUsername();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetUsername'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetUsername of class cClientHandle */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SetUsername00
+static int tolua_AllToLua_cClientHandle_SetUsername00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Username = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUsername'", NULL);
+#endif
+ {
+ self->SetUsername(a_Username);
+ tolua_pushcppstring(tolua_S,(const char*)a_Username);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetUsername'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPing of class cClientHandle */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetPing00
+static int tolua_AllToLua_cClientHandle_GetPing00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPing'", NULL);
+#endif
+ {
+ short tolua_ret = (short) self->GetPing();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPing'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetViewDistance of class cClientHandle */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SetViewDistance00
+static int tolua_AllToLua_cClientHandle_SetViewDistance00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0);
+ int a_ViewDistance = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetViewDistance'", NULL);
+#endif
+ {
+ self->SetViewDistance(a_ViewDistance);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetViewDistance'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetViewDistance of class cClientHandle */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetViewDistance00
+static int tolua_AllToLua_cClientHandle_GetViewDistance00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetViewDistance'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetViewDistance();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetViewDistance'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetUniqueID of class cClientHandle */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetUniqueID00
+static int tolua_AllToLua_cClientHandle_GetUniqueID00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUniqueID'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetUniqueID();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetUniqueID'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: DamageType of class TakeDamageInfo */
+#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_DamageType
+static int tolua_get_TakeDamageInfo_DamageType(lua_State* tolua_S)
+{
+ TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'DamageType'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->DamageType);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: DamageType of class TakeDamageInfo */
+#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_DamageType
+static int tolua_set_TakeDamageInfo_DamageType(lua_State* tolua_S)
+{
+ TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'DamageType'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Attacker of class TakeDamageInfo */
+#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_Attacker_ptr
+static int tolua_get_TakeDamageInfo_Attacker_ptr(lua_State* tolua_S)
+{
+ TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Attacker'",NULL);
+#endif
+ tolua_pushusertype(tolua_S,(void*)self->Attacker,"cEntity");
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Attacker of class TakeDamageInfo */
+#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_Attacker_ptr
+static int tolua_set_TakeDamageInfo_Attacker_ptr(lua_State* tolua_S)
+{
+ TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Attacker'",NULL);
+ if (!tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Attacker = ((cEntity*) tolua_tousertype(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: RawDamage of class TakeDamageInfo */
+#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_RawDamage
+static int tolua_get_TakeDamageInfo_RawDamage(lua_State* tolua_S)
+{
+ TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RawDamage'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->RawDamage);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: RawDamage of class TakeDamageInfo */
+#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_RawDamage
+static int tolua_set_TakeDamageInfo_RawDamage(lua_State* tolua_S)
+{
+ TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RawDamage'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->RawDamage = ((int) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: FinalDamage of class TakeDamageInfo */
+#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_FinalDamage
+static int tolua_get_TakeDamageInfo_FinalDamage(lua_State* tolua_S)
+{
+ TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'FinalDamage'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->FinalDamage);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: FinalDamage of class TakeDamageInfo */
+#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_FinalDamage
+static int tolua_set_TakeDamageInfo_FinalDamage(lua_State* tolua_S)
+{
+ TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'FinalDamage'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->FinalDamage = ((int) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Knockback of class TakeDamageInfo */
+#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_Knockback
+static int tolua_get_TakeDamageInfo_Knockback(lua_State* tolua_S)
+{
+ TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Knockback'",NULL);
+#endif
+ tolua_pushusertype(tolua_S,(void*)&self->Knockback,"Vector3d");
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Knockback of class TakeDamageInfo */
+#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_Knockback
+static int tolua_set_TakeDamageInfo_Knockback(lua_State* tolua_S)
+{
+ TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Knockback'",NULL);
+ if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3d",0,&tolua_err)))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Knockback = *((Vector3d*) tolua_tousertype(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEntityType of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEntityType00
+static int tolua_AllToLua_cEntity_GetEntityType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEntityType'", NULL);
+#endif
+ {
+ cEntity::eEntityType tolua_ret = (cEntity::eEntityType) self->GetEntityType();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEntityType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsPlayer of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsPlayer00
+static int tolua_AllToLua_cEntity_IsPlayer00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPlayer'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsPlayer();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsPlayer'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsPickup of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsPickup00
+static int tolua_AllToLua_cEntity_IsPickup00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPickup'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsPickup();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsPickup'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsMob of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsMob00
+static int tolua_AllToLua_cEntity_IsMob00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsMob'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsMob();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsMob'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsFallingBlock of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsFallingBlock00
+static int tolua_AllToLua_cEntity_IsFallingBlock00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsFallingBlock'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsFallingBlock();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsFallingBlock'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsMinecart of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsMinecart00
+static int tolua_AllToLua_cEntity_IsMinecart00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsMinecart'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsMinecart();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsMinecart'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsBoat of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsBoat00
+static int tolua_AllToLua_cEntity_IsBoat00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsBoat'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsBoat();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsBoat'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsTNT of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsTNT00
+static int tolua_AllToLua_cEntity_IsTNT00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsTNT'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsTNT();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsTNT'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsProjectile of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsProjectile00
+static int tolua_AllToLua_cEntity_IsProjectile00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsProjectile'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsProjectile();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsProjectile'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsA of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsA00
+static int tolua_AllToLua_cEntity_IsA00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+ const char* a_ClassName = ((const char*) tolua_tostring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsA'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsA(a_ClassName);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsA'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetClass of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetClass00
+static int tolua_AllToLua_cEntity_GetClass00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetClass'", NULL);
+#endif
+ {
+ const char* tolua_ret = (const char*) self->GetClass();
+ tolua_pushstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetClass'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetClassStatic of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetClassStatic00
+static int tolua_AllToLua_cEntity_GetClassStatic00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ const char* tolua_ret = (const char*) cEntity::GetClassStatic();
+ tolua_pushstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetClassStatic'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetParentClass of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetParentClass00
+static int tolua_AllToLua_cEntity_GetParentClass00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetParentClass'", NULL);
+#endif
+ {
+ const char* tolua_ret = (const char*) self->GetParentClass();
+ tolua_pushstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetParentClass'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWorld of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetWorld00
+static int tolua_AllToLua_cEntity_GetWorld00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL);
+#endif
+ {
+ cWorld* tolua_ret = (cWorld*) self->GetWorld();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHeadYaw of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHeadYaw00
+static int tolua_AllToLua_cEntity_GetHeadYaw00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeadYaw'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetHeadYaw();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHeadYaw'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHeight of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHeight00
+static int tolua_AllToLua_cEntity_GetHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetHeight();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMass of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetMass00
+static int tolua_AllToLua_cEntity_GetMass00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMass'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetMass();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMass'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPosition of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosition00
+static int tolua_AllToLua_cEntity_GetPosition00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosition'", NULL);
+#endif
+ {
+ const Vector3d& tolua_ret = (const Vector3d&) self->GetPosition();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPosition'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPosX of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosX00
+static int tolua_AllToLua_cEntity_GetPosX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosX'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetPosX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPosX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPosY of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosY00
+static int tolua_AllToLua_cEntity_GetPosY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosY'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetPosY();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPosY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPosZ of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosZ00
+static int tolua_AllToLua_cEntity_GetPosZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosZ'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetPosZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPosZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRot of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRot00
+static int tolua_AllToLua_cEntity_GetRot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRot'", NULL);
+#endif
+ {
+ const Vector3d& tolua_ret = (const Vector3d&) self->GetRot();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRotation of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRotation00
+static int tolua_AllToLua_cEntity_GetRotation00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRotation'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetRotation();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRotation'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetYaw of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetYaw00
+static int tolua_AllToLua_cEntity_GetYaw00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetYaw'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetYaw();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetYaw'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPitch of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPitch00
+static int tolua_AllToLua_cEntity_GetPitch00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPitch'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetPitch();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPitch'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRoll of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRoll00
+static int tolua_AllToLua_cEntity_GetRoll00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRoll'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetRoll();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRoll'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetLookVector of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetLookVector00
+static int tolua_AllToLua_cEntity_GetLookVector00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLookVector'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->GetLookVector();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetLookVector'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSpeed of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeed00
+static int tolua_AllToLua_cEntity_GetSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeed'", NULL);
+#endif
+ {
+ const Vector3d& tolua_ret = (const Vector3d&) self->GetSpeed();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSpeedX of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedX00
+static int tolua_AllToLua_cEntity_GetSpeedX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedX'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetSpeedX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSpeedX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSpeedY of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedY00
+static int tolua_AllToLua_cEntity_GetSpeedY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedY'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetSpeedY();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSpeedY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSpeedZ of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedZ00
+static int tolua_AllToLua_cEntity_GetSpeedZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedZ'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetSpeedZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSpeedZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWidth of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetWidth00
+static int tolua_AllToLua_cEntity_GetWidth00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetWidth();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetChunkX of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkX00
+static int tolua_AllToLua_cEntity_GetChunkX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetChunkX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetChunkZ of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkZ00
+static int tolua_AllToLua_cEntity_GetChunkZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetChunkZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetHeadYaw of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHeadYaw00
+static int tolua_AllToLua_cEntity_SetHeadYaw00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_HeadYaw = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeadYaw'", NULL);
+#endif
+ {
+ self->SetHeadYaw(a_HeadYaw);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetHeadYaw'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetHeight of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHeight00
+static int tolua_AllToLua_cEntity_SetHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_Height = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeight'", NULL);
+#endif
+ {
+ self->SetHeight(a_Height);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetMass of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetMass00
+static int tolua_AllToLua_cEntity_SetMass00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_Mass = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMass'", NULL);
+#endif
+ {
+ self->SetMass(a_Mass);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetMass'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetPosX of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosX00
+static int tolua_AllToLua_cEntity_SetPosX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_PosX = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosX'", NULL);
+#endif
+ {
+ self->SetPosX(a_PosX);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetPosX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetPosY of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosY00
+static int tolua_AllToLua_cEntity_SetPosY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_PosY = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosY'", NULL);
+#endif
+ {
+ self->SetPosY(a_PosY);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetPosY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetPosZ of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosZ00
+static int tolua_AllToLua_cEntity_SetPosZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_PosZ = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosZ'", NULL);
+#endif
+ {
+ self->SetPosZ(a_PosZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetPosZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetPosition of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosition00
+static int tolua_AllToLua_cEntity_SetPosition00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_PosX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_PosY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosition'", NULL);
+#endif
+ {
+ self->SetPosition(a_PosX,a_PosY,a_PosZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetPosition'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetPosition of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosition01
+static int tolua_AllToLua_cEntity_SetPosition01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosition'", NULL);
+#endif
+ {
+ self->SetPosition(*a_Pos);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cEntity_SetPosition00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRot of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRot00
+static int tolua_AllToLua_cEntity_SetRot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* a_Rot = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRot'", NULL);
+#endif
+ {
+ self->SetRot(*a_Rot);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRotation of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRotation00
+static int tolua_AllToLua_cEntity_SetRotation00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_Rotation = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRotation'", NULL);
+#endif
+ {
+ self->SetRotation(a_Rotation);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRotation'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetYaw of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetYaw00
+static int tolua_AllToLua_cEntity_SetYaw00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_Yaw = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetYaw'", NULL);
+#endif
+ {
+ self->SetYaw(a_Yaw);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetYaw'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetPitch of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPitch00
+static int tolua_AllToLua_cEntity_SetPitch00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_Pitch = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitch'", NULL);
+#endif
+ {
+ self->SetPitch(a_Pitch);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetPitch'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRoll of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRoll00
+static int tolua_AllToLua_cEntity_SetRoll00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_Roll = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRoll'", NULL);
+#endif
+ {
+ self->SetRoll(a_Roll);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRoll'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSpeed of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeed00
+static int tolua_AllToLua_cEntity_SetSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_SpeedX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_SpeedY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_SpeedZ = ((double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeed'", NULL);
+#endif
+ {
+ self->SetSpeed(a_SpeedX,a_SpeedY,a_SpeedZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSpeed of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeed01
+static int tolua_AllToLua_cEntity_SetSpeed01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_Speed = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeed'", NULL);
+#endif
+ {
+ self->SetSpeed(*a_Speed);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cEntity_SetSpeed00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSpeedX of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedX00
+static int tolua_AllToLua_cEntity_SetSpeedX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_SpeedX = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedX'", NULL);
+#endif
+ {
+ self->SetSpeedX(a_SpeedX);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSpeedX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSpeedY of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedY00
+static int tolua_AllToLua_cEntity_SetSpeedY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_SpeedY = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedY'", NULL);
+#endif
+ {
+ self->SetSpeedY(a_SpeedY);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSpeedY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSpeedZ of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedZ00
+static int tolua_AllToLua_cEntity_SetSpeedZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_SpeedZ = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedZ'", NULL);
+#endif
+ {
+ self->SetSpeedZ(a_SpeedZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSpeedZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetWidth of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetWidth00
+static int tolua_AllToLua_cEntity_SetWidth00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_Width = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWidth'", NULL);
+#endif
+ {
+ self->SetWidth(a_Width);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetWidth'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddPosX of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosX00
+static int tolua_AllToLua_cEntity_AddPosX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_AddPosX = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosX'", NULL);
+#endif
+ {
+ self->AddPosX(a_AddPosX);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddPosX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddPosY of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosY00
+static int tolua_AllToLua_cEntity_AddPosY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_AddPosY = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosY'", NULL);
+#endif
+ {
+ self->AddPosY(a_AddPosY);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddPosY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddPosZ of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosZ00
+static int tolua_AllToLua_cEntity_AddPosZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_AddPosZ = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosZ'", NULL);
+#endif
+ {
+ self->AddPosZ(a_AddPosZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddPosZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddPosition of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosition00
+static int tolua_AllToLua_cEntity_AddPosition00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_AddPosX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_AddPosY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_AddPosZ = ((double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosition'", NULL);
+#endif
+ {
+ self->AddPosition(a_AddPosX,a_AddPosY,a_AddPosZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddPosition'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddPosition of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosition01
+static int tolua_AllToLua_cEntity_AddPosition01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_AddPos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosition'", NULL);
+#endif
+ {
+ self->AddPosition(*a_AddPos);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cEntity_AddPosition00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddSpeed of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeed00
+static int tolua_AllToLua_cEntity_AddSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_AddSpeedX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_AddSpeedY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_AddSpeedZ = ((double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeed'", NULL);
+#endif
+ {
+ self->AddSpeed(a_AddSpeedX,a_AddSpeedY,a_AddSpeedZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddSpeed of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeed01
+static int tolua_AllToLua_cEntity_AddSpeed01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_AddSpeed = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeed'", NULL);
+#endif
+ {
+ self->AddSpeed(*a_AddSpeed);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cEntity_AddSpeed00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddSpeedX of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedX00
+static int tolua_AllToLua_cEntity_AddSpeedX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_AddSpeedX = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedX'", NULL);
+#endif
+ {
+ self->AddSpeedX(a_AddSpeedX);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddSpeedX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddSpeedY of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedY00
+static int tolua_AllToLua_cEntity_AddSpeedY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_AddSpeedY = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedY'", NULL);
+#endif
+ {
+ self->AddSpeedY(a_AddSpeedY);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddSpeedY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddSpeedZ of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedZ00
+static int tolua_AllToLua_cEntity_AddSpeedZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_AddSpeedZ = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedZ'", NULL);
+#endif
+ {
+ self->AddSpeedZ(a_AddSpeedZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddSpeedZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SteerVehicle of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SteerVehicle00
+static int tolua_AllToLua_cEntity_SteerVehicle00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ float a_Forward = ((float) tolua_tonumber(tolua_S,2,0));
+ float a_Sideways = ((float) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SteerVehicle'", NULL);
+#endif
+ {
+ self->SteerVehicle(a_Forward,a_Sideways);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SteerVehicle'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetUniqueID of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetUniqueID00
+static int tolua_AllToLua_cEntity_GetUniqueID00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUniqueID'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetUniqueID();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetUniqueID'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsDestroyed of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsDestroyed00
+static int tolua_AllToLua_cEntity_IsDestroyed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDestroyed'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsDestroyed();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsDestroyed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Destroy of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_Destroy00
+static int tolua_AllToLua_cEntity_Destroy00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ bool a_ShouldBroadcast = ((bool) tolua_toboolean(tolua_S,2,true));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Destroy'", NULL);
+#endif
+ {
+ self->Destroy(a_ShouldBroadcast);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Destroy'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: TakeDamage of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage00
+static int tolua_AllToLua_cEntity_TakeDamage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL);
+#endif
+ {
+ self->TakeDamage(*a_Attacker);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'TakeDamage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: TakeDamage of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage01
+static int tolua_AllToLua_cEntity_TakeDamage01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,3,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0));
+ cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,3,0));
+ int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0));
+ double a_KnockbackAmount = ((double) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL);
+#endif
+ {
+ self->TakeDamage(a_DamageType,a_Attacker,a_RawDamage,a_KnockbackAmount);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cEntity_TakeDamage00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: TakeDamage of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage02
+static int tolua_AllToLua_cEntity_TakeDamage02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,3,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0));
+ cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,3,0));
+ int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_FinalDamage = ((int) tolua_tonumber(tolua_S,5,0));
+ double a_KnockbackAmount = ((double) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL);
+#endif
+ {
+ self->TakeDamage(a_DamageType,a_Attacker,a_RawDamage,a_FinalDamage,a_KnockbackAmount);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cEntity_TakeDamage01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetGravity of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetGravity00
+static int tolua_AllToLua_cEntity_GetGravity00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGravity'", NULL);
+#endif
+ {
+ float tolua_ret = (float) self->GetGravity();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetGravity'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetGravity of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetGravity00
+static int tolua_AllToLua_cEntity_SetGravity00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ float a_Gravity = ((float) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetGravity'", NULL);
+#endif
+ {
+ self->SetGravity(a_Gravity);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetGravity'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRotationFromSpeed of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRotationFromSpeed00
+static int tolua_AllToLua_cEntity_SetRotationFromSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRotationFromSpeed'", NULL);
+#endif
+ {
+ self->SetRotationFromSpeed();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRotationFromSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetPitchFromSpeed of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPitchFromSpeed00
+static int tolua_AllToLua_cEntity_SetPitchFromSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitchFromSpeed'", NULL);
+#endif
+ {
+ self->SetPitchFromSpeed();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetPitchFromSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRawDamageAgainst of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRawDamageAgainst00
+static int tolua_AllToLua_cEntity_GetRawDamageAgainst00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ const cEntity* a_Receiver = ((const cEntity*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRawDamageAgainst'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetRawDamageAgainst(*a_Receiver);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRawDamageAgainst'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetArmorCoverAgainst of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetArmorCoverAgainst00
+static int tolua_AllToLua_cEntity_GetArmorCoverAgainst00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ const cEntity* a_Attacker = ((const cEntity*) tolua_tousertype(tolua_S,2,0));
+ eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,3,0));
+ int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorCoverAgainst'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetArmorCoverAgainst(a_Attacker,a_DamageType,a_RawDamage);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetArmorCoverAgainst'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetKnockbackAmountAgainst of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00
+static int tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ const cEntity* a_Receiver = ((const cEntity*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKnockbackAmountAgainst'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetKnockbackAmountAgainst(*a_Receiver);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetKnockbackAmountAgainst'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedWeapon of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedWeapon00
+static int tolua_AllToLua_cEntity_GetEquippedWeapon00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedWeapon'", NULL);
+#endif
+ {
+ cItem tolua_ret = (cItem) self->GetEquippedWeapon();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((cItem)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedWeapon'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedHelmet of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedHelmet00
+static int tolua_AllToLua_cEntity_GetEquippedHelmet00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedHelmet'", NULL);
+#endif
+ {
+ cItem tolua_ret = (cItem) self->GetEquippedHelmet();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((cItem)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedHelmet'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedChestplate of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedChestplate00
+static int tolua_AllToLua_cEntity_GetEquippedChestplate00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedChestplate'", NULL);
+#endif
+ {
+ cItem tolua_ret = (cItem) self->GetEquippedChestplate();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((cItem)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedChestplate'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedLeggings of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedLeggings00
+static int tolua_AllToLua_cEntity_GetEquippedLeggings00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedLeggings'", NULL);
+#endif
+ {
+ cItem tolua_ret = (cItem) self->GetEquippedLeggings();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((cItem)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedLeggings'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedBoots of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedBoots00
+static int tolua_AllToLua_cEntity_GetEquippedBoots00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedBoots'", NULL);
+#endif
+ {
+ cItem tolua_ret = (cItem) self->GetEquippedBoots();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((cItem)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedBoots'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: KilledBy of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_KilledBy00
+static int tolua_AllToLua_cEntity_KilledBy00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ cEntity* a_Killer = ((cEntity*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'KilledBy'", NULL);
+#endif
+ {
+ self->KilledBy(a_Killer);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'KilledBy'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Heal of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_Heal00
+static int tolua_AllToLua_cEntity_Heal00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ int a_HitPoints = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Heal'", NULL);
+#endif
+ {
+ self->Heal(a_HitPoints);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Heal'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHealth of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHealth00
+static int tolua_AllToLua_cEntity_GetHealth00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHealth'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetHealth();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHealth'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetHealth of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHealth00
+static int tolua_AllToLua_cEntity_SetHealth00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ int a_Health = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHealth'", NULL);
+#endif
+ {
+ self->SetHealth(a_Health);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetHealth'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetMaxHealth of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetMaxHealth00
+static int tolua_AllToLua_cEntity_SetMaxHealth00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ int a_MaxHealth = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMaxHealth'", NULL);
+#endif
+ {
+ self->SetMaxHealth(a_MaxHealth);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetMaxHealth'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMaxHealth of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetMaxHealth00
+static int tolua_AllToLua_cEntity_GetMaxHealth00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxHealth'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetMaxHealth();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMaxHealth'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: StartBurning of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_StartBurning00
+static int tolua_AllToLua_cEntity_StartBurning00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ int a_TicksLeftBurning = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'StartBurning'", NULL);
+#endif
+ {
+ self->StartBurning(a_TicksLeftBurning);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StartBurning'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: StopBurning of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_StopBurning00
+static int tolua_AllToLua_cEntity_StopBurning00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'StopBurning'", NULL);
+#endif
+ {
+ self->StopBurning();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StopBurning'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: TeleportToEntity of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TeleportToEntity00
+static int tolua_AllToLua_cEntity_TeleportToEntity00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ cEntity* a_Entity = ((cEntity*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TeleportToEntity'", NULL);
+#endif
+ {
+ self->TeleportToEntity(*a_Entity);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'TeleportToEntity'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: TeleportToCoords of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TeleportToCoords00
+static int tolua_AllToLua_cEntity_TeleportToCoords00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_PosX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_PosY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TeleportToCoords'", NULL);
+#endif
+ {
+ self->TeleportToCoords(a_PosX,a_PosY,a_PosZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'TeleportToCoords'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsOnFire of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsOnFire00
+static int tolua_AllToLua_cEntity_IsOnFire00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsOnFire'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsOnFire();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsOnFire'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsCrouched of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsCrouched00
+static int tolua_AllToLua_cEntity_IsCrouched00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCrouched'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsCrouched();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsCrouched'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsRiding of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsRiding00
+static int tolua_AllToLua_cEntity_IsRiding00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsRiding'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsRiding();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsRiding'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSprinting of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsSprinting00
+static int tolua_AllToLua_cEntity_IsSprinting00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSprinting'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSprinting();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSprinting'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsRclking of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsRclking00
+static int tolua_AllToLua_cEntity_IsRclking00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsRclking'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsRclking();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsRclking'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInvisible of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsInvisible00
+static int tolua_AllToLua_cEntity_IsInvisible00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInvisible'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsInvisible();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsInvisible'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetCurrentExperience of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetCurrentExperience00
+static int tolua_AllToLua_cPlayer_SetCurrentExperience00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ short a_XpTotal = ((short) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetCurrentExperience'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->SetCurrentExperience(a_XpTotal);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetCurrentExperience'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DeltaExperience of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_DeltaExperience00
+static int tolua_AllToLua_cPlayer_DeltaExperience00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ short a_Xp_delta = ((short) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeltaExperience'", NULL);
+#endif
+ {
+ short tolua_ret = (short) self->DeltaExperience(a_Xp_delta);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DeltaExperience'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetXpLifetimeTotal of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetXpLifetimeTotal00
+static int tolua_AllToLua_cPlayer_GetXpLifetimeTotal00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetXpLifetimeTotal'", NULL);
+#endif
+ {
+ short tolua_ret = (short) self->GetXpLifetimeTotal();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetXpLifetimeTotal'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetCurrentXp of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetCurrentXp00
+static int tolua_AllToLua_cPlayer_GetCurrentXp00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCurrentXp'", NULL);
+#endif
+ {
+ short tolua_ret = (short) self->GetCurrentXp();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetCurrentXp'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetXpLevel of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetXpLevel00
+static int tolua_AllToLua_cPlayer_GetXpLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetXpLevel'", NULL);
+#endif
+ {
+ short tolua_ret = (short) self->GetXpLevel();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetXpLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetXpPercentage of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetXpPercentage00
+static int tolua_AllToLua_cPlayer_GetXpPercentage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetXpPercentage'", NULL);
+#endif
+ {
+ float tolua_ret = (float) self->GetXpPercentage();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetXpPercentage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: XpForLevel of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_XpForLevel00
+static int tolua_AllToLua_cPlayer_XpForLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short int a_Level = ((short int) tolua_tonumber(tolua_S,2,0));
+ {
+ short tolua_ret = (short) cPlayer::XpForLevel(a_Level);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'XpForLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CalcLevelFromXp of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CalcLevelFromXp00
+static int tolua_AllToLua_cPlayer_CalcLevelFromXp00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ short int a_CurrentXp = ((short int) tolua_tonumber(tolua_S,2,0));
+ {
+ short tolua_ret = (short) cPlayer::CalcLevelFromXp(a_CurrentXp);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CalcLevelFromXp'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEyeHeight of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEyeHeight00
+static int tolua_AllToLua_cPlayer_GetEyeHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEyeHeight'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetEyeHeight();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEyeHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEyePosition of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEyePosition00
+static int tolua_AllToLua_cPlayer_GetEyePosition00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEyePosition'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->GetEyePosition();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEyePosition'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsOnGround of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsOnGround00
+static int tolua_AllToLua_cPlayer_IsOnGround00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsOnGround'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsOnGround();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsOnGround'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetStance of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetStance00
+static int tolua_AllToLua_cPlayer_GetStance00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStance'", NULL);
+#endif
+ {
+ const double tolua_ret = (const double) self->GetStance();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetStance'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetInventory of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetInventory00
+static int tolua_AllToLua_cPlayer_GetInventory00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventory'", NULL);
+#endif
+ {
+ cInventory& tolua_ret = (cInventory&) self->GetInventory();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cInventory");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetInventory'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedItem of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEquippedItem00
+static int tolua_AllToLua_cPlayer_GetEquippedItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedItem'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetEquippedItem();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetThrowStartPos of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetThrowStartPos00
+static int tolua_AllToLua_cPlayer_GetThrowStartPos00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetThrowStartPos'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->GetThrowStartPos();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetThrowStartPos'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetThrowSpeed of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetThrowSpeed00
+static int tolua_AllToLua_cPlayer_GetThrowSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+ double a_SpeedCoeff = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetThrowSpeed'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->GetThrowSpeed(a_SpeedCoeff);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetThrowSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetGameMode of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetGameMode00
+static int tolua_AllToLua_cPlayer_GetGameMode00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGameMode'", NULL);
+#endif
+ {
+ eGameMode tolua_ret = (eGameMode) self->GetGameMode();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetGameMode'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEffectiveGameMode of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEffectiveGameMode00
+static int tolua_AllToLua_cPlayer_GetEffectiveGameMode00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEffectiveGameMode'", NULL);
+#endif
+ {
+ eGameMode tolua_ret = (eGameMode) self->GetEffectiveGameMode();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEffectiveGameMode'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetGameMode of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetGameMode00
+static int tolua_AllToLua_cPlayer_SetGameMode00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ eGameMode a_GameMode = ((eGameMode) (int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetGameMode'", NULL);
+#endif
+ {
+ self->SetGameMode(a_GameMode);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetGameMode'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsGameModeCreative of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeCreative00
+static int tolua_AllToLua_cPlayer_IsGameModeCreative00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeCreative'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsGameModeCreative();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeCreative'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsGameModeSurvival of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeSurvival00
+static int tolua_AllToLua_cPlayer_IsGameModeSurvival00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeSurvival'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsGameModeSurvival();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeSurvival'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsGameModeAdventure of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeAdventure00
+static int tolua_AllToLua_cPlayer_IsGameModeAdventure00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeAdventure'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsGameModeAdventure();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeAdventure'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetIP of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetIP00
+static int tolua_AllToLua_cPlayer_GetIP00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIP'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetIP();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetIP'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MoveTo of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_MoveTo00
+static int tolua_AllToLua_cPlayer_MoveTo00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_NewPos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MoveTo'", NULL);
+#endif
+ {
+ self->MoveTo(*a_NewPos);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MoveTo'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWindow of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetWindow00
+static int tolua_AllToLua_cPlayer_GetWindow00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindow'", NULL);
+#endif
+ {
+ cWindow* tolua_ret = (cWindow*) self->GetWindow();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWindow");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWindow'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CloseWindow of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CloseWindow00
+static int tolua_AllToLua_cPlayer_CloseWindow00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ bool a_CanRefuse = ((bool) tolua_toboolean(tolua_S,2,true));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CloseWindow'", NULL);
+#endif
+ {
+ self->CloseWindow(a_CanRefuse);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CloseWindow'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CloseWindowIfID of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CloseWindowIfID00
+static int tolua_AllToLua_cPlayer_CloseWindowIfID00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,3,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ char a_WindowID = ((char) tolua_tonumber(tolua_S,2,0));
+ bool a_CanRefuse = ((bool) tolua_toboolean(tolua_S,3,true));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CloseWindowIfID'", NULL);
+#endif
+ {
+ self->CloseWindowIfID(a_WindowID,a_CanRefuse);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CloseWindowIfID'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetClientHandle of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetClientHandle00
+static int tolua_AllToLua_cPlayer_GetClientHandle00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetClientHandle'", NULL);
+#endif
+ {
+ cClientHandle* tolua_ret = (cClientHandle*) self->GetClientHandle();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cClientHandle");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetClientHandle'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SendMessage of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SendMessage00
+static int tolua_AllToLua_cPlayer_SendMessage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendMessage'", NULL);
+#endif
+ {
+ self->SendMessage(a_Message);
+ tolua_pushcppstring(tolua_S,(const char*)a_Message);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SendMessage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetName of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetName00
+static int tolua_AllToLua_cPlayer_GetName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetName();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetName of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetName00
+static int tolua_AllToLua_cPlayer_SetName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Name = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL);
+#endif
+ {
+ self->SetName(a_Name);
+ tolua_pushcppstring(tolua_S,(const char*)a_Name);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddToGroup of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_AddToGroup00
+static int tolua_AllToLua_cPlayer_AddToGroup00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const AString a_GroupName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddToGroup'", NULL);
+#endif
+ {
+ self->AddToGroup(a_GroupName);
+ tolua_pushcppstring(tolua_S,(const char*)a_GroupName);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddToGroup'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RemoveFromGroup of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_RemoveFromGroup00
+static int tolua_AllToLua_cPlayer_RemoveFromGroup00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const AString a_GroupName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveFromGroup'", NULL);
+#endif
+ {
+ self->RemoveFromGroup(a_GroupName);
+ tolua_pushcppstring(tolua_S,(const char*)a_GroupName);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RemoveFromGroup'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CanUseCommand of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CanUseCommand00
+static int tolua_AllToLua_cPlayer_CanUseCommand00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CanUseCommand'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->CanUseCommand(a_Command);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Command);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CanUseCommand'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HasPermission of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_HasPermission00
+static int tolua_AllToLua_cPlayer_HasPermission00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Permission = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasPermission'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->HasPermission(a_Permission);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Permission);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HasPermission'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInGroup of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsInGroup00
+static int tolua_AllToLua_cPlayer_IsInGroup00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Group = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInGroup'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsInGroup(a_Group);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Group);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsInGroup'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetColor of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetColor00
+static int tolua_AllToLua_cPlayer_GetColor00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetColor'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetColor();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetColor'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: TossItem of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_TossItem00
+static int tolua_AllToLua_cPlayer_TossItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ bool a_bDraggingItem = ((bool) tolua_toboolean(tolua_S,2,0));
+ char a_Amount = ((char) tolua_tonumber(tolua_S,3,1));
+ short a_CreateType = ((short) tolua_tonumber(tolua_S,4,0));
+ short a_CreateHealth = ((short) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TossItem'", NULL);
+#endif
+ {
+ self->TossItem(a_bDraggingItem,a_Amount,a_CreateType,a_CreateHealth);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'TossItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Heal of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Heal00
+static int tolua_AllToLua_cPlayer_Heal00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ int a_Health = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Heal'", NULL);
+#endif
+ {
+ self->Heal(a_Health);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Heal'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetFoodLevel of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodLevel00
+static int tolua_AllToLua_cPlayer_GetFoodLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodLevel'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetFoodLevel();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetFoodLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetFoodSaturationLevel of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodSaturationLevel00
+static int tolua_AllToLua_cPlayer_GetFoodSaturationLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodSaturationLevel'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetFoodSaturationLevel();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetFoodSaturationLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetFoodTickTimer of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodTickTimer00
+static int tolua_AllToLua_cPlayer_GetFoodTickTimer00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodTickTimer'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetFoodTickTimer();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetFoodTickTimer'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetFoodExhaustionLevel of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00
+static int tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodExhaustionLevel'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetFoodExhaustionLevel();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetFoodExhaustionLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetFoodPoisonedTicksRemaining of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00
+static int tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodPoisonedTicksRemaining'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetFoodPoisonedTicksRemaining();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetFoodPoisonedTicksRemaining'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetAirLevel of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetAirLevel00
+static int tolua_AllToLua_cPlayer_GetAirLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetAirLevel'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetAirLevel();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetAirLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSatiated of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSatiated00
+static int tolua_AllToLua_cPlayer_IsSatiated00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSatiated'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSatiated();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSatiated'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetFoodLevel of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodLevel00
+static int tolua_AllToLua_cPlayer_SetFoodLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ int a_FoodLevel = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodLevel'", NULL);
+#endif
+ {
+ self->SetFoodLevel(a_FoodLevel);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetFoodLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetFoodSaturationLevel of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodSaturationLevel00
+static int tolua_AllToLua_cPlayer_SetFoodSaturationLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ double a_FoodSaturationLevel = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodSaturationLevel'", NULL);
+#endif
+ {
+ self->SetFoodSaturationLevel(a_FoodSaturationLevel);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetFoodSaturationLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetFoodTickTimer of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodTickTimer00
+static int tolua_AllToLua_cPlayer_SetFoodTickTimer00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ int a_FoodTickTimer = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodTickTimer'", NULL);
+#endif
+ {
+ self->SetFoodTickTimer(a_FoodTickTimer);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetFoodTickTimer'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetFoodExhaustionLevel of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00
+static int tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ double a_FoodExhaustionLevel = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodExhaustionLevel'", NULL);
+#endif
+ {
+ self->SetFoodExhaustionLevel(a_FoodExhaustionLevel);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetFoodExhaustionLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetFoodPoisonedTicksRemaining of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00
+static int tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ int a_FoodPoisonedTicksRemaining = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodPoisonedTicksRemaining'", NULL);
+#endif
+ {
+ self->SetFoodPoisonedTicksRemaining(a_FoodPoisonedTicksRemaining);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetFoodPoisonedTicksRemaining'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Feed of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Feed00
+static int tolua_AllToLua_cPlayer_Feed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ int a_Food = ((int) tolua_tonumber(tolua_S,2,0));
+ double a_Saturation = ((double) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Feed'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Feed(a_Food,a_Saturation);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Feed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddFoodExhaustion of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_AddFoodExhaustion00
+static int tolua_AllToLua_cPlayer_AddFoodExhaustion00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ double a_Exhaustion = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddFoodExhaustion'", NULL);
+#endif
+ {
+ self->AddFoodExhaustion(a_Exhaustion);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddFoodExhaustion'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FoodPoison of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_FoodPoison00
+static int tolua_AllToLua_cPlayer_FoodPoison00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ int a_NumTicks = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FoodPoison'", NULL);
+#endif
+ {
+ self->FoodPoison(a_NumTicks);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FoodPoison'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsEating of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsEating00
+static int tolua_AllToLua_cPlayer_IsEating00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEating'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsEating();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsEating'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Respawn of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Respawn00
+static int tolua_AllToLua_cPlayer_Respawn00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Respawn'", NULL);
+#endif
+ {
+ self->Respawn();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Respawn'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetVisible of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetVisible00
+static int tolua_AllToLua_cPlayer_SetVisible00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ bool a_bVisible = ((bool) tolua_toboolean(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetVisible'", NULL);
+#endif
+ {
+ self->SetVisible(a_bVisible);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetVisible'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsVisible of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsVisible00
+static int tolua_AllToLua_cPlayer_IsVisible00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsVisible'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsVisible();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsVisible'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MoveToWorld of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_MoveToWorld00
+static int tolua_AllToLua_cPlayer_MoveToWorld00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const char* a_WorldName = ((const char*) tolua_tostring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MoveToWorld'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->MoveToWorld(a_WorldName);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MoveToWorld'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: LoadPermissionsFromDisk of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00
+static int tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadPermissionsFromDisk'", NULL);
+#endif
+ {
+ self->LoadPermissionsFromDisk();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'LoadPermissionsFromDisk'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMaxSpeed of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetMaxSpeed00
+static int tolua_AllToLua_cPlayer_GetMaxSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxSpeed'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetMaxSpeed();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMaxSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNormalMaxSpeed of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetNormalMaxSpeed00
+static int tolua_AllToLua_cPlayer_GetNormalMaxSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNormalMaxSpeed'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetNormalMaxSpeed();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNormalMaxSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSprintingMaxSpeed of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00
+static int tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSprintingMaxSpeed'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetSprintingMaxSpeed();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSprintingMaxSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetNormalMaxSpeed of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetNormalMaxSpeed00
+static int tolua_AllToLua_cPlayer_SetNormalMaxSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ double a_Speed = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetNormalMaxSpeed'", NULL);
+#endif
+ {
+ self->SetNormalMaxSpeed(a_Speed);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetNormalMaxSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSprintingMaxSpeed of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00
+static int tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ double a_Speed = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSprintingMaxSpeed'", NULL);
+#endif
+ {
+ self->SetSprintingMaxSpeed(a_Speed);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSprintingMaxSpeed'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetCrouch of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetCrouch00
+static int tolua_AllToLua_cPlayer_SetCrouch00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ bool a_IsCrouched = ((bool) tolua_toboolean(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetCrouch'", NULL);
+#endif
+ {
+ self->SetCrouch(a_IsCrouched);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetCrouch'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSprint of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetSprint00
+static int tolua_AllToLua_cPlayer_SetSprint00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ bool a_IsSprinting = ((bool) tolua_toboolean(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSprint'", NULL);
+#endif
+ {
+ self->SetSprint(a_IsSprinting);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSprint'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSwimming of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSwimming00
+static int tolua_AllToLua_cPlayer_IsSwimming00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSwimming'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSwimming();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSwimming'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSubmerged of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSubmerged00
+static int tolua_AllToLua_cPlayer_IsSubmerged00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSubmerged'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSubmerged();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSubmerged'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cPickup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_new00
+static int tolua_AllToLua_cPickup_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cPickup",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const cItem",0,&tolua_err)) ||
+ !tolua_isboolean(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,9,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,10,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ double a_PosX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_PosY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0));
+ bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,6,0));
+ float a_SpeedX = ((float) tolua_tonumber(tolua_S,7,0.f));
+ float a_SpeedY = ((float) tolua_tonumber(tolua_S,8,0.f));
+ float a_SpeedZ = ((float) tolua_tonumber(tolua_S,9,0.f));
+ {
+ cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_PosX,a_PosY,a_PosZ,*a_Item,IsPlayerCreated,a_SpeedX,a_SpeedY,a_SpeedZ));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cPickup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_new00_local
+static int tolua_AllToLua_cPickup_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cPickup",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const cItem",0,&tolua_err)) ||
+ !tolua_isboolean(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,9,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,10,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ double a_PosX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_PosY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0));
+ bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,6,0));
+ float a_SpeedX = ((float) tolua_tonumber(tolua_S,7,0.f));
+ float a_SpeedY = ((float) tolua_tonumber(tolua_S,8,0.f));
+ float a_SpeedZ = ((float) tolua_tonumber(tolua_S,9,0.f));
+ {
+ cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_PosX,a_PosY,a_PosZ,*a_Item,IsPlayerCreated,a_SpeedX,a_SpeedY,a_SpeedZ));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetItem of class cPickup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_GetItem00
+static int tolua_AllToLua_cPickup_GetItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPickup",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetItem'", NULL);
+#endif
+ {
+ cItem& tolua_ret = (cItem&) self->GetItem();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CollectedBy of class cPickup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_CollectedBy00
+static int tolua_AllToLua_cPickup_CollectedBy00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPickup",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0);
+ cPlayer* a_Dest = ((cPlayer*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CollectedBy'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->CollectedBy(a_Dest);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CollectedBy'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetAge of class cPickup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_GetAge00
+static int tolua_AllToLua_cPickup_GetAge00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetAge'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetAge();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetAge'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsCollected of class cPickup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_IsCollected00
+static int tolua_AllToLua_cPickup_IsCollected00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCollected'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsCollected();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsCollected'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsPlayerCreated of class cPickup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_IsPlayerCreated00
+static int tolua_AllToLua_cPickup_IsPlayerCreated00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPlayerCreated'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsPlayerCreated();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsPlayerCreated'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetProjectileKind of class cProjectileEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetProjectileKind00
+static int tolua_AllToLua_cProjectileEntity_GetProjectileKind00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetProjectileKind'", NULL);
+#endif
+ {
+ cProjectileEntity::eKind tolua_ret = (cProjectileEntity::eKind) self->GetProjectileKind();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetProjectileKind'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetCreator of class cProjectileEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetCreator00
+static int tolua_AllToLua_cProjectileEntity_GetCreator00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cProjectileEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cProjectileEntity* self = (cProjectileEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCreator'", NULL);
+#endif
+ {
+ cEntity* tolua_ret = (cEntity*) self->GetCreator();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEntity");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetCreator'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMCAClassName of class cProjectileEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetMCAClassName00
+static int tolua_AllToLua_cProjectileEntity_GetMCAClassName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMCAClassName'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetMCAClassName();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMCAClassName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInGround of class cProjectileEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_IsInGround00
+static int tolua_AllToLua_cProjectileEntity_IsInGround00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInGround'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsInGround();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsInGround'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPickupState of class cArrowEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_GetPickupState00
+static int tolua_AllToLua_cArrowEntity_GetPickupState00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPickupState'", NULL);
+#endif
+ {
+ cArrowEntity::ePickupState tolua_ret = (cArrowEntity::ePickupState) self->GetPickupState();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPickupState'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetPickupState of class cArrowEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetPickupState00
+static int tolua_AllToLua_cArrowEntity_SetPickupState00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0);
+ cArrowEntity::ePickupState a_PickupState = ((cArrowEntity::ePickupState) (int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPickupState'", NULL);
+#endif
+ {
+ self->SetPickupState(a_PickupState);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetPickupState'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetDamageCoeff of class cArrowEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_GetDamageCoeff00
+static int tolua_AllToLua_cArrowEntity_GetDamageCoeff00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDamageCoeff'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetDamageCoeff();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetDamageCoeff'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetDamageCoeff of class cArrowEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetDamageCoeff00
+static int tolua_AllToLua_cArrowEntity_SetDamageCoeff00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0);
+ double a_DamageCoeff = ((double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetDamageCoeff'", NULL);
+#endif
+ {
+ self->SetDamageCoeff(a_DamageCoeff);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetDamageCoeff'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CanPickup of class cArrowEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_CanPickup00
+static int tolua_AllToLua_cArrowEntity_CanPickup00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cPlayer",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0);
+ const cPlayer* a_Player = ((const cPlayer*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CanPickup'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->CanPickup(*a_Player);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CanPickup'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsCritical of class cArrowEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_IsCritical00
+static int tolua_AllToLua_cArrowEntity_IsCritical00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCritical'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsCritical();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsCritical'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetIsCritical of class cArrowEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetIsCritical00
+static int tolua_AllToLua_cArrowEntity_SetIsCritical00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0);
+ bool a_IsCritical = ((bool) tolua_toboolean(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIsCritical'", NULL);
+#endif
+ {
+ self->SetIsCritical(a_IsCritical);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetIsCritical'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Get of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_Get00
+static int tolua_AllToLua_cPluginManager_Get00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cPluginManager* tolua_ret = (cPluginManager*) cPluginManager::Get();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPluginManager");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPlugin of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetPlugin00
+static int tolua_AllToLua_cPluginManager_GetPlugin00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPluginManager",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPluginManager* self = (const cPluginManager*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Plugin = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPlugin'", NULL);
+#endif
+ {
+ cPlugin* tolua_ret = (cPlugin*) self->GetPlugin(a_Plugin);
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPlugin");
+ tolua_pushcppstring(tolua_S,(const char*)a_Plugin);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPlugin'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FindPlugins of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_FindPlugins00
+static int tolua_AllToLua_cPluginManager_FindPlugins00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindPlugins'", NULL);
+#endif
+ {
+ self->FindPlugins();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FindPlugins'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ReloadPlugins of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ReloadPlugins00
+static int tolua_AllToLua_cPluginManager_ReloadPlugins00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReloadPlugins'", NULL);
+#endif
+ {
+ self->ReloadPlugins();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ReloadPlugins'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumPlugins of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetNumPlugins00
+static int tolua_AllToLua_cPluginManager_GetNumPlugins00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPluginManager",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPluginManager* self = (const cPluginManager*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlugins'", NULL);
+#endif
+ {
+ unsigned int tolua_ret = (unsigned int) self->GetNumPlugins();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNumPlugins'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DisablePlugin of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_DisablePlugin00
+static int tolua_AllToLua_cPluginManager_DisablePlugin00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+ const AString a_PluginName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DisablePlugin'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DisablePlugin(a_PluginName);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_PluginName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DisablePlugin'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: LoadPlugin of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_LoadPlugin00
+static int tolua_AllToLua_cPluginManager_LoadPlugin00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+ const AString a_PluginName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadPlugin'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->LoadPlugin(a_PluginName);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_PluginName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'LoadPlugin'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsCommandBound of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_IsCommandBound00
+static int tolua_AllToLua_cPluginManager_IsCommandBound00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCommandBound'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsCommandBound(a_Command);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Command);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsCommandBound'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetCommandPermission of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetCommandPermission00
+static int tolua_AllToLua_cPluginManager_GetCommandPermission00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCommandPermission'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetCommandPermission(a_Command);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Command);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetCommandPermission'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ExecuteCommand of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ExecuteCommand00
+static int tolua_AllToLua_cPluginManager_ExecuteCommand00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+ cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0));
+ const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ExecuteCommand'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->ExecuteCommand(a_Player,a_Command);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Command);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ExecuteCommand'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ForceExecuteCommand of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ForceExecuteCommand00
+static int tolua_AllToLua_cPluginManager_ForceExecuteCommand00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+ cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0));
+ const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ForceExecuteCommand'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->ForceExecuteCommand(a_Player,a_Command);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Command);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ForceExecuteCommand'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsConsoleCommandBound of class cPluginManager */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_IsConsoleCommandBound00
+static int tolua_AllToLua_cPluginManager_IsConsoleCommandBound00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsConsoleCommandBound'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsConsoleCommandBound(a_Command);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Command);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsConsoleCommandBound'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetName of class cPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetName00
+static int tolua_AllToLua_cPlugin_GetName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetName();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetName of class cPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_SetName00
+static int tolua_AllToLua_cPlugin_SetName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlugin",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Name = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL);
+#endif
+ {
+ self->SetName(a_Name);
+ tolua_pushcppstring(tolua_S,(const char*)a_Name);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetVersion of class cPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetVersion00
+static int tolua_AllToLua_cPlugin_GetVersion00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetVersion'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetVersion();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetVersion'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetVersion of class cPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_SetVersion00
+static int tolua_AllToLua_cPlugin_SetVersion00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cPlugin",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0);
+ int a_Version = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetVersion'", NULL);
+#endif
+ {
+ self->SetVersion(a_Version);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetVersion'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetDirectory of class cPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetDirectory00
+static int tolua_AllToLua_cPlugin_GetDirectory00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDirectory'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetDirectory();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetDirectory'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetLocalDirectory of class cPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetLocalDirectory00
+static int tolua_AllToLua_cPlugin_GetLocalDirectory00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLocalDirectory'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetLocalDirectory();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetLocalDirectory'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetLocalFolder of class cPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetLocalFolder00
+static int tolua_AllToLua_cPlugin_GetLocalFolder00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLocalFolder'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetLocalFolder();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetLocalFolder'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: __cWebPlugin__ of class cPluginLua */
+#ifndef TOLUA_DISABLE_tolua_get_cPluginLua___cWebPlugin__
+static int tolua_get_cPluginLua___cWebPlugin__(lua_State* tolua_S)
+{
+ cPluginLua* self = (cPluginLua*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable '__cWebPlugin__'",NULL);
+#endif
+#ifdef __cplusplus
+ tolua_pushusertype(tolua_S,(void*)static_cast<cWebPlugin*>(self), "cWebPlugin");
+#else
+ tolua_pushusertype(tolua_S,(void*)((cWebPlugin*)self), "cWebPlugin");
+#endif
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetDescription of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetDescription00
+static int tolua_AllToLua_cServer_GetDescription00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDescription'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetDescription();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetDescription'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMaxPlayers of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetMaxPlayers00
+static int tolua_AllToLua_cServer_GetMaxPlayers00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxPlayers'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetMaxPlayers();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMaxPlayers'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumPlayers of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetNumPlayers00
+static int tolua_AllToLua_cServer_GetNumPlayers00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlayers'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNumPlayers();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNumPlayers'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetMaxPlayers of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_SetMaxPlayers00
+static int tolua_AllToLua_cServer_SetMaxPlayers00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0);
+ int a_MaxPlayers = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMaxPlayers'", NULL);
+#endif
+ {
+ self->SetMaxPlayers(a_MaxPlayers);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetMaxPlayers'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsHardcore of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_IsHardcore00
+static int tolua_AllToLua_cServer_IsHardcore00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsHardcore'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsHardcore();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsHardcore'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetServerID of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetServerID00
+static int tolua_AllToLua_cServer_GetServerID00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetServerID'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetServerID();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetServerID'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetTicksUntilWeatherChange of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00
+static int tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTicksUntilWeatherChange'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetTicksUntilWeatherChange();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetTicksUntilWeatherChange'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWorldAge of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetWorldAge00
+static int tolua_AllToLua_cWorld_GetWorldAge00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorldAge'", NULL);
+#endif
+ {
+ long long tolua_ret = ( long long) self->GetWorldAge();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWorldAge'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetTimeOfDay of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetTimeOfDay00
+static int tolua_AllToLua_cWorld_GetTimeOfDay00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTimeOfDay'", NULL);
+#endif
+ {
+ long long tolua_ret = ( long long) self->GetTimeOfDay();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetTimeOfDay'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetTicksUntilWeatherChange of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00
+static int tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_WeatherInterval = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetTicksUntilWeatherChange'", NULL);
+#endif
+ {
+ self->SetTicksUntilWeatherChange(a_WeatherInterval);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetTicksUntilWeatherChange'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetTimeOfDay of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetTimeOfDay00
+static int tolua_AllToLua_cWorld_SetTimeOfDay00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ long long a_TimeOfDay = (( long long) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetTimeOfDay'", NULL);
+#endif
+ {
+ self->SetTimeOfDay(a_TimeOfDay);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetTimeOfDay'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetGameMode of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetGameMode00
+static int tolua_AllToLua_cWorld_GetGameMode00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGameMode'", NULL);
+#endif
+ {
+ eGameMode tolua_ret = (eGameMode) self->GetGameMode();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetGameMode'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsGameModeCreative of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeCreative00
+static int tolua_AllToLua_cWorld_IsGameModeCreative00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeCreative'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsGameModeCreative();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeCreative'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsGameModeSurvival of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeSurvival00
+static int tolua_AllToLua_cWorld_IsGameModeSurvival00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeSurvival'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsGameModeSurvival();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeSurvival'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsGameModeAdventure of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeAdventure00
+static int tolua_AllToLua_cWorld_IsGameModeAdventure00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeAdventure'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsGameModeAdventure();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeAdventure'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsPVPEnabled of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsPVPEnabled00
+static int tolua_AllToLua_cWorld_IsPVPEnabled00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPVPEnabled'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsPVPEnabled();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsPVPEnabled'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsDeepSnowEnabled of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsDeepSnowEnabled00
+static int tolua_AllToLua_cWorld_IsDeepSnowEnabled00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDeepSnowEnabled'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsDeepSnowEnabled();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsDeepSnowEnabled'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetDimension of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetDimension00
+static int tolua_AllToLua_cWorld_GetDimension00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDimension'", NULL);
+#endif
+ {
+ eDimension tolua_ret = (eDimension) self->GetDimension();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetDimension'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHeight of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetHeight00
+static int tolua_AllToLua_cWorld_GetHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetHeight(a_BlockX,a_BlockZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: BroadcastChat of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastChat00
+static int tolua_AllToLua_cWorld_BroadcastChat00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,3,"const cClientHandle",1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,3,NULL));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastChat'", NULL);
+#endif
+ {
+ self->BroadcastChat(a_Message,a_Exclude);
+ tolua_pushcppstring(tolua_S,(const char*)a_Message);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'BroadcastChat'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: BroadcastSoundEffect of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastSoundEffect00
+static int tolua_AllToLua_cWorld_BroadcastSoundEffect00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,8,"const cClientHandle",1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,9,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ const AString a_SoundName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ int a_SrcX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_SrcY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_SrcZ = ((int) tolua_tonumber(tolua_S,5,0));
+ float a_Volume = ((float) tolua_tonumber(tolua_S,6,0));
+ float a_Pitch = ((float) tolua_tonumber(tolua_S,7,0));
+ const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,8,NULL));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastSoundEffect'", NULL);
+#endif
+ {
+ self->BroadcastSoundEffect(a_SoundName,a_SrcX,a_SrcY,a_SrcZ,a_Volume,a_Pitch,a_Exclude);
+ tolua_pushcppstring(tolua_S,(const char*)a_SoundName);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'BroadcastSoundEffect'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: BroadcastSoundParticleEffect of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00
+static int tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,7,"const cClientHandle",1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_EffectID = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_SrcX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_SrcY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_SrcZ = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_Data = ((int) tolua_tonumber(tolua_S,6,0));
+ const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,7,NULL));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastSoundParticleEffect'", NULL);
+#endif
+ {
+ self->BroadcastSoundParticleEffect(a_EffectID,a_SrcX,a_SrcY,a_SrcZ,a_Data,a_Exclude);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'BroadcastSoundParticleEffect'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: UnloadUnusedChunks of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_UnloadUnusedChunks00
+static int tolua_AllToLua_cWorld_UnloadUnusedChunks00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'UnloadUnusedChunks'", NULL);
+#endif
+ {
+ self->UnloadUnusedChunks();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'UnloadUnusedChunks'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RegenerateChunk of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_RegenerateChunk00
+static int tolua_AllToLua_cWorld_RegenerateChunk00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RegenerateChunk'", NULL);
+#endif
+ {
+ self->RegenerateChunk(a_ChunkX,a_ChunkZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RegenerateChunk'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GenerateChunk of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GenerateChunk00
+static int tolua_AllToLua_cWorld_GenerateChunk00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GenerateChunk'", NULL);
+#endif
+ {
+ self->GenerateChunk(a_ChunkX,a_ChunkZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GenerateChunk'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlock of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlock00
+static int tolua_AllToLua_cWorld_SetBlock00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlock'", NULL);
+#endif
+ {
+ self->SetBlock(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBlock'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FastSetBlock of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_FastSetBlock00
+static int tolua_AllToLua_cWorld_FastSetBlock00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FastSetBlock'", NULL);
+#endif
+ {
+ self->FastSetBlock(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FastSetBlock'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: QueueSetBlock of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueSetBlock00
+static int tolua_AllToLua_cWorld_QueueSetBlock00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BLockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+ int a_TickDelay = ((int) tolua_tonumber(tolua_S,7,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueSetBlock'", NULL);
+#endif
+ {
+ self->QueueSetBlock(a_BlockX,a_BLockY,a_BlockZ,a_BlockType,a_BlockMeta,a_TickDelay);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'QueueSetBlock'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlock of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlock00
+static int tolua_AllToLua_cWorld_GetBlock00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlock'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlock(a_BlockX,a_BlockY,a_BlockZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlock'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockMeta of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockMeta00
+static int tolua_AllToLua_cWorld_GetBlockMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_BlockX,a_BlockY,a_BlockZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlockMeta of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlockMeta00
+static int tolua_AllToLua_cWorld_SetBlockMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_MetaData = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL);
+#endif
+ {
+ self->SetBlockMeta(a_BlockX,a_BlockY,a_BlockZ,a_MetaData);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockSkyLight of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockSkyLight00
+static int tolua_AllToLua_cWorld_GetBlockSkyLight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockSkyLight'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockSkyLight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockBlockLight of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockBlockLight00
+static int tolua_AllToLua_cWorld_GetBlockBlockLight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockBlockLight'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockBlockLight(a_BlockX,a_BlockY,a_BlockZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockBlockLight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FastSetBlock of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_FastSetBlock01
+static int tolua_AllToLua_cWorld_FastSetBlock01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FastSetBlock'", NULL);
+#endif
+ {
+ self->FastSetBlock(*a_Pos,a_BlockType,a_BlockMeta);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cWorld_FastSetBlock00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlock of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlock01
+static int tolua_AllToLua_cWorld_GetBlock01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlock'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlock(*a_Pos);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cWorld_GetBlock00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockMeta of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockMeta01
+static int tolua_AllToLua_cWorld_GetBlockMeta01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(*a_Pos);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cWorld_GetBlockMeta00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlockMeta of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlockMeta01
+static int tolua_AllToLua_cWorld_SetBlockMeta01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+ unsigned char a_MetaData = (( unsigned char) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL);
+#endif
+ {
+ self->SetBlockMeta(*a_Pos,a_MetaData);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cWorld_SetBlockMeta00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SpawnItemPickups of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnItemPickups00
+static int tolua_AllToLua_cWorld_SpawnItemPickups00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItems",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,1,&tolua_err) ||
+ !tolua_isboolean(tolua_S,7,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ const cItems* a_Pickups = ((const cItems*) tolua_tousertype(tolua_S,2,0));
+ double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0));
+ double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0));
+ double a_FlyAwaySpeed = ((double) tolua_tonumber(tolua_S,6,1.0));
+ bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,7,false));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnItemPickups'", NULL);
+#endif
+ {
+ self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_FlyAwaySpeed,IsPlayerCreated);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SpawnItemPickups'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SpawnItemPickups of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnItemPickups01
+static int tolua_AllToLua_cWorld_SpawnItemPickups01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItems",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,9,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,10,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ const cItems* a_Pickups = ((const cItems*) tolua_tousertype(tolua_S,2,0));
+ double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0));
+ double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0));
+ double a_SpeedX = ((double) tolua_tonumber(tolua_S,6,0));
+ double a_SpeedY = ((double) tolua_tonumber(tolua_S,7,0));
+ double a_SpeedZ = ((double) tolua_tonumber(tolua_S,8,0));
+ bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,9,false));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnItemPickups'", NULL);
+#endif
+ {
+ self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_SpeedX,a_SpeedY,a_SpeedZ,IsPlayerCreated);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cWorld_SpawnItemPickups00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SpawnPrimedTNT of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnPrimedTNT00
+static int tolua_AllToLua_cWorld_SpawnPrimedTNT00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ double a_X = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_Y = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_Z = ((double) tolua_tonumber(tolua_S,4,0));
+ double a_FuseTimeInSec = ((double) tolua_tonumber(tolua_S,5,0));
+ double a_InitialVelocityCoeff = ((double) tolua_tonumber(tolua_S,6,1));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnPrimedTNT'", NULL);
+#endif
+ {
+ self->SpawnPrimedTNT(a_X,a_Y,a_Z,a_FuseTimeInSec,a_InitialVelocityCoeff);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SpawnPrimedTNT'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DigBlock of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_DigBlock00
+static int tolua_AllToLua_cWorld_DigBlock00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_Z = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DigBlock'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DigBlock(a_X,a_Y,a_Z);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DigBlock'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SendBlockTo of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SendBlockTo00
+static int tolua_AllToLua_cWorld_SendBlockTo00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,5,"cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_Z = ((int) tolua_tonumber(tolua_S,4,0));
+ cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendBlockTo'", NULL);
+#endif
+ {
+ self->SendBlockTo(a_X,a_Y,a_Z,a_Player);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SendBlockTo'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSpawnX of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnX00
+static int tolua_AllToLua_cWorld_GetSpawnX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpawnX'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetSpawnX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSpawnX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSpawnY of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnY00
+static int tolua_AllToLua_cWorld_GetSpawnY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpawnY'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetSpawnY();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSpawnY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSpawnZ of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnZ00
+static int tolua_AllToLua_cWorld_GetSpawnZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpawnZ'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->GetSpawnZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSpawnZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: WakeUpSimulators of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_WakeUpSimulators00
+static int tolua_AllToLua_cWorld_WakeUpSimulators00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WakeUpSimulators'", NULL);
+#endif
+ {
+ self->WakeUpSimulators(a_BlockX,a_BlockY,a_BlockZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'WakeUpSimulators'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: WakeUpSimulatorsInArea of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00
+static int tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_MinBlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,7,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WakeUpSimulatorsInArea'", NULL);
+#endif
+ {
+ self->WakeUpSimulatorsInArea(a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'WakeUpSimulatorsInArea'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DoExplosionAt of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_DoExplosionAt00
+static int tolua_AllToLua_cWorld_DoExplosionAt00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isuserdata(tolua_S,8,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,9,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ double a_ExplosionSize = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0));
+ double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0));
+ bool a_CanCauseFire = ((bool) tolua_toboolean(tolua_S,6,0));
+ eExplosionSource a_Source = ((eExplosionSource) (int) tolua_tonumber(tolua_S,7,0));
+ void* a_SourceData = ((void*) tolua_touserdata(tolua_S,8,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoExplosionAt'", NULL);
+#endif
+ {
+ self->DoExplosionAt(a_ExplosionSize,a_BlockX,a_BlockY,a_BlockZ,a_CanCauseFire,a_Source,a_SourceData);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DoExplosionAt'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: UseBlockEntity of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_UseBlockEntity00
+static int tolua_AllToLua_cWorld_UseBlockEntity00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0));
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'UseBlockEntity'", NULL);
+#endif
+ {
+ self->UseBlockEntity(a_Player,a_BlockX,a_BlockY,a_BlockZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'UseBlockEntity'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GrowTree of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTree00
+static int tolua_AllToLua_cWorld_GrowTree00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTree'", NULL);
+#endif
+ {
+ self->GrowTree(a_BlockX,a_BlockY,a_BlockZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GrowTree'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GrowTreeFromSapling of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTreeFromSapling00
+static int tolua_AllToLua_cWorld_GrowTreeFromSapling00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_SaplingMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeFromSapling'", NULL);
+#endif
+ {
+ self->GrowTreeFromSapling(a_BlockX,a_BlockY,a_BlockZ,a_SaplingMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GrowTreeFromSapling'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GrowTreeByBiome of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTreeByBiome00
+static int tolua_AllToLua_cWorld_GrowTreeByBiome00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeByBiome'", NULL);
+#endif
+ {
+ self->GrowTreeByBiome(a_BlockX,a_BlockY,a_BlockZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GrowTreeByBiome'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GrowRipePlant of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowRipePlant00
+static int tolua_AllToLua_cWorld_GrowRipePlant00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,5,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ bool a_IsByBonemeal = ((bool) tolua_toboolean(tolua_S,5,false));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowRipePlant'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->GrowRipePlant(a_BlockX,a_BlockY,a_BlockZ,a_IsByBonemeal);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GrowRipePlant'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GrowCactus of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowCactus00
+static int tolua_AllToLua_cWorld_GrowCactus00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_NumBlocksToGrow = ((int) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowCactus'", NULL);
+#endif
+ {
+ self->GrowCactus(a_BlockX,a_BlockY,a_BlockZ,a_NumBlocksToGrow);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GrowCactus'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GrowMelonPumpkin of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowMelonPumpkin00
+static int tolua_AllToLua_cWorld_GrowMelonPumpkin00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowMelonPumpkin'", NULL);
+#endif
+ {
+ self->GrowMelonPumpkin(a_BlockX,a_BlockY,a_BlockZ,a_BlockType);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GrowMelonPumpkin'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GrowSugarcane of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowSugarcane00
+static int tolua_AllToLua_cWorld_GrowSugarcane00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_NumBlocksToGrow = ((int) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowSugarcane'", NULL);
+#endif
+ {
+ self->GrowSugarcane(a_BlockX,a_BlockY,a_BlockZ,a_NumBlocksToGrow);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GrowSugarcane'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBiomeAt of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBiomeAt00
+static int tolua_AllToLua_cWorld_GetBiomeAt00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBiomeAt'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetBiomeAt(a_BlockX,a_BlockZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBiomeAt'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetName of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetName00
+static int tolua_AllToLua_cWorld_GetName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetName();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetIniFileName of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetIniFileName00
+static int tolua_AllToLua_cWorld_GetIniFileName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIniFileName'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetIniFileName();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetIniFileName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: QueueSaveAllChunks of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueSaveAllChunks00
+static int tolua_AllToLua_cWorld_QueueSaveAllChunks00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueSaveAllChunks'", NULL);
+#endif
+ {
+ self->QueueSaveAllChunks();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'QueueSaveAllChunks'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumChunks of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetNumChunks00
+static int tolua_AllToLua_cWorld_GetNumChunks00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumChunks'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNumChunks();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNumChunks'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetGeneratorQueueLength of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetGeneratorQueueLength00
+static int tolua_AllToLua_cWorld_GetGeneratorQueueLength00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGeneratorQueueLength'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetGeneratorQueueLength();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetGeneratorQueueLength'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetLightingQueueLength of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetLightingQueueLength00
+static int tolua_AllToLua_cWorld_GetLightingQueueLength00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLightingQueueLength'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetLightingQueueLength();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetLightingQueueLength'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetStorageLoadQueueLength of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetStorageLoadQueueLength00
+static int tolua_AllToLua_cWorld_GetStorageLoadQueueLength00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStorageLoadQueueLength'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetStorageLoadQueueLength();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetStorageLoadQueueLength'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetStorageSaveQueueLength of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetStorageSaveQueueLength00
+static int tolua_AllToLua_cWorld_GetStorageSaveQueueLength00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStorageSaveQueueLength'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetStorageSaveQueueLength();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetStorageSaveQueueLength'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: QueueBlockForTick of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueBlockForTick00
+static int tolua_AllToLua_cWorld_QueueBlockForTick00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_TicksToWait = ((int) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueBlockForTick'", NULL);
+#endif
+ {
+ self->QueueBlockForTick(a_BlockX,a_BlockY,a_BlockZ,a_TicksToWait);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'QueueBlockForTick'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CastThunderbolt of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_CastThunderbolt00
+static int tolua_AllToLua_cWorld_CastThunderbolt00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CastThunderbolt'", NULL);
+#endif
+ {
+ self->CastThunderbolt(a_BlockX,a_BlockY,a_BlockZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CastThunderbolt'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetWeather of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetWeather00
+static int tolua_AllToLua_cWorld_SetWeather00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ eWeather a_NewWeather = ((eWeather) (int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWeather'", NULL);
+#endif
+ {
+ self->SetWeather(a_NewWeather);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetWeather'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ChangeWeather of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_ChangeWeather00
+static int tolua_AllToLua_cWorld_ChangeWeather00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeWeather'", NULL);
+#endif
+ {
+ self->ChangeWeather();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ChangeWeather'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWeather of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetWeather00
+static int tolua_AllToLua_cWorld_GetWeather00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWeather'", NULL);
+#endif
+ {
+ eWeather tolua_ret = (eWeather) self->GetWeather();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWeather'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsWeatherSunny of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherSunny00
+static int tolua_AllToLua_cWorld_IsWeatherSunny00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherSunny'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsWeatherSunny();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsWeatherSunny'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsWeatherRain of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherRain00
+static int tolua_AllToLua_cWorld_IsWeatherRain00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherRain'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsWeatherRain();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsWeatherRain'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsWeatherStorm of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherStorm00
+static int tolua_AllToLua_cWorld_IsWeatherStorm00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherStorm'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsWeatherStorm();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsWeatherStorm'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsWeatherWet of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherWet00
+static int tolua_AllToLua_cWorld_IsWeatherWet00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherWet'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsWeatherWet();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsWeatherWet'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetNextBlockTick of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetNextBlockTick00
+static int tolua_AllToLua_cWorld_SetNextBlockTick00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetNextBlockTick'", NULL);
+#endif
+ {
+ self->SetNextBlockTick(a_BlockX,a_BlockY,a_BlockZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetNextBlockTick'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMaxSugarcaneHeight of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00
+static int tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxSugarcaneHeight'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetMaxSugarcaneHeight();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMaxSugarcaneHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMaxCactusHeight of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetMaxCactusHeight00
+static int tolua_AllToLua_cWorld_GetMaxCactusHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxCactusHeight'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetMaxCactusHeight();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMaxCactusHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsBlockDirectlyWatered of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsBlockDirectlyWatered00
+static int tolua_AllToLua_cWorld_IsBlockDirectlyWatered00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsBlockDirectlyWatered'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsBlockDirectlyWatered(a_BlockX,a_BlockY,a_BlockZ);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsBlockDirectlyWatered'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SpawnMob of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnMob00
+static int tolua_AllToLua_cWorld_SpawnMob00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ double a_PosX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_PosY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0));
+ cMonster::eType a_MonsterType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnMob'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->SpawnMob(a_PosX,a_PosY,a_PosZ,a_MonsterType);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SpawnMob'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CreateProjectile of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_CreateProjectile00
+static int tolua_AllToLua_cWorld_CreateProjectile00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,6,"cEntity",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,7,"const Vector3d",1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ double a_PosX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_PosY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0));
+ cProjectileEntity::eKind a_Kind = ((cProjectileEntity::eKind) (int) tolua_tonumber(tolua_S,5,0));
+ cEntity* a_Creator = ((cEntity*) tolua_tousertype(tolua_S,6,0));
+ const Vector3d* a_Speed = ((const Vector3d*) tolua_tousertype(tolua_S,7,NULL));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CreateProjectile'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->CreateProjectile(a_PosX,a_PosY,a_PosZ,a_Kind,a_Creator,a_Speed);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CreateProjectile'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Clear of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_Clear00
+static int tolua_AllToLua_cInventory_Clear00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL);
+#endif
+ {
+ self->Clear();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HowManyCanFit of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyCanFit00
+static int tolua_AllToLua_cInventory_HowManyCanFit00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isboolean(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+ bool a_ConsiderEmptySlots = ((bool) tolua_toboolean(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_ConsiderEmptySlots);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HowManyCanFit'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HowManyCanFit of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyCanFit01
+static int tolua_AllToLua_cInventory_HowManyCanFit01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+ int a_BeginSlotNum = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_EndSlotNum = ((int) tolua_tonumber(tolua_S,4,0));
+ bool a_ConsiderEmptySlots = ((bool) tolua_toboolean(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_BeginSlotNum,a_EndSlotNum,a_ConsiderEmptySlots);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cInventory_HowManyCanFit00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddItem of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_AddItem00
+static int tolua_AllToLua_cInventory_AddItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isboolean(tolua_S,3,1,&tolua_err) ||
+ !tolua_isboolean(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+ bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true));
+ bool a_tryToFillEquippedFirst = ((bool) tolua_toboolean(tolua_S,4,false));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItem'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->AddItem(*a_ItemStack,a_AllowNewStacks,a_tryToFillEquippedFirst);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddItems of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_AddItems00
+static int tolua_AllToLua_cInventory_AddItems00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) ||
+ !tolua_isboolean(tolua_S,3,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ cItems* a_ItemStackList = ((cItems*) tolua_tousertype(tolua_S,2,0));
+ bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,0));
+ bool a_tryToFillEquippedFirst = ((bool) tolua_toboolean(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItems'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->AddItems(*a_ItemStackList,a_AllowNewStacks,a_tryToFillEquippedFirst);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddItems'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RemoveOneEquippedItem of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_RemoveOneEquippedItem00
+static int tolua_AllToLua_cInventory_RemoveOneEquippedItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneEquippedItem'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->RemoveOneEquippedItem();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RemoveOneEquippedItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HowManyItems of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyItems00
+static int tolua_AllToLua_cInventory_HowManyItems00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyItems'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->HowManyItems(*a_Item);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HowManyItems'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HasItems of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HasItems00
+static int tolua_AllToLua_cInventory_HasItems00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasItems'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->HasItems(*a_ItemStack);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HasItems'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetArmorGrid of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetArmorGrid00
+static int tolua_AllToLua_cInventory_GetArmorGrid00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorGrid'", NULL);
+#endif
+ {
+ cItemGrid& tolua_ret = (cItemGrid&) self->GetArmorGrid();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetArmorGrid'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetInventoryGrid of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetInventoryGrid00
+static int tolua_AllToLua_cInventory_GetInventoryGrid00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventoryGrid'", NULL);
+#endif
+ {
+ cItemGrid& tolua_ret = (cItemGrid&) self->GetInventoryGrid();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetInventoryGrid'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHotbarGrid of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetHotbarGrid00
+static int tolua_AllToLua_cInventory_GetHotbarGrid00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHotbarGrid'", NULL);
+#endif
+ {
+ cItemGrid& tolua_ret = (cItemGrid&) self->GetHotbarGrid();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHotbarGrid'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetOwner of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetOwner00
+static int tolua_AllToLua_cInventory_GetOwner00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOwner'", NULL);
+#endif
+ {
+ cPlayer& tolua_ret = (cPlayer&) self->GetOwner();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cPlayer");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetOwner'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CopyToItems of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_CopyToItems00
+static int tolua_AllToLua_cInventory_CopyToItems00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ cItems* a_Items = ((cItems*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyToItems'", NULL);
+#endif
+ {
+ self->CopyToItems(*a_Items);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CopyToItems'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSlot of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetSlot00
+static int tolua_AllToLua_cInventory_GetSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetArmorSlot of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetArmorSlot00
+static int tolua_AllToLua_cInventory_GetArmorSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_ArmorSlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorSlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetArmorSlot(a_ArmorSlotNum);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetArmorSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetInventorySlot of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetInventorySlot00
+static int tolua_AllToLua_cInventory_GetInventorySlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_InventorySlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventorySlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetInventorySlot(a_InventorySlotNum);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetInventorySlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHotbarSlot of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetHotbarSlot00
+static int tolua_AllToLua_cInventory_GetHotbarSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_HotBarSlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHotbarSlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetHotbarSlot(a_HotBarSlotNum);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHotbarSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedItem of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedItem00
+static int tolua_AllToLua_cInventory_GetEquippedItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedItem'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetEquippedItem();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSlot of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetSlot00
+static int tolua_AllToLua_cInventory_SetSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL);
+#endif
+ {
+ self->SetSlot(a_SlotNum,*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetArmorSlot of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetArmorSlot00
+static int tolua_AllToLua_cInventory_SetArmorSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_ArmorSlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetArmorSlot'", NULL);
+#endif
+ {
+ self->SetArmorSlot(a_ArmorSlotNum,*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetArmorSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetInventorySlot of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetInventorySlot00
+static int tolua_AllToLua_cInventory_SetInventorySlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_InventorySlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetInventorySlot'", NULL);
+#endif
+ {
+ self->SetInventorySlot(a_InventorySlotNum,*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetInventorySlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetHotbarSlot of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetHotbarSlot00
+static int tolua_AllToLua_cInventory_SetHotbarSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_HotBarSlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHotbarSlot'", NULL);
+#endif
+ {
+ self->SetHotbarSlot(a_HotBarSlotNum,*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetHotbarSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetEquippedSlotNum of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetEquippedSlotNum00
+static int tolua_AllToLua_cInventory_SetEquippedSlotNum00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetEquippedSlotNum'", NULL);
+#endif
+ {
+ self->SetEquippedSlotNum(a_SlotNum);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetEquippedSlotNum'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedSlotNum of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedSlotNum00
+static int tolua_AllToLua_cInventory_GetEquippedSlotNum00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedSlotNum'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetEquippedSlotNum();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedSlotNum'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ChangeSlotCount of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_ChangeSlotCount00
+static int tolua_AllToLua_cInventory_ChangeSlotCount00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_AddToCount = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->ChangeSlotCount(a_SlotNum,a_AddToCount);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ChangeSlotCount'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DamageItem of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_DamageItem00
+static int tolua_AllToLua_cInventory_DamageItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ short a_Amount = ((short) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DamageItem(a_SlotNum,a_Amount);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DamageEquippedItem of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_DamageEquippedItem00
+static int tolua_AllToLua_cInventory_DamageEquippedItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0);
+ short a_Amount = ((short) tolua_tonumber(tolua_S,2,1));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageEquippedItem'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DamageEquippedItem(a_Amount);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DamageEquippedItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedHelmet of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedHelmet00
+static int tolua_AllToLua_cInventory_GetEquippedHelmet00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedHelmet'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetEquippedHelmet();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedHelmet'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedChestplate of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedChestplate00
+static int tolua_AllToLua_cInventory_GetEquippedChestplate00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedChestplate'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetEquippedChestplate();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedChestplate'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedLeggings of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedLeggings00
+static int tolua_AllToLua_cInventory_GetEquippedLeggings00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedLeggings'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetEquippedLeggings();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedLeggings'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetEquippedBoots of class cInventory */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedBoots00
+static int tolua_AllToLua_cInventory_GetEquippedBoots00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedBoots'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetEquippedBoots();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetEquippedBoots'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new00
+static int tolua_AllToLua_cEnchantments_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new00_local
+static int tolua_AllToLua_cEnchantments_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new01
+static int tolua_AllToLua_cEnchantments_new01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)(a_StringSpec));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments");
+ tolua_pushcppstring(tolua_S,(const char*)a_StringSpec);
+ }
+ }
+ return 2;
+tolua_lerror:
+ return tolua_AllToLua_cEnchantments_new00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new01_local
+static int tolua_AllToLua_cEnchantments_new01_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)(a_StringSpec));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ tolua_pushcppstring(tolua_S,(const char*)a_StringSpec);
+ }
+ }
+ return 2;
+tolua_lerror:
+ return tolua_AllToLua_cEnchantments_new00_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddFromString of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_AddFromString00
+static int tolua_AllToLua_cEnchantments_AddFromString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0);
+ const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddFromString'", NULL);
+#endif
+ {
+ self->AddFromString(a_StringSpec);
+ tolua_pushcppstring(tolua_S,(const char*)a_StringSpec);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddFromString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ToString of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_ToString00
+static int tolua_AllToLua_cEnchantments_ToString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ToString'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->ToString();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ToString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetLevel of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_GetLevel00
+static int tolua_AllToLua_cEnchantments_GetLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0);
+ int a_EnchantmentID = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLevel'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetLevel(a_EnchantmentID);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetLevel of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_SetLevel00
+static int tolua_AllToLua_cEnchantments_SetLevel00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0);
+ int a_EnchantmentID = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Level = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLevel'", NULL);
+#endif
+ {
+ self->SetLevel(a_EnchantmentID,a_Level);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetLevel'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Clear of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_Clear00
+static int tolua_AllToLua_cEnchantments_Clear00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL);
+#endif
+ {
+ self->Clear();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsEmpty of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_IsEmpty00
+static int tolua_AllToLua_cEnchantments_IsEmpty00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEmpty'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsEmpty();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsEmpty'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: StringToEnchantmentID of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_StringToEnchantmentID00
+static int tolua_AllToLua_cEnchantments_StringToEnchantmentID00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_EnchantmentName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ int tolua_ret = (int) cEnchantments::StringToEnchantmentID(a_EnchantmentName);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_EnchantmentName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StringToEnchantmentID'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator== of class cEnchantments */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments__eq00
+static int tolua_AllToLua_cEnchantments__eq00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEnchantments",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0);
+ const cEnchantments* a_Other = ((const cEnchantments*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator=='", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->operator==(*a_Other);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function '.eq'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new00
+static int tolua_AllToLua_cItem_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cItem* tolua_ret = (cItem*) Mtolua_new((cItem)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new00_local
+static int tolua_AllToLua_cItem_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cItem* tolua_ret = (cItem*) Mtolua_new((cItem)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new01
+static int tolua_AllToLua_cItem_new01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0));
+ char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,1));
+ short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0));
+ {
+ cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cItem_new00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new01_local
+static int tolua_AllToLua_cItem_new01_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0));
+ char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,1));
+ short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0));
+ {
+ cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cItem_new00_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new02
+static int tolua_AllToLua_cItem_new02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0));
+ char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0));
+ short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0));
+ const AString a_Enchantments = ((const AString) tolua_tocppstring(tolua_S,5,0));
+ {
+ cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage,a_Enchantments));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem");
+ tolua_pushcppstring(tolua_S,(const char*)a_Enchantments);
+ }
+ }
+ return 2;
+tolua_lerror:
+ return tolua_AllToLua_cItem_new01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new02_local
+static int tolua_AllToLua_cItem_new02_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0));
+ char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0));
+ short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0));
+ const AString a_Enchantments = ((const AString) tolua_tocppstring(tolua_S,5,0));
+ {
+ cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage,a_Enchantments));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ tolua_pushcppstring(tolua_S,(const char*)a_Enchantments);
+ }
+ }
+ return 2;
+tolua_lerror:
+ return tolua_AllToLua_cItem_new01_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new03
+static int tolua_AllToLua_cItem_new03(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cItem* a_CopyFrom = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+ {
+ cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(*a_CopyFrom));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cItem_new02(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new03_local
+static int tolua_AllToLua_cItem_new03_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cItem* a_CopyFrom = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+ {
+ cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(*a_CopyFrom));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cItem_new02_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Empty of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_Empty00
+static int tolua_AllToLua_cItem_Empty00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Empty'", NULL);
+#endif
+ {
+ self->Empty();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Empty'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Clear of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_Clear00
+static int tolua_AllToLua_cItem_Clear00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL);
+#endif
+ {
+ self->Clear();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsEmpty of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsEmpty00
+static int tolua_AllToLua_cItem_IsEmpty00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEmpty'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsEmpty();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsEmpty'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsEqual of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsEqual00
+static int tolua_AllToLua_cItem_IsEqual00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEqual'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsEqual(*a_Item);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsEqual'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSameType of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsSameType00
+static int tolua_AllToLua_cItem_IsSameType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSameType'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSameType(*a_Item);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSameType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CopyOne of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_CopyOne00
+static int tolua_AllToLua_cItem_CopyOne00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyOne'", NULL);
+#endif
+ {
+ cItem tolua_ret = (cItem) self->CopyOne();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((cItem)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CopyOne'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddCount of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_AddCount00
+static int tolua_AllToLua_cItem_AddCount00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+ char a_AmountToAdd = ((char) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddCount'", NULL);
+#endif
+ {
+ cItem& tolua_ret = (cItem&) self->AddCount(a_AmountToAdd);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddCount'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMaxDamage of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_GetMaxDamage00
+static int tolua_AllToLua_cItem_GetMaxDamage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxDamage'", NULL);
+#endif
+ {
+ short tolua_ret = (short) self->GetMaxDamage();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMaxDamage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DamageItem of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_DamageItem00
+static int tolua_AllToLua_cItem_DamageItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+ short a_Amount = ((short) tolua_tonumber(tolua_S,2,1));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DamageItem(a_Amount);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsDamageable of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsDamageable00
+static int tolua_AllToLua_cItem_IsDamageable00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDamageable'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsDamageable();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsDamageable'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsStackableWith of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsStackableWith00
+static int tolua_AllToLua_cItem_IsStackableWith00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_OtherStack = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsStackableWith'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsStackableWith(*a_OtherStack);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsStackableWith'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsFullStack of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsFullStack00
+static int tolua_AllToLua_cItem_IsFullStack00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsFullStack'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsFullStack();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsFullStack'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMaxStackSize of class cItem */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_GetMaxStackSize00
+static int tolua_AllToLua_cItem_GetMaxStackSize00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxStackSize'", NULL);
+#endif
+ {
+ char tolua_ret = (char) self->GetMaxStackSize();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMaxStackSize'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: m_ItemType of class cItem */
+#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemType
+static int tolua_get_cItem_m_ItemType(lua_State* tolua_S)
+{
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemType'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemType);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: m_ItemType of class cItem */
+#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemType
+static int tolua_set_cItem_m_ItemType(lua_State* tolua_S)
+{
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemType'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->m_ItemType = ((short) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: m_ItemCount of class cItem */
+#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemCount
+static int tolua_get_cItem_m_ItemCount(lua_State* tolua_S)
+{
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemCount'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemCount);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: m_ItemCount of class cItem */
+#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemCount
+static int tolua_set_cItem_m_ItemCount(lua_State* tolua_S)
+{
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemCount'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->m_ItemCount = ((char) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: m_ItemDamage of class cItem */
+#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemDamage
+static int tolua_get_cItem_m_ItemDamage(lua_State* tolua_S)
+{
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemDamage'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemDamage);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: m_ItemDamage of class cItem */
+#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemDamage
+static int tolua_set_cItem_m_ItemDamage(lua_State* tolua_S)
+{
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemDamage'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->m_ItemDamage = ((short) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: m_Enchantments of class cItem */
+#ifndef TOLUA_DISABLE_tolua_get_cItem_m_Enchantments
+static int tolua_get_cItem_m_Enchantments(lua_State* tolua_S)
+{
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_Enchantments'",NULL);
+#endif
+ tolua_pushusertype(tolua_S,(void*)&self->m_Enchantments,"cEnchantments");
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: m_Enchantments of class cItem */
+#ifndef TOLUA_DISABLE_tolua_set_cItem_m_Enchantments
+static int tolua_set_cItem_m_Enchantments(lua_State* tolua_S)
+{
+ cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_Enchantments'",NULL);
+ if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEnchantments",0,&tolua_err)))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->m_Enchantments = *((cEnchantments*) tolua_tousertype(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_new00
+static int tolua_AllToLua_cItems_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cItems",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cItems* tolua_ret = (cItems*) Mtolua_new((cItems)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItems");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_new00_local
+static int tolua_AllToLua_cItems_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cItems",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cItems* tolua_ret = (cItems*) Mtolua_new((cItems)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItems");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Get of class cItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Get00
+static int tolua_AllToLua_cItems_Get00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+ int a_Idx = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Get'", NULL);
+#endif
+ {
+ cItem* tolua_ret = (cItem*) self->Get(a_Idx);
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Set of class cItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Set00
+static int tolua_AllToLua_cItems_Set00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+ int a_Idx = ((int) tolua_tonumber(tolua_S,2,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL);
+#endif
+ {
+ self->Set(a_Idx,*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Add of class cItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Add00
+static int tolua_AllToLua_cItems_Add00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Add'", NULL);
+#endif
+ {
+ self->Add(*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Add'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Delete of class cItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Delete00
+static int tolua_AllToLua_cItems_Delete00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+ int a_Idx = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Delete'", NULL);
+#endif
+ {
+ self->Delete(a_Idx);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Delete'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Clear of class cItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Clear00
+static int tolua_AllToLua_cItems_Clear00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL);
+#endif
+ {
+ self->Clear();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Size of class cItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Size00
+static int tolua_AllToLua_cItems_Size00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Size'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->Size();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Size'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Set of class cItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Set01
+static int tolua_AllToLua_cItems_Set01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+ int a_Idx = ((int) tolua_tonumber(tolua_S,2,0));
+ ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,3,0));
+ char a_ItemCount = ((char) tolua_tonumber(tolua_S,4,0));
+ short a_ItemDamage = ((short) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL);
+#endif
+ {
+ self->Set(a_Idx,a_ItemType,a_ItemCount,a_ItemDamage);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cItems_Set00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Add of class cItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Add01
+static int tolua_AllToLua_cItems_Add01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+ ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,2,0));
+ char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0));
+ short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Add'", NULL);
+#endif
+ {
+ self->Add(a_ItemType,a_ItemCount,a_ItemDamage);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cItems_Add00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWidth of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetWidth00
+static int tolua_AllToLua_cItemGrid_GetWidth00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetWidth();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHeight of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetHeight00
+static int tolua_AllToLua_cItemGrid_GetHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetHeight();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumSlots of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNumSlots00
+static int tolua_AllToLua_cItemGrid_GetNumSlots00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumSlots'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNumSlots();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNumSlots'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSlotNum of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlotNum00
+static int tolua_AllToLua_cItemGrid_GetSlotNum00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlotNum'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetSlotNum(a_X,a_Y);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSlotNum'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlot00
+static int tolua_AllToLua_cItemGrid_GetSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetSlot(a_X,a_Y);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlot01
+static int tolua_AllToLua_cItemGrid_GetSlot01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cItemGrid_GetSlot00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot00
+static int tolua_AllToLua_cItemGrid_SetSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL);
+#endif
+ {
+ self->SetSlot(a_X,a_Y,*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot01
+static int tolua_AllToLua_cItemGrid_SetSlot01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,4,0));
+ char a_ItemCount = ((char) tolua_tonumber(tolua_S,5,0));
+ short a_ItemDamage = ((short) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL);
+#endif
+ {
+ self->SetSlot(a_X,a_Y,a_ItemType,a_ItemCount,a_ItemDamage);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cItemGrid_SetSlot00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot02
+static int tolua_AllToLua_cItemGrid_SetSlot02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL);
+#endif
+ {
+ self->SetSlot(a_SlotNum,*a_Item);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cItemGrid_SetSlot01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot03
+static int tolua_AllToLua_cItemGrid_SetSlot03(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ short a_ItemType = ((short) tolua_tonumber(tolua_S,3,0));
+ char a_ItemCount = ((char) tolua_tonumber(tolua_S,4,0));
+ short a_ItemDamage = ((short) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL);
+#endif
+ {
+ self->SetSlot(a_SlotNum,a_ItemType,a_ItemCount,a_ItemDamage);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cItemGrid_SetSlot02(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: EmptySlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_EmptySlot00
+static int tolua_AllToLua_cItemGrid_EmptySlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EmptySlot'", NULL);
+#endif
+ {
+ self->EmptySlot(a_X,a_Y);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'EmptySlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: EmptySlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_EmptySlot01
+static int tolua_AllToLua_cItemGrid_EmptySlot01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EmptySlot'", NULL);
+#endif
+ {
+ self->EmptySlot(a_SlotNum);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cItemGrid_EmptySlot00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSlotEmpty of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_IsSlotEmpty00
+static int tolua_AllToLua_cItemGrid_IsSlotEmpty00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotEmpty'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSlotEmpty(a_SlotNum);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSlotEmpty'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSlotEmpty of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_IsSlotEmpty01
+static int tolua_AllToLua_cItemGrid_IsSlotEmpty01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotEmpty'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSlotEmpty(a_X,a_Y);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cItemGrid_IsSlotEmpty00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Clear of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_Clear00
+static int tolua_AllToLua_cItemGrid_Clear00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL);
+#endif
+ {
+ self->Clear();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HowManyCanFit of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HowManyCanFit00
+static int tolua_AllToLua_cItemGrid_HowManyCanFit00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isboolean(tolua_S,3,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+ bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_AllowNewStacks);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HowManyCanFit'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddItem of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_AddItem00
+static int tolua_AllToLua_cItemGrid_AddItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItem",0,&tolua_err)) ||
+ !tolua_isboolean(tolua_S,3,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ cItem* a_ItemStack = ((cItem*) tolua_tousertype(tolua_S,2,0));
+ bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true));
+ int a_PrioritarySlot = ((int) tolua_tonumber(tolua_S,4,-1));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItem'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->AddItem(*a_ItemStack,a_AllowNewStacks,a_PrioritarySlot);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddItems of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_AddItems00
+static int tolua_AllToLua_cItemGrid_AddItems00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) ||
+ !tolua_isboolean(tolua_S,3,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ cItems* a_ItemStackList = ((cItems*) tolua_tousertype(tolua_S,2,0));
+ bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true));
+ int a_PrioritarySlot = ((int) tolua_tonumber(tolua_S,4,-1));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItems'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->AddItems(*a_ItemStackList,a_AllowNewStacks,a_PrioritarySlot);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddItems'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ChangeSlotCount of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_ChangeSlotCount00
+static int tolua_AllToLua_cItemGrid_ChangeSlotCount00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_AddToCount = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->ChangeSlotCount(a_SlotNum,a_AddToCount);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ChangeSlotCount'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ChangeSlotCount of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_ChangeSlotCount01
+static int tolua_AllToLua_cItemGrid_ChangeSlotCount01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_AddToCount = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->ChangeSlotCount(a_X,a_Y,a_AddToCount);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cItemGrid_ChangeSlotCount00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RemoveOneItem of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_RemoveOneItem00
+static int tolua_AllToLua_cItemGrid_RemoveOneItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneItem'", NULL);
+#endif
+ {
+ cItem tolua_ret = (cItem) self->RemoveOneItem(a_SlotNum);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((cItem)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RemoveOneItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RemoveOneItem of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_RemoveOneItem01
+static int tolua_AllToLua_cItemGrid_RemoveOneItem01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneItem'", NULL);
+#endif
+ {
+ cItem tolua_ret = (cItem) self->RemoveOneItem(a_X,a_Y);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((cItem)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem));
+ tolua_pushusertype(tolua_S,tolua_obj,"cItem");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cItemGrid_RemoveOneItem00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HowManyItems of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HowManyItems00
+static int tolua_AllToLua_cItemGrid_HowManyItems00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyItems'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->HowManyItems(*a_Item);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HowManyItems'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HasItems of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HasItems00
+static int tolua_AllToLua_cItemGrid_HasItems00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasItems'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->HasItems(*a_ItemStack);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HasItems'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetFirstEmptySlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetFirstEmptySlot00
+static int tolua_AllToLua_cItemGrid_GetFirstEmptySlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFirstEmptySlot'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetFirstEmptySlot();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetFirstEmptySlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetFirstUsedSlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetFirstUsedSlot00
+static int tolua_AllToLua_cItemGrid_GetFirstUsedSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFirstUsedSlot'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetFirstUsedSlot();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetFirstUsedSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetLastEmptySlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetLastEmptySlot00
+static int tolua_AllToLua_cItemGrid_GetLastEmptySlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLastEmptySlot'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetLastEmptySlot();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetLastEmptySlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetLastUsedSlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetLastUsedSlot00
+static int tolua_AllToLua_cItemGrid_GetLastUsedSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLastUsedSlot'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetLastUsedSlot();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetLastUsedSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNextEmptySlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNextEmptySlot00
+static int tolua_AllToLua_cItemGrid_GetNextEmptySlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_StartFrom = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNextEmptySlot'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNextEmptySlot(a_StartFrom);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNextEmptySlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNextUsedSlot of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNextUsedSlot00
+static int tolua_AllToLua_cItemGrid_GetNextUsedSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_StartFrom = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNextUsedSlot'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNextUsedSlot(a_StartFrom);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNextUsedSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CopyToItems of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_CopyToItems00
+static int tolua_AllToLua_cItemGrid_CopyToItems00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ cItems* a_Items = ((cItems*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyToItems'", NULL);
+#endif
+ {
+ self->CopyToItems(*a_Items);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CopyToItems'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DamageItem of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_DamageItem00
+static int tolua_AllToLua_cItemGrid_DamageItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ short a_Amount = ((short) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DamageItem(a_SlotNum,a_Amount);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DamageItem of class cItemGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_DamageItem01
+static int tolua_AllToLua_cItemGrid_DamageItem01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+ short a_Amount = ((short) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DamageItem(a_X,a_Y,a_Amount);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cItemGrid_DamageItem00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPosX of class cBlockEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosX00
+static int tolua_AllToLua_cBlockEntity_GetPosX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosX'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetPosX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPosX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPosY of class cBlockEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosY00
+static int tolua_AllToLua_cBlockEntity_GetPosY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosY'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetPosY();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPosY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPosZ of class cBlockEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosZ00
+static int tolua_AllToLua_cBlockEntity_GetPosZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosZ'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetPosZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPosZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockType of class cBlockEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetBlockType00
+static int tolua_AllToLua_cBlockEntity_GetBlockType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockType();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWorld of class cBlockEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetWorld00
+static int tolua_AllToLua_cBlockEntity_GetWorld00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL);
+#endif
+ {
+ cWorld* tolua_ret = (cWorld*) self->GetWorld();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetChunkX of class cBlockEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetChunkX00
+static int tolua_AllToLua_cBlockEntity_GetChunkX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetChunkX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetChunkZ of class cBlockEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetChunkZ00
+static int tolua_AllToLua_cBlockEntity_GetChunkZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetChunkZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRelX of class cBlockEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetRelX00
+static int tolua_AllToLua_cBlockEntity_GetRelX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelX'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetRelX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRelX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRelZ of class cBlockEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetRelZ00
+static int tolua_AllToLua_cBlockEntity_GetRelZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelZ'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetRelZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRelZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSlot of class cBlockEntityWithItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetSlot00
+static int tolua_AllToLua_cBlockEntityWithItems_GetSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntityWithItems",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockEntityWithItems* self = (const cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSlot of class cBlockEntityWithItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetSlot01
+static int tolua_AllToLua_cBlockEntityWithItems_GetSlot01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockEntityWithItems",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cBlockEntityWithItems* self = (const cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetSlot(a_X,a_Y);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBlockEntityWithItems_GetSlot00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSlot of class cBlockEntityWithItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_SetSlot00
+static int tolua_AllToLua_cBlockEntityWithItems_SetSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL);
+#endif
+ {
+ self->SetSlot(a_SlotNum,*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSlot of class cBlockEntityWithItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_SetSlot01
+static int tolua_AllToLua_cBlockEntityWithItems_SetSlot01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL);
+#endif
+ {
+ self->SetSlot(a_X,a_Y,*a_Item);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cBlockEntityWithItems_SetSlot00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetContents of class cBlockEntityWithItems */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetContents00
+static int tolua_AllToLua_cBlockEntityWithItems_GetContents00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetContents'", NULL);
+#endif
+ {
+ cItemGrid& tolua_ret = (cItemGrid&) self->GetContents();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetContents'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddDropSpenserDir of class cDropSpenserEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00
+static int tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_Direction = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddDropSpenserDir'", NULL);
+#endif
+ {
+ self->AddDropSpenserDir(a_BlockX,a_BlockY,a_BlockZ,a_Direction);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockX);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockY);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockZ);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddDropSpenserDir'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Activate of class cDropSpenserEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_Activate00
+static int tolua_AllToLua_cDropSpenserEntity_Activate00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Activate'", NULL);
+#endif
+ {
+ self->Activate();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Activate'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRedstonePower of class cDropSpenserEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00
+static int tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0);
+ bool a_IsPowered = ((bool) tolua_toboolean(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRedstonePower'", NULL);
+#endif
+ {
+ self->SetRedstonePower(a_IsPowered);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRedstonePower'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetInputSlot of class cFurnaceEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetInputSlot00
+static int tolua_AllToLua_cFurnaceEntity_GetInputSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInputSlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetInputSlot();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetInputSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetFuelSlot of class cFurnaceEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetFuelSlot00
+static int tolua_AllToLua_cFurnaceEntity_GetFuelSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFuelSlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetFuelSlot();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetFuelSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetOutputSlot of class cFurnaceEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetOutputSlot00
+static int tolua_AllToLua_cFurnaceEntity_GetOutputSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOutputSlot'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetOutputSlot();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetOutputSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetInputSlot of class cFurnaceEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetInputSlot00
+static int tolua_AllToLua_cFurnaceEntity_SetInputSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetInputSlot'", NULL);
+#endif
+ {
+ self->SetInputSlot(*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetInputSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetFuelSlot of class cFurnaceEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetFuelSlot00
+static int tolua_AllToLua_cFurnaceEntity_SetFuelSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFuelSlot'", NULL);
+#endif
+ {
+ self->SetFuelSlot(*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetFuelSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetOutputSlot of class cFurnaceEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetOutputSlot00
+static int tolua_AllToLua_cFurnaceEntity_SetOutputSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetOutputSlot'", NULL);
+#endif
+ {
+ self->SetOutputSlot(*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetOutputSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetTimeCooked of class cFurnaceEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetTimeCooked00
+static int tolua_AllToLua_cFurnaceEntity_GetTimeCooked00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTimeCooked'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetTimeCooked();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetTimeCooked'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetCookTimeLeft of class cFurnaceEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00
+static int tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCookTimeLeft'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetCookTimeLeft();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetCookTimeLeft'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetFuelBurnTimeLeft of class cFurnaceEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00
+static int tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFuelBurnTimeLeft'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetFuelBurnTimeLeft();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetFuelBurnTimeLeft'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HasFuelTimeLeft of class cFurnaceEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00
+static int tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasFuelTimeLeft'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->HasFuelTimeLeft();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HasFuelTimeLeft'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRecord of class cJukeboxEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_GetRecord00
+static int tolua_AllToLua_cJukeboxEntity_GetRecord00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRecord'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetRecord();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRecord'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRecord of class cJukeboxEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_SetRecord00
+static int tolua_AllToLua_cJukeboxEntity_SetRecord00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0);
+ int a_Record = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRecord'", NULL);
+#endif
+ {
+ self->SetRecord(a_Record);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRecord'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: PlayRecord of class cJukeboxEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_PlayRecord00
+static int tolua_AllToLua_cJukeboxEntity_PlayRecord00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'PlayRecord'", NULL);
+#endif
+ {
+ self->PlayRecord();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'PlayRecord'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: EjectRecord of class cJukeboxEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_EjectRecord00
+static int tolua_AllToLua_cJukeboxEntity_EjectRecord00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EjectRecord'", NULL);
+#endif
+ {
+ self->EjectRecord();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'EjectRecord'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPitch of class cNoteEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_GetPitch00
+static int tolua_AllToLua_cNoteEntity_GetPitch00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPitch'", NULL);
+#endif
+ {
+ char tolua_ret = (char) self->GetPitch();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPitch'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetPitch of class cNoteEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_SetPitch00
+static int tolua_AllToLua_cNoteEntity_SetPitch00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0);
+ char a_Pitch = ((char) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitch'", NULL);
+#endif
+ {
+ self->SetPitch(a_Pitch);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetPitch'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IncrementPitch of class cNoteEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_IncrementPitch00
+static int tolua_AllToLua_cNoteEntity_IncrementPitch00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IncrementPitch'", NULL);
+#endif
+ {
+ self->IncrementPitch();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IncrementPitch'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MakeSound of class cNoteEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_MakeSound00
+static int tolua_AllToLua_cNoteEntity_MakeSound00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MakeSound'", NULL);
+#endif
+ {
+ self->MakeSound();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MakeSound'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetLines of class cSignEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_SetLines00
+static int tolua_AllToLua_cSignEntity_SetLines00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cSignEntity",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,4,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cSignEntity* self = (cSignEntity*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Line1 = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ const AString a_Line2 = ((const AString) tolua_tocppstring(tolua_S,3,0));
+ const AString a_Line3 = ((const AString) tolua_tocppstring(tolua_S,4,0));
+ const AString a_Line4 = ((const AString) tolua_tocppstring(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLines'", NULL);
+#endif
+ {
+ self->SetLines(a_Line1,a_Line2,a_Line3,a_Line4);
+ tolua_pushcppstring(tolua_S,(const char*)a_Line1);
+ tolua_pushcppstring(tolua_S,(const char*)a_Line2);
+ tolua_pushcppstring(tolua_S,(const char*)a_Line3);
+ tolua_pushcppstring(tolua_S,(const char*)a_Line4);
+ }
+ }
+ return 4;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetLines'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetLine of class cSignEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_SetLine00
+static int tolua_AllToLua_cSignEntity_SetLine00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cSignEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cSignEntity* self = (cSignEntity*) tolua_tousertype(tolua_S,1,0);
+ int a_Index = ((int) tolua_tonumber(tolua_S,2,0));
+ const AString a_Line = ((const AString) tolua_tocppstring(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLine'", NULL);
+#endif
+ {
+ self->SetLine(a_Index,a_Line);
+ tolua_pushcppstring(tolua_S,(const char*)a_Line);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetLine'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetLine of class cSignEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_GetLine00
+static int tolua_AllToLua_cSignEntity_GetLine00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cSignEntity",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cSignEntity* self = (const cSignEntity*) tolua_tousertype(tolua_S,1,0);
+ int a_Index = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLine'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetLine(a_Index);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetLine'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Name of class HTTPFormData */
+#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Name
+static int tolua_get_HTTPFormData_Name(lua_State* tolua_S)
+{
+ HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Name'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->Name);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Name of class HTTPFormData */
+#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Name
+static int tolua_set_HTTPFormData_Name(lua_State* tolua_S)
+{
+ HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Name'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Name = ((std::string) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Value of class HTTPFormData */
+#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Value
+static int tolua_get_HTTPFormData_Value(lua_State* tolua_S)
+{
+ HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Value'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->Value);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Value of class HTTPFormData */
+#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Value
+static int tolua_set_HTTPFormData_Value(lua_State* tolua_S)
+{
+ HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Value'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Value = ((std::string) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Type of class HTTPFormData */
+#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Type
+static int tolua_get_HTTPFormData_Type(lua_State* tolua_S)
+{
+ HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Type'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->Type);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Type of class HTTPFormData */
+#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Type
+static int tolua_set_HTTPFormData_Type(lua_State* tolua_S)
+{
+ HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Type'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Type = ((std::string) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Method of class HTTPRequest */
+#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Method
+static int tolua_get_HTTPRequest_Method(lua_State* tolua_S)
+{
+ HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Method'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->Method);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Method of class HTTPRequest */
+#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Method
+static int tolua_set_HTTPRequest_Method(lua_State* tolua_S)
+{
+ HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Method'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Method = ((AString) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Path of class HTTPRequest */
+#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Path
+static int tolua_get_HTTPRequest_Path(lua_State* tolua_S)
+{
+ HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Path'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->Path);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Path of class HTTPRequest */
+#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Path
+static int tolua_set_HTTPRequest_Path(lua_State* tolua_S)
+{
+ HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Path'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Path = ((AString) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Username of class HTTPRequest */
+#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Username
+static int tolua_get_HTTPRequest_Username(lua_State* tolua_S)
+{
+ HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Username'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->Username);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Username of class HTTPRequest */
+#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Username
+static int tolua_set_HTTPRequest_Username(lua_State* tolua_S)
+{
+ HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Username'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Username = ((AString) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Request of class HTTPTemplateRequest */
+#ifndef TOLUA_DISABLE_tolua_get_HTTPTemplateRequest_Request
+static int tolua_get_HTTPTemplateRequest_Request(lua_State* tolua_S)
+{
+ HTTPTemplateRequest* self = (HTTPTemplateRequest*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Request'",NULL);
+#endif
+ tolua_pushusertype(tolua_S,(void*)&self->Request,"HTTPRequest");
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Request of class HTTPTemplateRequest */
+#ifndef TOLUA_DISABLE_tolua_set_HTTPTemplateRequest_Request
+static int tolua_set_HTTPTemplateRequest_Request(lua_State* tolua_S)
+{
+ HTTPTemplateRequest* self = (HTTPTemplateRequest*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Request'",NULL);
+ if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"HTTPRequest",0,&tolua_err)))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Request = *((HTTPRequest*) tolua_tousertype(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Content of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_Content
+static int tolua_get_sWebAdminPage_Content(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Content'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->Content);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Content of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_Content
+static int tolua_set_sWebAdminPage_Content(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Content'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Content = ((AString) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: PluginName of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_PluginName
+static int tolua_get_sWebAdminPage_PluginName(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PluginName'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->PluginName);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: PluginName of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_PluginName
+static int tolua_set_sWebAdminPage_PluginName(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PluginName'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->PluginName = ((AString) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: TabName of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_TabName
+static int tolua_get_sWebAdminPage_TabName(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'TabName'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->TabName);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: TabName of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_TabName
+static int tolua_set_sWebAdminPage_TabName(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'TabName'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->TabName = ((AString) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPage of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00
+static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
+ const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPage'", NULL);
+#endif
+ {
+ sWebAdminPage tolua_ret = (sWebAdminPage) self->GetPage(*a_Request);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((sWebAdminPage)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(sWebAdminPage));
+ tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetDefaultPage of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetDefaultPage00
+static int tolua_AllToLua_cWebAdmin_GetDefaultPage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultPage'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetDefaultPage();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetDefaultPage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBaseURL of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetBaseURL00
+static int tolua_AllToLua_cWebAdmin_GetBaseURL00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
+ const AString a_URL = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBaseURL'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetBaseURL(a_URL);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_URL);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBaseURL'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHTMLEscapedString of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00
+static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_Input = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ AString tolua_ret = (AString) cWebAdmin::GetHTMLEscapedString(a_Input);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_Input);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHTMLEscapedString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWebTitle of class cWebPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_GetWebTitle00
+static int tolua_AllToLua_cWebPlugin_GetWebTitle00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWebPlugin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWebPlugin* self = (const cWebPlugin*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWebTitle'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetWebTitle();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWebTitle'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HandleWebRequest of class cWebPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_HandleWebRequest00
+static int tolua_AllToLua_cWebPlugin_HandleWebRequest00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWebPlugin",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S,1,0);
+ const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HandleWebRequest'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->HandleWebRequest(a_Request);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HandleWebRequest'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SafeString of class cWebPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_SafeString00
+static int tolua_AllToLua_cWebPlugin_SafeString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cWebPlugin",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_String = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ AString tolua_ret = (AString) cWebPlugin::SafeString(a_String);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_String);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SafeString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Get of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_Get00
+static int tolua_AllToLua_cRoot_Get00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cRoot* tolua_ret = (cRoot*) cRoot::Get();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cRoot");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetServer of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetServer00
+static int tolua_AllToLua_cRoot_GetServer00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetServer'", NULL);
+#endif
+ {
+ cServer* tolua_ret = (cServer*) self->GetServer();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cServer");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetServer'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetDefaultWorld of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetDefaultWorld00
+static int tolua_AllToLua_cRoot_GetDefaultWorld00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultWorld'", NULL);
+#endif
+ {
+ cWorld* tolua_ret = (cWorld*) self->GetDefaultWorld();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetDefaultWorld'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWorld of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetWorld00
+static int tolua_AllToLua_cRoot_GetWorld00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+ const AString a_WorldName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL);
+#endif
+ {
+ cWorld* tolua_ret = (cWorld*) self->GetWorld(a_WorldName);
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld");
+ tolua_pushcppstring(tolua_S,(const char*)a_WorldName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPrimaryServerVersion of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPrimaryServerVersion00
+static int tolua_AllToLua_cRoot_GetPrimaryServerVersion00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cRoot* self = (const cRoot*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPrimaryServerVersion'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetPrimaryServerVersion();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPrimaryServerVersion'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetPrimaryServerVersion of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_SetPrimaryServerVersion00
+static int tolua_AllToLua_cRoot_SetPrimaryServerVersion00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+ int a_Version = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPrimaryServerVersion'", NULL);
+#endif
+ {
+ self->SetPrimaryServerVersion(a_Version);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetPrimaryServerVersion'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetGroupManager of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetGroupManager00
+static int tolua_AllToLua_cRoot_GetGroupManager00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGroupManager'", NULL);
+#endif
+ {
+ cGroupManager* tolua_ret = (cGroupManager*) self->GetGroupManager();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cGroupManager");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetGroupManager'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetCraftingRecipes of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetCraftingRecipes00
+static int tolua_AllToLua_cRoot_GetCraftingRecipes00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCraftingRecipes'", NULL);
+#endif
+ {
+ cCraftingRecipes* tolua_ret = (cCraftingRecipes*) self->GetCraftingRecipes();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingRecipes");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetCraftingRecipes'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetFurnaceFuelBurnTime of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetFurnaceFuelBurnTime00
+static int tolua_AllToLua_cRoot_GetFurnaceFuelBurnTime00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cItem* a_Fuel = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+ {
+ int tolua_ret = (int) cRoot::GetFurnaceFuelBurnTime(*a_Fuel);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetFurnaceFuelBurnTime'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWebAdmin of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetWebAdmin00
+static int tolua_AllToLua_cRoot_GetWebAdmin00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWebAdmin'", NULL);
+#endif
+ {
+ cWebAdmin* tolua_ret = (cWebAdmin*) self->GetWebAdmin();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWebAdmin");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWebAdmin'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPluginManager of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPluginManager00
+static int tolua_AllToLua_cRoot_GetPluginManager00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPluginManager'", NULL);
+#endif
+ {
+ cPluginManager* tolua_ret = (cPluginManager*) self->GetPluginManager();
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPluginManager");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPluginManager'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: QueueExecuteConsoleCommand of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00
+static int tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Cmd = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueExecuteConsoleCommand'", NULL);
+#endif
+ {
+ self->QueueExecuteConsoleCommand(a_Cmd);
+ tolua_pushcppstring(tolua_S,(const char*)a_Cmd);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'QueueExecuteConsoleCommand'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetTotalChunkCount of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetTotalChunkCount00
+static int tolua_AllToLua_cRoot_GetTotalChunkCount00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTotalChunkCount'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetTotalChunkCount();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetTotalChunkCount'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SaveAllChunks of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_SaveAllChunks00
+static int tolua_AllToLua_cRoot_SaveAllChunks00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SaveAllChunks'", NULL);
+#endif
+ {
+ self->SaveAllChunks();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SaveAllChunks'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: BroadcastChat of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_BroadcastChat00
+static int tolua_AllToLua_cRoot_BroadcastChat00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
+ const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastChat'", NULL);
+#endif
+ {
+ self->BroadcastChat(a_Message);
+ tolua_pushcppstring(tolua_S,(const char*)a_Message);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'BroadcastChat'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetProtocolVersionTextFromInt of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00
+static int tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ int a_ProtocolVersionNum = ((int) tolua_tonumber(tolua_S,2,0));
+ {
+ AString tolua_ret = (AString) cRoot::GetProtocolVersionTextFromInt(a_ProtocolVersionNum);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetProtocolVersionTextFromInt'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetVirtualRAMUsage of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetVirtualRAMUsage00
+static int tolua_AllToLua_cRoot_GetVirtualRAMUsage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ int tolua_ret = (int) cRoot::GetVirtualRAMUsage();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetVirtualRAMUsage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPhysicalRAMUsage of class cRoot */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPhysicalRAMUsage00
+static int tolua_AllToLua_cRoot_GetPhysicalRAMUsage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ int tolua_ret = (int) cRoot::GetPhysicalRAMUsage();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPhysicalRAMUsage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new00
+static int tolua_AllToLua_Vector3f_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new00_local
+static int tolua_AllToLua_Vector3f_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new01
+static int tolua_AllToLua_Vector3f_new01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_new00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new01_local
+static int tolua_AllToLua_Vector3f_new01_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_new00_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new02
+static int tolua_AllToLua_Vector3f_new02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_new01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new02_local
+static int tolua_AllToLua_Vector3f_new02_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_new01_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new03
+static int tolua_AllToLua_Vector3f_new03(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_new02(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new03_local
+static int tolua_AllToLua_Vector3f_new03_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_new02_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new04
+static int tolua_AllToLua_Vector3f_new04(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_new03(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new04_local
+static int tolua_AllToLua_Vector3f_new04_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_new03_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new05
+static int tolua_AllToLua_Vector3f_new05(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ float a_x = ((float) tolua_tonumber(tolua_S,2,0));
+ float a_y = ((float) tolua_tonumber(tolua_S,3,0));
+ float a_z = ((float) tolua_tonumber(tolua_S,4,0));
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(a_x,a_y,a_z));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_new04(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new05_local
+static int tolua_AllToLua_Vector3f_new05_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ float a_x = ((float) tolua_tonumber(tolua_S,2,0));
+ float a_y = ((float) tolua_tonumber(tolua_S,3,0));
+ float a_z = ((float) tolua_tonumber(tolua_S,4,0));
+ {
+ Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(a_x,a_y,a_z));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_new04_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Set of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Set00
+static int tolua_AllToLua_Vector3f_Set00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+ float a_x = ((float) tolua_tonumber(tolua_S,2,0));
+ float a_y = ((float) tolua_tonumber(tolua_S,3,0));
+ float a_z = ((float) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL);
+#endif
+ {
+ self->Set(a_x,a_y,a_z);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Normalize of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Normalize00
+static int tolua_AllToLua_Vector3f_Normalize00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"Vector3f",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Normalize'", NULL);
+#endif
+ {
+ self->Normalize();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Normalize'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: NormalizeCopy of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_NormalizeCopy00
+static int tolua_AllToLua_Vector3f_NormalizeCopy00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL);
+#endif
+ {
+ Vector3f tolua_ret = (Vector3f) self->NormalizeCopy();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'NormalizeCopy'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: NormalizeCopy of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_NormalizeCopy01
+static int tolua_AllToLua_Vector3f_NormalizeCopy01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+ Vector3f* a_V = ((Vector3f*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL);
+#endif
+ {
+ self->NormalizeCopy(*a_V);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f_NormalizeCopy00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Length of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Length00
+static int tolua_AllToLua_Vector3f_Length00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL);
+#endif
+ {
+ float tolua_ret = (float) self->Length();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SqrLength of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_SqrLength00
+static int tolua_AllToLua_Vector3f_SqrLength00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL);
+#endif
+ {
+ float tolua_ret = (float) self->SqrLength();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Dot of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Dot00
+static int tolua_AllToLua_Vector3f_Dot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* a_V = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dot'", NULL);
+#endif
+ {
+ float tolua_ret = (float) self->Dot(*a_V);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Dot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Cross of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Cross00
+static int tolua_AllToLua_Vector3f_Cross00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Cross'", NULL);
+#endif
+ {
+ Vector3f tolua_ret = (Vector3f) self->Cross(*v);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Cross'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Equals of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Equals00
+static int tolua_AllToLua_Vector3f_Equals00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Equals(*v);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator+ of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__add00
+static int tolua_AllToLua_Vector3f__add00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL);
+#endif
+ {
+ Vector3f tolua_ret = (Vector3f) self->operator+(*v2);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function '.add'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator+ of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__add01
+static int tolua_AllToLua_Vector3f__add01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL);
+#endif
+ {
+ Vector3f tolua_ret = (Vector3f) self->operator+(v2);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f__add00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator- of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__sub00
+static int tolua_AllToLua_Vector3f__sub00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL);
+#endif
+ {
+ Vector3f tolua_ret = (Vector3f) self->operator-(*v2);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function '.sub'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator- of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__sub01
+static int tolua_AllToLua_Vector3f__sub01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL);
+#endif
+ {
+ Vector3f tolua_ret = (Vector3f) self->operator-(v2);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f__sub00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator* of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__mul00
+static int tolua_AllToLua_Vector3f__mul00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+ const float f = ((const float) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL);
+#endif
+ {
+ Vector3f tolua_ret = (Vector3f) self->operator*(f);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function '.mul'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator* of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__mul01
+static int tolua_AllToLua_Vector3f__mul01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL);
+#endif
+ {
+ Vector3f tolua_ret = (Vector3f) self->operator*(*v2);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3f");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3f__mul00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: x of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3f_x
+static int tolua_get_Vector3f_x(lua_State* tolua_S)
+{
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->x);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: x of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_set_Vector3f_x
+static int tolua_set_Vector3f_x(lua_State* tolua_S)
+{
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->x = ((float) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: y of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3f_y
+static int tolua_get_Vector3f_y(lua_State* tolua_S)
+{
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->y);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: y of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_set_Vector3f_y
+static int tolua_set_Vector3f_y(lua_State* tolua_S)
+{
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->y = ((float) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: z of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3f_z
+static int tolua_get_Vector3f_z(lua_State* tolua_S)
+{
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->z);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: z of class Vector3f */
+#ifndef TOLUA_DISABLE_tolua_set_Vector3f_z
+static int tolua_set_Vector3f_z(lua_State* tolua_S)
+{
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->z = ((float) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new00
+static int tolua_AllToLua_Vector3d_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(*v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new00_local
+static int tolua_AllToLua_Vector3d_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(*v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new01
+static int tolua_AllToLua_Vector3d_new01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3d_new00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new01_local
+static int tolua_AllToLua_Vector3d_new01_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3d_new00_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new02
+static int tolua_AllToLua_Vector3d_new02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ {
+ Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3d_new01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new02_local
+static int tolua_AllToLua_Vector3d_new02_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ {
+ Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3d_new01_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new03
+static int tolua_AllToLua_Vector3d_new03(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ double a_x = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_y = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_z = ((double) tolua_tonumber(tolua_S,4,0));
+ {
+ Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(a_x,a_y,a_z));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3d_new02(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new03_local
+static int tolua_AllToLua_Vector3d_new03_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ double a_x = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_y = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_z = ((double) tolua_tonumber(tolua_S,4,0));
+ {
+ Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(a_x,a_y,a_z));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3d_new02_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Set of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Set00
+static int tolua_AllToLua_Vector3d_Set00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+ double a_x = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_y = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_z = ((double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL);
+#endif
+ {
+ self->Set(a_x,a_y,a_z);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Normalize of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Normalize00
+static int tolua_AllToLua_Vector3d_Normalize00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Normalize'", NULL);
+#endif
+ {
+ self->Normalize();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Normalize'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: NormalizeCopy of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_NormalizeCopy00
+static int tolua_AllToLua_Vector3d_NormalizeCopy00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->NormalizeCopy();
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'NormalizeCopy'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: NormalizeCopy of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_NormalizeCopy01
+static int tolua_AllToLua_Vector3d_NormalizeCopy01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+ Vector3d* a_V = ((Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL);
+#endif
+ {
+ self->NormalizeCopy(*a_V);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_Vector3d_NormalizeCopy00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Length of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Length00
+static int tolua_AllToLua_Vector3d_Length00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->Length();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SqrLength of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_SqrLength00
+static int tolua_AllToLua_Vector3d_SqrLength00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->SqrLength();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Dot of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Dot00
+static int tolua_AllToLua_Vector3d_Dot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_V = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dot'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->Dot(*a_V);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Dot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Cross of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Cross00
+static int tolua_AllToLua_Vector3d_Cross00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Cross'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->Cross(*v);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Cross'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: LineCoeffToXYPlane of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToXYPlane00
+static int tolua_AllToLua_Vector3d_LineCoeffToXYPlane00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ double a_Z = ((double) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToXYPlane'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->LineCoeffToXYPlane(*a_OtherEnd,a_Z);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'LineCoeffToXYPlane'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: LineCoeffToXZPlane of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToXZPlane00
+static int tolua_AllToLua_Vector3d_LineCoeffToXZPlane00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ double a_Y = ((double) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToXZPlane'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->LineCoeffToXZPlane(*a_OtherEnd,a_Y);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'LineCoeffToXZPlane'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: LineCoeffToYZPlane of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToYZPlane00
+static int tolua_AllToLua_Vector3d_LineCoeffToYZPlane00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ double a_X = ((double) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToYZPlane'", NULL);
+#endif
+ {
+ double tolua_ret = (double) self->LineCoeffToYZPlane(*a_OtherEnd,a_X);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'LineCoeffToYZPlane'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Equals of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Equals00
+static int tolua_AllToLua_Vector3d_Equals00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Equals(*v);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator+ of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__add00
+static int tolua_AllToLua_Vector3d__add00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->operator+(*v2);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function '.add'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator+ of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__add01
+static int tolua_AllToLua_Vector3d__add01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->operator+(v2);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3d__add00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator- of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__sub00
+static int tolua_AllToLua_Vector3d__sub00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->operator-(*v2);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function '.sub'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator- of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__sub01
+static int tolua_AllToLua_Vector3d__sub01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->operator-(v2);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3d__sub00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator* of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__mul00
+static int tolua_AllToLua_Vector3d__mul00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const double f = ((const double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->operator*(f);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function '.mul'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator* of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__mul01
+static int tolua_AllToLua_Vector3d__mul01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->operator*(*v2);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3d__mul00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: operator/ of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__div00
+static int tolua_AllToLua_Vector3d__div00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0);
+ const double f = ((const double) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator/'", NULL);
+#endif
+ {
+ Vector3d tolua_ret = (Vector3d) self->operator/(f);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d));
+ tolua_pushusertype(tolua_S,tolua_obj,"Vector3d");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function '.div'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: x of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3d_x
+static int tolua_get_Vector3d_x(lua_State* tolua_S)
+{
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->x);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: x of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_set_Vector3d_x
+static int tolua_set_Vector3d_x(lua_State* tolua_S)
+{
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->x = ((double) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: y of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3d_y
+static int tolua_get_Vector3d_y(lua_State* tolua_S)
+{
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->y);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: y of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_set_Vector3d_y
+static int tolua_set_Vector3d_y(lua_State* tolua_S)
+{
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->y = ((double) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: z of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3d_z
+static int tolua_get_Vector3d_z(lua_State* tolua_S)
+{
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->z);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: z of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_set_Vector3d_z
+static int tolua_set_Vector3d_z(lua_State* tolua_S)
+{
+ Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->z = ((double) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: EPS of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3d_EPS
+static int tolua_get_Vector3d_EPS(lua_State* tolua_S)
+{
+ tolua_pushnumber(tolua_S,(lua_Number)Vector3d::EPS);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: NO_INTERSECTION of class Vector3d */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3d_NO_INTERSECTION
+static int tolua_get_Vector3d_NO_INTERSECTION(lua_State* tolua_S)
+{
+ tolua_pushnumber(tolua_S,(lua_Number)Vector3d::NO_INTERSECTION);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new00
+static int tolua_AllToLua_Vector3i_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(*v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new00_local
+static int tolua_AllToLua_Vector3i_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ {
+ Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(*v));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new01
+static int tolua_AllToLua_Vector3i_new01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ {
+ Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3i_new00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new01_local
+static int tolua_AllToLua_Vector3i_new01_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ {
+ Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3i_new00_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new02
+static int tolua_AllToLua_Vector3i_new02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ int a_x = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_y = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_z = ((int) tolua_tonumber(tolua_S,4,0));
+ {
+ Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(a_x,a_y,a_z));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3i_new01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new02_local
+static int tolua_AllToLua_Vector3i_new02_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ int a_x = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_y = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_z = ((int) tolua_tonumber(tolua_S,4,0));
+ {
+ Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(a_x,a_y,a_z));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3i_new01_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Set of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Set00
+static int tolua_AllToLua_Vector3i_Set00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"Vector3i",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0);
+ int a_x = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_y = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_z = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL);
+#endif
+ {
+ self->Set(a_x,a_y,a_z);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Length of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Length00
+static int tolua_AllToLua_Vector3i_Length00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL);
+#endif
+ {
+ float tolua_ret = (float) self->Length();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SqrLength of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_SqrLength00
+static int tolua_AllToLua_Vector3i_SqrLength00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->SqrLength();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Equals of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Equals00
+static int tolua_AllToLua_Vector3i_Equals00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0);
+ const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Equals(*v);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Equals of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Equals01
+static int tolua_AllToLua_Vector3i_Equals01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0);
+ const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Equals(v);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_Vector3i_Equals00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: x of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3i_x
+static int tolua_get_Vector3i_x(lua_State* tolua_S)
+{
+ Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->x);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: x of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_set_Vector3i_x
+static int tolua_set_Vector3i_x(lua_State* tolua_S)
+{
+ Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->x = ((int) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: y of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3i_y
+static int tolua_get_Vector3i_y(lua_State* tolua_S)
+{
+ Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->y);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: y of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_set_Vector3i_y
+static int tolua_set_Vector3i_y(lua_State* tolua_S)
+{
+ Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->y = ((int) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: z of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_get_Vector3i_z
+static int tolua_get_Vector3i_z(lua_State* tolua_S)
+{
+ Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL);
+#endif
+ tolua_pushnumber(tolua_S,(lua_Number)self->z);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: z of class Vector3i */
+#ifndef TOLUA_DISABLE_tolua_set_Vector3i_z
+static int tolua_set_Vector3i_z(lua_State* tolua_S)
+{
+ Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL);
+ if (!tolua_isnumber(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->z = ((int) tolua_tonumber(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: p1 of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_get_cCuboid_p1
+static int tolua_get_cCuboid_p1(lua_State* tolua_S)
+{
+ cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p1'",NULL);
+#endif
+ tolua_pushusertype(tolua_S,(void*)&self->p1,"Vector3i");
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: p1 of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_set_cCuboid_p1
+static int tolua_set_cCuboid_p1(lua_State* tolua_S)
+{
+ cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p1'",NULL);
+ if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3i",0,&tolua_err)))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->p1 = *((Vector3i*) tolua_tousertype(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: p2 of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_get_cCuboid_p2
+static int tolua_get_cCuboid_p2(lua_State* tolua_S)
+{
+ cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p2'",NULL);
+#endif
+ tolua_pushusertype(tolua_S,(void*)&self->p2,"Vector3i");
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: p2 of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_set_cCuboid_p2
+static int tolua_set_cCuboid_p2(lua_State* tolua_S)
+{
+ cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p2'",NULL);
+ if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3i",0,&tolua_err)))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->p2 = *((Vector3i*) tolua_tousertype(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new00
+static int tolua_AllToLua_cCuboid_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new00_local
+static int tolua_AllToLua_cCuboid_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new01
+static int tolua_AllToLua_cCuboid_new01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cCuboid* a_Cuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0));
+ {
+ cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_Cuboid));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cCuboid_new00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new01_local
+static int tolua_AllToLua_cCuboid_new01_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cCuboid* a_Cuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0));
+ {
+ cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_Cuboid));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cCuboid_new00_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new02
+static int tolua_AllToLua_cCuboid_new02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3i",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3i* a_p1 = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+ const Vector3i* a_p2 = ((const Vector3i*) tolua_tousertype(tolua_S,3,0));
+ {
+ cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_p1,*a_p2));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cCuboid_new01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new02_local
+static int tolua_AllToLua_cCuboid_new02_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3i",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3i* a_p1 = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+ const Vector3i* a_p2 = ((const Vector3i*) tolua_tousertype(tolua_S,3,0));
+ {
+ cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_p1,*a_p2));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cCuboid_new01_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new03
+static int tolua_AllToLua_cCuboid_new03(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ int a_X1 = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0));
+ {
+ cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cCuboid_new02(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new03_local
+static int tolua_AllToLua_cCuboid_new03_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ int a_X1 = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0));
+ {
+ cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cCuboid_new02_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new04
+static int tolua_AllToLua_cCuboid_new04(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ int a_X1 = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_X2 = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0));
+ {
+ cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cCuboid_new03(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new04_local
+static int tolua_AllToLua_cCuboid_new04_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ int a_X1 = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_X2 = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0));
+ {
+ cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cCuboid_new03_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Assign of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Assign00
+static int tolua_AllToLua_cCuboid_Assign00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0);
+ int a_X1 = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_X2 = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Assign'", NULL);
+#endif
+ {
+ self->Assign(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Assign'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Sort of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Sort00
+static int tolua_AllToLua_cCuboid_Sort00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Sort'", NULL);
+#endif
+ {
+ self->Sort();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Sort'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DifX of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifX00
+static int tolua_AllToLua_cCuboid_DifX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifX'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->DifX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DifX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DifY of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifY00
+static int tolua_AllToLua_cCuboid_DifY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifY'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->DifY();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DifY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DifZ of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifZ00
+static int tolua_AllToLua_cCuboid_DifZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifZ'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->DifZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DifZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DoesIntersect of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DoesIntersect00
+static int tolua_AllToLua_cCuboid_DoesIntersect00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0);
+ const cCuboid* a_Other = ((const cCuboid*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoesIntersect'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DoesIntersect(*a_Other);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DoesIntersect'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInside of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside00
+static int tolua_AllToLua_cCuboid_IsInside00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0);
+ const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsInside(*v);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsInside'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInside of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside01
+static int tolua_AllToLua_cCuboid_IsInside01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0);
+ int a_X = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_Z = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsInside(a_X,a_Y,a_Z);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cCuboid_IsInside00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInside of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside02
+static int tolua_AllToLua_cCuboid_IsInside02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsInside(*v);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cCuboid_IsInside01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsCompletelyInside of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsCompletelyInside00
+static int tolua_AllToLua_cCuboid_IsCompletelyInside00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0);
+ const cCuboid* a_Outer = ((const cCuboid*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCompletelyInside'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsCompletelyInside(*a_Outer);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsCompletelyInside'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Move of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Move00
+static int tolua_AllToLua_cCuboid_Move00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0);
+ int a_OfsX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_OfsY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_OfsZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL);
+#endif
+ {
+ self->Move(a_OfsX,a_OfsY,a_OfsZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Move'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSorted of class cCuboid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsSorted00
+static int tolua_AllToLua_cCuboid_IsSorted00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSorted'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSorted();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSorted'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new00
+static int tolua_AllToLua_cBoundingBox_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ double a_MinX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_MaxX = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_MinY = ((double) tolua_tonumber(tolua_S,4,0));
+ double a_MaxY = ((double) tolua_tonumber(tolua_S,5,0));
+ double a_MinZ = ((double) tolua_tonumber(tolua_S,6,0));
+ double a_MaxZ = ((double) tolua_tonumber(tolua_S,7,0));
+ {
+ cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new00_local
+static int tolua_AllToLua_cBoundingBox_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ double a_MinX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_MaxX = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_MinY = ((double) tolua_tonumber(tolua_S,4,0));
+ double a_MaxY = ((double) tolua_tonumber(tolua_S,5,0));
+ double a_MinZ = ((double) tolua_tonumber(tolua_S,6,0));
+ double a_MaxZ = ((double) tolua_tonumber(tolua_S,7,0));
+ {
+ cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new01
+static int tolua_AllToLua_cBoundingBox_new01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0));
+ {
+ cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Min,*a_Max));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_new00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new01_local
+static int tolua_AllToLua_cBoundingBox_new01_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0));
+ {
+ cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Min,*a_Max));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_new00_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new02
+static int tolua_AllToLua_cBoundingBox_new02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ double a_Radius = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_Height = ((double) tolua_tonumber(tolua_S,4,0));
+ {
+ cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Pos,a_Radius,a_Height));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_new01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new02_local
+static int tolua_AllToLua_cBoundingBox_new02_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ double a_Radius = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_Height = ((double) tolua_tonumber(tolua_S,4,0));
+ {
+ cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Pos,a_Radius,a_Height));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_new01_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new03
+static int tolua_AllToLua_cBoundingBox_new03(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cBoundingBox* a_Orig = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0));
+ {
+ cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Orig));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox");
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_new02(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new03_local
+static int tolua_AllToLua_cBoundingBox_new03_local(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const cBoundingBox* a_Orig = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0));
+ {
+ cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Orig));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_new02_local(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Move of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Move00
+static int tolua_AllToLua_cBoundingBox_Move00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ double a_OffX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_OffY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_OffZ = ((double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL);
+#endif
+ {
+ self->Move(a_OffX,a_OffY,a_OffZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Move'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Move of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Move01
+static int tolua_AllToLua_cBoundingBox_Move01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_Off = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL);
+#endif
+ {
+ self->Move(*a_Off);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_Move00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Expand of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Expand00
+static int tolua_AllToLua_cBoundingBox_Expand00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ double a_ExpandX = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_ExpandY = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_ExpandZ = ((double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Expand'", NULL);
+#endif
+ {
+ self->Expand(a_ExpandX,a_ExpandY,a_ExpandZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Expand'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DoesIntersect of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_DoesIntersect00
+static int tolua_AllToLua_cBoundingBox_DoesIntersect00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ const cBoundingBox* a_Other = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoesIntersect'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->DoesIntersect(*a_Other);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DoesIntersect'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Union of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Union00
+static int tolua_AllToLua_cBoundingBox_Union00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ const cBoundingBox* a_Other = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Union'", NULL);
+#endif
+ {
+ cBoundingBox tolua_ret = (cBoundingBox) self->Union(*a_Other);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((cBoundingBox)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"cBoundingBox");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cBoundingBox));
+ tolua_pushusertype(tolua_S,tolua_obj,"cBoundingBox");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Union'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInside of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside00
+static int tolua_AllToLua_cBoundingBox_IsInside00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_Point = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsInside(*a_Point);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsInside'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInside of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside01
+static int tolua_AllToLua_cBoundingBox_IsInside01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ double a_X = ((double) tolua_tonumber(tolua_S,2,0));
+ double a_Y = ((double) tolua_tonumber(tolua_S,3,0));
+ double a_Z = ((double) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsInside(a_X,a_Y,a_Z);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_IsInside00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInside of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside02
+static int tolua_AllToLua_cBoundingBox_IsInside02(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBoundingBox",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ cBoundingBox* a_Other = ((cBoundingBox*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsInside(*a_Other);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_IsInside01(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInside of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside03
+static int tolua_AllToLua_cBoundingBox_IsInside03(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsInside(*a_Min,*a_Max);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_IsInside02(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInside of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside04
+static int tolua_AllToLua_cBoundingBox_IsInside04(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0));
+ const Vector3d* a_Point = ((const Vector3d*) tolua_tousertype(tolua_S,4,0));
+ {
+ bool tolua_ret = (bool) cBoundingBox::IsInside(*a_Min,*a_Max,*a_Point);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_IsInside03(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsInside of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside05
+static int tolua_AllToLua_cBoundingBox_IsInside05(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0));
+ double a_X = ((double) tolua_tonumber(tolua_S,4,0));
+ double a_Y = ((double) tolua_tonumber(tolua_S,5,0));
+ double a_Z = ((double) tolua_tonumber(tolua_S,6,0));
+ {
+ bool tolua_ret = (bool) cBoundingBox::IsInside(*a_Min,*a_Max,a_X,a_Y,a_Z);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_IsInside04(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CalcLineIntersection of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_CalcLineIntersection00
+static int tolua_AllToLua_cBoundingBox_CalcLineIntersection00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0);
+ const Vector3d* a_Line1 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ const Vector3d* a_Line2 = ((const Vector3d*) tolua_tousertype(tolua_S,3,0));
+ double a_LineCoeff = ((double) tolua_tonumber(tolua_S,4,0));
+ char a_Face = ((char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CalcLineIntersection'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->CalcLineIntersection(*a_Line1,*a_Line2,a_LineCoeff,a_Face);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushnumber(tolua_S,(lua_Number)a_LineCoeff);
+ tolua_pushnumber(tolua_S,(lua_Number)a_Face);
+ }
+ }
+ return 3;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CalcLineIntersection'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CalcLineIntersection of class cBoundingBox */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_CalcLineIntersection01
+static int tolua_AllToLua_cBoundingBox_CalcLineIntersection01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const Vector3d",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const Vector3d",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0));
+ const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0));
+ const Vector3d* a_Line1 = ((const Vector3d*) tolua_tousertype(tolua_S,4,0));
+ const Vector3d* a_Line2 = ((const Vector3d*) tolua_tousertype(tolua_S,5,0));
+ double a_LineCoeff = ((double) tolua_tonumber(tolua_S,6,0));
+ char a_Face = ((char) tolua_tonumber(tolua_S,7,0));
+ {
+ bool tolua_ret = (bool) cBoundingBox::CalcLineIntersection(*a_Min,*a_Max,*a_Line1,*a_Line2,a_LineCoeff,a_Face);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushnumber(tolua_S,(lua_Number)a_LineCoeff);
+ tolua_pushnumber(tolua_S,(lua_Number)a_Face);
+ }
+ }
+ return 3;
+tolua_lerror:
+ return tolua_AllToLua_cBoundingBox_CalcLineIntersection00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: BlockHitPosition of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_get_cTracer_BlockHitPosition
+static int tolua_get_cTracer_BlockHitPosition(lua_State* tolua_S)
+{
+ cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'BlockHitPosition'",NULL);
+#endif
+ tolua_pushusertype(tolua_S,(void*)&self->BlockHitPosition,"Vector3f");
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: BlockHitPosition of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_set_cTracer_BlockHitPosition
+static int tolua_set_cTracer_BlockHitPosition(lua_State* tolua_S)
+{
+ cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'BlockHitPosition'",NULL);
+ if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err)))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->BlockHitPosition = *((Vector3f*) tolua_tousertype(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: HitNormal of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_get_cTracer_HitNormal
+static int tolua_get_cTracer_HitNormal(lua_State* tolua_S)
+{
+ cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'HitNormal'",NULL);
+#endif
+ tolua_pushusertype(tolua_S,(void*)&self->HitNormal,"Vector3f");
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: HitNormal of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_set_cTracer_HitNormal
+static int tolua_set_cTracer_HitNormal(lua_State* tolua_S)
+{
+ cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'HitNormal'",NULL);
+ if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err)))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->HitNormal = *((Vector3f*) tolua_tousertype(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: RealHit of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_get_cTracer_RealHit
+static int tolua_get_cTracer_RealHit(lua_State* tolua_S)
+{
+ cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RealHit'",NULL);
+#endif
+ tolua_pushusertype(tolua_S,(void*)&self->RealHit,"Vector3f");
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: RealHit of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_set_cTracer_RealHit
+static int tolua_set_cTracer_RealHit(lua_State* tolua_S)
+{
+ cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RealHit'",NULL);
+ if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err)))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->RealHit = *((Vector3f*) tolua_tousertype(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_new00
+static int tolua_AllToLua_cTracer_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cTracer",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0));
+ {
+ cTracer* tolua_ret = (cTracer*) Mtolua_new((cTracer)(a_World));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cTracer");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_new00_local
+static int tolua_AllToLua_cTracer_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cTracer",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0));
+ {
+ cTracer* tolua_ret = (cTracer*) Mtolua_new((cTracer)(a_World));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cTracer");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: delete of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_delete00
+static int tolua_AllToLua_cTracer_delete00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL);
+#endif
+ Mtolua_delete(self);
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Trace of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_Trace00
+static int tolua_AllToLua_cTracer_Trace00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* a_Start = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+ const Vector3f* a_Direction = ((const Vector3f*) tolua_tousertype(tolua_S,3,0));
+ int a_Distance = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Trace'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Trace(*a_Start,*a_Direction,a_Distance);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Trace'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Trace of class cTracer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_Trace01
+static int tolua_AllToLua_cTracer_Trace01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) ||
+ (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3f",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0);
+ const Vector3f* a_Start = ((const Vector3f*) tolua_tousertype(tolua_S,2,0));
+ const Vector3f* a_Direction = ((const Vector3f*) tolua_tousertype(tolua_S,3,0));
+ int a_Distance = ((int) tolua_tonumber(tolua_S,4,0));
+ bool a_LineOfSight = ((bool) tolua_toboolean(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Trace'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Trace(*a_Start,*a_Direction,a_Distance,a_LineOfSight);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cTracer_Trace00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetName of class cGroup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_SetName00
+static int tolua_AllToLua_cGroup_SetName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0);
+ std::string a_Name = ((std::string) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL);
+#endif
+ {
+ self->SetName(a_Name);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetName of class cGroup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_GetName00
+static int tolua_AllToLua_cGroup_GetName00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cGroup",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cGroup* self = (const cGroup*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL);
+#endif
+ {
+ const std::string tolua_ret = (const std::string) self->GetName();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetColor of class cGroup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_SetColor00
+static int tolua_AllToLua_cGroup_SetColor00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0);
+ std::string a_Color = ((std::string) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetColor'", NULL);
+#endif
+ {
+ self->SetColor(a_Color);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetColor'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddCommand of class cGroup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_AddCommand00
+static int tolua_AllToLua_cGroup_AddCommand00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0);
+ std::string a_Command = ((std::string) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddCommand'", NULL);
+#endif
+ {
+ self->AddCommand(a_Command);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddCommand'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: AddPermission of class cGroup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_AddPermission00
+static int tolua_AllToLua_cGroup_AddPermission00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0);
+ std::string a_Permission = ((std::string) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPermission'", NULL);
+#endif
+ {
+ self->AddPermission(a_Permission);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'AddPermission'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: InheritFrom of class cGroup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_InheritFrom00
+static int tolua_AllToLua_cGroup_InheritFrom00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cGroup",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0);
+ cGroup* a_Group = ((cGroup*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'InheritFrom'", NULL);
+#endif
+ {
+ self->InheritFrom(a_Group);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'InheritFrom'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HasCommand of class cGroup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_HasCommand00
+static int tolua_AllToLua_cGroup_HasCommand00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0);
+ std::string a_Command = ((std::string) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasCommand'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->HasCommand(a_Command);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HasCommand'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetColor of class cGroup */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_GetColor00
+static int tolua_AllToLua_cGroup_GetColor00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cGroup",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cGroup* self = (const cGroup*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetColor'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetColor();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetColor'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_new00
+static int tolua_AllToLua_cBlockArea_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cBlockArea* tolua_ret = (cBlockArea*) Mtolua_new((cBlockArea)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockArea");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_new00_local
+static int tolua_AllToLua_cBlockArea_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ cBlockArea* tolua_ret = (cBlockArea*) Mtolua_new((cBlockArea)());
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockArea");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: delete of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_delete00
+static int tolua_AllToLua_cBlockArea_delete00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL);
+#endif
+ Mtolua_delete(self);
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Clear of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Clear00
+static int tolua_AllToLua_cBlockArea_Clear00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL);
+#endif
+ {
+ self->Clear();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Create of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Create00
+static int tolua_AllToLua_cBlockArea_Create00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_SizeX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_SizeY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_SizeZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Create'", NULL);
+#endif
+ {
+ self->Create(a_SizeX,a_SizeY,a_SizeZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Create'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Create of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Create01
+static int tolua_AllToLua_cBlockArea_Create01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_SizeX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_SizeY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_SizeZ = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_DataTypes = ((int) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Create'", NULL);
+#endif
+ {
+ self->Create(a_SizeX,a_SizeY,a_SizeZ,a_DataTypes);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cBlockArea_Create00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetOrigin of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetOrigin00
+static int tolua_AllToLua_cBlockArea_SetOrigin00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_OriginX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_OriginY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_OriginZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetOrigin'", NULL);
+#endif
+ {
+ self->SetOrigin(a_OriginX,a_OriginY,a_OriginZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetOrigin'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Read of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Read00
+static int tolua_AllToLua_cBlockArea_Read00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,9,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0));
+ int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MinBlockY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,7,0));
+ int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,8,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Read'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Read(a_World,a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Read'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Read of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Read01
+static int tolua_AllToLua_cBlockArea_Read01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,9,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,10,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0));
+ int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MinBlockY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,7,0));
+ int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,8,0));
+ int a_DataTypes = ((int) tolua_tonumber(tolua_S,9,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Read'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Read(a_World,a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ,a_DataTypes);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBlockArea_Read00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Write of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Write00
+static int tolua_AllToLua_cBlockArea_Write00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0));
+ int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Write'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Write(a_World,a_MinBlockX,a_MinBlockY,a_MinBlockZ);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Write'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Write of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Write01
+static int tolua_AllToLua_cBlockArea_Write01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0));
+ int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_DataTypes = ((int) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Write'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->Write(a_World,a_MinBlockX,a_MinBlockY,a_MinBlockZ,a_DataTypes);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ return tolua_AllToLua_cBlockArea_Write00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CopyTo of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_CopyTo00
+static int tolua_AllToLua_cBlockArea_CopyTo00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBlockArea",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ cBlockArea* a_Into = ((cBlockArea*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyTo'", NULL);
+#endif
+ {
+ self->CopyTo(*a_Into);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CopyTo'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: CopyFrom of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_CopyFrom00
+static int tolua_AllToLua_cBlockArea_CopyFrom00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ const cBlockArea* a_From = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyFrom'", NULL);
+#endif
+ {
+ self->CopyFrom(*a_From);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'CopyFrom'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: DumpToRawFile of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_DumpToRawFile00
+static int tolua_AllToLua_cBlockArea_DumpToRawFile00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DumpToRawFile'", NULL);
+#endif
+ {
+ self->DumpToRawFile(a_FileName);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'DumpToRawFile'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: LoadFromSchematicFile of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_LoadFromSchematicFile00
+static int tolua_AllToLua_cBlockArea_LoadFromSchematicFile00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadFromSchematicFile'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->LoadFromSchematicFile(a_FileName);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'LoadFromSchematicFile'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SaveToSchematicFile of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SaveToSchematicFile00
+static int tolua_AllToLua_cBlockArea_SaveToSchematicFile00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SaveToSchematicFile'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->SaveToSchematicFile(a_FileName);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_FileName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SaveToSchematicFile'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Crop of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Crop00
+static int tolua_AllToLua_cBlockArea_Crop00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_AddMinX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_SubMaxX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_AddMinY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_SubMaxY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_AddMinZ = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_SubMaxZ = ((int) tolua_tonumber(tolua_S,7,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Crop'", NULL);
+#endif
+ {
+ self->Crop(a_AddMinX,a_SubMaxX,a_AddMinY,a_SubMaxY,a_AddMinZ,a_SubMaxZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Crop'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Expand of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Expand00
+static int tolua_AllToLua_cBlockArea_Expand00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,8,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_SubMinX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_AddMaxX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_SubMinY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_AddMaxY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_SubMinZ = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_AddMaxZ = ((int) tolua_tonumber(tolua_S,7,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Expand'", NULL);
+#endif
+ {
+ self->Expand(a_SubMinX,a_AddMaxX,a_SubMinY,a_AddMaxY,a_SubMinZ,a_AddMaxZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Expand'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Merge of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Merge00
+static int tolua_AllToLua_cBlockArea_Merge00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ const cBlockArea* a_Src = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0));
+ int a_RelX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,5,0));
+ cBlockArea::eMergeStrategy a_Strategy = ((cBlockArea::eMergeStrategy) (int) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Merge'", NULL);
+#endif
+ {
+ self->Merge(*a_Src,a_RelX,a_RelY,a_RelZ,a_Strategy);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Merge'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Fill of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Fill00
+static int tolua_AllToLua_cBlockArea_Fill00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_DataTypes = ((int) tolua_tonumber(tolua_S,2,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,6,0x0f));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Fill'", NULL);
+#endif
+ {
+ self->Fill(a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Fill'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FillRelCuboid of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_FillRelCuboid00
+static int tolua_AllToLua_cBlockArea_FillRelCuboid00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,9,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,10,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,11,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,12,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,13,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_MinRelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_MaxRelX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MinRelY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MaxRelY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_MinRelZ = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_MaxRelZ = ((int) tolua_tonumber(tolua_S,7,0));
+ int a_DataTypes = ((int) tolua_tonumber(tolua_S,8,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,9,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,10,0));
+ unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,11,0));
+ unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,12,0x0f));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL);
+#endif
+ {
+ self->FillRelCuboid(a_MinRelX,a_MaxRelX,a_MinRelY,a_MaxRelY,a_MinRelZ,a_MaxRelZ,a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FillRelCuboid'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RelLine of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RelLine00
+static int tolua_AllToLua_cBlockArea_RelLine00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,9,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,10,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,11,1,&tolua_err) ||
+ !tolua_isnumber(tolua_S,12,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,13,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX1 = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY1 = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ1 = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_RelX2 = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_RelY2 = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_RelZ2 = ((int) tolua_tonumber(tolua_S,7,0));
+ int a_DataTypes = ((int) tolua_tonumber(tolua_S,8,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,9,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,10,0));
+ unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,11,0));
+ unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,12,0x0f));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RelLine'", NULL);
+#endif
+ {
+ self->RelLine(a_RelX1,a_RelY1,a_RelZ1,a_RelX2,a_RelY2,a_RelZ2,a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RelLine'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RotateCCW of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCCW00
+static int tolua_AllToLua_cBlockArea_RotateCCW00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCCW'", NULL);
+#endif
+ {
+ self->RotateCCW();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RotateCCW'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RotateCW of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCW00
+static int tolua_AllToLua_cBlockArea_RotateCW00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCW'", NULL);
+#endif
+ {
+ self->RotateCW();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RotateCW'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MirrorXY of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXY00
+static int tolua_AllToLua_cBlockArea_MirrorXY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXY'", NULL);
+#endif
+ {
+ self->MirrorXY();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MirrorXY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MirrorXZ of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXZ00
+static int tolua_AllToLua_cBlockArea_MirrorXZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXZ'", NULL);
+#endif
+ {
+ self->MirrorXZ();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MirrorXZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MirrorYZ of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorYZ00
+static int tolua_AllToLua_cBlockArea_MirrorYZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorYZ'", NULL);
+#endif
+ {
+ self->MirrorYZ();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MirrorYZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RotateCCWNoMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCCWNoMeta00
+static int tolua_AllToLua_cBlockArea_RotateCCWNoMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCCWNoMeta'", NULL);
+#endif
+ {
+ self->RotateCCWNoMeta();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RotateCCWNoMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RotateCWNoMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCWNoMeta00
+static int tolua_AllToLua_cBlockArea_RotateCWNoMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCWNoMeta'", NULL);
+#endif
+ {
+ self->RotateCWNoMeta();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RotateCWNoMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MirrorXYNoMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXYNoMeta00
+static int tolua_AllToLua_cBlockArea_MirrorXYNoMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXYNoMeta'", NULL);
+#endif
+ {
+ self->MirrorXYNoMeta();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MirrorXYNoMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MirrorXZNoMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXZNoMeta00
+static int tolua_AllToLua_cBlockArea_MirrorXZNoMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXZNoMeta'", NULL);
+#endif
+ {
+ self->MirrorXZNoMeta();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MirrorXZNoMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MirrorYZNoMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorYZNoMeta00
+static int tolua_AllToLua_cBlockArea_MirrorYZNoMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorYZNoMeta'", NULL);
+#endif
+ {
+ self->MirrorYZNoMeta();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MirrorYZNoMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRelBlockType of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockType00
+static int tolua_AllToLua_cBlockArea_SetRelBlockType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockType'", NULL);
+#endif
+ {
+ self->SetRelBlockType(a_RelX,a_RelY,a_RelZ,a_BlockType);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRelBlockType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlockType of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockType00
+static int tolua_AllToLua_cBlockArea_SetBlockType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockType'", NULL);
+#endif
+ {
+ self->SetBlockType(a_BlockX,a_BlockY,a_BlockZ,a_BlockType);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBlockType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRelBlockMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockMeta00
+static int tolua_AllToLua_cBlockArea_SetRelBlockMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockMeta'", NULL);
+#endif
+ {
+ self->SetRelBlockMeta(a_RelX,a_RelY,a_RelZ,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRelBlockMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlockMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockMeta00
+static int tolua_AllToLua_cBlockArea_SetBlockMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL);
+#endif
+ {
+ self->SetBlockMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRelBlockLight of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockLight00
+static int tolua_AllToLua_cBlockArea_SetRelBlockLight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockLight'", NULL);
+#endif
+ {
+ self->SetRelBlockLight(a_RelX,a_RelY,a_RelZ,a_BlockLight);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRelBlockLight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlockLight of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockLight00
+static int tolua_AllToLua_cBlockArea_SetBlockLight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockLight'", NULL);
+#endif
+ {
+ self->SetBlockLight(a_BlockX,a_BlockY,a_BlockZ,a_BlockLight);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBlockLight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRelBlockSkyLight of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00
+static int tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockSkyLight'", NULL);
+#endif
+ {
+ self->SetRelBlockSkyLight(a_RelX,a_RelY,a_RelZ,a_BlockSkyLight);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRelBlockSkyLight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlockSkyLight of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockSkyLight00
+static int tolua_AllToLua_cBlockArea_SetBlockSkyLight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockSkyLight'", NULL);
+#endif
+ {
+ self->SetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ,a_BlockSkyLight);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBlockSkyLight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRelBlockType of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockType00
+static int tolua_AllToLua_cBlockArea_GetRelBlockType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockType'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetRelBlockType(a_RelX,a_RelY,a_RelZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRelBlockType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockType of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockType00
+static int tolua_AllToLua_cBlockArea_GetBlockType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockType(a_BlockX,a_BlockY,a_BlockZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRelBlockMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockMeta00
+static int tolua_AllToLua_cBlockArea_GetRelBlockMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockMeta'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetRelBlockMeta(a_RelX,a_RelY,a_RelZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRelBlockMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockMeta00
+static int tolua_AllToLua_cBlockArea_GetBlockMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_BlockX,a_BlockY,a_BlockZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRelBlockLight of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockLight00
+static int tolua_AllToLua_cBlockArea_GetRelBlockLight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockLight'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetRelBlockLight(a_RelX,a_RelY,a_RelZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRelBlockLight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockLight of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockLight00
+static int tolua_AllToLua_cBlockArea_GetBlockLight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockLight'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockLight(a_BlockX,a_BlockY,a_BlockZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockLight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRelBlockSkyLight of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00
+static int tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockSkyLight'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetRelBlockSkyLight(a_RelX,a_RelY,a_RelZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRelBlockSkyLight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockSkyLight of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockSkyLight00
+static int tolua_AllToLua_cBlockArea_GetBlockSkyLight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockSkyLight'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockSkyLight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlockTypeMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockTypeMeta00
+static int tolua_AllToLua_cBlockArea_SetBlockTypeMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockTypeMeta'", NULL);
+#endif
+ {
+ self->SetBlockTypeMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBlockTypeMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetRelBlockTypeMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00
+static int tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockTypeMeta'", NULL);
+#endif
+ {
+ self->SetRelBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetRelBlockTypeMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockTypeMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockTypeMeta00
+static int tolua_AllToLua_cBlockArea_GetBlockTypeMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockTypeMeta'", NULL);
+#endif
+ {
+ self->GetBlockTypeMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockType);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockTypeMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetRelBlockTypeMeta of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00
+static int tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockTypeMeta'", NULL);
+#endif
+ {
+ self->GetRelBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockType);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetRelBlockTypeMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSizeX of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeX00
+static int tolua_AllToLua_cBlockArea_GetSizeX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeX'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetSizeX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSizeX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSizeY of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeY00
+static int tolua_AllToLua_cBlockArea_GetSizeY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeY'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetSizeY();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSizeY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSizeZ of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeZ00
+static int tolua_AllToLua_cBlockArea_GetSizeZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeZ'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetSizeZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSizeZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetOriginX of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginX00
+static int tolua_AllToLua_cBlockArea_GetOriginX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginX'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetOriginX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetOriginX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetOriginY of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginY00
+static int tolua_AllToLua_cBlockArea_GetOriginY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginY'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetOriginY();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetOriginY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetOriginZ of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginZ00
+static int tolua_AllToLua_cBlockArea_GetOriginZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginZ'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetOriginZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetOriginZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetDataTypes of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetDataTypes00
+static int tolua_AllToLua_cBlockArea_GetDataTypes00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDataTypes'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetDataTypes();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetDataTypes'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HasBlockTypes of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockTypes00
+static int tolua_AllToLua_cBlockArea_HasBlockTypes00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockTypes'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->HasBlockTypes();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HasBlockTypes'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HasBlockMetas of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockMetas00
+static int tolua_AllToLua_cBlockArea_HasBlockMetas00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockMetas'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->HasBlockMetas();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HasBlockMetas'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HasBlockLights of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockLights00
+static int tolua_AllToLua_cBlockArea_HasBlockLights00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockLights'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->HasBlockLights();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HasBlockLights'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: HasBlockSkyLights of class cBlockArea */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockSkyLights00
+static int tolua_AllToLua_cBlockArea_HasBlockSkyLights00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockSkyLights'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->HasBlockSkyLights();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'HasBlockSkyLights'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetChunkX of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetChunkX00
+static int tolua_AllToLua_cChunkDesc_GetChunkX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetChunkX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetChunkZ of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetChunkZ00
+static int tolua_AllToLua_cChunkDesc_GetChunkZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetChunkZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FillBlocks of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillBlocks00
+static int tolua_AllToLua_cChunkDesc_FillBlocks00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,2,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillBlocks'", NULL);
+#endif
+ {
+ self->FillBlocks(a_BlockType,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FillBlocks'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlockTypeMeta of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00
+static int tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockTypeMeta'", NULL);
+#endif
+ {
+ self->SetBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBlockTypeMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockTypeMeta of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00
+static int tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockTypeMeta'", NULL);
+#endif
+ {
+ self->GetBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockType);
+ tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockTypeMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlockType of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockType00
+static int tolua_AllToLua_cChunkDesc_SetBlockType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockType'", NULL);
+#endif
+ {
+ self->SetBlockType(a_RelX,a_RelY,a_RelZ,a_BlockType);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBlockType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockType of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockType00
+static int tolua_AllToLua_cChunkDesc_GetBlockType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockType(a_RelX,a_RelY,a_RelZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBlockMeta of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockMeta00
+static int tolua_AllToLua_cChunkDesc_SetBlockMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL);
+#endif
+ {
+ self->SetBlockMeta(a_RelX,a_RelY,a_RelZ,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockMeta of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockMeta00
+static int tolua_AllToLua_cChunkDesc_GetBlockMeta00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_RelX,a_RelY,a_RelZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetBiome of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBiome00
+static int tolua_AllToLua_cChunkDesc_SetBiome00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BiomeID = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBiome'", NULL);
+#endif
+ {
+ self->SetBiome(a_RelX,a_RelZ,a_BiomeID);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetBiome'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBiome of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBiome00
+static int tolua_AllToLua_cChunkDesc_GetBiome00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBiome'", NULL);
+#endif
+ {
+ EMCSBiome tolua_ret = (EMCSBiome) self->GetBiome(a_RelX,a_RelZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBiome'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetHeight of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetHeight00
+static int tolua_AllToLua_cChunkDesc_SetHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_Height = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeight'", NULL);
+#endif
+ {
+ self->SetHeight(a_RelX,a_RelZ,a_Height);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHeight of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetHeight00
+static int tolua_AllToLua_cChunkDesc_GetHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetHeight(a_RelX,a_RelZ);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetUseDefaultBiomes of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00
+static int tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ bool a_bUseDefaultBiomes = ((bool) tolua_toboolean(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultBiomes'", NULL);
+#endif
+ {
+ self->SetUseDefaultBiomes(a_bUseDefaultBiomes);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetUseDefaultBiomes'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsUsingDefaultBiomes of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00
+static int tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultBiomes'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsUsingDefaultBiomes();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultBiomes'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetUseDefaultHeight of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00
+static int tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ bool a_bUseDefaultHeight = ((bool) tolua_toboolean(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultHeight'", NULL);
+#endif
+ {
+ self->SetUseDefaultHeight(a_bUseDefaultHeight);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetUseDefaultHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsUsingDefaultHeight of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00
+static int tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultHeight'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsUsingDefaultHeight();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetUseDefaultComposition of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00
+static int tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ bool a_bUseDefaultComposition = ((bool) tolua_toboolean(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultComposition'", NULL);
+#endif
+ {
+ self->SetUseDefaultComposition(a_bUseDefaultComposition);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetUseDefaultComposition'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsUsingDefaultComposition of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00
+static int tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultComposition'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsUsingDefaultComposition();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultComposition'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetUseDefaultStructures of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00
+static int tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ bool a_bUseDefaultStructures = ((bool) tolua_toboolean(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultStructures'", NULL);
+#endif
+ {
+ self->SetUseDefaultStructures(a_bUseDefaultStructures);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetUseDefaultStructures'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsUsingDefaultStructures of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00
+static int tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultStructures'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsUsingDefaultStructures();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultStructures'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetUseDefaultFinish of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00
+static int tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isboolean(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ bool a_bUseDefaultFinish = ((bool) tolua_toboolean(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultFinish'", NULL);
+#endif
+ {
+ self->SetUseDefaultFinish(a_bUseDefaultFinish);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetUseDefaultFinish'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsUsingDefaultFinish of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00
+static int tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultFinish'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsUsingDefaultFinish();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultFinish'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: WriteBlockArea of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_WriteBlockArea00
+static int tolua_AllToLua_cChunkDesc_WriteBlockArea00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,1,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ const cBlockArea* a_BlockArea = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0));
+ int a_RelX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,5,0));
+ cBlockArea::eMergeStrategy a_MergeStrategy = ((cBlockArea::eMergeStrategy) (int) tolua_tonumber(tolua_S,6,cBlockArea::msOverwrite));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WriteBlockArea'", NULL);
+#endif
+ {
+ self->WriteBlockArea(*a_BlockArea,a_RelX,a_RelY,a_RelZ,a_MergeStrategy);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'WriteBlockArea'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ReadBlockArea of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReadBlockArea00
+static int tolua_AllToLua_cChunkDesc_ReadBlockArea00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBlockArea",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,9,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ cBlockArea* a_Dest = ((cBlockArea*) tolua_tousertype(tolua_S,2,0));
+ int a_MinRelX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MaxRelX = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MinRelY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_MaxRelY = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_MinRelZ = ((int) tolua_tonumber(tolua_S,7,0));
+ int a_MaxRelZ = ((int) tolua_tonumber(tolua_S,8,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReadBlockArea'", NULL);
+#endif
+ {
+ self->ReadBlockArea(*a_Dest,a_MinRelX,a_MaxRelX,a_MinRelY,a_MaxRelY,a_MinRelZ,a_MaxRelZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ReadBlockArea'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMaxHeight of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetMaxHeight00
+static int tolua_AllToLua_cChunkDesc_GetMaxHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxHeight'", NULL);
+#endif
+ {
+ unsigned char tolua_ret = ( unsigned char) self->GetMaxHeight();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMaxHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FillRelCuboid of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillRelCuboid00
+static int tolua_AllToLua_cChunkDesc_FillRelCuboid00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,9,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,10,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_MinX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MinY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,8,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL);
+#endif
+ {
+ self->FillRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_BlockType,a_BlockMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FillRelCuboid'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FillRelCuboid of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillRelCuboid01
+static int tolua_AllToLua_cChunkDesc_FillRelCuboid01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL);
+#endif
+ {
+ self->FillRelCuboid(*a_RelCuboid,a_BlockType,a_BlockMeta);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cChunkDesc_FillRelCuboid00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ReplaceRelCuboid of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00
+static int tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,9,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,10,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,11,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,12,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_MinX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MinY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0));
+ unsigned char a_SrcType = (( unsigned char) tolua_tonumber(tolua_S,8,0));
+ unsigned char a_SrcMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0));
+ unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,10,0));
+ unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,11,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReplaceRelCuboid'", NULL);
+#endif
+ {
+ self->ReplaceRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_SrcType,a_SrcMeta,a_DstType,a_DstMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ReplaceRelCuboid'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ReplaceRelCuboid of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01
+static int tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0));
+ unsigned char a_SrcType = (( unsigned char) tolua_tonumber(tolua_S,3,0));
+ unsigned char a_SrcMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0));
+ unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,5,0));
+ unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReplaceRelCuboid'", NULL);
+#endif
+ {
+ self->ReplaceRelCuboid(*a_RelCuboid,a_SrcType,a_SrcMeta,a_DstType,a_DstMeta);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FloorRelCuboid of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FloorRelCuboid00
+static int tolua_AllToLua_cChunkDesc_FloorRelCuboid00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,9,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,10,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_MinX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MinY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0));
+ unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,8,0));
+ unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FloorRelCuboid'", NULL);
+#endif
+ {
+ self->FloorRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_DstType,a_DstMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FloorRelCuboid'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FloorRelCuboid of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FloorRelCuboid01
+static int tolua_AllToLua_cChunkDesc_FloorRelCuboid01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0));
+ unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,3,0));
+ unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FloorRelCuboid'", NULL);
+#endif
+ {
+ self->FloorRelCuboid(*a_RelCuboid,a_DstType,a_DstMeta);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cChunkDesc_FloorRelCuboid00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RandomFillRelCuboid of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00
+static int tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,7,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,8,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,9,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,10,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,11,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,12,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_MinX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_MinY = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0));
+ int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,8,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0));
+ int a_RandomSeed = ((int) tolua_tonumber(tolua_S,10,0));
+ int a_ChanceOutOf10k = ((int) tolua_tonumber(tolua_S,11,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RandomFillRelCuboid'", NULL);
+#endif
+ {
+ self->RandomFillRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_BlockType,a_BlockMeta,a_RandomSeed,a_ChanceOutOf10k);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RandomFillRelCuboid'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: RandomFillRelCuboid of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01
+static int tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0));
+ unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0));
+ unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0));
+ int a_RandomSeed = ((int) tolua_tonumber(tolua_S,5,0));
+ int a_ChanceOutOf10k = ((int) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RandomFillRelCuboid'", NULL);
+#endif
+ {
+ self->RandomFillRelCuboid(*a_RelCuboid,a_BlockType,a_BlockMeta,a_RandomSeed,a_ChanceOutOf10k);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBlockEntity of class cChunkDesc */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockEntity00
+static int tolua_AllToLua_cChunkDesc_GetBlockEntity00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0);
+ int a_RelX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_RelY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockEntity'", NULL);
+#endif
+ {
+ cBlockEntity* tolua_ret = (cBlockEntity*) self->GetBlockEntity(a_RelX,a_RelY,a_RelZ);
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockEntity");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBlockEntity'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cCraftingGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_new00
+static int tolua_AllToLua_cCraftingGrid_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCraftingGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ int a_Width = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Height = ((int) tolua_tonumber(tolua_S,3,0));
+ {
+ cCraftingGrid* tolua_ret = (cCraftingGrid*) Mtolua_new((cCraftingGrid)(a_Width,a_Height));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingGrid");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cCraftingGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_new00_local
+static int tolua_AllToLua_cCraftingGrid_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cCraftingGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ int a_Width = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Height = ((int) tolua_tonumber(tolua_S,3,0));
+ {
+ cCraftingGrid* tolua_ret = (cCraftingGrid*) Mtolua_new((cCraftingGrid)(a_Width,a_Height));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingGrid");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWidth of class cCraftingGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetWidth00
+static int tolua_AllToLua_cCraftingGrid_GetWidth00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetWidth();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetHeight of class cCraftingGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetHeight00
+static int tolua_AllToLua_cCraftingGrid_GetHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetHeight();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetItem of class cCraftingGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetItem00
+static int tolua_AllToLua_cCraftingGrid_GetItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0);
+ int x = ((int) tolua_tonumber(tolua_S,2,0));
+ int y = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetItem'", NULL);
+#endif
+ {
+ cItem& tolua_ret = (cItem&) self->GetItem(x,y);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetItem of class cCraftingGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_SetItem00
+static int tolua_AllToLua_cCraftingGrid_SetItem00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0);
+ int x = ((int) tolua_tonumber(tolua_S,2,0));
+ int y = ((int) tolua_tonumber(tolua_S,3,0));
+ ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,4,0));
+ int a_ItemCount = ((int) tolua_tonumber(tolua_S,5,0));
+ short a_ItemHealth = ((short) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetItem'", NULL);
+#endif
+ {
+ self->SetItem(x,y,a_ItemType,a_ItemCount,a_ItemHealth);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetItem'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetItem of class cCraftingGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_SetItem01
+static int tolua_AllToLua_cCraftingGrid_SetItem01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0);
+ int x = ((int) tolua_tonumber(tolua_S,2,0));
+ int y = ((int) tolua_tonumber(tolua_S,3,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetItem'", NULL);
+#endif
+ {
+ self->SetItem(x,y,*a_Item);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cCraftingGrid_SetItem00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Clear of class cCraftingGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_Clear00
+static int tolua_AllToLua_cCraftingGrid_Clear00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL);
+#endif
+ {
+ self->Clear();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ConsumeGrid of class cCraftingGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_ConsumeGrid00
+static int tolua_AllToLua_cCraftingGrid_ConsumeGrid00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCraftingGrid",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0);
+ const cCraftingGrid* a_Grid = ((const cCraftingGrid*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ConsumeGrid'", NULL);
+#endif
+ {
+ self->ConsumeGrid(*a_Grid);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ConsumeGrid'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Dump of class cCraftingGrid */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_Dump00
+static int tolua_AllToLua_cCraftingGrid_Dump00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dump'", NULL);
+#endif
+ {
+ self->Dump();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Dump'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Clear of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_Clear00
+static int tolua_AllToLua_cCraftingRecipe_Clear00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL);
+#endif
+ {
+ self->Clear();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetIngredientsWidth of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00
+static int tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredientsWidth'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetIngredientsWidth();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetIngredientsWidth'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetIngredientsHeight of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00
+static int tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredientsHeight'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetIngredientsHeight();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetIngredientsHeight'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetIngredient of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredient00
+static int tolua_AllToLua_cCraftingRecipe_GetIngredient00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+ int x = ((int) tolua_tonumber(tolua_S,2,0));
+ int y = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredient'", NULL);
+#endif
+ {
+ cItem& tolua_ret = (cItem&) self->GetIngredient(x,y);
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetIngredient'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetResult of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetResult00
+static int tolua_AllToLua_cCraftingRecipe_GetResult00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetResult'", NULL);
+#endif
+ {
+ const cItem& tolua_ret = (const cItem&) self->GetResult();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetResult'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetResult of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetResult00
+static int tolua_AllToLua_cCraftingRecipe_SetResult00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+ ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,2,0));
+ int a_ItemCount = ((int) tolua_tonumber(tolua_S,3,0));
+ short a_ItemHealth = ((short) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetResult'", NULL);
+#endif
+ {
+ self->SetResult(a_ItemType,a_ItemCount,a_ItemHealth);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetResult'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetResult of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetResult01
+static int tolua_AllToLua_cCraftingRecipe_SetResult01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetResult'", NULL);
+#endif
+ {
+ self->SetResult(*a_Item);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cCraftingRecipe_SetResult00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetIngredient of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetIngredient00
+static int tolua_AllToLua_cCraftingRecipe_SetIngredient00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,6,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,7,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+ int x = ((int) tolua_tonumber(tolua_S,2,0));
+ int y = ((int) tolua_tonumber(tolua_S,3,0));
+ ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,4,0));
+ int a_ItemCount = ((int) tolua_tonumber(tolua_S,5,0));
+ short a_ItemHealth = ((short) tolua_tonumber(tolua_S,6,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIngredient'", NULL);
+#endif
+ {
+ self->SetIngredient(x,y,a_ItemType,a_ItemCount,a_ItemHealth);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetIngredient'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetIngredient of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetIngredient01
+static int tolua_AllToLua_cCraftingRecipe_SetIngredient01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+ int x = ((int) tolua_tonumber(tolua_S,2,0));
+ int y = ((int) tolua_tonumber(tolua_S,3,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIngredient'", NULL);
+#endif
+ {
+ self->SetIngredient(x,y,*a_Item);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cCraftingRecipe_SetIngredient00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: ConsumeIngredients of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00
+static int tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cCraftingGrid",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+ cCraftingGrid* a_CraftingGrid = ((cCraftingGrid*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ConsumeIngredients'", NULL);
+#endif
+ {
+ self->ConsumeIngredients(*a_CraftingGrid);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'ConsumeIngredients'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: Dump of class cCraftingRecipe */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_Dump00
+static int tolua_AllToLua_cCraftingRecipe_Dump00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dump'", NULL);
+#endif
+ {
+ self->Dump();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'Dump'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWindowID of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowID00
+static int tolua_AllToLua_cWindow_GetWindowID00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowID'", NULL);
+#endif
+ {
+ char tolua_ret = (char) self->GetWindowID();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWindowID'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWindowType of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowType00
+static int tolua_AllToLua_cWindow_GetWindowType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowType'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetWindowType();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWindowType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSlot of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetSlot00
+static int tolua_AllToLua_cWindow_GetSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0);
+ cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0));
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL);
+#endif
+ {
+ const cItem* tolua_ret = (const cItem*) self->GetSlot(*a_Player,a_SlotNum);
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"const cItem");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetSlot of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetSlot00
+static int tolua_AllToLua_cWindow_SetSlot00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err)) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0);
+ cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0));
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,3,0));
+ const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL);
+#endif
+ {
+ self->SetSlot(*a_Player,a_SlotNum,*a_Item);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSlotInPlayerMainInventory of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00
+static int tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerMainInventory'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSlotInPlayerMainInventory(a_SlotNum);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerMainInventory'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSlotInPlayerHotbar of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00
+static int tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerHotbar'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSlotInPlayerHotbar(a_SlotNum);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerHotbar'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsSlotInPlayerInventory of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerInventory00
+static int tolua_AllToLua_cWindow_IsSlotInPlayerInventory00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0);
+ int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerInventory'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSlotInPlayerInventory(a_SlotNum);
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerInventory'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWindowTitle of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowTitle00
+static int tolua_AllToLua_cWindow_GetWindowTitle00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowTitle'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetWindowTitle();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWindowTitle'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetWindowTitle of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetWindowTitle00
+static int tolua_AllToLua_cWindow_SetWindowTitle00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0);
+ const AString a_WindowTitle = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWindowTitle'", NULL);
+#endif
+ {
+ self->SetWindowTitle(a_WindowTitle);
+ tolua_pushcppstring(tolua_S,(const char*)a_WindowTitle);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetWindowTitle'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetProperty of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetProperty00
+static int tolua_AllToLua_cWindow_SetProperty00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0);
+ int a_Property = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Value = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetProperty'", NULL);
+#endif
+ {
+ self->SetProperty(a_Property,a_Value);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetProperty'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetProperty of class cWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetProperty01
+static int tolua_AllToLua_cWindow_SetProperty01(lua_State* tolua_S)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"cPlayer",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ {
+ cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0);
+ int a_Property = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_Value = ((int) tolua_tonumber(tolua_S,3,0));
+ cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetProperty'", NULL);
+#endif
+ {
+ self->SetProperty(a_Property,a_Value,*a_Player);
+ }
+ }
+ return 0;
+tolua_lerror:
+ return tolua_AllToLua_cWindow_SetProperty00(tolua_S);
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new of class cLuaWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_new00
+static int tolua_AllToLua_cLuaWindow_new00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cLuaWindow",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWindow::WindowType a_WindowType = ((cWindow::WindowType) (int) tolua_tonumber(tolua_S,2,0));
+ int a_SlotsX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_SlotsY = ((int) tolua_tonumber(tolua_S,4,0));
+ const AString a_Title = ((const AString) tolua_tocppstring(tolua_S,5,0));
+ {
+ cLuaWindow* tolua_ret = (cLuaWindow*) Mtolua_new((cLuaWindow)(a_WindowType,a_SlotsX,a_SlotsY,a_Title));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cLuaWindow");
+ tolua_pushcppstring(tolua_S,(const char*)a_Title);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: new_local of class cLuaWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_new00_local
+static int tolua_AllToLua_cLuaWindow_new00_local(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cLuaWindow",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWindow::WindowType a_WindowType = ((cWindow::WindowType) (int) tolua_tonumber(tolua_S,2,0));
+ int a_SlotsX = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_SlotsY = ((int) tolua_tonumber(tolua_S,4,0));
+ const AString a_Title = ((const AString) tolua_tocppstring(tolua_S,5,0));
+ {
+ cLuaWindow* tolua_ret = (cLuaWindow*) Mtolua_new((cLuaWindow)(a_WindowType,a_SlotsX,a_SlotsY,a_Title));
+ tolua_pushusertype(tolua_S,(void*)tolua_ret,"cLuaWindow");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+ tolua_pushcppstring(tolua_S,(const char*)a_Title);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: delete of class cLuaWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_delete00
+static int tolua_AllToLua_cLuaWindow_delete00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cLuaWindow",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL);
+#endif
+ Mtolua_delete(self);
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetContents of class cLuaWindow */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_GetContents00
+static int tolua_AllToLua_cLuaWindow_GetContents00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cLuaWindow",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetContents'", NULL);
+#endif
+ {
+ cItemGrid& tolua_ret = (cItemGrid&) self->GetContents();
+ tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid");
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetContents'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMobType of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobType00
+static int tolua_AllToLua_cMonster_GetMobType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobType'", NULL);
+#endif
+ {
+ cMonster::eType tolua_ret = (cMonster::eType) self->GetMobType();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMobType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMobFamily of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobFamily00
+static int tolua_AllToLua_cMonster_GetMobFamily00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobFamily'", NULL);
+#endif
+ {
+ cMonster::eFamily tolua_ret = (cMonster::eFamily) self->GetMobFamily();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMobFamily'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: MobTypeToString of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_MobTypeToString00
+static int tolua_AllToLua_cMonster_MobTypeToString00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0));
+ {
+ AString tolua_ret = (AString) cMonster::MobTypeToString(a_MobType);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'MobTypeToString'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: StringToMobType of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_StringToMobType00
+static int tolua_AllToLua_cMonster_StringToMobType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const AString a_MobTypeName = ((const AString) tolua_tocppstring(tolua_S,2,0));
+ {
+ cMonster::eType tolua_ret = (cMonster::eType) cMonster::StringToMobType(a_MobTypeName);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_MobTypeName);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'StringToMobType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: FamilyFromType of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_FamilyFromType00
+static int tolua_AllToLua_cMonster_FamilyFromType00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0));
+ {
+ cMonster::eFamily tolua_ret = (cMonster::eFamily) cMonster::FamilyFromType(a_MobType);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'FamilyFromType'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetSpawnDelay of class cMonster */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetSpawnDelay00
+static int tolua_AllToLua_cMonster_GetSpawnDelay00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cMonster::eFamily a_MobFamily = ((cMonster::eFamily) (int) tolua_tonumber(tolua_S,2,0));
+ {
+ int tolua_ret = (int) cMonster::GetSpawnDelay(a_MobFamily);
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetSpawnDelay'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* Open function */
+TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
+{
+ tolua_open(tolua_S);
+ tolua_reg_types(tolua_S);
+ tolua_module(tolua_S,NULL,1);
+ tolua_beginmodule(tolua_S,NULL);
+ tolua_constant(tolua_S,"biOcean",biOcean);
+ tolua_constant(tolua_S,"biPlains",biPlains);
+ tolua_constant(tolua_S,"biDesert",biDesert);
+ tolua_constant(tolua_S,"biExtremeHills",biExtremeHills);
+ tolua_constant(tolua_S,"biForest",biForest);
+ tolua_constant(tolua_S,"biTaiga",biTaiga);
+ tolua_constant(tolua_S,"biSwampland",biSwampland);
+ tolua_constant(tolua_S,"biRiver",biRiver);
+ tolua_constant(tolua_S,"biHell",biHell);
+ tolua_constant(tolua_S,"biNether",biNether);
+ tolua_constant(tolua_S,"biSky",biSky);
+ tolua_constant(tolua_S,"biEnd",biEnd);
+ tolua_constant(tolua_S,"biFrozenOcean",biFrozenOcean);
+ tolua_constant(tolua_S,"biFrozenRiver",biFrozenRiver);
+ tolua_constant(tolua_S,"biIcePlains",biIcePlains);
+ tolua_constant(tolua_S,"biTundra",biTundra);
+ tolua_constant(tolua_S,"biIceMountains",biIceMountains);
+ tolua_constant(tolua_S,"biMushroomIsland",biMushroomIsland);
+ tolua_constant(tolua_S,"biMushroomShore",biMushroomShore);
+ tolua_constant(tolua_S,"biBeach",biBeach);
+ tolua_constant(tolua_S,"biDesertHills",biDesertHills);
+ tolua_constant(tolua_S,"biForestHills",biForestHills);
+ tolua_constant(tolua_S,"biTaigaHills",biTaigaHills);
+ tolua_constant(tolua_S,"biExtremeHillsEdge",biExtremeHillsEdge);
+ tolua_constant(tolua_S,"biJungle",biJungle);
+ tolua_constant(tolua_S,"biJungleHills",biJungleHills);
+ tolua_constant(tolua_S,"biJungleEdge",biJungleEdge);
+ tolua_constant(tolua_S,"biDeepOcean",biDeepOcean);
+ tolua_constant(tolua_S,"biStoneBeach",biStoneBeach);
+ tolua_constant(tolua_S,"biColdBeach",biColdBeach);
+ tolua_constant(tolua_S,"biBirchForest",biBirchForest);
+ tolua_constant(tolua_S,"biBirchForestHills",biBirchForestHills);
+ tolua_constant(tolua_S,"biRoofedForest",biRoofedForest);
+ tolua_constant(tolua_S,"biColdTaiga",biColdTaiga);
+ tolua_constant(tolua_S,"biColdTaigaHills",biColdTaigaHills);
+ tolua_constant(tolua_S,"biMegaTaiga",biMegaTaiga);
+ tolua_constant(tolua_S,"biMegaTaigaHills",biMegaTaigaHills);
+ tolua_constant(tolua_S,"biExtremeHillsPlus",biExtremeHillsPlus);
+ tolua_constant(tolua_S,"biSavanna",biSavanna);
+ tolua_constant(tolua_S,"biSavannaPlateau",biSavannaPlateau);
+ tolua_constant(tolua_S,"biMesa",biMesa);
+ tolua_constant(tolua_S,"biMesaPlateauF",biMesaPlateauF);
+ tolua_constant(tolua_S,"biMesaPlateau",biMesaPlateau);
+ tolua_constant(tolua_S,"biNumBiomes",biNumBiomes);
+ tolua_constant(tolua_S,"biMaxBiome",biMaxBiome);
+ tolua_constant(tolua_S,"biVariant",biVariant);
+ tolua_constant(tolua_S,"biSunflowerPlains",biSunflowerPlains);
+ tolua_constant(tolua_S,"biDesertM",biDesertM);
+ tolua_constant(tolua_S,"biExtremeHillsM",biExtremeHillsM);
+ tolua_constant(tolua_S,"biFlowerForest",biFlowerForest);
+ tolua_constant(tolua_S,"biTaigaM",biTaigaM);
+ tolua_constant(tolua_S,"biSwamplandM",biSwamplandM);
+ tolua_constant(tolua_S,"biIcePlainsSpikes",biIcePlainsSpikes);
+ tolua_constant(tolua_S,"biJungleM",biJungleM);
+ tolua_constant(tolua_S,"biJungleEdgeM",biJungleEdgeM);
+ tolua_constant(tolua_S,"biBirchForestM",biBirchForestM);
+ tolua_constant(tolua_S,"biBirchForestHillsM",biBirchForestHillsM);
+ tolua_constant(tolua_S,"biRoofedForestM",biRoofedForestM);
+ tolua_constant(tolua_S,"biColdTaigaM",biColdTaigaM);
+ tolua_constant(tolua_S,"biMegaSpruceTaiga",biMegaSpruceTaiga);
+ tolua_constant(tolua_S,"biMegaSpruceTaigaHills",biMegaSpruceTaigaHills);
+ tolua_constant(tolua_S,"biExtremeHillsPlusM",biExtremeHillsPlusM);
+ tolua_constant(tolua_S,"biSavannaM",biSavannaM);
+ tolua_constant(tolua_S,"biSavannaPlateauM",biSavannaPlateauM);
+ tolua_constant(tolua_S,"biMesaBryce",biMesaBryce);
+ tolua_constant(tolua_S,"biMesaPlateauFM",biMesaPlateauFM);
+ tolua_constant(tolua_S,"biMesaPlateauM",biMesaPlateauM);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cIniFile","cIniFile","",tolua_collect_cIniFile);
+ #else
+ tolua_cclass(tolua_S,"cIniFile","cIniFile","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cIniFile");
+ tolua_constant(tolua_S,"noID",cIniFile::noID);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cIniFile_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cIniFile_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cIniFile_new00_local);
+ tolua_function(tolua_S,"CaseSensitive",tolua_AllToLua_cIniFile_CaseSensitive00);
+ tolua_function(tolua_S,"CaseInsensitive",tolua_AllToLua_cIniFile_CaseInsensitive00);
+ tolua_function(tolua_S,"ReadFile",tolua_AllToLua_cIniFile_ReadFile00);
+ tolua_function(tolua_S,"WriteFile",tolua_AllToLua_cIniFile_WriteFile00);
+ tolua_function(tolua_S,"Clear",tolua_AllToLua_cIniFile_Clear00);
+ tolua_function(tolua_S,"FindKey",tolua_AllToLua_cIniFile_FindKey00);
+ tolua_function(tolua_S,"FindValue",tolua_AllToLua_cIniFile_FindValue00);
+ tolua_function(tolua_S,"GetNumKeys",tolua_AllToLua_cIniFile_GetNumKeys00);
+ tolua_function(tolua_S,"AddKeyName",tolua_AllToLua_cIniFile_AddKeyName00);
+ tolua_function(tolua_S,"GetKeyName",tolua_AllToLua_cIniFile_GetKeyName00);
+ tolua_function(tolua_S,"GetNumValues",tolua_AllToLua_cIniFile_GetNumValues00);
+ tolua_function(tolua_S,"GetNumValues",tolua_AllToLua_cIniFile_GetNumValues01);
+ tolua_function(tolua_S,"GetValueName",tolua_AllToLua_cIniFile_GetValueName00);
+ tolua_function(tolua_S,"GetValueName",tolua_AllToLua_cIniFile_GetValueName01);
+ tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue00);
+ tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue01);
+ tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue02);
+ tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue03);
+ tolua_function(tolua_S,"GetValueF",tolua_AllToLua_cIniFile_GetValueF00);
+ tolua_function(tolua_S,"GetValueI",tolua_AllToLua_cIniFile_GetValueI00);
+ tolua_function(tolua_S,"GetValueB",tolua_AllToLua_cIniFile_GetValueB00);
+ tolua_function(tolua_S,"GetValueSet",tolua_AllToLua_cIniFile_GetValueSet00);
+ tolua_function(tolua_S,"GetValueSet",tolua_AllToLua_cIniFile_GetValueSet01);
+ tolua_function(tolua_S,"GetValueSetF",tolua_AllToLua_cIniFile_GetValueSetF00);
+ tolua_function(tolua_S,"GetValueSetI",tolua_AllToLua_cIniFile_GetValueSetI00);
+ tolua_function(tolua_S,"GetValueSetB",tolua_AllToLua_cIniFile_GetValueSetB00);
+ tolua_function(tolua_S,"SetValue",tolua_AllToLua_cIniFile_SetValue00);
+ tolua_function(tolua_S,"SetValue",tolua_AllToLua_cIniFile_SetValue01);
+ tolua_function(tolua_S,"SetValueI",tolua_AllToLua_cIniFile_SetValueI00);
+ tolua_function(tolua_S,"SetValueB",tolua_AllToLua_cIniFile_SetValueB00);
+ tolua_function(tolua_S,"SetValueF",tolua_AllToLua_cIniFile_SetValueF00);
+ tolua_function(tolua_S,"DeleteValueByID",tolua_AllToLua_cIniFile_DeleteValueByID00);
+ tolua_function(tolua_S,"DeleteValue",tolua_AllToLua_cIniFile_DeleteValue00);
+ tolua_function(tolua_S,"DeleteKey",tolua_AllToLua_cIniFile_DeleteKey00);
+ tolua_function(tolua_S,"GetNumHeaderComments",tolua_AllToLua_cIniFile_GetNumHeaderComments00);
+ tolua_function(tolua_S,"AddHeaderComment",tolua_AllToLua_cIniFile_AddHeaderComment00);
+ tolua_function(tolua_S,"GetHeaderComment",tolua_AllToLua_cIniFile_GetHeaderComment00);
+ tolua_function(tolua_S,"DeleteHeaderComment",tolua_AllToLua_cIniFile_DeleteHeaderComment00);
+ tolua_function(tolua_S,"DeleteHeaderComments",tolua_AllToLua_cIniFile_DeleteHeaderComments00);
+ tolua_function(tolua_S,"GetNumKeyComments",tolua_AllToLua_cIniFile_GetNumKeyComments00);
+ tolua_function(tolua_S,"GetNumKeyComments",tolua_AllToLua_cIniFile_GetNumKeyComments01);
+ tolua_function(tolua_S,"AddKeyComment",tolua_AllToLua_cIniFile_AddKeyComment00);
+ tolua_function(tolua_S,"AddKeyComment",tolua_AllToLua_cIniFile_AddKeyComment01);
+ tolua_function(tolua_S,"GetKeyComment",tolua_AllToLua_cIniFile_GetKeyComment00);
+ tolua_function(tolua_S,"GetKeyComment",tolua_AllToLua_cIniFile_GetKeyComment01);
+ tolua_function(tolua_S,"DeleteKeyComment",tolua_AllToLua_cIniFile_DeleteKeyComment00);
+ tolua_function(tolua_S,"DeleteKeyComment",tolua_AllToLua_cIniFile_DeleteKeyComment01);
+ tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments00);
+ tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments01);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cFile","cFile","",NULL);
+ tolua_beginmodule(tolua_S,"cFile");
+ tolua_function(tolua_S,"Exists",tolua_AllToLua_cFile_Exists00);
+ tolua_function(tolua_S,"Delete",tolua_AllToLua_cFile_Delete00);
+ tolua_function(tolua_S,"Rename",tolua_AllToLua_cFile_Rename00);
+ tolua_function(tolua_S,"Copy",tolua_AllToLua_cFile_Copy00);
+ tolua_function(tolua_S,"IsFolder",tolua_AllToLua_cFile_IsFolder00);
+ tolua_function(tolua_S,"IsFile",tolua_AllToLua_cFile_IsFile00);
+ tolua_function(tolua_S,"GetSize",tolua_AllToLua_cFile_GetSize00);
+ tolua_function(tolua_S,"CreateFolder",tolua_AllToLua_cFile_CreateFolder00);
+ tolua_function(tolua_S,"ReadWholeFile",tolua_AllToLua_cFile_ReadWholeFile00);
+ tolua_endmodule(tolua_S);
+ tolua_constant(tolua_S,"E_BLOCK_AIR",E_BLOCK_AIR);
+ tolua_constant(tolua_S,"E_BLOCK_STONE",E_BLOCK_STONE);
+ tolua_constant(tolua_S,"E_BLOCK_GRASS",E_BLOCK_GRASS);
+ tolua_constant(tolua_S,"E_BLOCK_DIRT",E_BLOCK_DIRT);
+ tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE",E_BLOCK_COBBLESTONE);
+ tolua_constant(tolua_S,"E_BLOCK_PLANKS",E_BLOCK_PLANKS);
+ tolua_constant(tolua_S,"E_BLOCK_SAPLING",E_BLOCK_SAPLING);
+ tolua_constant(tolua_S,"E_BLOCK_BEDROCK",E_BLOCK_BEDROCK);
+ tolua_constant(tolua_S,"E_BLOCK_WATER",E_BLOCK_WATER);
+ tolua_constant(tolua_S,"E_BLOCK_STATIONARY_WATER",E_BLOCK_STATIONARY_WATER);
+ tolua_constant(tolua_S,"E_BLOCK_LAVA",E_BLOCK_LAVA);
+ tolua_constant(tolua_S,"E_BLOCK_STATIONARY_LAVA",E_BLOCK_STATIONARY_LAVA);
+ tolua_constant(tolua_S,"E_BLOCK_SAND",E_BLOCK_SAND);
+ tolua_constant(tolua_S,"E_BLOCK_GRAVEL",E_BLOCK_GRAVEL);
+ tolua_constant(tolua_S,"E_BLOCK_GOLD_ORE",E_BLOCK_GOLD_ORE);
+ tolua_constant(tolua_S,"E_BLOCK_IRON_ORE",E_BLOCK_IRON_ORE);
+ tolua_constant(tolua_S,"E_BLOCK_COAL_ORE",E_BLOCK_COAL_ORE);
+ tolua_constant(tolua_S,"E_BLOCK_LOG",E_BLOCK_LOG);
+ tolua_constant(tolua_S,"E_BLOCK_LEAVES",E_BLOCK_LEAVES);
+ tolua_constant(tolua_S,"E_BLOCK_SPONGE",E_BLOCK_SPONGE);
+ tolua_constant(tolua_S,"E_BLOCK_GLASS",E_BLOCK_GLASS);
+ tolua_constant(tolua_S,"E_BLOCK_LAPIS_ORE",E_BLOCK_LAPIS_ORE);
+ tolua_constant(tolua_S,"E_BLOCK_LAPIS_BLOCK",E_BLOCK_LAPIS_BLOCK);
+ tolua_constant(tolua_S,"E_BLOCK_DISPENSER",E_BLOCK_DISPENSER);
+ tolua_constant(tolua_S,"E_BLOCK_SANDSTONE",E_BLOCK_SANDSTONE);
+ tolua_constant(tolua_S,"E_BLOCK_NOTE_BLOCK",E_BLOCK_NOTE_BLOCK);
+ tolua_constant(tolua_S,"E_BLOCK_BED",E_BLOCK_BED);
+ tolua_constant(tolua_S,"E_BLOCK_POWERED_RAIL",E_BLOCK_POWERED_RAIL);
+ tolua_constant(tolua_S,"E_BLOCK_DETECTOR_RAIL",E_BLOCK_DETECTOR_RAIL);
+ tolua_constant(tolua_S,"E_BLOCK_STICKY_PISTON",E_BLOCK_STICKY_PISTON);
+ tolua_constant(tolua_S,"E_BLOCK_COBWEB",E_BLOCK_COBWEB);
+ tolua_constant(tolua_S,"E_BLOCK_TALL_GRASS",E_BLOCK_TALL_GRASS);
+ tolua_constant(tolua_S,"E_BLOCK_DEAD_BUSH",E_BLOCK_DEAD_BUSH);
+ tolua_constant(tolua_S,"E_BLOCK_PISTON",E_BLOCK_PISTON);
+ tolua_constant(tolua_S,"E_BLOCK_PISTON_EXTENSION",E_BLOCK_PISTON_EXTENSION);
+ tolua_constant(tolua_S,"E_BLOCK_WOOL",E_BLOCK_WOOL);
+ tolua_constant(tolua_S,"E_BLOCK_PISTON_MOVED_BLOCK",E_BLOCK_PISTON_MOVED_BLOCK);
+ tolua_constant(tolua_S,"E_BLOCK_DANDELION",E_BLOCK_DANDELION);
+ tolua_constant(tolua_S,"E_BLOCK_FLOWER",E_BLOCK_FLOWER);
+ tolua_constant(tolua_S,"E_BLOCK_BROWN_MUSHROOM",E_BLOCK_BROWN_MUSHROOM);
+ tolua_constant(tolua_S,"E_BLOCK_RED_MUSHROOM",E_BLOCK_RED_MUSHROOM);
+ tolua_constant(tolua_S,"E_BLOCK_GOLD_BLOCK",E_BLOCK_GOLD_BLOCK);
+ tolua_constant(tolua_S,"E_BLOCK_IRON_BLOCK",E_BLOCK_IRON_BLOCK);
+ tolua_constant(tolua_S,"E_BLOCK_DOUBLE_STONE_SLAB",E_BLOCK_DOUBLE_STONE_SLAB);
+ tolua_constant(tolua_S,"E_BLOCK_STONE_SLAB",E_BLOCK_STONE_SLAB);
+ tolua_constant(tolua_S,"E_BLOCK_BRICK",E_BLOCK_BRICK);
+ tolua_constant(tolua_S,"E_BLOCK_TNT",E_BLOCK_TNT);
+ tolua_constant(tolua_S,"E_BLOCK_BOOKCASE",E_BLOCK_BOOKCASE);
+ tolua_constant(tolua_S,"E_BLOCK_MOSSY_COBBLESTONE",E_BLOCK_MOSSY_COBBLESTONE);
+ tolua_constant(tolua_S,"E_BLOCK_OBSIDIAN",E_BLOCK_OBSIDIAN);
+ tolua_constant(tolua_S,"E_BLOCK_TORCH",E_BLOCK_TORCH);
+ tolua_constant(tolua_S,"E_BLOCK_FIRE",E_BLOCK_FIRE);
+ tolua_constant(tolua_S,"E_BLOCK_MOB_SPAWNER",E_BLOCK_MOB_SPAWNER);
+ tolua_constant(tolua_S,"E_BLOCK_WOODEN_STAIRS",E_BLOCK_WOODEN_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_CHEST",E_BLOCK_CHEST);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_WIRE",E_BLOCK_REDSTONE_WIRE);
+ tolua_constant(tolua_S,"E_BLOCK_DIAMOND_ORE",E_BLOCK_DIAMOND_ORE);
+ tolua_constant(tolua_S,"E_BLOCK_DIAMOND_BLOCK",E_BLOCK_DIAMOND_BLOCK);
+ tolua_constant(tolua_S,"E_BLOCK_CRAFTING_TABLE",E_BLOCK_CRAFTING_TABLE);
+ tolua_constant(tolua_S,"E_BLOCK_WORKBENCH",E_BLOCK_WORKBENCH);
+ tolua_constant(tolua_S,"E_BLOCK_CROPS",E_BLOCK_CROPS);
+ tolua_constant(tolua_S,"E_BLOCK_FARMLAND",E_BLOCK_FARMLAND);
+ tolua_constant(tolua_S,"E_BLOCK_FURNACE",E_BLOCK_FURNACE);
+ tolua_constant(tolua_S,"E_BLOCK_LIT_FURNACE",E_BLOCK_LIT_FURNACE);
+ tolua_constant(tolua_S,"E_BLOCK_BURNING_FURNACE",E_BLOCK_BURNING_FURNACE);
+ tolua_constant(tolua_S,"E_BLOCK_SIGN_POST",E_BLOCK_SIGN_POST);
+ tolua_constant(tolua_S,"E_BLOCK_WOODEN_DOOR",E_BLOCK_WOODEN_DOOR);
+ tolua_constant(tolua_S,"E_BLOCK_LADDER",E_BLOCK_LADDER);
+ tolua_constant(tolua_S,"E_BLOCK_RAIL",E_BLOCK_RAIL);
+ tolua_constant(tolua_S,"E_BLOCK_MINECART_TRACKS",E_BLOCK_MINECART_TRACKS);
+ tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE_STAIRS",E_BLOCK_COBBLESTONE_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_WALLSIGN",E_BLOCK_WALLSIGN);
+ tolua_constant(tolua_S,"E_BLOCK_LEVER",E_BLOCK_LEVER);
+ tolua_constant(tolua_S,"E_BLOCK_STONE_PRESSURE_PLATE",E_BLOCK_STONE_PRESSURE_PLATE);
+ tolua_constant(tolua_S,"E_BLOCK_IRON_DOOR",E_BLOCK_IRON_DOOR);
+ tolua_constant(tolua_S,"E_BLOCK_WOODEN_PRESSURE_PLATE",E_BLOCK_WOODEN_PRESSURE_PLATE);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_ORE",E_BLOCK_REDSTONE_ORE);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_ORE_GLOWING",E_BLOCK_REDSTONE_ORE_GLOWING);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_TORCH_OFF",E_BLOCK_REDSTONE_TORCH_OFF);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_TORCH_ON",E_BLOCK_REDSTONE_TORCH_ON);
+ tolua_constant(tolua_S,"E_BLOCK_STONE_BUTTON",E_BLOCK_STONE_BUTTON);
+ tolua_constant(tolua_S,"E_BLOCK_SNOW",E_BLOCK_SNOW);
+ tolua_constant(tolua_S,"E_BLOCK_ICE",E_BLOCK_ICE);
+ tolua_constant(tolua_S,"E_BLOCK_SNOW_BLOCK",E_BLOCK_SNOW_BLOCK);
+ tolua_constant(tolua_S,"E_BLOCK_CACTUS",E_BLOCK_CACTUS);
+ tolua_constant(tolua_S,"E_BLOCK_CLAY",E_BLOCK_CLAY);
+ tolua_constant(tolua_S,"E_BLOCK_SUGARCANE",E_BLOCK_SUGARCANE);
+ tolua_constant(tolua_S,"E_BLOCK_REEDS",E_BLOCK_REEDS);
+ tolua_constant(tolua_S,"E_BLOCK_JUKEBOX",E_BLOCK_JUKEBOX);
+ tolua_constant(tolua_S,"E_BLOCK_FENCE",E_BLOCK_FENCE);
+ tolua_constant(tolua_S,"E_BLOCK_PUMPKIN",E_BLOCK_PUMPKIN);
+ tolua_constant(tolua_S,"E_BLOCK_NETHERRACK",E_BLOCK_NETHERRACK);
+ tolua_constant(tolua_S,"E_BLOCK_SOULSAND",E_BLOCK_SOULSAND);
+ tolua_constant(tolua_S,"E_BLOCK_GLOWSTONE",E_BLOCK_GLOWSTONE);
+ tolua_constant(tolua_S,"E_BLOCK_NETHER_PORTAL",E_BLOCK_NETHER_PORTAL);
+ tolua_constant(tolua_S,"E_BLOCK_JACK_O_LANTERN",E_BLOCK_JACK_O_LANTERN);
+ tolua_constant(tolua_S,"E_BLOCK_CAKE",E_BLOCK_CAKE);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_REPEATER_OFF",E_BLOCK_REDSTONE_REPEATER_OFF);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_REPEATER_ON",E_BLOCK_REDSTONE_REPEATER_ON);
+ tolua_constant(tolua_S,"E_BLOCK_STAINED_GLASS",E_BLOCK_STAINED_GLASS);
+ tolua_constant(tolua_S,"E_BLOCK_TRAPDOOR",E_BLOCK_TRAPDOOR);
+ tolua_constant(tolua_S,"E_BLOCK_SILVERFISH_EGG",E_BLOCK_SILVERFISH_EGG);
+ tolua_constant(tolua_S,"E_BLOCK_STONE_BRICKS",E_BLOCK_STONE_BRICKS);
+ tolua_constant(tolua_S,"E_BLOCK_HUGE_BROWN_MUSHROOM",E_BLOCK_HUGE_BROWN_MUSHROOM);
+ tolua_constant(tolua_S,"E_BLOCK_HUGE_RED_MUSHROOM",E_BLOCK_HUGE_RED_MUSHROOM);
+ tolua_constant(tolua_S,"E_BLOCK_IRON_BARS",E_BLOCK_IRON_BARS);
+ tolua_constant(tolua_S,"E_BLOCK_GLASS_PANE",E_BLOCK_GLASS_PANE);
+ tolua_constant(tolua_S,"E_BLOCK_MELON",E_BLOCK_MELON);
+ tolua_constant(tolua_S,"E_BLOCK_PUMPKIN_STEM",E_BLOCK_PUMPKIN_STEM);
+ tolua_constant(tolua_S,"E_BLOCK_MELON_STEM",E_BLOCK_MELON_STEM);
+ tolua_constant(tolua_S,"E_BLOCK_VINES",E_BLOCK_VINES);
+ tolua_constant(tolua_S,"E_BLOCK_FENCE_GATE",E_BLOCK_FENCE_GATE);
+ tolua_constant(tolua_S,"E_BLOCK_BRICK_STAIRS",E_BLOCK_BRICK_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_STONE_BRICK_STAIRS",E_BLOCK_STONE_BRICK_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_MYCELIUM",E_BLOCK_MYCELIUM);
+ tolua_constant(tolua_S,"E_BLOCK_LILY_PAD",E_BLOCK_LILY_PAD);
+ tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK",E_BLOCK_NETHER_BRICK);
+ tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK_FENCE",E_BLOCK_NETHER_BRICK_FENCE);
+ tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK_STAIRS",E_BLOCK_NETHER_BRICK_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_NETHER_WART",E_BLOCK_NETHER_WART);
+ tolua_constant(tolua_S,"E_BLOCK_ENCHANTMENT_TABLE",E_BLOCK_ENCHANTMENT_TABLE);
+ tolua_constant(tolua_S,"E_BLOCK_BREWING_STAND",E_BLOCK_BREWING_STAND);
+ tolua_constant(tolua_S,"E_BLOCK_CAULDRON",E_BLOCK_CAULDRON);
+ tolua_constant(tolua_S,"E_BLOCK_END_PORTAL",E_BLOCK_END_PORTAL);
+ tolua_constant(tolua_S,"E_BLOCK_END_PORTAL_FRAME",E_BLOCK_END_PORTAL_FRAME);
+ tolua_constant(tolua_S,"E_BLOCK_END_STONE",E_BLOCK_END_STONE);
+ tolua_constant(tolua_S,"E_BLOCK_DRAGON_EGG",E_BLOCK_DRAGON_EGG);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_LAMP_OFF",E_BLOCK_REDSTONE_LAMP_OFF);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_LAMP_ON",E_BLOCK_REDSTONE_LAMP_ON);
+ tolua_constant(tolua_S,"E_BLOCK_DOUBLE_WOODEN_SLAB",E_BLOCK_DOUBLE_WOODEN_SLAB);
+ tolua_constant(tolua_S,"E_BLOCK_WOODEN_SLAB",E_BLOCK_WOODEN_SLAB);
+ tolua_constant(tolua_S,"E_BLOCK_COCOA_POD",E_BLOCK_COCOA_POD);
+ tolua_constant(tolua_S,"E_BLOCK_SANDSTONE_STAIRS",E_BLOCK_SANDSTONE_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_EMERALD_ORE",E_BLOCK_EMERALD_ORE);
+ tolua_constant(tolua_S,"E_BLOCK_ENDER_CHEST",E_BLOCK_ENDER_CHEST);
+ tolua_constant(tolua_S,"E_BLOCK_TRIPWIRE_HOOK",E_BLOCK_TRIPWIRE_HOOK);
+ tolua_constant(tolua_S,"E_BLOCK_TRIPWIRE",E_BLOCK_TRIPWIRE);
+ tolua_constant(tolua_S,"E_BLOCK_EMERALD_BLOCK",E_BLOCK_EMERALD_BLOCK);
+ tolua_constant(tolua_S,"E_BLOCK_SPRUCE_WOOD_STAIRS",E_BLOCK_SPRUCE_WOOD_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_BIRCH_WOOD_STAIRS",E_BLOCK_BIRCH_WOOD_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_JUNGLE_WOOD_STAIRS",E_BLOCK_JUNGLE_WOOD_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_COMMAND_BLOCK",E_BLOCK_COMMAND_BLOCK);
+ tolua_constant(tolua_S,"E_BLOCK_BEACON",E_BLOCK_BEACON);
+ tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE_WALL",E_BLOCK_COBBLESTONE_WALL);
+ tolua_constant(tolua_S,"E_BLOCK_FLOWER_POT",E_BLOCK_FLOWER_POT);
+ tolua_constant(tolua_S,"E_BLOCK_CARROTS",E_BLOCK_CARROTS);
+ tolua_constant(tolua_S,"E_BLOCK_POTATOES",E_BLOCK_POTATOES);
+ tolua_constant(tolua_S,"E_BLOCK_WOODEN_BUTTON",E_BLOCK_WOODEN_BUTTON);
+ tolua_constant(tolua_S,"E_BLOCK_HEAD",E_BLOCK_HEAD);
+ tolua_constant(tolua_S,"E_BLOCK_ANVIL",E_BLOCK_ANVIL);
+ tolua_constant(tolua_S,"E_BLOCK_TRAPPED_CHEST",E_BLOCK_TRAPPED_CHEST);
+ tolua_constant(tolua_S,"E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE",E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE);
+ tolua_constant(tolua_S,"E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE",E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE);
+ tolua_constant(tolua_S,"E_BLOCK_INACTIVE_COMPARATOR",E_BLOCK_INACTIVE_COMPARATOR);
+ tolua_constant(tolua_S,"E_BLOCK_ACTIVE_COMPARATOR",E_BLOCK_ACTIVE_COMPARATOR);
+ tolua_constant(tolua_S,"E_BLOCK_DAYLIGHT_SENSOR",E_BLOCK_DAYLIGHT_SENSOR);
+ tolua_constant(tolua_S,"E_BLOCK_BLOCK_OF_REDSTONE",E_BLOCK_BLOCK_OF_REDSTONE);
+ tolua_constant(tolua_S,"E_BLOCK_NETHER_QUARTZ_ORE",E_BLOCK_NETHER_QUARTZ_ORE);
+ tolua_constant(tolua_S,"E_BLOCK_HOPPER",E_BLOCK_HOPPER);
+ tolua_constant(tolua_S,"E_BLOCK_QUARTZ_BLOCK",E_BLOCK_QUARTZ_BLOCK);
+ tolua_constant(tolua_S,"E_BLOCK_QUARTZ_STAIRS",E_BLOCK_QUARTZ_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_ACTIVATOR_RAIL",E_BLOCK_ACTIVATOR_RAIL);
+ tolua_constant(tolua_S,"E_BLOCK_DROPPER",E_BLOCK_DROPPER);
+ tolua_constant(tolua_S,"E_BLOCK_STAINED_CLAY",E_BLOCK_STAINED_CLAY);
+ tolua_constant(tolua_S,"E_BLOCK_STAINED_GLASS_PANE",E_BLOCK_STAINED_GLASS_PANE);
+ tolua_constant(tolua_S,"E_BLOCK_NEW_LEAVES",E_BLOCK_NEW_LEAVES);
+ tolua_constant(tolua_S,"E_BLOCK_NEW_LOG",E_BLOCK_NEW_LOG);
+ tolua_constant(tolua_S,"E_BLOCK_ACACIA_WOOD_STAIRS",E_BLOCK_ACACIA_WOOD_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_DARK_OAK_WOOD_STAIRS",E_BLOCK_DARK_OAK_WOOD_STAIRS);
+ tolua_constant(tolua_S,"E_BLOCK_HAY_BALE",E_BLOCK_HAY_BALE);
+ tolua_constant(tolua_S,"E_BLOCK_CARPET",E_BLOCK_CARPET);
+ tolua_constant(tolua_S,"E_BLOCK_HARDENED_CLAY",E_BLOCK_HARDENED_CLAY);
+ tolua_constant(tolua_S,"E_BLOCK_BLOCK_OF_COAL",E_BLOCK_BLOCK_OF_COAL);
+ tolua_constant(tolua_S,"E_BLOCK_PACKED_ICE",E_BLOCK_PACKED_ICE);
+ tolua_constant(tolua_S,"E_BLOCK_BIG_FLOWER",E_BLOCK_BIG_FLOWER);
+ tolua_constant(tolua_S,"E_BLOCK_NUMBER_OF_TYPES",E_BLOCK_NUMBER_OF_TYPES);
+ tolua_constant(tolua_S,"E_BLOCK_MAX_TYPE_ID",E_BLOCK_MAX_TYPE_ID);
+ tolua_constant(tolua_S,"E_BLOCK_YELLOW_FLOWER",E_BLOCK_YELLOW_FLOWER);
+ tolua_constant(tolua_S,"E_BLOCK_RED_ROSE",E_BLOCK_RED_ROSE);
+ tolua_constant(tolua_S,"E_BLOCK_LOCKED_CHEST",E_BLOCK_LOCKED_CHEST);
+ tolua_constant(tolua_S,"E_ITEM_EMPTY",E_ITEM_EMPTY);
+ tolua_constant(tolua_S,"E_ITEM_FIRST",E_ITEM_FIRST);
+ tolua_constant(tolua_S,"E_ITEM_IRON_SHOVEL",E_ITEM_IRON_SHOVEL);
+ tolua_constant(tolua_S,"E_ITEM_IRON_PICKAXE",E_ITEM_IRON_PICKAXE);
+ tolua_constant(tolua_S,"E_ITEM_IRON_AXE",E_ITEM_IRON_AXE);
+ tolua_constant(tolua_S,"E_ITEM_FLINT_AND_STEEL",E_ITEM_FLINT_AND_STEEL);
+ tolua_constant(tolua_S,"E_ITEM_RED_APPLE",E_ITEM_RED_APPLE);
+ tolua_constant(tolua_S,"E_ITEM_BOW",E_ITEM_BOW);
+ tolua_constant(tolua_S,"E_ITEM_ARROW",E_ITEM_ARROW);
+ tolua_constant(tolua_S,"E_ITEM_COAL",E_ITEM_COAL);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND",E_ITEM_DIAMOND);
+ tolua_constant(tolua_S,"E_ITEM_IRON",E_ITEM_IRON);
+ tolua_constant(tolua_S,"E_ITEM_GOLD",E_ITEM_GOLD);
+ tolua_constant(tolua_S,"E_ITEM_IRON_SWORD",E_ITEM_IRON_SWORD);
+ tolua_constant(tolua_S,"E_ITEM_WOODEN_SWORD",E_ITEM_WOODEN_SWORD);
+ tolua_constant(tolua_S,"E_ITEM_WOODEN_SHOVEL",E_ITEM_WOODEN_SHOVEL);
+ tolua_constant(tolua_S,"E_ITEM_WOODEN_PICKAXE",E_ITEM_WOODEN_PICKAXE);
+ tolua_constant(tolua_S,"E_ITEM_WOODEN_AXE",E_ITEM_WOODEN_AXE);
+ tolua_constant(tolua_S,"E_ITEM_STONE_SWORD",E_ITEM_STONE_SWORD);
+ tolua_constant(tolua_S,"E_ITEM_STONE_SHOVEL",E_ITEM_STONE_SHOVEL);
+ tolua_constant(tolua_S,"E_ITEM_STONE_PICKAXE",E_ITEM_STONE_PICKAXE);
+ tolua_constant(tolua_S,"E_ITEM_STONE_AXE",E_ITEM_STONE_AXE);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND_SWORD",E_ITEM_DIAMOND_SWORD);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND_SHOVEL",E_ITEM_DIAMOND_SHOVEL);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND_PICKAXE",E_ITEM_DIAMOND_PICKAXE);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND_AXE",E_ITEM_DIAMOND_AXE);
+ tolua_constant(tolua_S,"E_ITEM_STICK",E_ITEM_STICK);
+ tolua_constant(tolua_S,"E_ITEM_BOWL",E_ITEM_BOWL);
+ tolua_constant(tolua_S,"E_ITEM_MUSHROOM_SOUP",E_ITEM_MUSHROOM_SOUP);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_SWORD",E_ITEM_GOLD_SWORD);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_SHOVEL",E_ITEM_GOLD_SHOVEL);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_PICKAXE",E_ITEM_GOLD_PICKAXE);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_AXE",E_ITEM_GOLD_AXE);
+ tolua_constant(tolua_S,"E_ITEM_STRING",E_ITEM_STRING);
+ tolua_constant(tolua_S,"E_ITEM_FEATHER",E_ITEM_FEATHER);
+ tolua_constant(tolua_S,"E_ITEM_GUNPOWDER",E_ITEM_GUNPOWDER);
+ tolua_constant(tolua_S,"E_ITEM_WOODEN_HOE",E_ITEM_WOODEN_HOE);
+ tolua_constant(tolua_S,"E_ITEM_STONE_HOE",E_ITEM_STONE_HOE);
+ tolua_constant(tolua_S,"E_ITEM_IRON_HOE",E_ITEM_IRON_HOE);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND_HOE",E_ITEM_DIAMOND_HOE);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_HOE",E_ITEM_GOLD_HOE);
+ tolua_constant(tolua_S,"E_ITEM_SEEDS",E_ITEM_SEEDS);
+ tolua_constant(tolua_S,"E_ITEM_WHEAT",E_ITEM_WHEAT);
+ tolua_constant(tolua_S,"E_ITEM_BREAD",E_ITEM_BREAD);
+ tolua_constant(tolua_S,"E_ITEM_LEATHER_CAP",E_ITEM_LEATHER_CAP);
+ tolua_constant(tolua_S,"E_ITEM_LEATHER_TUNIC",E_ITEM_LEATHER_TUNIC);
+ tolua_constant(tolua_S,"E_ITEM_LEATHER_PANTS",E_ITEM_LEATHER_PANTS);
+ tolua_constant(tolua_S,"E_ITEM_LEATHER_BOOTS",E_ITEM_LEATHER_BOOTS);
+ tolua_constant(tolua_S,"E_ITEM_CHAIN_HELMET",E_ITEM_CHAIN_HELMET);
+ tolua_constant(tolua_S,"E_ITEM_CHAIN_CHESTPLATE",E_ITEM_CHAIN_CHESTPLATE);
+ tolua_constant(tolua_S,"E_ITEM_CHAIN_LEGGINGS",E_ITEM_CHAIN_LEGGINGS);
+ tolua_constant(tolua_S,"E_ITEM_CHAIN_BOOTS",E_ITEM_CHAIN_BOOTS);
+ tolua_constant(tolua_S,"E_ITEM_IRON_HELMET",E_ITEM_IRON_HELMET);
+ tolua_constant(tolua_S,"E_ITEM_IRON_CHESTPLATE",E_ITEM_IRON_CHESTPLATE);
+ tolua_constant(tolua_S,"E_ITEM_IRON_LEGGINGS",E_ITEM_IRON_LEGGINGS);
+ tolua_constant(tolua_S,"E_ITEM_IRON_BOOTS",E_ITEM_IRON_BOOTS);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND_HELMET",E_ITEM_DIAMOND_HELMET);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND_CHESTPLATE",E_ITEM_DIAMOND_CHESTPLATE);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND_LEGGINGS",E_ITEM_DIAMOND_LEGGINGS);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND_BOOTS",E_ITEM_DIAMOND_BOOTS);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_HELMET",E_ITEM_GOLD_HELMET);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_CHESTPLATE",E_ITEM_GOLD_CHESTPLATE);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_LEGGINGS",E_ITEM_GOLD_LEGGINGS);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_BOOTS",E_ITEM_GOLD_BOOTS);
+ tolua_constant(tolua_S,"E_ITEM_FLINT",E_ITEM_FLINT);
+ tolua_constant(tolua_S,"E_ITEM_RAW_PORKCHOP",E_ITEM_RAW_PORKCHOP);
+ tolua_constant(tolua_S,"E_ITEM_COOKED_PORKCHOP",E_ITEM_COOKED_PORKCHOP);
+ tolua_constant(tolua_S,"E_ITEM_PAINTINGS",E_ITEM_PAINTINGS);
+ tolua_constant(tolua_S,"E_ITEM_GOLDEN_APPLE",E_ITEM_GOLDEN_APPLE);
+ tolua_constant(tolua_S,"E_ITEM_SIGN",E_ITEM_SIGN);
+ tolua_constant(tolua_S,"E_ITEM_WOODEN_DOOR",E_ITEM_WOODEN_DOOR);
+ tolua_constant(tolua_S,"E_ITEM_BUCKET",E_ITEM_BUCKET);
+ tolua_constant(tolua_S,"E_ITEM_WATER_BUCKET",E_ITEM_WATER_BUCKET);
+ tolua_constant(tolua_S,"E_ITEM_LAVA_BUCKET",E_ITEM_LAVA_BUCKET);
+ tolua_constant(tolua_S,"E_ITEM_MINECART",E_ITEM_MINECART);
+ tolua_constant(tolua_S,"E_ITEM_SADDLE",E_ITEM_SADDLE);
+ tolua_constant(tolua_S,"E_ITEM_IRON_DOOR",E_ITEM_IRON_DOOR);
+ tolua_constant(tolua_S,"E_ITEM_REDSTONE_DUST",E_ITEM_REDSTONE_DUST);
+ tolua_constant(tolua_S,"E_ITEM_SNOWBALL",E_ITEM_SNOWBALL);
+ tolua_constant(tolua_S,"E_ITEM_BOAT",E_ITEM_BOAT);
+ tolua_constant(tolua_S,"E_ITEM_LEATHER",E_ITEM_LEATHER);
+ tolua_constant(tolua_S,"E_ITEM_MILK",E_ITEM_MILK);
+ tolua_constant(tolua_S,"E_ITEM_CLAY_BRICK",E_ITEM_CLAY_BRICK);
+ tolua_constant(tolua_S,"E_ITEM_CLAY",E_ITEM_CLAY);
+ tolua_constant(tolua_S,"E_ITEM_SUGARCANE",E_ITEM_SUGARCANE);
+ tolua_constant(tolua_S,"E_ITEM_SUGAR_CANE",E_ITEM_SUGAR_CANE);
+ tolua_constant(tolua_S,"E_ITEM_PAPER",E_ITEM_PAPER);
+ tolua_constant(tolua_S,"E_ITEM_BOOK",E_ITEM_BOOK);
+ tolua_constant(tolua_S,"E_ITEM_SLIMEBALL",E_ITEM_SLIMEBALL);
+ tolua_constant(tolua_S,"E_ITEM_CHEST_MINECART",E_ITEM_CHEST_MINECART);
+ tolua_constant(tolua_S,"E_ITEM_FURNACE_MINECART",E_ITEM_FURNACE_MINECART);
+ tolua_constant(tolua_S,"E_ITEM_EGG",E_ITEM_EGG);
+ tolua_constant(tolua_S,"E_ITEM_COMPASS",E_ITEM_COMPASS);
+ tolua_constant(tolua_S,"E_ITEM_FISHING_ROD",E_ITEM_FISHING_ROD);
+ tolua_constant(tolua_S,"E_ITEM_CLOCK",E_ITEM_CLOCK);
+ tolua_constant(tolua_S,"E_ITEM_GLOWSTONE_DUST",E_ITEM_GLOWSTONE_DUST);
+ tolua_constant(tolua_S,"E_ITEM_RAW_FISH",E_ITEM_RAW_FISH);
+ tolua_constant(tolua_S,"E_ITEM_COOKED_FISH",E_ITEM_COOKED_FISH);
+ tolua_constant(tolua_S,"E_ITEM_DYE",E_ITEM_DYE);
+ tolua_constant(tolua_S,"E_ITEM_BONE",E_ITEM_BONE);
+ tolua_constant(tolua_S,"E_ITEM_SUGAR",E_ITEM_SUGAR);
+ tolua_constant(tolua_S,"E_ITEM_CAKE",E_ITEM_CAKE);
+ tolua_constant(tolua_S,"E_ITEM_BED",E_ITEM_BED);
+ tolua_constant(tolua_S,"E_ITEM_REDSTONE_REPEATER",E_ITEM_REDSTONE_REPEATER);
+ tolua_constant(tolua_S,"E_ITEM_COOKIE",E_ITEM_COOKIE);
+ tolua_constant(tolua_S,"E_ITEM_MAP",E_ITEM_MAP);
+ tolua_constant(tolua_S,"E_ITEM_SHEARS",E_ITEM_SHEARS);
+ tolua_constant(tolua_S,"E_ITEM_MELON_SLICE",E_ITEM_MELON_SLICE);
+ tolua_constant(tolua_S,"E_ITEM_PUMPKIN_SEEDS",E_ITEM_PUMPKIN_SEEDS);
+ tolua_constant(tolua_S,"E_ITEM_MELON_SEEDS",E_ITEM_MELON_SEEDS);
+ tolua_constant(tolua_S,"E_ITEM_RAW_BEEF",E_ITEM_RAW_BEEF);
+ tolua_constant(tolua_S,"E_ITEM_STEAK",E_ITEM_STEAK);
+ tolua_constant(tolua_S,"E_ITEM_RAW_CHICKEN",E_ITEM_RAW_CHICKEN);
+ tolua_constant(tolua_S,"E_ITEM_COOKED_CHICKEN",E_ITEM_COOKED_CHICKEN);
+ tolua_constant(tolua_S,"E_ITEM_ROTTEN_FLESH",E_ITEM_ROTTEN_FLESH);
+ tolua_constant(tolua_S,"E_ITEM_ENDER_PEARL",E_ITEM_ENDER_PEARL);
+ tolua_constant(tolua_S,"E_ITEM_BLAZE_ROD",E_ITEM_BLAZE_ROD);
+ tolua_constant(tolua_S,"E_ITEM_GHAST_TEAR",E_ITEM_GHAST_TEAR);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_NUGGET",E_ITEM_GOLD_NUGGET);
+ tolua_constant(tolua_S,"E_ITEM_NETHER_WART",E_ITEM_NETHER_WART);
+ tolua_constant(tolua_S,"E_ITEM_POTIONS",E_ITEM_POTIONS);
+ tolua_constant(tolua_S,"E_ITEM_GLASS_BOTTLE",E_ITEM_GLASS_BOTTLE);
+ tolua_constant(tolua_S,"E_ITEM_SPIDER_EYE",E_ITEM_SPIDER_EYE);
+ tolua_constant(tolua_S,"E_ITEM_FERMENTED_SPIDER_EYE",E_ITEM_FERMENTED_SPIDER_EYE);
+ tolua_constant(tolua_S,"E_ITEM_BLAZE_POWDER",E_ITEM_BLAZE_POWDER);
+ tolua_constant(tolua_S,"E_ITEM_MAGMA_CREAM",E_ITEM_MAGMA_CREAM);
+ tolua_constant(tolua_S,"E_ITEM_BREWING_STAND",E_ITEM_BREWING_STAND);
+ tolua_constant(tolua_S,"E_ITEM_CAULDRON",E_ITEM_CAULDRON);
+ tolua_constant(tolua_S,"E_ITEM_EYE_OF_ENDER",E_ITEM_EYE_OF_ENDER);
+ tolua_constant(tolua_S,"E_ITEM_GLISTERING_MELON",E_ITEM_GLISTERING_MELON);
+ tolua_constant(tolua_S,"E_ITEM_SPAWN_EGG",E_ITEM_SPAWN_EGG);
+ tolua_constant(tolua_S,"E_ITEM_BOTTLE_O_ENCHANTING",E_ITEM_BOTTLE_O_ENCHANTING);
+ tolua_constant(tolua_S,"E_ITEM_FIRE_CHARGE",E_ITEM_FIRE_CHARGE);
+ tolua_constant(tolua_S,"E_ITEM_BOOK_AND_QUILL",E_ITEM_BOOK_AND_QUILL);
+ tolua_constant(tolua_S,"E_ITEM_WRITTEN_BOOK",E_ITEM_WRITTEN_BOOK);
+ tolua_constant(tolua_S,"E_ITEM_EMERALD",E_ITEM_EMERALD);
+ tolua_constant(tolua_S,"E_ITEM_ITEM_FRAME",E_ITEM_ITEM_FRAME);
+ tolua_constant(tolua_S,"E_ITEM_FLOWER_POT",E_ITEM_FLOWER_POT);
+ tolua_constant(tolua_S,"E_ITEM_CARROT",E_ITEM_CARROT);
+ tolua_constant(tolua_S,"E_ITEM_POTATO",E_ITEM_POTATO);
+ tolua_constant(tolua_S,"E_ITEM_BAKED_POTATO",E_ITEM_BAKED_POTATO);
+ tolua_constant(tolua_S,"E_ITEM_POISONOUS_POTATO",E_ITEM_POISONOUS_POTATO);
+ tolua_constant(tolua_S,"E_ITEM_EMPTY_MAP",E_ITEM_EMPTY_MAP);
+ tolua_constant(tolua_S,"E_ITEM_GOLDEN_CARROT",E_ITEM_GOLDEN_CARROT);
+ tolua_constant(tolua_S,"E_ITEM_HEAD",E_ITEM_HEAD);
+ tolua_constant(tolua_S,"E_ITEM_CARROT_ON_STICK",E_ITEM_CARROT_ON_STICK);
+ tolua_constant(tolua_S,"E_ITEM_NETHER_STAR",E_ITEM_NETHER_STAR);
+ tolua_constant(tolua_S,"E_ITEM_PUMPKIN_PIE",E_ITEM_PUMPKIN_PIE);
+ tolua_constant(tolua_S,"E_ITEM_FIREWORK_ROCKET",E_ITEM_FIREWORK_ROCKET);
+ tolua_constant(tolua_S,"E_ITEM_FIREWORK_STAR",E_ITEM_FIREWORK_STAR);
+ tolua_constant(tolua_S,"E_ITEM_ENCHANTED_BOOK",E_ITEM_ENCHANTED_BOOK);
+ tolua_constant(tolua_S,"E_ITEM_COMPARATOR",E_ITEM_COMPARATOR);
+ tolua_constant(tolua_S,"E_ITEM_NETHER_BRICK",E_ITEM_NETHER_BRICK);
+ tolua_constant(tolua_S,"E_ITEM_NETHER_QUARTZ",E_ITEM_NETHER_QUARTZ);
+ tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_TNT",E_ITEM_MINECART_WITH_TNT);
+ tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_HOPPER",E_ITEM_MINECART_WITH_HOPPER);
+ tolua_constant(tolua_S,"E_ITEM_IRON_HORSE_ARMOR",E_ITEM_IRON_HORSE_ARMOR);
+ tolua_constant(tolua_S,"E_ITEM_GOLD_HORSE_ARMOR",E_ITEM_GOLD_HORSE_ARMOR);
+ tolua_constant(tolua_S,"E_ITEM_DIAMOND_HORSE_ARMOR",E_ITEM_DIAMOND_HORSE_ARMOR);
+ tolua_constant(tolua_S,"E_ITEM_LEAD",E_ITEM_LEAD);
+ tolua_constant(tolua_S,"E_ITEM_NAME_TAG",E_ITEM_NAME_TAG);
+ tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_COMMAND_BLOCK",E_ITEM_MINECART_WITH_COMMAND_BLOCK);
+ tolua_constant(tolua_S,"E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES",E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES);
+ tolua_constant(tolua_S,"E_ITEM_MAX_CONSECUTIVE_TYPE_ID",E_ITEM_MAX_CONSECUTIVE_TYPE_ID);
+ tolua_constant(tolua_S,"E_ITEM_FIRST_DISC",E_ITEM_FIRST_DISC);
+ tolua_constant(tolua_S,"E_ITEM_13_DISC",E_ITEM_13_DISC);
+ tolua_constant(tolua_S,"E_ITEM_CAT_DISC",E_ITEM_CAT_DISC);
+ tolua_constant(tolua_S,"E_ITEM_BLOCKS_DISC",E_ITEM_BLOCKS_DISC);
+ tolua_constant(tolua_S,"E_ITEM_CHIRP_DISC",E_ITEM_CHIRP_DISC);
+ tolua_constant(tolua_S,"E_ITEM_FAR_DISC",E_ITEM_FAR_DISC);
+ tolua_constant(tolua_S,"E_ITEM_MALL_DISC",E_ITEM_MALL_DISC);
+ tolua_constant(tolua_S,"E_ITEM_MELLOHI_DISC",E_ITEM_MELLOHI_DISC);
+ tolua_constant(tolua_S,"E_ITEM_STAL_DISC",E_ITEM_STAL_DISC);
+ tolua_constant(tolua_S,"E_ITEM_STRAD_DISC",E_ITEM_STRAD_DISC);
+ tolua_constant(tolua_S,"E_ITEM_WARD_DISC",E_ITEM_WARD_DISC);
+ tolua_constant(tolua_S,"E_ITEM_11_DISC",E_ITEM_11_DISC);
+ tolua_constant(tolua_S,"E_ITEM_WAIT_DISC",E_ITEM_WAIT_DISC);
+ tolua_constant(tolua_S,"E_ITEM_LAST_DISC_PLUS_ONE",E_ITEM_LAST_DISC_PLUS_ONE);
+ tolua_constant(tolua_S,"E_ITEM_LAST_DISC",E_ITEM_LAST_DISC);
+ tolua_constant(tolua_S,"E_ITEM_LAST",E_ITEM_LAST);
+ tolua_constant(tolua_S,"E_META_CHEST_FACING_ZM",E_META_CHEST_FACING_ZM);
+ tolua_constant(tolua_S,"E_META_CHEST_FACING_ZP",E_META_CHEST_FACING_ZP);
+ tolua_constant(tolua_S,"E_META_CHEST_FACING_XM",E_META_CHEST_FACING_XM);
+ tolua_constant(tolua_S,"E_META_CHEST_FACING_XP",E_META_CHEST_FACING_XP);
+ tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_YM",E_META_DROPSPENSER_FACING_YM);
+ tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_YP",E_META_DROPSPENSER_FACING_YP);
+ tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_ZM",E_META_DROPSPENSER_FACING_ZM);
+ tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_ZP",E_META_DROPSPENSER_FACING_ZP);
+ tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_XM",E_META_DROPSPENSER_FACING_XM);
+ tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_XP",E_META_DROPSPENSER_FACING_XP);
+ tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE",E_META_DOUBLE_STONE_SLAB_STONE);
+ tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_SANDSTONE",E_META_DOUBLE_STONE_SLAB_SANDSTONE);
+ tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_WOODEN",E_META_DOUBLE_STONE_SLAB_WOODEN);
+ tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_COBBLESTONE",E_META_DOUBLE_STONE_SLAB_COBBLESTONE);
+ tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_BRICK",E_META_DOUBLE_STONE_SLAB_BRICK);
+ tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE_BRICK",E_META_DOUBLE_STONE_SLAB_STONE_BRICK);
+ tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_NETHER_BRICK",E_META_DOUBLE_STONE_SLAB_NETHER_BRICK);
+ tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE_SECRET",E_META_DOUBLE_STONE_SLAB_STONE_SECRET);
+ tolua_constant(tolua_S,"E_META_HOPPER_FACING_YM",E_META_HOPPER_FACING_YM);
+ tolua_constant(tolua_S,"E_META_HOPPER_UNATTACHED",E_META_HOPPER_UNATTACHED);
+ tolua_constant(tolua_S,"E_META_HOPPER_FACING_ZM",E_META_HOPPER_FACING_ZM);
+ tolua_constant(tolua_S,"E_META_HOPPER_FACING_ZP",E_META_HOPPER_FACING_ZP);
+ tolua_constant(tolua_S,"E_META_HOPPER_FACING_XM",E_META_HOPPER_FACING_XM);
+ tolua_constant(tolua_S,"E_META_HOPPER_FACING_XP",E_META_HOPPER_FACING_XP);
+ tolua_constant(tolua_S,"E_META_LEAVES_APPLE",E_META_LEAVES_APPLE);
+ tolua_constant(tolua_S,"E_META_LEAVES_CONIFER",E_META_LEAVES_CONIFER);
+ tolua_constant(tolua_S,"E_META_LEAVES_BIRCH",E_META_LEAVES_BIRCH);
+ tolua_constant(tolua_S,"E_META_LEAVES_JUNGLE",E_META_LEAVES_JUNGLE);
+ tolua_constant(tolua_S,"E_META_LOG_APPLE",E_META_LOG_APPLE);
+ tolua_constant(tolua_S,"E_META_LOG_CONIFER",E_META_LOG_CONIFER);
+ tolua_constant(tolua_S,"E_META_LOG_BIRCH",E_META_LOG_BIRCH);
+ tolua_constant(tolua_S,"E_META_LOG_JUNGLE",E_META_LOG_JUNGLE);
+ tolua_constant(tolua_S,"E_META_PLANKS_APPLE",E_META_PLANKS_APPLE);
+ tolua_constant(tolua_S,"E_META_PLANKS_CONIFER",E_META_PLANKS_CONIFER);
+ tolua_constant(tolua_S,"E_META_PLANKS_BIRCH",E_META_PLANKS_BIRCH);
+ tolua_constant(tolua_S,"E_META_PLANKS_JUNGLE",E_META_PLANKS_JUNGLE);
+ tolua_constant(tolua_S,"E_META_SANDSTONE_NORMAL",E_META_SANDSTONE_NORMAL);
+ tolua_constant(tolua_S,"E_META_SANDSTONE_ORNAMENT",E_META_SANDSTONE_ORNAMENT);
+ tolua_constant(tolua_S,"E_META_SANDSTONE_SMOOTH",E_META_SANDSTONE_SMOOTH);
+ tolua_constant(tolua_S,"E_META_SAPLING_APPLE",E_META_SAPLING_APPLE);
+ tolua_constant(tolua_S,"E_META_SAPLING_CONIFER",E_META_SAPLING_CONIFER);
+ tolua_constant(tolua_S,"E_META_SAPLING_BIRCH",E_META_SAPLING_BIRCH);
+ tolua_constant(tolua_S,"E_META_SAPLING_JUNGLE",E_META_SAPLING_JUNGLE);
+ tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_STONE",E_META_SILVERFISH_EGG_STONE);
+ tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_COBBLESTONE",E_META_SILVERFISH_EGG_COBBLESTONE);
+ tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_STONE_BRICK",E_META_SILVERFISH_EGG_STONE_BRICK);
+ tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE",E_META_STONE_SLAB_STONE);
+ tolua_constant(tolua_S,"E_META_STONE_SLAB_SANDSTONE",E_META_STONE_SLAB_SANDSTONE);
+ tolua_constant(tolua_S,"E_META_STONE_SLAB_PLANKS",E_META_STONE_SLAB_PLANKS);
+ tolua_constant(tolua_S,"E_META_STONE_SLAB_COBBLESTONE",E_META_STONE_SLAB_COBBLESTONE);
+ tolua_constant(tolua_S,"E_META_STONE_SLAB_BRICK",E_META_STONE_SLAB_BRICK);
+ tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE_BRICK",E_META_STONE_SLAB_STONE_BRICK);
+ tolua_constant(tolua_S,"E_META_STONE_SLAB_NETHER_BRICK",E_META_STONE_SLAB_NETHER_BRICK);
+ tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE_SECRET",E_META_STONE_SLAB_STONE_SECRET);
+ tolua_constant(tolua_S,"E_META_STONE_BRICK_NORMAL",E_META_STONE_BRICK_NORMAL);
+ tolua_constant(tolua_S,"E_META_STONE_BRICK_MOSSY",E_META_STONE_BRICK_MOSSY);
+ tolua_constant(tolua_S,"E_META_STONE_BRICK_CRACKED",E_META_STONE_BRICK_CRACKED);
+ tolua_constant(tolua_S,"E_META_STONE_BRICK_ORNAMENT",E_META_STONE_BRICK_ORNAMENT);
+ tolua_constant(tolua_S,"E_META_TALL_GRASS_DEAD_SHRUB",E_META_TALL_GRASS_DEAD_SHRUB);
+ tolua_constant(tolua_S,"E_META_TALL_GRASS_GRASS",E_META_TALL_GRASS_GRASS);
+ tolua_constant(tolua_S,"E_META_TALL_GRASS_FERN",E_META_TALL_GRASS_FERN);
+ tolua_constant(tolua_S,"E_META_TORCH_EAST",E_META_TORCH_EAST);
+ tolua_constant(tolua_S,"E_META_TORCH_WEST",E_META_TORCH_WEST);
+ tolua_constant(tolua_S,"E_META_TORCH_SOUTH",E_META_TORCH_SOUTH);
+ tolua_constant(tolua_S,"E_META_TORCH_NORTH",E_META_TORCH_NORTH);
+ tolua_constant(tolua_S,"E_META_TORCH_FLOOR",E_META_TORCH_FLOOR);
+ tolua_constant(tolua_S,"E_META_TORCH_XM",E_META_TORCH_XM);
+ tolua_constant(tolua_S,"E_META_TORCH_XP",E_META_TORCH_XP);
+ tolua_constant(tolua_S,"E_META_TORCH_ZM",E_META_TORCH_ZM);
+ tolua_constant(tolua_S,"E_META_TORCH_ZP",E_META_TORCH_ZP);
+ tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_APPLE",E_META_WOODEN_DOUBLE_SLAB_APPLE);
+ tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_CONIFER",E_META_WOODEN_DOUBLE_SLAB_CONIFER);
+ tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_BIRCH",E_META_WOODEN_DOUBLE_SLAB_BIRCH);
+ tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_JUNGLE",E_META_WOODEN_DOUBLE_SLAB_JUNGLE);
+ tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_ACACIA",E_META_WOODEN_DOUBLE_SLAB_ACACIA);
+ tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_DARK_OAK",E_META_WOODEN_DOUBLE_SLAB_DARK_OAK);
+ tolua_constant(tolua_S,"E_META_WOODEN_SLAB_APPLE",E_META_WOODEN_SLAB_APPLE);
+ tolua_constant(tolua_S,"E_META_WOODEN_SLAB_CONIFER",E_META_WOODEN_SLAB_CONIFER);
+ tolua_constant(tolua_S,"E_META_WOODEN_SLAB_BIRCH",E_META_WOODEN_SLAB_BIRCH);
+ tolua_constant(tolua_S,"E_META_WOODEN_SLAB_JUNGLE",E_META_WOODEN_SLAB_JUNGLE);
+ tolua_constant(tolua_S,"E_META_WOODEN_SLAB_ACACIA",E_META_WOODEN_SLAB_ACACIA);
+ tolua_constant(tolua_S,"E_META_WOODEN_SLAB_DARK_OAK",E_META_WOODEN_SLAB_DARK_OAK);
+ tolua_constant(tolua_S,"E_META_WOOL_WHITE",E_META_WOOL_WHITE);
+ tolua_constant(tolua_S,"E_META_WOOL_ORANGE",E_META_WOOL_ORANGE);
+ tolua_constant(tolua_S,"E_META_WOOL_MAGENTA",E_META_WOOL_MAGENTA);
+ tolua_constant(tolua_S,"E_META_WOOL_LIGHTBLUE",E_META_WOOL_LIGHTBLUE);
+ tolua_constant(tolua_S,"E_META_WOOL_YELLOW",E_META_WOOL_YELLOW);
+ tolua_constant(tolua_S,"E_META_WOOL_LIGHTGREEN",E_META_WOOL_LIGHTGREEN);
+ tolua_constant(tolua_S,"E_META_WOOL_PINK",E_META_WOOL_PINK);
+ tolua_constant(tolua_S,"E_META_WOOL_GRAY",E_META_WOOL_GRAY);
+ tolua_constant(tolua_S,"E_META_WOOL_LIGHTGRAY",E_META_WOOL_LIGHTGRAY);
+ tolua_constant(tolua_S,"E_META_WOOL_CYAN",E_META_WOOL_CYAN);
+ tolua_constant(tolua_S,"E_META_WOOL_PURPLE",E_META_WOOL_PURPLE);
+ tolua_constant(tolua_S,"E_META_WOOL_BLUE",E_META_WOOL_BLUE);
+ tolua_constant(tolua_S,"E_META_WOOL_BROWN",E_META_WOOL_BROWN);
+ tolua_constant(tolua_S,"E_META_WOOL_GREEN",E_META_WOOL_GREEN);
+ tolua_constant(tolua_S,"E_META_WOOL_RED",E_META_WOOL_RED);
+ tolua_constant(tolua_S,"E_META_WOOL_BLACK",E_META_WOOL_BLACK);
+ tolua_constant(tolua_S,"E_META_CARPET_WHITE",E_META_CARPET_WHITE);
+ tolua_constant(tolua_S,"E_META_CARPET_ORANGE",E_META_CARPET_ORANGE);
+ tolua_constant(tolua_S,"E_META_CARPET_MAGENTA",E_META_CARPET_MAGENTA);
+ tolua_constant(tolua_S,"E_META_CARPET_LIGHTBLUE",E_META_CARPET_LIGHTBLUE);
+ tolua_constant(tolua_S,"E_META_CARPET_YELLOW",E_META_CARPET_YELLOW);
+ tolua_constant(tolua_S,"E_META_CARPET_LIGHTGREEN",E_META_CARPET_LIGHTGREEN);
+ tolua_constant(tolua_S,"E_META_CARPET_PINK",E_META_CARPET_PINK);
+ tolua_constant(tolua_S,"E_META_CARPET_GRAY",E_META_CARPET_GRAY);
+ tolua_constant(tolua_S,"E_META_CARPET_LIGHTGRAY",E_META_CARPET_LIGHTGRAY);
+ tolua_constant(tolua_S,"E_META_CARPET_CYAN",E_META_CARPET_CYAN);
+ tolua_constant(tolua_S,"E_META_CARPET_PURPLE",E_META_CARPET_PURPLE);
+ tolua_constant(tolua_S,"E_META_CARPET_BLUE",E_META_CARPET_BLUE);
+ tolua_constant(tolua_S,"E_META_CARPET_BROWN",E_META_CARPET_BROWN);
+ tolua_constant(tolua_S,"E_META_CARPET_GREEN",E_META_CARPET_GREEN);
+ tolua_constant(tolua_S,"E_META_CARPET_RED",E_META_CARPET_RED);
+ tolua_constant(tolua_S,"E_META_CARPET_BLACK",E_META_CARPET_BLACK);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_WHITE",E_META_STAINED_CLAY_WHITE);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_ORANGE",E_META_STAINED_CLAY_ORANGE);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_MAGENTA",E_META_STAINED_CLAY_MAGENTA);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTBLUE",E_META_STAINED_CLAY_LIGHTBLUE);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_YELLOW",E_META_STAINED_CLAY_YELLOW);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTGREEN",E_META_STAINED_CLAY_LIGHTGREEN);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_PINK",E_META_STAINED_CLAY_PINK);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_GRAY",E_META_STAINED_CLAY_GRAY);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTGRAY",E_META_STAINED_CLAY_LIGHTGRAY);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_CYAN",E_META_STAINED_CLAY_CYAN);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_PURPLE",E_META_STAINED_CLAY_PURPLE);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_BLUE",E_META_STAINED_CLAY_BLUE);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_BROWN",E_META_STAINED_CLAY_BROWN);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_GREEN",E_META_STAINED_CLAY_GREEN);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_RED",E_META_STAINED_CLAY_RED);
+ tolua_constant(tolua_S,"E_META_STAINED_CLAY_BLACK",E_META_STAINED_CLAY_BLACK);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_WHITE",E_META_STAINED_GLASS_WHITE);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_ORANGE",E_META_STAINED_GLASS_ORANGE);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_MAGENTA",E_META_STAINED_GLASS_MAGENTA);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTBLUE",E_META_STAINED_GLASS_LIGHTBLUE);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_YELLOW",E_META_STAINED_GLASS_YELLOW);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTGREEN",E_META_STAINED_GLASS_LIGHTGREEN);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PINK",E_META_STAINED_GLASS_PINK);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_GRAY",E_META_STAINED_GLASS_GRAY);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTGRAY",E_META_STAINED_GLASS_LIGHTGRAY);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_CYAN",E_META_STAINED_GLASS_CYAN);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PURPLE",E_META_STAINED_GLASS_PURPLE);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_BLUE",E_META_STAINED_GLASS_BLUE);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_BROWN",E_META_STAINED_GLASS_BROWN);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_GREEN",E_META_STAINED_GLASS_GREEN);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_RED",E_META_STAINED_GLASS_RED);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_BLACK",E_META_STAINED_GLASS_BLACK);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_WHITE",E_META_STAINED_GLASS_PANE_WHITE);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_ORANGE",E_META_STAINED_GLASS_PANE_ORANGE);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_MAGENTA",E_META_STAINED_GLASS_PANE_MAGENTA);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTBLUE",E_META_STAINED_GLASS_PANE_LIGHTBLUE);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_YELLOW",E_META_STAINED_GLASS_PANE_YELLOW);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTGREEN",E_META_STAINED_GLASS_PANE_LIGHTGREEN);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_PINK",E_META_STAINED_GLASS_PANE_PINK);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_GRAY",E_META_STAINED_GLASS_PANE_GRAY);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTGRAY",E_META_STAINED_GLASS_PANE_LIGHTGRAY);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_CYAN",E_META_STAINED_GLASS_PANE_CYAN);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_PURPLE",E_META_STAINED_GLASS_PANE_PURPLE);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BLUE",E_META_STAINED_GLASS_PANE_BLUE);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BROWN",E_META_STAINED_GLASS_PANE_BROWN);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_GREEN",E_META_STAINED_GLASS_PANE_GREEN);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_RED",E_META_STAINED_GLASS_PANE_RED);
+ tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BLACK",E_META_STAINED_GLASS_PANE_BLACK);
+ tolua_constant(tolua_S,"E_META_SNOW_LAYER_ONE",E_META_SNOW_LAYER_ONE);
+ tolua_constant(tolua_S,"E_META_SNOW_LAYER_TWO",E_META_SNOW_LAYER_TWO);
+ tolua_constant(tolua_S,"E_META_SNOW_LAYER_THREE",E_META_SNOW_LAYER_THREE);
+ tolua_constant(tolua_S,"E_META_SNOW_LAYER_FOUR",E_META_SNOW_LAYER_FOUR);
+ tolua_constant(tolua_S,"E_META_SNOW_LAYER_FIVE",E_META_SNOW_LAYER_FIVE);
+ tolua_constant(tolua_S,"E_META_SNOW_LAYER_SIX",E_META_SNOW_LAYER_SIX);
+ tolua_constant(tolua_S,"E_META_SNOW_LAYER_SEVEN",E_META_SNOW_LAYER_SEVEN);
+ tolua_constant(tolua_S,"E_META_SNOW_LAYER_EIGHT",E_META_SNOW_LAYER_EIGHT);
+ tolua_constant(tolua_S,"E_META_RAIL_ZM_ZP",E_META_RAIL_ZM_ZP);
+ tolua_constant(tolua_S,"E_META_RAIL_XM_XP",E_META_RAIL_XM_XP);
+ tolua_constant(tolua_S,"E_META_RAIL_ASCEND_XP",E_META_RAIL_ASCEND_XP);
+ tolua_constant(tolua_S,"E_META_RAIL_ASCEND_XM",E_META_RAIL_ASCEND_XM);
+ tolua_constant(tolua_S,"E_META_RAIL_ASCEND_ZM",E_META_RAIL_ASCEND_ZM);
+ tolua_constant(tolua_S,"E_META_RAIL_ASCEND_ZP",E_META_RAIL_ASCEND_ZP);
+ tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZP_XP",E_META_RAIL_CURVED_ZP_XP);
+ tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZP_XM",E_META_RAIL_CURVED_ZP_XM);
+ tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZM_XM",E_META_RAIL_CURVED_ZM_XM);
+ tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZM_XP",E_META_RAIL_CURVED_ZM_XP);
+ tolua_constant(tolua_S,"E_META_NEW_LEAVES_ACACIA_WOOD",E_META_NEW_LEAVES_ACACIA_WOOD);
+ tolua_constant(tolua_S,"E_META_NEW_LEAVES_DARK_OAK_WOOD",E_META_NEW_LEAVES_DARK_OAK_WOOD);
+ tolua_constant(tolua_S,"E_META_NEW_LOG_ACACIA_WOOD",E_META_NEW_LOG_ACACIA_WOOD);
+ tolua_constant(tolua_S,"E_META_NEW_LOG_DARK_OAK_WOOD",E_META_NEW_LOG_DARK_OAK_WOOD);
+ tolua_constant(tolua_S,"E_META_FLOWER_POPPY",E_META_FLOWER_POPPY);
+ tolua_constant(tolua_S,"E_META_FLOWER_BLUE_ORCHID",E_META_FLOWER_BLUE_ORCHID);
+ tolua_constant(tolua_S,"E_META_FLOWER_ALLIUM",E_META_FLOWER_ALLIUM);
+ tolua_constant(tolua_S,"E_META_FLOWER_RED_TULIP",E_META_FLOWER_RED_TULIP);
+ tolua_constant(tolua_S,"E_META_FLOWER_ORANGE_TULIP",E_META_FLOWER_ORANGE_TULIP);
+ tolua_constant(tolua_S,"E_META_FLOWER_WHITE_TULIP",E_META_FLOWER_WHITE_TULIP);
+ tolua_constant(tolua_S,"E_META_FLOWER_PINK_TULIP",E_META_FLOWER_PINK_TULIP);
+ tolua_constant(tolua_S,"E_META_FLOWER_OXEYE_DAISY",E_META_FLOWER_OXEYE_DAISY);
+ tolua_constant(tolua_S,"E_META_BIG_FLOWER_SUNFLOWER",E_META_BIG_FLOWER_SUNFLOWER);
+ tolua_constant(tolua_S,"E_META_BIG_FLOWER_LILAC",E_META_BIG_FLOWER_LILAC);
+ tolua_constant(tolua_S,"E_META_BIG_FLOWER_DOUBLE_TALL_GRASS",E_META_BIG_FLOWER_DOUBLE_TALL_GRASS);
+ tolua_constant(tolua_S,"E_META_BIG_FLOWER_LARGE_FERN",E_META_BIG_FLOWER_LARGE_FERN);
+ tolua_constant(tolua_S,"E_META_BIG_FLOWER_ROSE_BUSH",E_META_BIG_FLOWER_ROSE_BUSH);
+ tolua_constant(tolua_S,"E_META_BIG_FLOWER_PEONY",E_META_BIG_FLOWER_PEONY);
+ tolua_constant(tolua_S,"E_META_COAL_NORMAL",E_META_COAL_NORMAL);
+ tolua_constant(tolua_S,"E_META_COAL_CHARCOAL",E_META_COAL_CHARCOAL);
+ tolua_constant(tolua_S,"E_META_DYE_BLACK",E_META_DYE_BLACK);
+ tolua_constant(tolua_S,"E_META_DYE_RED",E_META_DYE_RED);
+ tolua_constant(tolua_S,"E_META_DYE_GREEN",E_META_DYE_GREEN);
+ tolua_constant(tolua_S,"E_META_DYE_BROWN",E_META_DYE_BROWN);
+ tolua_constant(tolua_S,"E_META_DYE_BLUE",E_META_DYE_BLUE);
+ tolua_constant(tolua_S,"E_META_DYE_PURPLE",E_META_DYE_PURPLE);
+ tolua_constant(tolua_S,"E_META_DYE_CYAN",E_META_DYE_CYAN);
+ tolua_constant(tolua_S,"E_META_DYE_LIGHTGRAY",E_META_DYE_LIGHTGRAY);
+ tolua_constant(tolua_S,"E_META_DYE_GRAY",E_META_DYE_GRAY);
+ tolua_constant(tolua_S,"E_META_DYE_PINK",E_META_DYE_PINK);
+ tolua_constant(tolua_S,"E_META_DYE_LIGHTGREEN",E_META_DYE_LIGHTGREEN);
+ tolua_constant(tolua_S,"E_META_DYE_YELLOW",E_META_DYE_YELLOW);
+ tolua_constant(tolua_S,"E_META_DYE_LIGHTBLUE",E_META_DYE_LIGHTBLUE);
+ tolua_constant(tolua_S,"E_META_DYE_MAGENTA",E_META_DYE_MAGENTA);
+ tolua_constant(tolua_S,"E_META_DYE_ORANGE",E_META_DYE_ORANGE);
+ tolua_constant(tolua_S,"E_META_DYE_WHITE",E_META_DYE_WHITE);
+ tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_NORMAL",E_META_GOLDEN_APPLE_NORMAL);
+ tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_ENCHANTED",E_META_GOLDEN_APPLE_ENCHANTED);
+ tolua_constant(tolua_S,"E_META_RAW_FISH_FISH",E_META_RAW_FISH_FISH);
+ tolua_constant(tolua_S,"E_META_RAW_FISH_SALMON",E_META_RAW_FISH_SALMON);
+ tolua_constant(tolua_S,"E_META_RAW_FISH_CLOWNFISH",E_META_RAW_FISH_CLOWNFISH);
+ tolua_constant(tolua_S,"E_META_RAW_FISH_PUFFERFISH",E_META_RAW_FISH_PUFFERFISH);
+ tolua_constant(tolua_S,"E_META_COOKED_FISH_FISH",E_META_COOKED_FISH_FISH);
+ tolua_constant(tolua_S,"E_META_COOKED_FISH_SALMON",E_META_COOKED_FISH_SALMON);
+ tolua_constant(tolua_S,"E_META_COOKED_FISH_CLOWNFISH",E_META_COOKED_FISH_CLOWNFISH);
+ tolua_constant(tolua_S,"E_META_COOKED_FISH_PUFFERFISH",E_META_COOKED_FISH_PUFFERFISH);
+ tolua_constant(tolua_S,"E_META_TRACKS_X",E_META_TRACKS_X);
+ tolua_constant(tolua_S,"E_META_TRACKS_Z",E_META_TRACKS_Z);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_PICKUP",E_META_SPAWN_EGG_PICKUP);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXPERIENCE_ORB",E_META_SPAWN_EGG_EXPERIENCE_ORB);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_LEASH_KNOT",E_META_SPAWN_EGG_LEASH_KNOT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_PAINTING",E_META_SPAWN_EGG_PAINTING);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ARROW",E_META_SPAWN_EGG_ARROW);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SNOWBALL",E_META_SPAWN_EGG_SNOWBALL);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREBALL",E_META_SPAWN_EGG_FIREBALL);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SMALL_FIREBALL",E_META_SPAWN_EGG_SMALL_FIREBALL);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_PEARL",E_META_SPAWN_EGG_ENDER_PEARL);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_EYE_OF_ENDER",E_META_SPAWN_EGG_EYE_OF_ENDER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPLASH_POTION",E_META_SPAWN_EGG_SPLASH_POTION);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXP_BOTTLE",E_META_SPAWN_EGG_EXP_BOTTLE);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ITEM_FRAME",E_META_SPAWN_EGG_ITEM_FRAME);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITHER_SKULL",E_META_SPAWN_EGG_WITHER_SKULL);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_PRIMED_TNT",E_META_SPAWN_EGG_PRIMED_TNT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_FALLING_BLOCK",E_META_SPAWN_EGG_FALLING_BLOCK);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREWORK",E_META_SPAWN_EGG_FIREWORK);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_BOAT",E_META_SPAWN_EGG_BOAT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART",E_META_SPAWN_EGG_MINECART);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_CHEST",E_META_SPAWN_EGG_MINECART_CHEST);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_FURNACE",E_META_SPAWN_EGG_MINECART_FURNACE);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_TNT",E_META_SPAWN_EGG_MINECART_TNT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_HOPPER",E_META_SPAWN_EGG_MINECART_HOPPER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_SPAWNER",E_META_SPAWN_EGG_MINECART_SPAWNER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_CREEPER",E_META_SPAWN_EGG_CREEPER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SKELETON",E_META_SPAWN_EGG_SKELETON);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPIDER",E_META_SPAWN_EGG_SPIDER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_GIANT",E_META_SPAWN_EGG_GIANT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE",E_META_SPAWN_EGG_ZOMBIE);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SLIME",E_META_SPAWN_EGG_SLIME);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_GHAST",E_META_SPAWN_EGG_GHAST);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE_PIGMAN",E_META_SPAWN_EGG_ZOMBIE_PIGMAN);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDERMAN",E_META_SPAWN_EGG_ENDERMAN);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_CAVE_SPIDER",E_META_SPAWN_EGG_CAVE_SPIDER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SILVERFISH",E_META_SPAWN_EGG_SILVERFISH);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_BLAZE",E_META_SPAWN_EGG_BLAZE);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MAGMA_CUBE",E_META_SPAWN_EGG_MAGMA_CUBE);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_DRAGON",E_META_SPAWN_EGG_ENDER_DRAGON);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITHER",E_META_SPAWN_EGG_WITHER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_BAT",E_META_SPAWN_EGG_BAT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITCH",E_META_SPAWN_EGG_WITCH);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_PIG",E_META_SPAWN_EGG_PIG);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SHEEP",E_META_SPAWN_EGG_SHEEP);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_COW",E_META_SPAWN_EGG_COW);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_CHICKEN",E_META_SPAWN_EGG_CHICKEN);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SQUID",E_META_SPAWN_EGG_SQUID);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_WOLF",E_META_SPAWN_EGG_WOLF);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_MOOSHROOM",E_META_SPAWN_EGG_MOOSHROOM);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_SNOW_GOLEM",E_META_SPAWN_EGG_SNOW_GOLEM);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_OCELOT",E_META_SPAWN_EGG_OCELOT);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_IRON_GOLEM",E_META_SPAWN_EGG_IRON_GOLEM);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_HORSE",E_META_SPAWN_EGG_HORSE);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_VILLAGER",E_META_SPAWN_EGG_VILLAGER);
+ tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_CRYSTAL",E_META_SPAWN_EGG_ENDER_CRYSTAL);
+ tolua_constant(tolua_S,"dimNether",dimNether);
+ tolua_constant(tolua_S,"dimOverworld",dimOverworld);
+ tolua_constant(tolua_S,"dimEnd",dimEnd);
+ tolua_constant(tolua_S,"dtAttack",dtAttack);
+ tolua_constant(tolua_S,"dtRangedAttack",dtRangedAttack);
+ tolua_constant(tolua_S,"dtLightning",dtLightning);
+ tolua_constant(tolua_S,"dtFalling",dtFalling);
+ tolua_constant(tolua_S,"dtDrowning",dtDrowning);
+ tolua_constant(tolua_S,"dtSuffocating",dtSuffocating);
+ tolua_constant(tolua_S,"dtStarving",dtStarving);
+ tolua_constant(tolua_S,"dtCactusContact",dtCactusContact);
+ tolua_constant(tolua_S,"dtLavaContact",dtLavaContact);
+ tolua_constant(tolua_S,"dtPoisoning",dtPoisoning);
+ tolua_constant(tolua_S,"dtOnFire",dtOnFire);
+ tolua_constant(tolua_S,"dtFireContact",dtFireContact);
+ tolua_constant(tolua_S,"dtInVoid",dtInVoid);
+ tolua_constant(tolua_S,"dtPotionOfHarming",dtPotionOfHarming);
+ tolua_constant(tolua_S,"dtEnderPearl",dtEnderPearl);
+ tolua_constant(tolua_S,"dtAdmin",dtAdmin);
+ tolua_constant(tolua_S,"dtPawnAttack",dtPawnAttack);
+ tolua_constant(tolua_S,"dtEntityAttack",dtEntityAttack);
+ tolua_constant(tolua_S,"dtMob",dtMob);
+ tolua_constant(tolua_S,"dtMobAttack",dtMobAttack);
+ tolua_constant(tolua_S,"dtArrowAttack",dtArrowAttack);
+ tolua_constant(tolua_S,"dtArrow",dtArrow);
+ tolua_constant(tolua_S,"dtProjectile",dtProjectile);
+ tolua_constant(tolua_S,"dtFall",dtFall);
+ tolua_constant(tolua_S,"dtDrown",dtDrown);
+ tolua_constant(tolua_S,"dtSuffocation",dtSuffocation);
+ tolua_constant(tolua_S,"dtStarvation",dtStarvation);
+ tolua_constant(tolua_S,"dtHunger",dtHunger);
+ tolua_constant(tolua_S,"dtCactus",dtCactus);
+ tolua_constant(tolua_S,"dtCactuses",dtCactuses);
+ tolua_constant(tolua_S,"dtCacti",dtCacti);
+ tolua_constant(tolua_S,"dtLava",dtLava);
+ tolua_constant(tolua_S,"dtPoison",dtPoison);
+ tolua_constant(tolua_S,"dtBurning",dtBurning);
+ tolua_constant(tolua_S,"dtInFire",dtInFire);
+ tolua_constant(tolua_S,"dtPlugin",dtPlugin);
+ tolua_constant(tolua_S,"esOther",esOther);
+ tolua_constant(tolua_S,"esPrimedTNT",esPrimedTNT);
+ tolua_constant(tolua_S,"esCreeper",esCreeper);
+ tolua_constant(tolua_S,"esBed",esBed);
+ tolua_constant(tolua_S,"esEnderCrystal",esEnderCrystal);
+ tolua_constant(tolua_S,"esGhastFireball",esGhastFireball);
+ tolua_constant(tolua_S,"esWitherSkullBlack",esWitherSkullBlack);
+ tolua_constant(tolua_S,"esWitherSkullBlue",esWitherSkullBlue);
+ tolua_constant(tolua_S,"esWitherBirth",esWitherBirth);
+ tolua_constant(tolua_S,"esPlugin",esPlugin);
+ tolua_function(tolua_S,"BlockStringToType",tolua_AllToLua_BlockStringToType00);
+ tolua_function(tolua_S,"StringToItem",tolua_AllToLua_StringToItem00);
+ tolua_function(tolua_S,"ItemToString",tolua_AllToLua_ItemToString00);
+ tolua_function(tolua_S,"ItemTypeToString",tolua_AllToLua_ItemTypeToString00);
+ tolua_function(tolua_S,"ItemToFullString",tolua_AllToLua_ItemToFullString00);
+ tolua_function(tolua_S,"StringToBiome",tolua_AllToLua_StringToBiome00);
+ tolua_function(tolua_S,"StringToMobType",tolua_AllToLua_StringToMobType00);
+ tolua_function(tolua_S,"StringToDimension",tolua_AllToLua_StringToDimension00);
+ tolua_function(tolua_S,"DamageTypeToString",tolua_AllToLua_DamageTypeToString00);
+ tolua_function(tolua_S,"StringToDamageType",tolua_AllToLua_StringToDamageType00);
+ tolua_function(tolua_S,"GetIniItemSet",tolua_AllToLua_GetIniItemSet00);
+ tolua_function(tolua_S,"TrimString",tolua_AllToLua_TrimString00);
+ tolua_function(tolua_S,"NoCaseCompare",tolua_AllToLua_NoCaseCompare00);
+ tolua_function(tolua_S,"ReplaceString",tolua_AllToLua_ReplaceString00);
+ tolua_function(tolua_S,"EscapeString",tolua_AllToLua_EscapeString00);
+ tolua_function(tolua_S,"StripColorCodes",tolua_AllToLua_StripColorCodes00);
+ tolua_array(tolua_S,"g_BlockLightValue",tolua_get_AllToLua_g_BlockLightValue,tolua_set_AllToLua_g_BlockLightValue);
+ tolua_array(tolua_S,"g_BlockSpreadLightFalloff",tolua_get_AllToLua_g_BlockSpreadLightFalloff,tolua_set_AllToLua_g_BlockSpreadLightFalloff);
+ tolua_array(tolua_S,"g_BlockTransparent",tolua_get_AllToLua_g_BlockTransparent,tolua_set_AllToLua_g_BlockTransparent);
+ tolua_array(tolua_S,"g_BlockOneHitDig",tolua_get_AllToLua_g_BlockOneHitDig,tolua_set_AllToLua_g_BlockOneHitDig);
+ tolua_array(tolua_S,"g_BlockPistonBreakable",tolua_get_AllToLua_g_BlockPistonBreakable,tolua_set_AllToLua_g_BlockPistonBreakable);
+ tolua_array(tolua_S,"g_BlockIsSnowable",tolua_get_AllToLua_g_BlockIsSnowable,tolua_set_AllToLua_g_BlockIsSnowable);
+ tolua_array(tolua_S,"g_BlockRequiresSpecialTool",tolua_get_AllToLua_g_BlockRequiresSpecialTool,tolua_set_AllToLua_g_BlockRequiresSpecialTool);
+ tolua_array(tolua_S,"g_BlockIsSolid",tolua_get_AllToLua_g_BlockIsSolid,tolua_set_AllToLua_g_BlockIsSolid);
+ tolua_array(tolua_S,"g_BlockIsTorchPlaceable",tolua_get_AllToLua_g_BlockIsTorchPlaceable,tolua_set_AllToLua_g_BlockIsTorchPlaceable);
+ tolua_constant(tolua_S,"MAX_EXPERIENCE_ORB_SIZE",MAX_EXPERIENCE_ORB_SIZE);
+ tolua_constant(tolua_S,"BLOCK_FACE_NONE",BLOCK_FACE_NONE);
+ tolua_constant(tolua_S,"BLOCK_FACE_XM",BLOCK_FACE_XM);
+ tolua_constant(tolua_S,"BLOCK_FACE_XP",BLOCK_FACE_XP);
+ tolua_constant(tolua_S,"BLOCK_FACE_YM",BLOCK_FACE_YM);
+ tolua_constant(tolua_S,"BLOCK_FACE_YP",BLOCK_FACE_YP);
+ tolua_constant(tolua_S,"BLOCK_FACE_ZM",BLOCK_FACE_ZM);
+ tolua_constant(tolua_S,"BLOCK_FACE_ZP",BLOCK_FACE_ZP);
+ tolua_constant(tolua_S,"BLOCK_FACE_BOTTOM",BLOCK_FACE_BOTTOM);
+ tolua_constant(tolua_S,"BLOCK_FACE_TOP",BLOCK_FACE_TOP);
+ tolua_constant(tolua_S,"BLOCK_FACE_NORTH",BLOCK_FACE_NORTH);
+ tolua_constant(tolua_S,"BLOCK_FACE_SOUTH",BLOCK_FACE_SOUTH);
+ tolua_constant(tolua_S,"BLOCK_FACE_WEST",BLOCK_FACE_WEST);
+ tolua_constant(tolua_S,"BLOCK_FACE_EAST",BLOCK_FACE_EAST);
+ tolua_constant(tolua_S,"DIG_STATUS_STARTED",DIG_STATUS_STARTED);
+ tolua_constant(tolua_S,"DIG_STATUS_CANCELLED",DIG_STATUS_CANCELLED);
+ tolua_constant(tolua_S,"DIG_STATUS_FINISHED",DIG_STATUS_FINISHED);
+ tolua_constant(tolua_S,"DIG_STATUS_DROP_HELD",DIG_STATUS_DROP_HELD);
+ tolua_constant(tolua_S,"DIG_STATUS_SHOOT_EAT",DIG_STATUS_SHOOT_EAT);
+ tolua_constant(tolua_S,"caLeftClick",caLeftClick);
+ tolua_constant(tolua_S,"caRightClick",caRightClick);
+ tolua_constant(tolua_S,"caShiftLeftClick",caShiftLeftClick);
+ tolua_constant(tolua_S,"caShiftRightClick",caShiftRightClick);
+ tolua_constant(tolua_S,"caNumber1",caNumber1);
+ tolua_constant(tolua_S,"caNumber2",caNumber2);
+ tolua_constant(tolua_S,"caNumber3",caNumber3);
+ tolua_constant(tolua_S,"caNumber4",caNumber4);
+ tolua_constant(tolua_S,"caNumber5",caNumber5);
+ tolua_constant(tolua_S,"caNumber6",caNumber6);
+ tolua_constant(tolua_S,"caNumber7",caNumber7);
+ tolua_constant(tolua_S,"caNumber8",caNumber8);
+ tolua_constant(tolua_S,"caNumber9",caNumber9);
+ tolua_constant(tolua_S,"caMiddleClick",caMiddleClick);
+ tolua_constant(tolua_S,"caDropKey",caDropKey);
+ tolua_constant(tolua_S,"caCtrlDropKey",caCtrlDropKey);
+ tolua_constant(tolua_S,"caLeftClickOutside",caLeftClickOutside);
+ tolua_constant(tolua_S,"caRightClickOutside",caRightClickOutside);
+ tolua_constant(tolua_S,"caLeftClickOutsideHoldNothing",caLeftClickOutsideHoldNothing);
+ tolua_constant(tolua_S,"caRightClickOutsideHoldNothing",caRightClickOutsideHoldNothing);
+ tolua_constant(tolua_S,"caLeftPaintBegin",caLeftPaintBegin);
+ tolua_constant(tolua_S,"caRightPaintBegin",caRightPaintBegin);
+ tolua_constant(tolua_S,"caLeftPaintProgress",caLeftPaintProgress);
+ tolua_constant(tolua_S,"caRightPaintProgress",caRightPaintProgress);
+ tolua_constant(tolua_S,"caLeftPaintEnd",caLeftPaintEnd);
+ tolua_constant(tolua_S,"caRightPaintEnd",caRightPaintEnd);
+ tolua_constant(tolua_S,"caDblClick",caDblClick);
+ tolua_constant(tolua_S,"caUnknown",caUnknown);
+ tolua_constant(tolua_S,"eGameMode_NotSet",eGameMode_NotSet);
+ tolua_constant(tolua_S,"eGameMode_Survival",eGameMode_Survival);
+ tolua_constant(tolua_S,"eGameMode_Creative",eGameMode_Creative);
+ tolua_constant(tolua_S,"eGameMode_Adventure",eGameMode_Adventure);
+ tolua_constant(tolua_S,"gmNotSet",gmNotSet);
+ tolua_constant(tolua_S,"gmSurvival",gmSurvival);
+ tolua_constant(tolua_S,"gmCreative",gmCreative);
+ tolua_constant(tolua_S,"gmAdventure",gmAdventure);
+ tolua_constant(tolua_S,"gmMax",gmMax);
+ tolua_constant(tolua_S,"gmMin",gmMin);
+ tolua_constant(tolua_S,"eWeather_Sunny",eWeather_Sunny);
+ tolua_constant(tolua_S,"eWeather_Rain",eWeather_Rain);
+ tolua_constant(tolua_S,"eWeather_ThunderStorm",eWeather_ThunderStorm);
+ tolua_constant(tolua_S,"wSunny",wSunny);
+ tolua_constant(tolua_S,"wRain",wRain);
+ tolua_constant(tolua_S,"wThunderstorm",wThunderstorm);
+ tolua_constant(tolua_S,"wStorm",wStorm);
+ tolua_function(tolua_S,"ClickActionToString",tolua_AllToLua_ClickActionToString00);
+ tolua_function(tolua_S,"IsValidBlock",tolua_AllToLua_IsValidBlock00);
+ tolua_function(tolua_S,"IsValidItem",tolua_AllToLua_IsValidItem00);
+ tolua_function(tolua_S,"AddFaceDirection",tolua_AllToLua_AddFaceDirection00);
+ tolua_module(tolua_S,"ItemCategory",0);
+ tolua_beginmodule(tolua_S,"ItemCategory");
+ tolua_function(tolua_S,"IsPickaxe",tolua_AllToLua_ItemCategory_IsPickaxe00);
+ tolua_function(tolua_S,"IsAxe",tolua_AllToLua_ItemCategory_IsAxe00);
+ tolua_function(tolua_S,"IsSword",tolua_AllToLua_ItemCategory_IsSword00);
+ tolua_function(tolua_S,"IsHoe",tolua_AllToLua_ItemCategory_IsHoe00);
+ tolua_function(tolua_S,"IsShovel",tolua_AllToLua_ItemCategory_IsShovel00);
+ tolua_function(tolua_S,"IsTool",tolua_AllToLua_ItemCategory_IsTool00);
+ tolua_function(tolua_S,"IsHelmet",tolua_AllToLua_ItemCategory_IsHelmet00);
+ tolua_function(tolua_S,"IsChestPlate",tolua_AllToLua_ItemCategory_IsChestPlate00);
+ tolua_function(tolua_S,"IsLeggings",tolua_AllToLua_ItemCategory_IsLeggings00);
+ tolua_function(tolua_S,"IsBoots",tolua_AllToLua_ItemCategory_IsBoots00);
+ tolua_function(tolua_S,"IsArmor",tolua_AllToLua_ItemCategory_IsArmor00);
+ tolua_endmodule(tolua_S);
+ tolua_function(tolua_S,"GetTime",tolua_AllToLua_GetTime00);
+ tolua_function(tolua_S,"GetChar",tolua_AllToLua_GetChar00);
+ tolua_cclass(tolua_S,"cChatColor","cChatColor","",NULL);
+ tolua_beginmodule(tolua_S,"cChatColor");
+ tolua_variable(tolua_S,"Color",tolua_get_cChatColor_Color,NULL);
+ tolua_variable(tolua_S,"Delimiter",tolua_get_cChatColor_Delimiter,NULL);
+ tolua_variable(tolua_S,"Black",tolua_get_cChatColor_Black,NULL);
+ tolua_variable(tolua_S,"Navy",tolua_get_cChatColor_Navy,NULL);
+ tolua_variable(tolua_S,"Green",tolua_get_cChatColor_Green,NULL);
+ tolua_variable(tolua_S,"Blue",tolua_get_cChatColor_Blue,NULL);
+ tolua_variable(tolua_S,"Red",tolua_get_cChatColor_Red,NULL);
+ tolua_variable(tolua_S,"Purple",tolua_get_cChatColor_Purple,NULL);
+ tolua_variable(tolua_S,"Gold",tolua_get_cChatColor_Gold,NULL);
+ tolua_variable(tolua_S,"LightGray",tolua_get_cChatColor_LightGray,NULL);
+ tolua_variable(tolua_S,"Gray",tolua_get_cChatColor_Gray,NULL);
+ tolua_variable(tolua_S,"DarkPurple",tolua_get_cChatColor_DarkPurple,NULL);
+ tolua_variable(tolua_S,"LightGreen",tolua_get_cChatColor_LightGreen,NULL);
+ tolua_variable(tolua_S,"LightBlue",tolua_get_cChatColor_LightBlue,NULL);
+ tolua_variable(tolua_S,"Rose",tolua_get_cChatColor_Rose,NULL);
+ tolua_variable(tolua_S,"LightPurple",tolua_get_cChatColor_LightPurple,NULL);
+ tolua_variable(tolua_S,"Yellow",tolua_get_cChatColor_Yellow,NULL);
+ tolua_variable(tolua_S,"White",tolua_get_cChatColor_White,NULL);
+ tolua_variable(tolua_S,"Random",tolua_get_cChatColor_Random,NULL);
+ tolua_variable(tolua_S,"Bold",tolua_get_cChatColor_Bold,NULL);
+ tolua_variable(tolua_S,"Strikethrough",tolua_get_cChatColor_Strikethrough,NULL);
+ tolua_variable(tolua_S,"Underlined",tolua_get_cChatColor_Underlined,NULL);
+ tolua_variable(tolua_S,"Italic",tolua_get_cChatColor_Italic,NULL);
+ tolua_variable(tolua_S,"Plain",tolua_get_cChatColor_Plain,NULL);
+ tolua_function(tolua_S,"MakeColor",tolua_AllToLua_cChatColor_MakeColor00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cClientHandle","cClientHandle","",NULL);
+ tolua_beginmodule(tolua_S,"cClientHandle");
+ tolua_function(tolua_S,"GetPlayer",tolua_AllToLua_cClientHandle_GetPlayer00);
+ tolua_function(tolua_S,"Kick",tolua_AllToLua_cClientHandle_Kick00);
+ tolua_function(tolua_S,"SendBlockChange",tolua_AllToLua_cClientHandle_SendBlockChange00);
+ tolua_function(tolua_S,"GetUsername",tolua_AllToLua_cClientHandle_GetUsername00);
+ tolua_function(tolua_S,"SetUsername",tolua_AllToLua_cClientHandle_SetUsername00);
+ tolua_function(tolua_S,"GetPing",tolua_AllToLua_cClientHandle_GetPing00);
+ tolua_function(tolua_S,"SetViewDistance",tolua_AllToLua_cClientHandle_SetViewDistance00);
+ tolua_function(tolua_S,"GetViewDistance",tolua_AllToLua_cClientHandle_GetViewDistance00);
+ tolua_function(tolua_S,"GetUniqueID",tolua_AllToLua_cClientHandle_GetUniqueID00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"TakeDamageInfo","TakeDamageInfo","",NULL);
+ tolua_beginmodule(tolua_S,"TakeDamageInfo");
+ tolua_variable(tolua_S,"DamageType",tolua_get_TakeDamageInfo_DamageType,tolua_set_TakeDamageInfo_DamageType);
+ tolua_variable(tolua_S,"Attacker",tolua_get_TakeDamageInfo_Attacker_ptr,tolua_set_TakeDamageInfo_Attacker_ptr);
+ tolua_variable(tolua_S,"RawDamage",tolua_get_TakeDamageInfo_RawDamage,tolua_set_TakeDamageInfo_RawDamage);
+ tolua_variable(tolua_S,"FinalDamage",tolua_get_TakeDamageInfo_FinalDamage,tolua_set_TakeDamageInfo_FinalDamage);
+ tolua_variable(tolua_S,"Knockback",tolua_get_TakeDamageInfo_Knockback,tolua_set_TakeDamageInfo_Knockback);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cEntity","cEntity","",NULL);
+ tolua_beginmodule(tolua_S,"cEntity");
+ tolua_constant(tolua_S,"etEntity",cEntity::etEntity);
+ tolua_constant(tolua_S,"etPlayer",cEntity::etPlayer);
+ tolua_constant(tolua_S,"etPickup",cEntity::etPickup);
+ tolua_constant(tolua_S,"etMonster",cEntity::etMonster);
+ tolua_constant(tolua_S,"etFallingBlock",cEntity::etFallingBlock);
+ tolua_constant(tolua_S,"etMinecart",cEntity::etMinecart);
+ tolua_constant(tolua_S,"etBoat",cEntity::etBoat);
+ tolua_constant(tolua_S,"etTNT",cEntity::etTNT);
+ tolua_constant(tolua_S,"etProjectile",cEntity::etProjectile);
+ tolua_constant(tolua_S,"etMob",cEntity::etMob);
+ tolua_function(tolua_S,"GetEntityType",tolua_AllToLua_cEntity_GetEntityType00);
+ tolua_function(tolua_S,"IsPlayer",tolua_AllToLua_cEntity_IsPlayer00);
+ tolua_function(tolua_S,"IsPickup",tolua_AllToLua_cEntity_IsPickup00);
+ tolua_function(tolua_S,"IsMob",tolua_AllToLua_cEntity_IsMob00);
+ tolua_function(tolua_S,"IsFallingBlock",tolua_AllToLua_cEntity_IsFallingBlock00);
+ tolua_function(tolua_S,"IsMinecart",tolua_AllToLua_cEntity_IsMinecart00);
+ tolua_function(tolua_S,"IsBoat",tolua_AllToLua_cEntity_IsBoat00);
+ tolua_function(tolua_S,"IsTNT",tolua_AllToLua_cEntity_IsTNT00);
+ tolua_function(tolua_S,"IsProjectile",tolua_AllToLua_cEntity_IsProjectile00);
+ tolua_function(tolua_S,"IsA",tolua_AllToLua_cEntity_IsA00);
+ tolua_function(tolua_S,"GetClass",tolua_AllToLua_cEntity_GetClass00);
+ tolua_function(tolua_S,"GetClassStatic",tolua_AllToLua_cEntity_GetClassStatic00);
+ tolua_function(tolua_S,"GetParentClass",tolua_AllToLua_cEntity_GetParentClass00);
+ tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cEntity_GetWorld00);
+ tolua_function(tolua_S,"GetHeadYaw",tolua_AllToLua_cEntity_GetHeadYaw00);
+ tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cEntity_GetHeight00);
+ tolua_function(tolua_S,"GetMass",tolua_AllToLua_cEntity_GetMass00);
+ tolua_function(tolua_S,"GetPosition",tolua_AllToLua_cEntity_GetPosition00);
+ tolua_function(tolua_S,"GetPosX",tolua_AllToLua_cEntity_GetPosX00);
+ tolua_function(tolua_S,"GetPosY",tolua_AllToLua_cEntity_GetPosY00);
+ tolua_function(tolua_S,"GetPosZ",tolua_AllToLua_cEntity_GetPosZ00);
+ tolua_function(tolua_S,"GetRot",tolua_AllToLua_cEntity_GetRot00);
+ tolua_function(tolua_S,"GetRotation",tolua_AllToLua_cEntity_GetRotation00);
+ tolua_function(tolua_S,"GetYaw",tolua_AllToLua_cEntity_GetYaw00);
+ tolua_function(tolua_S,"GetPitch",tolua_AllToLua_cEntity_GetPitch00);
+ tolua_function(tolua_S,"GetRoll",tolua_AllToLua_cEntity_GetRoll00);
+ tolua_function(tolua_S,"GetLookVector",tolua_AllToLua_cEntity_GetLookVector00);
+ tolua_function(tolua_S,"GetSpeed",tolua_AllToLua_cEntity_GetSpeed00);
+ tolua_function(tolua_S,"GetSpeedX",tolua_AllToLua_cEntity_GetSpeedX00);
+ tolua_function(tolua_S,"GetSpeedY",tolua_AllToLua_cEntity_GetSpeedY00);
+ tolua_function(tolua_S,"GetSpeedZ",tolua_AllToLua_cEntity_GetSpeedZ00);
+ tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cEntity_GetWidth00);
+ tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cEntity_GetChunkX00);
+ tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cEntity_GetChunkZ00);
+ tolua_function(tolua_S,"SetHeadYaw",tolua_AllToLua_cEntity_SetHeadYaw00);
+ tolua_function(tolua_S,"SetHeight",tolua_AllToLua_cEntity_SetHeight00);
+ tolua_function(tolua_S,"SetMass",tolua_AllToLua_cEntity_SetMass00);
+ tolua_function(tolua_S,"SetPosX",tolua_AllToLua_cEntity_SetPosX00);
+ tolua_function(tolua_S,"SetPosY",tolua_AllToLua_cEntity_SetPosY00);
+ tolua_function(tolua_S,"SetPosZ",tolua_AllToLua_cEntity_SetPosZ00);
+ tolua_function(tolua_S,"SetPosition",tolua_AllToLua_cEntity_SetPosition00);
+ tolua_function(tolua_S,"SetPosition",tolua_AllToLua_cEntity_SetPosition01);
+ tolua_function(tolua_S,"SetRot",tolua_AllToLua_cEntity_SetRot00);
+ tolua_function(tolua_S,"SetRotation",tolua_AllToLua_cEntity_SetRotation00);
+ tolua_function(tolua_S,"SetYaw",tolua_AllToLua_cEntity_SetYaw00);
+ tolua_function(tolua_S,"SetPitch",tolua_AllToLua_cEntity_SetPitch00);
+ tolua_function(tolua_S,"SetRoll",tolua_AllToLua_cEntity_SetRoll00);
+ tolua_function(tolua_S,"SetSpeed",tolua_AllToLua_cEntity_SetSpeed00);
+ tolua_function(tolua_S,"SetSpeed",tolua_AllToLua_cEntity_SetSpeed01);
+ tolua_function(tolua_S,"SetSpeedX",tolua_AllToLua_cEntity_SetSpeedX00);
+ tolua_function(tolua_S,"SetSpeedY",tolua_AllToLua_cEntity_SetSpeedY00);
+ tolua_function(tolua_S,"SetSpeedZ",tolua_AllToLua_cEntity_SetSpeedZ00);
+ tolua_function(tolua_S,"SetWidth",tolua_AllToLua_cEntity_SetWidth00);
+ tolua_function(tolua_S,"AddPosX",tolua_AllToLua_cEntity_AddPosX00);
+ tolua_function(tolua_S,"AddPosY",tolua_AllToLua_cEntity_AddPosY00);
+ tolua_function(tolua_S,"AddPosZ",tolua_AllToLua_cEntity_AddPosZ00);
+ tolua_function(tolua_S,"AddPosition",tolua_AllToLua_cEntity_AddPosition00);
+ tolua_function(tolua_S,"AddPosition",tolua_AllToLua_cEntity_AddPosition01);
+ tolua_function(tolua_S,"AddSpeed",tolua_AllToLua_cEntity_AddSpeed00);
+ tolua_function(tolua_S,"AddSpeed",tolua_AllToLua_cEntity_AddSpeed01);
+ tolua_function(tolua_S,"AddSpeedX",tolua_AllToLua_cEntity_AddSpeedX00);
+ tolua_function(tolua_S,"AddSpeedY",tolua_AllToLua_cEntity_AddSpeedY00);
+ tolua_function(tolua_S,"AddSpeedZ",tolua_AllToLua_cEntity_AddSpeedZ00);
+ tolua_function(tolua_S,"SteerVehicle",tolua_AllToLua_cEntity_SteerVehicle00);
+ tolua_function(tolua_S,"GetUniqueID",tolua_AllToLua_cEntity_GetUniqueID00);
+ tolua_function(tolua_S,"IsDestroyed",tolua_AllToLua_cEntity_IsDestroyed00);
+ tolua_function(tolua_S,"Destroy",tolua_AllToLua_cEntity_Destroy00);
+ tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage00);
+ tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage01);
+ tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage02);
+ tolua_function(tolua_S,"GetGravity",tolua_AllToLua_cEntity_GetGravity00);
+ tolua_function(tolua_S,"SetGravity",tolua_AllToLua_cEntity_SetGravity00);
+ tolua_function(tolua_S,"SetRotationFromSpeed",tolua_AllToLua_cEntity_SetRotationFromSpeed00);
+ tolua_function(tolua_S,"SetPitchFromSpeed",tolua_AllToLua_cEntity_SetPitchFromSpeed00);
+ tolua_function(tolua_S,"GetRawDamageAgainst",tolua_AllToLua_cEntity_GetRawDamageAgainst00);
+ tolua_function(tolua_S,"GetArmorCoverAgainst",tolua_AllToLua_cEntity_GetArmorCoverAgainst00);
+ tolua_function(tolua_S,"GetKnockbackAmountAgainst",tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00);
+ tolua_function(tolua_S,"GetEquippedWeapon",tolua_AllToLua_cEntity_GetEquippedWeapon00);
+ tolua_function(tolua_S,"GetEquippedHelmet",tolua_AllToLua_cEntity_GetEquippedHelmet00);
+ tolua_function(tolua_S,"GetEquippedChestplate",tolua_AllToLua_cEntity_GetEquippedChestplate00);
+ tolua_function(tolua_S,"GetEquippedLeggings",tolua_AllToLua_cEntity_GetEquippedLeggings00);
+ tolua_function(tolua_S,"GetEquippedBoots",tolua_AllToLua_cEntity_GetEquippedBoots00);
+ tolua_function(tolua_S,"KilledBy",tolua_AllToLua_cEntity_KilledBy00);
+ tolua_function(tolua_S,"Heal",tolua_AllToLua_cEntity_Heal00);
+ tolua_function(tolua_S,"GetHealth",tolua_AllToLua_cEntity_GetHealth00);
+ tolua_function(tolua_S,"SetHealth",tolua_AllToLua_cEntity_SetHealth00);
+ tolua_function(tolua_S,"SetMaxHealth",tolua_AllToLua_cEntity_SetMaxHealth00);
+ tolua_function(tolua_S,"GetMaxHealth",tolua_AllToLua_cEntity_GetMaxHealth00);
+ tolua_function(tolua_S,"StartBurning",tolua_AllToLua_cEntity_StartBurning00);
+ tolua_function(tolua_S,"StopBurning",tolua_AllToLua_cEntity_StopBurning00);
+ tolua_function(tolua_S,"TeleportToEntity",tolua_AllToLua_cEntity_TeleportToEntity00);
+ tolua_function(tolua_S,"TeleportToCoords",tolua_AllToLua_cEntity_TeleportToCoords00);
+ tolua_function(tolua_S,"IsOnFire",tolua_AllToLua_cEntity_IsOnFire00);
+ tolua_function(tolua_S,"IsCrouched",tolua_AllToLua_cEntity_IsCrouched00);
+ tolua_function(tolua_S,"IsRiding",tolua_AllToLua_cEntity_IsRiding00);
+ tolua_function(tolua_S,"IsSprinting",tolua_AllToLua_cEntity_IsSprinting00);
+ tolua_function(tolua_S,"IsRclking",tolua_AllToLua_cEntity_IsRclking00);
+ tolua_function(tolua_S,"IsInvisible",tolua_AllToLua_cEntity_IsInvisible00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cPawn","cPawn","cEntity",NULL);
+ tolua_beginmodule(tolua_S,"cPawn");
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cPlayer","cPlayer","cPawn",NULL);
+ tolua_beginmodule(tolua_S,"cPlayer");
+ tolua_constant(tolua_S,"MAX_HEALTH",cPlayer::MAX_HEALTH);
+ tolua_constant(tolua_S,"MAX_FOOD_LEVEL",cPlayer::MAX_FOOD_LEVEL);
+ tolua_constant(tolua_S,"EATING_TICKS",cPlayer::EATING_TICKS);
+ tolua_constant(tolua_S,"MAX_AIR_LEVEL",cPlayer::MAX_AIR_LEVEL);
+ tolua_constant(tolua_S,"DROWNING_TICKS",cPlayer::DROWNING_TICKS);
+ tolua_function(tolua_S,"SetCurrentExperience",tolua_AllToLua_cPlayer_SetCurrentExperience00);
+ tolua_function(tolua_S,"DeltaExperience",tolua_AllToLua_cPlayer_DeltaExperience00);
+ tolua_function(tolua_S,"GetXpLifetimeTotal",tolua_AllToLua_cPlayer_GetXpLifetimeTotal00);
+ tolua_function(tolua_S,"GetCurrentXp",tolua_AllToLua_cPlayer_GetCurrentXp00);
+ tolua_function(tolua_S,"GetXpLevel",tolua_AllToLua_cPlayer_GetXpLevel00);
+ tolua_function(tolua_S,"GetXpPercentage",tolua_AllToLua_cPlayer_GetXpPercentage00);
+ tolua_function(tolua_S,"XpForLevel",tolua_AllToLua_cPlayer_XpForLevel00);
+ tolua_function(tolua_S,"CalcLevelFromXp",tolua_AllToLua_cPlayer_CalcLevelFromXp00);
+ tolua_function(tolua_S,"GetEyeHeight",tolua_AllToLua_cPlayer_GetEyeHeight00);
+ tolua_function(tolua_S,"GetEyePosition",tolua_AllToLua_cPlayer_GetEyePosition00);
+ tolua_function(tolua_S,"IsOnGround",tolua_AllToLua_cPlayer_IsOnGround00);
+ tolua_function(tolua_S,"GetStance",tolua_AllToLua_cPlayer_GetStance00);
+ tolua_function(tolua_S,"GetInventory",tolua_AllToLua_cPlayer_GetInventory00);
+ tolua_function(tolua_S,"GetEquippedItem",tolua_AllToLua_cPlayer_GetEquippedItem00);
+ tolua_function(tolua_S,"GetThrowStartPos",tolua_AllToLua_cPlayer_GetThrowStartPos00);
+ tolua_function(tolua_S,"GetThrowSpeed",tolua_AllToLua_cPlayer_GetThrowSpeed00);
+ tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cPlayer_GetGameMode00);
+ tolua_function(tolua_S,"GetEffectiveGameMode",tolua_AllToLua_cPlayer_GetEffectiveGameMode00);
+ tolua_function(tolua_S,"SetGameMode",tolua_AllToLua_cPlayer_SetGameMode00);
+ tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cPlayer_IsGameModeCreative00);
+ tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cPlayer_IsGameModeSurvival00);
+ tolua_function(tolua_S,"IsGameModeAdventure",tolua_AllToLua_cPlayer_IsGameModeAdventure00);
+ tolua_function(tolua_S,"GetIP",tolua_AllToLua_cPlayer_GetIP00);
+ tolua_function(tolua_S,"MoveTo",tolua_AllToLua_cPlayer_MoveTo00);
+ tolua_function(tolua_S,"GetWindow",tolua_AllToLua_cPlayer_GetWindow00);
+ tolua_function(tolua_S,"CloseWindow",tolua_AllToLua_cPlayer_CloseWindow00);
+ tolua_function(tolua_S,"CloseWindowIfID",tolua_AllToLua_cPlayer_CloseWindowIfID00);
+ tolua_function(tolua_S,"GetClientHandle",tolua_AllToLua_cPlayer_GetClientHandle00);
+ tolua_function(tolua_S,"SendMessage",tolua_AllToLua_cPlayer_SendMessage00);
+ tolua_function(tolua_S,"GetName",tolua_AllToLua_cPlayer_GetName00);
+ tolua_function(tolua_S,"SetName",tolua_AllToLua_cPlayer_SetName00);
+ tolua_function(tolua_S,"AddToGroup",tolua_AllToLua_cPlayer_AddToGroup00);
+ tolua_function(tolua_S,"RemoveFromGroup",tolua_AllToLua_cPlayer_RemoveFromGroup00);
+ tolua_function(tolua_S,"CanUseCommand",tolua_AllToLua_cPlayer_CanUseCommand00);
+ tolua_function(tolua_S,"HasPermission",tolua_AllToLua_cPlayer_HasPermission00);
+ tolua_function(tolua_S,"IsInGroup",tolua_AllToLua_cPlayer_IsInGroup00);
+ tolua_function(tolua_S,"GetColor",tolua_AllToLua_cPlayer_GetColor00);
+ tolua_function(tolua_S,"TossItem",tolua_AllToLua_cPlayer_TossItem00);
+ tolua_function(tolua_S,"Heal",tolua_AllToLua_cPlayer_Heal00);
+ tolua_function(tolua_S,"GetFoodLevel",tolua_AllToLua_cPlayer_GetFoodLevel00);
+ tolua_function(tolua_S,"GetFoodSaturationLevel",tolua_AllToLua_cPlayer_GetFoodSaturationLevel00);
+ tolua_function(tolua_S,"GetFoodTickTimer",tolua_AllToLua_cPlayer_GetFoodTickTimer00);
+ tolua_function(tolua_S,"GetFoodExhaustionLevel",tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00);
+ tolua_function(tolua_S,"GetFoodPoisonedTicksRemaining",tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00);
+ tolua_function(tolua_S,"GetAirLevel",tolua_AllToLua_cPlayer_GetAirLevel00);
+ tolua_function(tolua_S,"IsSatiated",tolua_AllToLua_cPlayer_IsSatiated00);
+ tolua_function(tolua_S,"SetFoodLevel",tolua_AllToLua_cPlayer_SetFoodLevel00);
+ tolua_function(tolua_S,"SetFoodSaturationLevel",tolua_AllToLua_cPlayer_SetFoodSaturationLevel00);
+ tolua_function(tolua_S,"SetFoodTickTimer",tolua_AllToLua_cPlayer_SetFoodTickTimer00);
+ tolua_function(tolua_S,"SetFoodExhaustionLevel",tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00);
+ tolua_function(tolua_S,"SetFoodPoisonedTicksRemaining",tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00);
+ tolua_function(tolua_S,"Feed",tolua_AllToLua_cPlayer_Feed00);
+ tolua_function(tolua_S,"AddFoodExhaustion",tolua_AllToLua_cPlayer_AddFoodExhaustion00);
+ tolua_function(tolua_S,"FoodPoison",tolua_AllToLua_cPlayer_FoodPoison00);
+ tolua_function(tolua_S,"IsEating",tolua_AllToLua_cPlayer_IsEating00);
+ tolua_function(tolua_S,"Respawn",tolua_AllToLua_cPlayer_Respawn00);
+ tolua_function(tolua_S,"SetVisible",tolua_AllToLua_cPlayer_SetVisible00);
+ tolua_function(tolua_S,"IsVisible",tolua_AllToLua_cPlayer_IsVisible00);
+ tolua_function(tolua_S,"MoveToWorld",tolua_AllToLua_cPlayer_MoveToWorld00);
+ tolua_function(tolua_S,"LoadPermissionsFromDisk",tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00);
+ tolua_function(tolua_S,"GetMaxSpeed",tolua_AllToLua_cPlayer_GetMaxSpeed00);
+ tolua_function(tolua_S,"GetNormalMaxSpeed",tolua_AllToLua_cPlayer_GetNormalMaxSpeed00);
+ tolua_function(tolua_S,"GetSprintingMaxSpeed",tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00);
+ tolua_function(tolua_S,"SetNormalMaxSpeed",tolua_AllToLua_cPlayer_SetNormalMaxSpeed00);
+ tolua_function(tolua_S,"SetSprintingMaxSpeed",tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00);
+ tolua_function(tolua_S,"SetCrouch",tolua_AllToLua_cPlayer_SetCrouch00);
+ tolua_function(tolua_S,"SetSprint",tolua_AllToLua_cPlayer_SetSprint00);
+ tolua_function(tolua_S,"IsSwimming",tolua_AllToLua_cPlayer_IsSwimming00);
+ tolua_function(tolua_S,"IsSubmerged",tolua_AllToLua_cPlayer_IsSubmerged00);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cPickup","cPickup","cEntity",tolua_collect_cPickup);
+ #else
+ tolua_cclass(tolua_S,"cPickup","cPickup","cEntity",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cPickup");
+ tolua_function(tolua_S,"new",tolua_AllToLua_cPickup_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cPickup_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cPickup_new00_local);
+ tolua_function(tolua_S,"GetItem",tolua_AllToLua_cPickup_GetItem00);
+ tolua_function(tolua_S,"CollectedBy",tolua_AllToLua_cPickup_CollectedBy00);
+ tolua_function(tolua_S,"GetAge",tolua_AllToLua_cPickup_GetAge00);
+ tolua_function(tolua_S,"IsCollected",tolua_AllToLua_cPickup_IsCollected00);
+ tolua_function(tolua_S,"IsPlayerCreated",tolua_AllToLua_cPickup_IsPlayerCreated00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cProjectileEntity","cProjectileEntity","cEntity",NULL);
+ tolua_beginmodule(tolua_S,"cProjectileEntity");
+ tolua_constant(tolua_S,"pkArrow",cProjectileEntity::pkArrow);
+ tolua_constant(tolua_S,"pkSnowball",cProjectileEntity::pkSnowball);
+ tolua_constant(tolua_S,"pkEgg",cProjectileEntity::pkEgg);
+ tolua_constant(tolua_S,"pkGhastFireball",cProjectileEntity::pkGhastFireball);
+ tolua_constant(tolua_S,"pkFireCharge",cProjectileEntity::pkFireCharge);
+ tolua_constant(tolua_S,"pkEnderPearl",cProjectileEntity::pkEnderPearl);
+ tolua_constant(tolua_S,"pkExpBottle",cProjectileEntity::pkExpBottle);
+ tolua_constant(tolua_S,"pkSplashPotion",cProjectileEntity::pkSplashPotion);
+ tolua_constant(tolua_S,"pkFirework",cProjectileEntity::pkFirework);
+ tolua_constant(tolua_S,"pkWitherSkull",cProjectileEntity::pkWitherSkull);
+ tolua_constant(tolua_S,"pkFishingFloat",cProjectileEntity::pkFishingFloat);
+ tolua_function(tolua_S,"GetProjectileKind",tolua_AllToLua_cProjectileEntity_GetProjectileKind00);
+ tolua_function(tolua_S,"GetCreator",tolua_AllToLua_cProjectileEntity_GetCreator00);
+ tolua_function(tolua_S,"GetMCAClassName",tolua_AllToLua_cProjectileEntity_GetMCAClassName00);
+ tolua_function(tolua_S,"IsInGround",tolua_AllToLua_cProjectileEntity_IsInGround00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cArrowEntity","cArrowEntity","cProjectileEntity",NULL);
+ tolua_beginmodule(tolua_S,"cArrowEntity");
+ tolua_constant(tolua_S,"psNoPickup",cArrowEntity::psNoPickup);
+ tolua_constant(tolua_S,"psInSurvivalOrCreative",cArrowEntity::psInSurvivalOrCreative);
+ tolua_constant(tolua_S,"psInCreative",cArrowEntity::psInCreative);
+ tolua_function(tolua_S,"GetPickupState",tolua_AllToLua_cArrowEntity_GetPickupState00);
+ tolua_function(tolua_S,"SetPickupState",tolua_AllToLua_cArrowEntity_SetPickupState00);
+ tolua_function(tolua_S,"GetDamageCoeff",tolua_AllToLua_cArrowEntity_GetDamageCoeff00);
+ tolua_function(tolua_S,"SetDamageCoeff",tolua_AllToLua_cArrowEntity_SetDamageCoeff00);
+ tolua_function(tolua_S,"CanPickup",tolua_AllToLua_cArrowEntity_CanPickup00);
+ tolua_function(tolua_S,"IsCritical",tolua_AllToLua_cArrowEntity_IsCritical00);
+ tolua_function(tolua_S,"SetIsCritical",tolua_AllToLua_cArrowEntity_SetIsCritical00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cThrownEggEntity","cThrownEggEntity","cProjectileEntity",NULL);
+ tolua_beginmodule(tolua_S,"cThrownEggEntity");
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cThrownEnderPearlEntity","cThrownEnderPearlEntity","cProjectileEntity",NULL);
+ tolua_beginmodule(tolua_S,"cThrownEnderPearlEntity");
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cThrownSnowballEntity","cThrownSnowballEntity","cProjectileEntity",NULL);
+ tolua_beginmodule(tolua_S,"cThrownSnowballEntity");
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cExpBottleEntity","cExpBottleEntity","cProjectileEntity",NULL);
+ tolua_beginmodule(tolua_S,"cExpBottleEntity");
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cFireworkEntity","cFireworkEntity","cProjectileEntity",NULL);
+ tolua_beginmodule(tolua_S,"cFireworkEntity");
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cGhastFireballEntity","cGhastFireballEntity","cProjectileEntity",NULL);
+ tolua_beginmodule(tolua_S,"cGhastFireballEntity");
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cFireChargeEntity","cFireChargeEntity","cProjectileEntity",NULL);
+ tolua_beginmodule(tolua_S,"cFireChargeEntity");
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cPluginManager","cPluginManager","",NULL);
+ tolua_beginmodule(tolua_S,"cPluginManager");
+ tolua_constant(tolua_S,"HOOK_BLOCK_TO_PICKUPS",cPluginManager::HOOK_BLOCK_TO_PICKUPS);
+ tolua_constant(tolua_S,"HOOK_CHAT",cPluginManager::HOOK_CHAT);
+ tolua_constant(tolua_S,"HOOK_CHUNK_AVAILABLE",cPluginManager::HOOK_CHUNK_AVAILABLE);
+ tolua_constant(tolua_S,"HOOK_CHUNK_GENERATED",cPluginManager::HOOK_CHUNK_GENERATED);
+ tolua_constant(tolua_S,"HOOK_CHUNK_GENERATING",cPluginManager::HOOK_CHUNK_GENERATING);
+ tolua_constant(tolua_S,"HOOK_CHUNK_UNLOADED",cPluginManager::HOOK_CHUNK_UNLOADED);
+ tolua_constant(tolua_S,"HOOK_CHUNK_UNLOADING",cPluginManager::HOOK_CHUNK_UNLOADING);
+ tolua_constant(tolua_S,"HOOK_COLLECTING_PICKUP",cPluginManager::HOOK_COLLECTING_PICKUP);
+ tolua_constant(tolua_S,"HOOK_CRAFTING_NO_RECIPE",cPluginManager::HOOK_CRAFTING_NO_RECIPE);
+ tolua_constant(tolua_S,"HOOK_DISCONNECT",cPluginManager::HOOK_DISCONNECT);
+ tolua_constant(tolua_S,"HOOK_EXECUTE_COMMAND",cPluginManager::HOOK_EXECUTE_COMMAND);
+ tolua_constant(tolua_S,"HOOK_EXPLODED",cPluginManager::HOOK_EXPLODED);
+ tolua_constant(tolua_S,"HOOK_EXPLODING",cPluginManager::HOOK_EXPLODING);
+ tolua_constant(tolua_S,"HOOK_HANDSHAKE",cPluginManager::HOOK_HANDSHAKE);
+ tolua_constant(tolua_S,"HOOK_HOPPER_PULLING_ITEM",cPluginManager::HOOK_HOPPER_PULLING_ITEM);
+ tolua_constant(tolua_S,"HOOK_HOPPER_PUSHING_ITEM",cPluginManager::HOOK_HOPPER_PUSHING_ITEM);
+ tolua_constant(tolua_S,"HOOK_KILLING",cPluginManager::HOOK_KILLING);
+ tolua_constant(tolua_S,"HOOK_LOGIN",cPluginManager::HOOK_LOGIN);
+ tolua_constant(tolua_S,"HOOK_PLAYER_ANIMATION",cPluginManager::HOOK_PLAYER_ANIMATION);
+ tolua_constant(tolua_S,"HOOK_PLAYER_BREAKING_BLOCK",cPluginManager::HOOK_PLAYER_BREAKING_BLOCK);
+ tolua_constant(tolua_S,"HOOK_PLAYER_BROKEN_BLOCK",cPluginManager::HOOK_PLAYER_BROKEN_BLOCK);
+ tolua_constant(tolua_S,"HOOK_PLAYER_EATING",cPluginManager::HOOK_PLAYER_EATING);
+ tolua_constant(tolua_S,"HOOK_PLAYER_JOINED",cPluginManager::HOOK_PLAYER_JOINED);
+ tolua_constant(tolua_S,"HOOK_PLAYER_LEFT_CLICK",cPluginManager::HOOK_PLAYER_LEFT_CLICK);
+ tolua_constant(tolua_S,"HOOK_PLAYER_MOVING",cPluginManager::HOOK_PLAYER_MOVING);
+ tolua_constant(tolua_S,"HOOK_PLAYER_PLACED_BLOCK",cPluginManager::HOOK_PLAYER_PLACED_BLOCK);
+ tolua_constant(tolua_S,"HOOK_PLAYER_PLACING_BLOCK",cPluginManager::HOOK_PLAYER_PLACING_BLOCK);
+ tolua_constant(tolua_S,"HOOK_PLAYER_RIGHT_CLICK",cPluginManager::HOOK_PLAYER_RIGHT_CLICK);
+ tolua_constant(tolua_S,"HOOK_PLAYER_RIGHT_CLICKING_ENTITY",cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY);
+ tolua_constant(tolua_S,"HOOK_PLAYER_SHOOTING",cPluginManager::HOOK_PLAYER_SHOOTING);
+ tolua_constant(tolua_S,"HOOK_PLAYER_SPAWNED",cPluginManager::HOOK_PLAYER_SPAWNED);
+ tolua_constant(tolua_S,"HOOK_PLAYER_TOSSING_ITEM",cPluginManager::HOOK_PLAYER_TOSSING_ITEM);
+ tolua_constant(tolua_S,"HOOK_PLAYER_USED_BLOCK",cPluginManager::HOOK_PLAYER_USED_BLOCK);
+ tolua_constant(tolua_S,"HOOK_PLAYER_USED_ITEM",cPluginManager::HOOK_PLAYER_USED_ITEM);
+ tolua_constant(tolua_S,"HOOK_PLAYER_USING_BLOCK",cPluginManager::HOOK_PLAYER_USING_BLOCK);
+ tolua_constant(tolua_S,"HOOK_PLAYER_USING_ITEM",cPluginManager::HOOK_PLAYER_USING_ITEM);
+ tolua_constant(tolua_S,"HOOK_POST_CRAFTING",cPluginManager::HOOK_POST_CRAFTING);
+ tolua_constant(tolua_S,"HOOK_PRE_CRAFTING",cPluginManager::HOOK_PRE_CRAFTING);
+ tolua_constant(tolua_S,"HOOK_SPAWNED_ENTITY",cPluginManager::HOOK_SPAWNED_ENTITY);
+ tolua_constant(tolua_S,"HOOK_SPAWNED_MONSTER",cPluginManager::HOOK_SPAWNED_MONSTER);
+ tolua_constant(tolua_S,"HOOK_SPAWNING_ENTITY",cPluginManager::HOOK_SPAWNING_ENTITY);
+ tolua_constant(tolua_S,"HOOK_SPAWNING_MONSTER",cPluginManager::HOOK_SPAWNING_MONSTER);
+ tolua_constant(tolua_S,"HOOK_TAKE_DAMAGE",cPluginManager::HOOK_TAKE_DAMAGE);
+ tolua_constant(tolua_S,"HOOK_TICK",cPluginManager::HOOK_TICK);
+ tolua_constant(tolua_S,"HOOK_UPDATED_SIGN",cPluginManager::HOOK_UPDATED_SIGN);
+ tolua_constant(tolua_S,"HOOK_UPDATING_SIGN",cPluginManager::HOOK_UPDATING_SIGN);
+ tolua_constant(tolua_S,"HOOK_WEATHER_CHANGED",cPluginManager::HOOK_WEATHER_CHANGED);
+ tolua_constant(tolua_S,"HOOK_WEATHER_CHANGING",cPluginManager::HOOK_WEATHER_CHANGING);
+ tolua_constant(tolua_S,"HOOK_WORLD_TICK",cPluginManager::HOOK_WORLD_TICK);
+ tolua_constant(tolua_S,"HOOK_NUM_HOOKS",cPluginManager::HOOK_NUM_HOOKS);
+ tolua_constant(tolua_S,"HOOK_MAX",cPluginManager::HOOK_MAX);
+ tolua_function(tolua_S,"Get",tolua_AllToLua_cPluginManager_Get00);
+ tolua_function(tolua_S,"GetPlugin",tolua_AllToLua_cPluginManager_GetPlugin00);
+ tolua_function(tolua_S,"FindPlugins",tolua_AllToLua_cPluginManager_FindPlugins00);
+ tolua_function(tolua_S,"ReloadPlugins",tolua_AllToLua_cPluginManager_ReloadPlugins00);
+ tolua_function(tolua_S,"GetNumPlugins",tolua_AllToLua_cPluginManager_GetNumPlugins00);
+ tolua_function(tolua_S,"DisablePlugin",tolua_AllToLua_cPluginManager_DisablePlugin00);
+ tolua_function(tolua_S,"LoadPlugin",tolua_AllToLua_cPluginManager_LoadPlugin00);
+ tolua_function(tolua_S,"IsCommandBound",tolua_AllToLua_cPluginManager_IsCommandBound00);
+ tolua_function(tolua_S,"GetCommandPermission",tolua_AllToLua_cPluginManager_GetCommandPermission00);
+ tolua_function(tolua_S,"ExecuteCommand",tolua_AllToLua_cPluginManager_ExecuteCommand00);
+ tolua_function(tolua_S,"ForceExecuteCommand",tolua_AllToLua_cPluginManager_ForceExecuteCommand00);
+ tolua_function(tolua_S,"IsConsoleCommandBound",tolua_AllToLua_cPluginManager_IsConsoleCommandBound00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cPlugin","cPlugin","",NULL);
+ tolua_beginmodule(tolua_S,"cPlugin");
+ tolua_function(tolua_S,"GetName",tolua_AllToLua_cPlugin_GetName00);
+ tolua_function(tolua_S,"SetName",tolua_AllToLua_cPlugin_SetName00);
+ tolua_function(tolua_S,"GetVersion",tolua_AllToLua_cPlugin_GetVersion00);
+ tolua_function(tolua_S,"SetVersion",tolua_AllToLua_cPlugin_SetVersion00);
+ tolua_function(tolua_S,"GetDirectory",tolua_AllToLua_cPlugin_GetDirectory00);
+ tolua_function(tolua_S,"GetLocalDirectory",tolua_AllToLua_cPlugin_GetLocalDirectory00);
+ tolua_function(tolua_S,"GetLocalFolder",tolua_AllToLua_cPlugin_GetLocalFolder00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cPluginLua","cPluginLua","cPlugin",NULL);
+ tolua_beginmodule(tolua_S,"cPluginLua");
+ tolua_variable(tolua_S,"__cWebPlugin__",tolua_get_cPluginLua___cWebPlugin__,NULL);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cServer","cServer","",NULL);
+ tolua_beginmodule(tolua_S,"cServer");
+ tolua_function(tolua_S,"GetDescription",tolua_AllToLua_cServer_GetDescription00);
+ tolua_function(tolua_S,"GetMaxPlayers",tolua_AllToLua_cServer_GetMaxPlayers00);
+ tolua_function(tolua_S,"GetNumPlayers",tolua_AllToLua_cServer_GetNumPlayers00);
+ tolua_function(tolua_S,"SetMaxPlayers",tolua_AllToLua_cServer_SetMaxPlayers00);
+ tolua_function(tolua_S,"IsHardcore",tolua_AllToLua_cServer_IsHardcore00);
+ tolua_function(tolua_S,"GetServerID",tolua_AllToLua_cServer_GetServerID00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cWorld","cWorld","",NULL);
+ tolua_beginmodule(tolua_S,"cWorld");
+ tolua_function(tolua_S,"GetTicksUntilWeatherChange",tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00);
+ tolua_function(tolua_S,"GetWorldAge",tolua_AllToLua_cWorld_GetWorldAge00);
+ tolua_function(tolua_S,"GetTimeOfDay",tolua_AllToLua_cWorld_GetTimeOfDay00);
+ tolua_function(tolua_S,"SetTicksUntilWeatherChange",tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00);
+ tolua_function(tolua_S,"SetTimeOfDay",tolua_AllToLua_cWorld_SetTimeOfDay00);
+ tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cWorld_GetGameMode00);
+ tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cWorld_IsGameModeCreative00);
+ tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cWorld_IsGameModeSurvival00);
+ tolua_function(tolua_S,"IsGameModeAdventure",tolua_AllToLua_cWorld_IsGameModeAdventure00);
+ tolua_function(tolua_S,"IsPVPEnabled",tolua_AllToLua_cWorld_IsPVPEnabled00);
+ tolua_function(tolua_S,"IsDeepSnowEnabled",tolua_AllToLua_cWorld_IsDeepSnowEnabled00);
+ tolua_function(tolua_S,"GetDimension",tolua_AllToLua_cWorld_GetDimension00);
+ tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cWorld_GetHeight00);
+ tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cWorld_BroadcastChat00);
+ tolua_function(tolua_S,"BroadcastSoundEffect",tolua_AllToLua_cWorld_BroadcastSoundEffect00);
+ tolua_function(tolua_S,"BroadcastSoundParticleEffect",tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00);
+ tolua_function(tolua_S,"UnloadUnusedChunks",tolua_AllToLua_cWorld_UnloadUnusedChunks00);
+ tolua_function(tolua_S,"RegenerateChunk",tolua_AllToLua_cWorld_RegenerateChunk00);
+ tolua_function(tolua_S,"GenerateChunk",tolua_AllToLua_cWorld_GenerateChunk00);
+ tolua_function(tolua_S,"SetBlock",tolua_AllToLua_cWorld_SetBlock00);
+ tolua_function(tolua_S,"FastSetBlock",tolua_AllToLua_cWorld_FastSetBlock00);
+ tolua_function(tolua_S,"QueueSetBlock",tolua_AllToLua_cWorld_QueueSetBlock00);
+ tolua_function(tolua_S,"GetBlock",tolua_AllToLua_cWorld_GetBlock00);
+ tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cWorld_GetBlockMeta00);
+ tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cWorld_SetBlockMeta00);
+ tolua_function(tolua_S,"GetBlockSkyLight",tolua_AllToLua_cWorld_GetBlockSkyLight00);
+ tolua_function(tolua_S,"GetBlockBlockLight",tolua_AllToLua_cWorld_GetBlockBlockLight00);
+ tolua_function(tolua_S,"FastSetBlock",tolua_AllToLua_cWorld_FastSetBlock01);
+ tolua_function(tolua_S,"GetBlock",tolua_AllToLua_cWorld_GetBlock01);
+ tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cWorld_GetBlockMeta01);
+ tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cWorld_SetBlockMeta01);
+ tolua_function(tolua_S,"SpawnItemPickups",tolua_AllToLua_cWorld_SpawnItemPickups00);
+ tolua_function(tolua_S,"SpawnItemPickups",tolua_AllToLua_cWorld_SpawnItemPickups01);
+ tolua_function(tolua_S,"SpawnPrimedTNT",tolua_AllToLua_cWorld_SpawnPrimedTNT00);
+ tolua_function(tolua_S,"DigBlock",tolua_AllToLua_cWorld_DigBlock00);
+ tolua_function(tolua_S,"SendBlockTo",tolua_AllToLua_cWorld_SendBlockTo00);
+ tolua_function(tolua_S,"GetSpawnX",tolua_AllToLua_cWorld_GetSpawnX00);
+ tolua_function(tolua_S,"GetSpawnY",tolua_AllToLua_cWorld_GetSpawnY00);
+ tolua_function(tolua_S,"GetSpawnZ",tolua_AllToLua_cWorld_GetSpawnZ00);
+ tolua_function(tolua_S,"WakeUpSimulators",tolua_AllToLua_cWorld_WakeUpSimulators00);
+ tolua_function(tolua_S,"WakeUpSimulatorsInArea",tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00);
+ tolua_function(tolua_S,"DoExplosionAt",tolua_AllToLua_cWorld_DoExplosionAt00);
+ tolua_function(tolua_S,"UseBlockEntity",tolua_AllToLua_cWorld_UseBlockEntity00);
+ tolua_function(tolua_S,"GrowTree",tolua_AllToLua_cWorld_GrowTree00);
+ tolua_function(tolua_S,"GrowTreeFromSapling",tolua_AllToLua_cWorld_GrowTreeFromSapling00);
+ tolua_function(tolua_S,"GrowTreeByBiome",tolua_AllToLua_cWorld_GrowTreeByBiome00);
+ tolua_function(tolua_S,"GrowRipePlant",tolua_AllToLua_cWorld_GrowRipePlant00);
+ tolua_function(tolua_S,"GrowCactus",tolua_AllToLua_cWorld_GrowCactus00);
+ tolua_function(tolua_S,"GrowMelonPumpkin",tolua_AllToLua_cWorld_GrowMelonPumpkin00);
+ tolua_function(tolua_S,"GrowSugarcane",tolua_AllToLua_cWorld_GrowSugarcane00);
+ tolua_function(tolua_S,"GetBiomeAt",tolua_AllToLua_cWorld_GetBiomeAt00);
+ tolua_function(tolua_S,"GetName",tolua_AllToLua_cWorld_GetName00);
+ tolua_function(tolua_S,"GetIniFileName",tolua_AllToLua_cWorld_GetIniFileName00);
+ tolua_function(tolua_S,"QueueSaveAllChunks",tolua_AllToLua_cWorld_QueueSaveAllChunks00);
+ tolua_function(tolua_S,"GetNumChunks",tolua_AllToLua_cWorld_GetNumChunks00);
+ tolua_function(tolua_S,"GetGeneratorQueueLength",tolua_AllToLua_cWorld_GetGeneratorQueueLength00);
+ tolua_function(tolua_S,"GetLightingQueueLength",tolua_AllToLua_cWorld_GetLightingQueueLength00);
+ tolua_function(tolua_S,"GetStorageLoadQueueLength",tolua_AllToLua_cWorld_GetStorageLoadQueueLength00);
+ tolua_function(tolua_S,"GetStorageSaveQueueLength",tolua_AllToLua_cWorld_GetStorageSaveQueueLength00);
+ tolua_function(tolua_S,"QueueBlockForTick",tolua_AllToLua_cWorld_QueueBlockForTick00);
+ tolua_function(tolua_S,"CastThunderbolt",tolua_AllToLua_cWorld_CastThunderbolt00);
+ tolua_function(tolua_S,"SetWeather",tolua_AllToLua_cWorld_SetWeather00);
+ tolua_function(tolua_S,"ChangeWeather",tolua_AllToLua_cWorld_ChangeWeather00);
+ tolua_function(tolua_S,"GetWeather",tolua_AllToLua_cWorld_GetWeather00);
+ tolua_function(tolua_S,"IsWeatherSunny",tolua_AllToLua_cWorld_IsWeatherSunny00);
+ tolua_function(tolua_S,"IsWeatherRain",tolua_AllToLua_cWorld_IsWeatherRain00);
+ tolua_function(tolua_S,"IsWeatherStorm",tolua_AllToLua_cWorld_IsWeatherStorm00);
+ tolua_function(tolua_S,"IsWeatherWet",tolua_AllToLua_cWorld_IsWeatherWet00);
+ tolua_function(tolua_S,"SetNextBlockTick",tolua_AllToLua_cWorld_SetNextBlockTick00);
+ tolua_function(tolua_S,"GetMaxSugarcaneHeight",tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00);
+ tolua_function(tolua_S,"GetMaxCactusHeight",tolua_AllToLua_cWorld_GetMaxCactusHeight00);
+ tolua_function(tolua_S,"IsBlockDirectlyWatered",tolua_AllToLua_cWorld_IsBlockDirectlyWatered00);
+ tolua_function(tolua_S,"SpawnMob",tolua_AllToLua_cWorld_SpawnMob00);
+ tolua_function(tolua_S,"CreateProjectile",tolua_AllToLua_cWorld_CreateProjectile00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cInventory","cInventory","cItemGrid::cListener",NULL);
+ tolua_beginmodule(tolua_S,"cInventory");
+ tolua_constant(tolua_S,"invArmorCount",cInventory::invArmorCount);
+ tolua_constant(tolua_S,"invInventoryCount",cInventory::invInventoryCount);
+ tolua_constant(tolua_S,"invHotbarCount",cInventory::invHotbarCount);
+ tolua_constant(tolua_S,"invArmorOffset",cInventory::invArmorOffset);
+ tolua_constant(tolua_S,"invInventoryOffset",cInventory::invInventoryOffset);
+ tolua_constant(tolua_S,"invHotbarOffset",cInventory::invHotbarOffset);
+ tolua_constant(tolua_S,"invNumSlots",cInventory::invNumSlots);
+ tolua_function(tolua_S,"Clear",tolua_AllToLua_cInventory_Clear00);
+ tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cInventory_HowManyCanFit00);
+ tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cInventory_HowManyCanFit01);
+ tolua_function(tolua_S,"AddItem",tolua_AllToLua_cInventory_AddItem00);
+ tolua_function(tolua_S,"AddItems",tolua_AllToLua_cInventory_AddItems00);
+ tolua_function(tolua_S,"RemoveOneEquippedItem",tolua_AllToLua_cInventory_RemoveOneEquippedItem00);
+ tolua_function(tolua_S,"HowManyItems",tolua_AllToLua_cInventory_HowManyItems00);
+ tolua_function(tolua_S,"HasItems",tolua_AllToLua_cInventory_HasItems00);
+ tolua_function(tolua_S,"GetArmorGrid",tolua_AllToLua_cInventory_GetArmorGrid00);
+ tolua_function(tolua_S,"GetInventoryGrid",tolua_AllToLua_cInventory_GetInventoryGrid00);
+ tolua_function(tolua_S,"GetHotbarGrid",tolua_AllToLua_cInventory_GetHotbarGrid00);
+ tolua_function(tolua_S,"GetOwner",tolua_AllToLua_cInventory_GetOwner00);
+ tolua_function(tolua_S,"CopyToItems",tolua_AllToLua_cInventory_CopyToItems00);
+ tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cInventory_GetSlot00);
+ tolua_function(tolua_S,"GetArmorSlot",tolua_AllToLua_cInventory_GetArmorSlot00);
+ tolua_function(tolua_S,"GetInventorySlot",tolua_AllToLua_cInventory_GetInventorySlot00);
+ tolua_function(tolua_S,"GetHotbarSlot",tolua_AllToLua_cInventory_GetHotbarSlot00);
+ tolua_function(tolua_S,"GetEquippedItem",tolua_AllToLua_cInventory_GetEquippedItem00);
+ tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cInventory_SetSlot00);
+ tolua_function(tolua_S,"SetArmorSlot",tolua_AllToLua_cInventory_SetArmorSlot00);
+ tolua_function(tolua_S,"SetInventorySlot",tolua_AllToLua_cInventory_SetInventorySlot00);
+ tolua_function(tolua_S,"SetHotbarSlot",tolua_AllToLua_cInventory_SetHotbarSlot00);
+ tolua_function(tolua_S,"SetEquippedSlotNum",tolua_AllToLua_cInventory_SetEquippedSlotNum00);
+ tolua_function(tolua_S,"GetEquippedSlotNum",tolua_AllToLua_cInventory_GetEquippedSlotNum00);
+ tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cInventory_ChangeSlotCount00);
+ tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cInventory_DamageItem00);
+ tolua_function(tolua_S,"DamageEquippedItem",tolua_AllToLua_cInventory_DamageEquippedItem00);
+ tolua_function(tolua_S,"GetEquippedHelmet",tolua_AllToLua_cInventory_GetEquippedHelmet00);
+ tolua_function(tolua_S,"GetEquippedChestplate",tolua_AllToLua_cInventory_GetEquippedChestplate00);
+ tolua_function(tolua_S,"GetEquippedLeggings",tolua_AllToLua_cInventory_GetEquippedLeggings00);
+ tolua_function(tolua_S,"GetEquippedBoots",tolua_AllToLua_cInventory_GetEquippedBoots00);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cEnchantments","cEnchantments","",tolua_collect_cEnchantments);
+ #else
+ tolua_cclass(tolua_S,"cEnchantments","cEnchantments","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cEnchantments");
+ tolua_constant(tolua_S,"enchProtection",cEnchantments::enchProtection);
+ tolua_constant(tolua_S,"enchFireProtection",cEnchantments::enchFireProtection);
+ tolua_constant(tolua_S,"enchFeatherFalling",cEnchantments::enchFeatherFalling);
+ tolua_constant(tolua_S,"enchBlastProtection",cEnchantments::enchBlastProtection);
+ tolua_constant(tolua_S,"enchProjectileProtection",cEnchantments::enchProjectileProtection);
+ tolua_constant(tolua_S,"enchRespiration",cEnchantments::enchRespiration);
+ tolua_constant(tolua_S,"enchAquaAffinity",cEnchantments::enchAquaAffinity);
+ tolua_constant(tolua_S,"enchThorns",cEnchantments::enchThorns);
+ tolua_constant(tolua_S,"enchSharpness",cEnchantments::enchSharpness);
+ tolua_constant(tolua_S,"enchSmite",cEnchantments::enchSmite);
+ tolua_constant(tolua_S,"enchBaneOfArthropods",cEnchantments::enchBaneOfArthropods);
+ tolua_constant(tolua_S,"enchKnockback",cEnchantments::enchKnockback);
+ tolua_constant(tolua_S,"enchFireAspect",cEnchantments::enchFireAspect);
+ tolua_constant(tolua_S,"enchLooting",cEnchantments::enchLooting);
+ tolua_constant(tolua_S,"enchEfficiency",cEnchantments::enchEfficiency);
+ tolua_constant(tolua_S,"enchSilkTouch",cEnchantments::enchSilkTouch);
+ tolua_constant(tolua_S,"enchUnbreaking",cEnchantments::enchUnbreaking);
+ tolua_constant(tolua_S,"enchFortune",cEnchantments::enchFortune);
+ tolua_constant(tolua_S,"enchPower",cEnchantments::enchPower);
+ tolua_constant(tolua_S,"enchPunch",cEnchantments::enchPunch);
+ tolua_constant(tolua_S,"enchFlame",cEnchantments::enchFlame);
+ tolua_constant(tolua_S,"enchInfinity",cEnchantments::enchInfinity);
+ tolua_constant(tolua_S,"enchLuckOfTheSea",cEnchantments::enchLuckOfTheSea);
+ tolua_constant(tolua_S,"enchLure",cEnchantments::enchLure);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cEnchantments_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cEnchantments_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cEnchantments_new00_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cEnchantments_new01);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cEnchantments_new01_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cEnchantments_new01_local);
+ tolua_function(tolua_S,"AddFromString",tolua_AllToLua_cEnchantments_AddFromString00);
+ tolua_function(tolua_S,"ToString",tolua_AllToLua_cEnchantments_ToString00);
+ tolua_function(tolua_S,"GetLevel",tolua_AllToLua_cEnchantments_GetLevel00);
+ tolua_function(tolua_S,"SetLevel",tolua_AllToLua_cEnchantments_SetLevel00);
+ tolua_function(tolua_S,"Clear",tolua_AllToLua_cEnchantments_Clear00);
+ tolua_function(tolua_S,"IsEmpty",tolua_AllToLua_cEnchantments_IsEmpty00);
+ tolua_function(tolua_S,"StringToEnchantmentID",tolua_AllToLua_cEnchantments_StringToEnchantmentID00);
+ tolua_function(tolua_S,".eq",tolua_AllToLua_cEnchantments__eq00);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cItem","cItem","",tolua_collect_cItem);
+ #else
+ tolua_cclass(tolua_S,"cItem","cItem","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cItem");
+ tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new00_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new01);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new01_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new01_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new02);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new02_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new02_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new03);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new03_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new03_local);
+ tolua_function(tolua_S,"Empty",tolua_AllToLua_cItem_Empty00);
+ tolua_function(tolua_S,"Clear",tolua_AllToLua_cItem_Clear00);
+ tolua_function(tolua_S,"IsEmpty",tolua_AllToLua_cItem_IsEmpty00);
+ tolua_function(tolua_S,"IsEqual",tolua_AllToLua_cItem_IsEqual00);
+ tolua_function(tolua_S,"IsSameType",tolua_AllToLua_cItem_IsSameType00);
+ tolua_function(tolua_S,"CopyOne",tolua_AllToLua_cItem_CopyOne00);
+ tolua_function(tolua_S,"AddCount",tolua_AllToLua_cItem_AddCount00);
+ tolua_function(tolua_S,"GetMaxDamage",tolua_AllToLua_cItem_GetMaxDamage00);
+ tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItem_DamageItem00);
+ tolua_function(tolua_S,"IsDamageable",tolua_AllToLua_cItem_IsDamageable00);
+ tolua_function(tolua_S,"IsStackableWith",tolua_AllToLua_cItem_IsStackableWith00);
+ tolua_function(tolua_S,"IsFullStack",tolua_AllToLua_cItem_IsFullStack00);
+ tolua_function(tolua_S,"GetMaxStackSize",tolua_AllToLua_cItem_GetMaxStackSize00);
+ tolua_variable(tolua_S,"m_ItemType",tolua_get_cItem_m_ItemType,tolua_set_cItem_m_ItemType);
+ tolua_variable(tolua_S,"m_ItemCount",tolua_get_cItem_m_ItemCount,tolua_set_cItem_m_ItemCount);
+ tolua_variable(tolua_S,"m_ItemDamage",tolua_get_cItem_m_ItemDamage,tolua_set_cItem_m_ItemDamage);
+ tolua_variable(tolua_S,"m_Enchantments",tolua_get_cItem_m_Enchantments,tolua_set_cItem_m_Enchantments);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cItems","cItems","",tolua_collect_cItems);
+ #else
+ tolua_cclass(tolua_S,"cItems","cItems","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cItems");
+ tolua_function(tolua_S,"new",tolua_AllToLua_cItems_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cItems_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cItems_new00_local);
+ tolua_function(tolua_S,"Get",tolua_AllToLua_cItems_Get00);
+ tolua_function(tolua_S,"Set",tolua_AllToLua_cItems_Set00);
+ tolua_function(tolua_S,"Add",tolua_AllToLua_cItems_Add00);
+ tolua_function(tolua_S,"Delete",tolua_AllToLua_cItems_Delete00);
+ tolua_function(tolua_S,"Clear",tolua_AllToLua_cItems_Clear00);
+ tolua_function(tolua_S,"Size",tolua_AllToLua_cItems_Size00);
+ tolua_function(tolua_S,"Set",tolua_AllToLua_cItems_Set01);
+ tolua_function(tolua_S,"Add",tolua_AllToLua_cItems_Add01);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cItemGrid","cItemGrid","",NULL);
+ tolua_beginmodule(tolua_S,"cItemGrid");
+ tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cItemGrid_GetWidth00);
+ tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cItemGrid_GetHeight00);
+ tolua_function(tolua_S,"GetNumSlots",tolua_AllToLua_cItemGrid_GetNumSlots00);
+ tolua_function(tolua_S,"GetSlotNum",tolua_AllToLua_cItemGrid_GetSlotNum00);
+ tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cItemGrid_GetSlot00);
+ tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cItemGrid_GetSlot01);
+ tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot00);
+ tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot01);
+ tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot02);
+ tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot03);
+ tolua_function(tolua_S,"EmptySlot",tolua_AllToLua_cItemGrid_EmptySlot00);
+ tolua_function(tolua_S,"EmptySlot",tolua_AllToLua_cItemGrid_EmptySlot01);
+ tolua_function(tolua_S,"IsSlotEmpty",tolua_AllToLua_cItemGrid_IsSlotEmpty00);
+ tolua_function(tolua_S,"IsSlotEmpty",tolua_AllToLua_cItemGrid_IsSlotEmpty01);
+ tolua_function(tolua_S,"Clear",tolua_AllToLua_cItemGrid_Clear00);
+ tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cItemGrid_HowManyCanFit00);
+ tolua_function(tolua_S,"AddItem",tolua_AllToLua_cItemGrid_AddItem00);
+ tolua_function(tolua_S,"AddItems",tolua_AllToLua_cItemGrid_AddItems00);
+ tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cItemGrid_ChangeSlotCount00);
+ tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cItemGrid_ChangeSlotCount01);
+ tolua_function(tolua_S,"RemoveOneItem",tolua_AllToLua_cItemGrid_RemoveOneItem00);
+ tolua_function(tolua_S,"RemoveOneItem",tolua_AllToLua_cItemGrid_RemoveOneItem01);
+ tolua_function(tolua_S,"HowManyItems",tolua_AllToLua_cItemGrid_HowManyItems00);
+ tolua_function(tolua_S,"HasItems",tolua_AllToLua_cItemGrid_HasItems00);
+ tolua_function(tolua_S,"GetFirstEmptySlot",tolua_AllToLua_cItemGrid_GetFirstEmptySlot00);
+ tolua_function(tolua_S,"GetFirstUsedSlot",tolua_AllToLua_cItemGrid_GetFirstUsedSlot00);
+ tolua_function(tolua_S,"GetLastEmptySlot",tolua_AllToLua_cItemGrid_GetLastEmptySlot00);
+ tolua_function(tolua_S,"GetLastUsedSlot",tolua_AllToLua_cItemGrid_GetLastUsedSlot00);
+ tolua_function(tolua_S,"GetNextEmptySlot",tolua_AllToLua_cItemGrid_GetNextEmptySlot00);
+ tolua_function(tolua_S,"GetNextUsedSlot",tolua_AllToLua_cItemGrid_GetNextUsedSlot00);
+ tolua_function(tolua_S,"CopyToItems",tolua_AllToLua_cItemGrid_CopyToItems00);
+ tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItemGrid_DamageItem00);
+ tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItemGrid_DamageItem01);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cBlockEntity","cBlockEntity","",tolua_collect_cBlockEntity);
+ #else
+ tolua_cclass(tolua_S,"cBlockEntity","cBlockEntity","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cBlockEntity");
+ tolua_function(tolua_S,"GetPosX",tolua_AllToLua_cBlockEntity_GetPosX00);
+ tolua_function(tolua_S,"GetPosY",tolua_AllToLua_cBlockEntity_GetPosY00);
+ tolua_function(tolua_S,"GetPosZ",tolua_AllToLua_cBlockEntity_GetPosZ00);
+ tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cBlockEntity_GetBlockType00);
+ tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cBlockEntity_GetWorld00);
+ tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cBlockEntity_GetChunkX00);
+ tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cBlockEntity_GetChunkZ00);
+ tolua_function(tolua_S,"GetRelX",tolua_AllToLua_cBlockEntity_GetRelX00);
+ tolua_function(tolua_S,"GetRelZ",tolua_AllToLua_cBlockEntity_GetRelZ00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cBlockEntityWithItems","cBlockEntityWithItems","cBlockEntity",NULL);
+ tolua_beginmodule(tolua_S,"cBlockEntityWithItems");
+ tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cBlockEntityWithItems_GetSlot00);
+ tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cBlockEntityWithItems_GetSlot01);
+ tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cBlockEntityWithItems_SetSlot00);
+ tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cBlockEntityWithItems_SetSlot01);
+ tolua_function(tolua_S,"GetContents",tolua_AllToLua_cBlockEntityWithItems_GetContents00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cChestEntity","cChestEntity","cBlockEntityWithItems",NULL);
+ tolua_beginmodule(tolua_S,"cChestEntity");
+ tolua_constant(tolua_S,"ContentsHeight",cChestEntity::ContentsHeight);
+ tolua_constant(tolua_S,"ContentsWidth",cChestEntity::ContentsWidth);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cDropSpenserEntity","cDropSpenserEntity","cBlockEntityWithItems",NULL);
+ tolua_beginmodule(tolua_S,"cDropSpenserEntity");
+ tolua_constant(tolua_S,"ContentsHeight",cDropSpenserEntity::ContentsHeight);
+ tolua_constant(tolua_S,"ContentsWidth",cDropSpenserEntity::ContentsWidth);
+ tolua_function(tolua_S,"AddDropSpenserDir",tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00);
+ tolua_function(tolua_S,"Activate",tolua_AllToLua_cDropSpenserEntity_Activate00);
+ tolua_function(tolua_S,"SetRedstonePower",tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cDispenserEntity","cDispenserEntity","cDropSpenserEntity",NULL);
+ tolua_beginmodule(tolua_S,"cDispenserEntity");
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cDropperEntity","cDropperEntity","cDropSpenserEntity",NULL);
+ tolua_beginmodule(tolua_S,"cDropperEntity");
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cFurnaceEntity","cFurnaceEntity","cBlockEntityWithItems",NULL);
+ tolua_beginmodule(tolua_S,"cFurnaceEntity");
+ tolua_constant(tolua_S,"fsInput",cFurnaceEntity::fsInput);
+ tolua_constant(tolua_S,"fsFuel",cFurnaceEntity::fsFuel);
+ tolua_constant(tolua_S,"fsOutput",cFurnaceEntity::fsOutput);
+ tolua_constant(tolua_S,"ContentsWidth",cFurnaceEntity::ContentsWidth);
+ tolua_constant(tolua_S,"ContentsHeight",cFurnaceEntity::ContentsHeight);
+ tolua_function(tolua_S,"GetInputSlot",tolua_AllToLua_cFurnaceEntity_GetInputSlot00);
+ tolua_function(tolua_S,"GetFuelSlot",tolua_AllToLua_cFurnaceEntity_GetFuelSlot00);
+ tolua_function(tolua_S,"GetOutputSlot",tolua_AllToLua_cFurnaceEntity_GetOutputSlot00);
+ tolua_function(tolua_S,"SetInputSlot",tolua_AllToLua_cFurnaceEntity_SetInputSlot00);
+ tolua_function(tolua_S,"SetFuelSlot",tolua_AllToLua_cFurnaceEntity_SetFuelSlot00);
+ tolua_function(tolua_S,"SetOutputSlot",tolua_AllToLua_cFurnaceEntity_SetOutputSlot00);
+ tolua_function(tolua_S,"GetTimeCooked",tolua_AllToLua_cFurnaceEntity_GetTimeCooked00);
+ tolua_function(tolua_S,"GetCookTimeLeft",tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00);
+ tolua_function(tolua_S,"GetFuelBurnTimeLeft",tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00);
+ tolua_function(tolua_S,"HasFuelTimeLeft",tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cHopperEntity","cHopperEntity","cBlockEntityWithItems",NULL);
+ tolua_beginmodule(tolua_S,"cHopperEntity");
+ tolua_constant(tolua_S,"ContentsHeight",cHopperEntity::ContentsHeight);
+ tolua_constant(tolua_S,"ContentsWidth",cHopperEntity::ContentsWidth);
+ tolua_constant(tolua_S,"TICKS_PER_TRANSFER",cHopperEntity::TICKS_PER_TRANSFER);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cJukeboxEntity","cJukeboxEntity","cBlockEntity",NULL);
+ tolua_beginmodule(tolua_S,"cJukeboxEntity");
+ tolua_function(tolua_S,"GetRecord",tolua_AllToLua_cJukeboxEntity_GetRecord00);
+ tolua_function(tolua_S,"SetRecord",tolua_AllToLua_cJukeboxEntity_SetRecord00);
+ tolua_function(tolua_S,"PlayRecord",tolua_AllToLua_cJukeboxEntity_PlayRecord00);
+ tolua_function(tolua_S,"EjectRecord",tolua_AllToLua_cJukeboxEntity_EjectRecord00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cNoteEntity","cNoteEntity","cBlockEntity",NULL);
+ tolua_beginmodule(tolua_S,"cNoteEntity");
+ tolua_function(tolua_S,"GetPitch",tolua_AllToLua_cNoteEntity_GetPitch00);
+ tolua_function(tolua_S,"SetPitch",tolua_AllToLua_cNoteEntity_SetPitch00);
+ tolua_function(tolua_S,"IncrementPitch",tolua_AllToLua_cNoteEntity_IncrementPitch00);
+ tolua_function(tolua_S,"MakeSound",tolua_AllToLua_cNoteEntity_MakeSound00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cSignEntity","cSignEntity","cBlockEntity",NULL);
+ tolua_beginmodule(tolua_S,"cSignEntity");
+ tolua_function(tolua_S,"SetLines",tolua_AllToLua_cSignEntity_SetLines00);
+ tolua_function(tolua_S,"SetLine",tolua_AllToLua_cSignEntity_SetLine00);
+ tolua_function(tolua_S,"GetLine",tolua_AllToLua_cSignEntity_GetLine00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"HTTPFormData","HTTPFormData","",NULL);
+ tolua_beginmodule(tolua_S,"HTTPFormData");
+ tolua_variable(tolua_S,"Name",tolua_get_HTTPFormData_Name,tolua_set_HTTPFormData_Name);
+ tolua_variable(tolua_S,"Value",tolua_get_HTTPFormData_Value,tolua_set_HTTPFormData_Value);
+ tolua_variable(tolua_S,"Type",tolua_get_HTTPFormData_Type,tolua_set_HTTPFormData_Type);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"HTTPRequest","HTTPRequest","",NULL);
+ tolua_beginmodule(tolua_S,"HTTPRequest");
+ tolua_variable(tolua_S,"Method",tolua_get_HTTPRequest_Method,tolua_set_HTTPRequest_Method);
+ tolua_variable(tolua_S,"Path",tolua_get_HTTPRequest_Path,tolua_set_HTTPRequest_Path);
+ tolua_variable(tolua_S,"Username",tolua_get_HTTPRequest_Username,tolua_set_HTTPRequest_Username);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"HTTPTemplateRequest","HTTPTemplateRequest","",NULL);
+ tolua_beginmodule(tolua_S,"HTTPTemplateRequest");
+ tolua_variable(tolua_S,"Request",tolua_get_HTTPTemplateRequest_Request,tolua_set_HTTPTemplateRequest_Request);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"sWebAdminPage","sWebAdminPage","",tolua_collect_sWebAdminPage);
+ #else
+ tolua_cclass(tolua_S,"sWebAdminPage","sWebAdminPage","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"sWebAdminPage");
+ tolua_variable(tolua_S,"Content",tolua_get_sWebAdminPage_Content,tolua_set_sWebAdminPage_Content);
+ tolua_variable(tolua_S,"PluginName",tolua_get_sWebAdminPage_PluginName,tolua_set_sWebAdminPage_PluginName);
+ tolua_variable(tolua_S,"TabName",tolua_get_sWebAdminPage_TabName,tolua_set_sWebAdminPage_TabName);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","cHTTPServer::cCallbacks",NULL);
+ tolua_beginmodule(tolua_S,"cWebAdmin");
+ tolua_function(tolua_S,"GetPage",tolua_AllToLua_cWebAdmin_GetPage00);
+ tolua_function(tolua_S,"GetDefaultPage",tolua_AllToLua_cWebAdmin_GetDefaultPage00);
+ tolua_function(tolua_S,"GetBaseURL",tolua_AllToLua_cWebAdmin_GetBaseURL00);
+ tolua_function(tolua_S,"GetHTMLEscapedString",tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cWebPlugin","cWebPlugin","",NULL);
+ tolua_beginmodule(tolua_S,"cWebPlugin");
+ tolua_function(tolua_S,"GetWebTitle",tolua_AllToLua_cWebPlugin_GetWebTitle00);
+ tolua_function(tolua_S,"HandleWebRequest",tolua_AllToLua_cWebPlugin_HandleWebRequest00);
+ tolua_function(tolua_S,"SafeString",tolua_AllToLua_cWebPlugin_SafeString00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cRoot","cRoot","",NULL);
+ tolua_beginmodule(tolua_S,"cRoot");
+ tolua_function(tolua_S,"Get",tolua_AllToLua_cRoot_Get00);
+ tolua_function(tolua_S,"GetServer",tolua_AllToLua_cRoot_GetServer00);
+ tolua_function(tolua_S,"GetDefaultWorld",tolua_AllToLua_cRoot_GetDefaultWorld00);
+ tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cRoot_GetWorld00);
+ tolua_function(tolua_S,"GetPrimaryServerVersion",tolua_AllToLua_cRoot_GetPrimaryServerVersion00);
+ tolua_function(tolua_S,"SetPrimaryServerVersion",tolua_AllToLua_cRoot_SetPrimaryServerVersion00);
+ tolua_function(tolua_S,"GetGroupManager",tolua_AllToLua_cRoot_GetGroupManager00);
+ tolua_function(tolua_S,"GetCraftingRecipes",tolua_AllToLua_cRoot_GetCraftingRecipes00);
+ tolua_function(tolua_S,"GetFurnaceFuelBurnTime",tolua_AllToLua_cRoot_GetFurnaceFuelBurnTime00);
+ tolua_function(tolua_S,"GetWebAdmin",tolua_AllToLua_cRoot_GetWebAdmin00);
+ tolua_function(tolua_S,"GetPluginManager",tolua_AllToLua_cRoot_GetPluginManager00);
+ tolua_function(tolua_S,"QueueExecuteConsoleCommand",tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00);
+ tolua_function(tolua_S,"GetTotalChunkCount",tolua_AllToLua_cRoot_GetTotalChunkCount00);
+ tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cRoot_SaveAllChunks00);
+ tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cRoot_BroadcastChat00);
+ tolua_function(tolua_S,"GetProtocolVersionTextFromInt",tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00);
+ tolua_function(tolua_S,"GetVirtualRAMUsage",tolua_AllToLua_cRoot_GetVirtualRAMUsage00);
+ tolua_function(tolua_S,"GetPhysicalRAMUsage",tolua_AllToLua_cRoot_GetPhysicalRAMUsage00);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"Vector3f","Vector3f","",tolua_collect_Vector3f);
+ #else
+ tolua_cclass(tolua_S,"Vector3f","Vector3f","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"Vector3f");
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new00_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new01);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new01_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new01_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new02);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new02_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new02_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new03);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new03_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new03_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new04);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new04_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new04_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new05);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new05_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new05_local);
+ tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3f_Set00);
+ tolua_function(tolua_S,"Normalize",tolua_AllToLua_Vector3f_Normalize00);
+ tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3f_NormalizeCopy00);
+ tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3f_NormalizeCopy01);
+ tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3f_Length00);
+ tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3f_SqrLength00);
+ tolua_function(tolua_S,"Dot",tolua_AllToLua_Vector3f_Dot00);
+ tolua_function(tolua_S,"Cross",tolua_AllToLua_Vector3f_Cross00);
+ tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3f_Equals00);
+ tolua_function(tolua_S,".add",tolua_AllToLua_Vector3f__add00);
+ tolua_function(tolua_S,".add",tolua_AllToLua_Vector3f__add01);
+ tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3f__sub00);
+ tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3f__sub01);
+ tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3f__mul00);
+ tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3f__mul01);
+ tolua_variable(tolua_S,"x",tolua_get_Vector3f_x,tolua_set_Vector3f_x);
+ tolua_variable(tolua_S,"y",tolua_get_Vector3f_y,tolua_set_Vector3f_y);
+ tolua_variable(tolua_S,"z",tolua_get_Vector3f_z,tolua_set_Vector3f_z);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"Vector3d","Vector3d","",tolua_collect_Vector3d);
+ #else
+ tolua_cclass(tolua_S,"Vector3d","Vector3d","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"Vector3d");
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new00_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new01);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new01_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new01_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new02);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new02_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new02_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new03);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new03_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new03_local);
+ tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3d_Set00);
+ tolua_function(tolua_S,"Normalize",tolua_AllToLua_Vector3d_Normalize00);
+ tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3d_NormalizeCopy00);
+ tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3d_NormalizeCopy01);
+ tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3d_Length00);
+ tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3d_SqrLength00);
+ tolua_function(tolua_S,"Dot",tolua_AllToLua_Vector3d_Dot00);
+ tolua_function(tolua_S,"Cross",tolua_AllToLua_Vector3d_Cross00);
+ tolua_function(tolua_S,"LineCoeffToXYPlane",tolua_AllToLua_Vector3d_LineCoeffToXYPlane00);
+ tolua_function(tolua_S,"LineCoeffToXZPlane",tolua_AllToLua_Vector3d_LineCoeffToXZPlane00);
+ tolua_function(tolua_S,"LineCoeffToYZPlane",tolua_AllToLua_Vector3d_LineCoeffToYZPlane00);
+ tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3d_Equals00);
+ tolua_function(tolua_S,".add",tolua_AllToLua_Vector3d__add00);
+ tolua_function(tolua_S,".add",tolua_AllToLua_Vector3d__add01);
+ tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3d__sub00);
+ tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3d__sub01);
+ tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3d__mul00);
+ tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3d__mul01);
+ tolua_function(tolua_S,".div",tolua_AllToLua_Vector3d__div00);
+ tolua_variable(tolua_S,"x",tolua_get_Vector3d_x,tolua_set_Vector3d_x);
+ tolua_variable(tolua_S,"y",tolua_get_Vector3d_y,tolua_set_Vector3d_y);
+ tolua_variable(tolua_S,"z",tolua_get_Vector3d_z,tolua_set_Vector3d_z);
+ tolua_variable(tolua_S,"EPS",tolua_get_Vector3d_EPS,NULL);
+ tolua_variable(tolua_S,"NO_INTERSECTION",tolua_get_Vector3d_NO_INTERSECTION,NULL);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"Vector3i","Vector3i","",tolua_collect_Vector3i);
+ #else
+ tolua_cclass(tolua_S,"Vector3i","Vector3i","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"Vector3i");
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new00_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new01);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new01_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new01_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new02);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new02_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new02_local);
+ tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3i_Set00);
+ tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3i_Length00);
+ tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3i_SqrLength00);
+ tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3i_Equals00);
+ tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3i_Equals01);
+ tolua_variable(tolua_S,"x",tolua_get_Vector3i_x,tolua_set_Vector3i_x);
+ tolua_variable(tolua_S,"y",tolua_get_Vector3i_y,tolua_set_Vector3i_y);
+ tolua_variable(tolua_S,"z",tolua_get_Vector3i_z,tolua_set_Vector3i_z);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cCuboid","cCuboid","",tolua_collect_cCuboid);
+ #else
+ tolua_cclass(tolua_S,"cCuboid","cCuboid","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cCuboid");
+ tolua_variable(tolua_S,"p1",tolua_get_cCuboid_p1,tolua_set_cCuboid_p1);
+ tolua_variable(tolua_S,"p2",tolua_get_cCuboid_p2,tolua_set_cCuboid_p2);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new00_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new01);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new01_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new01_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new02);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new02_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new02_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new03);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new03_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new03_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new04);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new04_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new04_local);
+ tolua_function(tolua_S,"Assign",tolua_AllToLua_cCuboid_Assign00);
+ tolua_function(tolua_S,"Sort",tolua_AllToLua_cCuboid_Sort00);
+ tolua_function(tolua_S,"DifX",tolua_AllToLua_cCuboid_DifX00);
+ tolua_function(tolua_S,"DifY",tolua_AllToLua_cCuboid_DifY00);
+ tolua_function(tolua_S,"DifZ",tolua_AllToLua_cCuboid_DifZ00);
+ tolua_function(tolua_S,"DoesIntersect",tolua_AllToLua_cCuboid_DoesIntersect00);
+ tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside00);
+ tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside01);
+ tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside02);
+ tolua_function(tolua_S,"IsCompletelyInside",tolua_AllToLua_cCuboid_IsCompletelyInside00);
+ tolua_function(tolua_S,"Move",tolua_AllToLua_cCuboid_Move00);
+ tolua_function(tolua_S,"IsSorted",tolua_AllToLua_cCuboid_IsSorted00);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cBoundingBox","cBoundingBox","",tolua_collect_cBoundingBox);
+ #else
+ tolua_cclass(tolua_S,"cBoundingBox","cBoundingBox","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cBoundingBox");
+ tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new00_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new01);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new01_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new01_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new02);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new02_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new02_local);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new03);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new03_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new03_local);
+ tolua_function(tolua_S,"Move",tolua_AllToLua_cBoundingBox_Move00);
+ tolua_function(tolua_S,"Move",tolua_AllToLua_cBoundingBox_Move01);
+ tolua_function(tolua_S,"Expand",tolua_AllToLua_cBoundingBox_Expand00);
+ tolua_function(tolua_S,"DoesIntersect",tolua_AllToLua_cBoundingBox_DoesIntersect00);
+ tolua_function(tolua_S,"Union",tolua_AllToLua_cBoundingBox_Union00);
+ tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside00);
+ tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside01);
+ tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside02);
+ tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside03);
+ tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside04);
+ tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside05);
+ tolua_function(tolua_S,"CalcLineIntersection",tolua_AllToLua_cBoundingBox_CalcLineIntersection00);
+ tolua_function(tolua_S,"CalcLineIntersection",tolua_AllToLua_cBoundingBox_CalcLineIntersection01);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cTracer","cTracer","",tolua_collect_cTracer);
+ #else
+ tolua_cclass(tolua_S,"cTracer","cTracer","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cTracer");
+ tolua_variable(tolua_S,"BlockHitPosition",tolua_get_cTracer_BlockHitPosition,tolua_set_cTracer_BlockHitPosition);
+ tolua_variable(tolua_S,"HitNormal",tolua_get_cTracer_HitNormal,tolua_set_cTracer_HitNormal);
+ tolua_variable(tolua_S,"RealHit",tolua_get_cTracer_RealHit,tolua_set_cTracer_RealHit);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cTracer_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cTracer_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cTracer_new00_local);
+ tolua_function(tolua_S,"delete",tolua_AllToLua_cTracer_delete00);
+ tolua_function(tolua_S,"Trace",tolua_AllToLua_cTracer_Trace00);
+ tolua_function(tolua_S,"Trace",tolua_AllToLua_cTracer_Trace01);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cGroup","cGroup","",NULL);
+ tolua_beginmodule(tolua_S,"cGroup");
+ tolua_function(tolua_S,"SetName",tolua_AllToLua_cGroup_SetName00);
+ tolua_function(tolua_S,"GetName",tolua_AllToLua_cGroup_GetName00);
+ tolua_function(tolua_S,"SetColor",tolua_AllToLua_cGroup_SetColor00);
+ tolua_function(tolua_S,"AddCommand",tolua_AllToLua_cGroup_AddCommand00);
+ tolua_function(tolua_S,"AddPermission",tolua_AllToLua_cGroup_AddPermission00);
+ tolua_function(tolua_S,"InheritFrom",tolua_AllToLua_cGroup_InheritFrom00);
+ tolua_function(tolua_S,"HasCommand",tolua_AllToLua_cGroup_HasCommand00);
+ tolua_function(tolua_S,"GetColor",tolua_AllToLua_cGroup_GetColor00);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cBlockArea","cBlockArea","",tolua_collect_cBlockArea);
+ #else
+ tolua_cclass(tolua_S,"cBlockArea","cBlockArea","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cBlockArea");
+ tolua_constant(tolua_S,"baTypes",cBlockArea::baTypes);
+ tolua_constant(tolua_S,"baMetas",cBlockArea::baMetas);
+ tolua_constant(tolua_S,"baLight",cBlockArea::baLight);
+ tolua_constant(tolua_S,"baSkyLight",cBlockArea::baSkyLight);
+ tolua_constant(tolua_S,"msOverwrite",cBlockArea::msOverwrite);
+ tolua_constant(tolua_S,"msFillAir",cBlockArea::msFillAir);
+ tolua_constant(tolua_S,"msImprint",cBlockArea::msImprint);
+ tolua_constant(tolua_S,"msLake",cBlockArea::msLake);
+ tolua_function(tolua_S,"new",tolua_AllToLua_cBlockArea_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cBlockArea_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cBlockArea_new00_local);
+ tolua_function(tolua_S,"delete",tolua_AllToLua_cBlockArea_delete00);
+ tolua_function(tolua_S,"Clear",tolua_AllToLua_cBlockArea_Clear00);
+ tolua_function(tolua_S,"Create",tolua_AllToLua_cBlockArea_Create00);
+ tolua_function(tolua_S,"Create",tolua_AllToLua_cBlockArea_Create01);
+ tolua_function(tolua_S,"SetOrigin",tolua_AllToLua_cBlockArea_SetOrigin00);
+ tolua_function(tolua_S,"Read",tolua_AllToLua_cBlockArea_Read00);
+ tolua_function(tolua_S,"Read",tolua_AllToLua_cBlockArea_Read01);
+ tolua_function(tolua_S,"Write",tolua_AllToLua_cBlockArea_Write00);
+ tolua_function(tolua_S,"Write",tolua_AllToLua_cBlockArea_Write01);
+ tolua_function(tolua_S,"CopyTo",tolua_AllToLua_cBlockArea_CopyTo00);
+ tolua_function(tolua_S,"CopyFrom",tolua_AllToLua_cBlockArea_CopyFrom00);
+ tolua_function(tolua_S,"DumpToRawFile",tolua_AllToLua_cBlockArea_DumpToRawFile00);
+ tolua_function(tolua_S,"LoadFromSchematicFile",tolua_AllToLua_cBlockArea_LoadFromSchematicFile00);
+ tolua_function(tolua_S,"SaveToSchematicFile",tolua_AllToLua_cBlockArea_SaveToSchematicFile00);
+ tolua_function(tolua_S,"Crop",tolua_AllToLua_cBlockArea_Crop00);
+ tolua_function(tolua_S,"Expand",tolua_AllToLua_cBlockArea_Expand00);
+ tolua_function(tolua_S,"Merge",tolua_AllToLua_cBlockArea_Merge00);
+ tolua_function(tolua_S,"Fill",tolua_AllToLua_cBlockArea_Fill00);
+ tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cBlockArea_FillRelCuboid00);
+ tolua_function(tolua_S,"RelLine",tolua_AllToLua_cBlockArea_RelLine00);
+ tolua_function(tolua_S,"RotateCCW",tolua_AllToLua_cBlockArea_RotateCCW00);
+ tolua_function(tolua_S,"RotateCW",tolua_AllToLua_cBlockArea_RotateCW00);
+ tolua_function(tolua_S,"MirrorXY",tolua_AllToLua_cBlockArea_MirrorXY00);
+ tolua_function(tolua_S,"MirrorXZ",tolua_AllToLua_cBlockArea_MirrorXZ00);
+ tolua_function(tolua_S,"MirrorYZ",tolua_AllToLua_cBlockArea_MirrorYZ00);
+ tolua_function(tolua_S,"RotateCCWNoMeta",tolua_AllToLua_cBlockArea_RotateCCWNoMeta00);
+ tolua_function(tolua_S,"RotateCWNoMeta",tolua_AllToLua_cBlockArea_RotateCWNoMeta00);
+ tolua_function(tolua_S,"MirrorXYNoMeta",tolua_AllToLua_cBlockArea_MirrorXYNoMeta00);
+ tolua_function(tolua_S,"MirrorXZNoMeta",tolua_AllToLua_cBlockArea_MirrorXZNoMeta00);
+ tolua_function(tolua_S,"MirrorYZNoMeta",tolua_AllToLua_cBlockArea_MirrorYZNoMeta00);
+ tolua_function(tolua_S,"SetRelBlockType",tolua_AllToLua_cBlockArea_SetRelBlockType00);
+ tolua_function(tolua_S,"SetBlockType",tolua_AllToLua_cBlockArea_SetBlockType00);
+ tolua_function(tolua_S,"SetRelBlockMeta",tolua_AllToLua_cBlockArea_SetRelBlockMeta00);
+ tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cBlockArea_SetBlockMeta00);
+ tolua_function(tolua_S,"SetRelBlockLight",tolua_AllToLua_cBlockArea_SetRelBlockLight00);
+ tolua_function(tolua_S,"SetBlockLight",tolua_AllToLua_cBlockArea_SetBlockLight00);
+ tolua_function(tolua_S,"SetRelBlockSkyLight",tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00);
+ tolua_function(tolua_S,"SetBlockSkyLight",tolua_AllToLua_cBlockArea_SetBlockSkyLight00);
+ tolua_function(tolua_S,"GetRelBlockType",tolua_AllToLua_cBlockArea_GetRelBlockType00);
+ tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cBlockArea_GetBlockType00);
+ tolua_function(tolua_S,"GetRelBlockMeta",tolua_AllToLua_cBlockArea_GetRelBlockMeta00);
+ tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cBlockArea_GetBlockMeta00);
+ tolua_function(tolua_S,"GetRelBlockLight",tolua_AllToLua_cBlockArea_GetRelBlockLight00);
+ tolua_function(tolua_S,"GetBlockLight",tolua_AllToLua_cBlockArea_GetBlockLight00);
+ tolua_function(tolua_S,"GetRelBlockSkyLight",tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00);
+ tolua_function(tolua_S,"GetBlockSkyLight",tolua_AllToLua_cBlockArea_GetBlockSkyLight00);
+ tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cBlockArea_SetBlockTypeMeta00);
+ tolua_function(tolua_S,"SetRelBlockTypeMeta",tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00);
+ tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cBlockArea_GetBlockTypeMeta00);
+ tolua_function(tolua_S,"GetRelBlockTypeMeta",tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00);
+ tolua_function(tolua_S,"GetSizeX",tolua_AllToLua_cBlockArea_GetSizeX00);
+ tolua_function(tolua_S,"GetSizeY",tolua_AllToLua_cBlockArea_GetSizeY00);
+ tolua_function(tolua_S,"GetSizeZ",tolua_AllToLua_cBlockArea_GetSizeZ00);
+ tolua_function(tolua_S,"GetOriginX",tolua_AllToLua_cBlockArea_GetOriginX00);
+ tolua_function(tolua_S,"GetOriginY",tolua_AllToLua_cBlockArea_GetOriginY00);
+ tolua_function(tolua_S,"GetOriginZ",tolua_AllToLua_cBlockArea_GetOriginZ00);
+ tolua_function(tolua_S,"GetDataTypes",tolua_AllToLua_cBlockArea_GetDataTypes00);
+ tolua_function(tolua_S,"HasBlockTypes",tolua_AllToLua_cBlockArea_HasBlockTypes00);
+ tolua_function(tolua_S,"HasBlockMetas",tolua_AllToLua_cBlockArea_HasBlockMetas00);
+ tolua_function(tolua_S,"HasBlockLights",tolua_AllToLua_cBlockArea_HasBlockLights00);
+ tolua_function(tolua_S,"HasBlockSkyLights",tolua_AllToLua_cBlockArea_HasBlockSkyLights00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cChunkDesc","cChunkDesc","",NULL);
+ tolua_beginmodule(tolua_S,"cChunkDesc");
+ tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cChunkDesc_GetChunkX00);
+ tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cChunkDesc_GetChunkZ00);
+ tolua_function(tolua_S,"FillBlocks",tolua_AllToLua_cChunkDesc_FillBlocks00);
+ tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00);
+ tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00);
+ tolua_function(tolua_S,"SetBlockType",tolua_AllToLua_cChunkDesc_SetBlockType00);
+ tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cChunkDesc_GetBlockType00);
+ tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cChunkDesc_SetBlockMeta00);
+ tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cChunkDesc_GetBlockMeta00);
+ tolua_function(tolua_S,"SetBiome",tolua_AllToLua_cChunkDesc_SetBiome00);
+ tolua_function(tolua_S,"GetBiome",tolua_AllToLua_cChunkDesc_GetBiome00);
+ tolua_function(tolua_S,"SetHeight",tolua_AllToLua_cChunkDesc_SetHeight00);
+ tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cChunkDesc_GetHeight00);
+ tolua_function(tolua_S,"SetUseDefaultBiomes",tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00);
+ tolua_function(tolua_S,"IsUsingDefaultBiomes",tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00);
+ tolua_function(tolua_S,"SetUseDefaultHeight",tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00);
+ tolua_function(tolua_S,"IsUsingDefaultHeight",tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00);
+ tolua_function(tolua_S,"SetUseDefaultComposition",tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00);
+ tolua_function(tolua_S,"IsUsingDefaultComposition",tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00);
+ tolua_function(tolua_S,"SetUseDefaultStructures",tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00);
+ tolua_function(tolua_S,"IsUsingDefaultStructures",tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00);
+ tolua_function(tolua_S,"SetUseDefaultFinish",tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00);
+ tolua_function(tolua_S,"IsUsingDefaultFinish",tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00);
+ tolua_function(tolua_S,"WriteBlockArea",tolua_AllToLua_cChunkDesc_WriteBlockArea00);
+ tolua_function(tolua_S,"ReadBlockArea",tolua_AllToLua_cChunkDesc_ReadBlockArea00);
+ tolua_function(tolua_S,"GetMaxHeight",tolua_AllToLua_cChunkDesc_GetMaxHeight00);
+ tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cChunkDesc_FillRelCuboid00);
+ tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cChunkDesc_FillRelCuboid01);
+ tolua_function(tolua_S,"ReplaceRelCuboid",tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00);
+ tolua_function(tolua_S,"ReplaceRelCuboid",tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01);
+ tolua_function(tolua_S,"FloorRelCuboid",tolua_AllToLua_cChunkDesc_FloorRelCuboid00);
+ tolua_function(tolua_S,"FloorRelCuboid",tolua_AllToLua_cChunkDesc_FloorRelCuboid01);
+ tolua_function(tolua_S,"RandomFillRelCuboid",tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00);
+ tolua_function(tolua_S,"RandomFillRelCuboid",tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01);
+ tolua_function(tolua_S,"GetBlockEntity",tolua_AllToLua_cChunkDesc_GetBlockEntity00);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cCraftingGrid","cCraftingGrid","",tolua_collect_cCraftingGrid);
+ #else
+ tolua_cclass(tolua_S,"cCraftingGrid","cCraftingGrid","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cCraftingGrid");
+ tolua_function(tolua_S,"new",tolua_AllToLua_cCraftingGrid_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cCraftingGrid_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cCraftingGrid_new00_local);
+ tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cCraftingGrid_GetWidth00);
+ tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cCraftingGrid_GetHeight00);
+ tolua_function(tolua_S,"GetItem",tolua_AllToLua_cCraftingGrid_GetItem00);
+ tolua_function(tolua_S,"SetItem",tolua_AllToLua_cCraftingGrid_SetItem00);
+ tolua_function(tolua_S,"SetItem",tolua_AllToLua_cCraftingGrid_SetItem01);
+ tolua_function(tolua_S,"Clear",tolua_AllToLua_cCraftingGrid_Clear00);
+ tolua_function(tolua_S,"ConsumeGrid",tolua_AllToLua_cCraftingGrid_ConsumeGrid00);
+ tolua_function(tolua_S,"Dump",tolua_AllToLua_cCraftingGrid_Dump00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cCraftingRecipe","cCraftingRecipe","",NULL);
+ tolua_beginmodule(tolua_S,"cCraftingRecipe");
+ tolua_function(tolua_S,"Clear",tolua_AllToLua_cCraftingRecipe_Clear00);
+ tolua_function(tolua_S,"GetIngredientsWidth",tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00);
+ tolua_function(tolua_S,"GetIngredientsHeight",tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00);
+ tolua_function(tolua_S,"GetIngredient",tolua_AllToLua_cCraftingRecipe_GetIngredient00);
+ tolua_function(tolua_S,"GetResult",tolua_AllToLua_cCraftingRecipe_GetResult00);
+ tolua_function(tolua_S,"SetResult",tolua_AllToLua_cCraftingRecipe_SetResult00);
+ tolua_function(tolua_S,"SetResult",tolua_AllToLua_cCraftingRecipe_SetResult01);
+ tolua_function(tolua_S,"SetIngredient",tolua_AllToLua_cCraftingRecipe_SetIngredient00);
+ tolua_function(tolua_S,"SetIngredient",tolua_AllToLua_cCraftingRecipe_SetIngredient01);
+ tolua_function(tolua_S,"ConsumeIngredients",tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00);
+ tolua_function(tolua_S,"Dump",tolua_AllToLua_cCraftingRecipe_Dump00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cWindow","cWindow","",NULL);
+ tolua_beginmodule(tolua_S,"cWindow");
+ tolua_constant(tolua_S,"wtInventory",cWindow::wtInventory);
+ tolua_constant(tolua_S,"wtChest",cWindow::wtChest);
+ tolua_constant(tolua_S,"wtWorkbench",cWindow::wtWorkbench);
+ tolua_constant(tolua_S,"wtFurnace",cWindow::wtFurnace);
+ tolua_constant(tolua_S,"wtDropSpenser",cWindow::wtDropSpenser);
+ tolua_constant(tolua_S,"wtEnchantment",cWindow::wtEnchantment);
+ tolua_constant(tolua_S,"wtBrewery",cWindow::wtBrewery);
+ tolua_constant(tolua_S,"wtNPCTrade",cWindow::wtNPCTrade);
+ tolua_constant(tolua_S,"wtBeacon",cWindow::wtBeacon);
+ tolua_constant(tolua_S,"wtAnvil",cWindow::wtAnvil);
+ tolua_constant(tolua_S,"wtHopper",cWindow::wtHopper);
+ tolua_constant(tolua_S,"wtAnimalChest",cWindow::wtAnimalChest);
+ tolua_function(tolua_S,"GetWindowID",tolua_AllToLua_cWindow_GetWindowID00);
+ tolua_function(tolua_S,"GetWindowType",tolua_AllToLua_cWindow_GetWindowType00);
+ tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cWindow_GetSlot00);
+ tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cWindow_SetSlot00);
+ tolua_function(tolua_S,"IsSlotInPlayerMainInventory",tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00);
+ tolua_function(tolua_S,"IsSlotInPlayerHotbar",tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00);
+ tolua_function(tolua_S,"IsSlotInPlayerInventory",tolua_AllToLua_cWindow_IsSlotInPlayerInventory00);
+ tolua_function(tolua_S,"GetWindowTitle",tolua_AllToLua_cWindow_GetWindowTitle00);
+ tolua_function(tolua_S,"SetWindowTitle",tolua_AllToLua_cWindow_SetWindowTitle00);
+ tolua_function(tolua_S,"SetProperty",tolua_AllToLua_cWindow_SetProperty00);
+ tolua_function(tolua_S,"SetProperty",tolua_AllToLua_cWindow_SetProperty01);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"cLuaWindow","cLuaWindow","cWindow",tolua_collect_cLuaWindow);
+ #else
+ tolua_cclass(tolua_S,"cLuaWindow","cLuaWindow","cWindow",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"cLuaWindow");
+ tolua_function(tolua_S,"new",tolua_AllToLua_cLuaWindow_new00);
+ tolua_function(tolua_S,"new_local",tolua_AllToLua_cLuaWindow_new00_local);
+ tolua_function(tolua_S,".call",tolua_AllToLua_cLuaWindow_new00_local);
+ tolua_function(tolua_S,"delete",tolua_AllToLua_cLuaWindow_delete00);
+ tolua_function(tolua_S,"GetContents",tolua_AllToLua_cLuaWindow_GetContents00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cMonster","cMonster","cPawn",NULL);
+ tolua_beginmodule(tolua_S,"cMonster");
+ tolua_constant(tolua_S,"mtInvalidType",cMonster::mtInvalidType);
+ tolua_constant(tolua_S,"mtBat",cMonster::mtBat);
+ tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze);
+ tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider);
+ tolua_constant(tolua_S,"mtChicken",cMonster::mtChicken);
+ tolua_constant(tolua_S,"mtCow",cMonster::mtCow);
+ tolua_constant(tolua_S,"mtCreeper",cMonster::mtCreeper);
+ tolua_constant(tolua_S,"mtEnderDragon",cMonster::mtEnderDragon);
+ tolua_constant(tolua_S,"mtEnderman",cMonster::mtEnderman);
+ tolua_constant(tolua_S,"mtGhast",cMonster::mtGhast);
+ tolua_constant(tolua_S,"mtGiant",cMonster::mtGiant);
+ tolua_constant(tolua_S,"mtHorse",cMonster::mtHorse);
+ tolua_constant(tolua_S,"mtIronGolem",cMonster::mtIronGolem);
+ tolua_constant(tolua_S,"mtMagmaCube",cMonster::mtMagmaCube);
+ tolua_constant(tolua_S,"mtMooshroom",cMonster::mtMooshroom);
+ tolua_constant(tolua_S,"mtOcelot",cMonster::mtOcelot);
+ tolua_constant(tolua_S,"mtPig",cMonster::mtPig);
+ tolua_constant(tolua_S,"mtSheep",cMonster::mtSheep);
+ tolua_constant(tolua_S,"mtSilverfish",cMonster::mtSilverfish);
+ tolua_constant(tolua_S,"mtSkeleton",cMonster::mtSkeleton);
+ tolua_constant(tolua_S,"mtSlime",cMonster::mtSlime);
+ tolua_constant(tolua_S,"mtSnowGolem",cMonster::mtSnowGolem);
+ tolua_constant(tolua_S,"mtSpider",cMonster::mtSpider);
+ tolua_constant(tolua_S,"mtSquid",cMonster::mtSquid);
+ tolua_constant(tolua_S,"mtVillager",cMonster::mtVillager);
+ tolua_constant(tolua_S,"mtWitch",cMonster::mtWitch);
+ tolua_constant(tolua_S,"mtWither",cMonster::mtWither);
+ tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf);
+ tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie);
+ tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman);
+ tolua_constant(tolua_S,"mfHostile",cMonster::mfHostile);
+ tolua_constant(tolua_S,"mfPassive",cMonster::mfPassive);
+ tolua_constant(tolua_S,"mfAmbient",cMonster::mfAmbient);
+ tolua_constant(tolua_S,"mfWater",cMonster::mfWater);
+ tolua_constant(tolua_S,"mfMaxplusone",cMonster::mfMaxplusone);
+ tolua_function(tolua_S,"GetMobType",tolua_AllToLua_cMonster_GetMobType00);
+ tolua_function(tolua_S,"GetMobFamily",tolua_AllToLua_cMonster_GetMobFamily00);
+ tolua_function(tolua_S,"MobTypeToString",tolua_AllToLua_cMonster_MobTypeToString00);
+ tolua_function(tolua_S,"StringToMobType",tolua_AllToLua_cMonster_StringToMobType00);
+ tolua_function(tolua_S,"FamilyFromType",tolua_AllToLua_cMonster_FamilyFromType00);
+ tolua_function(tolua_S,"GetSpawnDelay",tolua_AllToLua_cMonster_GetSpawnDelay00);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cLineBlockTracer","cLineBlockTracer","",NULL);
+ tolua_beginmodule(tolua_S,"cLineBlockTracer");
+ tolua_endmodule(tolua_S);
+ tolua_endmodule(tolua_S);
+ return 1;
+}
+
+
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 501
+ TOLUA_API int luaopen_AllToLua (lua_State* tolua_S) {
+ return tolua_AllToLua_open(tolua_S);
+};
+#endif
+
diff --git a/src/Bindings.h b/src/Bindings.h
new file mode 100644
index 000000000..bc8589293
--- /dev/null
+++ b/src/Bindings.h
@@ -0,0 +1,8 @@
+/*
+** Lua binding: AllToLua
+** Generated automatically by tolua++-1.0.92 on 11/23/13 19:57:31.
+*/
+
+/* Exported function */
+TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);
+
diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp
new file mode 100644
index 000000000..5c15adfef
--- /dev/null
+++ b/src/BlockArea.cpp
@@ -0,0 +1,2124 @@
+
+// BlockArea.cpp
+
+// Implements the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries
+// The object also supports writing the blockdata back into cWorld, even into other coords
+
+#include "Globals.h"
+#include "BlockArea.h"
+#include "World.h"
+#include "OSSupport/GZipFile.h"
+#include "WorldStorage/FastNBT.h"
+#include "Blocks/BlockHandler.h"
+
+
+
+
+
+// This wild construct allows us to pass a function argument and still have it inlined by the compiler :)
+/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
+template<typename Combinator> void InternalMergeBlocks(
+ BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
+ NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
+ int a_SizeX, int a_SizeY, int a_SizeZ,
+ int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ,
+ int a_DstOffX, int a_DstOffY, int a_DstOffZ,
+ int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
+ int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ,
+ Combinator a_Combinator
+)
+{
+ for (int y = 0; y < a_SizeY; y++)
+ {
+ int SrcBaseY = (y + a_SrcOffY) * a_SrcSizeX * a_SrcSizeZ;
+ int DstBaseY = (y + a_DstOffY) * a_DstSizeX * a_DstSizeZ;
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int SrcBaseZ = SrcBaseY + (z + a_SrcOffZ) * a_SrcSizeX;
+ int DstBaseZ = DstBaseY + (z + a_DstOffZ) * a_DstSizeX;
+ int SrcIdx = SrcBaseZ + a_SrcOffX;
+ int DstIdx = DstBaseZ + a_DstOffX;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ a_Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]);
+ ++DstIdx;
+ ++SrcIdx;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msOverwrite merging
+static void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ a_DstType = a_SrcType;
+ a_DstMeta = a_SrcMeta;
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msFillAir merging
+static void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ if (a_DstType == E_BLOCK_AIR)
+ {
+ a_DstType = a_SrcType;
+ a_DstMeta = a_SrcMeta;
+ }
+ // "else" is the default, already in place
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msImprint merging
+static void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ if (a_SrcType != E_BLOCK_AIR)
+ {
+ a_DstType = a_SrcType;
+ a_DstMeta = a_SrcMeta;
+ }
+ // "else" is the default, already in place
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msLake merging
+static void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ // Sponge is the NOP block
+ if (a_SrcType == E_BLOCK_SPONGE)
+ {
+ return;
+ }
+
+ // Air is always hollowed out
+ if (a_SrcType == E_BLOCK_AIR)
+ {
+ a_DstType = E_BLOCK_AIR;
+ a_DstMeta = 0;
+ return;
+ }
+
+ // Water and lava are never overwritten
+ switch (a_DstType)
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ return;
+ }
+ }
+
+ // Water and lava always overwrite
+ switch (a_SrcType)
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ a_DstType = a_SrcType;
+ a_DstMeta = a_DstMeta;
+ return;
+ }
+ }
+
+ if (a_SrcType == E_BLOCK_STONE)
+ {
+ switch (a_DstType)
+ {
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_MYCELIUM:
+ {
+ a_DstType = E_BLOCK_STONE;
+ a_DstMeta = 0;
+ return;
+ }
+ }
+ }
+ // Everything else is left as it is
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBlockArea:
+
+cBlockArea::cBlockArea(void) :
+ m_SizeX(0),
+ m_SizeY(0),
+ m_SizeZ(0),
+ m_BlockTypes(NULL),
+ m_BlockMetas(NULL),
+ m_BlockLight(NULL),
+ m_BlockSkyLight(NULL)
+{
+}
+
+
+
+
+
+cBlockArea::~cBlockArea()
+{
+ Clear();
+}
+
+
+
+
+
+void cBlockArea::Clear(void)
+{
+ delete[] m_BlockTypes; m_BlockTypes = NULL;
+ delete[] m_BlockMetas; m_BlockMetas = NULL;
+ delete[] m_BlockLight; m_BlockLight = NULL;
+ delete[] m_BlockSkyLight; m_BlockSkyLight = NULL;
+ m_SizeX = 0;
+ m_SizeY = 0;
+ m_SizeZ = 0;
+}
+
+
+
+
+
+void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
+{
+ Clear();
+ int BlockCount = a_SizeX * a_SizeY * a_SizeZ;
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ m_BlockTypes = new BLOCKTYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockTypes[i] = E_BLOCK_AIR;
+ }
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ m_BlockMetas = new NIBBLETYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockMetas[i] = 0;
+ }
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ m_BlockLight = new NIBBLETYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockLight[i] = 0;
+ }
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ m_BlockSkyLight = new NIBBLETYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockSkyLight[i] = 0x0f;
+ }
+ }
+ m_SizeX = a_SizeX;
+ m_SizeY = a_SizeY;
+ m_SizeZ = a_SizeZ;
+ m_OriginX = 0;
+ m_OriginY = 0;
+ m_OriginZ = 0;
+}
+
+
+
+
+
+void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ)
+{
+ m_OriginX = a_OriginX;
+ m_OriginY = a_OriginY;
+ m_OriginZ = a_OriginZ;
+}
+
+
+
+
+
+bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
+{
+ // Normalize the coords:
+ if (a_MinBlockX > a_MaxBlockX)
+ {
+ std::swap(a_MinBlockX, a_MaxBlockX);
+ }
+ if (a_MinBlockY > a_MaxBlockY)
+ {
+ std::swap(a_MinBlockY, a_MaxBlockY);
+ }
+ if (a_MinBlockZ > a_MaxBlockZ)
+ {
+ std::swap(a_MinBlockZ, a_MaxBlockZ);
+ }
+
+ // Include the Max coords:
+ a_MaxBlockX += 1;
+ a_MaxBlockY += 1;
+ a_MaxBlockZ += 1;
+
+ // Check coords validity:
+ if (a_MinBlockY < 0)
+ {
+ LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__);
+ a_MinBlockY = 0;
+ }
+ else if (a_MinBlockY >= cChunkDef::Height)
+ {
+ LOGWARNING("%s: MinBlockY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MinBlockY = cChunkDef::Height - 1;
+ }
+ if (a_MaxBlockY < 0)
+ {
+ LOGWARNING("%s: MaxBlockY less than zero, adjusting to zero", __FUNCTION__);
+ a_MaxBlockY = 0;
+ }
+ else if (a_MaxBlockY >= cChunkDef::Height)
+ {
+ LOGWARNING("%s: MaxBlockY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MaxBlockY = cChunkDef::Height - 1;
+ }
+
+ // Allocate the needed memory:
+ Clear();
+ if (!SetSize(a_MaxBlockX - a_MinBlockX, a_MaxBlockY - a_MinBlockY, a_MaxBlockZ - a_MinBlockZ, a_DataTypes))
+ {
+ return false;
+ }
+ m_OriginX = a_MinBlockX;
+ m_OriginY = a_MinBlockY;
+ m_OriginZ = a_MinBlockZ;
+ cChunkReader Reader(*this);
+
+ // Convert block coords to chunks coords:
+ int MinChunkX, MaxChunkX;
+ int MinChunkZ, MaxChunkZ;
+ cChunkDef::AbsoluteToRelative(a_MinBlockX, a_MinBlockY, a_MinBlockZ, MinChunkX, MinChunkZ);
+ cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ);
+
+ // Query block data:
+ if (!a_World->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader))
+ {
+ Clear();
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
+{
+ ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have?
+ a_DataTypes = a_DataTypes & GetDataTypes(); // For release builds, silently cut off the datatypes that I don't have
+
+ // Check coords validity:
+ if (a_MinBlockY < 0)
+ {
+ LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__);
+ a_MinBlockY = 0;
+ }
+ else if (a_MinBlockY >= cChunkDef::Height - m_SizeY)
+ {
+ LOGWARNING("%s: MinBlockY + m_SizeY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MinBlockY = cChunkDef::Height - m_SizeY - 1;
+ }
+
+ return a_World->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
+}
+
+
+
+
+
+void cBlockArea::CopyTo(cBlockArea & a_Into) const
+{
+ if (&a_Into == this)
+ {
+ LOGWARNING("Trying to copy a cBlockArea into self, ignoring.");
+ return;
+ }
+
+ a_Into.Clear();
+ a_Into.SetSize(m_SizeX, m_SizeY, m_SizeZ, GetDataTypes());
+ a_Into.m_OriginX = m_OriginX;
+ a_Into.m_OriginY = m_OriginY;
+ a_Into.m_OriginZ = m_OriginZ;
+ int BlockCount = GetBlockCount();
+ if (HasBlockTypes())
+ {
+ memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE));
+ }
+ if (HasBlockMetas())
+ {
+ memcpy(a_Into.m_BlockMetas, m_BlockMetas, BlockCount * sizeof(NIBBLETYPE));
+ }
+ if (HasBlockLights())
+ {
+ memcpy(a_Into.m_BlockLight, m_BlockLight, BlockCount * sizeof(NIBBLETYPE));
+ }
+ if (HasBlockSkyLights())
+ {
+ memcpy(a_Into.m_BlockSkyLight, m_BlockSkyLight, BlockCount * sizeof(NIBBLETYPE));
+ }
+}
+
+
+
+
+
+void cBlockArea::CopyFrom(const cBlockArea & a_From)
+{
+ a_From.CopyTo(*this);
+}
+
+
+
+
+
+void cBlockArea::DumpToRawFile(const AString & a_FileName)
+{
+ cFile f;
+ if (!f.Open(a_FileName, cFile::fmWrite))
+ {
+ LOGWARNING("cBlockArea: Cannot open file \"%s\" for raw dump", a_FileName.c_str());
+ return;
+ }
+ UInt32 SizeX = ntohl(m_SizeX);
+ UInt32 SizeY = ntohl(m_SizeY);
+ UInt32 SizeZ = ntohl(m_SizeZ);
+ f.Write(&SizeX, 4);
+ f.Write(&SizeY, 4);
+ f.Write(&SizeZ, 4);
+ unsigned char DataTypes = GetDataTypes();
+ f.Write(&DataTypes, 1);
+ int NumBlocks = GetBlockCount();
+ if (HasBlockTypes())
+ {
+ f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE));
+ }
+ if (HasBlockMetas())
+ {
+ f.Write(m_BlockMetas, NumBlocks);
+ }
+ if (HasBlockLights())
+ {
+ f.Write(m_BlockLight, NumBlocks);
+ }
+ if (HasBlockSkyLights())
+ {
+ f.Write(m_BlockSkyLight, NumBlocks);
+ }
+}
+
+
+
+
+
+bool cBlockArea::LoadFromSchematicFile(const AString & a_FileName)
+{
+ // Un-GZip the contents:
+ AString Contents;
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmRead))
+ {
+ LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+ int NumBytesRead = File.ReadRestOfFile(Contents);
+ if (NumBytesRead < 0)
+ {
+ LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead);
+ return false;
+ }
+ File.Close();
+
+ // Parse the NBT:
+ cParsedNBT NBT(Contents.data(), Contents.size());
+ if (!NBT.IsValid())
+ {
+ LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+
+ return LoadFromSchematicNBT(NBT);
+}
+
+
+
+
+
+bool cBlockArea::SaveToSchematicFile(const AString & a_FileName)
+{
+ cFastNBTWriter Writer("Schematic");
+ Writer.AddShort("Width", m_SizeX);
+ Writer.AddShort("Height", m_SizeY);
+ Writer.AddShort("Length", m_SizeZ);
+ Writer.AddString("Materials", "Alpha");
+ if (HasBlockTypes())
+ {
+ Writer.AddByteArray("Blocks", (const char *)m_BlockTypes, GetBlockCount());
+ }
+ else
+ {
+ AString Dummy(GetBlockCount(), 0);
+ Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
+ }
+ if (HasBlockMetas())
+ {
+ Writer.AddByteArray("Data", (const char *)m_BlockMetas, GetBlockCount());
+ }
+ else
+ {
+ AString Dummy(GetBlockCount(), 0);
+ Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
+ }
+ // TODO: Save entities and block entities
+ Writer.BeginList("Entities", TAG_Compound);
+ Writer.EndList();
+ Writer.BeginList("TileEntities", TAG_Compound);
+ Writer.EndList();
+ Writer.Finish();
+
+ // Save to file
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmWrite))
+ {
+ LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str());
+ return false;
+ }
+ if (!File.Write(Writer.GetResult()))
+ {
+ LOG("Cannot write data to file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+void cBlockArea::Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
+{
+ if (
+ (a_AddMinX + a_SubMaxX >= m_SizeX) ||
+ (a_AddMinY + a_SubMaxY >= m_SizeY) ||
+ (a_AddMinZ + a_SubMaxZ >= m_SizeZ)
+ )
+ {
+ LOGWARNING("cBlockArea:Crop called with more croping than the dimensions: %d x %d x %d with cropping %d, %d and %d",
+ m_SizeX, m_SizeY, m_SizeZ,
+ a_AddMinX + a_SubMaxX, a_AddMinY + a_SubMaxY, a_AddMinZ + a_SubMaxZ
+ );
+ return;
+ }
+
+ if (HasBlockTypes())
+ {
+ CropBlockTypes(a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ if (HasBlockMetas())
+ {
+ CropNibbles(m_BlockMetas, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ if (HasBlockLights())
+ {
+ CropNibbles(m_BlockLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ if (HasBlockSkyLights())
+ {
+ CropNibbles(m_BlockSkyLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ m_OriginX += a_AddMinX;
+ m_OriginY += a_AddMinY;
+ m_OriginZ += a_AddMinZ;
+ m_SizeX -= a_AddMinX + a_SubMaxX;
+ m_SizeY -= a_AddMinY + a_SubMaxY;
+ m_SizeZ -= a_AddMinZ + a_SubMaxZ;
+}
+
+
+
+
+
+void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
+{
+ if (HasBlockTypes())
+ {
+ ExpandBlockTypes(a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ if (HasBlockMetas())
+ {
+ ExpandNibbles(m_BlockMetas, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ if (HasBlockLights())
+ {
+ ExpandNibbles(m_BlockLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ if (HasBlockSkyLights())
+ {
+ ExpandNibbles(m_BlockSkyLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ m_OriginX -= a_SubMinX;
+ m_OriginY -= a_SubMinY;
+ m_OriginZ -= a_SubMinZ;
+ m_SizeX += a_SubMinX + a_AddMaxX;
+ m_SizeY += a_SubMinY + a_AddMaxY;
+ m_SizeZ += a_SubMinZ + a_AddMaxZ;
+}
+
+
+
+
+
+void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy)
+{
+ // Block types are compulsory, block metas are voluntary
+ if (!HasBlockTypes() || !a_Src.HasBlockTypes())
+ {
+ LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
+ return;
+ }
+
+ // Dst is *this, Src is a_Src
+ int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading
+ int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing
+ int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy
+
+ int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading
+ int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing
+ int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy
+
+ int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading
+ int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing
+ int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy
+
+ const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas();
+ NIBBLETYPE * DstMetas = m_BlockMetas;
+ bool IsDummyMetas = ((SrcMetas == NULL) || (DstMetas == NULL));
+
+ if (IsDummyMetas)
+ {
+ SrcMetas = new NIBBLETYPE[a_Src.GetBlockCount()];
+ DstMetas = new NIBBLETYPE[GetBlockCount()];
+ }
+
+ switch (a_Strategy)
+ {
+ case msOverwrite:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorOverwrite
+ );
+ break;
+ } // case msOverwrite
+
+ case msFillAir:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorFillAir
+ );
+ break;
+ } // case msFillAir
+
+ case msImprint:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorImprint
+ );
+ break;
+ } // case msImprint
+
+ case msLake:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorLake
+ );
+ break;
+ } // case msLake
+
+ default:
+ {
+ LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
+ ASSERT(!"Unknown block area merge strategy");
+ break;
+ }
+ } // switch (a_Strategy)
+
+ if (IsDummyMetas)
+ {
+ delete[] SrcMetas;
+ delete[] DstMetas;
+ }
+}
+
+
+
+
+
+void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight)
+{
+ if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
+ {
+ LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
+ __FUNCTION__, a_DataTypes, GetDataTypes()
+ );
+ a_DataTypes = a_DataTypes & GetDataTypes();
+ }
+
+ int BlockCount = GetBlockCount();
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockTypes[i] = a_BlockType;
+ }
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockMetas[i] = a_BlockMeta;
+ }
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockLight[i] = a_BlockLight;
+ }
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockSkyLight[i] = a_BlockSkyLight;
+ }
+ }
+}
+
+
+
+
+
+void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
+ {
+ LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
+ __FUNCTION__, a_DataTypes, GetDataTypes()
+ );
+ a_DataTypes = a_DataTypes & GetDataTypes();
+ }
+
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockTypes[MakeIndex(x, y, z)] = a_BlockType;
+ } // for x, z, y
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockMetas[MakeIndex(x, y, z)] = a_BlockMeta;
+ } // for x, z, y
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockLight[MakeIndex(x, y, z)] = a_BlockLight;
+ } // for x, z, y
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockSkyLight[MakeIndex(x, y, z)] = a_BlockSkyLight;
+ } // for x, z, y
+ }
+}
+
+
+
+
+
+void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ // Bresenham-3D algorithm for drawing lines:
+ int dx = abs(a_RelX2 - a_RelX1);
+ int dy = abs(a_RelY2 - a_RelY1);
+ int dz = abs(a_RelZ2 - a_RelZ1);
+ int sx = (a_RelX1 < a_RelX2) ? 1 : -1;
+ int sy = (a_RelY1 < a_RelY2) ? 1 : -1;
+ int sz = (a_RelZ1 < a_RelZ2) ? 1 : -1;
+ int err = dx - dz;
+
+ if (dx >= std::max(dy, dz)) // x dominant
+ {
+ int yd = dy - dx / 2;
+ int zd = dz - dx / 2;
+
+ while (true)
+ {
+ RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
+
+ if (a_RelX1 == a_RelX2)
+ {
+ break;
+ }
+
+ if (yd >= 0) // move along y
+ {
+ a_RelY1 += sy;
+ yd -= dx;
+ }
+
+ if (zd >= 0) // move along z
+ {
+ a_RelZ1 += sz;
+ zd -= dx;
+ }
+
+ // move along x
+ a_RelX1 += sx;
+ yd += dy;
+ zd += dz;
+ }
+ }
+ else if (dy >= std::max(dx, dz)) // y dominant
+ {
+ int xd = dx - dy / 2;
+ int zd = dz - dy / 2;
+
+ while (true)
+ {
+ RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
+
+ if (a_RelY1 == a_RelY2)
+ {
+ break;
+ }
+
+ if (xd >= 0) // move along x
+ {
+ a_RelX1 += sx;
+ xd -= dy;
+ }
+
+ if (zd >= 0) // move along z
+ {
+ a_RelZ1 += sz;
+ zd -= dy;
+ }
+
+ // move along y
+ a_RelY1 += sy;
+ xd += dx;
+ zd += dz;
+ }
+ }
+ else
+ {
+ // z dominant
+ ASSERT(dz >= std::max(dx, dy));
+ int xd = dx - dz / 2;
+ int yd = dy - dz / 2;
+
+ while (true)
+ {
+ RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
+
+ if (a_RelZ1 == a_RelZ2)
+ {
+ break;
+ }
+
+ if (xd >= 0) // move along x
+ {
+ a_RelX1 += sx;
+ xd -= dz;
+ }
+
+ if (yd >= 0) // move along y
+ {
+ a_RelY1 += sy;
+ yd -= dz;
+ }
+
+ // move along z
+ a_RelZ1 += sz;
+ xd += dx;
+ yd += dy;
+ }
+ } // if (which dimension is dominant)
+}
+
+
+
+
+
+void cBlockArea::RotateCCW(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to rotate, just use the NoMeta function
+ RotateCCWNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = m_SizeX - x - 1;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = z;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
+ int OldIdx = MakeIndex(x, y, z);
+ NewTypes[NewIdx] = m_BlockTypes[OldIdx];
+ NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]);
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockTypes, NewTypes);
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewTypes;
+ delete[] NewMetas;
+
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::RotateCW(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to rotate, just use the NoMeta function
+ RotateCWNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = x;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = m_SizeZ - z - 1;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
+ int OldIdx = MakeIndex(x, y, z);
+ NewTypes[NewIdx] = m_BlockTypes[OldIdx];
+ NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]);
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockTypes, NewTypes);
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewTypes;
+ delete[] NewMetas;
+
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::MirrorXY(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to mirror, just use the NoMeta function
+ MirrorXYNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
+ int HalfZ = m_SizeZ / 2;
+ int MaxZ = m_SizeZ - 1;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < HalfZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int Idx1 = MakeIndex(x, y, z);
+ int Idx2 = MakeIndex(x, y, MaxZ - z);
+ std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
+ NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXY(m_BlockMetas[Idx1]);
+ NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXY(m_BlockMetas[Idx2]);
+ m_BlockMetas[Idx1] = Meta2;
+ m_BlockMetas[Idx2] = Meta1;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::MirrorXZ(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to mirror, just use the NoMeta function
+ MirrorXZNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
+ int HalfY = m_SizeY / 2;
+ int MaxY = m_SizeY - 1;
+ for (int y = 0; y < HalfY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int Idx1 = MakeIndex(x, y, z);
+ int Idx2 = MakeIndex(x, MaxY - y, z);
+ std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
+ NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXZ(m_BlockMetas[Idx1]);
+ NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXZ(m_BlockMetas[Idx2]);
+ m_BlockMetas[Idx1] = Meta2;
+ m_BlockMetas[Idx2] = Meta1;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::MirrorYZ(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to mirror, just use the NoMeta function
+ MirrorYZNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
+ int HalfX = m_SizeX / 2;
+ int MaxX = m_SizeX - 1;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < HalfX; x++)
+ {
+ int Idx1 = MakeIndex(x, y, z);
+ int Idx2 = MakeIndex(MaxX - x, y, z);
+ std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
+ NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorYZ(m_BlockMetas[Idx1]);
+ NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorYZ(m_BlockMetas[Idx2]);
+ m_BlockMetas[Idx1] = Meta2;
+ m_BlockMetas[Idx2] = Meta1;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::RotateCCWNoMeta(void)
+{
+ if (HasBlockTypes())
+ {
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = m_SizeX - x - 1;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = z;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockTypes, NewTypes);
+ delete[] NewTypes;
+ }
+ if (HasBlockMetas())
+ {
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = m_SizeX - x - 1;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = z;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewMetas;
+ }
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::RotateCWNoMeta(void)
+{
+ if (HasBlockTypes())
+ {
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = m_SizeZ - z - 1;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = x;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
+ } // for y
+ } // for x
+ } // for z
+ std::swap(m_BlockTypes, NewTypes);
+ delete[] NewTypes;
+ }
+ if (HasBlockMetas())
+ {
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = m_SizeZ - z - 1;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = x;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
+ } // for y
+ } // for x
+ } // for z
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewMetas;
+ }
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::MirrorXYNoMeta(void)
+{
+ int HalfZ = m_SizeZ / 2;
+ int MaxZ = m_SizeZ - 1;
+ if (HasBlockTypes())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < HalfZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, y, MaxZ - z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockTypes)
+
+ if (HasBlockMetas())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < HalfZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, y, MaxZ - z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockMetas)
+}
+
+
+
+
+
+void cBlockArea::MirrorXZNoMeta(void)
+{
+ int HalfY = m_SizeY / 2;
+ int MaxY = m_SizeY - 1;
+ if (HasBlockTypes())
+ {
+ for (int y = 0; y < HalfY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, MaxY - y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockTypes)
+
+ if (HasBlockMetas())
+ {
+ for (int y = 0; y < HalfY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, MaxY - y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockMetas)
+}
+
+
+
+
+
+void cBlockArea::MirrorYZNoMeta(void)
+{
+ int HalfX = m_SizeX / 2;
+ int MaxX = m_SizeX - 1;
+ if (HasBlockTypes())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < HalfX; x++)
+ {
+ std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(MaxX - x, y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockTypes)
+
+ if (HasBlockMetas())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < HalfX; x++)
+ {
+ std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(MaxX - x, y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockMetas)
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
+{
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockTypes have not been read!");
+ return;
+ }
+ m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_BlockType;
+}
+
+
+
+
+
+void cBlockArea::SetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
+{
+ SetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta)
+{
+ SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockMeta, m_BlockMetas);
+}
+
+
+
+
+
+void cBlockArea::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta)
+{
+ SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta, m_BlockMetas);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight)
+{
+ SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockLight, m_BlockLight);
+}
+
+
+
+
+
+void cBlockArea::SetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight)
+{
+ SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockLight, m_BlockLight);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight)
+{
+ SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockSkyLight, m_BlockSkyLight);
+}
+
+
+
+
+
+void cBlockArea::SetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight)
+{
+ SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockSkyLight, m_BlockSkyLight);
+}
+
+
+
+
+
+BLOCKTYPE cBlockArea::GetRelBlockType(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockTypes have not been read!");
+ return E_BLOCK_AIR;
+ }
+ return m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)];
+}
+
+
+
+
+
+BLOCKTYPE cBlockArea::GetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockMetas);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockMetas);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockLight);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockLight);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockSkyLight);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockSkyLight);
+}
+
+
+
+
+
+void cBlockArea::SetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ SetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ int idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("%s: BlockTypes not available but requested to be written to.", __FUNCTION__);
+ }
+ else
+ {
+ m_BlockTypes[idx] = a_BlockType;
+ }
+ if (m_BlockMetas == NULL)
+ {
+ LOGWARNING("%s: BlockMetas not available but requested to be written to.", __FUNCTION__);
+ }
+ else
+ {
+ m_BlockMetas[idx] = a_BlockMeta;
+ }
+}
+
+
+
+
+
+void cBlockArea::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
+{
+ return GetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
+{
+ int idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockTypes have not been read!");
+ a_BlockType = E_BLOCK_AIR;
+ }
+ else
+ {
+ a_BlockType = m_BlockTypes[idx];
+ }
+
+ if (m_BlockMetas == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockMetas have not been read!");
+ a_BlockMeta = 0;
+ }
+ else
+ {
+ a_BlockMeta = m_BlockMetas[idx];
+ }
+}
+
+
+
+
+
+int cBlockArea::GetDataTypes(void) const
+{
+ int res = 0;
+ if (m_BlockTypes != NULL)
+ {
+ res |= baTypes;
+ }
+ if (m_BlockMetas != NULL)
+ {
+ res |= baMetas;
+ }
+ if (m_BlockLight != NULL)
+ {
+ res |= baLight;
+ }
+ if (m_BlockSkyLight != NULL)
+ {
+ res |= baSkyLight;
+ }
+ return res;
+}
+
+
+
+
+
+bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
+{
+ ASSERT(m_BlockTypes == NULL); // Has been cleared
+
+ if (a_DataTypes & baTypes)
+ {
+ m_BlockTypes = new BLOCKTYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockTypes == NULL)
+ {
+ return false;
+ }
+ }
+ if (a_DataTypes & baMetas)
+ {
+ m_BlockMetas = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockMetas == NULL)
+ {
+ delete[] m_BlockTypes;
+ return false;
+ }
+ }
+ if (a_DataTypes & baLight)
+ {
+ m_BlockLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockLight == NULL)
+ {
+ delete[] m_BlockMetas;
+ delete[] m_BlockTypes;
+ return false;
+ }
+ }
+ if (a_DataTypes & baSkyLight)
+ {
+ m_BlockSkyLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockSkyLight == NULL)
+ {
+ delete[] m_BlockLight;
+ delete[] m_BlockMetas;
+ delete[] m_BlockTypes;
+ return false;
+ }
+ }
+ m_SizeX = a_SizeX;
+ m_SizeY = a_SizeY;
+ m_SizeZ = a_SizeZ;
+ return true;
+}
+
+
+
+
+
+int cBlockArea::MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ ASSERT(a_RelX >= 0);
+ ASSERT(a_RelX < m_SizeX);
+ ASSERT(a_RelY >= 0);
+ ASSERT(a_RelY < m_SizeY);
+ ASSERT(a_RelZ >= 0);
+ ASSERT(a_RelZ < m_SizeZ);
+
+ return a_RelX + a_RelZ * m_SizeX + a_RelY * m_SizeX * m_SizeZ;
+}
+
+
+
+
+
+void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
+{
+ if (a_Array == NULL)
+ {
+ LOGWARNING("cBlockArea: datatype has not been read!");
+ return;
+ }
+ a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value;
+}
+
+
+
+
+
+void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
+{
+ SetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Value, a_Array);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const
+{
+ if (a_Array == NULL)
+ {
+ LOGWARNING("cBlockArea: datatype has not been read!");
+ return 16;
+ }
+ return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)];
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const
+{
+ return GetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Array);
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBlockArea::cChunkReader:
+
+cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) :
+ m_Area(a_Area),
+ m_OriginX(a_Area.m_OriginX),
+ m_OriginY(a_Area.m_OriginY),
+ m_OriginZ(a_Area.m_OriginZ)
+{
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc)
+{
+ int SizeY = m_Area.m_SizeY;
+ int MinY = m_OriginY;
+
+ // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
+ // OffX, OffZ are the offsets of the current chunk data from the area origin
+ // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
+ int SizeX = cChunkDef::Width;
+ int SizeZ = cChunkDef::Width;
+ int OffX, OffZ;
+ int BaseX, BaseZ;
+ OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX;
+ if (OffX < 0)
+ {
+ BaseX = -OffX;
+ SizeX += OffX; // SizeX is decreased, OffX is negative
+ OffX = 0;
+ }
+ else
+ {
+ BaseX = 0;
+ }
+ OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ;
+ if (OffZ < 0)
+ {
+ BaseZ = -OffZ;
+ SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
+ OffZ = 0;
+ }
+ else
+ {
+ BaseZ = 0;
+ }
+ // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
+ if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX)
+ {
+ SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX);
+ }
+ if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ)
+ {
+ SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ);
+ }
+
+ for (int y = 0; y < SizeY; y++)
+ {
+ int ChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int ChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int ChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ);
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
+{
+ m_CurrentChunkX = a_ChunkX;
+ m_CurrentChunkZ = a_ChunkZ;
+ return true;
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes)
+{
+ if (m_Area.m_BlockTypes == NULL)
+ {
+ // Don't want BlockTypes
+ return;
+ }
+
+ int SizeY = m_Area.m_SizeY;
+ int MinY = m_OriginY;
+
+ // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
+ // OffX, OffZ are the offsets of the current chunk data from the area origin
+ // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
+ int SizeX = cChunkDef::Width;
+ int SizeZ = cChunkDef::Width;
+ int OffX, OffZ;
+ int BaseX, BaseZ;
+ OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX;
+ if (OffX < 0)
+ {
+ BaseX = -OffX;
+ SizeX += OffX; // SizeX is decreased, OffX is negative
+ OffX = 0;
+ }
+ else
+ {
+ BaseX = 0;
+ }
+ OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ;
+ if (OffZ < 0)
+ {
+ BaseZ = -OffZ;
+ SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
+ OffZ = 0;
+ }
+ else
+ {
+ BaseZ = 0;
+ }
+ // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
+ if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX)
+ {
+ SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX);
+ }
+ if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ)
+ {
+ SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ);
+ }
+
+ for (int y = 0; y < SizeY; y++)
+ {
+ int ChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int ChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int ChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetBlock(a_BlockTypes, ChunkX, ChunkY, ChunkZ);
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockMeta(const NIBBLETYPE * a_BlockMetas)
+{
+ if (m_Area.m_BlockMetas == NULL)
+ {
+ // Don't want metas
+ return;
+ }
+ CopyNibbles(m_Area.m_BlockMetas, a_BlockMetas);
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockLight(const NIBBLETYPE * a_BlockLight)
+{
+ if (m_Area.m_BlockLight == NULL)
+ {
+ // Don't want light
+ return;
+ }
+ CopyNibbles(m_Area.m_BlockLight, a_BlockLight);
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight)
+{
+ if (m_Area.m_BlockSkyLight == NULL)
+ {
+ // Don't want skylight
+ return;
+ }
+ CopyNibbles(m_Area.m_BlockSkyLight, a_BlockSkyLight);
+}
+
+
+
+
+
+void cBlockArea::CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
+{
+ int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
+ int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
+ int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
+ BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[NewSizeX * NewSizeY * NewSizeZ];
+ int idx = 0;
+ for (int y = 0; y < NewSizeY; y++)
+ {
+ for (int z = 0; z < NewSizeZ; z++)
+ {
+ for (int x = 0; x < NewSizeX; x++)
+ {
+ int OldIndex = MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ);
+ NewBlockTypes[idx++] = m_BlockTypes[OldIndex];
+ } // for x
+ } // for z
+ } // for y
+ delete m_BlockTypes;
+ m_BlockTypes = NewBlockTypes;
+}
+
+
+
+
+
+void cBlockArea::CropNibbles(NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
+{
+ int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
+ int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
+ int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
+ NIBBLETYPE * NewNibbles = new NIBBLETYPE[NewSizeX * NewSizeY * NewSizeZ];
+ int idx = 0;
+ for (int y = 0; y < NewSizeY; y++)
+ {
+ for (int z = 0; z < NewSizeZ; z++)
+ {
+ for (int x = 0; x < NewSizeX; x++)
+ {
+ NewNibbles[idx++] = a_Array[MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ)];
+ } // for x
+ } // for z
+ } // for y
+ delete a_Array;
+ a_Array = NewNibbles;
+}
+
+
+
+
+
+void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
+{
+ int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX;
+ int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY;
+ int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ;
+ int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
+ BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount];
+ memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE));
+ int OldIndex = 0;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX;
+ int idx = IndexBaseZ + a_SubMinX;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ NewBlockTypes[idx++] = m_BlockTypes[OldIndex++];
+ } // for x
+ } // for z
+ } // for y
+ delete m_BlockTypes;
+ m_BlockTypes = NewBlockTypes;
+}
+
+
+
+
+
+void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
+{
+ int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX;
+ int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY;
+ int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ;
+ int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
+ NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount];
+ memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE));
+ int OldIndex = 0;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX;
+ int idx = IndexBaseZ + a_SubMinX;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ NewNibbles[idx++] = a_Array[OldIndex++];
+ } // for x
+ } // for z
+ } // for y
+ delete a_Array;
+ a_Array = NewNibbles;
+}
+
+
+
+
+
+bool cBlockArea::LoadFromSchematicNBT(cParsedNBT & a_NBT)
+{
+ int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
+ if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String))
+ {
+ AString Materials = a_NBT.GetString(TMaterials);
+ if (Materials.compare("Alpha") != 0)
+ {
+ LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str());
+ return false;
+ }
+ }
+ int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width");
+ int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height");
+ int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length");
+ if (
+ (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) ||
+ (a_NBT.GetType(TSizeX) != TAG_Short) ||
+ (a_NBT.GetType(TSizeY) != TAG_Short) ||
+ (a_NBT.GetType(TSizeZ) != TAG_Short)
+ )
+ {
+ 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)
+ );
+ return false;
+ }
+
+ int SizeX = a_NBT.GetShort(TSizeX);
+ int SizeY = a_NBT.GetShort(TSizeY);
+ int SizeZ = a_NBT.GetShort(TSizeZ);
+ if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1))
+ {
+ LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ);
+ return false;
+ }
+
+ int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks");
+ int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data");
+ if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray))
+ {
+ LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes);
+ return false;
+ }
+ bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray);
+
+ Clear();
+ SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (baTypes | baMetas) : baTypes);
+
+ // Copy the block types and metas:
+ int NumBytes = m_SizeX * m_SizeY * m_SizeZ;
+ if (a_NBT.GetDataLength(TBlockTypes) < NumBytes)
+ {
+ LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
+ NumBytes, a_NBT.GetDataLength(TBlockTypes)
+ );
+ NumBytes = a_NBT.GetDataLength(TBlockTypes);
+ }
+ memcpy(m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes);
+
+ if (AreMetasPresent)
+ {
+ int NumBytes = m_SizeX * m_SizeY * m_SizeZ;
+ if (a_NBT.GetDataLength(TBlockMetas) < NumBytes)
+ {
+ LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
+ NumBytes, a_NBT.GetDataLength(TBlockMetas)
+ );
+ NumBytes = a_NBT.GetDataLength(TBlockMetas);
+ }
+ memcpy(m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes);
+ }
+
+ return true;
+}
+
+
+
+
+void cBlockArea::RelSetData(
+ int a_RelX, int a_RelY, int a_RelZ,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ int Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ m_BlockTypes[Index] = a_BlockType;
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ m_BlockMetas[Index] = a_BlockMeta;
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ m_BlockLight[Index] = a_BlockLight;
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ m_BlockSkyLight[Index] = a_BlockSkyLight;
+ }
+}
+
+
+
+
diff --git a/src/BlockArea.h b/src/BlockArea.h
new file mode 100644
index 000000000..075cc99ec
--- /dev/null
+++ b/src/BlockArea.h
@@ -0,0 +1,310 @@
+
+// BlockArea.h
+
+// Interfaces to the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries
+// The object also supports writing the blockdata back into cWorld, even into other coords
+
+// NOTE: All Nibble values (meta, blocklight, skylight) are stored one-nibble-per-byte for faster access / editting!
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd: World.h
+class cWorld;
+
+// fwd: FastNBT.h
+class cParsedNBT;
+
+
+
+
+
+// tolua_begin
+class cBlockArea
+{
+ // tolua_end
+ DISALLOW_COPY_AND_ASSIGN(cBlockArea);
+ // tolua_begin
+
+public:
+
+ /// What data is to be queried (bit-mask)
+ enum
+ {
+ baTypes = 1,
+ baMetas = 2,
+ baLight = 4,
+ baSkyLight = 8,
+ } ;
+
+ enum eMergeStrategy
+ {
+ msOverwrite,
+ msFillAir,
+ msImprint,
+ msLake,
+ } ;
+
+ cBlockArea(void);
+ ~cBlockArea();
+
+ /// Clears the data stored to reclaim memory
+ void Clear(void);
+
+ /** Creates a new area of the specified size and contents.
+ Origin is set to all zeroes.
+ BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light.
+ */
+ void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas);
+
+ /// Resets the origin. No other changes are made, contents are untouched.
+ void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ);
+
+ /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive.
+ bool Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas);
+
+ // TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write
+ // A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again
+
+ /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all
+ bool Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas);
+
+ /// Copies this object's contents into the specified BlockArea.
+ void CopyTo(cBlockArea & a_Into) const;
+
+ /// Copies the contents from the specified BlockArea into this object.
+ void CopyFrom(const cBlockArea & a_From);
+
+ /// For testing purposes only, dumps the area into a file.
+ void DumpToRawFile(const AString & a_FileName);
+
+ /// Loads an area from a .schematic file. Returns true if successful
+ bool LoadFromSchematicFile(const AString & a_FileName);
+
+ /// Saves the area into a .schematic file. Returns true if successful
+ bool SaveToSchematicFile(const AString & a_FileName);
+
+ /// Crops the internal contents by the specified amount of blocks from each border.
+ void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
+
+ /// Expands the internal contents by the specified amount of blocks from each border
+ void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
+
+ /** Merges another block area into this one, using the specified block combinating strategy
+ This function combines another BlockArea into the current object.
+ The strategy parameter specifies how individual blocks are combined together, using the table below.
+
+ | area block | result |
+ | this | Src | msOverwrite | msFillAir | msImprint |
+ +------+-----+-------------+-----------+-----------+
+ | air | air | air | air | air |
+ | A | air | air | A | A |
+ | air | B | B | B | B |
+ | A | B | B | A | B |
+
+ So to sum up:
+ - msOverwrite completely overwrites all blocks with the Src's blocks
+ - msFillAir overwrites only those blocks that were air
+ - msImprint overwrites with only those blocks that are non-air
+
+ Special strategies:
+ msLake (evaluate top-down, first match wins):
+ | area block | |
+ | this | Src | result |
+ +----------+--------+--------+
+ | A | sponge | A | Sponge is the NOP block
+ | * | air | air | Air always gets hollowed out, even under the oceans
+ | water | * | water | Water is never overwritten
+ | lava | * | lava | Lava is never overwritten
+ | * | water | water | Water always overwrites anything
+ | * | lava | lava | Lava always overwrites anything
+ | dirt | stone | stone | Stone overwrites dirt
+ | grass | stone | stone | ... and grass
+ | mycelium | stone | stone | ... and mycelium
+ | A | stone | A | ... but nothing else
+ | A | * | A | Everything else is left as it is
+
+ */
+ void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy);
+
+ /// Fills the entire block area with the specified data
+ void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f);
+
+ /// Fills a cuboid inside the block area with the specified data
+ void FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
+ NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
+ );
+
+ /// Draws a line from between two points with the specified data
+ void RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
+ NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
+ );
+
+ /// Rotates the entire area counter-clockwise around the Y axis
+ void RotateCCW(void);
+
+ /// Rotates the entire area clockwise around the Y axis
+ void RotateCW(void);
+
+ /// Mirrors the entire area around the XY plane
+ void MirrorXY(void);
+
+ /// Mirrors the entire area around the XZ plane
+ void MirrorXZ(void);
+
+ /// Mirrors the entire area around the YZ plane
+ void MirrorYZ(void);
+
+ /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta
+ void RotateCCWNoMeta(void);
+
+ /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta
+ void RotateCWNoMeta(void);
+
+ /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta
+ void MirrorXYNoMeta(void);
+
+ /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta
+ void MirrorXZNoMeta(void);
+
+ /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta
+ void MirrorYZNoMeta(void);
+
+ // Setters:
+ void SetRelBlockType (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType);
+ void SetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
+ void SetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta);
+ void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta);
+ void SetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight);
+ void SetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight);
+ void SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight);
+ void SetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight);
+
+ // Getters:
+ BLOCKTYPE GetRelBlockType (int a_RelX, int a_RelY, int a_RelZ) const;
+ BLOCKTYPE GetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ) const;
+ NIBBLETYPE GetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ) const;
+ NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ) const;
+ NIBBLETYPE GetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ) const;
+ NIBBLETYPE GetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ) const;
+ NIBBLETYPE GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const;
+ NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ) const;
+
+ void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ void GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
+ void GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
+
+ int GetSizeX(void) const { return m_SizeX; }
+ int GetSizeY(void) const { return m_SizeY; }
+ int GetSizeZ(void) const { return m_SizeZ; }
+
+ int GetOriginX(void) const { return m_OriginX; }
+ int GetOriginY(void) const { return m_OriginY; }
+ int GetOriginZ(void) const { return m_OriginZ; }
+
+ /// Returns the datatypes that are stored in the object (bitmask of baXXX values)
+ int GetDataTypes(void) const;
+
+ bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); }
+ bool HasBlockMetas (void) const { return (m_BlockMetas != NULL); }
+ bool HasBlockLights (void) const { return (m_BlockLight != NULL); }
+ bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != NULL); }
+
+ // tolua_end
+
+ // Clients can use these for faster access to all blocktypes. Be careful though!
+ /// Returns the internal pointer to the block types
+ BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; }
+ NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block!
+ NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block!
+ NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block!
+ int GetBlockCount(void) const { return m_SizeX * m_SizeY * m_SizeZ; }
+ int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const;
+
+protected:
+ friend class cChunkDesc;
+
+ class cChunkReader :
+ public cChunkDataCallback
+ {
+ public:
+ cChunkReader(cBlockArea & a_Area);
+
+ protected:
+ cBlockArea & m_Area;
+ int m_OriginX;
+ int m_OriginY;
+ int m_OriginZ;
+ int m_CurrentChunkX;
+ int m_CurrentChunkZ;
+
+ void CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc);
+
+ // cChunkDataCallback overrides:
+ virtual bool Coords (int a_ChunkX, int a_ChunkZ) override;
+ virtual void BlockTypes (const BLOCKTYPE * a_BlockTypes) override;
+ virtual void BlockMeta (const NIBBLETYPE * a_BlockMetas) override;
+ virtual void BlockLight (const NIBBLETYPE * a_BlockLight) override;
+ virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override;
+ } ;
+
+ typedef NIBBLETYPE * NIBBLEARRAY;
+
+
+ int m_OriginX;
+ int m_OriginY;
+ int m_OriginZ;
+ int m_SizeX;
+ int m_SizeY;
+ int m_SizeZ;
+
+ BLOCKTYPE * m_BlockTypes;
+ NIBBLETYPE * m_BlockMetas; // Each meta is stored as a separate byte for faster access
+ NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access
+ NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access
+
+ /// Clears the data stored and prepares a fresh new block area with the specified dimensions
+ bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes);
+
+ // Basic Setters:
+ void SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array);
+ void SetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array);
+
+ // Basic Getters:
+ NIBBLETYPE GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const;
+ NIBBLETYPE GetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const;
+
+ // Crop helpers:
+ void CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
+ void CropNibbles (NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
+
+ // Expand helpers:
+ void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
+ void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
+
+ /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful.
+ bool LoadFromSchematicNBT(cParsedNBT & a_NBT);
+
+ /// Sets the specified datatypes at the specified location.
+ void RelSetData(
+ int a_RelX, int a_RelY, int a_RelZ,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+ );
+ // tolua_begin
+} ;
+// tolua_end
+
+
+
+
diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp
new file mode 100644
index 000000000..41a488717
--- /dev/null
+++ b/src/BlockEntities/BlockEntity.cpp
@@ -0,0 +1,44 @@
+
+// BlockEntity.cpp
+
+// Implements the cBlockEntity class that is the common ancestor for all block entities
+
+#include "Globals.h"
+#include "BlockEntity.h"
+#include "ChestEntity.h"
+#include "DispenserEntity.h"
+#include "DropperEntity.h"
+#include "FurnaceEntity.h"
+#include "HopperEntity.h"
+#include "JukeboxEntity.h"
+#include "NoteEntity.h"
+#include "SignEntity.h"
+
+
+
+
+
+cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_DISPENSER: return new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
+ case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
+ case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ }
+ LOGD("%s: Requesting creation of an unknown block entity - block type %d (%s)",
+ __FUNCTION__, a_BlockType, ItemTypeToString(a_BlockType).c_str()
+ );
+ return NULL;
+}
+
+
+
+
diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h
new file mode 100644
index 000000000..0d358b556
--- /dev/null
+++ b/src/BlockEntities/BlockEntity.h
@@ -0,0 +1,106 @@
+
+#pragma once
+
+#include "../ClientHandle.h"
+#include "../World.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+};
+
+class cPlayer;
+class cPacket;
+
+
+
+
+
+// tolua_begin
+class cBlockEntity
+{
+protected:
+ cBlockEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ m_PosX(a_BlockX),
+ m_PosY(a_BlockY),
+ m_PosZ(a_BlockZ),
+ m_RelX(a_BlockX - cChunkDef::Width * FAST_FLOOR_DIV(a_BlockX, cChunkDef::Width)),
+ m_RelZ(a_BlockZ - cChunkDef::Width * FAST_FLOOR_DIV(a_BlockZ, cChunkDef::Width)),
+ m_BlockType(a_BlockType),
+ m_World(a_World)
+ {
+ }
+
+public:
+ // tolua_end
+
+ virtual ~cBlockEntity() {}; // force a virtual destructor in all descendants
+
+ virtual void Destroy(void) {};
+
+ void SetWorld(cWorld * a_World)
+ {
+ m_World = a_World;
+ }
+
+ /// Creates a new block entity for the specified block type
+ /// If a_World is valid, then the entity is created bound to that world
+ /// Returns NULL for unknown block types
+ static cBlockEntity * CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World = NULL);
+
+ static const char * GetClassStatic(void) // Needed for ManualBindings's ForEach templates
+ {
+ return "cBlockEntity";
+ }
+
+ // tolua_begin
+
+ // Position, in absolute block coordinates:
+ int GetPosX(void) const { return m_PosX; }
+ int GetPosY(void) const { return m_PosY; }
+ int GetPosZ(void) const { return m_PosZ; }
+
+ BLOCKTYPE GetBlockType(void) const { return m_BlockType; }
+
+ cWorld * GetWorld(void) const {return m_World; }
+
+ int GetChunkX(void) const { return FAST_FLOOR_DIV(m_PosX, cChunkDef::Width); }
+ int GetChunkZ(void) const { return FAST_FLOOR_DIV(m_PosZ, cChunkDef::Width); }
+
+ int GetRelX(void) const { return m_RelX; }
+ int GetRelZ(void) const { return m_RelZ; }
+
+ // tolua_end
+
+ virtual void SaveToJson (Json::Value & a_Value) = 0;
+
+ /// Called when a player uses this entity; should open the UI window
+ virtual void UsedBy( cPlayer * a_Player ) = 0;
+
+ /** Sends the packet defining the block entity to the client specified.
+ To send to all eligible clients, use cWorld::BroadcastBlockEntity()
+ */
+ 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) { return false; }
+
+protected:
+ /// Position in absolute block coordinates
+ int m_PosX, m_PosY, m_PosZ;
+
+ /// Position relative to the chunk, used to speed up ticking
+ int m_RelX, m_RelZ;
+
+ BLOCKTYPE m_BlockType;
+
+ cWorld * m_World;
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h
new file mode 100644
index 000000000..0846ae17e
--- /dev/null
+++ b/src/BlockEntities/BlockEntityWithItems.h
@@ -0,0 +1,86 @@
+
+// BlockEntityWithItems.h
+
+// Declares the cBlockEntityWithItems class representing a common ancestor for all block entities that have an ItemGrid
+
+
+
+
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../ItemGrid.h"
+
+
+
+
+
+// tolua_begin
+class cBlockEntityWithItems :
+ public cBlockEntity
+ // tolua_end
+ // tolua doesn't seem to support multiple inheritance?
+ , public cItemGrid::cListener
+ // tolua_begin
+{
+ typedef cBlockEntity super;
+
+public:
+ // tolua_end
+
+ cBlockEntityWithItems(
+ BLOCKTYPE a_BlockType, // Type of the block that the entity represents
+ int a_BlockX, int a_BlockY, int a_BlockZ, // Position of the block entity
+ int a_ItemGridWidth, int a_ItemGridHeight, // Dimensions of the ItemGrid
+ cWorld * a_World // Optional world to assign to the entity
+ ) :
+ super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World),
+ m_Contents(a_ItemGridWidth, a_ItemGridHeight)
+ {
+ m_Contents.AddListener(*this);
+ }
+
+ virtual void Destroy(void) override
+ {
+ // Drop the contents as pickups:
+ ASSERT(m_World != NULL);
+ cItems Pickups;
+ m_Contents.CopyToItems(Pickups);
+ m_Contents.Clear();
+ m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ);
+ }
+
+ // tolua_begin
+
+ const cItem & GetSlot(int a_SlotNum) const { return m_Contents.GetSlot(a_SlotNum); }
+ const cItem & GetSlot(int a_X, int a_Y) const { return m_Contents.GetSlot(a_X, a_Y); }
+
+ void SetSlot(int a_SlotNum, const cItem & a_Item) { m_Contents.SetSlot(a_SlotNum, a_Item); }
+ void SetSlot(int a_X, int a_Y, const cItem & a_Item) { m_Contents.SetSlot(a_X, a_Y, a_Item); }
+
+ /// Returns the ItemGrid used for storing the contents
+ cItemGrid & GetContents(void) { return m_Contents; }
+
+ // tolua_end
+
+ /// Const version of the GetContents() function for C++ type-safety
+ const cItemGrid & GetContents(void) const { return m_Contents; }
+
+protected:
+ cItemGrid m_Contents;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum)
+ {
+ ASSERT(a_Grid == &m_Contents);
+ if (m_World != NULL)
+ {
+ m_World->MarkChunkDirty(GetChunkX(), GetChunkZ());
+ }
+ }
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp
new file mode 100644
index 000000000..0bde5c9f2
--- /dev/null
+++ b/src/BlockEntities/ChestEntity.cpp
@@ -0,0 +1,172 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "ChestEntity.h"
+#include "../Item.h"
+#include "../Entities/Player.h"
+#include "../UI/Window.h"
+#include "lib/jsoncpp/include/json/json.h"
+
+
+
+
+
+cChestEntity::cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(E_BLOCK_CHEST, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World)
+{
+ cBlockEntityWindowOwner::SetBlockEntity(this);
+}
+
+
+
+
+
+cChestEntity::~cChestEntity()
+{
+ cWindow * Window = GetWindow();
+ if (Window != NULL)
+ {
+ Window->OwnerDestroyed();
+ }
+}
+
+
+
+
+
+bool cChestEntity::LoadFromJson(const Json::Value & a_Value)
+{
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
+
+ Json::Value AllSlots = a_Value.get("Slots", 0);
+ int SlotIdx = 0;
+ for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
+ {
+ cItem Item;
+ Item.FromJson(*itr);
+ SetSlot(SlotIdx, Item);
+ SlotIdx++;
+ }
+ return true;
+}
+
+
+
+
+
+void cChestEntity::SaveToJson(Json::Value & a_Value)
+{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ Json::Value AllSlots;
+ for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--)
+ {
+ Json::Value Slot;
+ m_Contents.GetSlot(i).GetJson(Slot);
+ AllSlots.append(Slot);
+ }
+ a_Value["Slots"] = AllSlots;
+}
+
+
+
+
+
+void cChestEntity::SendTo(cClientHandle & a_Client)
+{
+ // The chest entity doesn't need anything sent to the client when it's created / gets in the viewdistance
+ // All the actual handling is in the cWindow UI code that gets called when the chest is rclked
+
+ UNUSED(a_Client);
+}
+
+
+
+
+
+void cChestEntity::UsedBy(cPlayer * a_Player)
+{
+ // If the window is not created, open it anew:
+ cWindow * Window = GetWindow();
+ if (Window == NULL)
+ {
+ OpenNewWindow();
+ Window = GetWindow();
+ }
+
+ // Open the window for the player:
+ if (Window != NULL)
+ {
+ if (a_Player->GetWindow() != Window)
+ {
+ a_Player->OpenWindow(Window);
+ }
+ }
+
+ // This is rather a hack
+ // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now
+ // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first.
+ // The few false positives aren't much to worry about
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ);
+ m_World->MarkChunkDirty(ChunkX, ChunkZ);
+}
+
+
+
+
+
+void cChestEntity::OpenNewWindow(void)
+{
+ // Callback for opening together with neighbor chest:
+ class cOpenDouble :
+ public cChestCallback
+ {
+ cChestEntity * m_ThisChest;
+ public:
+ cOpenDouble(cChestEntity * a_ThisChest) :
+ m_ThisChest(a_ThisChest)
+ {
+ }
+
+ virtual bool Item(cChestEntity * a_Chest) override
+ {
+ // The primary chest should eb the one with lesser X or Z coord:
+ cChestEntity * Primary = a_Chest;
+ cChestEntity * Secondary = m_ThisChest;
+ if (
+ (Primary->GetPosX() > Secondary->GetPosX()) ||
+ (Primary->GetPosZ() > Secondary->GetPosZ())
+ )
+ {
+ std::swap(Primary, Secondary);
+ }
+ m_ThisChest->OpenWindow(new cChestWindow(Primary, Secondary));
+ return false;
+ }
+ } ;
+
+ // Scan neighbors for adjacent chests:
+ cOpenDouble OpenDbl(this);
+ if (
+ m_World->DoWithChestAt(m_PosX - 1, m_PosY, m_PosZ, OpenDbl) ||
+ m_World->DoWithChestAt(m_PosX + 1, m_PosY, m_PosZ, OpenDbl) ||
+ m_World->DoWithChestAt(m_PosX , m_PosY, m_PosZ - 1, OpenDbl) ||
+ m_World->DoWithChestAt(m_PosX , m_PosY, m_PosZ + 1, OpenDbl)
+ )
+ {
+ // The double-chest window has been opened in the callback
+ return;
+ }
+
+ // There is no chest neighbor, open a single-chest window:
+ OpenWindow(new cChestWindow(this));
+}
+
+
+
+
diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h
new file mode 100644
index 000000000..4f2c21e91
--- /dev/null
+++ b/src/BlockEntities/ChestEntity.h
@@ -0,0 +1,59 @@
+
+#pragma once
+
+#include "BlockEntityWithItems.h"
+#include "../UI/WindowOwner.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+};
+
+class cClientHandle;
+class cServer;
+class cNBTData;
+
+
+
+
+
+class cChestEntity : // tolua_export
+ public cBlockEntityWindowOwner,
+ // tolua_begin
+ public cBlockEntityWithItems
+{
+ typedef cBlockEntityWithItems super;
+
+public:
+ enum {
+ ContentsHeight = 3,
+ ContentsWidth = 9,
+ } ;
+
+ // tolua_end
+
+ /// Constructor used for normal operation
+ cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+
+ virtual ~cChestEntity();
+
+ static const char * GetClassStatic(void) { return "cChestEntity"; }
+
+ bool LoadFromJson(const Json::Value & a_Value);
+
+ // cBlockEntity overrides:
+ virtual void SaveToJson(Json::Value & a_Value) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+
+ /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate.
+ void OpenNewWindow(void);
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockEntities/DispenserEntity.cpp b/src/BlockEntities/DispenserEntity.cpp
new file mode 100644
index 000000000..374f3d6e3
--- /dev/null
+++ b/src/BlockEntities/DispenserEntity.cpp
@@ -0,0 +1,215 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "DispenserEntity.h"
+#include "../Entities/Player.h"
+#include "../Simulator/FluidSimulator.h"
+#include "../Chunk.h"
+
+
+
+
+
+cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, a_World)
+{
+ SetBlockEntity(this); // cBlockEntityWindowOwner
+}
+
+
+
+
+
+void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
+{
+ int DispX = m_RelX;
+ int DispY = m_PosY;
+ int DispZ = m_RelZ;
+ NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
+ AddDropSpenserDir(DispX, DispY, DispZ, Meta);
+ cChunk * DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispX, DispZ);
+ if (DispChunk == NULL)
+ {
+ // Would dispense into / interact with a non-loaded chunk, ignore the tick
+ return;
+ }
+ BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ);
+
+ // Dispense the item:
+ switch (m_Contents.GetSlot(a_SlotNum).m_ItemType)
+ {
+ case E_ITEM_BUCKET:
+ {
+ LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
+ switch (DispBlock)
+ {
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_WATER:
+ {
+ if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET))
+ {
+ DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0);
+ }
+ break;
+ }
+ case E_BLOCK_STATIONARY_LAVA:
+ case E_BLOCK_LAVA:
+ {
+ if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET))
+ {
+ DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0);
+ }
+ break;
+ }
+ default:
+ {
+ DropFromSlot(a_Chunk, a_SlotNum);
+ break;
+ }
+ }
+ break;
+ } // E_ITEM_BUCKET
+
+ case E_ITEM_WATER_BUCKET:
+ {
+ LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
+ if (EmptyLiquidBucket(DispBlock, a_SlotNum))
+ {
+ DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_WATER, 0);
+ }
+ else
+ {
+ DropFromSlot(a_Chunk, a_SlotNum);
+ }
+ break;
+ }
+
+ case E_ITEM_LAVA_BUCKET:
+ {
+ LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
+ if (EmptyLiquidBucket(DispBlock, a_SlotNum))
+ {
+ DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_LAVA, 0);
+ }
+ else
+ {
+ DropFromSlot(a_Chunk, a_SlotNum);
+ }
+ break;
+ }
+
+ case E_ITEM_SPAWN_EGG:
+ {
+ double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
+ double MobZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
+ if (m_World->SpawnMob(MobX, DispY, MobZ, (cMonster::eType)m_Contents.GetSlot(a_SlotNum).m_ItemDamage) >= 0)
+ {
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ break;
+ }
+
+ case E_BLOCK_TNT:
+ {
+ // Spawn a primed TNT entity, if space allows:
+ if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
+ {
+ double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
+ double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
+ m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 4, 0); // 4 seconds fuse, no initial velocity
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ break;
+ }
+
+ case E_ITEM_FLINT_AND_STEEL:
+ {
+ // Spawn fire if the block in front is air.
+ if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
+ {
+ DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0);
+ m_Contents.SetSlot(a_SlotNum, m_Contents.GetSlot(a_SlotNum).m_ItemType, m_Contents.GetSlot(a_SlotNum).m_ItemCount, m_Contents.GetSlot(a_SlotNum).m_ItemDamage + 1);
+ // If the durability has run out destroy the item.
+ if (m_Contents.GetSlot(a_SlotNum).m_ItemDamage > 64)
+ {
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ DropFromSlot(a_Chunk, a_SlotNum);
+ break;
+ }
+ } // switch (ItemType)
+}
+
+
+
+
+
+
+bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType)
+{
+ cItem LiquidBucket(a_BucketItemType, 1);
+ if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
+ {
+ // Special case: replacing one empty bucket with one full bucket
+ m_Contents.SetSlot(a_SlotNum, LiquidBucket);
+ return true;
+ }
+
+ // There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else
+ if (m_Contents.HowManyCanFit(LiquidBucket) < 1)
+ {
+ // Cannot fit into m_Contents
+ return false;
+ }
+
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ m_Contents.AddItem(LiquidBucket);
+ return true;
+}
+
+
+
+
+
+bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum)
+{
+ if (
+ (a_BlockInFront != E_BLOCK_AIR) &&
+ !IsBlockLiquid(a_BlockInFront) &&
+ !cFluidSimulator::CanWashAway(a_BlockInFront)
+ )
+ {
+ // Not a suitable block in front
+ return false;
+ }
+
+ cItem EmptyBucket(E_ITEM_BUCKET, 1);
+ if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
+ {
+ // Change the single full bucket present into a single empty bucket
+ m_Contents.SetSlot(a_SlotNum, EmptyBucket);
+ return true;
+ }
+
+ // There are full buckets stacked at this slot, check if we can fit in the empty bucket
+ if (m_Contents.HowManyCanFit(EmptyBucket) < 1)
+ {
+ // The empty bucket wouldn't fit into m_Contents
+ return false;
+ }
+
+ // The empty bucket fits in, remove one full bucket and add the empty one
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ m_Contents.AddItem(EmptyBucket);
+ return true;
+}
+
+
+
+
diff --git a/src/BlockEntities/DispenserEntity.h b/src/BlockEntities/DispenserEntity.h
new file mode 100644
index 000000000..fdfe4e5b4
--- /dev/null
+++ b/src/BlockEntities/DispenserEntity.h
@@ -0,0 +1,38 @@
+
+#pragma once
+
+#include "DropSpenserEntity.h"
+
+
+
+
+
+// tolua_begin
+class cDispenserEntity :
+ public cDropSpenserEntity
+{
+ typedef cDropSpenserEntity super;
+
+public:
+
+ // tolua_end
+
+ /// Constructor used for normal operation
+ cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+
+ static const char * GetClassStatic(void) { return "cDispenserEntity"; }
+
+private:
+ // cDropSpenser overrides:
+ virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override;
+
+ /// If such a bucket can fit, adds it to m_Contents and returns true
+ bool ScoopUpLiquid(int a_SlotNum, short a_BucketItemType);
+
+ /// If the a_BlockInFront is liquidable and the empty bucket can fit, does the m_Contents processing and returns true
+ bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum);
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp
new file mode 100644
index 000000000..823ed598f
--- /dev/null
+++ b/src/BlockEntities/DropSpenserEntity.cpp
@@ -0,0 +1,266 @@
+
+// DropSpenserEntity.cpp
+
+// Declares the cDropSpenserEntity class representing a common ancestor to the cDispenserEntity and cDropperEntity
+// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior
+
+#include "Globals.h"
+#include "DropSpenserEntity.h"
+#include "../Entities/Player.h"
+#include "../Chunk.h"
+
+
+
+
+
+cDropSpenserEntity::cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World),
+ m_ShouldDropSpense(false),
+ m_IsPowered(false)
+{
+ SetBlockEntity(this); // cBlockEntityWindowOwner
+}
+
+
+
+
+
+cDropSpenserEntity::~cDropSpenserEntity()
+{
+ // Tell window its owner is destroyed
+ cWindow * Window = GetWindow();
+ if (Window != NULL)
+ {
+ Window->OwnerDestroyed();
+ }
+}
+
+
+
+
+
+void cDropSpenserEntity::AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction)
+{
+ switch (a_Direction)
+ {
+ case E_META_DROPSPENSER_FACING_YM: a_BlockY--; return;
+ case E_META_DROPSPENSER_FACING_YP: a_BlockY++; return;
+ case E_META_DROPSPENSER_FACING_ZM: a_BlockZ--; return;
+ case E_META_DROPSPENSER_FACING_ZP: a_BlockZ++; return;
+ case E_META_DROPSPENSER_FACING_XM: a_BlockX--; return;
+ case E_META_DROPSPENSER_FACING_XP: a_BlockX++; return;
+ }
+ LOGWARNING("%s: Unhandled direction: %d", __FUNCTION__, a_Direction);
+ return;
+}
+
+
+
+
+
+void cDropSpenserEntity::DropSpense(cChunk & a_Chunk)
+{
+ // Pick one of the occupied slots:
+ int OccupiedSlots[9];
+ int SlotsCnt = 0;
+ for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--)
+ {
+ if (!m_Contents.GetSlot(i).IsEmpty())
+ {
+ OccupiedSlots[SlotsCnt] = i;
+ SlotsCnt++;
+ }
+ } // for i - m_Contents[]
+
+ if (SlotsCnt == 0)
+ {
+ // Nothing in the dropspenser, play the click sound
+ m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f);
+ return;
+ }
+
+ int RandomSlot = m_World->GetTickRandomNumber(SlotsCnt - 1);
+
+ // DropSpense the item, using the specialized behavior in the subclasses:
+ DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]);
+
+ // Broadcast a smoke and click effects:
+ NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
+ int SmokeDir = 0;
+ switch (Meta)
+ {
+ case E_META_DROPSPENSER_FACING_YP: SmokeDir = 4; break; // YP & YM don't have associated smoke dirs, just do 4 (centre of block)
+ case E_META_DROPSPENSER_FACING_YM: SmokeDir = 4; break;
+ case E_META_DROPSPENSER_FACING_XM: SmokeDir = 3; break;
+ case E_META_DROPSPENSER_FACING_XP: SmokeDir = 5; break;
+ case E_META_DROPSPENSER_FACING_ZM: SmokeDir = 1; break;
+ case E_META_DROPSPENSER_FACING_ZP: SmokeDir = 7; break;
+ }
+ m_World->BroadcastSoundParticleEffect(2000, m_PosX, m_PosY, m_PosZ, SmokeDir);
+ m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f);
+
+ // Update the UI window, if open:
+ cWindow * Window = GetWindow();
+ if (Window != NULL)
+ {
+ Window->BroadcastWholeWindow();
+ }
+}
+
+
+
+
+
+void cDropSpenserEntity::Activate(void)
+{
+ m_ShouldDropSpense = true;
+}
+
+
+
+
+
+void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered)
+{
+ if (a_IsPowered && !m_IsPowered)
+ {
+ Activate();
+ }
+ m_IsPowered = a_IsPowered;
+}
+
+
+
+
+
+bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ if (!m_ShouldDropSpense)
+ {
+ return false;
+ }
+
+ m_ShouldDropSpense = false;
+ DropSpense(a_Chunk);
+ return true;
+}
+
+
+
+
+
+bool cDropSpenserEntity::LoadFromJson(const Json::Value & a_Value)
+{
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
+
+ Json::Value AllSlots = a_Value.get("Slots", 0);
+ int SlotIdx = 0;
+ for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
+ {
+ cItem Contents;
+ Contents.FromJson(*itr);
+ m_Contents.SetSlot(SlotIdx, Contents);
+ SlotIdx++;
+ if (SlotIdx >= m_Contents.GetNumSlots())
+ {
+ return true;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+
+void cDropSpenserEntity::SaveToJson(Json::Value & a_Value)
+{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ Json::Value AllSlots;
+ int NumSlots = m_Contents.GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+ Json::Value Slot;
+ m_Contents.GetSlot(i).GetJson(Slot);
+ AllSlots.append(Slot);
+ }
+ a_Value["Slots"] = AllSlots;
+}
+
+
+
+
+
+void cDropSpenserEntity::SendTo(cClientHandle & a_Client)
+{
+ // Nothing needs to be sent
+ UNUSED(a_Client);
+}
+
+
+
+
+
+void cDropSpenserEntity::UsedBy(cPlayer * a_Player)
+{
+ cWindow * Window = GetWindow();
+ if (Window == NULL)
+ {
+ OpenWindow(new cDropSpenserWindow(m_PosX, m_PosY, m_PosZ, this));
+ Window = GetWindow();
+ }
+
+ if (Window != NULL)
+ {
+ if (a_Player->GetWindow() != Window)
+ {
+ a_Player->OpenWindow(Window);
+ }
+ }
+}
+
+
+
+
+
+void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum)
+{
+ int DispX = m_PosX;
+ int DispY = m_PosY;
+ int DispZ = m_PosZ;
+ NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
+ AddDropSpenserDir(DispX, DispY, DispZ, Meta);
+
+ cItems Pickups;
+ Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum));
+
+ const int PickupSpeed = m_World->GetTickRandomNumber(4) + 2; // At least 2, at most 6
+ int PickupSpeedX = 0, PickupSpeedY = 0, PickupSpeedZ = 0;
+ switch (Meta)
+ {
+ case E_META_DROPSPENSER_FACING_YP: PickupSpeedY = PickupSpeed; break;
+ case E_META_DROPSPENSER_FACING_YM: PickupSpeedY = -PickupSpeed; break;
+ case E_META_DROPSPENSER_FACING_XM: PickupSpeedX = -PickupSpeed; break;
+ case E_META_DROPSPENSER_FACING_XP: PickupSpeedX = PickupSpeed; break;
+ case E_META_DROPSPENSER_FACING_ZM: PickupSpeedZ = -PickupSpeed; break;
+ case E_META_DROPSPENSER_FACING_ZP: PickupSpeedZ = PickupSpeed; break;
+ }
+
+ double MicroX, MicroY, MicroZ;
+ MicroX = DispX + 0.5;
+ MicroY = DispY + 0.4; // Slightly less than half, to accomodate actual texture hole on DropSpenser
+ MicroZ = DispZ + 0.5;
+
+
+ m_World->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ, PickupSpeedX, PickupSpeedY, PickupSpeedZ);
+}
+
+
+
+
diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h
new file mode 100644
index 000000000..0e9039915
--- /dev/null
+++ b/src/BlockEntities/DropSpenserEntity.h
@@ -0,0 +1,89 @@
+
+// DropSpenser.h
+
+// Declares the cDropSpenser class representing a common ancestor to the cDispenserEntity and cDropperEntity
+// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior
+
+
+
+
+
+#pragma once
+
+#include "BlockEntityWithItems.h"
+#include "../UI/WindowOwner.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+}
+
+class cClientHandle;
+class cServer;
+
+
+
+
+
+class cDropSpenserEntity : // tolua_export
+ public cBlockEntityWindowOwner,
+ // tolua_begin
+ public cBlockEntityWithItems
+{
+ typedef cBlockEntityWithItems super;
+
+public:
+ enum {
+ ContentsHeight = 3,
+ ContentsWidth = 3,
+ } ;
+
+ // tolua_end
+
+ cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+ virtual ~cDropSpenserEntity();
+
+ static const char * GetClassStatic(void) { return "cDropSpenserEntity"; }
+
+ bool LoadFromJson(const Json::Value & a_Value);
+
+ // cBlockEntity overrides:
+ virtual void SaveToJson(Json::Value & a_Value) override;
+ virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+
+ // tolua_begin
+
+ /// Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups should materialize)
+ void AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction);
+
+ /// Sets the dropspenser to dropspense an item in the next tick
+ void Activate(void);
+
+ /// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate
+ void SetRedstonePower(bool a_IsPowered);
+
+ // tolua_end
+
+protected:
+ bool m_ShouldDropSpense; ///< If true, the dropspenser will dropspense an item in the next tick
+ bool m_IsPowered; ///< Set to true when the dropspenser receives redstone power.
+
+ /// Does the actual work on dropspensing an item. Chooses the slot, calls DropSpenseFromSlot() and handles smoke / sound effects
+ void DropSpense(cChunk & a_Chunk);
+
+ /// Override this function to provide the specific behavior for item dropspensing (drop / shoot / pour / ...)
+ virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) = 0;
+
+ /// Helper function, drops one item from the specified slot (like a dropper)
+ void DropFromSlot(cChunk & a_Chunk, int a_SlotNum);
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockEntities/DropperEntity.cpp b/src/BlockEntities/DropperEntity.cpp
new file mode 100644
index 000000000..5d4a8ad97
--- /dev/null
+++ b/src/BlockEntities/DropperEntity.cpp
@@ -0,0 +1,32 @@
+
+// DropperEntity.cpp
+
+// Implements the cRtopperEntity class representing a Dropper block entity
+
+#include "Globals.h"
+#include "DropperEntity.h"
+#include "../Entities/Player.h"
+#include "../Simulator/FluidSimulator.h"
+
+
+
+
+
+cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, a_World)
+{
+ SetBlockEntity(this); // cBlockEntityWindowOwner
+}
+
+
+
+
+
+void cDropperEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
+{
+ DropFromSlot(a_Chunk, a_SlotNum);
+}
+
+
+
+
diff --git a/src/BlockEntities/DropperEntity.h b/src/BlockEntities/DropperEntity.h
new file mode 100644
index 000000000..8e07bc6f8
--- /dev/null
+++ b/src/BlockEntities/DropperEntity.h
@@ -0,0 +1,46 @@
+
+// DropperEntity.h
+
+// Declares the cDropperEntity class representing a dropper block entity
+
+
+
+
+
+#pragma once
+
+#include "DropSpenserEntity.h"
+
+
+
+
+
+// tolua_begin
+class cDropperEntity :
+ public cDropSpenserEntity
+{
+ typedef cDropSpenserEntity super;
+
+public:
+
+ // tolua_end
+
+ /// Constructor used for normal operation
+ cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+
+ static const char * GetClassStatic(void) { return "cDropperEntity"; }
+
+protected:
+ // cDropSpenserEntity overrides:
+ virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override;
+
+ /** Takes an item from slot a_SlotNum and puts it into the container in front of the dropper.
+ Called when there's a container directly in front of the dropper,
+ so the dropper should store items there, rather than dropping.
+ */
+ void PutIntoContainer(cChunk & a_Chunk, int a_SlotNum, BLOCKTYPE a_ContainerBlock, int a_ContainerX, int a_ContainerY, int a_ContainerZ);
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp
new file mode 100644
index 000000000..d49e4f53b
--- /dev/null
+++ b/src/BlockEntities/FurnaceEntity.cpp
@@ -0,0 +1,479 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "FurnaceEntity.h"
+#include "../UI/Window.h"
+#include "../Entities/Player.h"
+#include "../Root.h"
+#include "../Chunk.h"
+#include "lib/jsoncpp/include/json/json.h"
+
+
+
+
+
+
+enum
+{
+ PROGRESSBAR_SMELTING = 0,
+ PROGRESSBAR_FUEL = 1,
+} ;
+
+
+
+
+
+cFurnaceEntity::cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World) :
+ super(E_BLOCK_FURNACE, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World),
+ m_BlockType(a_BlockType),
+ m_BlockMeta(a_BlockMeta),
+ m_CurrentRecipe(NULL),
+ m_IsCooking((a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_LIT_FURNACE)),
+ m_NeedCookTime(0),
+ m_TimeCooked(0),
+ m_FuelBurnTime(0),
+ m_TimeBurned(0),
+ m_LastProgressFuel(0),
+ m_LastProgressCook(0)
+{
+ cBlockEntityWindowOwner::SetBlockEntity(this);
+ m_Contents.AddListener(*this);
+}
+
+
+
+
+
+cFurnaceEntity::~cFurnaceEntity()
+{
+ // Tell window its owner is destroyed
+ cWindow * Window = GetWindow();
+ if (Window != NULL)
+ {
+ Window->OwnerDestroyed();
+ }
+}
+
+
+
+
+
+void cFurnaceEntity::UsedBy(cPlayer * a_Player)
+{
+ if (GetWindow() == NULL)
+ {
+ OpenWindow(new cFurnaceWindow(m_PosX, m_PosY, m_PosZ, this));
+ }
+ cWindow * Window = GetWindow();
+ if (Window != NULL)
+ {
+ if (a_Player->GetWindow() != Window)
+ {
+ a_Player->OpenWindow(Window);
+ BroadcastProgress(PROGRESSBAR_FUEL, m_LastProgressFuel);
+ BroadcastProgress(PROGRESSBAR_SMELTING, m_LastProgressCook);
+ }
+ }
+}
+
+
+
+
+
+/// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking.
+bool cFurnaceEntity::ContinueCooking(void)
+{
+ UpdateInput();
+ UpdateFuel();
+ return m_IsCooking;
+}
+
+
+
+
+
+bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ if (m_FuelBurnTime <= 0)
+ {
+ // No fuel is burning, reset progressbars and bail out
+ if ((m_LastProgressCook > 0) || (m_LastProgressFuel > 0))
+ {
+ UpdateProgressBars();
+ }
+ return false;
+ }
+
+ if (m_IsCooking)
+ {
+ m_TimeCooked++;
+ if (m_TimeCooked >= m_NeedCookTime)
+ {
+ // Finished smelting one item
+ FinishOne(a_Chunk);
+ }
+ }
+
+ m_TimeBurned++;
+ if (m_TimeBurned >= m_FuelBurnTime)
+ {
+ // The current fuel has been exhausted, use another one, if possible
+ BurnNewFuel();
+ }
+
+ UpdateProgressBars();
+
+ return true;
+}
+
+
+
+
+
+bool cFurnaceEntity::LoadFromJson(const Json::Value & a_Value)
+{
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
+
+ Json::Value AllSlots = a_Value.get("Slots", 0);
+ int SlotIdx = 0;
+ for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
+ {
+ cItem Item;
+ Item.FromJson(*itr);
+ SetSlot(SlotIdx, Item);
+ SlotIdx++;
+ }
+
+ m_NeedCookTime = (int)(a_Value.get("CookTime", 0).asDouble() / 50);
+ m_TimeCooked = (int)(a_Value.get("TimeCooked", 0).asDouble() / 50);
+ m_FuelBurnTime = (int)(a_Value.get("BurnTime", 0).asDouble() / 50);
+ m_TimeBurned = (int)(a_Value.get("TimeBurned", 0).asDouble() / 50);
+
+ return true;
+}
+
+
+
+
+
+void cFurnaceEntity::SaveToJson( Json::Value& a_Value )
+{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ Json::Value AllSlots;
+ int NumSlots = m_Contents.GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+ Json::Value Slot;
+ m_Contents.GetSlot(i).GetJson(Slot);
+ AllSlots.append(Slot);
+ }
+ a_Value["Slots"] = AllSlots;
+
+ a_Value["CookTime"] = m_NeedCookTime * 50;
+ a_Value["TimeCooked"] = m_TimeCooked * 50;
+ a_Value["BurnTime"] = m_FuelBurnTime * 50;
+ a_Value["TimeBurned"] = m_TimeBurned * 50;
+}
+
+
+
+
+
+void cFurnaceEntity::SendTo(cClientHandle & a_Client)
+{
+ // Nothing needs to be sent
+ UNUSED(a_Client);
+}
+
+
+
+
+
+void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value)
+{
+ cWindow * Window = GetWindow();
+ if (Window != NULL)
+ {
+ Window->BroadcastProgress(a_ProgressbarID, a_Value);
+ }
+}
+
+
+
+
+
+/// One item finished cooking
+void cFurnaceEntity::FinishOne(cChunk & a_Chunk)
+{
+ m_TimeCooked = 0;
+
+ if (m_Contents.GetSlot(fsOutput).IsEmpty())
+ {
+ m_Contents.SetSlot(fsOutput, *m_CurrentRecipe->Out);
+ }
+ else
+ {
+ m_Contents.ChangeSlotCount(fsOutput, m_CurrentRecipe->Out->m_ItemCount);
+ }
+ m_Contents.ChangeSlotCount(fsInput, -m_CurrentRecipe->In->m_ItemCount);
+
+ UpdateIsCooking();
+}
+
+
+
+
+
+void cFurnaceEntity::BurnNewFuel(void)
+{
+ cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe();
+ int NewTime = FR->GetBurnTime(m_Contents.GetSlot(fsFuel));
+ if (NewTime == 0)
+ {
+ // The item in the fuel slot is not suitable
+ m_FuelBurnTime = 0;
+ m_TimeBurned = 0;
+ SetIsCooking(false);
+ return;
+ }
+
+ // Is the input and output ready for cooking?
+ if (!CanCookInputToOutput())
+ {
+ return;
+ }
+
+ // Burn one new fuel:
+ m_FuelBurnTime = NewTime;
+ m_TimeBurned = 0;
+ SetIsCooking(true);
+ if (m_Contents.GetSlot(fsFuel).m_ItemType == E_ITEM_LAVA_BUCKET)
+ {
+ m_Contents.SetSlot(fsFuel, cItem(E_ITEM_BUCKET));
+ }
+ else
+ {
+ m_Contents.ChangeSlotCount(fsFuel, -1);
+ }
+}
+
+
+
+
+
+void cFurnaceEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ super::OnSlotChanged(a_ItemGrid, a_SlotNum);
+
+ if (m_World == NULL)
+ {
+ // The furnace isn't initialized yet, do no processing
+ return;
+ }
+
+ ASSERT(a_ItemGrid == &m_Contents);
+ switch (a_SlotNum)
+ {
+ case fsInput:
+ {
+ UpdateInput();
+ break;
+ }
+
+ case fsFuel:
+ {
+ UpdateFuel();
+ break;
+ }
+
+ case fsOutput:
+ {
+ UpdateOutput();
+ break;
+ }
+ }
+}
+
+
+
+
+
+
+/// Updates the current recipe, based on the current input
+void cFurnaceEntity::UpdateInput(void)
+{
+ if (!m_Contents.GetSlot(fsInput).IsStackableWith(m_LastInput))
+ {
+ // The input is different from what we had before, reset the cooking time
+ m_TimeCooked = 0;
+ }
+ m_LastInput = m_Contents.GetSlot(fsInput);
+
+ cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe();
+ m_CurrentRecipe = FR->GetRecipeFrom(m_Contents.GetSlot(fsInput));
+ if (!CanCookInputToOutput())
+ {
+ // This input cannot be cooked
+ m_NeedCookTime = 0;
+ SetIsCooking(false);
+ }
+ else
+ {
+ m_NeedCookTime = m_CurrentRecipe->CookTime;
+ SetIsCooking(true);
+
+ // Start burning new fuel if there's no flame now:
+ if (GetFuelBurnTimeLeft() <= 0)
+ {
+ BurnNewFuel();
+ }
+ }
+}
+
+
+
+
+
+/// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate
+void cFurnaceEntity::UpdateFuel(void)
+{
+ if (m_FuelBurnTime > m_TimeBurned)
+ {
+ // The current fuel is still burning, don't modify anything:
+ return;
+ }
+
+ // The current fuel is spent, try to burn some more:
+ BurnNewFuel();
+}
+
+
+
+
+
+/// Called when the output slot changes; starts burning if space became available
+void cFurnaceEntity::UpdateOutput(void)
+{
+ if (!CanCookInputToOutput())
+ {
+ // Cannot cook anymore:
+ m_TimeCooked = 0;
+ m_NeedCookTime = 0;
+ SetIsCooking(false);
+ return;
+ }
+
+ // No need to burn new fuel, the Tick() function will take care of that
+
+ // Can cook, start cooking if not already underway:
+ m_NeedCookTime = m_CurrentRecipe->CookTime;
+ SetIsCooking(m_FuelBurnTime > 0);
+}
+
+
+
+
+
+/// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned
+void cFurnaceEntity::UpdateIsCooking(void)
+{
+ if (
+ !CanCookInputToOutput() || // Cannot cook this
+ (m_FuelBurnTime <= 0) || // No fuel
+ (m_TimeBurned >= m_FuelBurnTime) // Fuel burnt out
+ )
+ {
+ // Reset everything
+ SetIsCooking(false);
+ m_TimeCooked = 0;
+ m_NeedCookTime = 0;
+ return;
+ }
+
+ SetIsCooking(true);
+}
+
+
+
+
+
+/// Returns true if the input can be cooked into output and the item counts allow for another cooking operation
+bool cFurnaceEntity::CanCookInputToOutput(void) const
+{
+ if (m_CurrentRecipe == NULL)
+ {
+ // This input cannot be cooked
+ return false;
+ }
+
+ if (m_Contents.GetSlot(fsOutput).IsEmpty())
+ {
+ // The output is empty, can cook
+ return true;
+ }
+
+ if (!m_Contents.GetSlot(fsOutput).IsStackableWith(*m_CurrentRecipe->Out))
+ {
+ // The output slot is blocked with something that cannot be stacked with the recipe's output
+ return false;
+ }
+
+ if (m_Contents.GetSlot(fsOutput).IsFullStack())
+ {
+ // Cannot add any more items to the output slot
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+/// Broadcasts progressbar updates, if needed
+void cFurnaceEntity::UpdateProgressBars(void)
+{
+ // In order to preserve bandwidth, an update is sent only every 10th tick
+ // That's why the comparisons use the division by eight
+
+ int CurFuel = (m_FuelBurnTime > 0) ? (200 - 200 * m_TimeBurned / m_FuelBurnTime) : 0;
+ if ((CurFuel / 8) != (m_LastProgressFuel / 8))
+ {
+ BroadcastProgress(PROGRESSBAR_FUEL, CurFuel);
+ m_LastProgressFuel = CurFuel;
+ }
+
+ int CurCook = (m_NeedCookTime > 0) ? (200 * m_TimeCooked / m_NeedCookTime) : 0;
+ if ((CurCook / 8) != (m_LastProgressCook / 8))
+ {
+ BroadcastProgress(PROGRESSBAR_SMELTING, CurCook);
+ m_LastProgressCook = CurCook;
+ }
+}
+
+
+
+
+
+void cFurnaceEntity::SetIsCooking(bool a_IsCooking)
+{
+ if (a_IsCooking == m_IsCooking)
+ {
+ return;
+ }
+
+ m_IsCooking = a_IsCooking;
+
+ // Light or extinguish the furnace:
+ m_World->FastSetBlock(m_PosX, m_PosY, m_PosZ, m_IsCooking ? E_BLOCK_LIT_FURNACE : E_BLOCK_FURNACE, m_BlockMeta);
+}
+
+
+
+
diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h
new file mode 100644
index 000000000..9464fd175
--- /dev/null
+++ b/src/BlockEntities/FurnaceEntity.h
@@ -0,0 +1,164 @@
+
+#pragma once
+
+#include "BlockEntityWithItems.h"
+#include "../UI/WindowOwner.h"
+#include "../FurnaceRecipe.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+}
+
+class cClientHandle;
+class cServer;
+
+
+
+
+
+class cFurnaceEntity : // tolua_export
+ public cBlockEntityWindowOwner,
+ // tolua_begin
+ public cBlockEntityWithItems
+{
+ typedef cBlockEntityWithItems super;
+
+public:
+ enum
+ {
+ fsInput = 0, // Input slot number
+ fsFuel = 1, // Fuel slot number
+ fsOutput = 2, // Output slot number
+
+ ContentsWidth = 3,
+ ContentsHeight = 1,
+ };
+
+ // tolua_end
+
+ /// Constructor used for normal operation
+ cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World);
+
+ virtual ~cFurnaceEntity();
+
+ static const char * GetClassStatic() { return "cFurnaceEntity"; }
+
+ bool LoadFromJson(const Json::Value & a_Value);
+
+ // cBlockEntity overrides:
+ virtual void SaveToJson(Json::Value & a_Value) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+
+ /// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking.
+ bool ContinueCooking(void);
+
+ void ResetCookTimer();
+
+ // tolua_begin
+
+ /// Returns the item in the input slot
+ const cItem & GetInputSlot(void) const { return GetSlot(fsInput); }
+
+ /// Returns the item in the fuel slot
+ const cItem & GetFuelSlot(void) const { return GetSlot(fsFuel); }
+
+ /// Returns the item in the output slot
+ const cItem & GetOutputSlot(void) const { return GetSlot(fsOutput); }
+
+ /// Sets the item in the input slot
+ void SetInputSlot(const cItem & a_Item) { SetSlot(fsInput, a_Item); }
+
+ /// Sets the item in the fuel slot
+ void SetFuelSlot(const cItem & a_Item) { SetSlot(fsFuel, a_Item); }
+
+ /// Sets the item in the output slot
+ void SetOutputSlot(const cItem & a_Item) { SetSlot(fsOutput, a_Item); }
+
+ /// Returns the time that the current item has been cooking, in ticks
+ int GetTimeCooked(void) const {return m_TimeCooked; }
+
+ /// Returns the time until the current item finishes cooking, in ticks
+ int GetCookTimeLeft(void) const { return m_NeedCookTime - m_TimeCooked; }
+
+ /// Returns the time until the current fuel is depleted, in ticks
+ int GetFuelBurnTimeLeft(void) const {return m_FuelBurnTime - m_TimeBurned; }
+
+ /// Returns true if there's time left before the current fuel is depleted
+ bool HasFuelTimeLeft(void) const { return (GetFuelBurnTimeLeft() > 0); }
+
+ // tolua_end
+
+ void SetBurnTimes(int a_FuelBurnTime, int a_TimeBurned) {m_FuelBurnTime = a_FuelBurnTime; m_TimeBurned = 0; }
+ void SetCookTimes(int a_NeedCookTime, int a_TimeCooked) {m_NeedCookTime = a_NeedCookTime; m_TimeCooked = a_TimeCooked; }
+
+protected:
+
+ /// Block type of the block currently represented by this entity (changes when furnace lights up)
+ BLOCKTYPE m_BlockType;
+
+ /// Block meta of the block currently represented by this entity
+ NIBBLETYPE m_BlockMeta;
+
+ /// The recipe for the current input slot
+ const cFurnaceRecipe::Recipe * m_CurrentRecipe;
+
+ /// The item that is being smelted
+ cItem m_LastInput;
+
+ bool m_IsCooking; ///< Set to true if the furnace is cooking an item
+
+ // All timers are in ticks
+ int m_NeedCookTime; ///< Amount of time needed to fully cook current item
+ int m_TimeCooked; ///< Amount of time that the current item has been cooking
+ int m_FuelBurnTime; ///< Amount of time that the current fuel can burn (in total); zero if no fuel burning
+ int m_TimeBurned; ///< Amount of time that the current fuel has been burning
+
+ int m_LastProgressFuel; ///< Last value sent as the progress for the fuel
+ int m_LastProgressCook; ///< Last value sent as the progress for the cooking
+
+
+ /// Sends the specified progressbar value to all clients of the window
+ void BroadcastProgress(int a_ProgressbarID, short a_Value);
+
+ /// One item finished cooking
+ void FinishOne(cChunk & a_Chunk);
+
+ /// Starts burning a new fuel, if possible
+ void BurnNewFuel(void);
+
+ /// Updates the recipe, based on the current input
+ void UpdateInput(void);
+
+ /// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate
+ void UpdateFuel(void);
+
+ /// Called when the output slot changes
+ void UpdateOutput(void);
+
+ /// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned
+ void UpdateIsCooking(void);
+
+ /// Returns true if the input can be cooked into output and the item counts allow for another cooking operation
+ bool CanCookInputToOutput(void) const;
+
+ /// Broadcasts progressbar updates, if needed
+ void UpdateProgressBars(void);
+
+ /// Sets the m_IsCooking variable, updates the furnace block type based on the value
+ void SetIsCooking(bool a_IsCooking);
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp
new file mode 100644
index 000000000..41849b1b3
--- /dev/null
+++ b/src/BlockEntities/HopperEntity.cpp
@@ -0,0 +1,566 @@
+
+// HopperEntity.cpp
+
+// Implements the cHopperEntity representing a hopper block entity
+
+#include "Globals.h"
+#include "HopperEntity.h"
+#include "../Chunk.h"
+#include "../Entities/Player.h"
+#include "../PluginManager.h"
+#include "ChestEntity.h"
+#include "DropSpenserEntity.h"
+#include "FurnaceEntity.h"
+
+
+
+
+
+cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World),
+ m_LastMoveItemsInTick(0),
+ m_LastMoveItemsOutTick(0)
+{
+}
+
+
+
+
+
+/** Returns the block coords of the block receiving the output items, based on the meta
+Returns false if unattached
+*/
+bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ)
+{
+ a_OutputX = m_PosX;
+ a_OutputY = m_PosY;
+ a_OutputZ = m_PosZ;
+ switch (a_BlockMeta)
+ {
+ case E_META_HOPPER_FACING_XM: a_OutputX--; return true;
+ case E_META_HOPPER_FACING_XP: a_OutputX++; return true;
+ case E_META_HOPPER_FACING_YM: a_OutputY--; return true;
+ case E_META_HOPPER_FACING_ZM: a_OutputZ--; return true;
+ case E_META_HOPPER_FACING_ZP: a_OutputZ++; return true;
+ default:
+ {
+ // Not attached
+ return false;
+ }
+ }
+}
+
+
+
+
+
+bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge();
+
+ bool res = false;
+ res = MoveItemsIn (a_Chunk, CurrentTick) || res;
+ res = MovePickupsIn(a_Chunk, CurrentTick) || res;
+ res = MoveItemsOut (a_Chunk, CurrentTick) || res;
+ return res;
+}
+
+
+
+
+
+void cHopperEntity::SaveToJson(Json::Value & a_Value)
+{
+ // TODO
+ LOGWARNING("%s: Not implemented yet", __FUNCTION__);
+}
+
+
+
+
+
+void cHopperEntity::SendTo(cClientHandle & a_Client)
+{
+ // The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance
+ // All the actual handling is in the cWindow UI code that gets called when the hopper is rclked
+
+ UNUSED(a_Client);
+}
+
+
+
+
+
+void cHopperEntity::UsedBy(cPlayer * a_Player)
+{
+ // If the window is not created, open it anew:
+ cWindow * Window = GetWindow();
+ if (Window == NULL)
+ {
+ OpenNewWindow();
+ Window = GetWindow();
+ }
+
+ // Open the window for the player:
+ if (Window != NULL)
+ {
+ if (a_Player->GetWindow() != Window)
+ {
+ a_Player->OpenWindow(Window);
+ }
+ }
+
+ // This is rather a hack
+ // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now
+ // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first.
+ // The few false positives aren't much to worry about
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ);
+ m_World->MarkChunkDirty(ChunkX, ChunkZ);
+}
+
+
+
+
+
+/// Opens a new window UI for this hopper
+void cHopperEntity::OpenNewWindow(void)
+{
+ OpenWindow(new cHopperWindow(m_PosX, m_PosY, m_PosZ, this));
+}
+
+
+
+
+
+/// Moves items from the container above it into this hopper. Returns true if the contents have changed.
+bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
+{
+ if (m_PosY >= cChunkDef::Height)
+ {
+ // This hopper is at the top of the world, no more blocks above
+ return false;
+ }
+
+ if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER)
+ {
+ // Too early after the previous transfer
+ return false;
+ }
+
+ // Try moving an item in:
+ bool res = false;
+ switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ))
+ {
+ case E_BLOCK_CHEST:
+ {
+ // Chests have special handling because of double-chests
+ res = MoveItemsFromChest(a_Chunk);
+ break;
+ }
+ case E_BLOCK_LIT_FURNACE:
+ case E_BLOCK_FURNACE:
+ {
+ // Furnaces have special handling because only the output and leftover fuel buckets shall be moved
+ res = MoveItemsFromFurnace(a_Chunk);
+ break;
+ }
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER:
+ case E_BLOCK_HOPPER:
+ {
+ res = MoveItemsFromGrid(*(cBlockEntityWithItems *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ));
+ break;
+ }
+ }
+
+ // If the item has been moved, reset the last tick:
+ if (res)
+ {
+ m_LastMoveItemsInTick = a_CurrentTick;
+ }
+
+ return res;
+}
+
+
+
+
+
+/// Moves pickups from above this hopper into it. Returns true if the contents have changed.
+bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
+{
+ // TODO
+ return false;
+}
+
+
+
+
+
+/// Moves items out from this hopper into the destination. Returns true if the contents have changed.
+bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick)
+{
+ if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER)
+ {
+ // Too early after the previous transfer
+ return false;
+ }
+
+ int bx, by, bz;
+ NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
+ if (!GetOutputBlockPos(Meta, bx, by, bz))
+ {
+ // Not attached to another container
+ return false;
+ }
+ if (by < 0)
+ {
+ // Cannot output below the zero-th block level
+ return false;
+ }
+
+ // Convert coords to relative:
+ int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width;
+ int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width;
+ cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz);
+ if (DestChunk == NULL)
+ {
+ // The destination chunk has been unloaded, don't tick
+ return false;
+ }
+
+ // Call proper moving function, based on the blocktype present at the coords:
+ bool res = false;
+ switch (DestChunk->GetBlock(rx, by, rz))
+ {
+ case E_BLOCK_CHEST:
+ {
+ // Chests have special handling because of double-chests
+ res = MoveItemsToChest(*DestChunk, bx, by, bz);
+ break;
+ }
+ case E_BLOCK_LIT_FURNACE:
+ case E_BLOCK_FURNACE:
+ {
+ // Furnaces have special handling because of the direction-to-slot relation
+ res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta);
+ break;
+ }
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER:
+ case E_BLOCK_HOPPER:
+ {
+ res = MoveItemsToGrid(*(cBlockEntityWithItems *)DestChunk->GetBlockEntity(bx, by, bz));
+ break;
+ }
+ }
+
+ // If the item has been moved, reset the last tick:
+ if (res)
+ {
+ m_LastMoveItemsOutTick = a_CurrentTick;
+ }
+
+ return res;
+}
+
+
+
+
+
+/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed.
+bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk)
+{
+ if (MoveItemsFromGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ)))
+ {
+ // Moved the item from the chest directly above the hopper
+ return true;
+ }
+
+ // Check if the chest is a double-chest, if so, try to move from there:
+ static const struct
+ {
+ int x, z;
+ }
+ Coords [] =
+ {
+ {1, 0},
+ {-1, 0},
+ {0, 1},
+ {0, -1},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ int x = m_RelX + Coords[i].x;
+ int z = m_RelZ + Coords[i].z;
+ cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z);
+ if (
+ (Neighbor == NULL) ||
+ (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST)
+ )
+ {
+ continue;
+ }
+ if (MoveItemsFromGrid(*(cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z)))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ // The chest was single and nothing could be moved
+ return false;
+}
+
+
+
+
+
+/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed.
+bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
+{
+ cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ);
+ ASSERT(Furnace != NULL);
+
+ // Try move from the output slot:
+ if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput, true))
+ {
+ cItem NewOutput(Furnace->GetOutputSlot());
+ Furnace->SetOutputSlot(NewOutput.AddCount(-1));
+ return true;
+ }
+
+ // No output moved, check if we can move an empty bucket out of the fuel slot:
+ if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET)
+ {
+ if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel, true))
+ {
+ Furnace->SetFuelSlot(cItem());
+ return true;
+ }
+ }
+
+ // Nothing can be moved
+ return false;
+}
+
+
+
+
+
+bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity)
+{
+ cItemGrid & Grid = a_Entity.GetContents();
+ int NumSlots = Grid.GetNumSlots();
+
+ // First try adding items of types already in the hopper:
+ for (int i = 0; i < NumSlots; i++)
+ {
+ if (Grid.IsSlotEmpty(i))
+ {
+ continue;
+ }
+ if (MoveItemsFromSlot(a_Entity, i, false))
+ {
+ Grid.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+
+ // No already existing stack can be topped up, try again with allowing new stacks:
+ for (int i = 0; i < NumSlots; i++)
+ {
+ if (Grid.IsSlotEmpty(i))
+ {
+ continue;
+ }
+ if (MoveItemsFromSlot(a_Entity, i, true))
+ {
+ Grid.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+/// Moves one piece of the specified a_Entity's slot itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
+bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum, bool a_AllowNewStacks)
+{
+ cItem One(a_Entity.GetSlot(a_SlotNum).CopyOne());
+ for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
+ {
+ if (m_Contents.IsSlotEmpty(i))
+ {
+ if (a_AllowNewStacks)
+ {
+ if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum))
+ {
+ // Plugin disagrees with the move
+ continue;
+ }
+ }
+ m_Contents.SetSlot(i, One);
+ return true;
+ }
+ else if (m_Contents.GetSlot(i).IsStackableWith(One))
+ {
+ if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum))
+ {
+ // Plugin disagrees with the move
+ continue;
+ }
+
+ m_Contents.ChangeSlotCount(i, 1);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+/// Moves items to the chest at the specified coords. Returns true if contents have changed
+bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Try the chest directly connected to the hopper:
+ if (MoveItemsToGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ)))
+ {
+ return true;
+ }
+
+ // Check if the chest is a double-chest, if so, try to move into the other half:
+ static const struct
+ {
+ int x, z;
+ }
+ Coords [] =
+ {
+ {1, 0},
+ {-1, 0},
+ {0, 1},
+ {0, -1},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ int x = m_RelX + Coords[i].x;
+ int z = m_RelZ + Coords[i].z;
+ cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z);
+ if (
+ (Neighbor == NULL) ||
+ (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST)
+ )
+ {
+ continue;
+ }
+ if (MoveItemsToGrid(*(cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ)))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ // The chest was single and nothing could be moved
+ return false;
+}
+
+
+
+
+
+/// Moves items to the furnace at the specified coords. Returns true if contents have changed
+bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta)
+{
+ cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ);
+ if (a_HopperMeta == E_META_HOPPER_FACING_YM)
+ {
+ // Feed the input slot of the furnace
+ return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsInput);
+ }
+ else
+ {
+ // Feed the fuel slot of the furnace
+ return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsFuel);
+ }
+ return false;
+}
+
+
+
+
+
+bool cHopperEntity::MoveItemsToGrid(cBlockEntityWithItems & a_Entity)
+{
+ // Iterate through our slots, try to move from each one:
+ int NumSlots = a_Entity.GetContents().GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+ if (MoveItemsToSlot(a_Entity, i))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cHopperEntity::MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum)
+{
+ cItemGrid & Grid = a_Entity.GetContents();
+ if (Grid.IsSlotEmpty(a_DstSlotNum))
+ {
+ // The slot is empty, move the first non-empty slot from our contents:
+ for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
+ {
+ if (!m_Contents.IsSlotEmpty(i))
+ {
+ if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum))
+ {
+ // A plugin disagrees with the move
+ continue;
+ }
+ Grid.SetSlot(a_DstSlotNum, m_Contents.GetSlot(i).CopyOne());
+ m_Contents.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+ }
+ else
+ {
+ // The slot is taken, try to top it up:
+ const cItem & DestSlot = Grid.GetSlot(a_DstSlotNum);
+ if (DestSlot.IsFullStack())
+ {
+ return false;
+ }
+ for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
+ {
+ if (m_Contents.GetSlot(i).IsStackableWith(DestSlot))
+ {
+ if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum))
+ {
+ // A plugin disagrees with the move
+ continue;
+ }
+ Grid.ChangeSlotCount(a_DstSlotNum, 1);
+ m_Contents.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+
+
+
diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h
new file mode 100644
index 000000000..3eaa05b7c
--- /dev/null
+++ b/src/BlockEntities/HopperEntity.h
@@ -0,0 +1,96 @@
+
+// HopperEntity.h
+
+// Declares the cHopperEntity representing a hopper block entity
+
+
+
+
+
+#pragma once
+
+#include "BlockEntityWithItems.h"
+#include "../UI/WindowOwner.h"
+
+
+
+
+
+class cHopperEntity : // tolua_export
+ public cBlockEntityWindowOwner,
+ // tolua_begin
+ public cBlockEntityWithItems
+{
+ typedef cBlockEntityWithItems super;
+
+public:
+ enum {
+ ContentsHeight = 1,
+ ContentsWidth = 5,
+ TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper
+ } ;
+
+ // tolua_end
+
+ /// Constructor used for normal operation
+ cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+
+ /** Returns the block coords of the block receiving the output items, based on the meta
+ Returns false if unattached.
+ Exported in ManualBindings.cpp
+ */
+ bool GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ);
+
+ static const char * GetClassStatic(void) { return "cHopperEntity"; }
+
+protected:
+
+ Int64 m_LastMoveItemsInTick;
+ Int64 m_LastMoveItemsOutTick;
+
+ // cBlockEntity overrides:
+ virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void SaveToJson(Json::Value & a_Value) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+
+ /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate.
+ void OpenNewWindow(void);
+
+ /// Moves items from the container above it into this hopper. Returns true if the contents have changed.
+ bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick);
+
+ /// Moves pickups from above this hopper into it. Returns true if the contents have changed.
+ bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick);
+
+ /// Moves items out from this hopper into the destination. Returns true if the contents have changed.
+ bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick);
+
+ /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed.
+ bool MoveItemsFromChest(cChunk & a_Chunk);
+
+ /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed.
+ bool MoveItemsFromFurnace(cChunk & a_Chunk);
+
+ /// Moves items from the specified a_Entity's Contents into this hopper. Returns true if contents have changed.
+ bool MoveItemsFromGrid(cBlockEntityWithItems & a_Entity);
+
+ /// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
+ bool MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SrcSlotNum, bool a_AllowNewStacks);
+
+ /// Moves items to the chest at the specified coords. Returns true if contents have changed
+ bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Moves items to the furnace at the specified coords. Returns true if contents have changed
+ bool MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta);
+
+ /// Moves items to the specified ItemGrid. Returns true if contents have changed
+ bool MoveItemsToGrid(cBlockEntityWithItems & a_Entity);
+
+ /// Moves one piece to the specified entity's contents' slot. Returns true if contents have changed.
+ bool MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum);
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockEntities/JukeboxEntity.cpp b/src/BlockEntities/JukeboxEntity.cpp
new file mode 100644
index 000000000..8e90ee70d
--- /dev/null
+++ b/src/BlockEntities/JukeboxEntity.cpp
@@ -0,0 +1,125 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "JukeboxEntity.h"
+#include "../World.h"
+#include "lib/jsoncpp/include/json/json.h"
+
+
+
+
+
+cJukeboxEntity::cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(E_BLOCK_JUKEBOX, a_BlockX, a_BlockY, a_BlockZ, a_World),
+ m_Record(0)
+{
+}
+
+
+
+
+
+cJukeboxEntity::~cJukeboxEntity()
+{
+ EjectRecord();
+}
+
+
+
+
+
+void cJukeboxEntity::UsedBy(cPlayer * a_Player)
+{
+ if (m_Record == 0)
+ {
+ const cItem & HeldItem = a_Player->GetEquippedItem();
+ if (HeldItem.m_ItemType >= 2256 && HeldItem.m_ItemType <= 2267)
+ {
+ m_Record = HeldItem.m_ItemType;
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ PlayRecord();
+ }
+ }
+ else
+ {
+ EjectRecord();
+ }
+}
+
+
+
+
+
+void cJukeboxEntity::PlayRecord(void)
+{
+ m_World->BroadcastSoundParticleEffect(1005, m_PosX, m_PosY, m_PosZ, m_Record);
+}
+
+
+
+
+
+void cJukeboxEntity::EjectRecord(void)
+{
+ if ((m_Record < E_ITEM_FIRST_DISC) || (m_Record > E_ITEM_LAST_DISC))
+ {
+ // There's no record here
+ return;
+ }
+
+ cItems Drops;
+ Drops.push_back(cItem(m_Record, 1, 0));
+ m_World->SpawnItemPickups(Drops, m_PosX + 0.5, m_PosY + 1, m_PosZ + 0.5, 8);
+ m_World->BroadcastSoundParticleEffect(1005, m_PosX, m_PosY, m_PosZ, 0);
+ m_Record = 0;
+}
+
+
+
+
+
+int cJukeboxEntity::GetRecord(void)
+{
+ return m_Record;
+}
+
+
+
+
+
+void cJukeboxEntity::SetRecord(int a_Record)
+{
+ m_Record = a_Record;
+}
+
+
+
+
+
+bool cJukeboxEntity::LoadFromJson(const Json::Value & a_Value)
+{
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
+
+ m_Record = a_Value.get("Record", 0).asInt();
+
+ return true;
+}
+
+
+
+
+
+void cJukeboxEntity::SaveToJson(Json::Value & a_Value)
+{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ a_Value["Record"] = m_Record;
+}
+
+
+
+
diff --git a/src/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h
new file mode 100644
index 000000000..fcafdc479
--- /dev/null
+++ b/src/BlockEntities/JukeboxEntity.h
@@ -0,0 +1,56 @@
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
+// tolua_begin
+
+class cJukeboxEntity :
+ public cBlockEntity
+{
+ typedef cBlockEntity super;
+public:
+
+ // tolua_end
+
+ cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+ virtual ~cJukeboxEntity();
+
+ bool LoadFromJson(const Json::Value & a_Value);
+ virtual void SaveToJson(Json::Value & a_Value) override;
+
+ // tolua_begin
+
+ int GetRecord(void);
+ void SetRecord(int a_Record);
+ void PlayRecord(void);
+
+ /// Ejects the currently held record as a pickup. Does nothing when no record inserted.
+ void EjectRecord(void);
+
+ // tolua_end
+
+ virtual void UsedBy(cPlayer * a_Player) override;
+ virtual void SendTo(cClientHandle & a_Client) override { };
+
+private:
+ int m_Record;
+} ; // tolua_end
+
+
+
+
diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp
new file mode 100644
index 000000000..493316bce
--- /dev/null
+++ b/src/BlockEntities/NoteEntity.cpp
@@ -0,0 +1,154 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "NoteEntity.h"
+#include "../World.h"
+#include "lib/jsoncpp/include/json/json.h"
+
+
+
+
+
+cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World),
+ m_Pitch(0)
+{
+}
+
+
+
+
+
+void cNoteEntity::UsedBy(cPlayer * a_Player)
+{
+ IncrementPitch();
+ MakeSound();
+}
+
+
+
+
+
+void cNoteEntity::MakeSound(void)
+{
+ char instrument;
+ AString sampleName;
+
+ switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ))
+ {
+ case E_BLOCK_PLANKS:
+ case E_BLOCK_LOG:
+ case E_BLOCK_NOTE_BLOCK:
+ {
+ // TODO: add other wood-based blocks if needed
+ instrument = E_INST_DOUBLE_BASS;
+ sampleName = "note.db";
+ break;
+ }
+
+ case E_BLOCK_SAND:
+ case E_BLOCK_GRAVEL:
+ case E_BLOCK_SOULSAND:
+ {
+ instrument = E_INST_SNARE_DRUM;
+ sampleName = "note.snare";
+ break;
+ }
+
+ case E_BLOCK_GLASS:
+ case E_BLOCK_GLASS_PANE:
+ case E_BLOCK_GLOWSTONE:
+ {
+ instrument = E_INST_CLICKS;
+ sampleName = "note.hat";
+ break;
+ }
+
+ case E_BLOCK_STONE:
+ case E_BLOCK_STONE_BRICKS:
+ case E_BLOCK_COBBLESTONE:
+ case E_BLOCK_OBSIDIAN:
+ case E_BLOCK_NETHERRACK:
+ case E_BLOCK_BRICK:
+ case E_BLOCK_NETHER_BRICK:
+ {
+ // TODO: add other stone-based blocks if needed
+ instrument = E_INST_BASS_DRUM;
+ sampleName = "note.bassattack";
+ break;
+ }
+
+ default:
+ {
+ instrument = E_INST_HARP_PIANO;
+ sampleName = "note.harp";
+ break;
+ }
+ }
+
+ m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK);
+
+ // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all
+ float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f);
+ m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch);
+}
+
+
+
+
+
+char cNoteEntity::GetPitch(void)
+{
+ return m_Pitch;
+}
+
+
+
+
+
+void cNoteEntity::SetPitch(char a_Pitch)
+{
+ m_Pitch = a_Pitch % 25;
+}
+
+
+
+
+
+void cNoteEntity::IncrementPitch(void)
+{
+ SetPitch(m_Pitch + 1);
+}
+
+
+
+
+
+bool cNoteEntity::LoadFromJson(const Json::Value & a_Value)
+{
+
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
+
+ m_Pitch = (char)a_Value.get("p", 0).asInt();
+
+ return true;
+}
+
+
+
+
+
+void cNoteEntity::SaveToJson(Json::Value & a_Value)
+{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ a_Value["p"] = m_Pitch;
+}
+
+
+
+
diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h
new file mode 100644
index 000000000..e2d088f44
--- /dev/null
+++ b/src/BlockEntities/NoteEntity.h
@@ -0,0 +1,63 @@
+
+#pragma once
+
+#include "BlockEntity.h"
+
+
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
+enum ENUM_NOTE_INSTRUMENTS
+{
+ E_INST_HARP_PIANO = 0,
+ E_INST_DOUBLE_BASS = 1,
+ E_INST_SNARE_DRUM = 2,
+ E_INST_CLICKS = 3,
+ E_INST_BASS_DRUM = 4
+};
+
+
+
+
+
+// tolua_begin
+
+class cNoteEntity :
+ public cBlockEntity
+{
+ typedef cBlockEntity super;
+public:
+
+ // tolua_end
+
+ /// Creates a new note entity. a_World may be NULL
+ cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
+
+ bool LoadFromJson(const Json::Value & a_Value);
+ virtual void SaveToJson(Json::Value & a_Value) override;
+
+ // tolua_begin
+
+ char GetPitch(void);
+ void SetPitch(char a_Pitch);
+ void IncrementPitch(void);
+ void MakeSound(void);
+
+ // tolua_end
+
+ virtual void UsedBy(cPlayer * a_Player) override;
+ virtual void SendTo(cClientHandle & a_Client) override { };
+
+private:
+ char m_Pitch;
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockEntities/SignEntity.cpp b/src/BlockEntities/SignEntity.cpp
new file mode 100644
index 000000000..39fcb6efc
--- /dev/null
+++ b/src/BlockEntities/SignEntity.cpp
@@ -0,0 +1,115 @@
+
+// SignEntity.cpp
+
+// Implements the cSignEntity class representing a single sign in the world
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "lib/jsoncpp/include/json/json.h"
+#include "SignEntity.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+cSignEntity::cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) :
+ super(a_BlockType, a_X, a_Y, a_Z, a_World)
+{
+}
+
+
+
+
+
+// It don't do anything when 'used'
+void cSignEntity::UsedBy(cPlayer * a_Player)
+{
+ UNUSED(a_Player);
+}
+
+
+
+
+
+void cSignEntity::SetLines(const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
+{
+ m_Line[0] = a_Line1;
+ m_Line[1] = a_Line2;
+ m_Line[2] = a_Line3;
+ m_Line[3] = a_Line4;
+}
+
+
+
+
+
+void cSignEntity::SetLine(int a_Index, const AString & a_Line)
+{
+ if ((a_Index < 0) || (a_Index >= ARRAYCOUNT(m_Line)))
+ {
+ LOGWARNING("%s: setting a non-existent line %d (value \"%s\"", __FUNCTION__, a_Index, a_Line.c_str());
+ return;
+ }
+ m_Line[a_Index] = a_Line;
+}
+
+
+
+
+
+AString cSignEntity::GetLine(int a_Index) const
+{
+ if ((a_Index < 0) || (a_Index >= ARRAYCOUNT(m_Line)))
+ {
+ LOGWARNING("%s: requesting a non-existent line %d", __FUNCTION__, a_Index);
+ return "";
+ }
+ return m_Line[a_Index];
+}
+
+
+
+
+
+void cSignEntity::SendTo(cClientHandle & a_Client)
+{
+ a_Client.SendUpdateSign(m_PosX, m_PosY, m_PosZ, m_Line[0], m_Line[1], m_Line[2], m_Line[3]);
+}
+
+
+
+
+
+bool cSignEntity::LoadFromJson(const Json::Value & a_Value)
+{
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
+
+ m_Line[0] = a_Value.get("Line1", "").asString();
+ m_Line[1] = a_Value.get("Line2", "").asString();
+ m_Line[2] = a_Value.get("Line3", "").asString();
+ m_Line[3] = a_Value.get("Line4", "").asString();
+
+ return true;
+}
+
+
+
+
+
+void cSignEntity::SaveToJson(Json::Value & a_Value)
+{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ a_Value["Line1"] = m_Line[0];
+ a_Value["Line2"] = m_Line[1];
+ a_Value["Line3"] = m_Line[2];
+ a_Value["Line4"] = m_Line[3];
+}
+
+
+
+
diff --git a/src/BlockEntities/SignEntity.h b/src/BlockEntities/SignEntity.h
new file mode 100644
index 000000000..d998ff1e8
--- /dev/null
+++ b/src/BlockEntities/SignEntity.h
@@ -0,0 +1,67 @@
+
+// SignEntity.h
+
+// Declares the cSignEntity class representing a single sign in the world
+
+
+
+
+
+#pragma once
+
+#include "BlockEntity.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
+// tolua_begin
+
+class cSignEntity :
+ public cBlockEntity
+{
+ typedef cBlockEntity super;
+
+public:
+
+ // tolua_end
+
+ /// Creates a new empty sign entity at the specified block coords and block type (wall or standing). a_World may be NULL
+ cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World);
+
+ bool LoadFromJson( const Json::Value& a_Value );
+ virtual void SaveToJson(Json::Value& a_Value ) override;
+
+ // tolua_begin
+
+ /// Sets all the sign's lines
+ void SetLines(const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
+
+ /// Sets individual line (zero-based index)
+ void SetLine(int a_Index, const AString & a_Line);
+
+ /// Retrieves individual line (zero-based index)
+ AString GetLine(int a_Index) const;
+
+ // tolua_end
+
+ virtual void UsedBy(cPlayer * a_Player) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+
+private:
+
+ AString m_Line[4];
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockID.cpp b/src/BlockID.cpp
new file mode 100644
index 000000000..708618d25
--- /dev/null
+++ b/src/BlockID.cpp
@@ -0,0 +1,958 @@
+// BlockID.cpp
+
+// Implements the helper functions for converting Block ID string to int etc.
+
+#include "Globals.h"
+#include "BlockID.h"
+#include "inifile/iniFile.h"
+#include "Item.h"
+#include "Mobs/Monster.h"
+
+
+
+
+
+NIBBLETYPE g_BlockLightValue[256];
+NIBBLETYPE g_BlockSpreadLightFalloff[256];
+bool g_BlockTransparent[256];
+bool g_BlockOneHitDig[256];
+bool g_BlockPistonBreakable[256];
+bool g_BlockIsSnowable[256];
+bool g_BlockRequiresSpecialTool[256];
+bool g_BlockIsSolid[256];
+bool g_BlockIsTorchPlaceable[256];
+
+
+
+
+
+class cBlockIDMap
+{
+ // Making the map case-insensitive:
+ struct Comparator
+ {
+ bool operator()(const AString & a_Item1, const AString & a_Item2) const
+ {
+ return (NoCaseCompare(a_Item1, a_Item2) > 0);
+ }
+ } ;
+
+ typedef std::map<AString, std::pair<short, short>, Comparator> ItemMap;
+
+public:
+ cBlockIDMap(void)
+ {
+ cIniFile Ini;
+ if (!Ini.ReadFile("items.ini"))
+ {
+ return;
+ }
+ int KeyID = Ini.FindKey("Items");
+ if (KeyID == cIniFile::noID)
+ {
+ return;
+ }
+ int NumValues = Ini.GetNumValues(KeyID);
+ for (int i = 0; i < NumValues; i++)
+ {
+ AString Name = Ini.GetValueName(KeyID, i);
+ if (Name.empty())
+ {
+ continue;
+ }
+ AString Value = Ini.GetValue(KeyID, i);
+ AddToMap(Name, Value);
+ } // for i - Ini.Values[]
+ }
+
+
+ int Resolve(const AString & a_ItemName)
+ {
+ ItemMap::iterator itr = m_Map.find(a_ItemName);
+ if (itr == m_Map.end())
+ {
+ return -1;
+ }
+ return itr->second.first;
+ }
+
+
+ bool ResolveItem(const AString & a_ItemName, cItem & a_Item)
+ {
+ // Split into parts divided by either ':' or '^'
+ AStringVector Split = StringSplitAndTrim(a_ItemName, ":^");
+ if (Split.empty())
+ {
+ return false;
+ }
+
+ ItemMap::iterator itr = m_Map.find(Split[0]);
+ if (itr != m_Map.end())
+ {
+ // Resolved as a string, assign the type and the default damage / count
+ a_Item.m_ItemType = itr->second.first;
+ a_Item.m_ItemDamage = itr->second.second;
+ if (a_Item.m_ItemDamage == -1)
+ {
+ a_Item.m_ItemDamage = 0;
+ }
+ }
+ else
+ {
+ // Not a resolvable string, try pure numbers: "45:6", "45^6" etc.
+ a_Item.m_ItemType = (short)atoi(Split[0].c_str());
+ if ((a_Item.m_ItemType == 0) && (Split[0] != "0"))
+ {
+ // Parsing the number failed
+ return false;
+ }
+ }
+
+ // Parse the damage, if present:
+ if (Split.size() < 2)
+ {
+ // Not present, set the item as valid and return success:
+ a_Item.m_ItemCount = 1;
+ return true;
+ }
+
+ a_Item.m_ItemDamage = atoi(Split[1].c_str());
+ if ((a_Item.m_ItemDamage == 0) && (Split[1] != "0"))
+ {
+ // Parsing the number failed
+ return false;
+ }
+ a_Item.m_ItemCount = 1;
+ return true;
+ }
+
+
+ AString Desolve(short a_ItemType, short a_ItemDamage)
+ {
+ // First try an exact match, both ItemType and ItemDamage ("birchplanks=5:2"):
+ for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr)
+ {
+ if ((itr->second.first == a_ItemType) && (itr->second.second == a_ItemDamage))
+ {
+ return itr->first;
+ }
+ } // for itr - m_Map[]
+
+ // There is no exact match, try matching ItemType only ("planks=5"):
+ if (a_ItemDamage == 0)
+ {
+ for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr)
+ {
+ if ((itr->second.first == a_ItemType) && (itr->second.second == -1))
+ {
+ return itr->first;
+ }
+ } // for itr - m_Map[]
+ }
+
+ // No match at all, synthesize a string ("5:1"):
+ AString res;
+ if (a_ItemDamage == -1)
+ {
+ Printf(res, "%d", a_ItemType);
+ }
+ else
+ {
+ Printf(res, "%d:%d", a_ItemType, a_ItemDamage);
+ }
+ return res;
+ }
+
+
+protected:
+ ItemMap m_Map;
+
+
+ void AddToMap(const AString & a_Name, const AString & a_Value)
+ {
+ AStringVector Split = StringSplit(a_Value, ":");
+ if (Split.size() == 1)
+ {
+ Split = StringSplit(a_Value, "^");
+ }
+ if (Split.empty())
+ {
+ return;
+ }
+ short ItemType = (short)atoi(Split[0].c_str());
+ short ItemDamage = (Split.size() > 1) ? (short)atoi(Split[1].c_str()) : -1;
+ m_Map[a_Name] = std::make_pair(ItemType, ItemDamage);
+ }
+} ;
+
+
+
+
+
+static cBlockIDMap gsBlockIDMap;
+
+
+
+
+
+/*
+// Quick self-test:
+class Tester
+{
+public:
+ Tester(void)
+ {
+ cItem Item;
+ gsBlockIDMap.ResolveItem("charcoal", Item);
+ AString Charcoal = gsBlockIDMap.Desolve(Item.m_ItemType, Item.m_ItemDamage);
+ ASSERT(Charcoal == "charcoal");
+ }
+} test;
+//*/
+
+
+
+
+
+BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString)
+{
+ int res = atoi(a_BlockTypeString.c_str());
+ if ((res != 0) || (a_BlockTypeString.compare("0") == 0))
+ {
+ // It was a valid number, return that
+ return res;
+ }
+
+ return gsBlockIDMap.Resolve(TrimString(a_BlockTypeString));
+}
+
+
+
+
+bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item)
+{
+ return gsBlockIDMap.ResolveItem(TrimString(a_ItemTypeString), a_Item);
+}
+
+
+
+
+
+AString ItemToString(const cItem & a_Item)
+{
+ return gsBlockIDMap.Desolve(a_Item.m_ItemType, a_Item.m_ItemDamage);
+}
+
+
+
+
+
+AString ItemTypeToString(short a_ItemType)
+{
+ return gsBlockIDMap.Desolve(a_ItemType, -1);
+}
+
+
+
+
+
+AString ItemToFullString(const cItem & a_Item)
+{
+ AString res;
+ Printf(res, "%s:%d * %d", ItemToString(a_Item).c_str(), a_Item.m_ItemDamage, a_Item.m_ItemCount);
+ return res;
+}
+
+
+
+
+
+EMCSBiome StringToBiome(const AString & a_BiomeString)
+{
+ // If it is a number, return it:
+ int res = atoi(a_BiomeString.c_str());
+ if ((res != 0) || (a_BiomeString.compare("0") == 0))
+ {
+ // It was a valid number
+ return (EMCSBiome)res;
+ }
+
+ // Convert using the built-in map:
+ static struct {
+ EMCSBiome m_Biome;
+ const char * m_String;
+ } BiomeMap[] =
+ {
+ {biOcean, "Ocean"} ,
+ {biPlains, "Plains"},
+ {biDesert, "Desert"},
+ {biExtremeHills, "ExtremeHills"},
+ {biForest, "Forest"},
+ {biTaiga, "Taiga"},
+ {biSwampland, "Swampland"},
+ {biRiver, "River"},
+ {biNether, "Hell"},
+ {biNether, "Nether"},
+ {biEnd, "Sky"},
+ {biEnd, "End"},
+ {biFrozenOcean, "FrozenOcean"},
+ {biFrozenRiver, "FrozenRiver"},
+ {biIcePlains, "IcePlains"},
+ {biIcePlains, "Tundra"},
+ {biIceMountains, "IceMountains"},
+ {biMushroomIsland, "MushroomIsland"},
+ {biMushroomShore, "MushroomShore"},
+ {biBeach, "Beach"},
+ {biDesertHills, "DesertHills"},
+ {biForestHills, "ForestHills"},
+ {biTaigaHills, "TaigaHills"},
+ {biExtremeHillsEdge, "ExtremeHillsEdge"},
+ {biJungle, "Jungle"},
+ {biJungleHills, "JungleHills"},
+
+ // Release 1.7 biomes:
+ {biJungleEdge, "JungleEdge"},
+ {biDeepOcean, "DeepOcean"},
+ {biStoneBeach, "StoneBeach"},
+ {biColdBeach, "ColdBeach"},
+ {biBirchForest, "BirchForest"},
+ {biBirchForestHills, "BirchForestHills"},
+ {biRoofedForest, "RoofedForest"},
+ {biColdTaiga, "ColdTaiga"},
+ {biColdTaigaHills, "ColdTaigaHills"},
+ {biMegaTaiga, "MegaTaiga"},
+ {biMegaTaigaHills, "MegaTaigaHills"},
+ {biExtremeHillsPlus, "ExtremeHillsPlus"},
+ {biSavanna, "Savanna"},
+ {biSavannaPlateau, "SavannaPlateau"},
+ {biMesa, "Mesa"},
+ {biMesaPlateauF, "MesaPlateauF"},
+ {biMesaPlateau, "MesaPlateau"},
+
+ // Release 1.7 variants:
+ {biSunflowerPlains, "SunflowerPlains"},
+ {biDesertM, "DesertM"},
+ {biExtremeHillsM, "ExtremeHillsM"},
+ {biFlowerForest, "FlowerForest"},
+ {biTaigaM, "TaigaM"},
+ {biSwamplandM, "SwamplandM"},
+ {biIcePlainsSpikes, "IcePlainsSpikes"},
+ {biJungleM, "JungleM"},
+ {biJungleEdgeM, "JungleEdgeM"},
+ {biBirchForestM, "BirchForestM"},
+ {biBirchForestHillsM, "BirchForestHillsM"},
+ {biRoofedForestM, "RoofedForestM"},
+ {biColdTaigaM, "ColdTaigaM"},
+ {biMegaSpruceTaiga, "MegaSpruceTaiga"},
+ {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"},
+ {biExtremeHillsPlusM, "ExtremeHillsPlusM"},
+ {biSavannaM, "SavannaM"},
+ {biSavannaPlateauM, "SavannaPlateauM"},
+ {biMesaBryce, "MesaBryce"},
+ {biMesaPlateauFM, "MesaPlateauFM"},
+ {biMesaPlateauM, "MesaPlateauM"},
+ } ;
+
+ for (int i = 0; i < ARRAYCOUNT(BiomeMap); i++)
+ {
+ if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0)
+ {
+ return BiomeMap[i].m_Biome;
+ }
+ } // for i - BiomeMap[]
+ return (EMCSBiome)-1;
+}
+
+
+
+
+
+int StringToMobType(const AString & a_MobString)
+{
+ static struct {
+ int m_MobType;
+ const char * m_String;
+ } MobMap [] =
+ {
+ {cMonster::mtCreeper, "Creeper"},
+ {cMonster::mtSkeleton, "Skeleton"},
+ {cMonster::mtSpider, "Spider"},
+ {cMonster::mtGiant, "Giant"},
+ {cMonster::mtZombie, "Zombie"},
+ {cMonster::mtSlime, "Slime"},
+ {cMonster::mtGhast, "Ghast"},
+ {cMonster::mtZombiePigman, "ZombiePigman"},
+ {cMonster::mtEnderman, "Enderman"},
+ {cMonster::mtCaveSpider, "CaveSpider"},
+ {cMonster::mtSilverfish, "SilverFish"},
+ {cMonster::mtBlaze, "Blaze"},
+ {cMonster::mtMagmaCube, "MagmaCube"},
+ {cMonster::mtEnderDragon, "EnderDragon"},
+ {cMonster::mtWither, "Wither"},
+ {cMonster::mtBat, "Bat"},
+ {cMonster::mtWitch, "Witch"},
+ {cMonster::mtPig, "Pig"},
+ {cMonster::mtSheep, "Sheep"},
+ {cMonster::mtCow, "Cow"},
+ {cMonster::mtChicken, "Chicken"},
+ {cMonster::mtSquid, "Squid"},
+ {cMonster::mtWolf, "Wolf"},
+ {cMonster::mtMooshroom, "Mooshroom"},
+ {cMonster::mtSnowGolem, "SnowGolem"},
+ {cMonster::mtOcelot, "Ocelot"},
+ {cMonster::mtIronGolem, "IronGolem"},
+ {cMonster::mtVillager, "Villager"},
+ };
+ for (int i = 0; i < ARRAYCOUNT(MobMap); i++)
+ {
+ if (NoCaseCompare(MobMap[i].m_String, a_MobString) == 0)
+ {
+ return MobMap[i].m_MobType;
+ }
+ } // for i - MobMap[]
+ return -1;
+}
+
+
+
+
+
+eDimension StringToDimension(const AString & a_DimensionString)
+{
+ // First try decoding as a number
+ int res = atoi(a_DimensionString.c_str());
+ if ((res != 0) || (a_DimensionString == "0"))
+ {
+ // It was a valid number
+ return (eDimension)res;
+ }
+
+ // Decode using a built-in map:
+ static struct
+ {
+ eDimension m_Dimension;
+ const char * m_String;
+ } DimensionMap [] =
+ {
+ { dimOverworld, "Overworld"},
+ { dimOverworld, "Normal"},
+ { dimOverworld, "World"},
+ { dimNether, "Nether"},
+ { dimNether, "Hell"}, // Alternate name for End
+ { dimEnd, "End"},
+ { dimEnd, "Sky"}, // Old name for End
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(DimensionMap); i++)
+ {
+ if (NoCaseCompare(DimensionMap[i].m_String, a_DimensionString) == 0)
+ {
+ return DimensionMap[i].m_Dimension;
+ }
+ } // for i - DimensionMap[]
+
+ // Not found
+ return (eDimension)-1000;
+}
+
+
+
+
+
+/// Translates damage type constant to a string representation (built-in).
+AString DamageTypeToString(eDamageType a_DamageType)
+{
+ // Make sure to keep this alpha-sorted.
+ switch (a_DamageType)
+ {
+ case dtAdmin: return "dtAdmin";
+ case dtAttack: return "dtAttack";
+ case dtCactusContact: return "dtCactusContact";
+ case dtDrowning: return "dtDrowning";
+ case dtEnderPearl: return "dtEnderPearl";
+ case dtFalling: return "dtFalling";
+ case dtFireContact: return "dtFireContact";
+ case dtInVoid: return "dtInVoid";
+ case dtLavaContact: return "dtLavaContact";
+ case dtLightning: return "dtLightning";
+ case dtOnFire: return "dtOnFire";
+ case dtPoisoning: return "dtPoisoning";
+ case dtPotionOfHarming: return "dtPotionOfHarming";
+ case dtRangedAttack: return "dtRangedAttack";
+ case dtStarving: return "dtStarving";
+ case dtSuffocating: return "dtSuffocation";
+
+ }
+
+ // Unknown damage type:
+ ASSERT(!"Unknown DamageType");
+ return Printf("dtUnknown_%d", (int)a_DamageType);
+}
+
+
+
+
+
+/// Translates a damage type string to damage type. Takes either a number or a damage type alias (built-in). Returns -1 on failure
+eDamageType StringToDamageType(const AString & a_DamageTypeString)
+{
+ // First try decoding as a number:
+ int res = atoi(a_DamageTypeString.c_str());
+ if ((res != 0) || (a_DamageTypeString == "0"))
+ {
+ // It was a valid number
+ return (eDamageType)res;
+ }
+
+ // Decode using a built-in map:
+ static struct
+ {
+ eDamageType m_DamageType;
+ const char * m_String;
+ } DamageTypeMap [] =
+ {
+ // Cannonical names:
+ { dtAttack, "dtAttack"},
+ { dtRangedAttack, "dtRangedAttack"},
+ { dtLightning, "dtLightning"},
+ { dtFalling, "dtFalling"},
+ { dtDrowning, "dtDrowning"},
+ { dtSuffocating, "dtSuffocation"},
+ { dtStarving, "dtStarving"},
+ { dtCactusContact, "dtCactusContact"},
+ { dtLavaContact, "dtLavaContact"},
+ { dtPoisoning, "dtPoisoning"},
+ { dtOnFire, "dtOnFire"},
+ { dtFireContact, "dtFireContact"},
+ { dtInVoid, "dtInVoid"},
+ { dtPotionOfHarming, "dtPotionOfHarming"},
+ { dtAdmin, "dtAdmin"},
+
+ // Common synonyms:
+ { dtAttack, "dtPawnAttack"},
+ { dtAttack, "dtEntityAttack"},
+ { dtAttack, "dtMob"},
+ { dtAttack, "dtMobAttack"},
+ { dtRangedAttack, "dtArrowAttack"},
+ { dtRangedAttack, "dtArrow"},
+ { dtRangedAttack, "dtProjectile"},
+ { dtFalling, "dtFall"},
+ { dtDrowning, "dtDrown"},
+ { dtSuffocating, "dtSuffocation"},
+ { dtStarving, "dtStarvation"},
+ { dtStarving, "dtHunger"},
+ { dtCactusContact, "dtCactus"},
+ { dtCactusContact, "dtCactuses"},
+ { dtCactusContact, "dtCacti"},
+ { dtLavaContact, "dtLava"},
+ { dtPoisoning, "dtPoison"},
+ { dtOnFire, "dtBurning"},
+ { dtFireContact, "dtInFire"},
+ { dtAdmin, "dtPlugin"},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(DamageTypeMap); i++)
+ {
+ if (NoCaseCompare(DamageTypeMap[i].m_String, a_DamageTypeString) == 0)
+ {
+ return DamageTypeMap[i].m_DamageType;
+ }
+ } // for i - DamageTypeMap[]
+
+ // Not found:
+ return (eDamageType)-1;
+}
+
+
+
+
+
+cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default)
+{
+ AString ItemStr = a_IniFile.GetValueSet(a_Section, a_Key, a_Default);
+ cItem res;
+ if (!StringToItem(ItemStr, res))
+ {
+ res.Empty();
+ }
+ return res;
+}
+
+
+
+
+
+// This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor:
+class cBlockPropertiesInitializer
+{
+public:
+ cBlockPropertiesInitializer(void)
+ {
+ memset(g_BlockLightValue, 0x00, sizeof(g_BlockLightValue));
+ memset(g_BlockSpreadLightFalloff, 0x0f, sizeof(g_BlockSpreadLightFalloff)); // 0x0f means total falloff
+ memset(g_BlockTransparent, 0x00, sizeof(g_BlockTransparent));
+ memset(g_BlockOneHitDig, 0x00, sizeof(g_BlockOneHitDig));
+ memset(g_BlockPistonBreakable, 0x00, sizeof(g_BlockPistonBreakable));
+ memset(g_BlockIsTorchPlaceable, 0x00, sizeof(g_BlockIsTorchPlaceable));
+
+ // Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415
+ for (int i = 0; i < ARRAYCOUNT(g_BlockIsSnowable); i++)
+ {
+ g_BlockIsSnowable[i] = true;
+ }
+ memset(g_BlockRequiresSpecialTool, 0x00, sizeof(g_BlockRequiresSpecialTool)); // Set all blocks to false
+
+ // Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415
+ for (int i = 0; i < ARRAYCOUNT(g_BlockIsSolid); i++)
+ {
+ g_BlockIsSolid[i] = true;
+ }
+
+ // Emissive blocks
+ g_BlockLightValue[E_BLOCK_FIRE] = 15;
+ g_BlockLightValue[E_BLOCK_GLOWSTONE] = 15;
+ g_BlockLightValue[E_BLOCK_JACK_O_LANTERN] = 15;
+ g_BlockLightValue[E_BLOCK_LAVA] = 15;
+ g_BlockLightValue[E_BLOCK_STATIONARY_LAVA] = 15;
+ g_BlockLightValue[E_BLOCK_END_PORTAL] = 15;
+ g_BlockLightValue[E_BLOCK_REDSTONE_LAMP_ON] = 15;
+ g_BlockLightValue[E_BLOCK_TORCH] = 14;
+ g_BlockLightValue[E_BLOCK_BURNING_FURNACE] = 13;
+ g_BlockLightValue[E_BLOCK_NETHER_PORTAL] = 11;
+ g_BlockLightValue[E_BLOCK_REDSTONE_ORE_GLOWING] = 9;
+ g_BlockLightValue[E_BLOCK_REDSTONE_REPEATER_ON] = 9;
+ g_BlockLightValue[E_BLOCK_REDSTONE_TORCH_ON] = 7;
+ g_BlockLightValue[E_BLOCK_BREWING_STAND] = 1;
+ g_BlockLightValue[E_BLOCK_BROWN_MUSHROOM] = 1;
+ g_BlockLightValue[E_BLOCK_DRAGON_EGG] = 1;
+
+ // Spread blocks
+ g_BlockSpreadLightFalloff[E_BLOCK_AIR] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_CAKE] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_CHEST] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_COBWEB] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_CROPS] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_FENCE] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_FENCE_GATE] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_FIRE] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_GLASS] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_GLASS_PANE] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_GLOWSTONE] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_IRON_BARS] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_IRON_DOOR] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_LEAVES] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_SIGN_POST] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_TORCH] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_VINES] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_WALLSIGN] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_WOODEN_DOOR] = 1;
+
+ // Light in water and lava dissapears faster:
+ g_BlockSpreadLightFalloff[E_BLOCK_LAVA] = 3;
+ g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_LAVA] = 3;
+ g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_WATER] = 3;
+ g_BlockSpreadLightFalloff[E_BLOCK_WATER] = 3;
+
+ // Transparent blocks
+ g_BlockTransparent[E_BLOCK_ACTIVATOR_RAIL] = true;
+ g_BlockTransparent[E_BLOCK_AIR] = true;
+ g_BlockTransparent[E_BLOCK_BIG_FLOWER] = true;
+ g_BlockTransparent[E_BLOCK_BROWN_MUSHROOM] = true;
+ g_BlockTransparent[E_BLOCK_CARROTS] = true;
+ g_BlockTransparent[E_BLOCK_CHEST] = true;
+ g_BlockTransparent[E_BLOCK_COBWEB] = true;
+ g_BlockTransparent[E_BLOCK_CROPS] = true;
+ g_BlockTransparent[E_BLOCK_DANDELION] = true;
+ g_BlockTransparent[E_BLOCK_DETECTOR_RAIL] = true;
+ g_BlockTransparent[E_BLOCK_FENCE] = true;
+ g_BlockTransparent[E_BLOCK_FENCE_GATE] = true;
+ g_BlockTransparent[E_BLOCK_FIRE] = true;
+ g_BlockTransparent[E_BLOCK_FLOWER] = true;
+ g_BlockTransparent[E_BLOCK_FLOWER_POT] = true;
+ g_BlockTransparent[E_BLOCK_GLASS] = true;
+ g_BlockTransparent[E_BLOCK_GLASS_PANE] = true;
+ g_BlockTransparent[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE] = true;
+ g_BlockTransparent[E_BLOCK_ICE] = true;
+ g_BlockTransparent[E_BLOCK_IRON_DOOR] = true;
+ g_BlockTransparent[E_BLOCK_LAVA] = true;
+ g_BlockTransparent[E_BLOCK_LEAVES] = true;
+ g_BlockTransparent[E_BLOCK_LEVER] = true;
+ g_BlockTransparent[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE] = true;
+ g_BlockTransparent[E_BLOCK_MELON_STEM] = true;
+ g_BlockTransparent[E_BLOCK_NETHER_BRICK_FENCE] = true;
+ g_BlockTransparent[E_BLOCK_NEW_LEAVES] = true;
+ g_BlockTransparent[E_BLOCK_POTATOES] = true;
+ g_BlockTransparent[E_BLOCK_POWERED_RAIL] = true;
+ g_BlockTransparent[E_BLOCK_PISTON_EXTENSION] = true;
+ g_BlockTransparent[E_BLOCK_PUMPKIN_STEM] = true;
+ g_BlockTransparent[E_BLOCK_RAIL] = true;
+ g_BlockTransparent[E_BLOCK_RED_MUSHROOM] = true;
+ g_BlockTransparent[E_BLOCK_SIGN_POST] = true;
+ g_BlockTransparent[E_BLOCK_SNOW] = true;
+ g_BlockTransparent[E_BLOCK_STAINED_GLASS] = true;
+ g_BlockTransparent[E_BLOCK_STAINED_GLASS_PANE] = true;
+ g_BlockTransparent[E_BLOCK_STATIONARY_LAVA] = true;
+ g_BlockTransparent[E_BLOCK_STATIONARY_WATER] = true;
+ g_BlockTransparent[E_BLOCK_STONE_PRESSURE_PLATE] = true;
+ g_BlockTransparent[E_BLOCK_TALL_GRASS] = true;
+ g_BlockTransparent[E_BLOCK_TORCH] = true;
+ g_BlockTransparent[E_BLOCK_VINES] = true;
+ g_BlockTransparent[E_BLOCK_WALLSIGN] = true;
+ g_BlockTransparent[E_BLOCK_WATER] = true;
+ g_BlockTransparent[E_BLOCK_WOODEN_DOOR] = true;
+ g_BlockTransparent[E_BLOCK_WOODEN_PRESSURE_PLATE] = true;
+
+ // TODO: Any other transparent blocks?
+
+ // One hit break blocks:
+ g_BlockOneHitDig[E_BLOCK_ACTIVE_COMPARATOR] = true;
+ g_BlockOneHitDig[E_BLOCK_BIG_FLOWER] = true;
+ g_BlockOneHitDig[E_BLOCK_BROWN_MUSHROOM] = true;
+ g_BlockOneHitDig[E_BLOCK_CARROTS] = true;
+ g_BlockOneHitDig[E_BLOCK_CROPS] = true;
+ g_BlockOneHitDig[E_BLOCK_DANDELION] = true;
+ g_BlockOneHitDig[E_BLOCK_FIRE] = true;
+ g_BlockOneHitDig[E_BLOCK_FLOWER] = true;
+ g_BlockOneHitDig[E_BLOCK_FLOWER_POT] = true;
+ g_BlockOneHitDig[E_BLOCK_INACTIVE_COMPARATOR] = true;
+ g_BlockOneHitDig[E_BLOCK_MELON_STEM] = true;
+ g_BlockOneHitDig[E_BLOCK_POTATOES] = true;
+ g_BlockOneHitDig[E_BLOCK_PUMPKIN_STEM] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_OFF] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_ON] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_OFF] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_ON] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true;
+ g_BlockOneHitDig[E_BLOCK_RED_MUSHROOM] = true;
+ g_BlockOneHitDig[E_BLOCK_REEDS] = true;
+ g_BlockOneHitDig[E_BLOCK_SAPLING] = true;
+ g_BlockOneHitDig[E_BLOCK_TNT] = true;
+ g_BlockOneHitDig[E_BLOCK_TALL_GRASS] = true;
+ g_BlockOneHitDig[E_BLOCK_TORCH] = true;
+
+ // Blocks that break when pushed by piston:
+ g_BlockPistonBreakable[E_BLOCK_ACTIVE_COMPARATOR] = true;
+ g_BlockPistonBreakable[E_BLOCK_AIR] = true;
+ g_BlockPistonBreakable[E_BLOCK_BED] = true;
+ g_BlockPistonBreakable[E_BLOCK_BIG_FLOWER] = true;
+ g_BlockPistonBreakable[E_BLOCK_BROWN_MUSHROOM] = true;
+ g_BlockPistonBreakable[E_BLOCK_COBWEB] = true;
+ g_BlockPistonBreakable[E_BLOCK_CROPS] = true;
+ g_BlockPistonBreakable[E_BLOCK_DANDELION] = true;
+ g_BlockPistonBreakable[E_BLOCK_DEAD_BUSH] = true;
+ g_BlockPistonBreakable[E_BLOCK_FIRE] = true;
+ g_BlockPistonBreakable[E_BLOCK_FLOWER] = true;
+ g_BlockPistonBreakable[E_BLOCK_INACTIVE_COMPARATOR] = true;
+ g_BlockPistonBreakable[E_BLOCK_IRON_DOOR] = true;
+ g_BlockPistonBreakable[E_BLOCK_JACK_O_LANTERN] = true;
+ g_BlockPistonBreakable[E_BLOCK_LADDER] = true;
+ g_BlockPistonBreakable[E_BLOCK_LAVA] = true;
+ g_BlockPistonBreakable[E_BLOCK_LEVER] = true;
+ g_BlockPistonBreakable[E_BLOCK_MELON] = true;
+ g_BlockPistonBreakable[E_BLOCK_MELON_STEM] = true;
+ g_BlockPistonBreakable[E_BLOCK_PUMPKIN] = true;
+ g_BlockPistonBreakable[E_BLOCK_PUMPKIN_STEM] = true;
+ g_BlockPistonBreakable[E_BLOCK_REDSTONE_REPEATER_OFF] = true;
+ g_BlockPistonBreakable[E_BLOCK_REDSTONE_REPEATER_ON] = true;
+ g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_OFF] = true;
+ g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_ON] = true;
+ g_BlockPistonBreakable[E_BLOCK_REDSTONE_WIRE] = true;
+ g_BlockPistonBreakable[E_BLOCK_RED_MUSHROOM] = true;
+ g_BlockPistonBreakable[E_BLOCK_REEDS] = true;
+ g_BlockPistonBreakable[E_BLOCK_SNOW] = true;
+ g_BlockPistonBreakable[E_BLOCK_STATIONARY_LAVA] = true;
+ g_BlockPistonBreakable[E_BLOCK_STATIONARY_WATER] = true;
+ g_BlockPistonBreakable[E_BLOCK_STONE_BUTTON] = true;
+ g_BlockPistonBreakable[E_BLOCK_STONE_PRESSURE_PLATE] = true;
+ g_BlockPistonBreakable[E_BLOCK_TALL_GRASS] = true;
+ g_BlockPistonBreakable[E_BLOCK_TORCH] = true;
+ g_BlockPistonBreakable[E_BLOCK_VINES] = true;
+ g_BlockPistonBreakable[E_BLOCK_WATER] = true;
+ g_BlockPistonBreakable[E_BLOCK_WOODEN_BUTTON] = true;
+ g_BlockPistonBreakable[E_BLOCK_WOODEN_DOOR] = true;
+ g_BlockPistonBreakable[E_BLOCK_WOODEN_PRESSURE_PLATE] = true;
+
+
+ // Blocks that cannot be snowed over:
+ g_BlockIsSnowable[E_BLOCK_ACTIVE_COMPARATOR] = false;
+ g_BlockIsSnowable[E_BLOCK_AIR] = false;
+ g_BlockIsSnowable[E_BLOCK_BIG_FLOWER] = false;
+ g_BlockIsSnowable[E_BLOCK_BROWN_MUSHROOM] = false;
+ g_BlockIsSnowable[E_BLOCK_CACTUS] = false;
+ g_BlockIsSnowable[E_BLOCK_CHEST] = false;
+ g_BlockIsSnowable[E_BLOCK_CROPS] = false;
+ g_BlockIsSnowable[E_BLOCK_DANDELION] = false;
+ g_BlockIsSnowable[E_BLOCK_FIRE] = false;
+ g_BlockIsSnowable[E_BLOCK_FLOWER] = false;
+ g_BlockIsSnowable[E_BLOCK_GLASS] = false;
+ g_BlockIsSnowable[E_BLOCK_ICE] = false;
+ g_BlockIsSnowable[E_BLOCK_INACTIVE_COMPARATOR] = false;
+ g_BlockIsSnowable[E_BLOCK_LAVA] = false;
+ g_BlockIsSnowable[E_BLOCK_LILY_PAD] = false;
+ g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_OFF] = false;
+ g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_ON] = false;
+ g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_OFF] = false;
+ g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_ON] = false;
+ g_BlockIsSnowable[E_BLOCK_REDSTONE_WIRE] = false;
+ g_BlockIsSnowable[E_BLOCK_RED_MUSHROOM] = false;
+ g_BlockIsSnowable[E_BLOCK_REEDS] = false;
+ g_BlockIsSnowable[E_BLOCK_SAPLING] = false;
+ g_BlockIsSnowable[E_BLOCK_SIGN_POST] = false;
+ g_BlockIsSnowable[E_BLOCK_SNOW] = false;
+ g_BlockIsSnowable[E_BLOCK_STAINED_GLASS] = false;
+ g_BlockIsSnowable[E_BLOCK_STAINED_GLASS_PANE] = false;
+ g_BlockIsSnowable[E_BLOCK_STATIONARY_LAVA] = false;
+ g_BlockIsSnowable[E_BLOCK_STATIONARY_WATER] = false;
+ g_BlockIsSnowable[E_BLOCK_TALL_GRASS] = false;
+ g_BlockIsSnowable[E_BLOCK_TNT] = false;
+ g_BlockIsSnowable[E_BLOCK_TORCH] = false;
+ g_BlockIsSnowable[E_BLOCK_VINES] = false;
+ g_BlockIsSnowable[E_BLOCK_WALLSIGN] = false;
+ g_BlockIsSnowable[E_BLOCK_WATER] = false;
+
+
+ // Blocks that don't drop without a special tool:
+ g_BlockRequiresSpecialTool[E_BLOCK_BRICK] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_CAULDRON] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_COAL_ORE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_COBBLESTONE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_COBBLESTONE_STAIRS] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_COBWEB] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_DIAMOND_BLOCK] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_DIAMOND_ORE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_DOUBLE_STONE_SLAB] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_EMERALD_ORE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_END_STONE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_GOLD_BLOCK] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_GOLD_ORE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_IRON_BLOCK] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_IRON_ORE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_LAPIS_BLOCK] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_LAPIS_ORE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_MOSSY_COBBLESTONE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_NETHERRACK] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_NETHER_BRICK] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_NETHER_BRICK_STAIRS] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_OBSIDIAN] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_REDSTONE_ORE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_REDSTONE_ORE_GLOWING] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_SANDSTONE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_SANDSTONE_STAIRS] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_SNOW] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_STONE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_STONE_BRICKS] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_STONE_BRICK_STAIRS] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_STONE_PRESSURE_PLATE] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_STONE_SLAB] = true;
+ g_BlockRequiresSpecialTool[E_BLOCK_VINES] = true;
+
+ // Nonsolid blocks:
+ g_BlockIsSolid[E_BLOCK_ACTIVATOR_RAIL] = false;
+ g_BlockIsSolid[E_BLOCK_AIR] = false;
+ g_BlockIsSolid[E_BLOCK_BIG_FLOWER] = false;
+ g_BlockIsSolid[E_BLOCK_BROWN_MUSHROOM] = false;
+ g_BlockIsSolid[E_BLOCK_CARROTS] = false;
+ g_BlockIsSolid[E_BLOCK_COBWEB] = false;
+ g_BlockIsSolid[E_BLOCK_CROPS] = false;
+ g_BlockIsSolid[E_BLOCK_DANDELION] = false;
+ g_BlockIsSolid[E_BLOCK_DETECTOR_RAIL] = false;
+ g_BlockIsSolid[E_BLOCK_END_PORTAL] = false;
+ g_BlockIsSolid[E_BLOCK_FIRE] = false;
+ g_BlockIsSolid[E_BLOCK_FLOWER] = false;
+ g_BlockIsSolid[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE] = false;
+ g_BlockIsSolid[E_BLOCK_LAVA] = false;
+ g_BlockIsSolid[E_BLOCK_LEVER] = false;
+ g_BlockIsSolid[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE] = false;
+ g_BlockIsSolid[E_BLOCK_MELON_STEM] = false;
+ g_BlockIsSolid[E_BLOCK_NETHER_PORTAL] = false;
+ g_BlockIsSolid[E_BLOCK_PISTON_EXTENSION] = false;
+ g_BlockIsSolid[E_BLOCK_RAIL] = false;
+ g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_OFF] = false;
+ g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_ON] = false;
+ g_BlockIsSolid[E_BLOCK_REDSTONE_WIRE] = false;
+ g_BlockIsSolid[E_BLOCK_RED_MUSHROOM] = false;
+ g_BlockIsSolid[E_BLOCK_REEDS] = false;
+ g_BlockIsSolid[E_BLOCK_SAPLING] = false;
+ g_BlockIsSolid[E_BLOCK_SIGN_POST] = false;
+ g_BlockIsSolid[E_BLOCK_SNOW] = false;
+ g_BlockIsSolid[E_BLOCK_STATIONARY_LAVA] = false;
+ g_BlockIsSolid[E_BLOCK_STATIONARY_WATER] = false;
+ g_BlockIsSolid[E_BLOCK_STONE_BUTTON] = false;
+ g_BlockIsSolid[E_BLOCK_STONE_PRESSURE_PLATE] = false;
+ g_BlockIsSolid[E_BLOCK_TALL_GRASS] = false;
+ g_BlockIsSolid[E_BLOCK_TORCH] = false;
+ g_BlockIsSolid[E_BLOCK_TRIPWIRE] = false;
+ g_BlockIsSolid[E_BLOCK_VINES] = false;
+ g_BlockIsSolid[E_BLOCK_WALLSIGN] = false;
+ g_BlockIsSolid[E_BLOCK_WATER] = false;
+ g_BlockIsSolid[E_BLOCK_WOODEN_BUTTON] = false;
+ g_BlockIsSolid[E_BLOCK_WOODEN_PRESSURE_PLATE] = false;
+ g_BlockIsSolid[E_BLOCK_WOODEN_SLAB] = false;
+
+ // Torch placeable blocks:
+ g_BlockIsTorchPlaceable[E_BLOCK_BEDROCK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_COAL] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_REDSTONE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_BOOKCASE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_BRICK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_CLAY] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_COAL_ORE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_COBBLESTONE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_COMMAND_BLOCK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_CRAFTING_TABLE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_BLOCK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_ORE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_DIRT] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_DISPENSER] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_STONE_SLAB] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_WOODEN_SLAB] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_DROPPER] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_BLOCK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_ORE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_END_STONE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_FURNACE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_GLOWSTONE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_GOLD_BLOCK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_GOLD_ORE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_GRASS] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_GRAVEL] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_HARDENED_CLAY] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_HAY_BALE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_HUGE_BROWN_MUSHROOM] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_HUGE_RED_MUSHROOM] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_IRON_BLOCK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_IRON_ORE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_JACK_O_LANTERN] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_JUKEBOX] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_BLOCK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_ORE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_LOG] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_MELON] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_MOSSY_COBBLESTONE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_MYCELIUM] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_NETHERRACK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_NETHER_BRICK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_NETHER_QUARTZ_ORE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_NOTE_BLOCK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_OBSIDIAN] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_PACKED_ICE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_PLANKS] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_PUMPKIN] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_QUARTZ_BLOCK] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_OFF] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_ON] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE_GLOWING] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_SANDSTONE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_SAND] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_SILVERFISH_EGG] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_SPONGE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_STAINED_CLAY] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_WOOL] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_STONE] = true;
+ g_BlockIsTorchPlaceable[E_BLOCK_STONE_BRICKS] = true;
+ }
+} BlockPropertiesInitializer;
+
+
+
+
+
diff --git a/src/BlockID.h b/src/BlockID.h
new file mode 100644
index 000000000..f3cbc46d6
--- /dev/null
+++ b/src/BlockID.h
@@ -0,0 +1,907 @@
+#pragma once
+
+// tolua_begin
+enum ENUM_BLOCK_ID
+{
+ E_BLOCK_AIR = 0,
+ E_BLOCK_STONE = 1,
+ E_BLOCK_GRASS = 2,
+ E_BLOCK_DIRT = 3,
+ E_BLOCK_COBBLESTONE = 4,
+ E_BLOCK_PLANKS = 5,
+ E_BLOCK_SAPLING = 6,
+ E_BLOCK_BEDROCK = 7,
+ E_BLOCK_WATER = 8,
+ E_BLOCK_STATIONARY_WATER = 9,
+ E_BLOCK_LAVA = 10,
+ E_BLOCK_STATIONARY_LAVA = 11,
+ E_BLOCK_SAND = 12,
+ E_BLOCK_GRAVEL = 13,
+ E_BLOCK_GOLD_ORE = 14,
+ E_BLOCK_IRON_ORE = 15,
+ E_BLOCK_COAL_ORE = 16,
+ E_BLOCK_LOG = 17,
+ E_BLOCK_LEAVES = 18,
+ E_BLOCK_SPONGE = 19,
+ E_BLOCK_GLASS = 20,
+ E_BLOCK_LAPIS_ORE = 21,
+ E_BLOCK_LAPIS_BLOCK = 22,
+ E_BLOCK_DISPENSER = 23,
+ E_BLOCK_SANDSTONE = 24,
+ E_BLOCK_NOTE_BLOCK = 25,
+ E_BLOCK_BED = 26,
+ E_BLOCK_POWERED_RAIL = 27,
+ E_BLOCK_DETECTOR_RAIL = 28,
+ E_BLOCK_STICKY_PISTON = 29,
+ E_BLOCK_COBWEB = 30,
+ E_BLOCK_TALL_GRASS = 31,
+ E_BLOCK_DEAD_BUSH = 32,
+ E_BLOCK_PISTON = 33,
+ E_BLOCK_PISTON_EXTENSION = 34,
+ E_BLOCK_WOOL = 35,
+ E_BLOCK_PISTON_MOVED_BLOCK = 36,
+ E_BLOCK_DANDELION = 37,
+ E_BLOCK_FLOWER = 38,
+ E_BLOCK_BROWN_MUSHROOM = 39,
+ E_BLOCK_RED_MUSHROOM = 40,
+ E_BLOCK_GOLD_BLOCK = 41,
+ E_BLOCK_IRON_BLOCK = 42,
+ E_BLOCK_DOUBLE_STONE_SLAB = 43,
+ E_BLOCK_STONE_SLAB = 44,
+ E_BLOCK_BRICK = 45,
+ E_BLOCK_TNT = 46,
+ E_BLOCK_BOOKCASE = 47,
+ E_BLOCK_MOSSY_COBBLESTONE = 48,
+ E_BLOCK_OBSIDIAN = 49,
+ E_BLOCK_TORCH = 50,
+ E_BLOCK_FIRE = 51,
+ E_BLOCK_MOB_SPAWNER = 52,
+ E_BLOCK_WOODEN_STAIRS = 53,
+ E_BLOCK_CHEST = 54,
+ E_BLOCK_REDSTONE_WIRE = 55,
+ E_BLOCK_DIAMOND_ORE = 56,
+ E_BLOCK_DIAMOND_BLOCK = 57,
+ E_BLOCK_CRAFTING_TABLE = 58,
+ E_BLOCK_WORKBENCH = 58,
+ E_BLOCK_CROPS = 59,
+ E_BLOCK_FARMLAND = 60,
+ E_BLOCK_FURNACE = 61,
+ E_BLOCK_LIT_FURNACE = 62,
+ E_BLOCK_BURNING_FURNACE = 62,
+ E_BLOCK_SIGN_POST = 63,
+ E_BLOCK_WOODEN_DOOR = 64,
+ E_BLOCK_LADDER = 65,
+ E_BLOCK_RAIL = 66,
+ E_BLOCK_MINECART_TRACKS = 66,
+ E_BLOCK_COBBLESTONE_STAIRS = 67,
+ E_BLOCK_WALLSIGN = 68,
+ E_BLOCK_LEVER = 69,
+ E_BLOCK_STONE_PRESSURE_PLATE = 70,
+ E_BLOCK_IRON_DOOR = 71,
+ E_BLOCK_WOODEN_PRESSURE_PLATE = 72,
+ E_BLOCK_REDSTONE_ORE = 73,
+ E_BLOCK_REDSTONE_ORE_GLOWING = 74,
+ E_BLOCK_REDSTONE_TORCH_OFF = 75,
+ E_BLOCK_REDSTONE_TORCH_ON = 76,
+ E_BLOCK_STONE_BUTTON = 77,
+ E_BLOCK_SNOW = 78,
+ E_BLOCK_ICE = 79,
+ E_BLOCK_SNOW_BLOCK = 80,
+ E_BLOCK_CACTUS = 81,
+ E_BLOCK_CLAY = 82,
+ E_BLOCK_SUGARCANE = 83,
+ E_BLOCK_REEDS = 83,
+ E_BLOCK_JUKEBOX = 84,
+ E_BLOCK_FENCE = 85,
+ E_BLOCK_PUMPKIN = 86,
+ E_BLOCK_NETHERRACK = 87,
+ E_BLOCK_SOULSAND = 88,
+ E_BLOCK_GLOWSTONE = 89,
+ E_BLOCK_NETHER_PORTAL = 90,
+ E_BLOCK_JACK_O_LANTERN = 91,
+ E_BLOCK_CAKE = 92,
+ E_BLOCK_REDSTONE_REPEATER_OFF = 93,
+ E_BLOCK_REDSTONE_REPEATER_ON = 94,
+ E_BLOCK_STAINED_GLASS = 95,
+ E_BLOCK_TRAPDOOR = 96,
+ E_BLOCK_SILVERFISH_EGG = 97,
+ E_BLOCK_STONE_BRICKS = 98,
+ E_BLOCK_HUGE_BROWN_MUSHROOM = 99,
+ E_BLOCK_HUGE_RED_MUSHROOM = 100,
+ E_BLOCK_IRON_BARS = 101,
+ E_BLOCK_GLASS_PANE = 102,
+ E_BLOCK_MELON = 103,
+ E_BLOCK_PUMPKIN_STEM = 104,
+ E_BLOCK_MELON_STEM = 105,
+ E_BLOCK_VINES = 106,
+ E_BLOCK_FENCE_GATE = 107,
+ E_BLOCK_BRICK_STAIRS = 108,
+ E_BLOCK_STONE_BRICK_STAIRS = 109,
+ E_BLOCK_MYCELIUM = 110,
+ E_BLOCK_LILY_PAD = 111,
+ E_BLOCK_NETHER_BRICK = 112,
+ E_BLOCK_NETHER_BRICK_FENCE = 113,
+ E_BLOCK_NETHER_BRICK_STAIRS = 114,
+ E_BLOCK_NETHER_WART = 115,
+ E_BLOCK_ENCHANTMENT_TABLE = 116,
+ E_BLOCK_BREWING_STAND = 117,
+ E_BLOCK_CAULDRON = 118,
+ E_BLOCK_END_PORTAL = 119,
+ E_BLOCK_END_PORTAL_FRAME = 120,
+ E_BLOCK_END_STONE = 121,
+ E_BLOCK_DRAGON_EGG = 122,
+ E_BLOCK_REDSTONE_LAMP_OFF = 123,
+ E_BLOCK_REDSTONE_LAMP_ON = 124,
+ E_BLOCK_DOUBLE_WOODEN_SLAB = 125,
+ E_BLOCK_WOODEN_SLAB = 126,
+ E_BLOCK_COCOA_POD = 127,
+ E_BLOCK_SANDSTONE_STAIRS = 128,
+ E_BLOCK_EMERALD_ORE = 129,
+ E_BLOCK_ENDER_CHEST = 130,
+ E_BLOCK_TRIPWIRE_HOOK = 131,
+ E_BLOCK_TRIPWIRE = 132,
+ E_BLOCK_EMERALD_BLOCK = 133,
+ E_BLOCK_SPRUCE_WOOD_STAIRS = 134,
+ E_BLOCK_BIRCH_WOOD_STAIRS = 135,
+ E_BLOCK_JUNGLE_WOOD_STAIRS = 136,
+ E_BLOCK_COMMAND_BLOCK = 137,
+ E_BLOCK_BEACON = 138,
+ E_BLOCK_COBBLESTONE_WALL = 139,
+ E_BLOCK_FLOWER_POT = 140,
+ E_BLOCK_CARROTS = 141,
+ E_BLOCK_POTATOES = 142,
+ E_BLOCK_WOODEN_BUTTON = 143,
+ E_BLOCK_HEAD = 144,
+ E_BLOCK_ANVIL = 145,
+ E_BLOCK_TRAPPED_CHEST = 146,
+ E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE = 147,
+ E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE = 148,
+
+ E_BLOCK_INACTIVE_COMPARATOR = 149,
+ E_BLOCK_ACTIVE_COMPARATOR = 150,
+ E_BLOCK_DAYLIGHT_SENSOR = 151,
+ E_BLOCK_BLOCK_OF_REDSTONE = 152,
+
+ E_BLOCK_NETHER_QUARTZ_ORE = 153,
+ E_BLOCK_HOPPER = 154,
+ E_BLOCK_QUARTZ_BLOCK = 155,
+ E_BLOCK_QUARTZ_STAIRS = 156,
+ E_BLOCK_ACTIVATOR_RAIL = 157,
+
+ E_BLOCK_DROPPER = 158,
+ E_BLOCK_STAINED_CLAY = 159,
+ E_BLOCK_STAINED_GLASS_PANE = 160,
+ E_BLOCK_NEW_LEAVES = 161, // Acacia and Dark Oak IDs in Minecraft 1.7.x
+ E_BLOCK_NEW_LOG = 162,
+ E_BLOCK_ACACIA_WOOD_STAIRS = 163,
+ E_BLOCK_DARK_OAK_WOOD_STAIRS = 164, /////////////////////////////////
+ E_BLOCK_HAY_BALE = 170,
+ E_BLOCK_CARPET = 171,
+ E_BLOCK_HARDENED_CLAY = 172,
+ E_BLOCK_BLOCK_OF_COAL = 173,
+ E_BLOCK_PACKED_ICE = 174,
+ E_BLOCK_BIG_FLOWER = 175,
+
+ // Keep these two as the last values, without a number - they will get their correct number assigned automagically by C++
+ // IsValidBlock() depends on this
+ E_BLOCK_NUMBER_OF_TYPES, ///< Number of individual (different) blocktypes
+ E_BLOCK_MAX_TYPE_ID = E_BLOCK_NUMBER_OF_TYPES - 1, ///< Maximum BlockType number used
+
+ // Synonym or ID compatibility
+
+ E_BLOCK_YELLOW_FLOWER = E_BLOCK_DANDELION,
+ E_BLOCK_RED_ROSE = E_BLOCK_FLOWER,
+ E_BLOCK_LOCKED_CHEST = E_BLOCK_STAINED_GLASS,
+};
+// tolua_end
+
+// tolua_begin
+enum ENUM_ITEM_ID
+{
+ E_ITEM_EMPTY = -1,
+
+ E_ITEM_FIRST = 256, // First true item type
+
+ E_ITEM_IRON_SHOVEL = 256,
+ E_ITEM_IRON_PICKAXE = 257,
+ E_ITEM_IRON_AXE = 258,
+ E_ITEM_FLINT_AND_STEEL = 259,
+ E_ITEM_RED_APPLE = 260,
+ E_ITEM_BOW = 261,
+ E_ITEM_ARROW = 262,
+ E_ITEM_COAL = 263,
+ E_ITEM_DIAMOND = 264,
+ E_ITEM_IRON = 265,
+ E_ITEM_GOLD = 266,
+ E_ITEM_IRON_SWORD = 267,
+ E_ITEM_WOODEN_SWORD = 268,
+ E_ITEM_WOODEN_SHOVEL = 269,
+ E_ITEM_WOODEN_PICKAXE = 270,
+ E_ITEM_WOODEN_AXE = 271,
+ E_ITEM_STONE_SWORD = 272,
+ E_ITEM_STONE_SHOVEL = 273,
+ E_ITEM_STONE_PICKAXE = 274,
+ E_ITEM_STONE_AXE = 275,
+ E_ITEM_DIAMOND_SWORD = 276,
+ E_ITEM_DIAMOND_SHOVEL = 277,
+ E_ITEM_DIAMOND_PICKAXE = 278,
+ E_ITEM_DIAMOND_AXE = 279,
+ E_ITEM_STICK = 280,
+ E_ITEM_BOWL = 281,
+ E_ITEM_MUSHROOM_SOUP = 282,
+ E_ITEM_GOLD_SWORD = 283,
+ E_ITEM_GOLD_SHOVEL = 284,
+ E_ITEM_GOLD_PICKAXE = 285,
+ E_ITEM_GOLD_AXE = 286,
+ E_ITEM_STRING = 287,
+ E_ITEM_FEATHER = 288,
+ E_ITEM_GUNPOWDER = 289,
+ E_ITEM_WOODEN_HOE = 290,
+ E_ITEM_STONE_HOE = 291,
+ E_ITEM_IRON_HOE = 292,
+ E_ITEM_DIAMOND_HOE = 293,
+ E_ITEM_GOLD_HOE = 294,
+ E_ITEM_SEEDS = 295,
+ E_ITEM_WHEAT = 296,
+ E_ITEM_BREAD = 297,
+ E_ITEM_LEATHER_CAP = 298,
+ E_ITEM_LEATHER_TUNIC = 299,
+ E_ITEM_LEATHER_PANTS = 300,
+ E_ITEM_LEATHER_BOOTS = 301,
+ E_ITEM_CHAIN_HELMET = 302,
+ E_ITEM_CHAIN_CHESTPLATE = 303,
+ E_ITEM_CHAIN_LEGGINGS = 304,
+ E_ITEM_CHAIN_BOOTS = 305,
+ E_ITEM_IRON_HELMET = 306,
+ E_ITEM_IRON_CHESTPLATE = 307,
+ E_ITEM_IRON_LEGGINGS = 308,
+ E_ITEM_IRON_BOOTS = 309,
+ E_ITEM_DIAMOND_HELMET = 310,
+ E_ITEM_DIAMOND_CHESTPLATE = 311,
+ E_ITEM_DIAMOND_LEGGINGS = 312,
+ E_ITEM_DIAMOND_BOOTS = 313,
+ E_ITEM_GOLD_HELMET = 314,
+ E_ITEM_GOLD_CHESTPLATE = 315,
+ E_ITEM_GOLD_LEGGINGS = 316,
+ E_ITEM_GOLD_BOOTS = 317,
+ E_ITEM_FLINT = 318,
+ E_ITEM_RAW_PORKCHOP = 319,
+ E_ITEM_COOKED_PORKCHOP = 320,
+ E_ITEM_PAINTINGS = 321,
+ E_ITEM_GOLDEN_APPLE = 322,
+ E_ITEM_SIGN = 323,
+ E_ITEM_WOODEN_DOOR = 324,
+ E_ITEM_BUCKET = 325,
+ E_ITEM_WATER_BUCKET = 326,
+ E_ITEM_LAVA_BUCKET = 327,
+ E_ITEM_MINECART = 328,
+ E_ITEM_SADDLE = 329,
+ E_ITEM_IRON_DOOR = 330,
+ E_ITEM_REDSTONE_DUST = 331,
+ E_ITEM_SNOWBALL = 332,
+ E_ITEM_BOAT = 333,
+ E_ITEM_LEATHER = 334,
+ E_ITEM_MILK = 335,
+ E_ITEM_CLAY_BRICK = 336,
+ E_ITEM_CLAY = 337,
+ E_ITEM_SUGARCANE = 338,
+ E_ITEM_SUGAR_CANE = 338,
+ E_ITEM_PAPER = 339,
+ E_ITEM_BOOK = 340,
+ E_ITEM_SLIMEBALL = 341,
+ E_ITEM_CHEST_MINECART = 342,
+ E_ITEM_FURNACE_MINECART = 343,
+ E_ITEM_EGG = 344,
+ E_ITEM_COMPASS = 345,
+ E_ITEM_FISHING_ROD = 346,
+ E_ITEM_CLOCK = 347,
+ E_ITEM_GLOWSTONE_DUST = 348,
+ E_ITEM_RAW_FISH = 349,
+ E_ITEM_COOKED_FISH = 350,
+ E_ITEM_DYE = 351,
+ E_ITEM_BONE = 352,
+ E_ITEM_SUGAR = 353,
+ E_ITEM_CAKE = 354,
+ E_ITEM_BED = 355,
+ E_ITEM_REDSTONE_REPEATER = 356,
+ E_ITEM_COOKIE = 357,
+ E_ITEM_MAP = 358,
+ E_ITEM_SHEARS = 359,
+ E_ITEM_MELON_SLICE = 360,
+ E_ITEM_PUMPKIN_SEEDS = 361,
+ E_ITEM_MELON_SEEDS = 362,
+ E_ITEM_RAW_BEEF = 363,
+ E_ITEM_STEAK = 364,
+ E_ITEM_RAW_CHICKEN = 365,
+ E_ITEM_COOKED_CHICKEN = 366,
+ E_ITEM_ROTTEN_FLESH = 367,
+ E_ITEM_ENDER_PEARL = 368,
+ E_ITEM_BLAZE_ROD = 369,
+ E_ITEM_GHAST_TEAR = 370,
+ E_ITEM_GOLD_NUGGET = 371,
+ E_ITEM_NETHER_WART = 372,
+ E_ITEM_POTIONS = 373,
+ E_ITEM_GLASS_BOTTLE = 374,
+ E_ITEM_SPIDER_EYE = 375,
+ E_ITEM_FERMENTED_SPIDER_EYE = 376,
+ E_ITEM_BLAZE_POWDER = 377,
+ E_ITEM_MAGMA_CREAM = 378,
+ E_ITEM_BREWING_STAND = 379,
+ E_ITEM_CAULDRON = 380,
+ E_ITEM_EYE_OF_ENDER = 381,
+ E_ITEM_GLISTERING_MELON = 382,
+ E_ITEM_SPAWN_EGG = 383,
+ E_ITEM_BOTTLE_O_ENCHANTING = 384,
+ E_ITEM_FIRE_CHARGE = 385,
+ E_ITEM_BOOK_AND_QUILL = 386,
+ E_ITEM_WRITTEN_BOOK = 387,
+ E_ITEM_EMERALD = 388,
+ E_ITEM_ITEM_FRAME = 389,
+ E_ITEM_FLOWER_POT = 390,
+ E_ITEM_CARROT = 391,
+ E_ITEM_POTATO = 392,
+ E_ITEM_BAKED_POTATO = 393,
+ E_ITEM_POISONOUS_POTATO = 394,
+ E_ITEM_EMPTY_MAP = 395,
+ E_ITEM_GOLDEN_CARROT = 396,
+ E_ITEM_HEAD = 397,
+ E_ITEM_CARROT_ON_STICK = 398,
+ E_ITEM_NETHER_STAR = 399,
+ E_ITEM_PUMPKIN_PIE = 400,
+ E_ITEM_FIREWORK_ROCKET = 401,
+ E_ITEM_FIREWORK_STAR = 402,
+ E_ITEM_ENCHANTED_BOOK = 403,
+ E_ITEM_COMPARATOR = 404,
+ E_ITEM_NETHER_BRICK = 405,
+ E_ITEM_NETHER_QUARTZ = 406,
+ E_ITEM_MINECART_WITH_TNT = 407,
+ E_ITEM_MINECART_WITH_HOPPER = 408,
+ E_ITEM_IRON_HORSE_ARMOR = 417,
+ E_ITEM_GOLD_HORSE_ARMOR = 418,
+ E_ITEM_DIAMOND_HORSE_ARMOR = 419,
+ E_ITEM_LEAD = 420,
+ E_ITEM_NAME_TAG = 421,
+ E_ITEM_MINECART_WITH_COMMAND_BLOCK = 422,
+
+ // Keep these two as the last values of the consecutive list, without a number - they will get their correct number assigned automagically by C++
+ // IsValidItem() depends on this!
+ E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES, ///< Number of individual (different) consecutive itemtypes
+ E_ITEM_MAX_CONSECUTIVE_TYPE_ID = E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES - 1, ///< Maximum consecutive ItemType number used
+
+ E_ITEM_FIRST_DISC = 2256,
+ E_ITEM_13_DISC = 2256,
+ E_ITEM_CAT_DISC = 2257,
+ E_ITEM_BLOCKS_DISC = 2258,
+ E_ITEM_CHIRP_DISC = 2259,
+ E_ITEM_FAR_DISC = 2260,
+ E_ITEM_MALL_DISC = 2261,
+ E_ITEM_MELLOHI_DISC = 2262,
+ E_ITEM_STAL_DISC = 2263,
+ E_ITEM_STRAD_DISC = 2264,
+ E_ITEM_WARD_DISC = 2265,
+ E_ITEM_11_DISC = 2266,
+ E_ITEM_WAIT_DISC = 2267,
+
+ // Keep these two as the last values of the disc list, without a number - they will get their correct number assigned automagically by C++
+ // IsValidItem() depends on this!
+ E_ITEM_LAST_DISC_PLUS_ONE, ///< Useless, really, but needs to be present for the following value
+ E_ITEM_LAST_DISC = E_ITEM_LAST_DISC_PLUS_ONE - 1, ///< Maximum disc itemtype number used
+
+ E_ITEM_LAST = E_ITEM_LAST_DISC, ///< Maximum valid ItemType
+};
+
+
+
+
+
+enum
+{
+ // Please keep this list alpha-sorted by the blocktype / itemtype part
+ // then number-sorted for the same block / item
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Block metas:
+
+ // E_BLOCK_CHEST metas:
+ E_META_CHEST_FACING_ZM = 2,
+ E_META_CHEST_FACING_ZP = 3,
+ E_META_CHEST_FACING_XM = 4,
+ E_META_CHEST_FACING_XP = 5,
+
+ // E_BLOCK_DISPENSER / E_BLOCK_DROPPER metas:
+ E_META_DROPSPENSER_FACING_YM = 0,
+ E_META_DROPSPENSER_FACING_YP = 1,
+ E_META_DROPSPENSER_FACING_ZM = 2,
+ E_META_DROPSPENSER_FACING_ZP = 3,
+ E_META_DROPSPENSER_FACING_XM = 4,
+ E_META_DROPSPENSER_FACING_XP = 5,
+
+ // E_BLOCK_DOUBLE_STONE_SLAB metas:
+ E_META_DOUBLE_STONE_SLAB_STONE = 0,
+ E_META_DOUBLE_STONE_SLAB_SANDSTONE = 1,
+ E_META_DOUBLE_STONE_SLAB_WOODEN = 2,
+ E_META_DOUBLE_STONE_SLAB_COBBLESTONE = 3,
+ E_META_DOUBLE_STONE_SLAB_BRICK = 4,
+ E_META_DOUBLE_STONE_SLAB_STONE_BRICK = 5,
+ E_META_DOUBLE_STONE_SLAB_NETHER_BRICK = 6,
+ E_META_DOUBLE_STONE_SLAB_STONE_SECRET = 7,
+
+
+
+ // E_BLOCK_HOPPER metas:
+ E_META_HOPPER_FACING_YM = 0,
+ E_META_HOPPER_UNATTACHED = 1, // Hopper doesn't move items up, there's no YP
+ E_META_HOPPER_FACING_ZM = 2,
+ E_META_HOPPER_FACING_ZP = 3,
+ E_META_HOPPER_FACING_XM = 4,
+ E_META_HOPPER_FACING_XP = 5,
+
+ // E_BLOCK_LEAVES metas:
+ E_META_LEAVES_APPLE = 0,
+ E_META_LEAVES_CONIFER = 1,
+ E_META_LEAVES_BIRCH = 2,
+ E_META_LEAVES_JUNGLE = 3,
+
+ // E_BLOCK_LOG metas:
+ E_META_LOG_APPLE = 0,
+ E_META_LOG_CONIFER = 1,
+ E_META_LOG_BIRCH = 2,
+ E_META_LOG_JUNGLE = 3,
+
+ // E_BLOCK_PLANKS metas:
+ E_META_PLANKS_APPLE = 0,
+ E_META_PLANKS_CONIFER = 1,
+ E_META_PLANKS_BIRCH = 2,
+ E_META_PLANKS_JUNGLE = 3,
+
+ // E_BLOCK_SANDSTONE metas:
+ E_META_SANDSTONE_NORMAL = 0,
+ E_META_SANDSTONE_ORNAMENT = 1,
+ E_META_SANDSTONE_SMOOTH = 2,
+
+ // E_BLOCK_SAPLING metas (lowest 3 bits):
+ E_META_SAPLING_APPLE = 0,
+ E_META_SAPLING_CONIFER = 1,
+ E_META_SAPLING_BIRCH = 2,
+ E_META_SAPLING_JUNGLE = 3,
+
+ // E_BLOCK_SILVERFISH_EGG metas:
+ E_META_SILVERFISH_EGG_STONE = 0,
+ E_META_SILVERFISH_EGG_COBBLESTONE = 1,
+ E_META_SILVERFISH_EGG_STONE_BRICK = 2,
+
+ // E_BLOCK_STONE_SLAB metas:
+ E_META_STONE_SLAB_STONE = 0,
+ E_META_STONE_SLAB_SANDSTONE = 1,
+ E_META_STONE_SLAB_PLANKS = 2,
+ E_META_STONE_SLAB_COBBLESTONE = 3,
+ E_META_STONE_SLAB_BRICK = 4,
+ E_META_STONE_SLAB_STONE_BRICK = 5,
+ E_META_STONE_SLAB_NETHER_BRICK = 6,
+ E_META_STONE_SLAB_STONE_SECRET = 7,
+
+ // E_BLOCK_STONE_BRICKS metas:
+ E_META_STONE_BRICK_NORMAL = 0,
+ E_META_STONE_BRICK_MOSSY = 1,
+ E_META_STONE_BRICK_CRACKED = 2,
+ E_META_STONE_BRICK_ORNAMENT = 3,
+
+ // E_BLOCK_TALL_GRASS metas:
+ E_META_TALL_GRASS_DEAD_SHRUB = 0,
+ E_META_TALL_GRASS_GRASS = 1,
+ E_META_TALL_GRASS_FERN = 2,
+
+ // E_BLOCK_TORCH, E_BLOCK_REDSTONE_TORCH_OFF, E_BLOCK_REDSTONE_TORCH_ON metas:
+ E_META_TORCH_EAST = 1, // east face of the block, pointing east
+ E_META_TORCH_WEST = 2,
+ E_META_TORCH_SOUTH = 3,
+ E_META_TORCH_NORTH = 4,
+ E_META_TORCH_FLOOR = 5,
+ E_META_TORCH_XM = 1, // Torch attached to the XM side of its block
+ E_META_TORCH_XP = 2, // Torch attached to the XP side of its block
+ E_META_TORCH_ZM = 3, // Torch attached to the ZM side of its block
+ E_META_TORCH_ZP = 4, // Torch attached to the ZP side of its block
+
+ // E_BLOCK_WOODEN_DOUBLE_SLAB metas:
+ E_META_WOODEN_DOUBLE_SLAB_APPLE = 0,
+ E_META_WOODEN_DOUBLE_SLAB_CONIFER = 1,
+ E_META_WOODEN_DOUBLE_SLAB_BIRCH = 2,
+ E_META_WOODEN_DOUBLE_SLAB_JUNGLE = 3,
+ E_META_WOODEN_DOUBLE_SLAB_ACACIA = 4,
+ E_META_WOODEN_DOUBLE_SLAB_DARK_OAK = 5,
+
+ // E_BLOCK_WOODEN_SLAB metas:
+ E_META_WOODEN_SLAB_APPLE = 0,
+ E_META_WOODEN_SLAB_CONIFER = 1,
+ E_META_WOODEN_SLAB_BIRCH = 2,
+ E_META_WOODEN_SLAB_JUNGLE = 3,
+ E_META_WOODEN_SLAB_ACACIA = 4,
+ E_META_WOODEN_SLAB_DARK_OAK = 5,
+
+ // E_BLOCK_WOOL metas:
+ E_META_WOOL_WHITE = 0,
+ E_META_WOOL_ORANGE = 1,
+ E_META_WOOL_MAGENTA = 2,
+ E_META_WOOL_LIGHTBLUE = 3,
+ E_META_WOOL_YELLOW = 4,
+ E_META_WOOL_LIGHTGREEN = 5,
+ E_META_WOOL_PINK = 6,
+ E_META_WOOL_GRAY = 7,
+ E_META_WOOL_LIGHTGRAY = 8,
+ E_META_WOOL_CYAN = 9,
+ E_META_WOOL_PURPLE = 10,
+ E_META_WOOL_BLUE = 11,
+ E_META_WOOL_BROWN = 12,
+ E_META_WOOL_GREEN = 13,
+ E_META_WOOL_RED = 14,
+ E_META_WOOL_BLACK = 15,
+
+ // E_BLOCK_CARPET metas:
+ E_META_CARPET_WHITE = 0,
+ E_META_CARPET_ORANGE = 1,
+ E_META_CARPET_MAGENTA = 2,
+ E_META_CARPET_LIGHTBLUE = 3,
+ E_META_CARPET_YELLOW = 4,
+ E_META_CARPET_LIGHTGREEN = 5,
+ E_META_CARPET_PINK = 6,
+ E_META_CARPET_GRAY = 7,
+ E_META_CARPET_LIGHTGRAY = 8,
+ E_META_CARPET_CYAN = 9,
+ E_META_CARPET_PURPLE = 10,
+ E_META_CARPET_BLUE = 11,
+ E_META_CARPET_BROWN = 12,
+ E_META_CARPET_GREEN = 13,
+ E_META_CARPET_RED = 14,
+ E_META_CARPET_BLACK = 15,
+
+ // E_BLOCK_STAINED_CLAY metas
+ E_META_STAINED_CLAY_WHITE = 0,
+ E_META_STAINED_CLAY_ORANGE = 1,
+ E_META_STAINED_CLAY_MAGENTA = 2,
+ E_META_STAINED_CLAY_LIGHTBLUE = 3,
+ E_META_STAINED_CLAY_YELLOW = 4,
+ E_META_STAINED_CLAY_LIGHTGREEN = 5,
+ E_META_STAINED_CLAY_PINK = 6,
+ E_META_STAINED_CLAY_GRAY = 7,
+ E_META_STAINED_CLAY_LIGHTGRAY = 8,
+ E_META_STAINED_CLAY_CYAN = 9,
+ E_META_STAINED_CLAY_PURPLE = 10,
+ E_META_STAINED_CLAY_BLUE = 11,
+ E_META_STAINED_CLAY_BROWN = 12,
+ E_META_STAINED_CLAY_GREEN = 13,
+ E_META_STAINED_CLAY_RED = 14,
+ E_META_STAINED_CLAY_BLACK = 15,
+
+ // E_BLOCK_STAINED_GLASS metas
+ E_META_STAINED_GLASS_WHITE = 0,
+ E_META_STAINED_GLASS_ORANGE = 1,
+ E_META_STAINED_GLASS_MAGENTA = 2,
+ E_META_STAINED_GLASS_LIGHTBLUE = 3,
+ E_META_STAINED_GLASS_YELLOW = 4,
+ E_META_STAINED_GLASS_LIGHTGREEN = 5,
+ E_META_STAINED_GLASS_PINK = 6,
+ E_META_STAINED_GLASS_GRAY = 7,
+ E_META_STAINED_GLASS_LIGHTGRAY = 8,
+ E_META_STAINED_GLASS_CYAN = 9,
+ E_META_STAINED_GLASS_PURPLE = 10,
+ E_META_STAINED_GLASS_BLUE = 11,
+ E_META_STAINED_GLASS_BROWN = 12,
+ E_META_STAINED_GLASS_GREEN = 13,
+ E_META_STAINED_GLASS_RED = 14,
+ E_META_STAINED_GLASS_BLACK = 15,
+
+ // E_BLOCK_STAINED_GLASS_PANE metas
+ E_META_STAINED_GLASS_PANE_WHITE = 0,
+ E_META_STAINED_GLASS_PANE_ORANGE = 1,
+ E_META_STAINED_GLASS_PANE_MAGENTA = 2,
+ E_META_STAINED_GLASS_PANE_LIGHTBLUE = 3,
+ E_META_STAINED_GLASS_PANE_YELLOW = 4,
+ E_META_STAINED_GLASS_PANE_LIGHTGREEN = 5,
+ E_META_STAINED_GLASS_PANE_PINK = 6,
+ E_META_STAINED_GLASS_PANE_GRAY = 7,
+ E_META_STAINED_GLASS_PANE_LIGHTGRAY = 8,
+ E_META_STAINED_GLASS_PANE_CYAN = 9,
+ E_META_STAINED_GLASS_PANE_PURPLE = 10,
+ E_META_STAINED_GLASS_PANE_BLUE = 11,
+ E_META_STAINED_GLASS_PANE_BROWN = 12,
+ E_META_STAINED_GLASS_PANE_GREEN = 13,
+ E_META_STAINED_GLASS_PANE_RED = 14,
+ E_META_STAINED_GLASS_PANE_BLACK = 15,
+
+ // E_BLOCK_SNOW metas:
+ E_META_SNOW_LAYER_ONE = 0,
+ E_META_SNOW_LAYER_TWO = 1,
+ E_META_SNOW_LAYER_THREE = 2,
+ E_META_SNOW_LAYER_FOUR = 3,
+ E_META_SNOW_LAYER_FIVE = 4,
+ E_META_SNOW_LAYER_SIX = 5,
+ E_META_SNOW_LAYER_SEVEN = 6,
+ E_META_SNOW_LAYER_EIGHT = 7,
+
+ // E_BLOCK_RAIL metas
+ E_META_RAIL_ZM_ZP = 0,
+ E_META_RAIL_XM_XP = 1,
+ E_META_RAIL_ASCEND_XP = 2,
+ E_META_RAIL_ASCEND_XM = 3,
+ E_META_RAIL_ASCEND_ZM = 4,
+ E_META_RAIL_ASCEND_ZP = 5,
+ E_META_RAIL_CURVED_ZP_XP = 6,
+ E_META_RAIL_CURVED_ZP_XM = 7,
+ E_META_RAIL_CURVED_ZM_XM = 8,
+ E_META_RAIL_CURVED_ZM_XP = 9,
+
+ //E_BLOCK_NEW_LEAVES metas
+ E_META_NEW_LEAVES_ACACIA_WOOD = 0,
+ E_META_NEW_LEAVES_DARK_OAK_WOOD = 1,
+
+ //E_BLOCK_NEW_LOG metas
+ E_META_NEW_LOG_ACACIA_WOOD = 0,
+ E_META_NEW_LOG_DARK_OAK_WOOD = 1,
+
+ //E_BLOCK_FLOWER metas
+ E_META_FLOWER_POPPY = 0,
+ E_META_FLOWER_BLUE_ORCHID = 1,
+ E_META_FLOWER_ALLIUM = 2,
+ E_META_FLOWER_RED_TULIP = 4,
+ E_META_FLOWER_ORANGE_TULIP = 5,
+ E_META_FLOWER_WHITE_TULIP = 6,
+ E_META_FLOWER_PINK_TULIP = 7,
+ E_META_FLOWER_OXEYE_DAISY = 8,
+
+ //E_BLOCK_BIG_FLOWER metas
+ E_META_BIG_FLOWER_SUNFLOWER = 0,
+ E_META_BIG_FLOWER_LILAC = 1,
+ E_META_BIG_FLOWER_DOUBLE_TALL_GRASS = 2,
+ E_META_BIG_FLOWER_LARGE_FERN = 3,
+ E_META_BIG_FLOWER_ROSE_BUSH = 4,
+ E_META_BIG_FLOWER_PEONY = 5,
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Item metas:
+
+ // E_ITEM_COAL metas:
+ E_META_COAL_NORMAL = 0,
+ E_META_COAL_CHARCOAL = 1,
+
+ // E_ITEM_DYE metas:
+ E_META_DYE_BLACK = 0,
+ E_META_DYE_RED = 1,
+ E_META_DYE_GREEN = 2,
+ E_META_DYE_BROWN = 3,
+ E_META_DYE_BLUE = 4,
+ E_META_DYE_PURPLE = 5,
+ E_META_DYE_CYAN = 6,
+ E_META_DYE_LIGHTGRAY = 7,
+ E_META_DYE_GRAY = 8,
+ E_META_DYE_PINK = 9,
+ E_META_DYE_LIGHTGREEN = 10,
+ E_META_DYE_YELLOW = 11,
+ E_META_DYE_LIGHTBLUE = 12,
+ E_META_DYE_MAGENTA = 13,
+ E_META_DYE_ORANGE = 14,
+ E_META_DYE_WHITE = 15,
+
+ // E_ITEM_GOLDEN_APPLE metas:
+ E_META_GOLDEN_APPLE_NORMAL = 0,
+ E_META_GOLDEN_APPLE_ENCHANTED = 1,
+
+ // E_ITEM_RAW_FISH metas:
+ E_META_RAW_FISH_FISH = 0,
+ E_META_RAW_FISH_SALMON = 1,
+ E_META_RAW_FISH_CLOWNFISH = 2,
+ E_META_RAW_FISH_PUFFERFISH = 3,
+
+ // E_ITEM_COOKED_FISH metas:
+ E_META_COOKED_FISH_FISH = 0,
+ E_META_COOKED_FISH_SALMON = 1,
+ E_META_COOKED_FISH_CLOWNFISH = 2,
+ E_META_COOKED_FISH_PUFFERFISH = 3,
+
+ // E_ITEM_MINECART_TRACKS metas:
+ E_META_TRACKS_X = 1,
+ E_META_TRACKS_Z = 0,
+
+ // E_ITEM_SPAWN_EGG metas:
+ // See also cMonster::eType, since monster type and spawn egg meta are the same
+ E_META_SPAWN_EGG_PICKUP = 1,
+ E_META_SPAWN_EGG_EXPERIENCE_ORB = 2,
+ E_META_SPAWN_EGG_LEASH_KNOT = 8,
+ E_META_SPAWN_EGG_PAINTING = 9,
+ E_META_SPAWN_EGG_ARROW = 10,
+ E_META_SPAWN_EGG_SNOWBALL = 11,
+ E_META_SPAWN_EGG_FIREBALL = 12,
+ E_META_SPAWN_EGG_SMALL_FIREBALL = 13,
+ E_META_SPAWN_EGG_ENDER_PEARL = 14,
+ E_META_SPAWN_EGG_EYE_OF_ENDER = 15,
+ E_META_SPAWN_EGG_SPLASH_POTION = 16,
+ E_META_SPAWN_EGG_EXP_BOTTLE = 17,
+ E_META_SPAWN_EGG_ITEM_FRAME = 18,
+ E_META_SPAWN_EGG_WITHER_SKULL = 19,
+ E_META_SPAWN_EGG_PRIMED_TNT = 20,
+ E_META_SPAWN_EGG_FALLING_BLOCK = 21,
+ E_META_SPAWN_EGG_FIREWORK = 22,
+ E_META_SPAWN_EGG_BOAT = 41,
+ E_META_SPAWN_EGG_MINECART = 42,
+ E_META_SPAWN_EGG_MINECART_CHEST = 43,
+ E_META_SPAWN_EGG_MINECART_FURNACE = 44,
+ E_META_SPAWN_EGG_MINECART_TNT = 45,
+ E_META_SPAWN_EGG_MINECART_HOPPER = 46,
+ E_META_SPAWN_EGG_MINECART_SPAWNER = 47,
+ E_META_SPAWN_EGG_CREEPER = 50,
+ E_META_SPAWN_EGG_SKELETON = 51,
+ E_META_SPAWN_EGG_SPIDER = 52,
+ E_META_SPAWN_EGG_GIANT = 53,
+ E_META_SPAWN_EGG_ZOMBIE = 54,
+ E_META_SPAWN_EGG_SLIME = 55,
+ E_META_SPAWN_EGG_GHAST = 56,
+ E_META_SPAWN_EGG_ZOMBIE_PIGMAN = 57,
+ E_META_SPAWN_EGG_ENDERMAN = 58,
+ E_META_SPAWN_EGG_CAVE_SPIDER = 59,
+ E_META_SPAWN_EGG_SILVERFISH = 60,
+ E_META_SPAWN_EGG_BLAZE = 61,
+ E_META_SPAWN_EGG_MAGMA_CUBE = 62,
+ E_META_SPAWN_EGG_ENDER_DRAGON = 63,
+ E_META_SPAWN_EGG_WITHER = 64,
+ E_META_SPAWN_EGG_BAT = 65,
+ E_META_SPAWN_EGG_WITCH = 66,
+ E_META_SPAWN_EGG_PIG = 90,
+ E_META_SPAWN_EGG_SHEEP = 91,
+ E_META_SPAWN_EGG_COW = 92,
+ E_META_SPAWN_EGG_CHICKEN = 93,
+ E_META_SPAWN_EGG_SQUID = 94,
+ E_META_SPAWN_EGG_WOLF = 95,
+ E_META_SPAWN_EGG_MOOSHROOM = 96,
+ E_META_SPAWN_EGG_SNOW_GOLEM = 97,
+ E_META_SPAWN_EGG_OCELOT = 98,
+ E_META_SPAWN_EGG_IRON_GOLEM = 99,
+ E_META_SPAWN_EGG_HORSE = 100,
+ E_META_SPAWN_EGG_VILLAGER = 120,
+ E_META_SPAWN_EGG_ENDER_CRYSTAL = 200,
+} ;
+
+
+
+
+
+/// Dimension of a world
+enum eDimension
+{
+ dimNether = -1,
+ dimOverworld = 0,
+ dimEnd = 1,
+} ;
+
+
+
+
+
+/// Damage type, used in the TakeDamageInfo structure and related functions
+enum eDamageType
+{
+ // Canonical names for the types (as documented in the plugin wiki):
+ dtAttack, // Being attacked by a mob
+ dtRangedAttack, // Being attacked by a projectile, possibly from a mob
+ dtLightning, // Hit by a lightning strike
+ dtFalling, // Falling down; dealt when hitting the ground
+ dtDrowning, // Drowning in water / lava
+ dtSuffocating, // Suffocating inside a block
+ dtStarving, // Hunger
+ dtCactusContact, // Contact with a cactus block
+ dtLavaContact, // Contact with a lava block
+ dtPoisoning, // Having the poison effect
+ dtOnFire, // Being on fire
+ dtFireContact, // Standing inside a fire block
+ dtInVoid, // Falling into the Void (Y < 0)
+ dtPotionOfHarming,
+ dtEnderPearl, // Thrown an ender pearl, teleported by it
+ dtAdmin, // Damage applied by an admin command
+
+ // Some common synonyms:
+ dtPawnAttack = dtAttack,
+ dtEntityAttack = dtAttack,
+ dtMob = dtAttack,
+ dtMobAttack = dtAttack,
+ dtArrowAttack = dtRangedAttack,
+ dtArrow = dtRangedAttack,
+ dtProjectile = dtRangedAttack,
+ dtFall = dtFalling,
+ dtDrown = dtDrowning,
+ dtSuffocation = dtSuffocating,
+ dtStarvation = dtStarving,
+ dtHunger = dtStarving,
+ dtCactus = dtCactusContact,
+ dtCactuses = dtCactusContact,
+ dtCacti = dtCactusContact,
+ dtLava = dtLavaContact,
+ dtPoison = dtPoisoning,
+ dtBurning = dtOnFire,
+ dtInFire = dtFireContact,
+ dtPlugin = dtAdmin,
+} ;
+
+
+
+
+
+enum eExplosionSource
+{
+ esOther,
+ esPrimedTNT,
+ esCreeper,
+ esBed,
+ esEnderCrystal,
+ esGhastFireball,
+ esWitherSkullBlack,
+ esWitherSkullBlue,
+ esWitherBirth,
+ esPlugin
+} ;
+
+// tolua_end
+
+
+
+
+// fwd:
+class cItem;
+class cIniFile;
+
+
+
+
+
+// tolua_begin
+
+/// Translates a blocktype string into blocktype. Takes either a number or an items.ini alias as input. Returns -1 on failure.
+extern BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString);
+
+/// Translates an itemtype string into an item. Takes either a number, number^number, number:number or an items.ini alias as input. Returns true if successful.
+extern bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item);
+
+/// Translates a full item into a string. If the ItemType is not recognized, the ItemType number is output into the string.
+extern AString ItemToString(const cItem & a_Item);
+
+/// Translates itemtype into a string. If the type is not recognized, the itemtype number is output into the string.
+extern AString ItemTypeToString(short a_ItemType);
+
+/// Translates a full item into a fully-specified string (including meta and count). If the ItemType is not recognized, the ItemType number is output into the string.
+extern AString ItemToFullString(const cItem & a_Item);
+
+/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns -1 on failure.
+extern EMCSBiome StringToBiome(const AString & a_BiomeString);
+
+/// Translates a mob string ("ocelot") to mobtype (E_ENTITY_TYPE_OCELOT)
+extern int StringToMobType(const AString & a_MobString);
+
+/// Translates a dimension string to dimension enum. Takes either a number or a dimension alias (built-in). Returns -1000 on failure
+extern eDimension StringToDimension(const AString & a_DimensionString);
+
+/// Translates damage type constant to a string representation (built-in).
+extern AString DamageTypeToString(eDamageType a_DamageType);
+
+/// Translates a damage type string to damage type. Takes either a number or a damage type alias (built-in). Returns -1 on failure
+extern eDamageType StringToDamageType(const AString & a_DamageString);
+
+/// Returns a cItem representing the item described in an IniFile's value; if the value doesn't exist, creates it with the provided default.
+extern cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default);
+
+// tolua_end
+
+
+
+
+
+// Block properties:
+extern NIBBLETYPE g_BlockLightValue[256];
+extern NIBBLETYPE g_BlockSpreadLightFalloff[256];
+extern bool g_BlockTransparent[256];
+extern bool g_BlockOneHitDig[256];
+extern bool g_BlockPistonBreakable[256];
+extern bool g_BlockIsSnowable[256];
+extern bool g_BlockRequiresSpecialTool[256];
+extern bool g_BlockIsSolid[256];
+extern bool g_BlockIsTorchPlaceable[256];
+
+
+
+
diff --git a/src/BlockTracer.h b/src/BlockTracer.h
new file mode 100644
index 000000000..d0a34811d
--- /dev/null
+++ b/src/BlockTracer.h
@@ -0,0 +1,104 @@
+
+// BlockTracer.h
+
+// Declares the classes common for all blocktracers
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd: World.h
+class cWorld;
+
+
+
+
+
+class cBlockTracer abstract
+{
+public:
+ /** The callback class is used to notify the caller of individual events that are being traced.
+ */
+ class cCallbacks abstract
+ {
+ public:
+ /** Called on each block encountered along the path, including the first block (path start)
+ When this callback returns true, the tracing is aborted.
+ */
+ virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) = 0;
+
+ /** Called on each block encountered along the path, including the first block (path start), if chunk data is not loaded
+ When this callback returns true, the tracing is aborted.
+ */
+ virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) { return false; }
+
+ /** Called when the path goes out of world, either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height)
+ The coords specify the exact point at which the path exited the world.
+ If this callback returns true, the tracing is aborted.
+ Note that some paths can go out of the world and come back again (parabola),
+ in such a case this callback is followed by OnIntoWorld() and further OnNextBlock() calls
+ */
+ virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) { return false; }
+
+ /** Called when the path goes into the world, from either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height)
+ The coords specify the exact point at which the path entered the world.
+ If this callback returns true, the tracing is aborted.
+ Note that some paths can go out of the world and come back again (parabola),
+ in such a case this callback is followed by further OnNextBlock() calls
+ */
+ virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) { return false; }
+
+ /** Called when the path is sure not to hit any more blocks.
+ Note that for some shapes this might never happen (line with constant Y)
+ */
+ virtual void OnNoMoreHits(void) {}
+
+ /** Called when the block tracing walks into a chunk that is not allocated.
+ This usually means that the tracing is aborted.
+ */
+ virtual void OnNoChunk(void) {}
+ } ;
+
+
+ /// Creates the BlockTracer parent with the specified callbacks
+ cBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) :
+ m_World(&a_World),
+ m_Callbacks(&a_Callbacks)
+ {
+ }
+
+
+ /// Sets new world, returns the old one. Note that both need to be valid
+ cWorld & SetWorld(cWorld & a_World)
+ {
+ cWorld & Old = *m_World;
+ m_World = &a_World;
+ return Old;
+ }
+
+
+ /// Sets new callbacks, returns the old ones. Note that both need to be valid
+ cCallbacks & SetCallbacks(cCallbacks & a_NewCallbacks)
+ {
+ cCallbacks & Old = *m_Callbacks;
+ m_Callbacks = &a_NewCallbacks;
+ return Old;
+ }
+
+protected:
+ /// The world upon which to operate
+ cWorld * m_World;
+
+ /// The callback to use for reporting
+ cCallbacks * m_Callbacks;
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp
new file mode 100644
index 000000000..66eb9130c
--- /dev/null
+++ b/src/Blocks/BlockBed.cpp
@@ -0,0 +1,88 @@
+#include "Globals.h"
+#include "BlockBed.h"
+
+
+
+
+
+void cBlockBedHandler::OnPlacedByPlayer(
+ cWorld * a_World, 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
+)
+{
+ if (a_BlockMeta < 8)
+ {
+ Vector3i Direction = MetaDataToDirection(a_BlockMeta);
+ a_World->SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8);
+ }
+}
+
+
+
+
+
+void cBlockBedHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ );
+ Vector3i Direction = MetaDataToDirection( OldMeta & 0x7 );
+ if (OldMeta & 0x8)
+ {
+ // Was pillow
+ if (a_World->GetBlock(ThisPos - Direction) == E_BLOCK_BED)
+ {
+ a_World->FastSetBlock(ThisPos - Direction, E_BLOCK_AIR, 0);
+ }
+ }
+ else
+ {
+ // Was foot end
+ if (a_World->GetBlock(ThisPos + Direction) == E_BLOCK_BED)
+ {
+ a_World->FastSetBlock(ThisPos + Direction, E_BLOCK_AIR, 0);
+ }
+ }
+}
+
+
+
+
+
+void cBlockBedHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+ if (a_World->GetDimension() != dimOverworld)
+ {
+ Vector3i Coords(a_BlockX, a_BlockY, a_BlockZ);
+ a_World->DoExplosionAt(5, a_BlockX, a_BlockY, a_BlockZ, true, esBed, &Coords);
+ }
+ else
+ {
+ if (a_World->GetTimeOfDay() > 13000)
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if (Meta & 0x8)
+ {
+ // Is pillow
+ a_World->BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ else
+ {
+ // Is foot end
+ Vector3i Direction = MetaDataToDirection( Meta & 0x7 );
+ if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping
+ {
+ a_World->BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z);
+ }
+ }
+ } else {
+ a_Player->SendMessage("You can only sleep at night");
+ }
+ }
+}
+
+
+
+
diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h
new file mode 100644
index 000000000..8a289b22c
--- /dev/null
+++ b/src/Blocks/BlockBed.h
@@ -0,0 +1,67 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cBlockBedHandler :
+ public cBlockHandler
+{
+public:
+ cBlockBedHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void OnPlacedByPlayer(cWorld * a_World, 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 void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void OnUse(cWorld * a_World, 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 IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to zero
+ a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0));
+ }
+
+
+ // Bed specific helper functions
+ static NIBBLETYPE RotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 180 + (180 / 4); // So its not aligned with axis
+ if (a_Rotation > 360) a_Rotation -= 360;
+
+ a_Rotation = (a_Rotation / 360) * 4;
+
+ return ((char)a_Rotation + 2) % 4;
+ }
+
+
+ static Vector3i MetaDataToDirection(NIBBLETYPE a_MetaData)
+ {
+ switch (a_MetaData)
+ {
+ case 0: return Vector3i(0, 0, 1);
+ case 1: return Vector3i(-1, 0, 0);
+ case 2: return Vector3i(0, 0, -1);
+ case 3: return Vector3i(1, 0, 0);
+ }
+ return Vector3i();
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockBrewingStand.h b/src/Blocks/BlockBrewingStand.h
new file mode 100644
index 000000000..57642bcb6
--- /dev/null
+++ b/src/Blocks/BlockBrewingStand.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockBrewingStandHandler :
+ public cBlockHandler
+{
+public:
+ cBlockBrewingStandHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_BREWING_STAND, 1, 0));
+ }
+
+ virtual bool IsUseable() override
+ {
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockButton.cpp b/src/Blocks/BlockButton.cpp
new file mode 100644
index 000000000..19b055b62
--- /dev/null
+++ b/src/Blocks/BlockButton.cpp
@@ -0,0 +1,32 @@
+
+#include "Globals.h"
+#include "BlockButton.h"
+
+
+
+
+
+cBlockButtonHandler::cBlockButtonHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockButtonHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+ // Flip the ON bit on/off using the XOR bitwise operation
+ NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f);
+
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta);
+ a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f);
+
+ // Queue a button reset (unpress)
+ a_World->QueueSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f), m_BlockType == E_BLOCK_STONE_BUTTON ? 20 : 30);
+}
+
+
+
+
diff --git a/src/Blocks/BlockButton.h b/src/Blocks/BlockButton.h
new file mode 100644
index 000000000..15649acc0
--- /dev/null
+++ b/src/Blocks/BlockButton.h
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockButtonHandler :
+ public cBlockHandler
+{
+public:
+ cBlockButtonHandler(BLOCKTYPE a_BlockType);
+
+ virtual void OnUse(cWorld * a_World, 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 void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(m_BlockType, 1, 0));
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = BlockFaceToMetaData(a_BlockFace);
+ return true;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return m_BlockType == E_BLOCK_WOODEN_BUTTON ? "step.wood" : "step.stone";
+ }
+
+
+ inline static NIBBLETYPE BlockFaceToMetaData(char a_BlockFace)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_ZM: { return 0x4; }
+ case BLOCK_FACE_ZP: { return 0x3; }
+ case BLOCK_FACE_XM: { return 0x2; }
+ case BLOCK_FACE_XP: { return 0x1; }
+ default:
+ {
+ ASSERT(!"Unhandled block face!");
+ return 0x0; // No idea, give a special meta (button in centre of block)
+ }
+ }
+ }
+
+ inline static NIBBLETYPE BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
+ {
+ switch (a_Meta & 0x7)
+ {
+ case 0x1: return BLOCK_FACE_XP;
+ case 0x2: return BLOCK_FACE_XM;
+ case 0x3: return BLOCK_FACE_ZP;
+ case 0x4: return BLOCK_FACE_ZM;
+ default:
+ {
+ ASSERT(!"Unhandled block meta!");
+ return BLOCK_FACE_NONE;
+ }
+ }
+ }
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
+
+ AddFaceDirection(a_RelX, a_RelY, a_RelZ, BlockMetaDataToBlockFace(Meta), true);
+ return (a_RelY > 0) && (g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY, a_RelZ)]);
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockCactus.h b/src/Blocks/BlockCactus.h
new file mode 100644
index 000000000..4147ad473
--- /dev/null
+++ b/src/Blocks/BlockCactus.h
@@ -0,0 +1,82 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockCactusHandler :
+ public cBlockHandler
+{
+public:
+ cBlockCactusHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(m_BlockType, 1, 0));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ if (a_RelY <= 0)
+ {
+ return false;
+ }
+ BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ);
+ if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS))
+ {
+ // Cactus can only be placed on sand and itself
+ return false;
+ }
+
+ // Check surroundings. Cacti may ONLY be surrounded by air
+ static const struct
+ {
+ int x, z;
+ } Coords[] =
+ {
+ {-1, 0},
+ { 1, 0},
+ { 0, -1},
+ { 0, 1},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (
+ a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) &&
+ (BlockType != E_BLOCK_AIR)
+ )
+ {
+ return false;
+ }
+ } // for i - Coords[]
+
+ return true;
+ }
+
+
+ void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ a_World->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1);
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.cloth";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockCarpet.h b/src/Blocks/BlockCarpet.h
new file mode 100644
index 000000000..5eafd8c21
--- /dev/null
+++ b/src/Blocks/BlockCarpet.h
@@ -0,0 +1,60 @@
+
+// BlockCarpet.h
+
+// Declares the cBlockCarpetHandler class representing the handler for the carpet block
+
+
+
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockCarpetHandler :
+ public cBlockHandler
+{
+public:
+ cBlockCarpetHandler(BLOCKTYPE a_BlockType) :
+ cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.cloth";
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = a_Player->GetEquippedItem().m_ItemDamage & 0x0f;
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_CARPET, 1, a_BlockMeta));
+ }
+
+
+ virtual bool CanBeAt(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);
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockCauldron.h b/src/Blocks/BlockCauldron.h
new file mode 100644
index 000000000..b0e00f869
--- /dev/null
+++ b/src/Blocks/BlockCauldron.h
@@ -0,0 +1,59 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockCauldronHandler :
+ public cBlockHandler
+{
+public:
+ cBlockCauldronHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_CAULDRON, 1, 0));
+ }
+
+ void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ char Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ );
+ switch( a_Player->GetEquippedItem().m_ItemType )
+ {
+ case E_ITEM_WATER_BUCKET:
+ {
+ a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, 3 );
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ cItem NewItem(E_ITEM_BUCKET, 1);
+ a_Player->GetInventory().AddItem(NewItem);
+ break;
+ }
+ case E_ITEM_GLASS_BOTTLE:
+ {
+ if( Meta > 0 )
+ {
+ a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta);
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ cItem NewItem(E_ITEM_POTIONS, 1, 0);
+ a_Player->GetInventory().AddItem(NewItem);
+ }
+ break;
+ }
+ }
+ }
+
+ virtual bool IsUseable() override
+ {
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h
new file mode 100644
index 000000000..488c58ac5
--- /dev/null
+++ b/src/Blocks/BlockChest.h
@@ -0,0 +1,223 @@
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../World.h"
+#include "../BlockArea.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cBlockChestHandler :
+ public cBlockEntityHandler
+{
+public:
+ cBlockChestHandler(BLOCKTYPE a_BlockType)
+ : cBlockEntityHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+
+ // Is there a doublechest already next to this block?
+ if (!CanBeAt(a_World, a_BlockX, a_BlockY, a_BlockZ))
+ {
+ // Yup, cannot form a triple-chest, refuse:
+ return false;
+ }
+
+ // Check if this forms a doublechest, if so, need to adjust the meta:
+ cBlockArea Area;
+ if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1))
+ {
+ return false;
+ }
+ double rot = a_Player->GetRotation();
+ if (
+ (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
+ )
+ {
+ a_BlockMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
+ return true;
+ }
+ if (
+ (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
+ )
+ {
+ a_BlockMeta = (rot < 0) ? 4 : 5;
+ return true;
+ }
+
+ // Single chest, get meta from rotation only
+ a_BlockMeta = RotationToMetaData(rot);
+ return true;
+ }
+
+
+ virtual void OnPlacedByPlayer(
+ cWorld * a_World, 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
+ {
+ // Check if this forms a doublechest, if so, need to adjust the meta:
+ cBlockArea Area;
+ if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1))
+ {
+ return;
+ }
+
+ double rot = a_Player->GetRotation();
+ // Choose meta from player rotation, choose only between 2 or 3
+ NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
+ if (
+ CheckAndAdjustNeighbor(a_World, Area, 0, 1, NewMeta) ||
+ CheckAndAdjustNeighbor(a_World, 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_World, Area, 1, 0, NewMeta) ||
+ CheckAndAdjustNeighbor(a_World, Area, 2, 2, NewMeta)
+ )
+ {
+ // Forming a double chest in the Z direction
+ return;
+ }
+
+ // Single chest, no further processing needed
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+
+
+ virtual bool CanBeAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ cBlockArea Area;
+ if (!Area.Read(a_World, a_BlockX - 2, a_BlockX + 2, a_BlockY, a_BlockY, a_BlockZ - 2, a_BlockZ + 2))
+ {
+ // Cannot read the surroundings, probably at the edge of loaded chunks. Disallow.
+ return false;
+ }
+
+ int NumChestNeighbors = 0;
+ if (Area.GetRelBlockType(1, 0, 2) == E_BLOCK_CHEST)
+ {
+ if (
+ (Area.GetRelBlockType(0, 0, 2) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST)
+ )
+ {
+ // Already a doublechest neighbor, disallow:
+ return false;
+ }
+ NumChestNeighbors += 1;
+ }
+ if (Area.GetRelBlockType(3, 0, 2) == E_BLOCK_CHEST)
+ {
+ if (
+ (Area.GetRelBlockType(4, 0, 2) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST)
+ )
+ {
+ // Already a doublechest neighbor, disallow:
+ return false;
+ }
+ NumChestNeighbors += 1;
+ }
+ if (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
+ {
+ if (
+ (Area.GetRelBlockType(2, 0, 0) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST)
+ )
+ {
+ // Already a doublechest neighbor, disallow:
+ return false;
+ }
+ NumChestNeighbors += 1;
+ }
+ if (Area.GetRelBlockType(2, 0, 3) == E_BLOCK_CHEST)
+ {
+ if (
+ (Area.GetRelBlockType(2, 0, 4) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST)
+ )
+ {
+ // Already a doublechest neighbor, disallow:
+ return false;
+ }
+ NumChestNeighbors += 1;
+ }
+ return (NumChestNeighbors < 2);
+ }
+
+
+ /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only
+ static NIBBLETYPE RotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 90 + 45; // So its not aligned with axis
+
+ if (a_Rotation > 360.f)
+ {
+ a_Rotation -= 360.f;
+ }
+ if ((a_Rotation >= 0.f) && (a_Rotation < 90.f))
+ {
+ return 0x4;
+ }
+ else if ((a_Rotation >= 180) && (a_Rotation < 270))
+ {
+ return 0x5;
+ }
+ else if ((a_Rotation >= 90) && (a_Rotation < 180))
+ {
+ return 0x2;
+ }
+ else
+ {
+ return 0x3;
+ }
+ }
+
+
+ /// If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true.
+ bool CheckAndAdjustNeighbor(cWorld * a_World, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta)
+ {
+ if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != E_BLOCK_CHEST)
+ {
+ return false;
+ }
+ a_World->SetBlockMeta(a_Area.GetOriginX() + a_RelX, a_Area.GetOriginY(), a_Area.GetOriginZ() + a_RelZ, a_NewMeta);
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockCloth.h b/src/Blocks/BlockCloth.h
new file mode 100644
index 000000000..a136d3b9d
--- /dev/null
+++ b/src/Blocks/BlockCloth.h
@@ -0,0 +1,34 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockClothHandler :
+ public cBlockHandler
+{
+public:
+ cBlockClothHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_WOOL, 1, a_BlockMeta));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.cloth";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockCobWeb.h b/src/Blocks/BlockCobWeb.h
new file mode 100644
index 000000000..982bfaa30
--- /dev/null
+++ b/src/Blocks/BlockCobWeb.h
@@ -0,0 +1,30 @@
+
+// BlockCobWeb.h
+
+// Declares the cBlockCobWebHandler object representing the BlockHandler for cobwebs
+
+#pragma once
+
+
+
+
+
+class cBlockCobWebHandler :
+ public cBlockHandler
+{
+public:
+ cBlockCobWebHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_STRING, 1, 0));
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockComparator.cpp b/src/Blocks/BlockComparator.cpp
new file mode 100644
index 000000000..8bc0ac5ac
--- /dev/null
+++ b/src/Blocks/BlockComparator.cpp
@@ -0,0 +1,53 @@
+
+#include "Globals.h"
+#include "BlockComparator.h"
+#include "BlockRedstoneRepeater.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+cBlockComparatorHandler::cBlockComparatorHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockComparatorHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+void cBlockComparatorHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ Meta ^= 0x04; // Toggle 3rd (addition/subtraction) bit with XOR
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
+}
+
+
+
+
+bool cBlockComparatorHandler::GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+)
+{
+ a_BlockType = m_BlockType;
+ a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation());
+ return true;
+}
+
+
+
+
diff --git a/src/Blocks/BlockComparator.h b/src/Blocks/BlockComparator.h
new file mode 100644
index 000000000..cb2941d3c
--- /dev/null
+++ b/src/Blocks/BlockComparator.h
@@ -0,0 +1,55 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockComparatorHandler :
+ public cBlockHandler
+{
+public:
+ cBlockComparatorHandler(BLOCKTYPE a_BlockType);
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+
+ virtual void OnUse(cWorld * a_World, 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 void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(E_ITEM_COMPARATOR, 1, 0));
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool CanBeAt(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));
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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 const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockCrops.h b/src/Blocks/BlockCrops.h
new file mode 100644
index 000000000..9dd65aae2
--- /dev/null
+++ b/src/Blocks/BlockCrops.h
@@ -0,0 +1,114 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+/// Common class that takes care of carrots, potatoes and wheat
+class cBlockCropsHandler :
+ public cBlockHandler
+{
+public:
+ cBlockCropsHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override
+ {
+ MTRand rand;
+
+ if (a_Meta == 0x7)
+ {
+ // Is fully grown, drop the entire produce:
+ switch (m_BlockType)
+ {
+ case E_BLOCK_CROPS:
+ {
+ a_Pickups.push_back(cItem(E_ITEM_WHEAT, 1, 0));
+ a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2
+ break;
+ }
+ case E_BLOCK_CARROTS:
+ {
+ a_Pickups.push_back(cItem(E_ITEM_CARROT, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2
+ break;
+ }
+ case E_BLOCK_POTATOES:
+ {
+ a_Pickups.push_back(cItem(E_ITEM_POTATO, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2
+ if (rand.randInt(20) == 0)
+ {
+ // With a 5% chance, drop a poisonous potato as well
+ a_Pickups.push_back(cItem(E_ITEM_POISONOUS_POTATO, 1, 0));
+ }
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled block type");
+ break;
+ }
+ } // switch (m_BlockType)
+ }
+ else
+ {
+ // Drop 1 item of whatever is growing
+ switch (m_BlockType)
+ {
+ case E_BLOCK_CROPS: a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); break;
+ case E_BLOCK_CARROTS: a_Pickups.push_back(cItem(E_ITEM_CARROT, 1, 0)); break;
+ case E_BLOCK_POTATOES: a_Pickups.push_back(cItem(E_ITEM_POTATO, 1, 0)); break;
+ default:
+ {
+ ASSERT(!"Unhandled block type");
+ break;
+ }
+ }
+ }
+ }
+
+
+ void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ NIBBLETYPE Light = a_World->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ);
+ NIBBLETYPE SkyLight = a_World->GetBlockSkyLight(a_BlockX, a_BlockY, a_BlockZ);
+
+ if (SkyLight > Light)
+ {
+ Light = SkyLight;
+ }
+
+ if ((Meta < 7) && (Light > 8))
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CROPS, ++Meta);
+ }
+ else if (Light < 9)
+ {
+ a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+
+
+ virtual bool CanBeAt(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_FARMLAND));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockDeadBush.h b/src/Blocks/BlockDeadBush.h
new file mode 100644
index 000000000..14617d006
--- /dev/null
+++ b/src/Blocks/BlockDeadBush.h
@@ -0,0 +1,35 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockDeadBushHandler :
+ public cBlockHandler
+{
+public:
+ cBlockDeadBushHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Don't drop anything
+ }
+
+
+ virtual bool CanBeAt(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_SAND);
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockDirt.h b/src/Blocks/BlockDirt.h
new file mode 100644
index 000000000..c694d79f6
--- /dev/null
+++ b/src/Blocks/BlockDirt.h
@@ -0,0 +1,88 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+/// Handler used for both dirt and grass
+class cBlockDirtHandler :
+ public cBlockHandler
+{
+public:
+ cBlockDirtHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0));
+ }
+
+
+ virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ if (m_BlockType != E_BLOCK_GRASS)
+ {
+ return;
+ }
+
+ // Grass becomes dirt if there is something on top of it:
+ if (a_BlockY < cChunkDef::Height - 1)
+ {
+ BLOCKTYPE Above = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
+ if ((!g_BlockTransparent[Above] && !g_BlockOneHitDig[Above]) || IsBlockWater(Above))
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0);
+ return;
+ }
+ }
+
+ // Grass spreads to adjacent blocks:
+ MTRand rand;
+ for (int i = 0; i < 2; i++) // Pick two blocks to grow to
+ {
+ int OfsX = rand.randInt(2) - 1; // [-1 .. 1]
+ int OfsY = rand.randInt(4) - 3; // [-3 .. 1]
+ int OfsZ = rand.randInt(2) - 1; // [-1 .. 1]
+
+ BLOCKTYPE DestBlock;
+ NIBBLETYPE DestMeta;
+ if ((a_BlockY + OfsY < 0) || (a_BlockY + OfsY >= cChunkDef::Height - 1))
+ {
+ // Y Coord out of range
+ continue;
+ }
+ bool IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, DestBlock, DestMeta);
+ if (!IsValid || (DestBlock != E_BLOCK_DIRT))
+ {
+ continue;
+ }
+
+ BLOCKTYPE AboveDest;
+ NIBBLETYPE AboveMeta;
+ IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, AboveDest, AboveMeta);
+ ASSERT(IsValid); // WTF - how did we get the DestBlock if AboveBlock is not valid?
+ if ((g_BlockOneHitDig[AboveDest] || g_BlockTransparent[AboveDest]) && !IsBlockWater(AboveDest))
+ {
+ a_World->FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, E_BLOCK_GRASS, 0);
+ }
+ } // for i - repeat twice
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.gravel";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp
new file mode 100644
index 000000000..e71ccd368
--- /dev/null
+++ b/src/Blocks/BlockDoor.cpp
@@ -0,0 +1,90 @@
+
+#include "Globals.h"
+#include "BlockDoor.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockDoorHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ if (OldMeta & 8)
+ {
+ // Was upper part of door
+ if (IsDoor(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
+ }
+ }
+ else
+ {
+ // Was lower part
+ if (IsDoor(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)))
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0);
+ }
+ }
+}
+
+
+
+
+
+void cBlockDoorHandler::OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+ if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR)
+ {
+ ChangeDoor(a_World, a_BlockX, a_BlockY, a_BlockZ);
+ }
+}
+
+
+
+
+
+void cBlockDoorHandler::OnPlacedByPlayer(
+ cWorld * a_World, 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
+)
+{
+ NIBBLETYPE a_TopBlockMeta = 8;
+ if (
+ (a_BlockMeta == 0) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType) ||
+ (a_BlockMeta == 1) && (a_World->GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType) ||
+ (a_BlockMeta == 2) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType) ||
+ (a_BlockMeta == 3) && (a_World->GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType)
+ )
+ {
+ a_TopBlockMeta = 9;
+ }
+ a_World->SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta);
+}
+
+
+
+
+
+const char * cBlockDoorHandler::GetStepSound(void)
+{
+ return (m_BlockType == E_BLOCK_WOODEN_DOOR) ? "step.wood" : "step.stone";
+}
+
+
+
+
diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h
new file mode 100644
index 000000000..03a79d47d
--- /dev/null
+++ b/src/Blocks/BlockDoor.h
@@ -0,0 +1,175 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cBlockDoorHandler :
+ public cBlockHandler
+{
+public:
+ cBlockDoorHandler(BLOCKTYPE a_BlockType);
+
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void OnUse(cWorld * a_World, 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 const char * GetStepSound(void) override;
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ // If clicking a bottom face, place the door one block lower:
+ if (a_BlockFace == BLOCK_FACE_BOTTOM)
+ {
+ a_BlockY--;
+ }
+
+ if (
+ !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) ||
+ !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))
+ )
+ {
+ return false;
+ }
+
+ a_BlockType = m_BlockType;
+ a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation());
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem((m_BlockType == E_BLOCK_WOODEN_DOOR) ? E_ITEM_WOODEN_DOOR : E_ITEM_IRON_DOOR, 1, 0));
+ }
+
+
+ virtual void OnPlacedByPlayer(
+ cWorld * a_World, 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 IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool CanBeAt(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));
+ }
+
+
+ bool CanReplaceBlock(BLOCKTYPE a_BlockType)
+ {
+ switch (a_BlockType)
+ {
+ case E_BLOCK_AIR:
+ case E_BLOCK_TALL_GRASS:
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_FIRE:
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /// Converts the player's yaw to placed door's blockmeta
+ inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
+ {
+ ASSERT((a_Yaw >= -180) && (a_Yaw < 180));
+
+ a_Yaw += 90 + 45;
+ if (a_Yaw > 360)
+ {
+ a_Yaw -= 360;
+ }
+ if ((a_Yaw >= 0) && (a_Yaw < 90))
+ {
+ return 0x0;
+ }
+ else if ((a_Yaw >= 180) && (a_Yaw < 270))
+ {
+ return 0x2;
+ }
+ else if ((a_Yaw >= 90) && (a_Yaw < 180))
+ {
+ return 0x1;
+ }
+ else
+ {
+ return 0x3;
+ }
+ }
+
+
+ /// Returns true if the specified blocktype is any kind of door
+ inline static bool IsDoor(BLOCKTYPE a_Block)
+ {
+ return (a_Block == E_BLOCK_WOODEN_DOOR) || (a_Block == E_BLOCK_IRON_DOOR);
+ }
+
+
+ /// Returns the metadata for the opposite door state (open vs closed)
+ static NIBBLETYPE ChangeStateMetaData(NIBBLETYPE a_MetaData)
+ {
+ return a_MetaData ^ 4;
+ }
+
+
+ /// Changes the door at the specified coords from open to close or vice versa
+ static void ChangeDoor(cWorld * a_World, int a_X, int a_Y, int a_Z)
+ {
+ NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_X, a_Y, a_Z);
+
+ a_World->SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData(OldMetaData));
+
+ if (OldMetaData & 8)
+ {
+ // Current block is top of the door
+ BLOCKTYPE BottomBlock = a_World->GetBlock(a_X, a_Y - 1, a_Z);
+ NIBBLETYPE BottomMeta = a_World->GetBlockMeta(a_X, a_Y - 1, a_Z);
+
+ if (IsDoor(BottomBlock) && !(BottomMeta & 8))
+ {
+ a_World->SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData(BottomMeta));
+ }
+ }
+ else
+ {
+ // Current block is bottom of the door
+ BLOCKTYPE TopBlock = a_World->GetBlock(a_X, a_Y + 1, a_Z);
+ NIBBLETYPE TopMeta = a_World->GetBlockMeta(a_X, a_Y + 1, a_Z);
+
+ if (IsDoor(TopBlock) && (TopMeta & 8))
+ {
+ a_World->SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData(TopMeta));
+ }
+ }
+ }
+
+
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockDropSpenser.h b/src/Blocks/BlockDropSpenser.h
new file mode 100644
index 000000000..b7f20825d
--- /dev/null
+++ b/src/Blocks/BlockDropSpenser.h
@@ -0,0 +1,41 @@
+
+// BlockDropSpenser.h
+
+// Declares the cBlockDropSpenserHandler class representing the BlockHandler for Dropper and Dispenser blocks
+
+#pragma once
+
+#include "../Piston.h"
+
+
+
+
+
+class cBlockDropSpenserHandler :
+ public cBlockEntityHandler
+{
+public:
+ cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) :
+ cBlockEntityHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+
+ // FIXME: Do not use cPiston class for dispenser placement!
+ a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch());
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockEnderchest.h b/src/Blocks/BlockEnderchest.h
new file mode 100644
index 000000000..0ce813f1c
--- /dev/null
+++ b/src/Blocks/BlockEnderchest.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockEnderchestHandler :
+ public cBlockHandler
+{
+public:
+ cBlockEnderchestHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ //todo: Drop Ender Chest if using silk touch pickaxe
+ a_Pickups.push_back(cItem(E_BLOCK_OBSIDIAN, 8, 0));
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockEntity.h b/src/Blocks/BlockEntity.h
new file mode 100644
index 000000000..9c6b23665
--- /dev/null
+++ b/src/Blocks/BlockEntity.h
@@ -0,0 +1,31 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockEntityHandler : public cBlockHandler
+{
+public:
+ cBlockEntityHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void OnUse(cWorld * a_World, 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
+ {
+ a_World->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+
+ virtual bool IsUseable() override
+ {
+ return true;
+ }
+};
+
+
+
+
diff --git a/src/Blocks/BlockFarmland.h b/src/Blocks/BlockFarmland.h
new file mode 100644
index 000000000..7bc71f7f3
--- /dev/null
+++ b/src/Blocks/BlockFarmland.h
@@ -0,0 +1,107 @@
+
+// BlockFarmland.h
+
+// Declares the cBlcokFarmlandHandler representing the block handler for farmland
+
+
+
+
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../BlockArea.h"
+
+
+
+
+
+class cBlockFarmlandHandler :
+ public cBlockHandler
+{
+ typedef cBlockHandler super;
+
+public:
+ cBlockFarmlandHandler(void) :
+ super(E_BLOCK_FARMLAND)
+ {
+ }
+
+
+ virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ bool Found = false;
+
+ int Biome = a_World->GetBiomeAt(a_BlockX, a_BlockZ);
+ if (a_World->IsWeatherWet() && (Biome != biDesert) && (Biome != biDesertHills))
+ {
+ // Rain hydrates farmland, too, except in Desert biomes.
+ Found = true;
+ }
+ else
+ {
+ // Search for water in a close proximity:
+ // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles
+ cBlockArea Area;
+ if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4))
+ {
+ // Too close to the world edge, cannot check surroudnings; don't tick at all
+ return;
+ }
+
+ int NumBlocks = Area.GetBlockCount();
+ BLOCKTYPE * BlockTypes = Area.GetBlockTypes();
+ for (int i = 0; i < NumBlocks; i++)
+ {
+ if (
+ (BlockTypes[i] == E_BLOCK_WATER) ||
+ (BlockTypes[i] == E_BLOCK_STATIONARY_WATER)
+ )
+ {
+ Found = true;
+ break;
+ }
+ } // for i - BlockTypes[]
+ }
+
+ NIBBLETYPE BlockMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ if (Found)
+ {
+ // Water was found, hydrate the block until hydration reaches 7:
+ if (BlockMeta < 7)
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ++BlockMeta);
+ }
+ return;
+ }
+
+ // Water wasn't found, de-hydrate block:
+ if (BlockMeta > 0)
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, --BlockMeta);
+ return;
+ }
+
+ // Farmland too dry. If nothing is growing on top, turn back to dirt:
+ switch (a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))
+ {
+ case E_BLOCK_CROPS:
+ case E_BLOCK_MELON_STEM:
+ case E_BLOCK_PUMPKIN_STEM:
+ {
+ // Produce on top, don't revert
+ break;
+ }
+ default:
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0);
+ break;
+ }
+ }
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockFenceGate.h b/src/Blocks/BlockFenceGate.h
new file mode 100644
index 000000000..6423a7cb0
--- /dev/null
+++ b/src/Blocks/BlockFenceGate.h
@@ -0,0 +1,88 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockFenceGateHandler :
+ public cBlockHandler
+{
+public:
+ cBlockFenceGateHandler(BLOCKTYPE a_BlockType) :
+ cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation());
+ return true;
+ }
+
+
+ virtual void OnUse(cWorld * a_World, 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
+ {
+ NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ NIBBLETYPE NewMetaData = PlayerYawToMetaData(a_Player->GetRotation());
+ OldMetaData ^= 4; // Toggle the gate
+ if ((OldMetaData & 1) == (NewMetaData & 1))
+ {
+ // Standing in front of the gate - apply new direction
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (OldMetaData & 4) | (NewMetaData & 3));
+ }
+ else
+ {
+ // Standing aside - use last direction
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData);
+ }
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ /// Converts the player's yaw to placed gate's blockmeta
+ inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
+ {
+ ASSERT((a_Yaw >= -180) && (a_Yaw < 180));
+
+ a_Yaw += 360 + 45;
+ if (a_Yaw > 360)
+ {
+ a_Yaw -= 360;
+ }
+ if ((a_Yaw >= 0) && (a_Yaw < 90))
+ {
+ return 0x0;
+ }
+ else if ((a_Yaw >= 180) && (a_Yaw < 270))
+ {
+ return 0x2;
+ }
+ else if ((a_Yaw >= 90) && (a_Yaw < 180))
+ {
+ return 0x1;
+ }
+ else
+ {
+ return 0x3;
+ }
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockFire.h b/src/Blocks/BlockFire.h
new file mode 100644
index 000000000..46b56d7e0
--- /dev/null
+++ b/src/Blocks/BlockFire.h
@@ -0,0 +1,228 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockFireHandler :
+ public cBlockHandler
+{
+public:
+ cBlockFireHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ /// Portal boundary and direction variables
+ int XZP, XZM, Dir; // For wont of a better name...
+
+ virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
+ {
+ /*
+ PORTAL FINDING ALGORITH
+ =======================
+ -Get clicked base block
+ -Trace upwards to find first obsidian block; aborts if anything other than obsidian or air is encountered.
+ Uses this value as a reference (the 'ceiling')
+ -For both directions (if one fails, try the other), BASE (clicked) block:
+ -Go in one direction, only stop if a non obsidian block is encountered (abort) OR a portal border is encountered (FindObsidianCeiling returns -1)
+ -If a border was encountered, go the other direction and repeat above
+ -Write borders to XZP and XZM, write direction portal faces to Dir
+ -Loop through boundary variables, and fill with portal blocks based on Dir with meta from Dir
+ */
+
+ a_BlockY--; // Because we want the block below the fire
+ FindAndSetPortalFrame(a_BlockX, a_BlockY, a_BlockZ, a_World); // Brought to you by Aperture Science
+ }
+
+ virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ);
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // No pickups from this block
+ }
+
+ virtual bool IsClickedThrough(void) override
+ {
+ return true;
+ }
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+
+ /// Traces along YP until it finds an obsidian block, returns Y difference or 0 if no portal, and -1 for border
+ /// Takes the X, Y, and Z of the base block; with an optional MaxY for portal border finding
+ int FindObsidianCeiling(int X, int Y, int Z, cWorld * a_World, int MaxY = 0)
+ {
+ if (a_World->GetBlock(X, Y, Z) != E_BLOCK_OBSIDIAN)
+ {
+ return 0;
+ }
+
+ int newY = Y + 1;
+
+ for (newY; newY < cChunkDef::Height; newY++)
+ {
+ BLOCKTYPE Block = a_World->GetBlock(X, newY, Z);
+ if ((Block == E_BLOCK_AIR) || (Block == E_BLOCK_FIRE))
+ {
+ continue;
+ }
+ else if (Block == E_BLOCK_OBSIDIAN)
+ {
+ // We found an obsidian ceiling
+ // Make sure MaxY has a value and newY ('ceiling' location) is at one above the base block
+ // This is because the frame is a solid obsidian pillar
+ if ((MaxY != 0) && (newY == Y + 1))
+ {
+ return EvaluatePortalBorder(X, newY, Z, MaxY, a_World);
+ }
+ else
+ {
+ // Return ceiling Y, whoever called this function will decide if it's part of a portal or not
+ return newY;
+ }
+ }
+ else { return 0; }
+ }
+
+ return 0;
+ }
+
+ /// Evaluates if coords have a valid border on top, based on MaxY
+ int EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cWorld * a_World)
+ {
+ for (int checkBorder = FoundObsidianY + 1; checkBorder <= MaxY - 1; checkBorder++) // FoundObsidianY + 1: FoundObsidianY has already been checked in FindObsidianCeiling; MaxY - 1: portal doesn't need corners
+ {
+ if (a_World->GetBlock(X, checkBorder, Z) != E_BLOCK_OBSIDIAN)
+ {
+ // Base obsidian, base + 1 obsidian, base + x NOT obsidian -> not complete portal
+ return 0;
+ }
+ }
+ // Everything was obsidian, found a border!
+ return -1; // Return -1 for a frame border
+ }
+
+ /// Finds entire frame in any direction with the coordinates of a base block and fills hole with nether portal (START HERE)
+ void FindAndSetPortalFrame(int X, int Y, int Z, cWorld * a_World)
+ {
+ int MaxY = FindObsidianCeiling(X, Y, Z, a_World); // Get topmost obsidian block as reference for all other checks
+ int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add/subtract one as we've checked the original already the line above
+
+ if (MaxY == 0) // Oh noes! Not a portal coordinate :(
+ {
+ return;
+ }
+
+ if (!FindPortalSliceX(X1, X2, Y, Z, MaxY, a_World))
+ {
+ if (!FindPortalSliceZ(X, Y, Z1, Z2, MaxY, a_World))
+ {
+ return; // No eligible portal construct, abort abort abort!!
+ }
+ }
+
+ for (int Height = Y + 1; Height <= MaxY - 1; Height++) // Loop through boundary to set portal blocks
+ {
+ for (int Width = XZM; Width <= XZP; Width++)
+ {
+ if (Dir == 1)
+ {
+ a_World->SetBlock(Width, Height, Z, E_BLOCK_NETHER_PORTAL, Dir);
+ }
+ else
+ {
+ a_World->SetBlock(X, Height, Width, E_BLOCK_NETHER_PORTAL, Dir);
+ }
+ }
+ }
+
+ return;
+ }
+
+ /// Evaluates if coordinates are a portal going XP/XM; returns true if so, and writes boundaries to variable
+ /// Takes coordinates of base block and Y coord of target obsidian ceiling
+ bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cWorld * a_World)
+ {
+ Dir = 1; // Set assumed direction (will change if portal turns out to be facing the other direction)
+ bool FoundFrameXP = false, FoundFrameXM = false;
+ for (X1; ((a_World->GetBlock(X1, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X1, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X1++) // Check XP for obsidian blocks, exempting corners
+ {
+ int Value = FindObsidianCeiling(X1, Y, Z, a_World, MaxY);
+ int ValueTwo = FindObsidianCeiling(X1, Y + 1, Z, a_World, MaxY); // For corners without obsidian
+ if ((Value == -1) || (ValueTwo == -1)) // FindObsidianCeiling returns -1 upon frame-find
+ {
+ FoundFrameXP = true; // Found a frame border in this direction, proceed in other direction (don't go further)
+ break;
+ }
+ else if ((Value != MaxY) && (ValueTwo != MaxY)) // Make sure that there is a valid portal 'slice'
+ {
+ return false; // Not valid slice, no portal can be formed
+ }
+ } XZP = X1 - 1; // Set boundary of frame interior, note that for some reason, the loop of X and the loop of Z go to different numbers, hence -1 here and -2 there
+ for (X2; ((a_World->GetBlock(X2, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X2, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X2--) // Go the other direction (XM)
+ {
+ int Value = FindObsidianCeiling(X2, Y, Z, a_World, MaxY);
+ int ValueTwo = FindObsidianCeiling(X2, Y + 1, Z, a_World, MaxY);
+ if ((Value == -1) || (ValueTwo == -1))
+ {
+ FoundFrameXM = true;
+ break;
+ }
+ else if ((Value != MaxY) && (ValueTwo != MaxY))
+ {
+ return false;
+ }
+ } XZM = X2 + 1; // Set boundary, see previous
+ return (FoundFrameXP && FoundFrameXM);
+ }
+
+ /// Evaluates if coords are a portal going ZP/ZM; returns true if so, and writes boundaries to variable
+ bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cWorld * a_World)
+ {
+ Dir = 2;
+ bool FoundFrameZP = false, FoundFrameZM = false;
+ for (Z1; ((a_World->GetBlock(X, Y, Z1) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z1) == E_BLOCK_OBSIDIAN)); Z1++)
+ {
+ int Value = FindObsidianCeiling(X, Y, Z1, a_World, MaxY);
+ int ValueTwo = FindObsidianCeiling(X, Y + 1, Z1, a_World, MaxY);
+ if ((Value == -1) || (ValueTwo == -1))
+ {
+ FoundFrameZP = true;
+ continue;
+ }
+ else if ((Value != MaxY) && (ValueTwo != MaxY))
+ {
+ return false;
+ }
+ } XZP = Z1 - 2;
+ for (Z2; ((a_World->GetBlock(X, Y, Z2) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z2) == E_BLOCK_OBSIDIAN)); Z2--)
+ {
+ int Value = FindObsidianCeiling(X, Y, Z2, a_World, MaxY);
+ int ValueTwo = FindObsidianCeiling(X, Y + 1, Z2, a_World, MaxY);
+ if ((Value == -1) || (ValueTwo == -1))
+ {
+ FoundFrameZM = true;
+ continue;
+ }
+ else if ((Value != MaxY) && (ValueTwo != MaxY))
+ {
+ return false;
+ }
+ } XZM = Z2 + 2;
+ return (FoundFrameZP && FoundFrameZM);
+ }
+};
+
+
+
+
diff --git a/src/Blocks/BlockFlower.h b/src/Blocks/BlockFlower.h
new file mode 100644
index 000000000..421e2d5d8
--- /dev/null
+++ b/src/Blocks/BlockFlower.h
@@ -0,0 +1,41 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockFlowerHandler :
+ public cBlockHandler
+{
+public:
+ cBlockFlowerHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(m_BlockType, 1, 0));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockFlowerPot.h b/src/Blocks/BlockFlowerPot.h
new file mode 100644
index 000000000..b0faf5218
--- /dev/null
+++ b/src/Blocks/BlockFlowerPot.h
@@ -0,0 +1,105 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockFlowerPotHandler :
+ public cBlockHandler
+{
+public:
+ cBlockFlowerPotHandler(BLOCKTYPE a_BlockType) :
+ cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_FLOWER_POT, 1, 0));
+ if (a_BlockMeta == 0)
+ {
+ return;
+ }
+ cItem Plant;
+ switch (a_BlockMeta)
+ {
+ case 1: Plant = cItem(E_BLOCK_RED_ROSE, 1, 0); break;
+ case 2: Plant = cItem(E_BLOCK_YELLOW_FLOWER, 1, 0); break;
+ case 3: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_APPLE); break;
+ case 4: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_CONIFER); break;
+ case 5: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_BIRCH); break;
+ case 6: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_JUNGLE); break;
+ case 7: Plant = cItem(E_BLOCK_RED_MUSHROOM, 1, 0); break;
+ case 8: Plant = cItem(E_BLOCK_BROWN_MUSHROOM, 1, 0); break;
+ case 9: Plant = cItem(E_BLOCK_CACTUS, 1, 0); break;
+ case 10: Plant = cItem(E_BLOCK_DEAD_BUSH, 1, 0); break;
+ case 11: Plant = cItem(E_BLOCK_TALL_GRASS, 1, E_META_TALL_GRASS_FERN); break;
+ default: return;
+ }
+ a_Pickups.push_back(Plant);
+ }
+
+
+ void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ );
+ if (Meta != 0)
+ {
+ // Already filled
+ return;
+ }
+
+ switch (a_Player->GetEquippedItem().m_ItemType)
+ {
+ case E_BLOCK_RED_ROSE: Meta = 1; break;
+ case E_BLOCK_YELLOW_FLOWER: Meta = 2; break;
+ case E_BLOCK_SAPLING:
+ {
+ switch (a_Player->GetEquippedItem().m_ItemDamage)
+ {
+ case E_META_SAPLING_APPLE: Meta = 3; break;
+ case E_META_SAPLING_CONIFER: Meta = 4; break;
+ case E_META_SAPLING_BIRCH: Meta = 5; break;
+ case E_META_SAPLING_JUNGLE: Meta = 6; break;
+ }
+ break;
+ }
+ case E_BLOCK_RED_MUSHROOM: Meta = 7; break;
+ case E_BLOCK_BROWN_MUSHROOM: Meta = 8; break;
+ case E_BLOCK_CACTUS: Meta = 9; break;
+ case E_BLOCK_DEAD_BUSH: Meta = 10; break;
+ case E_BLOCK_TALL_GRASS:
+ {
+ if (a_Player->GetEquippedItem().m_ItemDamage == E_META_TALL_GRASS_FERN)
+ {
+ Meta = 11;
+ }
+ else
+ {
+ return;
+ }
+ break;
+ }
+ }
+
+ if (a_Player->GetGameMode() != gmCreative)
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ }
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockFluid.h b/src/Blocks/BlockFluid.h
new file mode 100644
index 000000000..0db2f60c4
--- /dev/null
+++ b/src/Blocks/BlockFluid.h
@@ -0,0 +1,56 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockFluidHandler :
+ public cBlockHandler
+{
+ typedef cBlockHandler super;
+
+public:
+ cBlockFluidHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // No pickups
+ }
+
+
+ virtual bool DoesIgnoreBuildCollision(void) override
+ {
+ return true;
+ }
+
+
+ virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override
+ {
+ switch (m_BlockType)
+ {
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_LAVA, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
+ break;
+ }
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_WATER, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
+ break;
+ }
+ }
+ super::Check(a_RelX, a_RelY, a_RelZ, a_Chunk);
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockFurnace.h b/src/Blocks/BlockFurnace.h
new file mode 100644
index 000000000..fe35893d5
--- /dev/null
+++ b/src/Blocks/BlockFurnace.h
@@ -0,0 +1,47 @@
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../World.h"
+#include "../Piston.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cBlockFurnaceHandler :
+ public cBlockEntityHandler
+{
+public:
+ cBlockFurnaceHandler(BLOCKTYPE a_BlockType) :
+ cBlockEntityHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_FURNACE, 1, 0));
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+
+ // FIXME: Do not use cPiston class for furnace placement!
+ a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0);
+
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockGlass.h b/src/Blocks/BlockGlass.h
new file mode 100644
index 000000000..f6958bbb6
--- /dev/null
+++ b/src/Blocks/BlockGlass.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockGlassHandler :
+ public cBlockHandler
+{
+public:
+ cBlockGlassHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockGlowstone.h b/src/Blocks/BlockGlowstone.h
new file mode 100644
index 000000000..5f0d95dee
--- /dev/null
+++ b/src/Blocks/BlockGlowstone.h
@@ -0,0 +1,30 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockGlowstoneHandler :
+ public cBlockHandler
+{
+public:
+ cBlockGlowstoneHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ // TODO: More drops?
+ a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, 1, 0));
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockGravel.h b/src/Blocks/BlockGravel.h
new file mode 100644
index 000000000..e1c9ff390
--- /dev/null
+++ b/src/Blocks/BlockGravel.h
@@ -0,0 +1,27 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockGravelHandler :
+ public cBlockHandler
+{
+public:
+ cBlockGravelHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.gravel";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
new file mode 100644
index 000000000..cd07b3021
--- /dev/null
+++ b/src/Blocks/BlockHandler.cpp
@@ -0,0 +1,465 @@
+
+#include "Globals.h"
+#include "BlockHandler.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Root.h"
+#include "../PluginManager.h"
+#include "BlockBed.h"
+#include "BlockBrewingStand.h"
+#include "BlockButton.h"
+#include "BlockCactus.h"
+#include "BlockCarpet.h"
+#include "BlockCauldron.h"
+#include "BlockChest.h"
+#include "BlockCloth.h"
+#include "BlockCobWeb.h"
+#include "BlockComparator.h"
+#include "BlockCrops.h"
+#include "BlockDeadBush.h"
+#include "BlockDirt.h"
+#include "BlockDoor.h"
+#include "BlockDropSpenser.h"
+#include "BlockEnderchest.h"
+#include "BlockEntity.h"
+#include "BlockFarmland.h"
+#include "BlockFenceGate.h"
+#include "BlockFire.h"
+#include "BlockFlower.h"
+#include "BlockFlowerPot.h"
+#include "BlockFluid.h"
+#include "BlockFurnace.h"
+#include "BlockGlass.h"
+#include "BlockGlowstone.h"
+#include "BlockGravel.h"
+#include "BlockHopper.h"
+#include "BlockIce.h"
+#include "BlockLadder.h"
+#include "BlockLeaves.h"
+#include "BlockLever.h"
+#include "BlockMelon.h"
+#include "BlockMushroom.h"
+#include "BlockMycelium.h"
+#include "BlockNote.h"
+#include "BlockOre.h"
+#include "BlockPiston.h"
+#include "BlockPlanks.h"
+#include "BlockPortal.h"
+#include "BlockPumpkin.h"
+#include "BlockRail.h"
+#include "BlockRedstone.h"
+#include "BlockRedstoneRepeater.h"
+#include "BlockRedstoneTorch.h"
+#include "BlockSand.h"
+#include "BlockSapling.h"
+#include "BlockSign.h"
+#include "BlockSlab.h"
+#include "BlockSnow.h"
+#include "BlockStairs.h"
+#include "BlockStems.h"
+#include "BlockStone.h"
+#include "BlockSugarcane.h"
+#include "BlockTallGrass.h"
+#include "BlockTorch.h"
+#include "BlockVine.h"
+#include "BlockWood.h"
+#include "BlockWorkbench.h"
+
+
+
+
+
+bool cBlockHandler::m_HandlerInitialized = false;
+cBlockHandler * cBlockHandler::m_BlockHandler[256];
+
+
+
+
+
+cBlockHandler * cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockType)
+{
+ if (!m_HandlerInitialized)
+ {
+ // We have to initialize
+ memset(m_BlockHandler, 0, sizeof(m_BlockHandler));
+ m_HandlerInitialized = true;
+ }
+ if (m_BlockHandler[a_BlockType] != NULL)
+ {
+ return m_BlockHandler[a_BlockType];
+ }
+
+ return m_BlockHandler[a_BlockType] = CreateBlockHandler(a_BlockType);
+}
+
+
+
+
+
+cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
+{
+ switch(a_BlockType)
+ {
+ // Block handlers, alphabetically sorted:
+ case E_BLOCK_ACTIVATOR_RAIL: return new cBlockRailHandler (a_BlockType);
+ case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType);
+ case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_BREWING_STAND: return new cBlockBrewingStandHandler (a_BlockType);
+ case E_BLOCK_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_BROWN_MUSHROOM: return new cBlockMushroomHandler (a_BlockType);
+ case E_BLOCK_CACTUS: return new cBlockCactusHandler (a_BlockType);
+ case E_BLOCK_CARROTS: return new cBlockCropsHandler (a_BlockType);
+ case E_BLOCK_CARPET: return new cBlockCarpetHandler (a_BlockType);
+ case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType);
+ case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType);
+ case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_ACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType);
+ case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType);
+ case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType);
+ case E_BLOCK_CROPS: return new cBlockCropsHandler (a_BlockType);
+ case E_BLOCK_DEAD_BUSH: return new cBlockDeadBushHandler (a_BlockType);
+ case E_BLOCK_DETECTOR_RAIL: return new cBlockRailHandler (a_BlockType);
+ case E_BLOCK_DIAMOND_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_DIRT: return new cBlockDirtHandler (a_BlockType);
+ case E_BLOCK_DISPENSER: return new cBlockDropSpenserHandler (a_BlockType);
+ case E_BLOCK_DOUBLE_STONE_SLAB: return new cBlockDoubleSlabHandler (a_BlockType);
+ case E_BLOCK_DOUBLE_WOODEN_SLAB: return new cBlockDoubleSlabHandler (a_BlockType);
+ case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType);
+ case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType);
+ case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler ( );
+ case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType);
+ case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType);
+ case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType);
+ case E_BLOCK_FURNACE: return new cBlockFurnaceHandler (a_BlockType);
+ case E_BLOCK_GLOWSTONE: return new cBlockGlowstoneHandler (a_BlockType);
+ case E_BLOCK_GOLD_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_GLASS: return new cBlockGlassHandler (a_BlockType);
+ case E_BLOCK_GRASS: return new cBlockDirtHandler (a_BlockType);
+ case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType);
+ case E_BLOCK_HOPPER: return new cBlockHopperHandler (a_BlockType);
+ case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType);
+ case E_BLOCK_INACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType);
+ case E_BLOCK_IRON_DOOR: return new cBlockDoorHandler (a_BlockType);
+ case E_BLOCK_IRON_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_JUKEBOX: return new cBlockEntityHandler (a_BlockType);
+ case E_BLOCK_JUNGLE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_LADDER: return new cBlockLadderHandler (a_BlockType);
+ case E_BLOCK_LEVER: return new cBlockLeverHandler (a_BlockType);
+ case E_BLOCK_LAPIS_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_LAVA: return new cBlockFluidHandler (a_BlockType);
+ case E_BLOCK_LEAVES: return new cBlockLeavesHandler (a_BlockType);
+ case E_BLOCK_LIT_FURNACE: return new cBlockFurnaceHandler (a_BlockType);
+ case E_BLOCK_LOG: return new cBlockWoodHandler (a_BlockType);
+ case E_BLOCK_MELON: return new cBlockMelonHandler (a_BlockType);
+ case E_BLOCK_MELON_STEM: return new cBlockStemsHandler (a_BlockType);
+ case E_BLOCK_MYCELIUM: return new cBlockMyceliumHandler (a_BlockType);
+ case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType);
+ case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType);
+ case E_BLOCK_PISTON_EXTENSION: return new cBlockPistonHeadHandler ( );
+ case E_BLOCK_PLANKS: return new cBlockPlanksHandler (a_BlockType);
+ case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType);
+ case E_BLOCK_PUMPKIN: return new cBlockPumpkinHandler (a_BlockType);
+ case E_BLOCK_JACK_O_LANTERN: return new cBlockPumpkinHandler (a_BlockType);
+ case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType);
+ case E_BLOCK_QUARTZ_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType);
+ case E_BLOCK_POTATOES: return new cBlockCropsHandler (a_BlockType);
+ case E_BLOCK_POWERED_RAIL: return new cBlockRailHandler (a_BlockType);
+ case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_REDSTONE_REPEATER_OFF: return new cBlockRedstoneRepeaterHandler(a_BlockType);
+ case E_BLOCK_REDSTONE_REPEATER_ON: return new cBlockRedstoneRepeaterHandler(a_BlockType);
+ case E_BLOCK_REDSTONE_TORCH_OFF: return new cBlockRedstoneTorchHandler (a_BlockType);
+ case E_BLOCK_REDSTONE_TORCH_ON: return new cBlockRedstoneTorchHandler (a_BlockType);
+ case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType);
+ case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType);
+ case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType);
+ case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType);
+ case E_BLOCK_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_SAPLING: return new cBlockSaplingHandler (a_BlockType);
+ case E_BLOCK_SIGN_POST: return new cBlockSignHandler (a_BlockType);
+ case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType);
+ case E_BLOCK_SPRUCE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_STATIONARY_LAVA: return new cBlockFluidHandler (a_BlockType);
+ case E_BLOCK_STATIONARY_WATER: return new cBlockFluidHandler (a_BlockType);
+ case E_BLOCK_STICKY_PISTON: return new cBlockPistonHandler (a_BlockType);
+ case E_BLOCK_STONE: return new cBlockStoneHandler (a_BlockType);
+ case E_BLOCK_STONE_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_STONE_BUTTON: return new cBlockButtonHandler (a_BlockType);
+ case E_BLOCK_STONE_SLAB: return new cBlockSlabHandler (a_BlockType);
+ case E_BLOCK_SUGARCANE: return new cBlockSugarcaneHandler (a_BlockType);
+ case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType);
+ case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType);
+ case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType);
+ case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType);
+ case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType);
+ case E_BLOCK_WOODEN_BUTTON: return new cBlockButtonHandler (a_BlockType);
+ case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType);
+ case E_BLOCK_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType);
+ case E_BLOCK_WOODEN_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_WOOL: return new cBlockClothHandler (a_BlockType);
+ case E_BLOCK_WORKBENCH: return new cBlockWorkbenchHandler (a_BlockType);
+ case E_BLOCK_YELLOW_FLOWER: return new cBlockFlowerHandler (a_BlockType);
+
+ default: return new cBlockHandler(a_BlockType);
+ }
+}
+
+
+
+
+
+void cBlockHandler::Deinit()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ delete m_BlockHandler[i];
+ }
+ memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); // Don't leave any dangling pointers around, just in case
+ m_HandlerInitialized = false;
+}
+
+
+
+
+
+cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType)
+{
+ m_BlockType = a_BlockType;
+}
+
+
+
+
+
+bool cBlockHandler::GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+)
+{
+ // By default, all blocks can be placed and the meta is copied over from the item's damage value:
+ a_BlockType = m_BlockType;
+ a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f);
+ return true;
+}
+
+
+
+
+
+void cBlockHandler::OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+}
+
+
+
+
+
+void cBlockHandler::OnPlacedByPlayer(cWorld * a_World, 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)
+{
+}
+
+
+
+
+
+void cBlockHandler::OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+}
+
+
+
+
+
+void cBlockHandler::OnPlaced(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ // Notify the neighbors
+ NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1);
+ NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1);
+}
+
+
+
+
+
+void cBlockHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Notify the neighbors
+ NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1);
+ NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1);
+}
+
+
+
+
+
+void cBlockHandler::NeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ if ((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height))
+ {
+ GetBlockHandler(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnNeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ);
+ }
+}
+
+
+
+
+
+void cBlockHandler::OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+}
+
+
+
+
+
+void cBlockHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+}
+
+
+
+
+
+void cBlockHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+}
+
+
+
+
+
+void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta)
+{
+ // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this.
+ a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta));
+}
+
+
+
+
+
+void cBlockHandler::DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ cItems Pickups;
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ ConvertToPickups(Pickups, Meta);
+
+ // Allow plugins to modify the pickups:
+ cRoot::Get()->GetPluginManager()->CallHookBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups);
+
+ if (!Pickups.empty())
+ {
+ MTRand r1;
+
+ // Mid-block position first
+ double MicroX, MicroY, MicroZ;
+ MicroX = a_BlockX + 0.5;
+ MicroY = a_BlockY + 0.5;
+ MicroZ = a_BlockZ + 0.5;
+
+ // Add random offset second (this causes pickups to spawn inside blocks most times, it's a little buggy)
+ //MicroX += (int)(r1.randInt(16) + r1.randInt(16) - 16);
+ //MicroY += (int)(r1.randInt(16) + r1.randInt(16) - 16);
+ //MicroZ += (int)(r1.randInt(16) + r1.randInt(16) - 16);
+
+ a_World->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ);
+ }
+}
+
+
+
+
+
+const char * cBlockHandler::GetStepSound()
+{
+ return "step.stone";
+}
+
+
+
+
+
+bool cBlockHandler::CanBeAt(int a_BlockX, int a_BlockY, int a_BlockZ, const cChunk & a_Chunk)
+{
+ return true;
+}
+
+
+
+
+
+bool cBlockHandler::IsUseable()
+{
+ return false;
+}
+
+
+
+
+
+bool cBlockHandler::IsClickedThrough(void)
+{
+ return false;
+}
+
+
+
+
+
+bool cBlockHandler::DoesIgnoreBuildCollision(void)
+{
+ return (m_BlockType == E_BLOCK_AIR);
+}
+
+
+
+
+
+bool cBlockHandler::DoesDropOnUnsuitable(void)
+{
+ return true;
+}
+
+
+
+
+
+void cBlockHandler::Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk)
+{
+ if (!CanBeAt(a_RelX, a_RelY, a_RelZ, a_Chunk))
+ {
+ if (DoesDropOnUnsuitable())
+ {
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ);
+ }
+
+ a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
+ }
+ else
+ {
+ // Wake up the simulators for this block:
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk);
+ }
+}
+
+
+
+
diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h
new file mode 100644
index 000000000..81d9f240c
--- /dev/null
+++ b/src/Blocks/BlockHandler.h
@@ -0,0 +1,152 @@
+
+#pragma once
+
+#include "../Defines.h"
+#include "../Item.h"
+#include "../Chunk.h"
+
+
+
+
+
+// fwd:
+class cWorld;
+class cPlayer;
+
+
+
+
+
+class cBlockHandler
+{
+public:
+ cBlockHandler(BLOCKTYPE a_BlockType);
+
+ /// Called when the block gets ticked either by a random tick or by a queued tick
+ virtual void OnUpdate(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /** 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.
+ Called by cItemHandler::GetPlacementBlockTypeMeta() if the item is a block
+ */
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ );
+
+ /// Called by cWorld::SetBlock() after the block has been set
+ virtual void OnPlaced(cWorld * a_World, 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().
+ virtual void OnPlacedByPlayer(
+ cWorld * a_World, 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
+ );
+
+ /// Called before the player has destroyed a block
+ virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Called before a block gets destroyed / replaced with air
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Called when a direct neighbor of this block has been changed (The position is the own position, not the neighbor position)
+ virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Notifies all neighbors of the given block about a change
+ static void NeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Called while the player diggs the block.
+ virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Called if the user right clicks the block and the block is useable
+ virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
+
+ /// Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta);
+
+ /// Handles the dropping of a block based on what ConvertToDrops() returns. This will not destroy the block. a_Digger is the entity causing the drop; it may be NULL
+ virtual void DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Returns step sound name of block
+ virtual const char * GetStepSound(void);
+
+ /// Checks if the block can stay at the specified relative coords in the chunk
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk);
+
+ /** Checks if the block can be placed at this point.
+ Default: CanBeAt(...)
+ NOTE: This call doesn't actually place the block
+ */
+ // 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
+ virtual bool IsUseable(void);
+
+ /** Indicates whether the client will click through this block.
+ For example digging a fire will hit the block below the fire so fire is clicked through
+ */
+ virtual bool IsClickedThrough(void);
+
+ /** Checks if the player can build "inside" this block.
+ For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision
+ */
+ virtual bool DoesIgnoreBuildCollision(void);
+
+ /// Does this block drop 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.
+ */
+ virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk);
+
+ /// Returns the meta for a block after rotating it counter-clockwise from the specified meta. Default: no change
+ virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) { return a_Meta; }
+
+ /// Returns the meta for a block after rotating it clockwise from the specified meta. Default: no change
+ virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) { return a_Meta; }
+
+ /// Returns the meta for a block after mirroring it around the XY plane. Default: no change
+ virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) { return a_Meta; }
+
+ /// Returns the meta for a block after mirroring it around the XZ plane. Default: no change
+ virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) { return a_Meta; }
+
+ /// Returns the meta for a block after mirroring it around the YZ plane. Default: no change
+ virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; }
+
+
+ /// Get the blockhandler for a specific block id
+ static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockType);
+
+ /// Deletes all initialised block handlers
+ static void Deinit();
+
+protected:
+ BLOCKTYPE m_BlockType;
+
+ // Creates a new blockhandler for the given block type. For internal use only, use ::GetBlockHandler() instead.
+ static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockType);
+ static cBlockHandler *m_BlockHandler[256];
+ static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized
+};
+
+
+
+
+
+// Shortcut to get the blockhandler for a specific block
+inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType)
+{
+ return cBlockHandler::GetBlockHandler(a_BlockType);
+}
+
+
+
+
diff --git a/src/Blocks/BlockHopper.h b/src/Blocks/BlockHopper.h
new file mode 100644
index 000000000..3998276d7
--- /dev/null
+++ b/src/Blocks/BlockHopper.h
@@ -0,0 +1,46 @@
+
+// BlockHopper.h
+
+// Declares the cBlockHopperHandler class representing the handler for the Hopper block
+
+
+
+
+
+class cBlockHopperHandler :
+ public cBlockEntityHandler
+{
+public:
+ cBlockHopperHandler(BLOCKTYPE a_BlockType)
+ : cBlockEntityHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+
+ // Convert the blockface into meta:
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
+ case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
+ case BLOCK_FACE_EAST: a_BlockMeta = E_META_HOPPER_FACING_XM; break;
+ case BLOCK_FACE_NORTH: a_BlockMeta = E_META_HOPPER_FACING_ZP; break;
+ case BLOCK_FACE_SOUTH: a_BlockMeta = E_META_HOPPER_FACING_ZM; break;
+ case BLOCK_FACE_WEST: a_BlockMeta = E_META_HOPPER_FACING_XP; break;
+ default: a_BlockMeta = E_META_HOPPER_UNATTACHED; break;
+ }
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockIce.h b/src/Blocks/BlockIce.h
new file mode 100644
index 000000000..af4961114
--- /dev/null
+++ b/src/Blocks/BlockIce.h
@@ -0,0 +1,37 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockIceHandler :
+ public cBlockHandler
+{
+public:
+ cBlockIceHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // No pickups
+ }
+
+
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ // TODO: Ice destroyed with air below it should turn into air instead of water
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
+ // This is called later than the real destroying of this ice block
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockLadder.h b/src/Blocks/BlockLadder.h
new file mode 100644
index 000000000..c0aa25f60
--- /dev/null
+++ b/src/Blocks/BlockLadder.h
@@ -0,0 +1,115 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockLadderHandler :
+ public cBlockHandler
+{
+public:
+ cBlockLadderHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ if (!LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace))
+ {
+ a_BlockFace = FindSuitableBlockFace(a_World, a_BlockX, a_BlockY, a_BlockZ);
+
+ if (a_BlockFace == BLOCK_FACE_BOTTOM)
+ {
+ return false;
+ }
+ }
+
+ a_BlockType = m_BlockType;
+ a_BlockMeta = DirectionToMetaData(a_BlockFace);
+ return true;
+ }
+
+
+ static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export
+ { // tolua_export
+ switch (a_Direction)
+ {
+ case 0x2: return 0x2;
+ case 0x3: return 0x3;
+ case 0x4: return 0x4;
+ case 0x5: return 0x5;
+ default: return 0x2;
+ }
+ } // tolua_export
+
+
+ static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export
+ { // tolua_export
+ switch (a_MetaData)
+ {
+ case 0x2: return 0x2;
+ case 0x3: return 0x3;
+ case 0x4: return 0x4;
+ case 0x5: return 0x5;
+ default: return 0x2;
+ }
+ } // tolua_export
+
+
+ /// Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure
+ static char FindSuitableBlockFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ for (int Face = 2; Face <= 5; Face++)
+ {
+ if (LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, Face))
+ {
+ return Face;
+ }
+ }
+ return BLOCK_FACE_BOTTOM;
+ }
+
+
+ static bool LadderCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+ {
+ if ((a_BlockFace == BLOCK_FACE_BOTTOM) || (a_BlockFace == BLOCK_FACE_TOP))
+ {
+ return false;
+ }
+
+ AddFaceDirection( a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true);
+
+ return g_BlockIsSolid[a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)];
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison
+ char BlockFace = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ return LadderCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, BlockFace);
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h
new file mode 100644
index 000000000..6e015b8fa
--- /dev/null
+++ b/src/Blocks/BlockLeaves.h
@@ -0,0 +1,184 @@
+#pragma once
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+#include "../BlockArea.h"
+
+
+
+
+
+// Leaves can be this many blocks that away (inclusive) from the log not to decay
+#define LEAVES_CHECK_DISTANCE 6
+
+#define PROCESS_NEIGHBOR(x,y,z) \
+ switch (a_Area.GetBlockType(x, y, z)) \
+ { \
+ case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, (BLOCKTYPE)(E_BLOCK_SPONGE + i + 1)); break; \
+ case E_BLOCK_LOG: return true; \
+ }
+
+bool HasNearLog(cBlockArea &a_Area, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+
+
+
+
+class cBlockLeavesHandler :
+ public cBlockHandler
+{
+public:
+ cBlockLeavesHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ MTRand rand;
+
+ // Only the first 2 bits contain the display information, the others are for growing
+ if (rand.randInt(5) == 0)
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3));
+ }
+ if ((a_BlockMeta & 3) == E_META_SAPLING_APPLE)
+ {
+ if (rand.rand(100) == 0)
+ {
+ a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
+ }
+ }
+ }
+
+
+ void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ cBlockHandler::OnDestroyed(a_World, a_BlockX, a_BlockY, a_BlockZ);
+
+ //0.5% chance of dropping an apple
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ //check if Oak (0x1 and 0x2 bit not set)
+ MTRand rand;
+ if(!(Meta & 3) && rand.randInt(200) == 100)
+ {
+ cItems Drops;
+ Drops.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
+ a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+
+
+ virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x7); // Unset 0x8 bit so it gets checked for decay
+ }
+
+
+ virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if ((Meta & 0x04) != 0)
+ {
+ // Player-placed leaves, don't decay
+ return;
+ }
+
+ if ((Meta & 0x8) != 0)
+ {
+ // These leaves have been checked for decay lately and nothing around them changed
+ return;
+ }
+
+ // Get the data around the leaves:
+ cBlockArea Area;
+ if (!Area.Read(
+ a_World,
+ a_BlockX - LEAVES_CHECK_DISTANCE, a_BlockX + LEAVES_CHECK_DISTANCE,
+ a_BlockY - LEAVES_CHECK_DISTANCE, a_BlockY + LEAVES_CHECK_DISTANCE,
+ a_BlockZ - LEAVES_CHECK_DISTANCE, a_BlockZ + LEAVES_CHECK_DISTANCE,
+ cBlockArea::baTypes)
+ )
+ {
+ // Cannot check leaves, a chunk is missing too close
+ return;
+ }
+
+ if (HasNearLog(Area, a_BlockX, a_BlockY, a_BlockZ))
+ {
+ // Wood found, the leaves stay; mark them as checked:
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x8);
+ return;
+ }
+ // Decay the leaves:
+ DropBlock(a_World, NULL, a_BlockX, a_BlockY, a_BlockZ);
+
+ a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ);
+
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
+
+bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Filter the blocks into a {leaves, log, other (air)} set:
+ BLOCKTYPE * Types = a_Area.GetBlockTypes();
+ for (int i = a_Area.GetBlockCount() - 1; i > 0; i--)
+ {
+ switch (Types[i])
+ {
+ case E_BLOCK_LEAVES:
+ case E_BLOCK_LOG:
+ {
+ break;
+ }
+ default:
+ {
+ Types[i] = E_BLOCK_AIR;
+ break;
+ }
+ }
+ } // for i - Types[]
+
+ // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block:
+ // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log in 4 iterations
+ a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE);
+ for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++)
+ {
+ for (int y = a_BlockY - i; y <= a_BlockY + i; y++)
+ {
+ for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++)
+ {
+ for (int x = a_BlockX - i; x <= a_BlockX + i; x++)
+ {
+ if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i)
+ {
+ continue;
+ }
+ PROCESS_NEIGHBOR(x - 1, y, z);
+ PROCESS_NEIGHBOR(x + 1, y, z);
+ PROCESS_NEIGHBOR(x, y, z - 1);
+ PROCESS_NEIGHBOR(x, y, z + 1);
+ PROCESS_NEIGHBOR(x, y + 1, z);
+ PROCESS_NEIGHBOR(x, y - 1, z);
+ } // for x
+ } // for z
+ } // for y
+ } // for i - BFS iterations
+ return false;
+}
+
+
+
+
diff --git a/src/Blocks/BlockLever.cpp b/src/Blocks/BlockLever.cpp
new file mode 100644
index 000000000..2739fa3a9
--- /dev/null
+++ b/src/Blocks/BlockLever.cpp
@@ -0,0 +1,31 @@
+
+#include "Globals.h"
+#include "BlockLever.h"
+#include "../Entities/Player.h"
+#include "../Simulator/RedstoneSimulator.h"
+
+
+
+
+
+cBlockLeverHandler::cBlockLeverHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockLeverHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+ // Flip the ON bit on/off using the XOR bitwise operation
+ NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f);
+
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta);
+ a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f);
+}
+
+
+
+
diff --git a/src/Blocks/BlockLever.h b/src/Blocks/BlockLever.h
new file mode 100644
index 000000000..fe7ecdf7e
--- /dev/null
+++ b/src/Blocks/BlockLever.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockLeverHandler :
+ public cBlockHandler
+{
+public:
+ cBlockLeverHandler(BLOCKTYPE a_BlockType);
+
+ virtual void OnUse(cWorld * a_World, 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 void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(E_BLOCK_LEVER, 1, 0));
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = LeverDirectionToMetaData(a_BlockFace);
+ return true;
+ }
+
+
+ inline static NIBBLETYPE LeverDirectionToMetaData(char a_Dir)
+ {
+ // Determine lever direction:
+ switch (a_Dir)
+ {
+ case BLOCK_FACE_TOP: return 0x6;
+ case BLOCK_FACE_EAST: return 0x1;
+ case BLOCK_FACE_WEST: return 0x2;
+ case BLOCK_FACE_SOUTH: return 0x3;
+ case BLOCK_FACE_NORTH: return 0x4;
+ case BLOCK_FACE_BOTTOM: return 0x0;
+ default: return 0x6;
+ }
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockMelon.h b/src/Blocks/BlockMelon.h
new file mode 100644
index 000000000..2f7d9a461
--- /dev/null
+++ b/src/Blocks/BlockMelon.h
@@ -0,0 +1,35 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockMelonHandler :
+ public cBlockHandler
+{
+public:
+ cBlockMelonHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ MTRand r1;
+ a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, (char)(3 + r1.randInt(4)), 0));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockMushroom.h b/src/Blocks/BlockMushroom.h
new file mode 100644
index 000000000..2846a6317
--- /dev/null
+++ b/src/Blocks/BlockMushroom.h
@@ -0,0 +1,59 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockMushroomHandler :
+ public cBlockHandler
+{
+public:
+ cBlockMushroomHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(m_BlockType, 1, 0));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ if (a_RelY <= 0)
+ {
+ return false;
+ }
+
+ // TODO: Cannot be at too much daylight
+
+ switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))
+ {
+ case E_BLOCK_GLASS:
+ case E_BLOCK_CACTUS:
+ case E_BLOCK_ICE:
+ case E_BLOCK_LEAVES:
+ case E_BLOCK_AIR:
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockMycelium.h b/src/Blocks/BlockMycelium.h
new file mode 100644
index 000000000..7f897c72a
--- /dev/null
+++ b/src/Blocks/BlockMycelium.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockMyceliumHandler :
+ public cBlockHandler
+{
+public:
+ cBlockMyceliumHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0));
+ }
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.gravel";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockNote.h b/src/Blocks/BlockNote.h
new file mode 100644
index 000000000..fef38d845
--- /dev/null
+++ b/src/Blocks/BlockNote.h
@@ -0,0 +1,13 @@
+#pragma once
+#include "BlockHandler.h"
+#include "BlockEntity.h"
+
+class cBlockNoteHandler : public cBlockEntityHandler
+{
+public:
+ cBlockNoteHandler(BLOCKTYPE a_BlockType)
+ : cBlockEntityHandler(a_BlockType)
+ {
+ }
+
+};
diff --git a/src/Blocks/BlockOre.h b/src/Blocks/BlockOre.h
new file mode 100644
index 000000000..9684dbb19
--- /dev/null
+++ b/src/Blocks/BlockOre.h
@@ -0,0 +1,80 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockOreHandler :
+ public cBlockHandler
+{
+public:
+ cBlockOreHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ short ItemType = m_BlockType;
+ char Count = 1;
+ short Meta = 0;
+
+ MTRand r1;
+ switch (m_BlockType)
+ {
+ case E_BLOCK_LAPIS_ORE:
+ {
+ ItemType = E_ITEM_DYE;
+ Count = 4 + (char)r1.randInt(4);
+ Meta = 4;
+ break;
+ }
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_REDSTONE_ORE_GLOWING:
+ {
+ Count = 4 + (char)r1.randInt(1);
+ break;
+ }
+ default:
+ {
+ Count = 1;
+ break;
+ }
+ }
+
+ switch (m_BlockType)
+ {
+ case E_BLOCK_DIAMOND_ORE:
+ {
+ ItemType = E_ITEM_DIAMOND;
+ break;
+ }
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_REDSTONE_ORE_GLOWING:
+ {
+ ItemType = E_ITEM_REDSTONE_DUST;
+ break;
+ }
+ case E_BLOCK_EMERALD_ORE:
+ {
+ ItemType = E_ITEM_EMERALD;
+ break;
+ }
+ case E_BLOCK_COAL_ORE:
+ {
+ ItemType = E_ITEM_COAL;
+ break;
+ }
+ }
+ a_Pickups.push_back(cItem(ItemType, Count, Meta));
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp
new file mode 100644
index 000000000..d5750ebdd
--- /dev/null
+++ b/src/Blocks/BlockPiston.cpp
@@ -0,0 +1,102 @@
+
+#include "Globals.h"
+#include "BlockPiston.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+#include "../Piston.h"
+
+
+
+
+
+#define AddPistonDir(x, y, z, dir, amount) \
+ switch (dir) \
+ { \
+ case 0: (y) -= (amount); break; \
+ case 1: (y) += (amount); break; \
+ case 2: (z) -= (amount); break; \
+ case 3: (z) += (amount); break; \
+ case 4: (x) -= (amount); break; \
+ case 5: (x) += (amount); break; \
+ }
+
+
+
+
+cBlockPistonHandler::cBlockPistonHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockPistonHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ int newX = a_BlockX;
+ int newY = a_BlockY;
+ int newZ = a_BlockZ;
+ AddPistonDir(newX, newY, newZ, OldMeta & ~(8), 1);
+
+ if (a_World->GetBlock(newX, newY, newZ) == E_BLOCK_PISTON_EXTENSION)
+ {
+ a_World->SetBlock(newX, newY, newZ, E_BLOCK_AIR, 0);
+ }
+}
+
+
+
+
+
+bool cBlockPistonHandler::GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+)
+{
+ a_BlockType = m_BlockType;
+ a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch());
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBlockPistonHeadHandler:
+
+cBlockPistonHeadHandler::cBlockPistonHeadHandler(void) :
+ super(E_BLOCK_PISTON_EXTENSION)
+{
+}
+
+
+
+
+
+void cBlockPistonHeadHandler::OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ int newX = a_BlockX;
+ int newY = a_BlockY;
+ int newZ = a_BlockZ;
+ AddPistonDir(newX, newY, newZ, OldMeta & ~(8), -1);
+
+ BLOCKTYPE Block = a_World->GetBlock(newX, newY, newZ);
+ if ((Block == E_BLOCK_STICKY_PISTON) || (Block == E_BLOCK_PISTON))
+ {
+ a_World->DigBlock(newX, newY, newZ);
+ }
+}
+
+
+
+
+
diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h
new file mode 100644
index 000000000..109f5ea8b
--- /dev/null
+++ b/src/Blocks/BlockPiston.h
@@ -0,0 +1,43 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockPistonHandler :
+ public cBlockHandler
+{
+public:
+ cBlockPistonHandler(BLOCKTYPE a_BlockType);
+
+ virtual void OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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;
+} ;
+
+
+
+
+
+class cBlockPistonHeadHandler :
+ public cBlockHandler
+{
+ typedef cBlockHandler super;
+
+public:
+ cBlockPistonHeadHandler(void);
+
+ virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockPlanks.h b/src/Blocks/BlockPlanks.h
new file mode 100644
index 000000000..f3b8dbfb6
--- /dev/null
+++ b/src/Blocks/BlockPlanks.h
@@ -0,0 +1,41 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockPlanksHandler : public cBlockHandler
+{
+public:
+ cBlockPlanksHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage);
+ a_BlockMeta = Meta;
+ return true;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockPortal.h b/src/Blocks/BlockPortal.h
new file mode 100644
index 000000000..c56f0cbc8
--- /dev/null
+++ b/src/Blocks/BlockPortal.h
@@ -0,0 +1,108 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockPortalHandler :
+ public cBlockHandler
+{
+public:
+ cBlockPortalHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ // We set to zero so MCS doesn't stop you from building weird portals like vanilla does
+ // CanBeAt doesn't do anything if meta is zero
+ // We set to zero because the client sends meta = 2 to the server (it calculates rotation itself)
+
+ a_BlockType = m_BlockType;
+ a_BlockMeta = 0;
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ return; // No pickups
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ if ((a_RelY - 1 < 0) || (a_RelY + 1 > cChunkDef::Height))
+ {
+ return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1
+ }
+
+ switch (a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ))
+ {
+ case 0x1:
+ {
+ static const struct
+ {
+ int x, y, z;
+ } PortalCheck[] =
+ {
+ { 0, 1, 0},
+ { 0,-1, 0},
+ { 1, 0, 0},
+ {-1, 0, 0},
+ } ;
+
+ for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++)
+ {
+ BLOCKTYPE Block;
+ a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block);
+
+ if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
+ {
+ return false;
+ }
+ }
+ break;
+ }
+ case 0x2:
+ {
+ static const struct
+ {
+ int x, y, z;
+ } PortalCheck[] =
+ {
+ { 0, 1, 0},
+ { 0,-1, 0},
+ { 0, 0, -1},
+ { 0, 0, 1},
+ } ;
+
+ for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++)
+ {
+ BLOCKTYPE Block;
+ a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block);
+
+ if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
+ {
+ return false;
+ }
+ }
+ break;
+ }
+ }
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h
new file mode 100644
index 000000000..76abc6818
--- /dev/null
+++ b/src/Blocks/BlockPumpkin.h
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+class cBlockPumpkinHandler :
+ public cBlockHandler
+{
+public:
+ cBlockPumpkinHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation());
+ return true;
+ }
+
+ inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
+ {
+ ASSERT((a_Yaw >= -180) && (a_Yaw < 180));
+
+ a_Yaw += 180 + 45;
+ if (a_Yaw > 360)
+ {
+ a_Yaw -= 360;
+ }
+ if ((a_Yaw >= 0) && (a_Yaw < 90))
+ {
+ return 0x0;
+ }
+ else if ((a_Yaw >= 180) && (a_Yaw < 270))
+ {
+ return 0x2;
+ }
+ else if ((a_Yaw >= 90) && (a_Yaw < 180))
+ {
+ return 0x1;
+ }
+ else
+ {
+ return 0x3;
+ }
+ }
+
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h
new file mode 100644
index 000000000..24a101652
--- /dev/null
+++ b/src/Blocks/BlockRail.h
@@ -0,0 +1,398 @@
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../World.h"
+
+
+
+
+
+enum ENUM_PURE
+{
+ E_PURE_UPDOWN = 0,
+ E_PURE_DOWN = 1,
+ E_PURE_NONE = 2
+};
+
+
+
+
+
+class cBlockRailHandler :
+ public cBlockHandler
+{
+ typedef cBlockHandler super;
+
+public:
+ cBlockRailHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ);
+ return true;
+ }
+
+
+ virtual void OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ) && (Meta != FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ)))
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ));
+ }
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ super::ConvertToPickups(a_Pickups, 0);
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ if (a_RelY <= 0)
+ {
+ return false;
+ }
+ if (!g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)])
+ {
+ return false;
+ }
+
+ NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
+ switch (Meta)
+ {
+ case E_META_RAIL_ASCEND_XP:
+ case E_META_RAIL_ASCEND_XM:
+ case E_META_RAIL_ASCEND_ZM:
+ case E_META_RAIL_ASCEND_ZP:
+ {
+ // Mapping between the meta and the neighbors that need checking
+ Meta -= E_META_RAIL_ASCEND_XP; // Base index at zero
+ static const struct
+ {
+ int x, z;
+ } Coords[] =
+ {
+ { 1, 0}, // east, XP
+ {-1, 0}, // west, XM
+ { 0, -1}, // north, ZM
+ { 0, 1}, // south, ZP
+ } ;
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[Meta].x, a_RelY, a_RelZ + Coords[Meta].z, BlockType, BlockMeta))
+ {
+ // Too close to the edge, cannot simulate
+ return true;
+ }
+ return g_BlockIsSolid[BlockType];
+ }
+ }
+ return true;
+ }
+
+ NIBBLETYPE FindMeta(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ NIBBLETYPE Meta = 0;
+ char RailsCnt = 0;
+ bool Neighbors[8]; // 0 - EAST, 1 - WEST, 2 - NORTH, 3 - SOUTH, 4 - EAST UP, 5 - WEST UP, 6 - NORTH UP, 7 - SOUTH UP
+ memset(Neighbors, false, sizeof(Neighbors));
+ Neighbors[0] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN));
+ Neighbors[1] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN));
+ Neighbors[2] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN));
+ Neighbors[3] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN));
+ Neighbors[4] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST, E_PURE_NONE));
+ Neighbors[5] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST, E_PURE_NONE));
+ Neighbors[6] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE));
+ Neighbors[7] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE));
+ if (IsUnstable(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST))
+ Neighbors[0] = true;
+ if (IsUnstable(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST))
+ Neighbors[1] = true;
+ if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH))
+ Neighbors[2] = true;
+ if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH))
+ Neighbors[3] = true;
+ for (int i = 0; i < 8; i++)
+ {
+ if (Neighbors[i])
+ {
+ RailsCnt++;
+ }
+ }
+ if (RailsCnt == 1)
+ {
+ if (Neighbors[7]) return E_META_RAIL_ASCEND_ZP;
+ else if (Neighbors[6]) return E_META_RAIL_ASCEND_ZM;
+ else if (Neighbors[5]) return E_META_RAIL_ASCEND_XM;
+ else if (Neighbors[4]) return E_META_RAIL_ASCEND_XP;
+ else if (Neighbors[0] || Neighbors[1]) return E_META_RAIL_XM_XP;
+ else if (Neighbors[2] || Neighbors[3]) return E_META_RAIL_ZM_ZP;
+ ASSERT(!"Weird neighbor count");
+ return Meta;
+ }
+ for (int i = 0; i < 4; i++)
+ {
+ if (Neighbors[i + 4])
+ {
+ Neighbors[i] = true;
+ }
+ }
+ if (RailsCnt > 1)
+ {
+ if (Neighbors[3] && Neighbors[0]) return E_META_RAIL_CURVED_ZP_XP;
+ else if (Neighbors[3] && Neighbors[1]) return E_META_RAIL_CURVED_ZP_XM;
+ else if (Neighbors[2] && Neighbors[0]) return E_META_RAIL_CURVED_ZM_XP;
+ else if (Neighbors[2] && Neighbors[1]) return E_META_RAIL_CURVED_ZM_XM;
+ else if (Neighbors[7] && Neighbors[2]) return E_META_RAIL_ASCEND_ZP;
+ else if (Neighbors[3] && Neighbors[6]) return E_META_RAIL_ASCEND_ZM;
+ else if (Neighbors[5] && Neighbors[0]) return E_META_RAIL_ASCEND_XM;
+ else if (Neighbors[4] && Neighbors[1]) return E_META_RAIL_ASCEND_XP;
+ else if (Neighbors[0] && Neighbors[1]) return E_META_RAIL_XM_XP;
+ else if (Neighbors[2] && Neighbors[3]) return E_META_RAIL_ZM_ZP;
+ ASSERT(!"Weird neighbor count");
+ }
+ return Meta;
+ }
+
+
+ bool IsUnstable(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
+ {
+ return false;
+ }
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ switch (Meta)
+ {
+ case E_META_RAIL_ZM_ZP:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_META_RAIL_XM_XP:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_META_RAIL_ASCEND_XP:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_META_RAIL_ASCEND_XM:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_META_RAIL_ASCEND_ZM:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_META_RAIL_ASCEND_ZP:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_META_RAIL_CURVED_ZP_XP:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_META_RAIL_CURVED_ZP_XM:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_META_RAIL_CURVED_ZM_XM:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_META_RAIL_CURVED_ZM_XP:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+
+
+ bool IsNotConnected(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Pure = 0)
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false);
+ NIBBLETYPE Meta;
+ if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
+ {
+ if ((a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN))
+ {
+ if ((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE))
+ {
+ return true;
+ }
+ else
+ {
+ Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ);
+ }
+ }
+ else
+ {
+ Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ);
+ }
+ }
+ else
+ {
+ Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ }
+
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_NORTH:
+ {
+ if (
+ (Meta == E_META_RAIL_ZM_ZP) ||
+ (Meta == E_META_RAIL_ASCEND_ZM) ||
+ (Meta == E_META_RAIL_ASCEND_ZP) ||
+ (Meta == E_META_RAIL_CURVED_ZP_XP) ||
+ (Meta == E_META_RAIL_CURVED_ZP_XM)
+ )
+ {
+ return false;
+ }
+ break;
+ }
+
+ case BLOCK_FACE_SOUTH:
+ {
+ if (
+ (Meta == E_META_RAIL_ZM_ZP) ||
+ (Meta == E_META_RAIL_ASCEND_ZM) ||
+ (Meta == E_META_RAIL_ASCEND_ZP) ||
+ (Meta == E_META_RAIL_CURVED_ZM_XP) ||
+ (Meta == E_META_RAIL_CURVED_ZM_XM)
+ )
+ {
+ return false;
+ }
+ break;
+ }
+
+ case BLOCK_FACE_EAST:
+ {
+ if (
+ (Meta == E_META_RAIL_XM_XP) ||
+ (Meta == E_META_RAIL_ASCEND_XP) ||
+ (Meta == E_META_RAIL_ASCEND_XM) ||
+ (Meta == E_META_RAIL_CURVED_ZP_XM) ||
+ (Meta == E_META_RAIL_CURVED_ZM_XM)
+ )
+ {
+ return false;
+ }
+ break;
+ }
+ case BLOCK_FACE_WEST:
+ {
+ if (
+ (Meta == E_META_RAIL_XM_XP) ||
+ (Meta == E_META_RAIL_ASCEND_XP) ||
+ (Meta == E_META_RAIL_ASCEND_XM) ||
+ (Meta == E_META_RAIL_CURVED_ZP_XP) ||
+ (Meta == E_META_RAIL_CURVED_ZM_XP)
+ )
+ {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockRedstone.cpp b/src/Blocks/BlockRedstone.cpp
new file mode 100644
index 000000000..35cdc34cf
--- /dev/null
+++ b/src/Blocks/BlockRedstone.cpp
@@ -0,0 +1,27 @@
+
+#include "Globals.h"
+#include "BlockRedstone.h"
+#include "../Item.h"
+#include "../World.h"
+
+
+
+
+
+cBlockRedstoneHandler::cBlockRedstoneHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockRedstoneHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Nothing needed yet
+}
+
+
+
+
diff --git a/src/Blocks/BlockRedstone.h b/src/Blocks/BlockRedstone.h
new file mode 100644
index 000000000..f28f3f2d6
--- /dev/null
+++ b/src/Blocks/BlockRedstone.h
@@ -0,0 +1,35 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockRedstoneHandler :
+ public cBlockHandler
+{
+public:
+ cBlockRedstoneHandler(BLOCKTYPE a_BlockType);
+
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return ((a_RelY > 0) && g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]);
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, 1));
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockRedstoneRepeater.cpp b/src/Blocks/BlockRedstoneRepeater.cpp
new file mode 100644
index 000000000..3ab5bc559
--- /dev/null
+++ b/src/Blocks/BlockRedstoneRepeater.cpp
@@ -0,0 +1,50 @@
+
+#include "Globals.h"
+#include "BlockRedstoneRepeater.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+cBlockRedstoneRepeaterHandler::cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockRedstoneRepeaterHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+void cBlockRedstoneRepeaterHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) + 0x04) & 0x0f));
+}
+
+
+
+
+bool cBlockRedstoneRepeaterHandler::GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+)
+{
+ a_BlockType = m_BlockType;
+ a_BlockMeta = RepeaterRotationToMetaData(a_Player->GetRotation());
+ return true;
+}
+
+
+
+
diff --git a/src/Blocks/BlockRedstoneRepeater.h b/src/Blocks/BlockRedstoneRepeater.h
new file mode 100644
index 000000000..a61121d3a
--- /dev/null
+++ b/src/Blocks/BlockRedstoneRepeater.h
@@ -0,0 +1,82 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockRedstoneRepeaterHandler :
+ public cBlockHandler
+{
+public:
+ cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType);
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+
+ virtual void OnUse(cWorld * a_World, 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 void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(E_ITEM_REDSTONE_REPEATER, 1, 0));
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool CanBeAt(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));
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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 const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+
+
+ inline static NIBBLETYPE RepeaterRotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 90 + 45; // So its not aligned with axis
+ if (a_Rotation > 360)
+ {
+ a_Rotation -= 360;
+ }
+
+ if ((a_Rotation >= 0) && (a_Rotation < 90))
+ {
+ return 0x1;
+ }
+ else if ((a_Rotation >= 180) && (a_Rotation < 270))
+ {
+ return 0x3;
+ }
+ else if ((a_Rotation >= 90) && (a_Rotation < 180))
+ {
+ return 0x2;
+ }
+ else
+ {
+ return 0x0;
+ }
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockRedstoneTorch.h b/src/Blocks/BlockRedstoneTorch.h
new file mode 100644
index 000000000..cb897ba3f
--- /dev/null
+++ b/src/Blocks/BlockRedstoneTorch.h
@@ -0,0 +1,36 @@
+
+#pragma once
+
+#include "BlockRedstone.h"
+#include "BlockTorch.h"
+
+
+
+
+
+class cBlockRedstoneTorchHandler :
+ public cBlockTorchHandler
+{
+public:
+ cBlockRedstoneTorchHandler(BLOCKTYPE a_BlockType)
+ : cBlockTorchHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Always drop the ON torch, meta 0
+ a_Pickups.push_back(cItem(E_BLOCK_REDSTONE_TORCH_ON, 1, 0));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockSand.h b/src/Blocks/BlockSand.h
new file mode 100644
index 000000000..3fc271483
--- /dev/null
+++ b/src/Blocks/BlockSand.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockSandHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSandHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.sand";
+ }
+
+};
+
+
+
+
diff --git a/src/Blocks/BlockSapling.h b/src/Blocks/BlockSapling.h
new file mode 100644
index 000000000..fff2fa88b
--- /dev/null
+++ b/src/Blocks/BlockSapling.h
@@ -0,0 +1,57 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockSaplingHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSaplingHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Only the first 2 bits contain the display information, the others are for growing
+ a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ));
+ }
+
+
+ void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ if ((Meta & 0x08) != 0)
+ {
+ a_World->GrowTree(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ else
+ {
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x08);
+ }
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockSign.h b/src/Blocks/BlockSign.h
new file mode 100644
index 000000000..7fbe61893
--- /dev/null
+++ b/src/Blocks/BlockSign.h
@@ -0,0 +1,78 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cBlockSignHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSignHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_SIGN, 1, 0));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+
+
+ static char RotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 180 + (180 / 16); // So it's not aligned with axis
+ if (a_Rotation > 360)
+ {
+ a_Rotation -= 360;
+ }
+
+ a_Rotation = (a_Rotation / 360) * 16;
+
+ return ((char)a_Rotation) % 16;
+ }
+
+
+ static char DirectionToMetaData(char a_Direction)
+ {
+ switch (a_Direction)
+ {
+ case 0x2: return 0x2;
+ case 0x3: return 0x3;
+ case 0x4: return 0x4;
+ case 0x5: return 0x5;
+ default:
+ {
+ break;
+ }
+ }
+ return 0x2;
+ }
+
+
+ virtual void OnPlacedByPlayer(
+ cWorld * a_World, 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
+ {
+ a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockSlab.h b/src/Blocks/BlockSlab.h
new file mode 100644
index 000000000..7c1251b28
--- /dev/null
+++ b/src/Blocks/BlockSlab.h
@@ -0,0 +1,182 @@
+
+// BlockSlab.h
+
+// Declares cBlockSlabHandler and cBlockDoubleSlabHandler classes
+
+
+
+
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../Items/ItemHandler.h"
+
+
+
+
+
+class cBlockSlabHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSlabHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta));
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ BLOCKTYPE Type = (BLOCKTYPE) (a_Player->GetEquippedItem().m_ItemType);
+ NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x07);
+
+ // HandlePlaceBlock wants a cItemHandler pointer thing, so let's give it one
+ cItemHandler * ItemHandler = cItemHandler::GetItemHandler(GetDoubleSlabType(Type));
+
+ // Check if the block at the coordinates is a slab. Eligibility for combining has already been processed in ClientHandle
+ if (IsAnySlabType(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)))
+ {
+ // Call the function in ClientHandle that places a block when the client sends the packet,
+ // so that plugins may interfere with the placement.
+
+ if ((a_BlockFace == BLOCK_FACE_TOP) || (a_BlockFace == BLOCK_FACE_BOTTOM))
+ {
+ // Top and bottom faces need no parameter modification
+ a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
+ }
+ else
+ {
+ // The other faces need to distinguish between top and bottom cursor positions
+ if (a_CursorY > 7)
+ {
+ // Edit the call to use BLOCK_FACE_BOTTOM, otherwise it places incorrectly
+ a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_TOP, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
+ }
+ else
+ {
+ // Edit the call to use BLOCK_FACE_TOP, otherwise it places incorrectly
+ a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_BOTTOM, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
+ }
+ }
+ return false; // Cancel the event, because dblslabs were already placed, nothing else needed
+ }
+
+ // Place the single-slab with correct metas:
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_TOP:
+ {
+ // Bottom half slab block
+ a_BlockMeta = Meta & 0x7;
+ break;
+ }
+ case BLOCK_FACE_BOTTOM:
+ {
+ // Top half slab block
+ a_BlockMeta = Meta | 0x8;
+ break;
+ }
+ case BLOCK_FACE_EAST:
+ case BLOCK_FACE_NORTH:
+ case BLOCK_FACE_SOUTH:
+ case BLOCK_FACE_WEST:
+ {
+ if (a_CursorY > 7)
+ {
+ // Cursor at top half of block, place top slab
+ a_BlockMeta = Meta | 0x8; break;
+ }
+ else
+ {
+ // Cursor at bottom half of block, place bottom slab
+ a_BlockMeta = Meta & 0x7; break;
+ }
+ }
+ } // switch (a_BlockFace)
+ return true;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ switch (m_BlockType)
+ {
+ case E_BLOCK_WOODEN_SLAB: return "step.wood";
+ case E_BLOCK_STONE_SLAB: return "step.stone";
+ }
+ ASSERT(!"Unhandled slab type!");
+ return "";
+ }
+
+
+ /// Returns true if the specified blocktype is one of the slabs handled by this handler
+ static bool IsAnySlabType(BLOCKTYPE a_BlockType)
+ {
+ return ((a_BlockType == E_BLOCK_WOODEN_SLAB) || (a_BlockType == E_BLOCK_STONE_SLAB));
+ }
+
+
+ /// Converts the single-slab blocktype to its equivalent double-slab blocktype
+ static BLOCKTYPE GetDoubleSlabType(BLOCKTYPE a_SingleSlabBlockType)
+ {
+ switch (a_SingleSlabBlockType)
+ {
+ case E_BLOCK_STONE_SLAB: return E_BLOCK_DOUBLE_STONE_SLAB;
+ case E_BLOCK_WOODEN_SLAB: return E_BLOCK_DOUBLE_WOODEN_SLAB;
+ }
+ ASSERT(!"Unhandled slab type!");
+ return E_BLOCK_AIR;
+ }
+
+} ;
+
+
+
+
+
+class cBlockDoubleSlabHandler :
+ public cBlockHandler
+{
+public:
+ cBlockDoubleSlabHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ if (m_BlockType == E_BLOCK_DOUBLE_STONE_SLAB)
+ {
+ m_BlockType = E_BLOCK_STONE_SLAB;
+ }
+ else
+ {
+ m_BlockType = E_BLOCK_WOODEN_SLAB;
+ }
+ a_Pickups.push_back(cItem(m_BlockType, 2, a_BlockMeta));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return ((m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? "step.wood" : "step.stone";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockSnow.h b/src/Blocks/BlockSnow.h
new file mode 100644
index 000000000..b8d48362c
--- /dev/null
+++ b/src/Blocks/BlockSnow.h
@@ -0,0 +1,72 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockSnowHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSnowHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ NIBBLETYPE Meta = a_World->GetBlockMeta(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
+
+ if ((Meta < 7) && (Meta != 0)) // Is height at maximum (7) or at mininum (0)? Don't do anything if so
+ {
+ Meta++;
+ }
+
+ a_BlockMeta = Meta;
+ return true;
+ }
+
+
+ virtual bool DoesIgnoreBuildCollision(void) override
+ {
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_SNOWBALL, 1, 0));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return (a_RelY > 0) && g_BlockIsSnowable[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)];
+ }
+
+
+ virtual bool DoesDropOnUnsuitable(void) override
+ {
+ return false;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.cloth";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockStairs.h b/src/Blocks/BlockStairs.h
new file mode 100644
index 000000000..8d259eee3
--- /dev/null
+++ b/src/Blocks/BlockStairs.h
@@ -0,0 +1,152 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockStairsHandler :
+ public cBlockHandler
+{
+public:
+ cBlockStairsHandler(BLOCKTYPE a_BlockType) :
+ cBlockHandler(a_BlockType)
+ {
+
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = RotationToMetaData(a_Player->GetRotation());
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_TOP: break;
+ case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block
+ case BLOCK_FACE_EAST:
+ case BLOCK_FACE_NORTH:
+ case BLOCK_FACE_SOUTH:
+ case BLOCK_FACE_WEST:
+ {
+ // When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block
+ if (a_CursorY > 8)
+ {
+ a_BlockMeta |= 0x4;
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ // TODO: step sound
+
+
+ static NIBBLETYPE RotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 90 + 45; // So its not aligned with axis
+ if (a_Rotation > 360)
+ {
+ a_Rotation -= 360;
+ }
+ if ((a_Rotation >= 0) && (a_Rotation < 90))
+ {
+ return 0x0;
+ }
+ else if ((a_Rotation >= 180) && (a_Rotation < 270))
+ {
+ return 0x1;
+ }
+ else if ((a_Rotation >= 90) && (a_Rotation < 180))
+ {
+ return 0x2;
+ }
+ else
+ {
+ return 0x3;
+ }
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
+ {
+ // Bits 3 and 4 stay, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x0c);
+ switch (a_Meta & 0x03)
+ {
+ case 0x00: return TopBits | 0x03; // East -> North
+ case 0x01: return TopBits | 0x02; // West -> South
+ case 0x02: return TopBits | 0x00; // South -> East
+ case 0x03: return TopBits | 0x01; // North -> West
+ }
+ // Not reachable, but to avoid a compiler warning:
+ return 0;
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
+ {
+ // Bits 3 and 4 stay, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x0c);
+ switch (a_Meta & 0x03)
+ {
+ case 0x00: return TopBits | 0x02; // East -> South
+ case 0x01: return TopBits | 0x03; // West -> North
+ case 0x02: return TopBits | 0x01; // South -> West
+ case 0x03: return TopBits | 0x00; // North -> East
+ }
+ // Not reachable, but to avoid a compiler warning:
+ return 0;
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
+ {
+ // Bits 3 and 4 stay, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x0c);
+ switch (a_Meta & 0x03)
+ {
+ case 0x00: return TopBits | 0x00; // East -> East
+ case 0x01: return TopBits | 0x01; // West -> West
+ case 0x02: return TopBits | 0x03; // South -> North
+ case 0x03: return TopBits | 0x02; // North -> South
+ }
+ // Not reachable, but to avoid a compiler warning:
+ return 0;
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override
+ {
+ // Toggle bit 3:
+ return (a_Meta & 0x0b) | ((~a_Meta) & 0x04);
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
+ {
+ // Bits 3 and 4 stay, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x0c);
+ switch (a_Meta & 0x03)
+ {
+ case 0x00: return TopBits | 0x01; // East -> West
+ case 0x01: return TopBits | 0x00; // West -> East
+ case 0x02: return TopBits | 0x02; // South -> South
+ case 0x03: return TopBits | 0x03; // North -> North
+ }
+ // Not reachable, but to avoid a compiler warning:
+ return 0;
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockStems.h b/src/Blocks/BlockStems.h
new file mode 100644
index 000000000..ce02d9cb8
--- /dev/null
+++ b/src/Blocks/BlockStems.h
@@ -0,0 +1,58 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockStemsHandler :
+ public cBlockHandler
+{
+public:
+ cBlockStemsHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ int ItemType = (m_BlockType == E_BLOCK_MELON_STEM) ? E_ITEM_MELON_SEEDS : E_ITEM_PUMPKIN_SEEDS;
+ a_Pickups.push_back(cItem(ItemType, 1, 0));
+ }
+
+
+ void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if (Meta >= 7)
+ {
+ // Grow the produce:
+ a_World->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, m_BlockType);
+ }
+ else
+ {
+ // Grow the stem:
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta + 1);
+ }
+ }
+
+
+ virtual bool CanBeAt(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_FARMLAND));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockStone.h b/src/Blocks/BlockStone.h
new file mode 100644
index 000000000..af4c6509a
--- /dev/null
+++ b/src/Blocks/BlockStone.h
@@ -0,0 +1,29 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockStoneHandler :
+ public cBlockHandler
+{
+public:
+ cBlockStoneHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_COBBLESTONE, 1, 0));
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockSugarcane.h b/src/Blocks/BlockSugarcane.h
new file mode 100644
index 000000000..28a60df80
--- /dev/null
+++ b/src/Blocks/BlockSugarcane.h
@@ -0,0 +1,90 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockSugarcaneHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSugarcaneHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_SUGARCANE, 1, 0));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ if (a_RelY <= 0)
+ {
+ return false;
+ }
+ switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))
+ {
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_FARMLAND:
+ case E_BLOCK_SAND:
+ {
+ static const struct
+ {
+ int x, z;
+ } Coords[] =
+ {
+ {-1, 0},
+ { 1, 0},
+ { 0, -1},
+ { 0, 1},
+ } ;
+ a_RelY -= 1;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta))
+ {
+ // Too close to the edge, cannot simulate
+ return true;
+ }
+ if (IsBlockWater(BlockType))
+ {
+ return true;
+ }
+ } // for i - Coords[]
+ // Not directly neighboring a water block
+ return false;
+ }
+ case E_BLOCK_SUGARCANE:
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ a_World->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1);
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockTallGrass.h b/src/Blocks/BlockTallGrass.h
new file mode 100644
index 000000000..cd27ab7e6
--- /dev/null
+++ b/src/Blocks/BlockTallGrass.h
@@ -0,0 +1,51 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockTallGrassHandler :
+ public cBlockHandler
+{
+public:
+ cBlockTallGrassHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool DoesIgnoreBuildCollision(void) override
+ {
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Drop seeds, sometimes
+ MTRand r1;
+ if (r1.randInt(10) == 5)
+ {
+ a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0));
+ }
+ }
+
+
+ virtual bool CanBeAt(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));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h
new file mode 100644
index 000000000..72a313126
--- /dev/null
+++ b/src/Blocks/BlockTorch.h
@@ -0,0 +1,275 @@
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockTorchHandler :
+ public cBlockHandler
+{
+public:
+ cBlockTorchHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ // Find proper placement of torch
+
+ if ((a_BlockFace == BLOCK_FACE_TOP) || (a_BlockFace == BLOCK_FACE_BOTTOM))
+ {
+ a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); // Top or bottom faces clicked, find a suitable face
+ if (a_BlockFace == BLOCK_FACE_NONE)
+ {
+ // Client wouldn't have sent anything anyway, but whatever
+ return false;
+ }
+ }
+ else
+ {
+ // Not top or bottom faces, try to preserve whatever face was clicked
+ if (!TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace))
+ {
+ // Torch couldn't be placed on whatever face was clicked, last ditch resort - find another face
+ a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ);
+ if (a_BlockFace == BLOCK_FACE_NONE)
+ {
+ return false;
+ }
+ }
+ }
+
+ a_BlockType = m_BlockType;
+ a_BlockMeta = DirectionToMetaData(a_BlockFace);
+ return true;
+ }
+
+
+ static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export
+ { // tolua_export
+ switch (a_Direction)
+ {
+ case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0;
+ case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR;
+ case BLOCK_FACE_EAST: return E_META_TORCH_EAST;
+ case BLOCK_FACE_WEST: return E_META_TORCH_WEST;
+ case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH;
+ case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH;
+ default:
+ {
+ ASSERT(!"Unhandled torch direction!");
+ break;
+ }
+ };
+ return 0x0;
+ } // tolua_export
+
+
+ static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export
+ { // tolua_export
+ switch (a_MetaData)
+ {
+ case 0: return BLOCK_FACE_TOP; // by default, the torches stand on the ground
+ case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP;
+ case E_META_TORCH_EAST: return BLOCK_FACE_EAST;
+ case E_META_TORCH_WEST: return BLOCK_FACE_WEST;
+ case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH;
+ case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH;
+ default:
+ {
+ ASSERT(!"Unhandled torch metadata");
+ break;
+ }
+ }
+ return 0;
+ } // tolua_export
+
+
+ static bool IsAttachedTo(const Vector3i & a_TorchPos, char a_TorchMeta, const Vector3i & a_BlockPos)
+ {
+ switch (a_TorchMeta)
+ {
+ case 0x0:
+ case E_META_TORCH_FLOOR: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 1, 0)));
+ case E_META_TORCH_EAST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, -1)));
+ case E_META_TORCH_WEST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, 1)));
+ case E_META_TORCH_NORTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(-1, 0, 0)));
+ case E_META_TORCH_SOUTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(1, 0, 0)));
+ default:
+ {
+ ASSERT(!"Unhandled torch meta!");
+ break;
+ }
+ }
+ return false;
+ }
+
+
+ static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_BlockFace)
+ {
+ if ( !g_BlockIsTorchPlaceable[a_BlockType] )
+ {
+ return (a_BlockFace == BLOCK_FACE_TOP); // Allow placement only when torch upright
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+
+ static bool TorchCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true);
+ return CanBePlacedOn(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), a_BlockFace);
+ }
+
+
+ /// Finds a suitable face to place the torch, returning BLOCK_FACE_NONE on failure
+ static char FindSuitableFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ for (int i = 0; i <= 5; i++)
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, i, true);
+ BLOCKTYPE BlockInQuestion = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+
+ if (
+ ((BlockInQuestion == E_BLOCK_GLASS) ||
+ (BlockInQuestion == E_BLOCK_FENCE) ||
+ (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) ||
+ (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL)) &&
+ (i == BLOCK_FACE_TOP)
+ )
+ {
+ return i;
+ }
+ else if ((g_BlockIsTorchPlaceable[BlockInQuestion]) && (i != BLOCK_FACE_BOTTOM))
+ {
+ return i;
+ }
+ else
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, i, false);
+ }
+ }
+ return BLOCK_FACE_NONE;
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ char Face = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
+
+ AddFaceDirection(a_RelX, a_RelY, a_RelZ, Face, true);
+ BLOCKTYPE BlockInQuestion;
+ a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockInQuestion);
+
+ if (
+ (BlockInQuestion == E_BLOCK_GLASS) ||
+ (BlockInQuestion == E_BLOCK_FENCE) ||
+ (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) ||
+ (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL)
+ )
+ {
+ // Torches can be placed on tops of glass and fences, despite them being 'untorcheable'
+ // No need to check for upright orientation, it was done when the torch was placed
+ return true;
+ }
+ else if ( !g_BlockIsTorchPlaceable[BlockInQuestion] )
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Always drop meta = 0
+ a_Pickups.push_back(cItem(m_BlockType, 1, 0));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
+ {
+ // Bit 4 stays, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x08);
+ switch (a_Meta & 0x07)
+ {
+ case 0x01: return TopBits | 0x04; // East -> North
+ case 0x02: return TopBits | 0x03; // West -> South
+ case 0x03: return TopBits | 0x01; // South -> East
+ case 0x04: return TopBits | 0x02; // North -> West
+ default: return a_Meta; // Floor -> Floor
+ }
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
+ {
+ // Bit 4 stays, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x08);
+ switch (a_Meta & 0x07)
+ {
+ case 0x01: return TopBits | 0x03; // East -> South
+ case 0x02: return TopBits | 0x04; // West -> North
+ case 0x03: return TopBits | 0x02; // South -> West
+ case 0x04: return TopBits | 0x01; // North -> East
+ default: return a_Meta; // Floor -> Floor
+ }
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
+ {
+ // Bit 4 stays, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x08);
+ switch (a_Meta & 0x07)
+ {
+ case 0x03: return TopBits | 0x04; // South -> North
+ case 0x04: return TopBits | 0x03; // North -> South
+ default: return a_Meta; // Keep the rest
+ }
+ }
+
+
+ // Mirroring around the XZ plane doesn't make sense for floor torches,
+ // the others stay the same, so let's keep all the metas the same.
+ // The base class does tht for us, no need to override MetaMirrorXZ()
+
+
+ virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
+ {
+ // Bit 4 stays, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x08);
+ switch (a_Meta & 0x07)
+ {
+ case 0x01: return TopBits | 0x02; // East -> West
+ case 0x02: return TopBits | 0x01; // West -> East
+ default: return a_Meta; // Keep the rest
+ }
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockVine.h b/src/Blocks/BlockVine.h
new file mode 100644
index 000000000..2c9f67cab
--- /dev/null
+++ b/src/Blocks/BlockVine.h
@@ -0,0 +1,201 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockVineHandler :
+ public cBlockHandler
+{
+public:
+ cBlockVineHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ // TODO: Disallow placement where the vine doesn't attach to something properly
+ BLOCKTYPE BlockType = 0;
+ NIBBLETYPE BlockMeta;
+ a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
+ if (BlockType == m_BlockType)
+ {
+ a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace);
+ }
+ else
+ {
+ a_BlockMeta = DirectionToMetaData(a_BlockFace);
+ }
+ a_BlockType = m_BlockType;
+ return true;
+ }
+
+
+ static NIBBLETYPE DirectionToMetaData(char a_BlockFace)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_NORTH: return 0x1;
+ case BLOCK_FACE_SOUTH: return 0x4;
+ case BLOCK_FACE_WEST: return 0x8;
+ case BLOCK_FACE_EAST: return 0x2;
+ default: return 0x0;
+ }
+ }
+
+
+ static char MetaDataToDirection(NIBBLETYPE a_MetaData)
+ {
+ switch(a_MetaData)
+ {
+ case 0x1: return BLOCK_FACE_NORTH;
+ case 0x4: return BLOCK_FACE_SOUTH;
+ case 0x8: return BLOCK_FACE_WEST;
+ case 0x2: return BLOCK_FACE_EAST;
+ default: return BLOCK_FACE_TOP;
+ }
+ }
+
+
+ /// Returns true if the specified block type is good for vines to attach to
+ static bool IsBlockAttachable(BLOCKTYPE a_BlockType)
+ {
+ return (a_BlockType == E_BLOCK_LEAVES) || g_BlockIsSolid[a_BlockType];
+ }
+
+
+ /// Returns the meta that has the maximum allowable sides of the vine, given the surroundings
+ NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+ {
+ static const struct
+ {
+ int x, z;
+ int Bit;
+ } Coords[] =
+ {
+ { 0, 1, 1}, // south, ZP
+ {-1, 0, 2}, // west, XM
+ { 0, -1, 4}, // north, ZM
+ { 1, 0, 8}, // east, XP
+ } ;
+ int res = 0;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (
+ a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) &&
+ IsBlockAttachable(BlockType)
+ )
+ {
+ res |= Coords[i].Bit;
+ }
+ }
+ return res;
+ }
+
+
+ void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override
+ {
+ NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
+ NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelX, a_RelY, a_RelZ);
+
+ // Check if vine above us, add its meta to MaxMeta
+ if ((a_RelY < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == m_BlockType))
+ {
+ MaxMeta |= a_Chunk.GetMeta(a_RelX, a_RelY + 1, a_RelZ);
+ }
+
+ NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal
+ if (Common != CurMeta)
+ {
+ // There is a neighbor missing, need to update the meta or even destroy the block
+ bool HasTop = (a_RelY < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ));
+ if ((Common == 0) && !HasTop)
+ {
+ // The vine just lost all its support, destroy the block:
+ if (DoesDropOnUnsuitable())
+ {
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ);
+ }
+ a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
+ return;
+ }
+ a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, Common);
+ }
+ else
+ {
+ // Wake up the simulators for this block:
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk);
+ }
+ }
+
+
+ virtual bool DoesIgnoreBuildCollision(void) override
+ {
+ return true;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+
+
+ virtual bool DoesDropOnUnsuitable(void) override
+ {
+ return false;
+ }
+
+ virtual void OnUpdate(cWorld * a_World, int X, int Y, int Z)
+ {
+ if (a_World->GetBlock(X, Y - 1, Z) == E_BLOCK_AIR)
+ {
+ a_World->SetBlock(X, Y - 1, Z, E_BLOCK_VINES, a_World->GetBlockMeta(X, Y, Z));
+ }
+ }
+
+ virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
+ {
+ return ((a_Meta >> 1) | (a_Meta << 3)) & 0x0f; // Rotate bits to the right
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
+ {
+ return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
+ {
+ // Bits 2 and 4 stay, bits 1 and 3 swap
+ return ((a_Meta & 0x0a) | ((a_Meta & 0x01) << 2) | ((a_Meta & 0x04) >> 2));
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
+ {
+ // Bits 1 and 3 stay, bits 2 and 4 swap
+ return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2));
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockWood.h b/src/Blocks/BlockWood.h
new file mode 100644
index 000000000..cb5ee995a
--- /dev/null
+++ b/src/Blocks/BlockWood.h
@@ -0,0 +1,72 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockWoodHandler : public cBlockHandler
+{
+public:
+ cBlockWoodHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = m_BlockType;
+ NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage);
+ a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta);
+ return true;
+ }
+
+
+ inline static NIBBLETYPE BlockFaceToMetaData(char a_BlockFace, NIBBLETYPE a_WoodMeta)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_YM:
+ case BLOCK_FACE_YP:
+ {
+ return a_WoodMeta; // Top or bottom, just return original
+ }
+
+ case BLOCK_FACE_ZP:
+ case BLOCK_FACE_ZM:
+ {
+ return a_WoodMeta | 0x8; // North or south
+ }
+
+ case BLOCK_FACE_XP:
+ case BLOCK_FACE_XM:
+ {
+ return a_WoodMeta | 0x4; // East or west
+ }
+
+ default:
+ {
+ ASSERT(!"Unhandled block face!");
+ return a_WoodMeta | 0xC; // No idea, give a special meta (all sides bark)
+ }
+ }
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockWorkbench.h b/src/Blocks/BlockWorkbench.h
new file mode 100644
index 000000000..a2cc6119c
--- /dev/null
+++ b/src/Blocks/BlockWorkbench.h
@@ -0,0 +1,43 @@
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../UI/Window.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cBlockWorkbenchHandler:
+ public cBlockHandler
+{
+public:
+ cBlockWorkbenchHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void OnUse(cWorld * a_World, 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
+ {
+ cWindow * Window = new cCraftingWindow(a_BlockX, a_BlockY, a_BlockZ);
+ a_Player->OpenWindow(Window);
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/src/BoundingBox.cpp b/src/BoundingBox.cpp
new file mode 100644
index 000000000..02602992e
--- /dev/null
+++ b/src/BoundingBox.cpp
@@ -0,0 +1,331 @@
+
+// BoundingBox.cpp
+
+// Implements the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords
+
+#include "Globals.h"
+#include "BoundingBox.h"
+#include "Defines.h"
+
+
+
+
+
+#if 0
+
+/// A simple self-test that is executed on program start, used to verify bbox functionality
+class SelfTest
+{
+public:
+ SelfTest(void)
+ {
+ Vector3d Min(1, 1, 1);
+ Vector3d Max(2, 2, 2);
+ Vector3d LineDefs[] =
+ {
+ Vector3d(1.5, 4, 1.5), Vector3d(1.5, 3, 1.5), // Should intersect at 2, face 1 (YP)
+ Vector3d(1.5, 0, 1.5), Vector3d(1.5, 4, 1.5), // Should intersect at 0.25, face 0 (YM)
+ Vector3d(0, 0, 0), Vector3d(2, 2, 2), // Should intersect at 0.5, face 0, 3 or 5 (anyM)
+ Vector3d(0.999, 0, 1.5), Vector3d(0.999, 4, 1.5), // Should not intersect
+ Vector3d(1.999, 0, 1.5), Vector3d(1.999, 4, 1.5), // Should intersect at 0.25, face 0 (YM)
+ Vector3d(2.001, 0, 1.5), Vector3d(2.001, 4, 1.5), // Should not intersect
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++)
+ {
+ double LineCoeff;
+ char Face;
+ Vector3d Line1 = LineDefs[2 * i];
+ Vector3d Line2 = LineDefs[2 * i + 1];
+ bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face);
+ printf("LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n",
+ Line1.x, Line1.y, Line1.z,
+ Line2.x, Line2.y, Line2.z,
+ res ? 1 : 0, LineCoeff, Face
+ );
+ } // for i - LineDefs[]
+ printf("BoundingBox selftest complete.");
+ }
+} Test;
+
+#endif
+
+
+
+
+
+cBoundingBox::cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ) :
+ m_Min(a_MinX, a_MinY, a_MinZ),
+ m_Max(a_MaxX, a_MaxY, a_MaxZ)
+{
+}
+
+
+
+
+
+cBoundingBox::cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max) :
+ m_Min(a_Min),
+ m_Max(a_Max)
+{
+}
+
+
+
+
+
+cBoundingBox::cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height) :
+ m_Min(a_Pos.x - a_Radius, a_Pos.y, a_Pos.z - a_Radius),
+ m_Max(a_Pos.x + a_Radius, a_Pos.y + a_Height, a_Pos.z + a_Radius)
+{
+}
+
+
+
+
+
+cBoundingBox::cBoundingBox(const cBoundingBox & a_Orig) :
+ m_Min(a_Orig.m_Min),
+ m_Max(a_Orig.m_Max)
+{
+}
+
+
+
+
+
+void cBoundingBox::Move(double a_OffX, double a_OffY, double a_OffZ)
+{
+ m_Min.x += a_OffX;
+ m_Min.y += a_OffY;
+ m_Min.z += a_OffZ;
+ m_Max.x += a_OffX;
+ m_Max.y += a_OffY;
+ m_Max.z += a_OffZ;
+}
+
+
+
+
+
+void cBoundingBox::Move(const Vector3d & a_Off)
+{
+ m_Min.x += a_Off.x;
+ m_Min.y += a_Off.y;
+ m_Min.z += a_Off.z;
+ m_Max.x += a_Off.x;
+ m_Max.y += a_Off.y;
+ m_Max.z += a_Off.z;
+}
+
+
+
+
+
+void cBoundingBox::Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ)
+{
+ m_Min.x -= a_ExpandX;
+ m_Min.y -= a_ExpandY;
+ m_Min.z -= a_ExpandZ;
+ m_Max.x += a_ExpandX;
+ m_Max.y += a_ExpandY;
+ m_Max.z += a_ExpandZ;
+}
+
+
+
+
+
+bool cBoundingBox::DoesIntersect(const cBoundingBox & a_Other)
+{
+ return (
+ ((a_Other.m_Min.x <= m_Max.x) && (a_Other.m_Max.x >= m_Min.x)) && // X coords intersect
+ ((a_Other.m_Min.y <= m_Max.y) && (a_Other.m_Max.y >= m_Min.y)) && // Y coords intersect
+ ((a_Other.m_Min.z <= m_Max.z) && (a_Other.m_Max.z >= m_Min.z)) // Z coords intersect
+ );
+}
+
+
+
+
+
+cBoundingBox cBoundingBox::Union(const cBoundingBox & a_Other)
+{
+ return cBoundingBox(
+ std::min(m_Min.x, a_Other.m_Min.x),
+ std::min(m_Min.y, a_Other.m_Min.y),
+ std::min(m_Min.z, a_Other.m_Min.z),
+ std::max(m_Max.x, a_Other.m_Max.x),
+ std::max(m_Max.y, a_Other.m_Max.y),
+ std::max(m_Max.z, a_Other.m_Max.z)
+ );
+}
+
+
+
+
+
+bool cBoundingBox::IsInside(const Vector3d & a_Point)
+{
+ return IsInside(m_Min, m_Max, a_Point);
+}
+
+
+
+
+
+bool cBoundingBox::IsInside(double a_X, double a_Y,double a_Z)
+{
+ return IsInside(m_Min, m_Max, a_X, a_Y, a_Z);
+}
+
+
+
+
+
+bool cBoundingBox::IsInside(cBoundingBox & a_Other)
+{
+ // If both a_Other's coords are inside this, then the entire a_Other is inside
+ return (IsInside(a_Other.m_Min) && IsInside(a_Other.m_Max));
+}
+
+
+
+
+
+bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max)
+{
+ // If both coords are inside this, then the entire a_Other is inside
+ return (IsInside(a_Min) && IsInside(a_Max));
+}
+
+
+
+
+
+bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point)
+{
+ return (
+ ((a_Point.x >= a_Min.x) && (a_Point.x <= a_Max.x)) &&
+ ((a_Point.y >= a_Min.y) && (a_Point.y <= a_Max.y)) &&
+ ((a_Point.z >= a_Min.z) && (a_Point.z <= a_Max.z))
+ );
+}
+
+
+
+
+
+bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z)
+{
+ return (
+ ((a_X >= a_Min.x) && (a_X <= a_Max.x)) &&
+ ((a_Y >= a_Min.y) && (a_Y <= a_Max.y)) &&
+ ((a_Z >= a_Min.z) && (a_Z <= a_Max.z))
+ );
+}
+
+
+
+
+
+bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face)
+{
+ return CalcLineIntersection(m_Min, m_Max, a_Line1, a_Line2, a_LineCoeff, a_Face);
+}
+
+
+
+
+
+bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face)
+{
+ if (IsInside(a_Min, a_Max, a_Line1))
+ {
+ // The starting point is inside the bounding box.
+ a_LineCoeff = 0;
+ a_Face = BLOCK_FACE_NONE; // No faces hit
+ return true;
+ }
+
+ char Face = BLOCK_FACE_NONE;
+ double Coeff = Vector3d::NO_INTERSECTION;
+
+ // Check each individual bbox face for intersection with the line, remember the one with the lowest coeff
+ double c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Min.z);
+ if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
+ {
+ Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM;
+ Coeff = c;
+ }
+ c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Max.z);
+ if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
+ {
+ Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM;
+ Coeff = c;
+ }
+ c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Min.y);
+ if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
+ {
+ Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM;
+ Coeff = c;
+ }
+ c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Max.y);
+ if ((c >= 0) && (c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
+ {
+ Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM;
+ Coeff = c;
+ }
+ c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Min.x);
+ if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
+ {
+ Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM;
+ Coeff = c;
+ }
+ c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Max.x);
+ if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
+ {
+ Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM;
+ Coeff = c;
+ }
+
+ if (Coeff >= Vector3d::NO_INTERSECTION)
+ {
+ // There has been no intersection
+ return false;
+ }
+
+ a_LineCoeff = Coeff;
+ a_Face = Face;
+ return true;
+}
+
+
+
+
+
+bool cBoundingBox::Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection)
+{
+ a_Intersection.m_Min.x = std::max(m_Min.x, a_Other.m_Min.x);
+ a_Intersection.m_Max.x = std::min(m_Max.x, a_Other.m_Max.x);
+ if (a_Intersection.m_Min.x >= a_Intersection.m_Max.x)
+ {
+ return false;
+ }
+ a_Intersection.m_Min.y = std::max(m_Min.y, a_Other.m_Min.y);
+ a_Intersection.m_Max.y = std::min(m_Max.y, a_Other.m_Max.y);
+ if (a_Intersection.m_Min.y >= a_Intersection.m_Max.y)
+ {
+ return false;
+ }
+ a_Intersection.m_Min.z = std::max(m_Min.z, a_Other.m_Min.z);
+ a_Intersection.m_Max.z = std::min(m_Max.z, a_Other.m_Max.z);
+ if (a_Intersection.m_Min.z >= a_Intersection.m_Max.z)
+ {
+ return false;
+ }
+ return true;
+}
+
+
+
+
diff --git a/src/BoundingBox.h b/src/BoundingBox.h
new file mode 100644
index 000000000..ff9963989
--- /dev/null
+++ b/src/BoundingBox.h
@@ -0,0 +1,90 @@
+
+// BoundingBox.h
+
+// Declares the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords
+
+
+
+
+#pragma once
+
+#include "Vector3d.h"
+
+
+
+
+
+// tolua_begin
+
+/** Represents two sets of coords, minimum and maximum for each direction.
+All the coords within those limits (inclusive the edges) are considered "inside" the box.
+For intersection purposes, though, if the intersection is "sharp" in any coord (i. e. zero volume),
+the boxes are considered non-intersecting.
+*/
+class cBoundingBox
+{
+public:
+ cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ);
+ cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max);
+ cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height);
+ cBoundingBox(const cBoundingBox & a_Orig);
+
+ /// Moves the entire boundingbox by the specified offset
+ void Move(double a_OffX, double a_OffY, double a_OffZ);
+
+ /// Moves the entire boundingbox by the specified offset
+ void Move(const Vector3d & a_Off);
+
+ /// Expands the bounding box by the specified amount in each direction (so the box becomes larger by 2 * Expand in each direction)
+ void Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ);
+
+ /// Returns true if the two bounding boxes intersect
+ bool DoesIntersect(const cBoundingBox & a_Other);
+
+ /// Returns the union of the two bounding boxes
+ cBoundingBox Union(const cBoundingBox & a_Other);
+
+ /// Returns true if the point is inside the bounding box
+ bool IsInside(const Vector3d & a_Point);
+
+ /// Returns true if the point is inside the bounding box
+ bool IsInside(double a_X, double a_Y,double a_Z);
+
+ /// Returns true if a_Other is inside this bounding box
+ bool IsInside(cBoundingBox & a_Other);
+
+ /// Returns true if a boundingbox specified by a_Min and a_Max is inside this bounding box
+ bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max);
+
+ /// Returns true if the specified point is inside the bounding box specified by its min/max corners
+ static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point);
+
+ /// Returns true if the specified point is inside the bounding box specified by its min/max corners
+ static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z);
+
+ /** Returns true if this bounding box is intersected by the line specified by its two points
+ Also calculates the distance along the line in which the intersection occurs (0 .. 1)
+ Only forward collisions (a_LineCoeff >= 0) are returned.
+ */
+ bool CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face);
+
+ /** Returns true if the specified bounding box is intersected by the line specified by its two points
+ Also calculates the distance along the line in which the intersection occurs (0 .. 1) and the face hit (BLOCK_FACE_ constants)
+ Only forward collisions (a_LineCoeff >= 0) are returned.
+ */
+ static bool CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face);
+
+ // tolua_end
+
+ /// Calculates the intersection of the two bounding boxes; returns true if nonempty
+ bool Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection);
+
+protected:
+ Vector3d m_Min;
+ Vector3d m_Max;
+
+} ; // tolua_export
+
+
+
+
diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp
new file mode 100644
index 000000000..8f2b76c1f
--- /dev/null
+++ b/src/ByteBuffer.cpp
@@ -0,0 +1,841 @@
+
+// ByteBuffer.cpp
+
+// Implements the cByteBuffer class representing a ringbuffer of bytes
+
+#include "Globals.h"
+
+#include "ByteBuffer.h"
+#include "Endianness.h"
+#include "OSSupport/IsThread.h"
+
+
+
+
+
+// Try to determine endianness:
+#if ( \
+ defined(__i386__) || defined(__alpha__) || \
+ defined(__ia64) || defined(__ia64__) || \
+ defined(_M_IX86) || defined(_M_IA64) || \
+ defined(_M_ALPHA) || defined(__amd64) || \
+ defined(__amd64__) || defined(_M_AMD64) || \
+ defined(__x86_64) || defined(__x86_64__) || \
+ defined(_M_X64) || defined(__bfin__) || \
+ defined(__ARMEL__) || \
+ (defined(_WIN32) && defined(__ARM__) && defined(_MSC_VER)) \
+)
+ #define IS_LITTLE_ENDIAN
+#elif defined (__ARMEB__)
+ #define IS_BIG_ENDIAN
+#else
+ #error Cannot determine endianness of this platform
+#endif
+
+// If a string sent over the protocol is larger than this, a warning is emitted to the console
+#define MAX_STRING_SIZE (512 KiB)
+
+#define NEEDBYTES(Num) if (!CanReadBytes(Num)) return false; // Check if at least Num bytes can be read from the buffer, return false if not
+#define PUTBYTES(Num) if (!CanWriteBytes(Num)) return false; // Check if at least Num bytes can be written to the buffer, return false if not
+
+
+
+
+
+#if 0
+
+/// Self-test of the VarInt-reading and writing code
+class cByteBufferSelfTest
+{
+public:
+ cByteBufferSelfTest(void)
+ {
+ TestRead();
+ TestWrite();
+ }
+
+ void TestRead(void)
+ {
+ cByteBuffer buf(50);
+ buf.Write("\x05\xac\x02\x00", 4);
+ UInt32 v1;
+ ASSERT(buf.ReadVarInt(v1) && (v1 == 5));
+ UInt32 v2;
+ ASSERT(buf.ReadVarInt(v2) && (v2 == 300));
+ UInt32 v3;
+ ASSERT(buf.ReadVarInt(v3) && (v3 == 0));
+ }
+
+ void TestWrite(void)
+ {
+ cByteBuffer buf(50);
+ buf.WriteVarInt(5);
+ buf.WriteVarInt(300);
+ buf.WriteVarInt(0);
+ AString All;
+ buf.ReadAll(All);
+ ASSERT(All.size() == 4);
+ ASSERT(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0);
+ }
+} g_ByteBufferTest;
+
+#endif
+
+
+
+
+
+#ifdef _DEBUG
+
+/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously
+class cSingleThreadAccessChecker
+{
+public:
+ cSingleThreadAccessChecker(unsigned long * a_ThreadID) :
+ m_ThreadID(a_ThreadID)
+ {
+ ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID()));
+ }
+
+ ~cSingleThreadAccessChecker()
+ {
+ *m_ThreadID = 0;
+ }
+
+protected:
+ unsigned long * m_ThreadID;
+} ;
+
+#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast<unsigned long *>(&m_ThreadID))
+
+#else
+ #define CHECK_THREAD
+#endif
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cByteBuffer:
+
+cByteBuffer::cByteBuffer(int a_BufferSize) :
+ m_Buffer(new char[a_BufferSize + 1]),
+ m_BufferSize(a_BufferSize + 1),
+ #ifdef _DEBUG
+ m_ThreadID(0),
+ #endif // _DEBUG
+ m_DataStart(0),
+ m_WritePos(0),
+ m_ReadPos(0)
+{
+ // Allocating one byte more than the buffer size requested, so that we can distinguish between
+ // completely-full and completely-empty states
+}
+
+
+
+
+
+cByteBuffer::~cByteBuffer()
+{
+ CheckValid();
+ delete[] m_Buffer;
+}
+
+
+
+
+
+bool cByteBuffer::Write(const char * a_Bytes, int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+
+ // Store the current free space for a check after writing:
+ int CurFreeSpace = GetFreeSpace();
+ int CurReadableSpace = GetReadableSpace();
+ int WrittenBytes = 0;
+
+ if (GetFreeSpace() < a_Count)
+ {
+ return false;
+ }
+ int TillEnd = m_BufferSize - m_WritePos;
+ if (TillEnd <= a_Count)
+ {
+ // Need to wrap around the ringbuffer end
+ if (TillEnd > 0)
+ {
+ memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd);
+ a_Bytes += TillEnd;
+ a_Count -= TillEnd;
+ WrittenBytes = TillEnd;
+ }
+ m_WritePos = 0;
+ }
+
+ // We're guaranteed that we'll fit in a single write op
+ if (a_Count > 0)
+ {
+ memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count);
+ m_WritePos += a_Count;
+ WrittenBytes += a_Count;
+ }
+
+ ASSERT(GetFreeSpace() == CurFreeSpace - WrittenBytes);
+ ASSERT(GetReadableSpace() == CurReadableSpace + WrittenBytes);
+ return true;
+}
+
+
+
+
+
+int cByteBuffer::GetFreeSpace(void) const
+{
+ CHECK_THREAD;
+ CheckValid();
+ if (m_WritePos >= m_DataStart)
+ {
+ // Wrap around the buffer end:
+ return m_BufferSize - m_WritePos + m_DataStart - 1;
+ }
+ // Single free space partition:
+ return m_DataStart - m_WritePos - 1;
+}
+
+
+
+
+
+/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
+int cByteBuffer::GetUsedSpace(void) const
+{
+ CHECK_THREAD;
+ CheckValid();
+ return m_BufferSize - GetFreeSpace() - 1;
+}
+
+
+
+
+
+/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
+int cByteBuffer::GetReadableSpace(void) const
+{
+ CHECK_THREAD;
+ CheckValid();
+ if (m_ReadPos > m_WritePos)
+ {
+ // Wrap around the buffer end:
+ return m_BufferSize - m_ReadPos + m_WritePos;
+ }
+ // Single readable space partition:
+ return m_WritePos - m_ReadPos ;
+}
+
+
+
+
+
+bool cByteBuffer::CanReadBytes(int a_Count) const
+{
+ CHECK_THREAD;
+ CheckValid();
+ return (a_Count <= GetReadableSpace());
+}
+
+
+
+
+
+bool cByteBuffer::CanWriteBytes(int a_Count) const
+{
+ CHECK_THREAD;
+ CheckValid();
+ return (a_Count <= GetFreeSpace());
+}
+
+
+
+
+
+bool cByteBuffer::ReadChar(char & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(1);
+ ReadBuf(&a_Value, 1);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadByte(unsigned char & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(1);
+ ReadBuf(&a_Value, 1);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEShort(short & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(2);
+ ReadBuf(&a_Value, 2);
+ a_Value = ntohs(a_Value);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEInt(int & 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;
+ CheckValid();
+ NEEDBYTES(8);
+ ReadBuf(&a_Value, 8);
+ a_Value = NetworkToHostLong8(&a_Value);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEFloat(float & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(4);
+ ReadBuf(&a_Value, 4);
+ a_Value = NetworkToHostFloat4(&a_Value);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEDouble(double & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(8);
+ ReadBuf(&a_Value, 8);
+ a_Value = NetworkToHostDouble8(&a_Value);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBool(bool & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(1);
+ char Value = 0;
+ ReadBuf(&Value, 1);
+ a_Value = (Value != 0);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ short Length;
+ if (!ReadBEShort(Length))
+ {
+ return false;
+ }
+ if (Length < 0)
+ {
+ ASSERT(!"Negative string length? Are you sure?");
+ return true;
+ }
+ return ReadUTF16String(a_Value, Length);
+}
+
+
+
+
+
+bool cByteBuffer::ReadVarInt(UInt32 & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ UInt32 Value = 0;
+ int Shift = 0;
+ unsigned char b = 0;
+ do
+ {
+ NEEDBYTES(1);
+ ReadBuf(&b, 1);
+ Value = Value | (((Int64)(b & 0x7f)) << Shift);
+ Shift += 7;
+ } while ((b & 0x80) != 0);
+ a_Value = Value;
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ UInt32 Size = 0;
+ if (!ReadVarInt(Size))
+ {
+ return false;
+ }
+ if (Size > MAX_STRING_SIZE)
+ {
+ LOGWARNING("%s: String too large: %llu (%llu KiB)", __FUNCTION__, Size, Size / 1024);
+ }
+ return ReadString(a_Value, (int)Size);
+}
+
+
+
+
+
+bool cByteBuffer::ReadLEInt(int & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(4);
+ ReadBuf(&a_Value, 4);
+
+ #ifdef IS_BIG_ENDIAN
+ // Convert:
+ a_Value = ((a_Value >> 24) & 0xff) | ((a_Value >> 16) & 0xff00) | ((a_Value >> 8) & 0xff0000) | (a_Value & 0xff000000);
+ #endif
+
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::WriteChar(char a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(1);
+ return WriteBuf(&a_Value, 1);
+}
+
+
+
+
+
+bool cByteBuffer::WriteByte(unsigned char a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(1);
+ return WriteBuf(&a_Value, 1);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEShort(short a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(2);
+ short Converted = htons(a_Value);
+ return WriteBuf(&Converted, 2);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEInt(int a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(4);
+ int Converted = HostToNetwork4(&a_Value);
+ return WriteBuf(&Converted, 4);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEInt64(Int64 a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(8);
+ Int64 Converted = HostToNetwork8(&a_Value);
+ return WriteBuf(&Converted, 8);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEFloat(float a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(4);
+ int Converted = HostToNetwork4(&a_Value);
+ return WriteBuf(&Converted, 4);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEDouble(double a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(8);
+ Int64 Converted = HostToNetwork8(&a_Value);
+ return WriteBuf(&Converted, 8);
+}
+
+
+
+
+
+
+bool cByteBuffer::WriteBool(bool a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ return WriteChar(a_Value ? 1 : 0);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEUTF16String16(const AString & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(2);
+ AString UTF16BE;
+ UTF8ToRawBEUTF16(a_Value.data(), a_Value.size(), UTF16BE);
+ WriteBEShort((short)(UTF16BE.size() / 2));
+ PUTBYTES(UTF16BE.size());
+ WriteBuf(UTF16BE.data(), UTF16BE.size());
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::WriteVarInt(UInt32 a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+
+ // A 32-bit integer can be encoded by at most 5 bytes:
+ unsigned char b[5];
+ int idx = 0;
+ do
+ {
+ b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00);
+ a_Value = a_Value >> 7;
+ idx++;
+ } while (a_Value > 0);
+
+ return WriteBuf(b, idx);
+}
+
+
+
+
+bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early.
+ bool res = WriteVarInt(a_Value.size());
+ if (!res)
+ {
+ return false;
+ }
+ return WriteBuf(a_Value.data(), a_Value.size());
+}
+
+
+
+
+
+bool cByteBuffer::WriteLEInt(int a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ #ifdef IS_LITTLE_ENDIAN
+ return WriteBuf((const char *)&a_Value, 4);
+ #else
+ int Value = ((a_Value >> 24) & 0xff) | ((a_Value >> 16) & 0xff00) | ((a_Value >> 8) & 0xff0000) | (a_Value & 0xff000000);
+ return WriteBuf((const char *)&Value, 4);
+ #endif
+}
+
+
+
+
+
+bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+ ASSERT(a_Count >= 0);
+ NEEDBYTES(a_Count);
+ char * Dst = (char *)a_Buffer; // So that we can do byte math
+ int BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
+ ASSERT(BytesToEndOfBuffer >= 0); // Sanity check
+ if (BytesToEndOfBuffer <= a_Count)
+ {
+ // Reading across the ringbuffer end, read the first part and adjust parameters:
+ if (BytesToEndOfBuffer > 0)
+ {
+ memcpy(Dst, m_Buffer + m_ReadPos, BytesToEndOfBuffer);
+ Dst += BytesToEndOfBuffer;
+ a_Count -= BytesToEndOfBuffer;
+ }
+ m_ReadPos = 0;
+ }
+
+ // Read the rest of the bytes in a single read (guaranteed to fit):
+ if (a_Count > 0)
+ {
+ memcpy(Dst, m_Buffer + m_ReadPos, a_Count);
+ m_ReadPos += a_Count;
+ }
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+ ASSERT(a_Count >= 0);
+ PUTBYTES(a_Count);
+ char * Src = (char *)a_Buffer; // So that we can do byte math
+ int BytesToEndOfBuffer = m_BufferSize - m_WritePos;
+ if (BytesToEndOfBuffer <= a_Count)
+ {
+ // Reading across the ringbuffer end, read the first part and adjust parameters:
+ memcpy(m_Buffer + m_WritePos, Src, BytesToEndOfBuffer);
+ Src += BytesToEndOfBuffer;
+ a_Count -= BytesToEndOfBuffer;
+ m_WritePos = 0;
+ }
+
+ // Read the rest of the bytes in a single read (guaranteed to fit):
+ if (a_Count > 0)
+ {
+ memcpy(m_Buffer + m_WritePos, Src, a_Count);
+ m_WritePos += a_Count;
+ }
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadString(AString & a_String, int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+ ASSERT(a_Count >= 0);
+ NEEDBYTES(a_Count);
+ a_String.clear();
+ a_String.reserve(a_Count);
+ int BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
+ ASSERT(BytesToEndOfBuffer >= 0); // Sanity check
+ if (BytesToEndOfBuffer <= a_Count)
+ {
+ // Reading across the ringbuffer end, read the first part and adjust parameters:
+ if (BytesToEndOfBuffer > 0)
+ {
+ a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer);
+ a_Count -= BytesToEndOfBuffer;
+ }
+ m_ReadPos = 0;
+ }
+
+ // Read the rest of the bytes in a single read (guaranteed to fit):
+ if (a_Count > 0)
+ {
+ a_String.append(m_Buffer + m_ReadPos, a_Count);
+ m_ReadPos += a_Count;
+ }
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars)
+{
+ // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String
+ CHECK_THREAD;
+ CheckValid();
+ ASSERT(a_NumChars >= 0);
+ AString RawData;
+ if (!ReadString(RawData, a_NumChars * 2))
+ {
+ return false;
+ }
+ RawBEToUTF8((short *)(RawData.data()), a_NumChars, a_String);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::SkipRead(int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+ ASSERT(a_Count >= 0);
+ if (!CanReadBytes(a_Count))
+ {
+ return false;
+ }
+ AdvanceReadPos(a_Count);
+ return true;
+}
+
+
+
+
+
+void cByteBuffer::ReadAll(AString & a_Data)
+{
+ CHECK_THREAD;
+ CheckValid();
+ ReadString(a_Data, GetReadableSpace());
+}
+
+
+
+
+
+void cByteBuffer::CommitRead(void)
+{
+ CHECK_THREAD;
+ CheckValid();
+ m_DataStart = m_ReadPos;
+}
+
+
+
+
+
+void cByteBuffer::ResetRead(void)
+{
+ CHECK_THREAD;
+ CheckValid();
+ m_ReadPos = m_DataStart;
+}
+
+
+
+
+
+void cByteBuffer::ReadAgain(AString & a_Out)
+{
+ // Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed)
+ // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party
+ CHECK_THREAD;
+ CheckValid();
+ int DataStart = m_DataStart;
+ if (m_ReadPos < m_DataStart)
+ {
+ // Across the ringbuffer end, read the first part and adjust next part's start:
+ a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart);
+ DataStart = 0;
+ }
+ a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart);
+}
+
+
+
+
+
+void cByteBuffer::AdvanceReadPos(int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+ m_ReadPos += a_Count;
+ if (m_ReadPos > m_BufferSize)
+ {
+ m_ReadPos -= m_BufferSize;
+ }
+}
+
+
+
+
+
+void cByteBuffer::CheckValid(void) const
+{
+ ASSERT(m_ReadPos >= 0);
+ ASSERT(m_ReadPos < m_BufferSize);
+ ASSERT(m_WritePos >= 0);
+ ASSERT(m_WritePos < m_BufferSize);
+}
+
+
+
+
diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h
new file mode 100644
index 000000000..a9dd7f5ea
--- /dev/null
+++ b/src/ByteBuffer.h
@@ -0,0 +1,139 @@
+
+// ByteStream.h
+
+// Interfaces to the cByteBuffer class representing a ringbuffer of bytes
+
+
+
+
+
+#pragma once
+
+
+
+
+
+/** An object that can store incoming bytes and lets its clients read the bytes sequentially
+The bytes are stored in a ringbuffer of constant size; if more than that size
+is requested, the write operation fails.
+The bytes stored can be retrieved using various ReadXXX functions; these assume that the needed
+number of bytes are present in the buffer (ASSERT; for performance reasons).
+The reading doesn't actually remove the bytes, it only moves the internal read ptr.
+To remove the bytes, call CommitRead().
+To re-start reading from the beginning, call ResetRead().
+This class doesn't implement thread safety, the clients of this class need to provide
+their own synchronization.
+*/
+class cByteBuffer
+{
+public:
+ cByteBuffer(int a_BufferSize);
+ ~cByteBuffer();
+
+ /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not
+ bool Write(const char * a_Bytes, int a_Count);
+
+ /// Returns the number of bytes that can be successfully written to the ringbuffer
+ int GetFreeSpace(void) const;
+
+ /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
+ int GetUsedSpace(void) const;
+
+ /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
+ int GetReadableSpace(void) const;
+
+ /// Returns true if the specified amount of bytes are available for reading
+ bool CanReadBytes(int a_Count) const;
+
+ /// Returns true if the specified amount of bytes are available for writing
+ bool CanWriteBytes(int a_Count) const;
+
+ // Read the specified datatype and advance the read pointer; return true if successfully read:
+ bool ReadChar (char & a_Value);
+ bool ReadByte (unsigned char & a_Value);
+ bool ReadBEShort (short & a_Value);
+ bool ReadBEInt (int & a_Value);
+ bool ReadBEInt64 (Int64 & a_Value);
+ bool ReadBEFloat (float & a_Value);
+ bool ReadBEDouble (double & a_Value);
+ bool ReadBool (bool & a_Value);
+ bool ReadBEUTF16String16(AString & a_Value); // string length as BE short, then string as UTF-16BE
+ bool ReadVarInt (UInt32 & a_Value);
+ bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8
+ bool ReadLEInt (int & a_Value);
+
+ /// Reads VarInt, assigns it to anything that can be assigned from an UInt32 (unsigned short, char, Byte, double, ...)
+ template <typename T> bool ReadVarInt(T & a_Value)
+ {
+ UInt32 v;
+ bool res = ReadVarInt(v);
+ if (res)
+ {
+ a_Value = v;
+ }
+ return res;
+ }
+
+ // Write the specified datatype; return true if successfully written
+ bool WriteChar (char a_Value);
+ bool WriteByte (unsigned char a_Value);
+ bool WriteBEShort (short a_Value);
+ bool WriteBEInt (int a_Value);
+ bool WriteBEInt64 (Int64 a_Value);
+ bool WriteBEFloat (float a_Value);
+ bool WriteBEDouble (double a_Value);
+ bool WriteBool (bool a_Value);
+ bool WriteBEUTF16String16(const AString & a_Value); // string length as BE short, then string as UTF-16BE
+ bool WriteVarInt (UInt32 a_Value);
+ bool WriteVarUTF8String (const AString & a_Value); // string length as VarInt, then string as UTF-8
+ bool WriteLEInt (int a_Value);
+
+ /// Reads a_Count bytes into a_Buffer; returns true if successful
+ bool ReadBuf(void * a_Buffer, int a_Count);
+
+ /// Writes a_Count bytes into a_Buffer; returns true if successful
+ bool WriteBuf(const void * a_Buffer, int a_Count);
+
+ /// Reads a_Count bytes into a_String; returns true if successful
+ bool ReadString(AString & a_String, int a_Count);
+
+ /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String
+ bool ReadUTF16String(AString & a_String, int a_NumChars);
+
+ /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer
+ bool SkipRead(int a_Count);
+
+ /// Reads all available data into a_Data
+ void ReadAll(AString & a_Data);
+
+ /// Removes the bytes that have been read from the ringbuffer
+ void CommitRead(void);
+
+ /// Restarts next reading operation at the start of the ringbuffer
+ void ResetRead(void);
+
+ /// Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication
+ void ReadAgain(AString & a_Out);
+
+ /// Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs
+ void CheckValid(void) const;
+
+protected:
+ char * m_Buffer;
+ int m_BufferSize; // Total size of the ringbuffer
+
+ #ifdef _DEBUG
+ unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker
+ #endif // _DEBUG
+
+ int m_DataStart; // Where the data starts in the ringbuffer
+ int m_WritePos; // Where the data ends in the ringbuffer
+ int m_ReadPos; // Where the next read will start in the ringbuffer
+
+ /// Advances the m_ReadPos by a_Count bytes
+ void AdvanceReadPos(int a_Count);
+} ;
+
+
+
+
diff --git a/src/ChatColor.cpp b/src/ChatColor.cpp
new file mode 100644
index 000000000..2b223ee76
--- /dev/null
+++ b/src/ChatColor.cpp
@@ -0,0 +1,39 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "ChatColor.h"
+
+const std::string cChatColor::Color = "\xc2\xa7"; // or in other words: "§" in UTF-8
+const std::string cChatColor::Delimiter = "\xc2\xa7"; // or in other words: "§" in UTF-8
+const std::string cChatColor::Black = cChatColor::Color + "0";
+const std::string cChatColor::Navy = cChatColor::Color + "1";
+const std::string cChatColor::Green = cChatColor::Color + "2";
+const std::string cChatColor::Blue = cChatColor::Color + "3";
+const std::string cChatColor::Red = cChatColor::Color + "4";
+const std::string cChatColor::Purple = cChatColor::Color + "5";
+const std::string cChatColor::Gold = cChatColor::Color + "6";
+const std::string cChatColor::LightGray = cChatColor::Color + "7";
+const std::string cChatColor::Gray = cChatColor::Color + "8";
+const std::string cChatColor::DarkPurple = cChatColor::Color + "9";
+const std::string cChatColor::LightGreen = cChatColor::Color + "a";
+const std::string cChatColor::LightBlue = cChatColor::Color + "b";
+const std::string cChatColor::Rose = cChatColor::Color + "c";
+const std::string cChatColor::LightPurple = cChatColor::Color + "d";
+const std::string cChatColor::Yellow = cChatColor::Color + "e";
+const std::string cChatColor::White = cChatColor::Color + "f";
+
+const std::string cChatColor::Random = cChatColor::Color + "k";
+const std::string cChatColor::Bold = cChatColor::Color + "l";
+const std::string cChatColor::Strikethrough = cChatColor::Color + "m";
+const std::string cChatColor::Underlined = cChatColor::Color + "n";
+const std::string cChatColor::Italic = cChatColor::Color + "o";
+const std::string cChatColor::Plain = cChatColor::Color + "r";
+
+const std::string cChatColor::MakeColor( char a_Color )
+{
+ return cChatColor::Color + a_Color;
+}
+
+
+
+
diff --git a/src/ChatColor.h b/src/ChatColor.h
new file mode 100644
index 000000000..85b10f400
--- /dev/null
+++ b/src/ChatColor.h
@@ -0,0 +1,43 @@
+
+#pragma once
+
+
+
+
+
+// tolua_begin
+class cChatColor
+{
+public:
+ static const std::string Color;
+ static const std::string Delimiter;
+
+ static const std::string Black;
+ static const std::string Navy;
+ static const std::string Green;
+ static const std::string Blue;
+ static const std::string Red;
+ static const std::string Purple;
+ static const std::string Gold;
+ static const std::string LightGray;
+ static const std::string Gray;
+ static const std::string DarkPurple;
+ static const std::string LightGreen;
+ static const std::string LightBlue;
+ static const std::string Rose;
+ static const std::string LightPurple;
+ static const std::string Yellow;
+ static const std::string White;
+
+ // Styles ( source: http://wiki.vg/Chat )
+ static const std::string Random;
+ static const std::string Bold;
+ static const std::string Strikethrough;
+ static const std::string Underlined;
+ static const std::string Italic;
+ static const std::string Plain;
+
+ static const std::string MakeColor( char a_Color );
+};
+
+// tolua_end
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
new file mode 100644
index 000000000..83f866e56
--- /dev/null
+++ b/src/Chunk.cpp
@@ -0,0 +1,2776 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#ifndef _WIN32
+ #include <cstdlib>
+#endif
+
+
+#include "Chunk.h"
+#include "World.h"
+#include "ClientHandle.h"
+#include "Server.h"
+#include "zlib/zlib.h"
+#include "Defines.h"
+#include "BlockEntities/ChestEntity.h"
+#include "BlockEntities/DispenserEntity.h"
+#include "BlockEntities/DropperEntity.h"
+#include "BlockEntities/FurnaceEntity.h"
+#include "BlockEntities/HopperEntity.h"
+#include "BlockEntities/JukeboxEntity.h"
+#include "BlockEntities/NoteEntity.h"
+#include "BlockEntities/SignEntity.h"
+#include "Entities/Pickup.h"
+#include "Item.h"
+#include "Noise.h"
+#include "Root.h"
+#include "MersenneTwister.h"
+#include "Entities/Player.h"
+#include "BlockArea.h"
+#include "PluginManager.h"
+#include "Blocks/BlockHandler.h"
+#include "Simulator/FluidSimulator.h"
+#include "MobCensus.h"
+#include "MobSpawner.h"
+
+
+#include "lib/jsoncpp/include/json/json.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 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);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cChunk:
+
+cChunk::cChunk(
+ int a_ChunkX, int a_ChunkY, int a_ChunkZ,
+ cChunkMap * a_ChunkMap, cWorld * a_World,
+ cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP
+)
+ : m_PosX( a_ChunkX )
+ , m_PosY( a_ChunkY )
+ , m_PosZ( a_ChunkZ )
+ , m_BlockTickX( 0 )
+ , m_BlockTickY( 0 )
+ , m_BlockTickZ( 0 )
+ , m_World( a_World )
+ , m_ChunkMap(a_ChunkMap)
+ , m_IsValid(false)
+ , m_IsLightValid(false)
+ , m_IsDirty(false)
+ , m_IsSaving(false)
+ , m_StayCount(0)
+ , m_NeighborXM(a_NeighborXM)
+ , m_NeighborXP(a_NeighborXP)
+ , m_NeighborZM(a_NeighborZM)
+ , m_NeighborZP(a_NeighborZP)
+ , m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData())
+ , m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData())
+{
+ if (a_NeighborXM != NULL)
+ {
+ a_NeighborXM->m_NeighborXP = this;
+ }
+ if (a_NeighborXP != NULL)
+ {
+ a_NeighborXP->m_NeighborXM = this;
+ }
+ if (a_NeighborZM != NULL)
+ {
+ a_NeighborZM->m_NeighborZP = this;
+ }
+ if (a_NeighborZP != NULL)
+ {
+ a_NeighborZP->m_NeighborZM = this;
+ }
+}
+
+
+
+
+
+cChunk::~cChunk()
+{
+ cPluginManager::Get()->CallHookChunkUnloaded(m_World, m_PosX, m_PosZ);
+
+ // LOGINFO("### delete cChunk() (%i, %i) from %p, thread 0x%x ###", m_PosX, m_PosZ, this, GetCurrentThreadId() );
+
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ delete *itr;
+ }
+ m_BlockEntities.clear();
+
+ // Remove and destroy all entities that are not players:
+ cEntityList Entities;
+ std::swap(Entities, m_Entities); // Need another list because cEntity destructors check if they've been removed from chunk
+ for (cEntityList::const_iterator itr = Entities.begin(); itr != Entities.end(); ++itr)
+ {
+ if (!(*itr)->IsPlayer())
+ {
+ (*itr)->Destroy(false);
+ delete *itr;
+ }
+ }
+
+ if (m_NeighborXM != NULL)
+ {
+ m_NeighborXM->m_NeighborXP = NULL;
+ }
+ if (m_NeighborXP != NULL)
+ {
+ m_NeighborXP->m_NeighborXM = NULL;
+ }
+ if (m_NeighborZM != NULL)
+ {
+ m_NeighborZM->m_NeighborZP = NULL;
+ }
+ if (m_NeighborZP != NULL)
+ {
+ m_NeighborZP->m_NeighborZM = NULL;
+ }
+ delete m_WaterSimulatorData;
+ delete m_LavaSimulatorData;
+}
+
+
+
+
+
+void cChunk::SetValid(void)
+{
+ m_IsValid = true;
+
+ m_World->GetChunkMap()->ChunkValidated();
+}
+
+
+
+
+
+void cChunk::MarkRegenerating(void)
+{
+ // Tell all clients attached to this chunk that they want this chunk:
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
+ {
+ (*itr)->AddWantedChunk(m_PosX, m_PosZ);
+ } // for itr - m_LoadedByClient[]
+}
+
+
+
+
+
+bool cChunk::CanUnload(void)
+{
+ return m_LoadedByClient.empty() && !m_IsDirty && (m_StayCount == 0);
+}
+
+
+
+
+
+void cChunk::MarkSaving(void)
+{
+ m_IsSaving = true;
+}
+
+
+
+
+
+void cChunk::MarkSaved(void)
+{
+ if (!m_IsSaving)
+ {
+ return;
+ }
+ m_IsDirty = false;
+}
+
+
+
+
+
+void cChunk::MarkLoaded(void)
+{
+ m_IsDirty = false;
+ SetValid();
+}
+
+
+
+
+
+void cChunk::MarkLoadFailed(void)
+{
+ if (m_IsValid)
+ {
+ return;
+ }
+
+ m_HasLoadFailed = true;
+}
+
+
+
+
+
+void cChunk::GetAllData(cChunkDataCallback & a_Callback)
+{
+ a_Callback.HeightMap (&m_HeightMap);
+ a_Callback.BiomeData (&m_BiomeMap);
+ a_Callback.BlockTypes (m_BlockTypes);
+ a_Callback.BlockMeta (m_BlockMeta);
+ a_Callback.LightIsValid (m_IsLightValid);
+ a_Callback.BlockLight (m_BlockLight);
+ a_Callback.BlockSkyLight(m_BlockSkyLight);
+
+ for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
+ {
+ a_Callback.Entity(*itr);
+ }
+
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ a_Callback.BlockEntity(*itr);
+ }
+}
+
+
+
+
+
+void cChunk::SetAllData(
+ const BLOCKTYPE * a_BlockTypes,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
+ const HeightMap * a_HeightMap,
+ const BiomeMap & a_BiomeMap,
+ cBlockEntityList & a_BlockEntities
+)
+{
+ memcpy(m_BiomeMap, a_BiomeMap, sizeof(m_BiomeMap));
+
+ if (a_HeightMap != NULL)
+ {
+ memcpy(m_HeightMap, a_HeightMap, sizeof(m_HeightMap));
+ }
+
+ memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes));
+ memcpy(m_BlockMeta, a_BlockMeta, sizeof(m_BlockMeta));
+ if (a_BlockLight != NULL)
+ {
+ memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
+ }
+ if (a_BlockSkyLight != NULL)
+ {
+ memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
+ }
+
+ m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL);
+
+ if (a_HeightMap == NULL)
+ {
+ CalculateHeightmap();
+ }
+
+ // Clear the block entities present - either the loader / saver has better, or we'll create empty ones:
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ delete *itr;
+ }
+ std::swap(a_BlockEntities, m_BlockEntities);
+
+ // Set all block entities' World variable:
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ (*itr)->SetWorld(m_World);
+ }
+
+ // Create block entities that the loader didn't load; fill them with defaults
+ CreateBlockEntities();
+
+ // Set the chunk data as valid. This may be needed for some simulators that perform actions upon block adding (Vaporize)
+ SetValid();
+
+ // Wake up all simulators for their respective blocks:
+ WakeUpSimulators();
+
+ m_HasLoadFailed = false;
+}
+
+
+
+
+
+void cChunk::SetLight(
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
+)
+{
+ // TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation.
+ // Postponing until we see how bad it is :)
+ memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
+ memcpy(m_BlockSkyLight, a_SkyLight, sizeof(m_BlockSkyLight));
+ m_IsLightValid = true;
+}
+
+
+
+
+
+void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes)
+{
+ memcpy(a_BlockTypes, m_BlockTypes, NumBlocks);
+}
+
+
+
+
+
+void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
+{
+ if ((a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas)) != (cBlockArea::baTypes | cBlockArea::baMetas))
+ {
+ LOGWARNING("cChunk::WriteBlockArea(): unsupported datatype request, can write only types + metas (0x%x), requested 0x%x. Ignoring.",
+ (cBlockArea::baTypes | cBlockArea::baMetas), a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas)
+ );
+ return;
+ }
+
+ // SizeX, SizeZ are the dimensions of the block data to copy to the chunk (size of the geometric union)
+
+ int BlockStartX = std::max(a_MinBlockX, m_PosX * cChunkDef::Width);
+ int BlockEndX = std::min(a_MinBlockX + a_Area.GetSizeX(), (m_PosX + 1) * cChunkDef::Width);
+ int BlockStartZ = std::max(a_MinBlockZ, m_PosZ * cChunkDef::Width);
+ int BlockEndZ = std::min(a_MinBlockZ + a_Area.GetSizeZ(), (m_PosZ + 1) * cChunkDef::Width);
+ int SizeX = BlockEndX - BlockStartX;
+ int SizeZ = BlockEndZ - BlockStartZ;
+ int OffX = BlockStartX - m_PosX * cChunkDef::Width;
+ int OffZ = BlockStartZ - m_PosZ * cChunkDef::Width;
+ int BaseX = BlockStartX - a_MinBlockX;
+ int BaseZ = BlockStartZ - a_MinBlockZ;
+ int SizeY = a_Area.GetSizeY();
+
+ // TODO: Improve this by not calling FastSetBlock() and doing the processing here
+ // so that the heightmap is touched only once for each column.
+ BLOCKTYPE * AreaBlockTypes = a_Area.GetBlockTypes();
+ NIBBLETYPE * AreaBlockMetas = a_Area.GetBlockMetas();
+ for (int y = 0; y < SizeY; y++)
+ {
+ int ChunkY = a_MinBlockY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int ChunkZ = OffZ + z;
+ int AreaZ = BaseZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int ChunkX = OffX + x;
+ int AreaX = BaseX + x;
+ int idx = a_Area.MakeIndex(AreaX, AreaY, AreaZ);
+ BLOCKTYPE BlockType = AreaBlockTypes[idx];
+ NIBBLETYPE BlockMeta = AreaBlockMetas[idx];
+ FastSetBlock(ChunkX, ChunkY, ChunkZ, BlockType, BlockMeta);
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+/// Returns true if there is a block entity at the coords specified
+bool cChunk::HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ if (
+ ((*itr)->GetPosX() == a_BlockX) &&
+ ((*itr)->GetPosY() == a_BlockY) &&
+ ((*itr)->GetPosZ() == a_BlockZ)
+ )
+ {
+ return true;
+ }
+ } // for itr - m_BlockEntities[]
+ return false;
+}
+
+
+
+
+
+/// Sets or resets the internal flag that prevents chunk from being unloaded
+void cChunk::Stay(bool a_Stay)
+{
+ m_StayCount += (a_Stay ? 1 : -1);
+ ASSERT(m_StayCount >= 0);
+}
+
+
+
+
+void cChunk::CollectMobCensus(cMobCensus& toFill)
+{
+ toFill.CollectSpawnableChunk(*this);
+ std::list<const Vector3d*> playerPositions;
+ cPlayer* currentPlayer;
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
+ {
+ currentPlayer = (*itr)->GetPlayer();
+ playerPositions.push_back(&(currentPlayer->GetPosition()));
+ }
+
+ Vector3d currentPosition;
+ for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
+ {
+ //LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
+ if ((*itr)->IsMob())
+ {
+ cMonster& Monster = (cMonster&)(**itr);
+ currentPosition = Monster.GetPosition();
+ for (std::list<const Vector3d*>::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); itr2 ++)
+ {
+ toFill.CollectMob(Monster,*this,(currentPosition-**itr2).SqrLength());
+ }
+ }
+ } // for itr - m_Entitites[]
+}
+
+
+
+
+void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ)
+{
+ ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff);
+ int Random = m_World->GetTickRandomNumber(0x00ffffff);
+ a_X = Random % (a_MaxX * 2);
+ a_Y = (Random / (a_MaxX * 2)) % (a_MaxY * 2);
+ a_Z = ((Random / (a_MaxX * 2)) / (a_MaxY * 2)) % (a_MaxZ * 2);
+ a_X /= 2;
+ a_Y /= 2;
+ a_Z /= 2;
+}
+
+
+
+
+
+void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z)
+{
+ // MG TODO : check if this kind of optimization (only one random call) is still needed
+ // MG TODO : if so propagate it
+
+ getThreeRandomNumber(a_X, a_Y, a_Z, Width, Height-2, Width);
+ a_Y++;
+}
+
+
+
+
+
+void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner)
+{
+ int Center_X,Center_Y,Center_Z;
+ getRandomBlockCoords(Center_X,Center_Y,Center_Z);
+
+ BLOCKTYPE PackCenterBlock = GetBlock(Center_X, Center_Y, Center_Z);
+ if (a_MobSpawner.CheckPackCenter(PackCenterBlock))
+ {
+ a_MobSpawner.NewPack();
+ int NumberOfTries = 0;
+ int NumberOfSuccess = 0;
+ int MaxNbOfSuccess = 4; // this can be changed during the process for Wolves and Ghass
+ while (NumberOfTries < 12 && NumberOfSuccess < MaxNbOfSuccess)
+ {
+ const int HorizontalRange = 20; // MG TODO : relocate
+ const int VerticalRange = 0; // MG TODO : relocate
+ int Try_X, Try_Y, Try_Z;
+ getThreeRandomNumber(Try_X, Try_Y, Try_Z, 2*HorizontalRange+1 , 2*VerticalRange+1 , 2*HorizontalRange+1);
+ Try_X -= HorizontalRange;
+ Try_Y -= VerticalRange;
+ Try_Z -= HorizontalRange;
+ Try_X += Center_X;
+ Try_Y += Center_Y;
+ Try_Z += Center_Z;
+
+ ASSERT(Try_Y > 0);
+ ASSERT(Try_Y < cChunkDef::Height-1);
+
+ EMCSBiome Biome = m_ChunkMap->GetBiomeAt (Try_X, Try_Z);
+ // MG TODO :
+ // Moon cycle (for slime)
+ // check player and playerspawn presence < 24 blocks
+ // check mobs presence on the block
+
+ // MG TODO : check that "Level" really means Y
+
+ NIBBLETYPE SkyLight = 0;
+
+ NIBBLETYPE BlockLight = 0;
+
+ if (IsLightValid())
+ {
+ cEntity* newMob = a_MobSpawner.TryToSpawnHere(this, Try_X, Try_Y, Try_Z, Biome, MaxNbOfSuccess);
+ if (newMob)
+ {
+ int WorldX, WorldY, WorldZ;
+ PositionToWorldPosition(Try_X, Try_Y, Try_Z, WorldX, WorldY, WorldZ);
+ double ActualX = WorldX + 0.5;
+ double ActualZ = WorldZ + 0.5;
+ newMob->SetPosition(ActualX, WorldY, ActualZ);
+ LOGD("Spawning %s #%i at %d,%d,%d",newMob->GetClass(),newMob->GetUniqueID(),WorldX, WorldY, WorldZ);
+ NumberOfSuccess++;
+ }
+ }
+
+ NumberOfTries++;
+ }
+ }
+
+}
+
+
+
+
+
+void cChunk::Tick(float a_Dt)
+{
+ BroadcastPendingBlockChanges();
+
+ // Unload the chunk from all clients that have queued unloading:
+ for (cClientHandleList::iterator itr = m_UnloadQuery.begin(), end = m_UnloadQuery.end(); itr != end; ++itr)
+ {
+ (*itr)->SendUnloadChunk(m_PosX, m_PosZ);
+ }
+ m_UnloadQuery.clear();
+
+ // Set all blocks that have been queued for setting later:
+ ProcessQueuedSetBlocks();
+
+ CheckBlocks();
+
+ // Tick simulators:
+ m_World->GetSimulatorManager()->SimulateChunk(a_Dt, m_PosX, m_PosZ, this);
+
+ TickBlocks();
+
+ // Tick all block entities in this chunk:
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ m_IsDirty = (*itr)->Tick(a_Dt, *this) | m_IsDirty;
+ }
+
+ // Tick all entities in this chunk (except mobs):
+ for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
+ {
+ // Mobs are tickes inside MobTick (as we don't have to tick them if they are far away from players)
+ if (!((*itr)->IsMob()))
+ {
+ (*itr)->Tick(a_Dt, *this);
+ }
+ } // for itr - m_Entitites[]
+
+ // Remove all entities that were scheduled for removal:
+ for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();)
+ {
+ if ((*itr)->IsDestroyed())
+ {
+ LOGD("Destroying entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
+ cEntity * ToDelete = *itr;
+ itr = m_Entities.erase(itr);
+ delete ToDelete;
+ continue;
+ }
+ itr++;
+ } // for itr - m_Entitites[]
+
+ // If any entity moved out of the chunk, move it to the neighbor:
+ for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();)
+ {
+ if (
+ ((*itr)->GetChunkX() != m_PosX) ||
+ ((*itr)->GetChunkZ() != m_PosZ)
+ )
+ {
+ MoveEntityToNewChunk(*itr);
+ itr = m_Entities.erase(itr);
+ }
+ else
+ {
+ ++itr;
+ }
+ }
+
+ ApplyWeatherToTop();
+}
+
+
+
+
+
+void cChunk::MoveEntityToNewChunk(cEntity * a_Entity)
+{
+ cChunk * Neighbor = GetNeighborChunk(a_Entity->GetChunkX() * cChunkDef::Width, a_Entity->GetChunkZ() * cChunkDef::Width);
+ if (Neighbor == NULL)
+ {
+ Neighbor = m_ChunkMap->GetChunkNoLoad(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
+ if (Neighbor == NULL)
+ {
+ // TODO: What to do with this?
+ LOGWARNING("%s: Failed to move entity, destination chunk unreachable. Entity lost", __FUNCTION__);
+ return;
+ }
+ }
+
+ ASSERT(Neighbor != this); // Moving into the same chunk? wtf?
+
+ Neighbor->AddEntity(a_Entity);
+
+ class cMover :
+ public cClientDiffCallback
+ {
+ virtual void Removed(cClientHandle * a_Client) override
+ {
+ a_Client->SendDestroyEntity(*m_Entity);
+ }
+
+ virtual void Added(cClientHandle * a_Client) override
+ {
+ m_Entity->SpawnOn(*a_Client);
+ }
+
+ cEntity * m_Entity;
+
+ public:
+ cMover(cEntity * a_Entity) :
+ m_Entity(a_Entity)
+ {}
+ } Mover(a_Entity);
+
+ m_ChunkMap->CompareChunkClients(this, Neighbor, Mover);
+}
+
+
+
+
+
+void cChunk::ProcessQueuedSetBlocks(void)
+{
+ Int64 CurrTick = m_World->GetWorldAge();
+ for (sSetBlockQueueVector::iterator itr = m_SetBlockQueue.begin(); itr != m_SetBlockQueue.end();)
+ {
+ if (itr->m_Tick <= CurrTick)
+ {
+ // Current world age is bigger than/equal to target world age - delay time reached
+ SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
+ itr = m_SetBlockQueue.erase(itr);
+ }
+ else
+ {
+ // Not yet
+ ++itr;
+ continue;
+ }
+ } // for itr - m_SetBlockQueue[]
+}
+
+
+
+
+
+void cChunk::BroadcastPendingBlockChanges(void)
+{
+ if (m_PendingSendBlocks.empty())
+ {
+ return;
+ }
+
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
+ {
+ (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks);
+ }
+ m_PendingSendBlocks.clear();
+}
+
+
+
+
+
+void cChunk::CheckBlocks(void)
+{
+ if (m_ToTickBlocks.size() == 0)
+ {
+ return;
+ }
+ std::vector<unsigned int> ToTickBlocks;
+ std::swap(m_ToTickBlocks, ToTickBlocks);
+
+ for (std::vector<unsigned int>::const_iterator itr = ToTickBlocks.begin(), end = ToTickBlocks.end(); itr != end; ++itr)
+ {
+ unsigned int index = (*itr);
+ Vector3i BlockPos = IndexToCoordinate(index);
+
+ cBlockHandler * Handler = BlockHandler(GetBlock(index));
+ Handler->Check(BlockPos.x, BlockPos.y, BlockPos.z, *this);
+ } // for itr - ToTickBlocks[]
+}
+
+
+
+
+
+void cChunk::TickBlocks(void)
+{
+ // Tick dem blocks
+ // _X: We must limit the random number or else we get a nasty int overflow bug ( http://forum.mc-server.org/showthread.php?tid=457 )
+ int RandomX = m_World->GetTickRandomNumber(0x00ffffff);
+ int RandomY = m_World->GetTickRandomNumber(0x00ffffff);
+ int RandomZ = m_World->GetTickRandomNumber(0x00ffffff);
+ int TickX = m_BlockTickX;
+ int TickY = m_BlockTickY;
+ int TickZ = m_BlockTickZ;
+
+ // This for loop looks disgusting, but it actually does a simple thing - first processes m_BlockTick, then adds random to it
+ // This is so that SetNextBlockTick() works
+ for (int i = 0; i < 50; i++,
+
+ // This weird construct (*2, then /2) is needed,
+ // otherwise the blocktick distribution is too biased towards even coords!
+
+ TickX = (TickX + RandomX) % (Width * 2),
+ TickY = (TickY + RandomY) % (Height * 2),
+ TickZ = (TickZ + RandomZ) % (Width * 2),
+ m_BlockTickX = TickX / 2,
+ m_BlockTickY = TickY / 2,
+ m_BlockTickZ = TickZ / 2
+ )
+ {
+
+ if (m_BlockTickY > cChunkDef::GetHeight(m_HeightMap, m_BlockTickX, m_BlockTickZ))
+ {
+ continue; // It's all air up here
+ }
+
+ unsigned int Index = MakeIndexNoCheck(m_BlockTickX, m_BlockTickY, m_BlockTickZ);
+ cBlockHandler * Handler = BlockHandler(m_BlockTypes[Index]);
+ ASSERT(Handler != NULL); // Happenned on server restart, FS #243
+ Handler->OnUpdate(m_World, m_BlockTickX + m_PosX * Width, m_BlockTickY, m_BlockTickZ + m_PosZ * Width);
+ } // for i - tickblocks
+}
+
+
+
+
+
+void cChunk::ApplyWeatherToTop()
+{
+ if (
+ (m_World->GetTickRandomNumber(100) != 0) ||
+ (
+ (m_World->GetWeather() != eWeather_Rain) &&
+ (m_World->GetWeather() != eWeather_ThunderStorm)
+ )
+ )
+ {
+ // Not the right weather, or not at this tick; bail out
+ return;
+ }
+
+ int X = m_World->GetTickRandomNumber(15);
+ int Z = m_World->GetTickRandomNumber(15);
+ switch (GetBiomeAt(X, Z))
+ {
+ case biTaiga:
+ case biFrozenOcean:
+ case biFrozenRiver:
+ case biIcePlains:
+ case biIceMountains:
+ case biTaigaHills:
+ {
+ // TODO: Check light levels, don't snow over when the BlockLight is higher than (7?)
+ int Height = GetHeight(X, Z);
+ BLOCKTYPE TopBlock = GetBlock(X, Height, Z);
+ NIBBLETYPE TopMeta = GetMeta (X, Height, Z);
+ if (m_World->IsDeepSnowEnabled() && (TopBlock == E_BLOCK_SNOW))
+ {
+ int MaxSize = 7;
+ BLOCKTYPE BlockType[4];
+ NIBBLETYPE BlockMeta[4];
+ UnboundedRelGetBlock(X - 1, Height, Z, BlockType[0], BlockMeta[0]);
+ UnboundedRelGetBlock(X + 1, Height, Z, BlockType[1], BlockMeta[1]);
+ UnboundedRelGetBlock(X, Height, Z - 1, BlockType[2], BlockMeta[2]);
+ UnboundedRelGetBlock(X, Height, Z + 1, BlockType[3], BlockMeta[3]);
+ for (int i = 0; i < 4; i++)
+ {
+ switch (BlockType[i])
+ {
+ case E_BLOCK_AIR:
+ {
+ MaxSize = 0;
+ break;
+ }
+ case E_BLOCK_SNOW:
+ {
+ MaxSize = std::min(BlockMeta[i] + 1, MaxSize);
+ break;
+ }
+ }
+ }
+ if (TopMeta < MaxSize)
+ {
+ FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1);
+ }
+ else if (TopMeta > MaxSize)
+ {
+ FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta - 1);
+ }
+ }
+ else if (g_BlockIsSnowable[TopBlock])
+ {
+ SetBlock(X, Height + 1, Z, E_BLOCK_SNOW, 0);
+ }
+ else if ((TopBlock == E_BLOCK_WATER) || (TopBlock == E_BLOCK_STATIONARY_WATER))
+ {
+ SetBlock(X, Height, Z, E_BLOCK_ICE, 0);
+ }
+ else if (
+ (m_World->IsDeepSnowEnabled()) &&
+ (
+ (TopBlock == E_BLOCK_RED_ROSE) ||
+ (TopBlock == E_BLOCK_YELLOW_FLOWER) ||
+ (TopBlock == E_BLOCK_RED_MUSHROOM) ||
+ (TopBlock == E_BLOCK_BROWN_MUSHROOM)
+ )
+ )
+ {
+ SetBlock(X, Height, Z, E_BLOCK_SNOW, 0);
+ }
+ break;
+ } // case (snowy biomes)
+
+ // TODO: Rainy biomes should check for farmland and cauldrons
+ } // switch (biome)
+}
+
+
+
+
+
+void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom)
+{
+ // Convert the stem BlockType into produce BlockType
+ BLOCKTYPE ProduceType;
+ switch (a_BlockType)
+ {
+ case E_BLOCK_MELON_STEM: ProduceType = E_BLOCK_MELON; break;
+ case E_BLOCK_PUMPKIN_STEM: ProduceType = E_BLOCK_PUMPKIN; break;
+ default:
+ {
+ ASSERT(!"Unhandled blocktype in TickMelonPumpkin()");
+ return;
+ }
+ }
+
+ // Check if there's another melon / pumpkin around that stem, if so, abort:
+ bool IsValid;
+ BLOCKTYPE BlockType[4];
+ NIBBLETYPE BlockMeta; // unused
+ IsValid = UnboundedRelGetBlock(a_RelX + 1, a_RelY, a_RelZ, BlockType[0], BlockMeta);
+ IsValid = IsValid && UnboundedRelGetBlock(a_RelX - 1, a_RelY, a_RelZ, BlockType[1], BlockMeta);
+ IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + 1, BlockType[2], BlockMeta);
+ IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - 1, BlockType[3], BlockMeta);
+ if (
+ !IsValid ||
+ (BlockType[0] == ProduceType) ||
+ (BlockType[1] == ProduceType) ||
+ (BlockType[2] == ProduceType) ||
+ (BlockType[3] == ProduceType)
+ )
+ {
+ // Neighbors not valid or already taken by the same produce
+ return;
+ }
+
+ // Pick a direction in which to place the produce:
+ int x = 0, z = 0;
+ int CheckType = a_TickRandom.randInt(3); // The index to the neighbors array which should be checked for emptiness
+ switch (CheckType)
+ {
+ case 0: x = 1; break;
+ case 1: x = -1; break;
+ case 2: z = 1; break;
+ case 3: z = -1; break;
+ }
+
+ // Check that the block in that direction is empty:
+ switch (BlockType[CheckType])
+ {
+ case E_BLOCK_AIR:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_TALL_GRASS:
+ case E_BLOCK_DEAD_BUSH:
+ {
+ break;
+ }
+ default: return;
+ }
+
+ // Check if there's soil under the neighbor. We already know the neighbors are valid. Place produce if ok
+ BLOCKTYPE Soil;
+ UnboundedRelGetBlock(a_RelX + x, a_RelY - 1, a_RelZ + z, Soil, BlockMeta);
+ switch (Soil)
+ {
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_FARMLAND:
+ {
+ // DEBUG: This is here to catch FS #349 - melons growing over other crops.
+ LOG("Growing melon/pumpkin overwriting %s, growing on %s",
+ ItemTypeToString(BlockType[CheckType]).c_str(),
+ ItemTypeToString(Soil).c_str()
+ );
+ // Place a randomly-facing produce:
+ UnboundedRelFastSetBlock(a_RelX + x, a_RelY, a_RelZ + z, ProduceType, (NIBBLETYPE)(a_TickRandom.randInt(4) % 4));
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks)
+{
+ // Check the total height of the sugarcane blocks here:
+ int Top = a_RelY + 1;
+ while (
+ (Top < cChunkDef::Height) &&
+ (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_SUGARCANE)
+ )
+ {
+ ++Top;
+ }
+ int Bottom = a_RelY - 1;
+ while (
+ (Bottom > 0) &&
+ (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_SUGARCANE)
+ )
+ {
+ --Bottom;
+ }
+
+ // Grow by at most a_NumBlocks, but no more than max height:
+ int ToGrow = std::min(a_NumBlocks, m_World->GetMaxSugarcaneHeight() + 1 - (Top - Bottom));
+ for (int i = 0; i < ToGrow; i++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR))
+ {
+ UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_SUGARCANE, 0);
+ }
+ else
+ {
+ break;
+ }
+ } // for i
+}
+
+
+
+
+
+void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks)
+{
+ // Check the total height of the sugarcane blocks here:
+ int Top = a_RelY + 1;
+ while (
+ (Top < cChunkDef::Height) &&
+ (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_CACTUS)
+ )
+ {
+ ++Top;
+ }
+ int Bottom = a_RelY - 1;
+ while (
+ (Bottom > 0) &&
+ (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_CACTUS)
+ )
+ {
+ --Bottom;
+ }
+
+ // Grow by at most a_NumBlocks, but no more than max height:
+ int ToGrow = std::min(a_NumBlocks, m_World->GetMaxCactusHeight() + 1 - (Top - Bottom));
+ for (int i = 0; i < ToGrow; i++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR))
+ {
+ // TODO: Check the surrounding blocks, if they aren't air, break the cactus block into pickups (and continue breaking blocks above in the next loop iterations)
+ UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_CACTUS, 0);
+ }
+ else
+ {
+ break;
+ }
+ } // for i
+}
+
+
+
+
+
+bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
+{
+ if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
+ {
+ LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
+ return false;
+ }
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ // The chunk is not available, bail out
+ return false;
+ }
+ Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+ return true;
+}
+
+
+
+
+
+bool cChunk::UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const
+{
+ if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
+ {
+ LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
+ return false;
+ }
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ // The chunk is not available, bail out
+ return false;
+ }
+ a_BlockType = Chunk->GetBlock(a_RelX, a_RelY, a_RelZ);
+ return true;
+}
+
+
+
+
+
+bool cChunk::UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const
+{
+ if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
+ {
+ LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
+ return false;
+ }
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ // The chunk is not available, bail out
+ return false;
+ }
+ a_BlockMeta = Chunk->GetMeta(a_RelX, a_RelY, a_RelZ);
+ return true;
+}
+
+
+
+
+
+bool cChunk::UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockBlockLight) const
+{
+ if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
+ {
+ LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
+ return false;
+ }
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ // The chunk is not available, bail out
+ return false;
+ }
+ a_BlockBlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
+ return true;
+}
+
+
+
+
+
+bool cChunk::UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockSkyLight) const
+{
+ if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
+ {
+ LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
+ return false;
+ }
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ // The chunk is not available, bail out
+ return false;
+ }
+ a_BlockSkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
+ return true;
+}
+
+
+
+
+
+bool cChunk::UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const
+{
+ if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
+ {
+ LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY);
+ return false;
+ }
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ // The chunk is not available, bail out
+ return false;
+ }
+ int idx = Chunk->MakeIndex(a_RelX, a_RelY, a_RelZ);
+ a_BlockLight = Chunk->GetBlockLight(idx);
+ a_SkyLight = Chunk->GetSkyLight(idx);
+ return true;
+}
+
+
+
+
+
+bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ if ((a_RelY < 0) || (a_RelY > cChunkDef::Height))
+ {
+ LOGWARNING("UnboundedRelSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY);
+ return false;
+ }
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ // The chunk is not available, bail out
+ return false;
+ }
+ Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+ return true;
+}
+
+
+
+
+
+bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ if ((a_RelY < 0) || (a_RelY > cChunkDef::Height))
+ {
+ LOGWARNING("UnboundedRelFastSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY);
+ return false;
+ }
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ // The chunk is not available, bail out
+ return false;
+ }
+ Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+ return true;
+}
+
+
+
+
+
+void cChunk::UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ)
+{
+ if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height))
+ {
+ // Outside of chunkmap
+ return;
+ }
+ cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ Chunk->QueueTickBlock(a_RelX, a_RelY, a_RelZ);
+ }
+}
+
+
+
+
+
+int cChunk::GetHeight(int a_X, int a_Z)
+{
+ ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width));
+
+ if ((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width))
+ {
+ return m_HeightMap[a_X + a_Z * Width];
+ }
+ return 0;
+}
+
+
+
+
+
+void cChunk::CreateBlockEntities(void)
+{
+ for (int x = 0; x < Width; x++)
+ {
+ for (int z = 0; z < Width; z++)
+ {
+ for (int y = 0; y < Height; y++)
+ {
+ BLOCKTYPE BlockType = cChunkDef::GetBlock(m_BlockTypes, x, y, z);
+ switch (BlockType)
+ {
+ case E_BLOCK_CHEST:
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER:
+ case E_BLOCK_LIT_FURNACE:
+ case E_BLOCK_FURNACE:
+ case E_BLOCK_HOPPER:
+ case E_BLOCK_SIGN_POST:
+ case E_BLOCK_WALLSIGN:
+ case E_BLOCK_NOTE_BLOCK:
+ case E_BLOCK_JUKEBOX:
+ {
+ if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width))
+ {
+ m_BlockEntities.push_back(cBlockEntity::CreateByBlockType(
+ BlockType, GetMeta(x, y, z),
+ x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World
+ ));
+ }
+ break;
+ }
+ } // switch (BlockType)
+ } // for y
+ } // for z
+ } // for x
+}
+
+
+
+
+
+void cChunk::WakeUpSimulators(void)
+{
+ cSimulator * WaterSimulator = m_World->GetWaterSimulator();
+ cSimulator * LavaSimulator = m_World->GetLavaSimulator();
+ int BaseX = m_PosX * cChunkDef::Width;
+ int BaseZ = m_PosZ * cChunkDef::Width;
+ for (int x = 0; x < Width; x++)
+ {
+ int BlockX = x + BaseX;
+ for (int z = 0; z < Width; z++)
+ {
+ int BlockZ = z + BaseZ;
+ for (int y = GetHeight(x, z); y >= 0; y--)
+ {
+ switch (cChunkDef::GetBlock(m_BlockTypes, x, y, z))
+ {
+ case E_BLOCK_WATER:
+ {
+ WaterSimulator->AddBlock(BlockX, y, BlockZ, this);
+ break;
+ }
+ case E_BLOCK_LAVA:
+ {
+ LavaSimulator->AddBlock(BlockX, y, BlockZ, this);
+ break;
+ }
+ } // switch (BlockType)
+ } // for y
+ } // for z
+ } // for x
+}
+
+
+
+
+
+void cChunk::CalculateHeightmap()
+{
+ for (int x = 0; x < Width; x++)
+ {
+ for (int z = 0; z < Width; z++)
+ {
+ for (int y = Height - 1; y > -1; y--)
+ {
+ int index = MakeIndex( x, y, z );
+ if (m_BlockTypes[index] != E_BLOCK_AIR)
+ {
+ m_HeightMap[x + z * Width] = (unsigned char)y;
+ break;
+ }
+ } // for y
+ } // for z
+ } // for x
+}
+
+
+
+
+
+void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+
+ const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
+
+ // Tick this block and its neighbors:
+ m_ToTickBlocks.push_back(index);
+ QueueTickBlockNeighbors(a_RelX, a_RelY, a_RelZ);
+
+ // If there was a block entity, remove it:
+ Vector3i WorldPos = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ);
+ cBlockEntity * BlockEntity = GetBlockEntity(WorldPos);
+ if (BlockEntity != NULL)
+ {
+ BlockEntity->Destroy();
+ RemoveBlockEntity(BlockEntity);
+ delete BlockEntity;
+ }
+
+ // If the new block is a block entity, create the entity object:
+ switch (a_BlockType)
+ {
+ case E_BLOCK_CHEST:
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER:
+ case E_BLOCK_LIT_FURNACE:
+ case E_BLOCK_FURNACE:
+ case E_BLOCK_HOPPER:
+ case E_BLOCK_SIGN_POST:
+ case E_BLOCK_WALLSIGN:
+ case E_BLOCK_NOTE_BLOCK:
+ case E_BLOCK_JUKEBOX:
+ {
+ AddBlockEntity(cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, WorldPos.x, WorldPos.y, WorldPos.z, m_World));
+ break;
+ }
+ } // switch (a_BlockType)
+}
+
+
+
+
+
+void cChunk::QueueSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick)
+{
+ m_SetBlockQueue.push_back(sSetBlockQueueItem(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta, a_Tick));
+}
+
+
+
+
+
+void cChunk::QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ)
+{
+ ASSERT (
+ (a_RelX >= 0) && (a_RelX < Width) &&
+ (a_RelY >= 0) && (a_RelY < Height) &&
+ (a_RelZ >= 0) && (a_RelZ < Width)
+ ); // Coords need to be valid
+
+ if (!IsValid())
+ {
+ return;
+ }
+
+ m_ToTickBlocks.push_back(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ));
+}
+
+
+
+
+
+void cChunk::QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ)
+{
+ struct
+ {
+ int x, y, z;
+ }
+ Coords[] =
+ {
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 1, 0},
+ { 0, -1, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ UnboundedQueueTickBlock(a_RelX + Coords[i].x, a_RelY + Coords[i].y, a_RelZ + Coords[i].z);
+ } // for i - Coords[]
+}
+
+
+
+
+
+void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
+{
+ ASSERT(!((a_RelX < 0) || (a_RelX >= Width) || (a_RelY < 0) || (a_RelY >= Height) || (a_RelZ < 0) || (a_RelZ >= Width)));
+
+ ASSERT(IsValid());
+
+ const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
+ const BLOCKTYPE OldBlockType = cChunkDef::GetBlock(m_BlockTypes, index);
+ const BLOCKTYPE OldBlockMeta = GetNibble(m_BlockMeta, index);
+ if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta))
+ {
+ return;
+ }
+
+ MarkDirty();
+
+ m_BlockTypes[index] = a_BlockType;
+
+ // The client doesn't need to distinguish between stationary and nonstationary fluids:
+ if (
+ (OldBlockMeta != a_BlockMeta) || // Different meta always gets sent to the client
+ !(
+ ((OldBlockType == E_BLOCK_STATIONARY_WATER) && (a_BlockType == E_BLOCK_WATER)) || // Replacing stationary water with water
+ ((OldBlockType == E_BLOCK_WATER) && (a_BlockType == E_BLOCK_STATIONARY_WATER)) || // Replacing water with stationary water
+ ((OldBlockType == E_BLOCK_STATIONARY_LAVA) && (a_BlockType == E_BLOCK_LAVA)) || // Replacing stationary water with water
+ ((OldBlockType == E_BLOCK_LAVA) && (a_BlockType == E_BLOCK_STATIONARY_LAVA)) // Replacing water with stationary water
+ )
+ )
+ {
+ m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta));
+ }
+
+ SetNibble(m_BlockMeta, index, a_BlockMeta);
+
+ // ONLY recalculate lighting if it's necessary!
+ if(
+ (g_BlockLightValue[OldBlockType ] != g_BlockLightValue[a_BlockType]) ||
+ (g_BlockSpreadLightFalloff[OldBlockType] != g_BlockSpreadLightFalloff[a_BlockType]) ||
+ (g_BlockTransparent[OldBlockType] != g_BlockTransparent[a_BlockType])
+ )
+ {
+ m_IsLightValid = false;
+ }
+
+ // Update heightmap, if needed:
+ if (a_RelY >= m_HeightMap[a_RelX + a_RelZ * Width])
+ {
+ if (a_BlockType != E_BLOCK_AIR)
+ {
+ m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)a_RelY;
+ }
+ else
+ {
+ for (int y = a_RelY - 1; y > 0; --y)
+ {
+ if (m_BlockTypes[MakeIndexNoCheck(a_RelX, y, a_RelZ)] != E_BLOCK_AIR)
+ {
+ m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)y;
+ break;
+ }
+ } // for y - column in m_BlockData
+ }
+ }
+}
+
+
+
+
+
+void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client)
+{
+ // The coords must be valid, because the upper level already does chunk lookup. No need to check them again.
+ // There's an debug-time assert in MakeIndexNoCheck anyway
+ unsigned int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
+
+ if (a_Client == NULL)
+ {
+ // Queue the block for all clients in the chunk (will be sent in Tick())
+ m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(index), GetMeta(index)));
+ return;
+ }
+
+ Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ);
+ a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(index), GetMeta(index));
+
+ // FS #268 - if a BlockEntity digging is cancelled by a plugin, the entire block entity must be re-sent to the client:
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr)
+ {
+ if (((*itr)->GetPosX() == wp.x) && ((*itr)->GetPosY() == wp.y) && ((*itr)->GetPosZ() == wp.z))
+ {
+ (*itr)->SendTo(*a_Client);
+ }
+ } // for itr - m_BlockEntities
+}
+
+
+
+
+
+void cChunk::AddBlockEntity(cBlockEntity * a_BlockEntity)
+{
+ MarkDirty();
+ m_BlockEntities.push_back(a_BlockEntity);
+}
+
+
+
+
+
+cBlockEntity * cChunk::GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ if (
+ ((*itr)->GetPosX() == a_BlockX) &&
+ ((*itr)->GetPosY() == a_BlockY) &&
+ ((*itr)->GetPosZ() == a_BlockZ)
+ )
+ {
+ return *itr;
+ }
+ } // for itr - m_BlockEntities[]
+
+ return NULL;
+}
+
+
+
+
+
+void cChunk::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z)
+{
+ cBlockEntity * be = GetBlockEntity(a_X, a_Y, a_Z);
+ if (be != NULL)
+ {
+ be->UsedBy(a_Player);
+ }
+}
+
+
+
+
+
+void cChunk::CollectPickupsByPlayer(cPlayer * a_Player)
+{
+ double PosX = a_Player->GetPosX();
+ double PosY = a_Player->GetPosY();
+ double PosZ = a_Player->GetPosZ();
+
+ for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
+ {
+ if ((!(*itr)->IsPickup()) && (!(*itr)->IsProjectile()))
+ {
+ continue; // Only pickups and projectiles
+ }
+ float DiffX = (float)((*itr)->GetPosX() - PosX );
+ float DiffY = (float)((*itr)->GetPosY() - PosY );
+ float DiffZ = (float)((*itr)->GetPosZ() - PosZ );
+ float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ;
+ if (SqrDist < 1.5f * 1.5f) // 1.5 block
+ {
+ /*
+ LOG("Pickup %d being collected by player \"%s\", distance %f",
+ (*itr)->GetUniqueID(), a_Player->GetName().c_str(), SqrDist
+ );
+ */
+ MarkDirty();
+ if ((*itr)->IsPickup())
+ {
+ (reinterpret_cast<cPickup *>(*itr))->CollectedBy(a_Player);
+ }
+ else
+ {
+ (reinterpret_cast<cProjectileEntity *>(*itr))->CollectedBy(a_Player);
+ }
+ }
+ else if (SqrDist < 5 * 5)
+ {
+ /*
+ LOG("Pickup %d close to player \"%s\", but still too far to collect: %f",
+ (*itr)->GetUniqueID(), a_Player->GetName().c_str(), SqrDist
+ );
+ */
+ }
+ }
+}
+
+
+
+
+
+bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
+{
+ // Also sends update packets to all clients in the chunk
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ if (
+ ((*itr)->GetPosX() == a_PosX) &&
+ ((*itr)->GetPosY() == a_PosY) &&
+ ((*itr)->GetPosZ() == a_PosZ) &&
+ (
+ ((*itr)->GetBlockType() == E_BLOCK_WALLSIGN) ||
+ ((*itr)->GetBlockType() == E_BLOCK_SIGN_POST)
+ )
+ )
+ {
+ MarkDirty();
+ (reinterpret_cast<cSignEntity *>(*itr))->SetLines(a_Line1, a_Line2, a_Line3, a_Line4);
+ m_World->BroadcastBlockEntity(a_PosX, a_PosY, a_PosZ);
+ return true;
+ }
+ } // for itr - m_BlockEntities[]
+ return false;
+}
+
+
+
+
+
+void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity )
+{
+ MarkDirty();
+ m_BlockEntities.remove(a_BlockEntity);
+}
+
+
+
+
+
+bool cChunk::AddClient(cClientHandle* a_Client)
+{
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
+ {
+ if (a_Client == *itr)
+ {
+ // Already there, nothing needed
+ return false;
+ }
+ }
+ m_LoadedByClient.push_back( a_Client );
+
+ for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr )
+ {
+ LOGD("cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \"%s\"", (*itr)->GetUniqueID(), (*itr)->GetClass(), m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str());
+ (*itr)->SpawnOn(*a_Client);
+ }
+ return true;
+}
+
+
+
+
+
+void cChunk::RemoveClient( cClientHandle* a_Client )
+{
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
+ {
+ if (*itr != a_Client)
+ {
+ continue;
+ }
+
+ m_LoadedByClient.erase(itr);
+
+ if (!a_Client->IsDestroyed())
+ {
+ for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr )
+ {
+ LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"", m_PosX, m_PosZ, (*itr)->GetUniqueID(), a_Client->GetUsername().c_str() );
+ a_Client->SendDestroyEntity(*(*itr));
+ }
+ }
+ return;
+ } // for itr - m_LoadedByClient[]
+}
+
+
+
+
+
+bool cChunk::HasClient( cClientHandle* a_Client )
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
+ {
+ if ((*itr) == a_Client)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cChunk::HasAnyClients(void)
+{
+ return !m_LoadedByClient.empty();
+}
+
+
+
+
+
+void cChunk::AddEntity(cEntity * a_Entity)
+{
+ if (!a_Entity->IsPlayer())
+ {
+ MarkDirty();
+ }
+
+ ASSERT(std::find(m_Entities.begin(), m_Entities.end(), a_Entity) == m_Entities.end()); // Not there already
+
+ m_Entities.push_back(a_Entity);
+}
+
+
+
+
+
+void cChunk::RemoveEntity(cEntity * a_Entity)
+{
+ size_t SizeBefore = m_Entities.size();
+ m_Entities.remove(a_Entity);
+ size_t SizeAfter = m_Entities.size();
+
+ if (SizeBefore != SizeAfter)
+ {
+ // Mark as dirty if it was a server-generated entity:
+ if (!a_Entity->IsPlayer())
+ {
+ MarkDirty();
+ }
+ }
+}
+
+
+
+
+
+bool cChunk::HasEntity(int a_EntityID)
+{
+ for (cEntityList::const_iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr)
+ {
+ if ((*itr)->GetUniqueID() == a_EntityID)
+ {
+ return true;
+ }
+ } // for itr - m_Entities[]
+ return false;
+}
+
+
+
+
+
+bool cChunk::ForEachEntity(cEntityCallback & a_Callback)
+{
+ // The entity list is locked by the parent chunkmap's CS
+ for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (a_Callback.Item(*itr))
+ {
+ return false;
+ }
+ } // for itr - m_Entitites[]
+ return true;
+}
+
+
+
+
+
+bool cChunk::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult)
+{
+ // The entity list is locked by the parent chunkmap's CS
+ for (cEntityList::iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr)
+ {
+ if ((*itr)->GetUniqueID() == a_EntityID)
+ {
+ a_CallbackResult = a_Callback.Item(*itr);
+ return true;
+ }
+ } // for itr - m_Entitites[]
+ return false;
+}
+
+
+
+
+
+bool cChunk::ForEachBlockEntity(cBlockEntityCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (a_Callback.Item(*itr))
+ {
+ return false;
+ }
+ } // for itr - m_BlockEntitites[]
+ return true;
+}
+
+
+
+
+
+bool cChunk::ForEachChest(cChestCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if ((*itr)->GetBlockType() != E_BLOCK_CHEST)
+ {
+ continue;
+ }
+ if (a_Callback.Item((cChestEntity *)*itr))
+ {
+ return false;
+ }
+ } // for itr - m_BlockEntitites[]
+ return true;
+}
+
+
+
+
+
+bool cChunk::ForEachDispenser(cDispenserCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if ((*itr)->GetBlockType() != E_BLOCK_DISPENSER)
+ {
+ continue;
+ }
+ if (a_Callback.Item((cDispenserEntity *)*itr))
+ {
+ return false;
+ }
+ } // for itr - m_BlockEntitites[]
+ return true;
+}
+
+
+
+
+
+bool cChunk::ForEachDropper(cDropperCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if ((*itr)->GetBlockType() != E_BLOCK_DROPPER)
+ {
+ continue;
+ }
+ if (a_Callback.Item((cDropperEntity *)*itr))
+ {
+ return false;
+ }
+ } // for itr - m_BlockEntitites[]
+ return true;
+}
+
+
+
+
+
+bool cChunk::ForEachDropSpenser(cDropSpenserCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (((*itr)->GetBlockType() != E_BLOCK_DISPENSER) && ((*itr)->GetBlockType() != E_BLOCK_DROPPER))
+ {
+ continue;
+ }
+ if (a_Callback.Item((cDropSpenserEntity *)*itr))
+ {
+ return false;
+ }
+ } // for itr - m_BlockEntitites[]
+ return true;
+}
+
+
+
+
+
+bool cChunk::ForEachFurnace(cFurnaceCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ switch ((*itr)->GetBlockType())
+ {
+ case E_BLOCK_FURNACE:
+ case E_BLOCK_LIT_FURNACE:
+ {
+ break;
+ }
+ default:
+ {
+ continue;
+ }
+ }
+ if (a_Callback.Item((cFurnaceEntity *)*itr))
+ {
+ return false;
+ }
+ } // for itr - m_BlockEntitites[]
+ return true;
+}
+
+
+
+
+
+bool cChunk::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ))
+ {
+ continue;
+ }
+
+ if (a_Callback.Item(*itr))
+ {
+ return false;
+ }
+ return true;
+ } // for itr - m_BlockEntitites[]
+
+ // Not found:
+ return false;
+}
+
+
+
+
+
+bool cChunk::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ))
+ {
+ continue;
+ }
+ if ((*itr)->GetBlockType() != E_BLOCK_CHEST)
+ {
+ // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
+ return false;
+ }
+
+ // The correct block entity is here
+ if (a_Callback.Item((cChestEntity *)*itr))
+ {
+ return false;
+ }
+ return true;
+ } // for itr - m_BlockEntitites[]
+
+ // Not found:
+ return false;
+}
+
+
+
+
+
+bool cChunk::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ))
+ {
+ continue;
+ }
+ if ((*itr)->GetBlockType() != E_BLOCK_DISPENSER)
+ {
+ // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
+ return false;
+ }
+
+ // The correct block entity is here
+ if (a_Callback.Item((cDispenserEntity *)*itr))
+ {
+ return false;
+ }
+ return true;
+ } // for itr - m_BlockEntitites[]
+
+ // Not found:
+ return false;
+}
+
+
+
+
+
+bool cChunk::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ))
+ {
+ continue;
+ }
+ if ((*itr)->GetBlockType() != E_BLOCK_DROPPER)
+ {
+ // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
+ return false;
+ }
+
+ // The correct block entity is here
+ if (a_Callback.Item((cDropperEntity *)*itr))
+ {
+ return false;
+ }
+ return true;
+ } // for itr - m_BlockEntitites[]
+
+ // Not found:
+ return false;
+}
+
+
+
+
+
+bool cChunk::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ))
+ {
+ continue;
+ }
+ if (((*itr)->GetBlockType() != E_BLOCK_DISPENSER) && ((*itr)->GetBlockType() != E_BLOCK_DROPPER))
+ {
+ // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
+ return false;
+ }
+
+ // The correct block entity is here
+ if (a_Callback.Item((cDropSpenserEntity *)*itr))
+ {
+ return false;
+ }
+ return true;
+ } // for itr - m_BlockEntitites[]
+
+ // Not found:
+ return false;
+}
+
+
+
+
+
+bool cChunk::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ))
+ {
+ continue;
+ }
+ switch ((*itr)->GetBlockType())
+ {
+ case E_BLOCK_FURNACE:
+ case E_BLOCK_LIT_FURNACE:
+ {
+ break;
+ }
+ default:
+ {
+ // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
+ return false;
+ }
+ } // switch (BlockType)
+
+ // The correct block entity is here,
+ if (a_Callback.Item((cFurnaceEntity *)*itr))
+ {
+ return false;
+ }
+ return true;
+ } // for itr - m_BlockEntitites[]
+
+ // Not found:
+ return false;
+}
+
+
+
+
+
+bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ))
+ {
+ continue;
+ }
+ switch ((*itr)->GetBlockType())
+ {
+ case E_BLOCK_WALLSIGN:
+ case E_BLOCK_SIGN_POST:
+ {
+ a_Line1 = ((cSignEntity *)*itr)->GetLine(0);
+ a_Line2 = ((cSignEntity *)*itr)->GetLine(1);
+ a_Line3 = ((cSignEntity *)*itr)->GetLine(2);
+ a_Line4 = ((cSignEntity *)*itr)->GetLine(3);
+ return true;
+ }
+ } // switch (BlockType)
+
+ // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
+ return false;
+ } // for itr - m_BlockEntitites[]
+
+ // Not found:
+ return false;
+}
+
+
+
+
+
+BLOCKTYPE cChunk::GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ if (
+ (a_RelX < 0) || (a_RelX >= Width) ||
+ (a_RelY < 0) || (a_RelY >= Height) ||
+ (a_RelZ < 0) || (a_RelZ >= Width)
+ )
+ {
+ ASSERT(!"GetBlock(x, y, z) out of bounds!");
+ return 0; // Clip
+ }
+
+ return m_BlockTypes[MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ)];
+}
+
+
+
+
+
+BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
+{
+ if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks))
+ {
+ ASSERT(!"GetBlock(idx) out of bounds!");
+ return 0;
+ }
+
+ return m_BlockTypes[ a_BlockIdx ];
+}
+
+
+
+
+
+void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
+{
+ int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
+ a_BlockType = cChunkDef::GetBlock (m_BlockTypes, a_RelX, a_RelY, a_RelZ);
+ a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+void cChunk::GetBlockInfo(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight)
+{
+ int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
+ a_BlockType = cChunkDef::GetBlock (m_BlockTypes, Idx);
+ a_Meta = cChunkDef::GetNibble(m_BlockMeta, Idx);
+ a_SkyLight = cChunkDef::GetNibble(m_BlockSkyLight, Idx);
+ a_BlockLight = cChunkDef::GetNibble(m_BlockLight, Idx);
+}
+
+
+
+
+
+cChunk * cChunk::GetNeighborChunk(int a_BlockX, int a_BlockZ)
+{
+ // Convert coords to relative, then call the relative version:
+ a_BlockX -= m_PosX * cChunkDef::Width;
+ a_BlockZ -= m_PosZ * cChunkDef::Width;
+ return GetRelNeighborChunk(a_BlockX, a_BlockZ);
+}
+
+
+
+
+
+cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ)
+{
+ bool ReturnThis = true;
+ if (a_RelX < 0)
+ {
+ if (m_NeighborXM != NULL)
+ {
+ cChunk * Candidate = m_NeighborXM->GetRelNeighborChunk(a_RelX + cChunkDef::Width, a_RelZ);
+ if (Candidate != NULL)
+ {
+ return Candidate;
+ }
+ }
+ // Going X first failed, but if the request is crossing Z as well, let's try the Z first later on.
+ ReturnThis = false;
+ }
+ else if (a_RelX >= cChunkDef::Width)
+ {
+ if (m_NeighborXP != NULL)
+ {
+ cChunk * Candidate = m_NeighborXP->GetRelNeighborChunk(a_RelX - cChunkDef::Width, a_RelZ);
+ if (Candidate != NULL)
+ {
+ return Candidate;
+ }
+ }
+ // Going X first failed, but if the request is crossing Z as well, let's try the Z first later on.
+ ReturnThis = false;
+ }
+
+ if (a_RelZ < 0)
+ {
+ if (m_NeighborZM != NULL)
+ {
+ return m_NeighborZM->GetRelNeighborChunk(a_RelX, a_RelZ + cChunkDef::Width);
+ // For requests crossing both X and Z, the X-first way has been already tried
+ }
+ return NULL;
+ }
+ else if (a_RelZ >= cChunkDef::Width)
+ {
+ if (m_NeighborZP != NULL)
+ {
+ return m_NeighborZP->GetRelNeighborChunk(a_RelX, a_RelZ - cChunkDef::Width);
+ // For requests crossing both X and Z, the X-first way has been already tried
+ }
+ return NULL;
+ }
+
+ return (ReturnThis ? this : NULL);
+}
+
+
+
+
+
+cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const
+{
+ cChunk * ToReturn = const_cast<cChunk *>(this);
+
+ // The most common case: inside this chunk:
+ if (
+ (a_RelX >= 0) && (a_RelX < Width) &&
+ (a_RelZ >= 0) && (a_RelZ < Width)
+ )
+ {
+ return ToReturn;
+ }
+
+ // Request for a different chunk, calculate chunk offset:
+ int RelX = a_RelX; // Make a local copy of the coords (faster access)
+ int RelZ = a_RelZ;
+ while ((RelX >= Width) && (ToReturn != NULL))
+ {
+ RelX -= Width;
+ ToReturn = ToReturn->m_NeighborXP;
+ }
+ while ((RelX < 0) && (ToReturn != NULL))
+ {
+ RelX += Width;
+ ToReturn = ToReturn->m_NeighborXM;
+ }
+ while ((RelZ >= Width) && (ToReturn != NULL))
+ {
+ RelZ -= Width;
+ ToReturn = ToReturn->m_NeighborZP;
+ }
+ while ((RelZ < 0) && (ToReturn != NULL))
+ {
+ RelZ += Width;
+ ToReturn = ToReturn->m_NeighborZM;
+ }
+ if (ToReturn != NULL)
+ {
+ a_RelX = RelX;
+ a_RelZ = RelZ;
+ return ToReturn;
+ }
+
+ // The chunk cannot be walked through neighbors, find it through the chunkmap:
+ int AbsX = a_RelX + m_PosX * Width;
+ int AbsZ = a_RelZ + m_PosZ * Width;
+ int DstChunkX, DstChunkZ;
+ BlockToChunk(AbsX, AbsZ, DstChunkX, DstChunkZ);
+ ToReturn = m_ChunkMap->FindChunk(DstChunkX, DstChunkZ);
+ a_RelX = AbsX - DstChunkX * Width;
+ a_RelZ = AbsZ - DstChunkZ * Width;
+ return ToReturn;
+}
+
+
+
+
+
+void cChunk::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ (*itr)->SendAttachEntity(a_Entity, a_Vehicle);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendBlockBreakAnim(a_entityID, a_blockX, a_blockY, a_blockZ, a_stage);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude)
+{
+ // We can operate on entity pointers, we're inside the ChunkMap's CS lock which guards the list
+ cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ);
+ if (Entity == NULL)
+ {
+ return;
+ }
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ Entity->SendTo(*(*itr));
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastChunkData(cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendChunkData(m_PosX, m_PosZ, a_Serializer);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendCollectPickup(a_Pickup, a_Player);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendDestroyEntity(a_Entity);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendEntityHeadLook(a_Entity);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendEntityLook(a_Entity);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendEntityMetadata(a_Entity);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendEntityStatus(a_Entity, a_Status);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendEntityVelocity(a_Entity);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendPlayerAnimation(a_Player, a_Animation);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ a_Entity.SpawnOn(*(*itr));
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude)
+{
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ if (*itr == a_Exclude)
+ {
+ continue;
+ }
+ (*itr)->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
+{
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr )
+ {
+ (*itr)->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
+ } // for itr - LoadedByClient[]
+}
+
+
+
+
+
+void cChunk::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client)
+{
+ cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ);
+ if (Entity == NULL)
+ {
+ return;
+ }
+ Entity->SendTo(a_Client);
+}
+
+
+
+
+
+void cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ)
+{
+ a_BlockY = a_RelY;
+ a_BlockX = m_PosX * Width + a_RelX;
+ a_BlockZ = m_PosZ * Width + a_RelZ;
+}
+
+
+
+
+
+Vector3i cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ)
+{
+ return Vector3i(m_PosX * Width + a_RelX, m_PosY * Height + a_RelY, m_PosZ * Width + a_RelZ);
+}
+
+
+
+
+
+NIBBLETYPE cChunk::GetTimeAlteredLight(NIBBLETYPE a_Skylight) const
+{
+ a_Skylight -= m_World->GetSkyDarkness();
+ // Because NIBBLETYPE is unsigned, we clamp it to 0 .. 15 by checking for values above 15
+ return (a_Skylight < 16)? a_Skylight : 0;
+}
+
+
+
+
+
+#if !C_CHUNK_USE_INLINE
+# include "cChunk.inl.h"
+#endif
+
+
+
+
diff --git a/src/Chunk.h b/src/Chunk.h
new file mode 100644
index 000000000..895b407a3
--- /dev/null
+++ b/src/Chunk.h
@@ -0,0 +1,484 @@
+
+#pragma once
+
+#include "Entities/Entity.h"
+#include "ChunkDef.h"
+
+#include "Simulator/FireSimulator.h"
+#include "Simulator/SandSimulator.h"
+#include "Simulator/RedstoneSimulator.h"
+
+
+
+
+
+#define C_CHUNK_USE_INLINE 1
+
+// Do not touch
+#if C_CHUNK_USE_INLINE
+ #define __C_CHUNK_INLINE__ inline
+#else
+ #define __C_CHUNK_INLINE__
+#endif
+
+
+
+
+
+namespace Json
+{
+ class Value;
+};
+
+
+
+
+
+class cWorld;
+class cFurnaceEntity;
+class cClientHandle;
+class cServer;
+class MTRand;
+class cPlayer;
+class cChunkMap;
+class cChestEntity;
+class cDispenserEntity;
+class cFurnaceEntity;
+class cBlockArea;
+class cPawn;
+class cPickup;
+class cChunkDataSerializer;
+class cBlockArea;
+class cFluidSimulatorData;
+class cMobCensus;
+class cMobSpawner;
+
+typedef std::list<cClientHandle *> cClientHandleList;
+typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cChestEntity> cChestCallback;
+typedef cItemCallback<cDispenserEntity> cDispenserCallback;
+typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
+
+
+
+
+// This class is not to be used directly
+// Instead, call actions on cChunkMap (such as cChunkMap::SetBlock() etc.)
+class cChunk :
+ public cChunkDef // The inheritance is "misused" here only to inherit the functions and constants defined in cChunkDef
+{
+public:
+ cChunk(
+ int a_ChunkX, int a_ChunkY, int a_ChunkZ, // Chunk coords
+ cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects
+ cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP // Neighbor chunks
+ );
+ ~cChunk();
+
+ bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated)
+ void SetValid(void); // Also wakes up any calls to cChunkMap::GetHeight()
+ void MarkRegenerating(void); // Marks all clients attached to this chunk as wanting this chunk
+ bool IsDirty(void) const {return m_IsDirty; } // Returns true if the chunk has changed since it was last saved
+ bool HasLoadFailed(void) const {return m_HasLoadFailed; } // Returns true if the chunk failed to load and hasn't been generated since then
+ bool CanUnload(void);
+
+ bool IsLightValid(void) const {return m_IsLightValid; }
+
+ /*
+ To save a chunk, the WSSchema must:
+ 1. Mark the chunk as being saved (MarkSaving() )
+ 2. Get the chunk's data using GetAllData()
+ 3. Mark the chunk as saved (MarkSaved() )
+ If anywhere inside this sequence another thread mmodifies the chunk, the chunk will not get marked as saved in MarkSaved()
+ */
+ void MarkSaving(void); // Marks the chunk as being saved.
+ void MarkSaved(void); // Marks the chunk as saved, if it didn't change from the last call to MarkSaving()
+ void MarkLoaded(void); // Marks the chunk as freshly loaded. Fails if the chunk is already valid
+ void MarkLoadFailed(void); // Marks the chunk as failed to load. Ignored is the chunk is already valid
+
+ /// Gets all chunk data, calls the a_Callback's methods for each data type
+ void GetAllData(cChunkDataCallback & a_Callback);
+
+ /// Sets all chunk data
+ void SetAllData(
+ const BLOCKTYPE * a_BlockTypes,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
+ const cChunkDef::HeightMap * a_HeightMap,
+ const cChunkDef::BiomeMap & a_BiomeMap,
+ cBlockEntityList & a_BlockEntities
+ );
+
+ void SetLight(
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
+ );
+
+ /// Copies m_BlockData into a_BlockTypes, only the block types
+ void GetBlockTypes(BLOCKTYPE * a_BlockTypes);
+
+ /// Writes the specified cBlockArea at the coords specified. Note that the coords may extend beyond the chunk!
+ void WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes);
+
+ /// Returns true if there is a block entity at the coords specified
+ bool HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Sets or resets the internal flag that prevents chunk from being unloaded
+ void Stay(bool a_Stay = true);
+
+ /// Recence all mobs proximities to players in order to know what to do with them
+ void CollectMobCensus(cMobCensus& toFill);
+
+ /// Try to Spawn Monsters inside chunk
+ void SpawnMobs(cMobSpawner& a_MobSpawner);
+
+ void Tick(float a_Dt);
+
+ int GetPosX(void) const { return m_PosX; }
+ int GetPosY(void) const { return m_PosY; }
+ int GetPosZ(void) const { return m_PosZ; }
+
+ cWorld * GetWorld(void) const { return m_World; }
+
+ void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta );
+ // SetBlock() does a lot of work (heightmap, tickblocks, blockentities) so a BlockIdx version doesn't make sense
+ void SetBlock( const Vector3i & a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { SetBlock( a_RelBlockPos.x, a_RelBlockPos.y, a_RelBlockPos.z, a_BlockType, a_BlockMeta ); }
+
+ /// Queues a block change till the specified world tick
+ void QueueSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick);
+
+ /// Queues block for ticking (m_ToTickQueue)
+ void QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ);
+
+ /// Queues all 6 neighbors of the specified block for ticking (m_ToTickQueue). If any are outside the chunk, relays the checking to the proper neighboring chunk
+ void QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ);
+
+ void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc.
+ BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const;
+ BLOCKTYPE GetBlock(int a_BlockIdx) const;
+ void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
+ void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);
+
+ /** Returns the chunk into which the specified block belongs, by walking the neighbors.
+ Will return self if appropriate. Returns NULL if not reachable through neighbors.
+ */
+ cChunk * GetNeighborChunk(int a_BlockX, int a_BlockZ);
+
+ /**
+ Returns the chunk into which the relatively-specified block belongs, by walking the neighbors.
+ Will return self if appropriate. Returns NULL if not reachable through neighbors.
+ */
+ cChunk * GetRelNeighborChunk(int a_RelX, int a_RelZ);
+
+ /**
+ Returns the chunk into which the relatively-specified block belongs.
+ Also modifies the relative coords from this-relative to return-relative.
+ Will return self if appropriate.
+ Will try walking the neighbors first; if that fails, will query the chunkmap
+ */
+ cChunk * GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const;
+
+ EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); }
+
+ void CollectPickupsByPlayer(cPlayer * a_Player);
+
+ /// Sets the sign text. Returns true if successful. Also sends update packets to all clients in the chunk
+ bool SetSignLines(int a_RelX, int a_RelY, int a_RelZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
+
+ int GetHeight( int a_X, int a_Z );
+
+ void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client);
+
+ /// Adds a client to the chunk; returns true if added, false if already there
+ bool AddClient (cClientHandle* a_Client );
+
+ void RemoveClient (cClientHandle* a_Client );
+ bool HasClient (cClientHandle* a_Client );
+ bool HasAnyClients(void); // Returns true if theres any client in the chunk; false otherwise
+
+ void AddEntity(cEntity * a_Entity);
+ void RemoveEntity(cEntity * a_Entity);
+ bool HasEntity(int a_EntityID);
+
+ /// Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true
+ bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found.
+ bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult); // Lua-accessible
+
+ /// Calls the callback for each block entity; returns true if all block entities processed, false if the callback aborted by returning true
+ bool ForEachBlockEntity(cBlockEntityCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for each chest; returns true if all chests processed, false if the callback aborted by returning true
+ bool ForEachChest(cChestCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for each dispenser; returns true if all dispensers processed, false if the callback aborted by returning true
+ bool ForEachDispenser(cDispenserCallback & a_Callback);
+
+ /// Calls the callback for each dropper; returns true if all droppers processed, false if the callback aborted by returning true
+ bool ForEachDropper(cDropperCallback & a_Callback);
+
+ /// Calls the callback for each dropspenser; returns true if all dropspensers processed, false if the callback aborted by returning true
+ bool ForEachDropSpenser(cDropSpenserCallback & a_Callback);
+
+ /// Calls the callback for each furnace; returns true if all furnaces processed, false if the callback aborted by returning true
+ bool ForEachFurnace(cFurnaceCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found
+ bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Lua-acessible
+
+ /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found
+ bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible
+
+ /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found
+ bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback);
+
+ /// Calls the callback for the dispenser at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found
+ bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback);
+
+ /// Calls the callback for the dispenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found
+ bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback);
+
+ /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found
+ bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible
+
+ /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
+ bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible
+
+ void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords
+
+ void CalculateLighting(); // Recalculate right now
+ void CalculateHeightmap();
+
+ // Broadcast various packets to all clients of this chunk:
+ // (Please keep these alpha-sorted)
+ void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
+ void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL);
+ void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL);
+ void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL);
+ void BroadcastChunkData (cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL);
+ void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL);
+ void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityMetadata (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastPlayerAnimation (const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL);
+ void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8
+ void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL);
+ void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL);
+ void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ );
+
+ void SendBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client);
+
+ Vector3i PositionToWorldPosition(const Vector3i & a_RelPos)
+ {
+ return PositionToWorldPosition(a_RelPos.x, a_RelPos.y, a_RelPos.z);
+ }
+
+ void PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ);
+ Vector3i PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ );
+
+ inline void MarkDirty(void)
+ {
+ m_IsDirty = true;
+ m_IsSaving = false;
+ }
+
+ /// Sets the blockticking to start at the specified block. Only one blocktick may be set, second call overwrites the first call
+ inline void SetNextBlockTick(int a_RelX, int a_RelY, int a_RelZ)
+ {
+ m_BlockTickX = a_RelX;
+ m_BlockTickY = a_RelY;
+ m_BlockTickZ = a_RelZ;
+ }
+
+ inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); }
+ inline NIBBLETYPE GetMeta(int a_BlockIdx) const {return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); }
+ inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); }
+ inline void SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_BlockIdx, a_Meta); }
+
+ inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); }
+ inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); }
+ inline NIBBLETYPE GetBlockLight(int a_Idx) const {return cChunkDef::GetNibble(m_BlockLight, a_Idx); }
+ inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx); }
+
+ /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
+
+ /// Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const;
+
+ /// Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const;
+
+ /// Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight) const;
+
+ /// Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_SkyLight) const;
+
+ /// Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const;
+
+ /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /// Same as QueueTickBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s in such a case), ignores unsuccessful attempts
+ void UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ);
+
+ /// Light alterations based on time
+ NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const;
+
+
+ // Simulator data:
+ cFireSimulatorChunkData & GetFireSimulatorData (void) { return m_FireSimulatorData; }
+ cFluidSimulatorData * GetWaterSimulatorData(void) { return m_WaterSimulatorData; }
+ cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; }
+ cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; }
+ cRedstoneSimulatorChunkData & GetRedstoneSimulatorData(void) { return m_RedstoneSimulatorData; }
+
+ cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); }
+
+private:
+
+ friend class cChunkMap;
+
+ struct sSetBlockQueueItem
+ {
+ int m_RelX, m_RelY, m_RelZ;
+ BLOCKTYPE m_BlockType;
+ NIBBLETYPE m_BlockMeta;
+ Int64 m_Tick;
+
+ sSetBlockQueueItem(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick) :
+ m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ), m_BlockType(a_BlockType), m_BlockMeta(a_BlockMeta), m_Tick(a_Tick)
+ {
+ }
+ } ;
+
+ typedef std::vector<sSetBlockQueueItem> sSetBlockQueueVector;
+
+
+ bool m_IsValid; // True if the chunk is loaded / generated
+ bool m_IsLightValid; // True if the blocklight and skylight are calculated
+ bool m_IsDirty; // True if the chunk has changed since it was last saved
+ bool m_IsSaving; // True if the chunk is being saved
+ bool m_HasLoadFailed; // True if chunk failed to load and hasn't been generated yet since then
+
+ std::vector<unsigned int> m_ToTickBlocks;
+ sSetBlockVector m_PendingSendBlocks; ///< Blocks that have changed and need to be sent to all clients
+
+ sSetBlockQueueVector m_SetBlockQueue; ///< Block changes that are queued to a specific tick
+
+ // A critical section is not needed, because all chunk access is protected by its parent ChunkMap's csLayers
+ cClientHandleList m_LoadedByClient;
+ cClientHandleList m_UnloadQuery;
+ cEntityList m_Entities;
+ cBlockEntityList m_BlockEntities;
+
+ /// Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero, the chunk can be unloaded
+ int m_StayCount;
+
+ int m_PosX, m_PosY, m_PosZ;
+ cWorld * m_World;
+ cChunkMap * m_ChunkMap;
+
+ // TODO: Make these pointers and don't allocate what isn't needed
+ BLOCKTYPE m_BlockTypes [cChunkDef::NumBlocks];
+ NIBBLETYPE m_BlockMeta [cChunkDef::NumBlocks / 2];
+ NIBBLETYPE m_BlockLight [cChunkDef::NumBlocks / 2];
+ NIBBLETYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2];
+
+ cChunkDef::HeightMap m_HeightMap;
+ cChunkDef::BiomeMap m_BiomeMap;
+
+ int m_BlockTickX, m_BlockTickY, m_BlockTickZ;
+
+ cChunk * m_NeighborXM; // Neighbor at [X - 1, Z]
+ cChunk * m_NeighborXP; // Neighbor at [X + 1, Z]
+ cChunk * m_NeighborZM; // Neighbor at [X, Z - 1]
+ cChunk * m_NeighborZP; // Neighbor at [X, Z + 1]
+
+ // Per-chunk simulator data:
+ cFireSimulatorChunkData m_FireSimulatorData;
+ cFluidSimulatorData * m_WaterSimulatorData;
+ cFluidSimulatorData * m_LavaSimulatorData;
+ cSandSimulatorChunkData m_SandSimulatorData;
+ cRedstoneSimulatorChunkData m_RedstoneSimulatorData;
+
+
+ // pick up a random block of this chunk
+ void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z);
+ void getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ);
+
+ void RemoveBlockEntity(cBlockEntity * a_BlockEntity);
+ void AddBlockEntity (cBlockEntity * a_BlockEntity);
+
+ void SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff);
+
+ /// Creates a block entity for each block that needs a block entity and doesn't have one in the list
+ void CreateBlockEntities(void);
+
+ /// Wakes up each simulator for its specific blocks; through all the blocks in the chunk
+ void WakeUpSimulators(void);
+
+ // Makes a copy of the list
+ cClientHandleList GetAllClients(void) const {return m_LoadedByClient; }
+
+ /// Sends m_PendingSendBlocks to all clients
+ void BroadcastPendingBlockChanges(void);
+
+ /// Checks the block scheduled for checking in m_ToTickBlocks[]
+ void CheckBlocks(void);
+
+ /// Ticks several random blocks in the chunk
+ void TickBlocks(void);
+
+ /// Adds snow to the top of snowy biomes and hydrates farmland / fills cauldrons in rainy biomes
+ void ApplyWeatherToTop(void);
+
+ /// Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking)
+ void GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks);
+
+ /// Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking)
+ void GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks);
+
+ /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem)
+ void GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random);
+
+ /// Checks if a leaves block at the specified coords has a log up to 4 blocks away connected by other leaves blocks (false if no log)
+ bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends spawn / despawn packet to clients
+ void MoveEntityToNewChunk(cEntity * a_Entity);
+
+ /// Processes all blocks that have been scheduled for replacement by the QueueSetBlock() function
+ void ProcessQueuedSetBlocks(void);
+};
+
+typedef cChunk * cChunkPtr;
+
+typedef std::list<cChunkPtr> cChunkPtrList;
+
+
+
+
+
+#if C_CHUNK_USE_INLINE
+ #include "Chunk.inl.h"
+#endif
+
+
+
+
diff --git a/src/Chunk.inl.h b/src/Chunk.inl.h
new file mode 100644
index 000000000..fb9c4dad1
--- /dev/null
+++ b/src/Chunk.inl.h
@@ -0,0 +1,34 @@
+
+#ifndef __C_CHUNK_INL_H__
+#define __C_CHUNK_INL_H__
+
+#ifndef MAX
+# define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+
+
+
+
+__C_CHUNK_INLINE__
+void cChunk::SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff)
+{
+ unsigned char CurrentLight = cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z );
+ cChunkDef::SetNibble( a_LightBuffer, a_X-1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X-1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) );
+ cChunkDef::SetNibble( a_LightBuffer, a_X+1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X+1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) );
+ cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y-1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y-1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) );
+ cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y+1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y+1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) );
+ cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z-1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z-1 ), MAX(0,CurrentLight-a_Falloff) ) );
+ cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z+1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z+1 ), MAX(0,CurrentLight-a_Falloff) ) );
+ MarkDirty();
+}
+
+
+
+
+
+#endif
+
+
+
+
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
new file mode 100644
index 000000000..d6630df7e
--- /dev/null
+++ b/src/ChunkDef.h
@@ -0,0 +1,617 @@
+
+// ChunkDef.h
+
+// Interfaces to helper types for chunk definitions. Most modules want to include this instead of cChunk.h
+
+
+
+
+
+#pragma once
+
+#include "Vector3i.h"
+
+
+
+
+
+/** This is really only a placeholder to be used in places where we need to "make up" a chunk's Y coord.
+It will help us when the new chunk format comes out and we need to patch everything up for compatibility.
+*/
+#define ZERO_CHUNK_Y 0
+
+// Used to smoothly convert to new axis ordering. One will be removed when deemed stable.
+#define AXIS_ORDER_YZX 1 // Original (1.1-)
+#define AXIS_ORDER_XZY 2 // New (1.2+)
+#define AXIS_ORDER AXIS_ORDER_XZY
+
+
+
+
+
+// fwd
+class cBlockEntity;
+class cEntity;
+class cClientHandle;
+class cBlockEntity;
+
+typedef std::list<cEntity *> cEntityList;
+typedef std::list<cBlockEntity *> cBlockEntityList;
+
+
+
+
+// tolua_begin
+
+/// The datatype used by blockdata
+typedef unsigned char BLOCKTYPE;
+
+/// The datatype used by nibbledata (meta, light, skylight)
+typedef unsigned char NIBBLETYPE;
+
+/// The type used by the heightmap
+typedef unsigned char HEIGHTTYPE;
+
+// tolua_end
+
+
+
+
+
+
+// tolua_begin
+/** Biome IDs
+The first batch corresponds to the clientside biomes, used by MineCraft.
+BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client
+*/
+enum EMCSBiome
+{
+ biOcean = 0,
+ biPlains = 1,
+ biDesert = 2,
+ biExtremeHills = 3,
+ biForest = 4,
+ biTaiga = 5,
+ biSwampland = 6,
+ biRiver = 7,
+ biHell = 8, // same as Nether
+ biNether = 8,
+ biSky = 9, // same as biEnd
+ biEnd = 9,
+ biFrozenOcean = 10,
+ biFrozenRiver = 11,
+ biIcePlains = 12,
+ biTundra = 12, // same as Ice Plains
+ biIceMountains = 13,
+ biMushroomIsland = 14,
+ biMushroomShore = 15,
+ biBeach = 16,
+ biDesertHills = 17,
+ biForestHills = 18,
+ biTaigaHills = 19,
+ biExtremeHillsEdge = 20,
+ biJungle = 21,
+ biJungleHills = 22,
+
+ // Release 1.7 biomes:
+ biJungleEdge = 23,
+ biDeepOcean = 24,
+ biStoneBeach = 25,
+ biColdBeach = 26,
+ biBirchForest = 27,
+ biBirchForestHills = 28,
+ biRoofedForest = 29,
+ biColdTaiga = 30,
+ biColdTaigaHills = 31,
+ biMegaTaiga = 32,
+ biMegaTaigaHills = 33,
+ biExtremeHillsPlus = 34,
+ biSavanna = 35,
+ biSavannaPlateau = 36,
+ biMesa = 37,
+ biMesaPlateauF = 38,
+ biMesaPlateau = 39,
+
+ // Automatically capture the maximum consecutive biome value into biMaxBiome:
+ biNumBiomes, // True number of biomes, since they are zero-based
+ biMaxBiome = biNumBiomes - 1, // The maximum biome value
+
+ // Add this number to the biomes to get the variant
+ biVariant = 128,
+
+ // Release 1.7 biome variants:
+ biSunflowerPlains = 129,
+ biDesertM = 130,
+ biExtremeHillsM = 131,
+ biFlowerForest = 132,
+ biTaigaM = 133,
+ biSwamplandM = 134,
+ biIcePlainsSpikes = 140,
+ biJungleM = 149,
+ biJungleEdgeM = 151,
+ biBirchForestM = 155,
+ biBirchForestHillsM = 156,
+ biRoofedForestM = 157,
+ biColdTaigaM = 158,
+ biMegaSpruceTaiga = 160,
+ biMegaSpruceTaigaHills = 161,
+ biExtremeHillsPlusM = 162,
+ biSavannaM = 163,
+ biSavannaPlateauM = 164,
+ biMesaBryce = 165,
+ biMesaPlateauFM = 166,
+ biMesaPlateauM = 167,
+} ;
+
+// tolua_end
+
+
+
+
+/// Constants used throughout the code, useful typedefs and utility functions
+class cChunkDef
+{
+public:
+ static const int Width = 16;
+ static const int Height = 256;
+ static const int NumBlocks = Width * Height * Width;
+ static const int BlockDataSize = NumBlocks * 2 + (NumBlocks / 2); // 2.5 * numblocks
+
+ // Offsets to individual components in the joined blockdata array
+ static const int MetaOffset = NumBlocks;
+ static const int LightOffset = MetaOffset + NumBlocks / 2;
+ static const int SkyLightOffset = LightOffset + NumBlocks / 2;
+
+ static const unsigned int INDEX_OUT_OF_RANGE = 0xffffffff;
+
+ /// The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the highest non-air block in the column
+ typedef HEIGHTTYPE HeightMap[Width * Width];
+
+ /** The type used for any biomemap operations and storage inside MCServer,
+ using MCServer biomes (need not correspond to client representation!)
+ idx = x + Width * z // Need to verify this with the protocol spec, currently unknown!
+ */
+ typedef EMCSBiome BiomeMap[Width * Width];
+
+ /// The type used for block type operations and storage, AXIS_ORDER ordering
+ typedef BLOCKTYPE BlockTypes[NumBlocks];
+
+ /// The type used for block data in nibble format, AXIS_ORDER ordering
+ typedef NIBBLETYPE BlockNibbles[NumBlocks / 2];
+
+
+ /// Converts absolute block coords into relative (chunk + block) coords:
+ inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ )
+ {
+ BlockToChunk(a_X, a_Z, a_ChunkX, a_ChunkZ);
+
+ a_X = a_X - a_ChunkX * Width;
+ a_Z = a_Z - a_ChunkZ * Width;
+ }
+
+
+ /// Converts absolute block coords to chunk coords:
+ inline static void BlockToChunk(int a_X, int a_Z, int & a_ChunkX, int & a_ChunkZ)
+ {
+ a_ChunkX = a_X / Width;
+ if ((a_X < 0) && (a_X % Width != 0))
+ {
+ a_ChunkX--;
+ }
+ a_ChunkZ = a_Z / cChunkDef::Width;
+ if ((a_Z < 0) && (a_Z % Width != 0))
+ {
+ a_ChunkZ--;
+ }
+ }
+
+
+ inline static unsigned int MakeIndex(int x, int y, int z )
+ {
+ if (
+ (x < Width) && (x > -1) &&
+ (y < Height) && (y > -1) &&
+ (z < Width) && (z > -1)
+ )
+ {
+ return MakeIndexNoCheck(x, y, z);
+ }
+ ASSERT(!"cChunkDef::MakeIndex(): coords out of chunk range!");
+ return INDEX_OUT_OF_RANGE;
+ }
+
+
+ inline static unsigned int MakeIndexNoCheck(int x, int y, int z)
+ {
+ #if AXIS_ORDER == AXIS_ORDER_XZY
+ // For some reason, NOT using the Horner schema is faster. Weird.
+ return x + (z * cChunkDef::Width) + (y * cChunkDef::Width * cChunkDef::Width); // 1.2 is XZY
+ #elif AXIS_ORDER == AXIS_ORDER_YZX
+ return y + (z * cChunkDef::Width) + (x * cChunkDef::Height * cChunkDef::Width); // 1.1 is YZX
+ #endif
+ }
+
+
+ inline static Vector3i IndexToCoordinate( unsigned int index )
+ {
+ #if AXIS_ORDER == AXIS_ORDER_XZY
+ return Vector3i( // 1.2
+ index % cChunkDef::Width, // X
+ index / (cChunkDef::Width * cChunkDef::Width), // Y
+ (index / cChunkDef::Width) % cChunkDef::Width // Z
+ );
+ #elif AXIS_ORDER == AXIS_ORDER_YZX
+ return Vector3i( // 1.1
+ index / (cChunkDef::Height * cChunkDef::Width), // X
+ index % cChunkDef::Height, // Y
+ (index / cChunkDef::Height) % cChunkDef::Width // Z
+ );
+ #endif
+ }
+
+
+ inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Type)
+ {
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Y >= 0) && (a_Y < Height));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
+ a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)] = a_Type;
+ }
+
+
+ inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_Index, BLOCKTYPE a_Type)
+ {
+ ASSERT((a_Index >= 0) && (a_Index <= NumBlocks));
+ a_BlockTypes[a_Index] = a_Type;
+ }
+
+
+ inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z)
+ {
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Y >= 0) && (a_Y < Height));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
+ return a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)];
+ }
+
+
+ inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_Idx)
+ {
+ ASSERT((a_Idx >= 0) && (a_Idx < NumBlocks));
+ return a_BlockTypes[a_Idx];
+ }
+
+
+ inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z)
+ {
+ ASSERT((a_X >= 0) && (a_X <= Width));
+ ASSERT((a_Z >= 0) && (a_Z <= Width));
+ return a_HeightMap[a_X + Width * a_Z];
+ }
+
+
+ inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height)
+ {
+ ASSERT((a_X >= 0) && (a_X <= Width));
+ ASSERT((a_Z >= 0) && (a_Z <= Width));
+ a_HeightMap[a_X + Width * a_Z] = a_Height;
+ }
+
+
+ inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z)
+ {
+ ASSERT((a_X >= 0) && (a_X <= Width));
+ ASSERT((a_Z >= 0) && (a_Z <= Width));
+ return a_BiomeMap[a_X + Width * a_Z];
+ }
+
+
+ inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome)
+ {
+ ASSERT((a_X >= 0) && (a_X <= Width));
+ ASSERT((a_Z >= 0) && (a_Z <= Width));
+ a_BiomeMap[a_X + Width * a_Z] = a_Biome;
+ }
+
+
+ static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int a_BlockIdx)
+ {
+ if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks))
+ {
+ return (a_Buffer[a_BlockIdx / 2] >> ((a_BlockIdx & 1) * 4)) & 0x0f;
+ }
+ ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!");
+ return 0;
+ }
+
+
+ static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int x, int y, int z)
+ {
+ if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1))
+ {
+ int Index = MakeIndexNoCheck(x, y, z);
+ return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
+ }
+ ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!");
+ return 0;
+ }
+
+
+ static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
+ {
+ if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks))
+ {
+ ASSERT(!"cChunkDef::SetNibble(): index out of range!");
+ return;
+ }
+ a_Buffer[a_BlockIdx / 2] = (
+ (a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble
+ ((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set
+ );
+ }
+
+
+ static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble)
+ {
+ if (
+ (x >= Width) || (x < 0) ||
+ (y >= Height) || (y < 0) ||
+ (z >= Width) || (z < 0)
+ )
+ {
+ ASSERT(!"cChunkDef::SetNibble(): index out of range!");
+ return;
+ }
+
+ int Index = MakeIndexNoCheck(x, y, z);
+ a_Buffer[Index / 2] = (
+ (a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
+ ((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
+ );
+ }
+
+
+ inline static char GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos )
+ {
+ return GetNibble(a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z );
+ }
+
+
+ inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, char a_Value )
+ {
+ SetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Value );
+ }
+
+} ;
+
+
+
+
+
+/** Interface class used for getting data out of a chunk using the GetAllData() function.
+Implementation must use the pointers immediately and NOT store any of them for later use
+The virtual methods are called in the same order as they're declared here.
+*/
+class cChunkDataCallback abstract
+{
+public:
+ /** Called before any other callbacks to inform of the current coords
+ (only in processes where multiple chunks can be processed, such as cWorld::ForEachChunkInRect()).
+ If false is returned, the chunk is skipped.
+ */
+ virtual bool Coords(int a_ChunkX, int a_ChunkZ) { UNUSED(a_ChunkX); UNUSED(a_ChunkZ); return true; };
+
+ /// Called once to provide heightmap data
+ virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {UNUSED(a_HeightMap); };
+
+ /// Called once to provide biome data
+ virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) {UNUSED(a_BiomeMap); };
+
+ /// Called once to export block types
+ virtual void BlockTypes (const BLOCKTYPE * a_Type) {UNUSED(a_Type); };
+
+ /// Called once to export block meta
+ virtual void BlockMeta (const NIBBLETYPE * a_Meta) {UNUSED(a_Meta); };
+
+ /// Called once to let know if the chunk lighting is valid. Return value is used to control if BlockLight() and BlockSkyLight() are called next (if true)
+ virtual bool LightIsValid(bool a_IsLightValid) {UNUSED(a_IsLightValid); return true; };
+
+ /// Called once to export block light
+ virtual void BlockLight (const NIBBLETYPE * a_BlockLight) {UNUSED(a_BlockLight); };
+
+ /// Called once to export sky light
+ virtual void BlockSkyLight(const NIBBLETYPE * a_SkyLight) {UNUSED(a_SkyLight); };
+
+ /// Called for each entity in the chunk
+ virtual void Entity(cEntity * a_Entity) {UNUSED(a_Entity); };
+
+ /// Called for each blockentity in the chunk
+ virtual void BlockEntity(cBlockEntity * a_Entity) {UNUSED(a_Entity); };
+} ;
+
+
+
+
+
+/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer
+*/
+class cChunkDataCollector :
+ public cChunkDataCallback
+{
+public:
+
+ // Must be unsigned char instead of BLOCKTYPE or NIBBLETYPE, because it houses both.
+ unsigned char m_BlockData[cChunkDef::BlockDataSize];
+
+protected:
+
+ virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
+ {
+ memcpy(m_BlockData, a_BlockTypes, sizeof(cChunkDef::BlockTypes));
+ }
+
+
+ virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
+ {
+ memcpy(m_BlockData + cChunkDef::NumBlocks, a_BlockMeta, cChunkDef::NumBlocks / 2);
+ }
+
+
+ virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
+ {
+ memcpy(m_BlockData + 3 * cChunkDef::NumBlocks / 2, a_BlockLight, cChunkDef::NumBlocks / 2);
+ }
+
+
+ virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
+ {
+ memcpy(m_BlockData + 2 * cChunkDef::NumBlocks, a_BlockSkyLight, cChunkDef::NumBlocks / 2);
+ }
+} ;
+
+
+
+
+
+/** A simple implementation of the cChunkDataCallback interface that collects all block data into a separate buffers
+*/
+class cChunkDataSeparateCollector :
+ public cChunkDataCallback
+{
+public:
+
+ cChunkDef::BlockTypes m_BlockTypes;
+ cChunkDef::BlockNibbles m_BlockMetas;
+ cChunkDef::BlockNibbles m_BlockLight;
+ cChunkDef::BlockNibbles m_BlockSkyLight;
+
+protected:
+
+ virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
+ {
+ memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes));
+ }
+
+
+ virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
+ {
+ memcpy(m_BlockMetas, a_BlockMeta, sizeof(m_BlockMetas));
+ }
+
+
+ virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
+ {
+ memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
+ }
+
+
+ virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
+ {
+ memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
+ }
+} ;
+
+
+
+
+
+/** Interface class used for comparing clients of two chunks.
+Used primarily for entity moving while both chunks are locked.
+*/
+class cClientDiffCallback
+{
+public:
+ /// Called for clients that are in Chunk1 and not in Chunk2,
+ virtual void Removed(cClientHandle * a_Client) = 0;
+
+ /// Called for clients that are in Chunk2 and not in Chunk1.
+ virtual void Added(cClientHandle * a_Client) = 0;
+} ;
+
+
+
+
+
+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)
+ {}
+};
+
+typedef std::list<sSetBlock> sSetBlockList;
+typedef std::vector<sSetBlock> sSetBlockVector;
+
+
+
+
+
+class cChunkCoords
+{
+public:
+ int m_ChunkX;
+ int m_ChunkY;
+ int m_ChunkZ;
+
+ cChunkCoords(int a_ChunkX, int a_ChunkY, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ) {}
+
+ bool operator == (const cChunkCoords & a_Other) const
+ {
+ return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkY == a_Other.m_ChunkY) && (m_ChunkZ == a_Other.m_ChunkZ));
+ }
+} ;
+
+typedef std::list<cChunkCoords> cChunkCoordsList;
+
+
+
+
+
+/// Interface class used as a callback for operations that involve chunk coords
+class cChunkCoordCallback
+{
+public:
+ virtual void Call(int a_ChunkX, int a_ChunkZ) = 0;
+} ;
+
+
+
+
+
+/// Generic template that can store any kind of data together with a triplet of 3 coords:
+template <typename X> class cCoordWithData
+{
+public:
+ int x;
+ int y;
+ int z;
+ X Data;
+
+ cCoordWithData(int a_X, int a_Y, int a_Z) :
+ x(a_X), y(a_Y), z(a_Z)
+ {
+ }
+
+ cCoordWithData(int a_X, int a_Y, int a_Z, const X & a_Data) :
+ x(a_X), y(a_Y), z(a_Z), Data(a_Data)
+ {
+ }
+} ;
+
+// Illegal in C++03: typedef std::list< cCoordWithData<X> > cCoordWithDataList<X>;
+typedef cCoordWithData<int> cCoordWithInt;
+typedef std::list<cCoordWithInt> cCoordWithIntList;
+typedef std::vector<cCoordWithInt> cCoordWithIntVector;
+
+
+
+
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
new file mode 100644
index 000000000..5937ee2e4
--- /dev/null
+++ b/src/ChunkMap.cpp
@@ -0,0 +1,2701 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "ChunkMap.h"
+#include "World.h"
+#include "Root.h"
+#include "Entities/Player.h"
+#include "Item.h"
+#include "Entities/Pickup.h"
+#include "Chunk.h"
+#include "Generating/Trees.h" // used in cChunkMap::ReplaceTreeBlocks() for tree block discrimination
+#include "BlockArea.h"
+#include "PluginManager.h"
+#include "Entities/TNTEntity.h"
+#include "Blocks/BlockHandler.h"
+#include "MobCensus.h"
+#include "MobSpawner.h"
+
+#ifndef _WIN32
+ #include <cstdlib> // abs
+#endif
+
+#include "lib/zlib/zlib.h"
+#include "lib/jsoncpp/include/json/json.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cChunkMap:
+
+cChunkMap::cChunkMap(cWorld * a_World )
+ : m_World( a_World )
+{
+}
+
+
+
+
+
+cChunkMap::~cChunkMap()
+{
+ cCSLock Lock(m_CSLayers);
+ while (!m_Layers.empty())
+ {
+ delete m_Layers.back();
+ m_Layers.pop_back(); // Must pop, because further chunk deletions query the chunkmap for entities and that would touch deleted data
+ }
+}
+
+
+
+
+
+void cChunkMap::RemoveLayer( cChunkLayer* a_Layer )
+{
+ cCSLock Lock(m_CSLayers);
+ m_Layers.remove(a_Layer);
+}
+
+
+
+
+
+cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ)
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ))
+ {
+ return *itr;
+ }
+ }
+
+ // Not found, create new:
+ cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this);
+ if (Layer == NULL)
+ {
+ LOGERROR("cChunkMap: Cannot create new layer, server out of memory?");
+ return NULL;
+ }
+ m_Layers.push_back(Layer);
+ return Layer;
+}
+
+
+
+
+
+cChunkMap::cChunkLayer * cChunkMap::FindLayerForChunk(int a_ChunkX, int a_ChunkZ)
+{
+ const int LayerX = FAST_FLOOR_DIV(a_ChunkX, LAYER_SIZE);
+ const int LayerZ = FAST_FLOOR_DIV(a_ChunkZ, LAYER_SIZE);
+ return FindLayer(LayerX, LayerZ);
+}
+
+
+
+
+
+cChunkMap::cChunkLayer * cChunkMap::FindLayer(int a_LayerX, int a_LayerZ)
+{
+ ASSERT(m_CSLayers.IsLockedByCurrentThread());
+
+ for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ))
+ {
+ return *itr;
+ }
+ } // for itr - m_Layers[]
+
+ // Not found
+ return NULL;
+}
+
+
+
+
+
+cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk(int a_ChunkX, int a_ChunkZ)
+{
+ const int LayerX = FAST_FLOOR_DIV(a_ChunkX, LAYER_SIZE);
+ const int LayerZ = FAST_FLOOR_DIV(a_ChunkZ, LAYER_SIZE);
+ return GetLayer(LayerX, LayerZ);
+}
+
+
+
+
+
+cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
+{
+ // No need to lock m_CSLayers, since it's already locked by the operation that called us
+ ASSERT(m_CSLayers.IsLockedByCurrentThread());
+
+ cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ );
+ if (Layer == NULL)
+ {
+ // An error must have occurred, since layers are automatically created if they don't exist
+ return NULL;
+ }
+
+ cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return NULL;
+ }
+ if (!(Chunk->IsValid()))
+ {
+ m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, true);
+ }
+ return Chunk;
+}
+
+
+
+
+
+cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
+{
+ // No need to lock m_CSLayers, since it's already locked by the operation that called us
+ cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ );
+ if (Layer == NULL)
+ {
+ // An error must have occurred, since layers are automatically created if they don't exist
+ return NULL;
+ }
+
+ cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return NULL;
+ }
+ if (!(Chunk->IsValid()))
+ {
+ m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, false);
+ }
+
+ return Chunk;
+}
+
+
+
+
+
+cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
+{
+ // No need to lock m_CSLayers, since it's already locked by the operation that called us
+ cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ );
+ if (Layer == NULL)
+ {
+ // An error must have occurred, since layers are automatically created if they don't exist
+ return NULL;
+ }
+
+ return Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+}
+
+
+
+
+
+bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
+{
+ // We already have m_CSLayers locked since this can be called only from within the tick thread
+ ASSERT(m_CSLayers.IsLockedByCurrentThread());
+
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return false;
+ }
+
+ int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ);
+ a_BlockType = Chunk->GetBlock(Index);
+ a_BlockMeta = Chunk->GetMeta(Index);
+ return true;
+}
+
+
+
+
+
+bool cChunkMap::LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType)
+{
+ // We already have m_CSLayers locked since this can be called only from within the tick thread
+ ASSERT(m_CSLayers.IsLockedByCurrentThread());
+
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return false;
+ }
+
+ int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ);
+ a_BlockType = Chunk->GetBlock(Index);
+ return true;
+}
+
+
+
+
+
+bool cChunkMap::LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE & a_BlockMeta)
+{
+ // We already have m_CSLayers locked since this can be called only from within the tick thread
+ ASSERT(m_CSLayers.IsLockedByCurrentThread());
+
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return false;
+ }
+
+ int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ);
+ a_BlockMeta = Chunk->GetMeta(Index);
+ return true;
+}
+
+
+
+
+
+bool cChunkMap::LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ // We already have m_CSLayers locked since this can be called only from within the tick thread
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return false;
+ }
+
+ Chunk->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
+ return true;
+}
+
+
+
+
+
+bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ // We already have m_CSLayers locked since this can be called only from within the tick thread
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return false;
+ }
+
+ Chunk->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
+ return true;
+}
+
+
+
+
+
+cChunk * cChunkMap::FindChunk(int a_ChunkX, int a_ChunkZ)
+{
+ ASSERT(m_CSLayers.IsLockedByCurrentThread());
+
+ cChunkLayer * Layer = FindLayerForChunk(a_ChunkX, a_ChunkZ);
+ if (Layer == NULL)
+ {
+ return NULL;
+ }
+ return Layer->FindChunk(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastAttachEntity(a_Entity, a_Vehicle);
+}
+
+
+
+
+
+void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ int x, y, z, ChunkX, ChunkZ;
+ x = a_BlockX;
+ y = a_BlockY;
+ z = a_BlockZ;
+ cChunkDef::BlockToChunk(x, z, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ;
+
+ cChunkDef::BlockToChunk(a_blockX, a_blockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastBlockBreakAnimation(a_entityID, a_blockX, a_blockY, a_blockZ, a_stage, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return;
+ }
+ Chunk->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, 0, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastChunkData(a_Serializer, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Pickup.GetChunkX(), ZERO_CHUNK_Y, a_Pickup.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastDestroyEntity(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastEntityHeadLook(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastEntityLook(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastEntityMetadata(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastEntityVelocity(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Player.GetChunkX(), ZERO_CHUNK_Y, a_Player.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ;
+
+ cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcZ / 8, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ;
+
+ cChunkDef::BlockToChunk(a_SrcX, a_SrcZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastSpawnEntity(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude);
+}
+
+
+
+
+
+void cChunkMap::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
+{
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ;
+
+ cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->BroadcastUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cChunkMap::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client)
+{
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return;
+ }
+ Chunk->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client);
+}
+
+
+
+
+
+void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // a_Player rclked block entity at the coords specified, handle it
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return;
+ }
+ Chunk->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+bool cChunkMap::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return false;
+ }
+ return a_Callback.Item(Chunk);
+}
+
+
+
+
+
+void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return;
+ }
+ m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, Chunk);
+}
+
+
+
+
+
+/// Wakes up the simulators for the specified area of blocks
+void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ)
+{
+ cSimulatorManager * SimMgr = m_World->GetSimulatorManager();
+ int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ;
+ cChunkDef::BlockToChunk(a_MinBlockX, a_MinBlockZ, MinChunkX, MinChunkZ);
+ cChunkDef::BlockToChunk(a_MaxBlockX, a_MaxBlockZ, MaxChunkX, MaxChunkZ);
+ for (int z = MinChunkZ; z <= MaxChunkZ; z++)
+ {
+ int MinZ = std::max(a_MinBlockZ, z * cChunkDef::Width);
+ int MaxZ = std::min(a_MaxBlockZ, z * cChunkDef::Width + cChunkDef::Width - 1);
+ for (int x = MinChunkX; x <= MaxChunkX; x++)
+ {
+ cChunkPtr Chunk = GetChunkNoGen(x, 0, z);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ continue;
+ }
+ int MinX = std::max(a_MinBlockX, x * cChunkDef::Width);
+ int MaxX = std::min(a_MaxBlockX, x * cChunkDef::Width + cChunkDef::Width - 1);
+ for (int BlockY = a_MinBlockY; BlockY <= a_MaxBlockY; BlockY++)
+ {
+ for (int BlockZ = MinZ; BlockZ <= MaxZ; BlockZ++)
+ {
+ for (int BlockX = MinX; BlockX <= MaxX; BlockX++)
+ {
+ SimMgr->WakeUp(BlockX, BlockY, BlockZ, Chunk);
+ } // for BlockX
+ } // for BlockZ
+ } // for BlockY
+ } // for x - chunks
+ } // for z = chunks
+}
+
+
+
+
+
+void cChunkMap::MarkChunkDirty (int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return;
+ }
+ Chunk->MarkDirty();
+}
+
+
+
+
+
+void cChunkMap::MarkChunkSaving(int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return;
+ }
+ Chunk->MarkSaving();
+}
+
+
+
+
+
+void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return;
+ }
+ Chunk->MarkSaved();
+}
+
+
+
+
+
+void cChunkMap::SetChunkData(
+ int a_ChunkX, int a_ChunkZ,
+ const BLOCKTYPE * a_BlockTypes,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
+ const cChunkDef::HeightMap * a_HeightMap,
+ const cChunkDef::BiomeMap & a_BiomeMap,
+ cBlockEntityList & a_BlockEntities,
+ bool a_MarkDirty
+)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_BlockEntities);
+
+ if (a_MarkDirty)
+ {
+ Chunk->MarkDirty();
+ }
+
+ // Notify plugins of the chunk becoming available
+ cPluginManager::Get()->CallHookChunkAvailable(m_World, a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cChunkMap::ChunkLighted(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
+)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ Chunk->SetLight(a_BlockLight, a_SkyLight);
+ Chunk->MarkDirty();
+}
+
+
+
+
+
+bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return false;
+ }
+ Chunk->GetAllData(a_Callback);
+ return true;
+}
+
+
+
+
+
+bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return false;
+ }
+ Chunk->GetBlockTypes(a_BlockTypes);
+ return true;
+}
+
+
+
+
+
+bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ return (Chunk != NULL) && Chunk->IsValid();
+}
+
+
+
+
+
+bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ return (Chunk != NULL) && Chunk->HasAnyClients();
+}
+
+
+
+
+
+int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ)
+{
+ while (true)
+ {
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ, BlockY = 0;
+ cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk == NULL)
+ {
+ return 0;
+ }
+
+ if (Chunk->IsValid())
+ {
+ return Chunk->GetHeight(a_BlockX, a_BlockZ);
+ }
+
+ // The chunk is not valid, wait for it to become valid:
+ cCSUnlock Unlock(Lock);
+ m_evtChunkValid.Wait();
+ } // while (true)
+}
+
+
+
+
+
+bool cChunkMap::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height)
+{
+ // Returns false if chunk not loaded / generated
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ, BlockY = 0;
+ cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return false;
+ }
+ a_Height = Chunk->GetHeight(a_BlockX, a_BlockZ);
+ return true;
+}
+
+
+
+
+
+void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
+{
+ sSetBlockList Failed;
+
+ // 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;
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();)
+ {
+ if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
+ {
+ Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ itr = a_BlockList.erase(itr);
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - a_BlockList[]
+ }
+ else
+ {
+ // 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))
+ {
+ Failed.push_back(*itr);
+ itr = a_BlockList.erase(itr);
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - a_BlockList[]
+ }
+ }
+
+ // Return the failed:
+ std::swap(Failed, a_BlockList);
+}
+
+
+
+
+
+void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player)
+{
+ int BlockX = (int)(a_Player->GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway
+ int BlockY = (int)(a_Player->GetPosY());
+ int BlockZ = (int)(a_Player->GetPosZ());
+ int ChunkX, ChunkZ, ChunkY = ZERO_CHUNK_Y;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ int OtherChunkX = ChunkX + ((BlockX > 8) ? 1 : -1);
+ int OtherChunkZ = ChunkZ + ((BlockZ > 8) ? 1 : -1);
+
+ // We suppose that each player keeps their chunks in memory, therefore it makes little sense to try to re-load or even generate them.
+ // The only time the chunks are not valid is when the player is downloading the initial world and they should not call this at that moment
+
+ cCSLock Lock(m_CSLayers);
+ GetChunkNoLoad(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer(a_Player);
+
+ // Check the neighboring chunks as well:
+ GetChunkNoLoad(OtherChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player);
+ GetChunkNoLoad(OtherChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player);
+ GetChunkNoLoad(ChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player);
+ GetChunkNoLoad(ChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player);
+}
+
+
+
+
+
+BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ );
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ return 0;
+}
+
+
+
+
+
+NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ );
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
+ if ((Chunk != NULL) && Chunk->IsValid() )
+ {
+ return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ return 0;
+}
+
+
+
+
+
+NIBBLETYPE cChunkMap::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ );
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
+ if ((Chunk != NULL) && Chunk->IsValid() )
+ {
+ return Chunk->GetSkyLight(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ return 0;
+}
+
+
+
+
+
+NIBBLETYPE cChunkMap::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ );
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
+ if ((Chunk != NULL) && Chunk->IsValid() )
+ {
+ return Chunk->GetBlockLight(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ return 0;
+}
+
+
+
+
+
+void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+ // a_BlockXYZ now contains relative coords!
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ Chunk->SetMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta);
+ Chunk->MarkDirty();
+ Chunk->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, NULL);
+ }
+}
+
+
+
+
+
+void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
+{
+ int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
+ cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ );
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta );
+ m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, Chunk);
+ }
+}
+
+
+
+
+
+void cChunkMap::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick)
+{
+ int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ Chunk->QueueSetBlock(X, Y, Z, a_BlockType, a_BlockMeta, a_Tick);
+ }
+}
+
+
+
+
+
+bool cChunkMap::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
+{
+ int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
+ cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ );
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ Chunk->GetBlockTypeMeta(X, Y, Z, a_BlockType, a_BlockMeta);
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+bool cChunkMap::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight)
+{
+ int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
+ cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ );
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ Chunk->GetBlockInfo(X, Y, Z, a_BlockType, a_Meta, a_SkyLight, a_BlockLight);
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType)
+{
+ cCSLock Lock(m_CSLayers);
+ for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
+ {
+ cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ );
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ continue;
+ }
+ if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType)
+ {
+ Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ }
+ }
+}
+
+
+
+
+
+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, ZERO_CHUNK_Y, itr->ChunkZ );
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ continue;
+ }
+ switch (Chunk->GetBlock(itr->x, itr->y, itr->z))
+ {
+ CASE_TREE_OVERWRITTEN_BLOCKS:
+ {
+ Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ break;
+ }
+ case E_BLOCK_LEAVES:
+ {
+ if (itr->BlockType == E_BLOCK_LOG)
+ {
+ Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ }
+ break;
+ }
+ }
+ } // for itr - a_Blocks[]
+}
+
+
+
+
+
+EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ)
+{
+ int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ;
+ cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ );
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ return Chunk->GetBiomeAt(X, Z);
+ }
+ else
+ {
+ return m_World->GetGenerator().GetBiomeAt(a_BlockX, a_BlockZ);
+ }
+}
+
+
+
+
+
+bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
+{
+ bool res = true;
+ cCSLock Lock(m_CSLayers);
+ for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
+ {
+ cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ );
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ if (!a_ContinueOnFailure)
+ {
+ return false;
+ }
+ res = false;
+ continue;
+ }
+ int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z);
+ itr->BlockType = Chunk->GetBlock(idx);
+ itr->BlockMeta = Chunk->GetMeta(idx);
+ }
+ return res;
+}
+
+
+
+
+
+bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z)
+{
+ int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ;
+
+ cChunkDef::AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ );
+
+ {
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr DestChunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
+ if ((DestChunk == NULL) || !DestChunk->IsValid())
+ {
+ return false;
+ }
+
+ DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0 );
+ m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z, DestChunk);
+ }
+
+ return true;
+}
+
+
+
+
+
+void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ);
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk->IsValid())
+ {
+ Chunk->SendBlockTo(a_X, a_Y, a_Z, a_Player->GetClientHandle());
+ }
+}
+
+
+
+
+
+void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, ZERO_CHUNK_Y, a_ChunkZ1);
+ if (Chunk1 == NULL)
+ {
+ return;
+ }
+ cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, ZERO_CHUNK_Y, a_ChunkZ2);
+ if (Chunk2 == NULL)
+ {
+ return;
+ }
+
+ CompareChunkClients(Chunk1, Chunk2, a_Callback);
+}
+
+
+
+
+
+void cChunkMap::CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClientDiffCallback & a_Callback)
+{
+ cClientHandleList Clients1(a_Chunk1->GetAllClients());
+ cClientHandleList Clients2(a_Chunk2->GetAllClients());
+
+ // Find "removed" clients:
+ for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1)
+ {
+ bool Found = false;
+ for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2)
+ {
+ if (*itr1 == *itr2)
+ {
+ Found = true;
+ break;
+ }
+ } // for itr2 - Clients2[]
+ if (!Found)
+ {
+ a_Callback.Removed(*itr1);
+ }
+ } // for itr1 - Clients1[]
+
+ // Find "added" clients:
+ for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2)
+ {
+ bool Found = false;
+ for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1)
+ {
+ if (*itr1 == *itr2)
+ {
+ Found = true;
+ break;
+ }
+ } // for itr1 - Clients1[]
+ if (!Found)
+ {
+ a_Callback.Added(*itr2);
+ }
+ } // for itr2 - Clients2[]
+}
+
+
+
+
+
+bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return false;
+ }
+ return Chunk->AddClient(a_Client);
+}
+
+
+
+
+
+void cChunkMap::RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ Chunk->RemoveClient(a_Client);
+}
+
+
+
+
+
+void cChunkMap::RemoveClientFromChunks(cClientHandle * a_Client)
+{
+ cCSLock Lock(m_CSLayers);
+
+ for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ (*itr)->RemoveClient(a_Client);
+ } // for itr - m_Layers[]
+}
+
+
+
+
+
+void cChunkMap::AddEntity(cEntity * a_Entity)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ LOGWARNING("Entity at %p (%s, ID %d) spawning in a non-existent chunk, the entity is lost.",
+ a_Entity, a_Entity->GetClass(), a_Entity->GetUniqueID()
+ );
+ return;
+ }
+ Chunk->AddEntity(a_Entity);
+}
+
+
+
+
+
+bool cChunkMap::HasEntity(int a_UniqueID)
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ if ((*itr)->HasEntity(a_UniqueID))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+void cChunkMap::RemoveEntity(cEntity * a_Entity)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return;
+ }
+ Chunk->RemoveEntity(a_Entity);
+}
+
+
+
+
+
+bool cChunkMap::ForEachEntity(cEntityCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ if (!(*itr)->ForEachEntity(a_Callback))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+
+
+bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->ForEachEntity(a_Callback);
+}
+
+
+
+
+
+void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlocksAffected)
+{
+ // Don't explode if outside of Y range (prevents the following test running into unallocated memory):
+ if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height - 1))
+ {
+ return;
+ }
+
+ // Don't explode if the explosion center is inside a liquid block:
+ switch (m_World->GetBlock((int)floor(a_BlockX), (int)floor(a_BlockY), (int)floor(a_BlockZ)))
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ return;
+ }
+ }
+
+ cBlockArea area;
+ int bx = (int)floor(a_BlockX);
+ int by = (int)floor(a_BlockY);
+ int bz = (int)floor(a_BlockZ);
+ int ExplosionSizeInt = (int) ceil(a_ExplosionSize);
+ int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt;
+ a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt);
+ int MinY = std::max((int)floor(a_BlockY - ExplosionSizeInt), 0);
+ int MaxY = std::min((int)ceil(a_BlockY + ExplosionSizeInt), cChunkDef::Height - 1);
+ area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt));
+ for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++)
+ {
+ for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++)
+ {
+ if ((by + y >= cChunkDef::Height) || (by + y < 0))
+ {
+ // Outside of the world
+ continue;
+ }
+ for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++)
+ {
+ if ((x * x + y * y + z * z) > ExplosionSizeSq)
+ {
+ // Too far away
+ continue;
+ }
+
+ BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z);
+ switch (Block)
+ {
+ case E_BLOCK_TNT:
+ {
+ // Activate the TNT, with a random fuse between 10 to 30 game ticks
+ double FuseTime = (double)(10 + m_World->GetTickRandomNumber(20)) / 20;
+ m_World->SpawnPrimedTNT(a_BlockX + x + 0.5, a_BlockY + y + 0.5, a_BlockZ + z + 0.5, FuseTime);
+ area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR);
+ a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
+ break;
+ }
+ case E_BLOCK_OBSIDIAN:
+ case E_BLOCK_BEDROCK:
+ case E_BLOCK_WATER:
+ case E_BLOCK_LAVA:
+ {
+ // These blocks are not affected by explosions
+ break;
+ }
+
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ // Turn into simulated water:
+ area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_WATER);
+ break;
+ }
+
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ // Turn into simulated lava:
+ area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_LAVA);
+ break;
+ }
+
+ case E_BLOCK_AIR:
+ {
+ // No pickups for air
+ break;
+ }
+
+ default:
+ {
+ if (m_World->GetTickRandomNumber(10) == 5)
+ {
+ cItems Drops;
+ cBlockHandler * Handler = BlockHandler(Block);
+
+ Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z));
+ m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z);
+ }
+ area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR);
+ a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
+ }
+ } // switch (BlockType)
+ } // for z
+ } // for y
+ } // for x
+ area.Write(m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt);
+
+ // Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391):
+ WakeUpSimulatorsInArea(
+ bx - ExplosionSizeInt, bx + ExplosionSizeInt + 1,
+ MinY, MaxY,
+ bz - ExplosionSizeInt, bz + ExplosionSizeInt + 1
+ );
+}
+
+
+
+
+
+bool cChunkMap::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ bool res = false;
+ for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ if ((*itr)->DoWithEntityByID(a_UniqueID, a_Callback, res))
+ {
+ return res;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cChunkMap::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->ForEachBlockEntity(a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->ForEachChest(a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->ForEachDispenser(a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->ForEachDropper(a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->ForEachDropSpenser(a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->ForEachFurnace(a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithDispenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithDropperAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4);
+}
+
+
+
+
+
+void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+}
+
+
+
+
+
+/// Loads the chunk synchronously, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before)
+bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ {
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ // Internal error
+ return false;
+ }
+ if (Chunk->IsValid())
+ {
+ // Already loaded
+ return true;
+ }
+ if (Chunk->HasLoadFailed())
+ {
+ // Already tried loading and it failed
+ return false;
+ }
+ }
+ return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+}
+
+
+
+
+
+/// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid()
+void cChunkMap::LoadChunks(const cChunkCoordsList & a_Chunks)
+{
+ for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr)
+ {
+ LoadChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ } // for itr - a_Chunks[]
+}
+
+
+
+
+
+void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ Chunk->MarkLoadFailed();
+}
+
+
+
+
+
+bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
+{
+ cCSLock Lock(m_CSLayers);
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4);
+}
+
+
+
+
+
+void cChunkMap::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay)
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr)
+ {
+ cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ if (Chunk == NULL)
+ {
+ continue;
+ }
+ Chunk->Stay(a_Stay);
+ }
+}
+
+
+
+
+
+void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ // Not present
+ return;
+ }
+ Chunk->MarkRegenerating();
+}
+
+
+
+
+
+bool cChunkMap::IsChunkLighted(int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ // Not present
+ return false;
+ }
+ return Chunk->IsLightValid();
+}
+
+
+
+
+
+bool cChunkMap::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback)
+{
+ bool Result = true;
+ cCSLock Lock(m_CSLayers);
+ for (int z = a_MinChunkZ; z <= a_MaxChunkZ; z++)
+ {
+ for (int x = a_MinChunkX; x <= a_MaxChunkX; x++)
+ {
+ cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z);
+ if ((Chunk == NULL) || (!Chunk->IsValid()))
+ {
+ // Not present / not valid
+ Result = false;
+ continue;
+ }
+ if (!a_Callback.Coords(x, z))
+ {
+ continue;
+ }
+ Chunk->GetAllData(a_Callback);
+ }
+ }
+ return Result;
+}
+
+
+
+
+
+bool cChunkMap::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
+{
+ // Convert block coords to chunks coords:
+ int MinChunkX, MaxChunkX;
+ int MinChunkZ, MaxChunkZ;
+ int MinBlockX = a_MinBlockX;
+ int MinBlockY = a_MinBlockY;
+ int MinBlockZ = a_MinBlockZ;
+ int MaxBlockX = a_MinBlockX + a_Area.GetSizeX();
+ int MaxBlockY = a_MinBlockY + a_Area.GetSizeY();
+ int MaxBlockZ = a_MinBlockZ + a_Area.GetSizeZ();
+ cChunkDef::AbsoluteToRelative(MinBlockX, MinBlockY, MinBlockZ, MinChunkX, MinChunkZ);
+ cChunkDef::AbsoluteToRelative(MaxBlockX, MaxBlockY, MaxBlockZ, MaxChunkX, MaxChunkZ);
+
+ // Iterate over chunks, write data into each:
+ bool Result = true;
+ cCSLock Lock(m_CSLayers);
+ for (int z = MinChunkZ; z <= MaxChunkZ; z++)
+ {
+ for (int x = MinChunkX; x <= MaxChunkX; x++)
+ {
+ cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z);
+ if ((Chunk == NULL) || (!Chunk->IsValid()))
+ {
+ // Not present / not valid
+ Result = false;
+ continue;
+ }
+ Chunk->WriteBlockArea(a_Area, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
+ } // for x
+ } // for z
+ return Result;
+}
+
+
+
+
+
+void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty)
+{
+ a_NumChunksValid = 0;
+ a_NumChunksDirty = 0;
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ int NumValid = 0, NumDirty = 0;
+ (*itr)->GetChunkStats(NumValid, NumDirty);
+ a_NumChunksValid += NumValid;
+ a_NumChunksDirty += NumDirty;
+ } // for itr - m_Layers[]
+}
+
+
+
+
+
+void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk != NULL)
+ {
+ Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand);
+ }
+}
+
+
+
+
+
+void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk != NULL)
+ {
+ Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
+ }
+}
+
+
+
+
+
+void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk != NULL)
+ {
+ Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
+ }
+}
+
+
+
+
+
+void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk != NULL)
+ {
+ Chunk->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ);
+ }
+}
+
+
+
+
+void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill)
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ (*itr)->CollectMobCensus(a_ToFill);
+ } // for itr - m_Layers
+}
+
+
+
+
+
+
+void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner)
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ (*itr)->SpawnMobs(a_MobSpawner);
+ } // for itr - m_Layers
+}
+
+
+
+
+
+void cChunkMap::Tick(float a_Dt)
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ (*itr)->Tick(a_Dt);
+ } // for itr - m_Layers
+}
+
+
+
+
+
+void cChunkMap::UnloadUnusedChunks()
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ (*itr)->UnloadUnusedChunks();
+ } // for itr - m_Layers
+}
+
+
+
+
+
+void cChunkMap::SaveAllChunks(void)
+{
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ (*itr)->Save();
+ } // for itr - m_Layers[]
+}
+
+
+
+
+
+int cChunkMap::GetNumChunks(void)
+{
+ cCSLock Lock(m_CSLayers);
+ int NumChunks = 0;
+ for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ NumChunks += (*itr)->GetNumChunksLoaded();
+ }
+ return NumChunks;
+}
+
+
+
+
+
+void cChunkMap::ChunkValidated(void)
+{
+ m_evtChunkValid.Set();
+}
+
+
+
+
+
+void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
+ // a_BlockXYZ now contains relative coords!
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if (Chunk != NULL)
+ {
+ Chunk->QueueTickBlock(a_BlockX, a_BlockY, a_BlockZ);
+ }
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cChunkMap::cChunkLayer:
+
+cChunkMap::cChunkLayer::cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent)
+ : m_LayerX( a_LayerX )
+ , m_LayerZ( a_LayerZ )
+ , m_Parent( a_Parent )
+ , m_NumChunksLoaded( 0 )
+{
+ memset(m_Chunks, 0, sizeof(m_Chunks));
+}
+
+
+
+
+
+cChunkMap::cChunkLayer::~cChunkLayer()
+{
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i)
+ {
+ delete m_Chunks[i];
+ m_Chunks[i] = NULL; // // Must zero out, because further chunk deletions query the chunkmap for entities and that would touch deleted data
+ } // for i - m_Chunks[]
+}
+
+
+
+
+
+cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
+{
+ // Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check
+
+ const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE;
+ const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE;
+
+ if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1)))
+ {
+ ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!");
+ return NULL;
+ }
+
+ int Index = LocalX + LocalZ * LAYER_SIZE;
+ if (m_Chunks[Index] == NULL)
+ {
+ cChunk * neixm = (LocalX > 0) ? m_Chunks[Index - 1] : m_Parent->FindChunk(a_ChunkX - 1, a_ChunkZ);
+ cChunk * neixp = (LocalX < LAYER_SIZE - 1) ? m_Chunks[Index + 1] : m_Parent->FindChunk(a_ChunkX + 1, a_ChunkZ);
+ cChunk * neizm = (LocalZ > 0) ? m_Chunks[Index - LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ - 1);
+ cChunk * neizp = (LocalZ < LAYER_SIZE - 1) ? m_Chunks[Index + LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ + 1);
+ m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp);
+ }
+ return m_Chunks[Index];
+}
+
+
+
+
+
+cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ)
+{
+ const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE;
+ const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE;
+
+ if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1)))
+ {
+ ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!");
+ return NULL;
+ }
+
+ int Index = LocalX + LocalZ * LAYER_SIZE;
+ return m_Chunks[Index];
+}
+
+
+
+
+void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill)
+{
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
+ {
+ // We do count every Mobs in the world. But we are assuming that every chunk not loaded by any client
+ // doesn't affect us. Normally they should not have mobs because every "too far" mobs despawn
+ // If they have (f.i. when player disconnect) we assume we don't have to make them live or despawn
+ if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients())
+ {
+ m_Chunks[i]->CollectMobCensus(a_ToFill);
+ }
+ } // for i - m_Chunks[]
+}
+
+
+
+
+
+
+void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner)
+{
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
+ {
+ // We only spawn close to players
+ if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients())
+ {
+ m_Chunks[i]->SpawnMobs(a_MobSpawner);
+ }
+ } // for i - m_Chunks[]
+}
+
+
+
+void cChunkMap::cChunkLayer::Tick(float a_Dt)
+{
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
+ {
+ // Only tick chunks that are valid and have clients:
+ if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients())
+ {
+ m_Chunks[i]->Tick(a_Dt);
+ }
+ } // for i - m_Chunks[]
+}
+
+
+
+
+
+void cChunkMap::cChunkLayer::RemoveClient(cClientHandle * a_Client)
+{
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
+ {
+ if (m_Chunks[i] != NULL)
+ {
+ m_Chunks[i]->RemoveClient(a_Client);
+ }
+ } // for i - m_Chunks[]
+}
+
+
+
+
+
+bool cChunkMap::cChunkLayer::ForEachEntity(cEntityCallback & a_Callback)
+{
+ // Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
+ {
+ if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid())
+ {
+ if (!m_Chunks[i]->ForEachEntity(a_Callback))
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+
+
+
+bool cChunkMap::cChunkLayer::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackReturn)
+{
+ // Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found.
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
+ {
+ if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid())
+ {
+ if (m_Chunks[i]->DoWithEntityByID(a_EntityID, a_Callback, a_CallbackReturn))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cChunkMap::cChunkLayer::HasEntity(int a_EntityID)
+{
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
+ {
+ if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid())
+ {
+ if (m_Chunks[i]->HasEntity(a_EntityID))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+
+
+
+int cChunkMap::cChunkLayer::GetNumChunksLoaded(void) const
+{
+ int NumChunks = 0;
+ for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i )
+ {
+ if (m_Chunks[i] != NULL)
+ {
+ NumChunks++;
+ }
+ } // for i - m_Chunks[]
+ return NumChunks;
+}
+
+
+
+
+
+void cChunkMap::cChunkLayer::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const
+{
+ int NumValid = 0;
+ int NumDirty = 0;
+ for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i )
+ {
+ if (m_Chunks[i] == NULL)
+ {
+ continue;
+ }
+ NumValid++;
+ if (m_Chunks[i]->IsDirty())
+ {
+ NumDirty++;
+ }
+ } // for i - m_Chunks[]
+ a_NumChunksValid = NumValid;
+ a_NumChunksDirty = NumDirty;
+}
+
+
+
+
+
+void cChunkMap::cChunkLayer::Save(void)
+{
+ cWorld * World = m_Parent->GetWorld();
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i)
+ {
+ if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->IsDirty())
+ {
+ World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosY(), m_Chunks[i]->GetPosZ());
+ }
+ } // for i - m_Chunks[]
+}
+
+
+
+
+
+void cChunkMap::cChunkLayer::UnloadUnusedChunks(void)
+{
+ for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
+ {
+ if (
+ (m_Chunks[i] != NULL) && // Is valid
+ (m_Chunks[i]->CanUnload()) && // Can unload
+ !cPluginManager::Get()->CallHookChunkUnloading(m_Parent->GetWorld(), m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosZ()) // Plugins agree
+ )
+ {
+ // The cChunk destructor calls our GetChunk() while removing its entities
+ // so we still need to be able to return the chunk. Therefore we first delete, then NULLify
+ // Doing otherwise results in bug http://forum.mc-server.org/showthread.php?tid=355
+ delete m_Chunks[i];
+ m_Chunks[i] = NULL;
+ }
+ } // for i - m_Chunks[]
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cChunkStay:
+
+cChunkStay::cChunkStay(cWorld * a_World) :
+ m_World(a_World),
+ m_IsEnabled(false)
+{
+}
+
+
+
+
+
+cChunkStay::~cChunkStay()
+{
+ Clear();
+}
+
+
+
+
+
+void cChunkStay::Clear(void)
+{
+ if (m_IsEnabled)
+ {
+ Disable();
+ }
+ m_Chunks.clear();
+}
+
+
+
+
+
+void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ ASSERT(!m_IsEnabled);
+
+ for (cChunkCoordsList::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ {
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
+ {
+ // Already present
+ return;
+ }
+ } // for itr - Chunks[]
+ m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
+}
+
+
+
+
+
+void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ ASSERT(!m_IsEnabled);
+
+ for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ {
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
+ {
+ // Found, un-"stay"
+ m_Chunks.erase(itr);
+ return;
+ }
+ } // for itr - m_Chunks[]
+}
+
+
+
+
+
+void cChunkStay::Enable(void)
+{
+ ASSERT(!m_IsEnabled);
+
+ m_World->ChunksStay(*this, true);
+ m_IsEnabled = true;
+}
+
+
+
+
+
+void cChunkStay::Load(void)
+{
+ for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ {
+ m_World->TouchChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ } // for itr - m_Chunks[]
+}
+
+
+
+
+
+void cChunkStay::Disable(void)
+{
+ ASSERT(m_IsEnabled);
+
+ m_World->ChunksStay(*this, false);
+ m_IsEnabled = false;
+}
+
+
+
+
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
new file mode 100644
index 000000000..2a1d78ff8
--- /dev/null
+++ b/src/ChunkMap.h
@@ -0,0 +1,439 @@
+
+// cChunkMap.h
+
+// Interfaces to the cChunkMap class representing the chunk storage for a single world
+
+#pragma once
+
+#include "ChunkDef.h"
+
+
+
+
+
+class cWorld;
+class cItem;
+class MTRand;
+class cChunkStay;
+class cChunk;
+class cPlayer;
+class cChestEntity;
+class cDispenserEntity;
+class cDropperEntity;
+class cDropSpenserEntity;
+class cFurnaceEntity;
+class cPawn;
+class cPickup;
+class cChunkDataSerializer;
+class cBlockArea;
+class cMobCensus;
+class cMobSpawner;
+
+typedef std::list<cClientHandle *> cClientHandleList;
+typedef cChunk * cChunkPtr;
+typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cBlockEntity> cBlockEntityCallback;
+typedef cItemCallback<cChestEntity> cChestCallback;
+typedef cItemCallback<cDispenserEntity> cDispenserCallback;
+typedef cItemCallback<cDropperEntity> cDropperCallback;
+typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback;
+typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
+typedef cItemCallback<cChunk> cChunkCallback;
+
+
+
+
+
+class cChunkMap
+{
+public:
+
+ static const int LAYER_SIZE = 32;
+
+ cChunkMap(cWorld* a_World );
+ ~cChunkMap();
+
+ // Broadcast respective packets to all clients of the chunk where the event is taking place
+ // (Please keep these alpha-sorted)
+ void BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle);
+ void BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL);
+ void BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude = NULL);
+ void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude);
+ void BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL);
+ void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL);
+ void BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL);
+ void BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8
+ void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL);
+ void BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL);
+ void BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ );
+
+ /// Sends the block entity, if it is at the coords specified, to a_Client
+ void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client);
+
+ /// a_Player rclked block entity at the coords specified, handle it
+ void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z);
+
+ /// Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback
+ bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback);
+
+ /// Wakes up simulators for the specified block
+ void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Wakes up the simulators for the specified area of blocks
+ void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ);
+
+ void MarkChunkDirty (int a_ChunkX, int a_ChunkZ);
+ void MarkChunkSaving (int a_ChunkX, int a_ChunkZ);
+ void MarkChunkSaved (int a_ChunkX, int a_ChunkZ);
+
+ /** Sets the chunk data as either loaded from the storage or generated.
+ a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted.
+ a_BiomeMap is optional, if not present, biomes will be calculated by the generator
+ a_HeightMap is optional, if not present, will be calculated.
+ If a_MarkDirty is set, the chunk is set as dirty (used after generating)
+ */
+ void SetChunkData(
+ int a_ChunkX, int a_ChunkZ,
+ const BLOCKTYPE * a_BlockTypes,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
+ const cChunkDef::HeightMap * a_HeightMap,
+ const cChunkDef::BiomeMap & a_BiomeMap,
+ cBlockEntityList & a_BlockEntities,
+ bool a_MarkDirty
+ );
+
+ void ChunkLighted(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
+ );
+
+ bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback);
+
+ /// Copies the chunk's blocktypes into a_Blocks; returns true if successful
+ bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks);
+
+ bool IsChunkValid (int a_ChunkX, int a_ChunkZ);
+ bool HasChunkAnyClients (int a_ChunkX, int a_ChunkZ);
+ int GetHeight (int a_BlockX, int a_BlockZ); // Waits for the chunk to get loaded / generated
+ bool TryGetHeight (int a_BlockX, int a_BlockZ, int & a_Height); // Returns false if chunk not loaded / generated
+ void FastSetBlocks (sSetBlockList & a_BlockList);
+ void CollectPickupsByPlayer(cPlayer * a_Player);
+
+ BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ);
+ NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ);
+ NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ);
+ NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ);
+ void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockMeta);
+ void SetBlock (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta);
+ void QueueSetBlock (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick);
+ bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
+ bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);
+
+ /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType
+ void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType);
+
+ /// Special function used for growing trees, replaces only blocks that tree may overwrite
+ void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks);
+
+ EMCSBiome GetBiomeAt (int a_BlockX, int a_BlockZ);
+
+ /// 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.
+ 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);
+
+ /// 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);
+
+ /// Compares clients of two chunks, calls the callback accordingly
+ void CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClientDiffCallback & a_Callback);
+
+ /// Adds client to a chunk, if not already present; returns true if added, false if present
+ bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
+
+ /// Removes the client from the chunk
+ void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
+
+ /// Removes the client from all chunks it is present in
+ void RemoveClientFromChunks(cClientHandle * a_Client);
+
+ /// Adds the entity to its appropriate chunk, takes ownership of the entity pointer
+ void AddEntity(cEntity * a_Entity);
+
+ /// Returns true if the entity with specified ID is present in the chunks
+ bool HasEntity(int a_EntityID);
+
+ /// Removes the entity from its appropriate chunk
+ void RemoveEntity(cEntity * a_Entity);
+
+ /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true
+ bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true
+ bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible
+
+ /// Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates
+ void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected);
+
+ /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false.
+ bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for each block entity in the specified chunk; returns true if all block entities processed, false if the callback aborted by returning true
+ bool ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true
+ bool ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true
+ bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback);
+
+ /// Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true
+ bool ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback);
+
+ /// Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true
+ bool ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback);
+
+ /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true
+ bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found
+ bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Lua-acessible
+
+ /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found
+ bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible
+
+ /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found
+ bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found
+ bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found
+ bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found
+ bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible
+
+ /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
+ bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible
+
+ /// Touches the chunk, causing it to be loaded or generated
+ void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before)
+ bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid()
+ void LoadChunks(const cChunkCoordsList & a_Chunks);
+
+ /// Marks the chunk as failed-to-load
+ void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Sets the sign text. Returns true if sign text changed.
+ bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
+
+ /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable; to be used only by cChunkStay!
+ void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true);
+
+ /// Marks the chunk as being regenerated - all its clients want that chunk again (used by cWorld::RegenerateChunk() )
+ void MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ);
+
+ bool IsChunkLighted(int a_ChunkX, int a_ChunkZ);
+
+ /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully
+ bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback);
+
+ /// Writes the block area into the specified coords. Returns true if all chunks have been processed. Prefer cBlockArea::Write() instead.
+ bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes);
+
+ /// Returns the number of valid chunks and the number of dirty chunks
+ void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty);
+
+ /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem)
+ void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand);
+
+ /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config
+ void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
+
+ /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config
+ void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
+
+ /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call
+ void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player
+ void CollectMobCensus(cMobCensus& a_ToFill);
+
+ /// Try to Spawn Monsters inside all Chunks
+ void SpawnMobs(cMobSpawner& a_MobSpawner);
+
+ void Tick(float a_Dt);
+
+ void UnloadUnusedChunks(void);
+ void SaveAllChunks(void);
+
+ cWorld * GetWorld(void) { return m_World; }
+
+ int GetNumChunks(void);
+
+ void ChunkValidated(void); // Called by chunks that have become valid
+
+ /// Queues the specified block for ticking (block update)
+ void QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Returns the CS for locking the chunkmap; only cWorld::cLock may use this function!
+ cCriticalSection & GetCS(void) { return m_CSLayers; }
+
+private:
+
+ friend class cChunk; // The chunks can manipulate neighbors while in their Tick() method, using LockedGetBlock() and LockedSetBlock()
+
+ class cChunkLayer
+ {
+ public:
+ cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent);
+ ~cChunkLayer();
+
+ /// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check
+ cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ );
+
+ /// Returns the specified chunk, or NULL if not created yet
+ cChunk * FindChunk(int a_ChunkX, int a_ChunkZ);
+
+ int GetX(void) const {return m_LayerX; }
+ int GetZ(void) const {return m_LayerZ; }
+
+ int GetNumChunksLoaded(void) const ;
+
+ void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const;
+
+ void Save(void);
+ void UnloadUnusedChunks(void);
+
+ /// Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player
+ void CollectMobCensus(cMobCensus& a_ToFill);
+ /// Try to Spawn Monsters inside all Chunks
+ void SpawnMobs(cMobSpawner& a_MobSpawner);
+
+ void Tick(float a_Dt);
+
+ void RemoveClient(cClientHandle * a_Client);
+
+ /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true
+ bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible
+
+ /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found.
+ bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackReturn); // Lua-accessible
+
+ /// Returns true if there is an entity with the specified ID within this layer's chunks
+ bool HasEntity(int a_EntityID);
+
+ protected:
+
+ cChunkPtr m_Chunks[LAYER_SIZE * LAYER_SIZE];
+ int m_LayerX;
+ int m_LayerZ;
+ cChunkMap * m_Parent;
+ int m_NumChunksLoaded;
+ };
+
+ typedef std::list<cChunkLayer *> cChunkLayerList;
+
+ /// Finds the cChunkLayer object responsible for the specified chunk; returns NULL if not found. Assumes m_CSLayers is locked.
+ cChunkLayer * FindLayerForChunk(int a_ChunkX, int a_ChunkZ);
+
+ /// Returns the specified cChunkLayer object; returns NULL if not found. Assumes m_CSLayers is locked.
+ cChunkLayer * FindLayer(int a_LayerX, int a_LayerZ);
+
+ /// Returns the cChunkLayer object responsible for the specified chunk; creates it if not found.
+ cChunkLayer * GetLayerForChunk (int a_ChunkX, int a_ChunkZ);
+
+ /// Returns the specified cChunkLayer object; creates it if not found.
+ cChunkLayer * GetLayer(int a_LayerX, int a_LayerZ);
+
+ void RemoveLayer(cChunkLayer * a_Layer);
+
+ cCriticalSection m_CSLayers;
+ cChunkLayerList m_Layers;
+ cEvent m_evtChunkValid; // Set whenever any chunk becomes valid, via ChunkValidated()
+
+ cWorld * m_World;
+
+ cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid
+ cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate
+ cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate
+
+ /// Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
+ bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
+
+ /// Gets a block type in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
+ bool LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType);
+
+ /// Gets a block meta in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
+ bool LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE & a_BlockMeta);
+
+ /// Sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
+ bool LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /// Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
+ bool LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /// Locates a chunk ptr in the chunkmap; doesn't create it when not found; assumes m_CSLayers is locked. To be called only from cChunkMap.
+ cChunk * FindChunk(int a_ChunkX, int a_ChunkZ);
+};
+
+
+
+
+
+/** Makes chunks stay loaded until this object is cleared or destroyed
+Works by setting internal flags in the cChunk that it should not be unloaded.
+To optimize for speed, cChunkStay has an Enabled flag, it will "stay" the chunks only when enabled and it will refuse manipulations when enabled
+The object itself is not made thread-safe, it's supposed to be used from a single thread only.
+*/
+class cChunkStay
+{
+public:
+ cChunkStay(cWorld * a_World);
+ ~cChunkStay();
+
+ void Clear(void);
+
+ void Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ void Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ void Enable(void);
+ void Disable(void);
+
+ /// Queues each chunk in m_Chunks[] for loading / generating
+ void Load(void);
+
+ // Allow cChunkStay be passed to functions expecting a const cChunkCoordsList &
+ operator const cChunkCoordsList(void) const {return m_Chunks; }
+
+protected:
+
+ cWorld * m_World;
+
+ bool m_IsEnabled;
+
+ cChunkCoordsList m_Chunks;
+} ;
+
+
+
+
diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp
new file mode 100644
index 000000000..005cfe29d
--- /dev/null
+++ b/src/ChunkSender.cpp
@@ -0,0 +1,295 @@
+
+// ChunkSender.cpp
+
+// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients
+
+
+
+
+
+#include "Globals.h"
+#include "ChunkSender.h"
+#include "World.h"
+#include "BlockEntities/BlockEntity.h"
+#include "Protocol/ChunkDataSerializer.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cNotifyChunkSender:
+
+void cNotifyChunkSender::Call(int a_ChunkX, int a_ChunkZ)
+{
+ m_ChunkSender->ChunkReady(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cChunkSender:
+
+cChunkSender::cChunkSender(void) :
+ super("ChunkSender"),
+ m_World(NULL),
+ m_Notify(NULL),
+ m_RemoveCount(0)
+{
+ m_Notify.SetChunkSender(this);
+}
+
+
+
+
+
+cChunkSender::~cChunkSender()
+{
+ Stop();
+}
+
+
+
+
+
+bool cChunkSender::Start(cWorld * a_World)
+{
+ m_ShouldTerminate = false;
+ m_World = a_World;
+ return super::Start();
+}
+
+
+
+
+
+void cChunkSender::Stop(void)
+{
+ m_ShouldTerminate = true;
+ m_evtQueue.Set();
+ Wait();
+}
+
+
+
+
+
+void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkZ)
+{
+ // This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check
+ {
+ cCSLock Lock(m_CS);
+ m_ChunksReady.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
+ }
+ m_evtQueue.Set();
+}
+
+
+
+
+
+void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
+{
+ ASSERT(a_Client != NULL);
+ {
+ cCSLock Lock(m_CS);
+ if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)) != m_SendChunks.end())
+ {
+ // Already queued, bail out
+ return;
+ }
+ m_SendChunks.push_back(sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client));
+ }
+ m_evtQueue.Set();
+}
+
+
+
+
+
+void cChunkSender::RemoveClient(cClientHandle * a_Client)
+{
+ {
+ cCSLock Lock(m_CS);
+ for (sSendChunkList::iterator itr = m_SendChunks.begin(); itr != m_SendChunks.end();)
+ {
+ if (itr->m_Client == a_Client)
+ {
+ itr = m_SendChunks.erase(itr);
+ continue;
+ }
+ ++itr;
+ } // for itr - m_SendChunks[]
+ m_RemoveCount++;
+ }
+ m_evtQueue.Set();
+ m_evtRemoved.Wait(); // Wait for removal confirmation
+}
+
+
+
+
+
+void cChunkSender::Execute(void)
+{
+ while (!m_ShouldTerminate)
+ {
+ cCSLock Lock(m_CS);
+ while (m_ChunksReady.empty() && m_SendChunks.empty())
+ {
+ int RemoveCount = m_RemoveCount;
+ m_RemoveCount = 0;
+ cCSUnlock Unlock(Lock);
+ for (int i = 0; i < RemoveCount; i++)
+ {
+ m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted
+ }
+ m_evtQueue.Wait();
+ if (m_ShouldTerminate)
+ {
+ return;
+ }
+ } // while (empty)
+
+ if (!m_ChunksReady.empty())
+ {
+ // Take one from the queue:
+ cChunkCoords Coords(m_ChunksReady.front());
+ m_ChunksReady.pop_front();
+ Lock.Unlock();
+
+ SendChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, NULL);
+ }
+ else
+ {
+ // Take one from the queue:
+ sSendChunk Chunk(m_SendChunks.front());
+ m_SendChunks.pop_front();
+ Lock.Unlock();
+
+ SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkY, Chunk.m_ChunkZ, Chunk.m_Client);
+ }
+ Lock.Lock();
+ int RemoveCount = m_RemoveCount;
+ m_RemoveCount = 0;
+ Lock.Unlock();
+ for (int i = 0; i < RemoveCount; i++)
+ {
+ m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted
+ }
+ } // while (!mShouldTerminate)
+}
+
+
+
+
+
+void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
+{
+ ASSERT(m_World != NULL);
+
+ // Ask the client if it still wants the chunk:
+ if (a_Client != NULL)
+ {
+ if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ))
+ {
+ return;
+ }
+ }
+
+ // If the chunk has no clients, no need to packetize it:
+ if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ))
+ {
+ return;
+ }
+
+ // If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating
+ if (!m_World->IsChunkValid(a_ChunkX, a_ChunkZ))
+ {
+ return;
+ }
+
+ // If the chunk is not lighted, queue it for relighting and get notified when it's ready:
+ if (!m_World->IsChunkLighted(a_ChunkX, a_ChunkZ))
+ {
+ m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, &m_Notify);
+ return;
+ }
+
+ // Query and prepare chunk data:
+ if (!m_World->GetChunkData(a_ChunkX, a_ChunkZ, *this))
+ {
+ return;
+ }
+ cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap);
+
+ // Send:
+ if (a_Client == NULL)
+ {
+ m_World->BroadcastChunkData(a_ChunkX, a_ChunkZ, Data);
+ }
+ else
+ {
+ a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data);
+ }
+
+ // Send block-entity packets:
+ for (sBlockCoords::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ if (a_Client == NULL)
+ {
+ m_World->BroadcastBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ);
+ }
+ else
+ {
+ m_World->SendBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ, *a_Client);
+ }
+ } // for itr - m_Packets[]
+ m_BlockEntities.clear();
+
+ // TODO: Send entity spawn packets
+}
+
+
+
+
+
+void cChunkSender::BlockEntity(cBlockEntity * a_Entity)
+{
+ m_BlockEntities.push_back(sBlockCoord(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ()));
+}
+
+
+
+
+void cChunkSender::Entity(cEntity * a_Entity)
+{
+ // Nothing needed yet, perhaps in the future when we save entities into chunks we'd like to send them upon load, too ;)
+}
+
+
+
+
+
+void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
+{
+ for (int i = 0; i < ARRAYCOUNT(m_BiomeMap); i++)
+ {
+ if ((*a_BiomeMap)[i] < 255)
+ {
+ // Normal MC biome, copy as-is:
+ m_BiomeMap[i] = (unsigned char)((*a_BiomeMap)[i]);
+ }
+ else
+ {
+ // TODO: MCS-specific biome, need to map to some basic MC biome:
+ ASSERT(!"Unimplemented MCS-specific biome");
+ }
+ } // for i - m_BiomeMap[]
+}
+
+
+
+
diff --git a/src/ChunkSender.h b/src/ChunkSender.h
new file mode 100644
index 000000000..a26f764a7
--- /dev/null
+++ b/src/ChunkSender.h
@@ -0,0 +1,169 @@
+
+// ChunkSender.h
+
+// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients
+
+/*
+The whole thing is a thread that runs in a loop, waiting for either:
+ "finished chunks" (ChunkReady()), or
+ "chunks to send" (QueueSendChunkTo() )
+to come to a queue.
+And once they do, it requests the chunk data and sends it all away, either
+ broadcasting (ChunkReady), or
+ sends to a specific client (QueueSendChunkTo)
+Chunk data is queried using the cChunkDataCallback interface.
+It is cached inside the ChunkSender object during the query and then processed after the query ends.
+Note that the data needs to be compressed only *after* the query finishes,
+because the query callbacks run with ChunkMap's CS locked.
+
+A client may remove itself from all direct requests(QueueSendChunkTo()) by calling RemoveClient();
+this ensures that the client's Send() won't be called anymore by ChunkSender.
+Note that it may be called by world's BroadcastToChunk() if the client is still in the chunk.
+*/
+
+
+
+#pragma once
+
+#include "OSSupport/IsThread.h"
+#include "ChunkDef.h"
+
+
+
+
+
+class cWorld;
+class cClientHandle;
+
+
+
+
+
+// fwd:
+class cChunkSender;
+
+
+
+
+
+/// Callback that can be used to notify chunk sender upon another chunkcoord notification
+class cNotifyChunkSender :
+ public cChunkCoordCallback
+{
+ virtual void Call(int a_ChunkX, int a_ChunkZ) override;
+
+ cChunkSender * m_ChunkSender;
+public:
+ cNotifyChunkSender(cChunkSender * a_ChunkSender) : m_ChunkSender(a_ChunkSender) {}
+
+ void SetChunkSender(cChunkSender * a_ChunkSender)
+ {
+ m_ChunkSender = a_ChunkSender;
+ }
+} ;
+
+
+
+
+
+class cChunkSender:
+ public cIsThread,
+ public cChunkDataSeparateCollector
+{
+ typedef cIsThread super;
+public:
+ cChunkSender(void);
+ ~cChunkSender();
+
+ bool Start(cWorld * a_World);
+
+ void Stop(void);
+
+ /// Notifies that a chunk has become ready and it should be sent to all its clients
+ void ChunkReady(int a_ChunkX, int a_ChunkZ);
+
+ /// Queues a chunk to be sent to a specific client
+ void QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
+
+ /// Removes the a_Client from all waiting chunk send operations
+ void RemoveClient(cClientHandle * a_Client);
+
+protected:
+
+ /// Used for sending chunks to specific clients
+ struct sSendChunk
+ {
+ int m_ChunkX;
+ int m_ChunkY;
+ int m_ChunkZ;
+ cClientHandle * m_Client;
+
+ sSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) :
+ m_ChunkX(a_ChunkX),
+ m_ChunkY(a_ChunkY),
+ m_ChunkZ(a_ChunkZ),
+ m_Client(a_Client)
+ {
+ }
+
+ bool operator ==(const sSendChunk & a_Other)
+ {
+ return (
+ (a_Other.m_ChunkX == m_ChunkX) &&
+ (a_Other.m_ChunkY == m_ChunkY) &&
+ (a_Other.m_ChunkZ == m_ChunkZ) &&
+ (a_Other.m_Client == m_Client)
+ );
+ }
+ } ;
+ typedef std::list<sSendChunk> sSendChunkList;
+
+ struct sBlockCoord
+ {
+ int m_BlockX;
+ int m_BlockY;
+ int m_BlockZ;
+
+ sBlockCoord(int a_BlockX, int a_BlockY, int a_BlockZ) :
+ m_BlockX(a_BlockX),
+ m_BlockY(a_BlockY),
+ m_BlockZ(a_BlockZ)
+ {
+ }
+ } ;
+
+ typedef std::vector<sBlockCoord> sBlockCoords;
+
+ cWorld * m_World;
+
+ cCriticalSection m_CS;
+ cChunkCoordsList m_ChunksReady;
+ sSendChunkList m_SendChunks;
+ cEvent m_evtQueue; // Set when anything is added to m_ChunksReady
+ cEvent m_evtRemoved; // Set when removed clients are safe to be deleted
+ int m_RemoveCount; // Number of threads waiting for a client removal (m_evtRemoved needs to be set this many times)
+
+ cNotifyChunkSender m_Notify; // Used for chunks that don't have a valid lighting - they will be re-queued after lightcalc
+
+ // Data about the chunk that is being sent:
+ // NOTE that m_BlockData[] is inherited from the cChunkDataCollector
+ unsigned char m_BiomeMap[cChunkDef::Width * cChunkDef::Width];
+ sBlockCoords m_BlockEntities; // Coords of the block entities to send
+ // TODO: sEntityIDs m_Entities; // Entity-IDs of the entities to send
+
+ // cIsThread override:
+ virtual void Execute(void) override;
+
+ // cChunkDataCollector overrides:
+ // (Note that they are called while the ChunkMap's CS is locked - don't do heavy calculations here!)
+ virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) override;
+ virtual void Entity (cEntity * a_Entity) override;
+ virtual void BlockEntity (cBlockEntity * a_Entity) override;
+
+ /// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == NULL
+ void SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client);
+} ;
+
+
+
+
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
new file mode 100644
index 000000000..daf09d4ea
--- /dev/null
+++ b/src/ClientHandle.cpp
@@ -0,0 +1,2210 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "ClientHandle.h"
+#include "Server.h"
+#include "World.h"
+#include "Entities/Pickup.h"
+#include "PluginManager.h"
+#include "Entities/Player.h"
+#include "Inventory.h"
+#include "BlockEntities/ChestEntity.h"
+#include "BlockEntities/SignEntity.h"
+#include "UI/Window.h"
+#include "Item.h"
+#include "Piston.h"
+#include "Mobs/Monster.h"
+#include "ChatColor.h"
+#include "OSSupport/Socket.h"
+#include "OSSupport/Timer.h"
+#include "Items/ItemHandler.h"
+#include "Blocks/BlockHandler.h"
+#include "Blocks/BlockSlab.h"
+
+#include "Vector3f.h"
+#include "Vector3d.h"
+
+#include "Root.h"
+
+#include "Authenticator.h"
+#include "MersenneTwister.h"
+
+#include "Protocol/ProtocolRecognizer.h"
+
+
+
+
+
+#define AddPistonDir(x, y, z, dir, amount) switch (dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\
+ case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\
+ case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; }
+
+
+
+
+
+/// If the number of queued outgoing packets reaches this, the client will be kicked
+#define MAX_OUTGOING_PACKETS 2000
+
+/// How many explosions per single game tick are allowed
+static const int MAX_EXPLOSIONS_PER_TICK = 100;
+
+/// How many explosions in the recent history are allowed
+static const int MAX_RUNNING_SUM_EXPLOSIONS = cClientHandle::NUM_CHECK_EXPLOSIONS_TICKS * MAX_EXPLOSIONS_PER_TICK / 8;
+
+/// How many ticks before the socket is closed after the client is destroyed (#31)
+static const int TICKS_BEFORE_CLOSE = 20;
+
+
+
+
+
+#define RECI_RAND_MAX (1.f/RAND_MAX)
+inline int fRadRand(MTRand & r1, int a_BlockCoord)
+{
+ return a_BlockCoord * 32 + (int)(16 * ((float)r1.rand() * RECI_RAND_MAX) * 16 - 8);
+}
+
+
+
+
+
+int cClientHandle::s_ClientCount = 0;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cClientHandle:
+
+cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance)
+ : m_ViewDistance(a_ViewDistance)
+ , m_IPString(a_Socket->GetIPString())
+ , m_OutgoingData(64 KiB)
+ , m_Player(NULL)
+ , m_HasSentDC(false)
+ , m_TimeSinceLastPacket(0)
+ , m_bKeepThreadGoing(true)
+ , m_Ping(1000)
+ , m_PingID(1)
+ , m_TicksSinceDestruction(0)
+ , m_State(csConnected)
+ , m_LastStreamedChunkX(0x7fffffff) // bogus chunk coords to force streaming upon login
+ , m_LastStreamedChunkZ(0x7fffffff)
+ , m_ShouldCheckDownloaded(false)
+ , m_UniqueID(0)
+ , m_BlockDigAnimStage(-1)
+ , m_HasStartedDigging(false)
+ , m_CurrentExplosionTick(0)
+ , m_RunningSumExplosions(0)
+ , m_HasSentPlayerChunk(false)
+{
+ m_Protocol = new cProtocolRecognizer(this);
+
+ s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread
+ m_UniqueID = s_ClientCount;
+
+ cTimer t1;
+ m_LastPingTime = t1.GetNowTime();
+
+ LOGD("New ClientHandle created at %p", this);
+}
+
+
+
+
+
+cClientHandle::~cClientHandle()
+{
+ ASSERT(m_State >= csDestroyedWaiting); // Has Destroy() been called?
+
+ LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), this);
+
+ // Remove from cSocketThreads, we're not to be called anymore:
+ cRoot::Get()->GetServer()->ClientDestroying(this);
+
+ {
+ cCSLock Lock(m_CSChunkLists);
+ m_LoadedChunks.clear();
+ m_ChunksToSend.clear();
+ }
+
+ if (m_Player != NULL)
+ {
+ cWorld * World = m_Player->GetWorld();
+ if (!m_Username.empty() && (World != NULL))
+ {
+ // Send the Offline PlayerList packet:
+ World->BroadcastPlayerListItem(*m_Player, false, this);
+ }
+ if (World != NULL)
+ {
+ World->RemovePlayer(m_Player);
+ m_Player->Destroy();
+ }
+ delete m_Player;
+ m_Player = NULL;
+ }
+
+ if (!m_HasSentDC)
+ {
+ SendDisconnect("Server shut down? Kthnxbai");
+ }
+
+ // Queue all remaining outgoing packets to cSocketThreads:
+ {
+ cCSLock Lock(m_CSOutgoingData);
+ AString Data;
+ m_OutgoingData.ReadAll(Data);
+ m_OutgoingData.CommitRead();
+ cRoot::Get()->GetServer()->WriteToClient(this, Data);
+ }
+
+ // Queue the socket to close as soon as it sends all outgoing data:
+ cRoot::Get()->GetServer()->QueueClientClose(this);
+ cRoot::Get()->GetServer()->RemoveClient(this);
+
+ delete m_Protocol;
+ m_Protocol = NULL;
+
+ LOGD("ClientHandle at %p deleted", this);
+}
+
+
+
+
+
+void cClientHandle::Destroy(void)
+{
+ {
+ cCSLock Lock(m_CSDestroyingState);
+ if (m_State >= csDestroying)
+ {
+ // Already called
+ return;
+ }
+ m_State = csDestroying;
+ }
+
+ // DEBUG:
+ LOGD("%s: client %p, \"%s\"", __FUNCTION__, this, m_Username.c_str());
+
+ if ((m_Player != NULL) && (m_Player->GetWorld() != NULL))
+ {
+ RemoveFromAllChunks();
+ m_Player->GetWorld()->RemoveClientFromChunkSender(this);
+ }
+ m_State = csDestroyedWaiting;
+}
+
+
+
+
+
+void cClientHandle::Kick(const AString & a_Reason)
+{
+ if (m_State >= csAuthenticating) // Don't log pings
+ {
+ LOG("Kicking user \"%s\" for \"%s\"", m_Username.c_str(), StripColorCodes(a_Reason).c_str());
+ }
+ SendDisconnect(a_Reason);
+}
+
+
+
+
+
+void cClientHandle::Authenticate(void)
+{
+ if (m_State != csAuthenticating)
+ {
+ return;
+ }
+
+ ASSERT( m_Player == NULL );
+
+ // Spawn player (only serversided, so data is loaded)
+ m_Player = new cPlayer(this, GetUsername());
+
+ cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName());
+ if (World == NULL)
+ {
+ World = cRoot::Get()->GetDefaultWorld();
+ }
+
+ if (m_Player->GetGameMode() == eGameMode_NotSet)
+ {
+ m_Player->LoginSetGameMode(World->GetGameMode());
+ }
+
+ m_Player->SetIP (m_IPString);
+
+ cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player);
+
+ m_ConfirmPosition = m_Player->GetPosition();
+
+ // Return a server login packet
+ m_Protocol->SendLogin(*m_Player, *World);
+
+ // Send Weather if raining:
+ if ((World->GetWeather() == 1) || (World->GetWeather() == 2))
+ {
+ m_Protocol->SendWeather(World->GetWeather());
+ }
+
+ // Send time
+ m_Protocol->SendTimeUpdate(World->GetWorldAge(), World->GetTimeOfDay());
+
+ // Send contents of the inventory window
+ m_Protocol->SendWholeInventory(*m_Player->GetWindow());
+
+ // Send health
+ m_Player->SendHealth();
+
+ // Send experience
+ m_Player->SendExperience();
+
+ // Send gamemode (1.6.1 movementSpeed):
+ SendGameMode(m_Player->GetGameMode());
+
+ m_Player->Initialize(World);
+ m_State = csAuthenticated;
+
+ // Broadcast this player's spawning to all other players in the same chunk
+ m_Player->GetWorld()->BroadcastSpawnEntity(*m_Player, this);
+
+ cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
+}
+
+
+
+
+
+void cClientHandle::StreamChunks(void)
+{
+ if ((m_State < csAuthenticated) || (m_State >= csDestroying))
+ {
+ return;
+ }
+
+ ASSERT(m_Player != NULL);
+
+ int ChunkPosX = FAST_FLOOR_DIV((int)m_Player->GetPosX(), cChunkDef::Width);
+ int ChunkPosZ = FAST_FLOOR_DIV((int)m_Player->GetPosZ(), cChunkDef::Width);
+ if ((ChunkPosX == m_LastStreamedChunkX) && (ChunkPosZ == m_LastStreamedChunkZ))
+ {
+ // Already streamed for this position
+ return;
+ }
+ m_LastStreamedChunkX = ChunkPosX;
+ m_LastStreamedChunkZ = ChunkPosZ;
+
+ LOGD("Streaming chunks centered on [%d, %d], view distance %d", ChunkPosX, ChunkPosZ, m_ViewDistance);
+
+ cWorld * World = m_Player->GetWorld();
+ ASSERT(World != NULL);
+
+ // Remove all loaded chunks that are no longer in range; deferred to out-of-CS:
+ cChunkCoordsList RemoveChunks;
+ {
+ cCSLock Lock(m_CSChunkLists);
+ for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();)
+ {
+ int RelX = (*itr).m_ChunkX - ChunkPosX;
+ int RelZ = (*itr).m_ChunkZ - ChunkPosZ;
+ if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance))
+ {
+ RemoveChunks.push_back(*itr);
+ itr = m_LoadedChunks.erase(itr);
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - m_LoadedChunks[]
+ for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();)
+ {
+ int RelX = (*itr).m_ChunkX - ChunkPosX;
+ int RelZ = (*itr).m_ChunkZ - ChunkPosZ;
+ if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance))
+ {
+ itr = m_ChunksToSend.erase(itr);
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - m_ChunksToSend[]
+ }
+ for (cChunkCoordsList::iterator itr = RemoveChunks.begin(); itr != RemoveChunks.end(); ++itr)
+ {
+ World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
+ m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
+ } // for itr - RemoveChunks[]
+
+ // Add all chunks that are in range and not yet in m_LoadedChunks:
+ // Queue these smartly - from the center out to the edge
+ for (int d = 0; d <= m_ViewDistance; ++d) // cycle through (square) distance, from nearest to furthest
+ {
+ // For each distance add chunks in a hollow square centered around current position:
+ for (int i = -d; i <= d; ++i)
+ {
+ StreamChunk(ChunkPosX + d, ChunkPosZ + i);
+ StreamChunk(ChunkPosX - d, ChunkPosZ + i);
+ } // for i
+ for (int i = -d + 1; i < d; ++i)
+ {
+ StreamChunk(ChunkPosX + i, ChunkPosZ + d);
+ StreamChunk(ChunkPosX + i, ChunkPosZ - d);
+ } // for i
+ } // for d
+
+ // Touch chunks GENERATEDISTANCE ahead to let them generate:
+ for (int d = m_ViewDistance + 1; d <= m_ViewDistance + GENERATEDISTANCE; ++d) // cycle through (square) distance, from nearest to furthest
+ {
+ // For each distance touch chunks in a hollow square centered around current position:
+ for (int i = -d; i <= d; ++i)
+ {
+ World->TouchChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i);
+ World->TouchChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i);
+ } // for i
+ for (int i = -d + 1; i < d; ++i)
+ {
+ World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d);
+ World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d);
+ } // for i
+ } // for d
+}
+
+
+
+
+void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ)
+{
+ if (m_State >= csDestroying)
+ {
+ // Don't stream chunks to clients that are being destroyed
+ return;
+ }
+
+ cWorld * World = m_Player->GetWorld();
+ ASSERT(World != NULL);
+
+ if (World->AddChunkClient(a_ChunkX, a_ChunkZ, this))
+ {
+ {
+ cCSLock Lock(m_CSChunkLists);
+ m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
+ m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
+ }
+ World->SendChunkTo(a_ChunkX, a_ChunkZ, this);
+ }
+}
+
+
+
+
+
+// Removes the client from all chunks. Used when switching worlds or destroying the player
+void cClientHandle::RemoveFromAllChunks()
+{
+ cWorld * World = m_Player->GetWorld();
+ if (World != NULL)
+ {
+ World->RemoveClientFromChunks(this);
+ }
+
+ {
+ cCSLock Lock(m_CSChunkLists);
+ m_LoadedChunks.clear();
+ m_ChunksToSend.clear();
+
+ // Also reset the LastStreamedChunk coords to bogus coords,
+ // so that all chunks are streamed in subsequent StreamChunks() call (FS #407)
+ m_LastStreamedChunkX = 0x7fffffff;
+ m_LastStreamedChunkZ = 0x7fffffff;
+ }
+}
+
+
+
+
+
+void cClientHandle::HandlePing(void)
+{
+ // Somebody tries to retrieve information about the server
+ AString Reply;
+ Printf(Reply, "%s%s%i%s%i",
+ cRoot::Get()->GetServer()->GetDescription().c_str(),
+ cChatColor::Delimiter.c_str(),
+ cRoot::Get()->GetServer()->GetNumPlayers(),
+ cChatColor::Delimiter.c_str(),
+ cRoot::Get()->GetServer()->GetMaxPlayers()
+ );
+ Kick(Reply.c_str());
+}
+
+
+
+
+
+bool cClientHandle::HandleLogin(int a_ProtocolVersion, const AString & a_Username)
+{
+ LOGD("LOGIN %s", a_Username.c_str());
+ m_Username = a_Username;
+
+ if (cRoot::Get()->GetPluginManager()->CallHookLogin(this, a_ProtocolVersion, a_Username))
+ {
+ Destroy();
+ return false;
+ }
+
+ // Schedule for authentication; until then, let them wait (but do not block)
+ m_State = csAuthenticating;
+ cRoot::Get()->GetAuthenticator().Authenticate(GetUniqueID(), GetUsername(), m_Protocol->GetAuthServerID());
+ return true;
+}
+
+
+
+
+
+void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem)
+{
+ // This is for creative Inventory changes
+ if (!m_Player->IsGameModeCreative())
+ {
+ LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str());
+ return;
+ }
+ if (m_Player->GetWindow()->GetWindowType() != cWindow::wtInventory)
+ {
+ LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str());
+ return;
+ }
+
+ m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, (a_SlotNum >= 0) ? caLeftClick : caLeftClickOutside, a_HeldItem);
+}
+
+
+
+
+
+void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround)
+{
+ if ((m_Player == NULL) || (m_State != csPlaying))
+ {
+ // The client hasn't been spawned yet and sends nonsense, we know better
+ return;
+ }
+
+ /*
+ // TODO: Invalid stance check
+ if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65))
+ {
+ LOGD("Invalid stance");
+ SendPlayerMoveLook();
+ return;
+ }
+ */
+
+ // If the player has moved too far, "repair" them:
+ Vector3d Pos(a_PosX, a_PosY, a_PosZ);
+ if ((m_Player->GetPosition() - Pos).SqrLength() > 100 * 100)
+ {
+ LOGD("Too far away (%0.2f), \"repairing\" the client", (m_Player->GetPosition() - Pos).Length());
+ SendPlayerMoveLook();
+ return;
+ }
+
+ // If a jump just started, process food exhaustion:
+ if ((a_PosY > m_Player->GetPosY()) && !a_IsOnGround && m_Player->IsOnGround())
+ {
+ // we only add this exhaustion if the player is not swimming - otherwise we end up with both jump + swim exhaustion
+
+ if (!m_Player->IsSwimming())
+ {
+ m_Player->AddFoodExhaustion(m_Player->IsSprinting() ? 0.8 : 0.2);
+ }
+ }
+
+ m_Player->MoveTo(Pos);
+ m_Player->SetStance(a_Stance);
+ m_Player->SetTouchGround(a_IsOnGround);
+}
+
+
+
+
+
+void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status)
+{
+ LOGD("HandleLeftClick: {%i, %i, %i}; Face: %i; Stat: %i",
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status
+ );
+
+ cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
+ if (PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status))
+ {
+ // A plugin doesn't agree with the action, replace the block on the client and quit:
+ m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
+ return;
+ }
+
+ if (!CheckBlockInteractionsRate())
+ {
+ // Too many interactions per second, simply ignore. Probably a hacked client, so don't even send bak the block
+ return;
+ }
+
+ switch (a_Status)
+ {
+ case DIG_STATUS_DROP_HELD: // Drop held item
+ {
+ if (PlgMgr->CallHookPlayerTossingItem(*m_Player))
+ {
+ // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
+ return;
+ }
+ m_Player->TossItem(false);
+ return;
+ }
+
+ case DIG_STATUS_SHOOT_EAT:
+ {
+ cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem());
+ if (ItemHandler->IsFood())
+ {
+ m_Player->AbortEating();
+ return;
+ }
+ else
+ {
+ if (PlgMgr->CallHookPlayerShooting(*m_Player))
+ {
+ // A plugin doesn't agree with the action. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
+ return;
+ }
+ ItemHandler->OnItemShoot(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ }
+ return;
+ }
+
+ case DIG_STATUS_STARTED:
+ {
+ BLOCKTYPE OldBlock;
+ NIBBLETYPE OldMeta;
+ m_Player->GetWorld()->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta);
+ HandleBlockDigStarted(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta);
+ return;
+ }
+
+ case DIG_STATUS_FINISHED:
+ {
+ BLOCKTYPE OldBlock;
+ NIBBLETYPE OldMeta;
+ m_Player->GetWorld()->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta);
+ HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta);
+ return;
+ }
+
+ case DIG_STATUS_CANCELLED:
+ {
+ // Block breaking cancelled by player
+ return;
+ }
+
+ default:
+ {
+ ASSERT(!"Unhandled DIG_STATUS");
+ return;
+ }
+ } // switch (a_Status)
+}
+
+
+
+
+
+void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta)
+{
+ if (
+ m_HasStartedDigging &&
+ (a_BlockX == m_LastDigBlockX) &&
+ (a_BlockY == m_LastDigBlockY) &&
+ (a_BlockZ == m_LastDigBlockZ)
+ )
+ {
+ // It is a duplicate packet, drop it right away
+ return;
+ }
+
+ if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta))
+ {
+ // A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
+ m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
+ return;
+ }
+
+ // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig/aim bug in the client:
+ m_HasStartedDigging = true;
+ m_LastDigBlockX = a_BlockX;
+ m_LastDigBlockY = a_BlockY;
+ m_LastDigBlockZ = a_BlockZ;
+
+ if (
+ (m_Player->IsGameModeCreative()) || // In creative mode, digging is done immediately
+ g_BlockOneHitDig[a_OldBlock] // One-hit blocks get destroyed immediately, too
+ )
+ {
+ HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta);
+ return;
+ }
+
+ // Start dig animation
+ // TODO: calculate real animation speed
+ // TODO: Send animation packets even without receiving any other packets
+ m_BlockDigAnimSpeed = 10;
+ m_BlockDigAnimX = a_BlockX;
+ m_BlockDigAnimY = a_BlockY;
+ m_BlockDigAnimZ = a_BlockZ;
+ m_BlockDigAnimStage = 0;
+ m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 0, this);
+
+ cWorld * World = m_Player->GetWorld();
+
+ cBlockHandler * Handler = cBlockHandler::GetBlockHandler(a_OldBlock);
+ Handler->OnDigging(World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
+
+ cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem());
+ ItemHandler->OnDiggingBlock(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ // Check for clickthrough-blocks:
+ if (a_BlockFace != BLOCK_FACE_NONE)
+ {
+ int pX = a_BlockX;
+ int pY = a_BlockY;
+ int pZ = a_BlockZ;
+ AddFaceDirection(pX, pY, pZ, a_BlockFace);
+
+ Handler = cBlockHandler::GetBlockHandler(World->GetBlock(pX, pY, pZ));
+
+ // 2013_01_05 _X: This looks weird
+ // Why do we ask the block "behind" the one being clicked if it is clicked through? Shouldn't we ask the primary block instead?
+ if (Handler->IsClickedThrough())
+ {
+ Handler->OnDigging(World, m_Player, pX, pY, pZ);
+ }
+ }
+}
+
+
+
+
+
+void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta)
+{
+ if (
+ !m_HasStartedDigging || // Hasn't received the DIG_STARTED packet
+ (m_LastDigBlockX != a_BlockX) || // DIG_STARTED has had different pos
+ (m_LastDigBlockY != a_BlockY) ||
+ (m_LastDigBlockZ != a_BlockZ)
+ )
+ {
+ LOGD("Prevented a dig/aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)",
+ a_BlockX, a_BlockY, a_BlockZ,
+ m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ,
+ m_HasStartedDigging
+ );
+ return;
+ }
+
+ m_HasStartedDigging = false;
+ if (m_BlockDigAnimStage != -1)
+ {
+ // End dig animation
+ m_BlockDigAnimStage = -1;
+ // It seems that 10 ends block animation
+ m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 10, this);
+ }
+
+ cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem());
+
+ if (a_OldBlock == E_BLOCK_AIR)
+ {
+ LOGD("Dug air - what the function?");
+ return;
+ }
+
+ cWorld * World = m_Player->GetWorld();
+ ItemHandler->OnBlockDestroyed(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ);
+ // The ItemHandler is also responsible for spawning the pickups
+
+ BlockHandler(a_OldBlock)->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
+ World->BroadcastSoundParticleEffect(2001, a_BlockX, a_BlockY, a_BlockZ, a_OldBlock, this);
+ World->DigBlock(a_BlockX, a_BlockY, a_BlockZ);
+
+ cRoot::Get()->GetPluginManager()->CallHookPlayerBrokenBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta);
+}
+
+
+
+
+
+void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem)
+{
+ LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s",
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str()
+ );
+
+ cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
+ if (PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
+ {
+ // A plugin doesn't agree with the action, replace the block on the client and quit:
+ if (a_BlockFace > -1)
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
+ }
+ return;
+ }
+
+ if (!CheckBlockInteractionsRate())
+ {
+ LOGD("Too many block interactions, aborting placement");
+ return;
+ }
+
+ const cItem & Equipped = m_Player->GetInventory().GetEquippedItem();
+
+ if ((Equipped.m_ItemType != a_HeldItem.m_ItemType) && (a_HeldItem.m_ItemType != -1))
+ {
+ // Only compare ItemType, not meta (torches have different metas)
+ // The -1 check is there because sometimes the client sends -1 instead of the held item
+ // ( http://forum.mc-server.org/showthread.php?tid=549&pid=4502#pid4502 )
+ LOGWARN("Player %s tried to place a block that was not equipped (exp %d, got %d)",
+ m_Username.c_str(), Equipped.m_ItemType, a_HeldItem.m_ItemType
+ );
+
+ // Let's send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block
+ if (a_BlockFace > -1)
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
+ }
+ return;
+ }
+
+ cWorld * World = m_Player->GetWorld();
+
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
+ cBlockHandler * BlockHandler = cBlockHandler::GetBlockHandler(BlockType);
+
+ if (BlockHandler->IsUseable() && !m_Player->IsCrouched())
+ {
+ if (PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
+ {
+ // A plugin doesn't agree with using the block, abort
+ return;
+ }
+ BlockHandler->OnUse(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
+ PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
+ return;
+ }
+
+ cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType);
+
+ if (ItemHandler->IsPlaceable())
+ {
+ HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
+ }
+ else if (ItemHandler->IsFood())
+ {
+ if (m_Player->IsSatiated())
+ {
+ // The player is satiated, they cannot eat
+ return;
+ }
+ m_Player->StartEating();
+ if (PlgMgr->CallHookPlayerEating(*m_Player))
+ {
+ // A plugin won't let us eat, abort (send the proper packets to the client, too):
+ m_Player->AbortEating();
+ return;
+ }
+ return;
+ }
+ else
+ {
+ if (PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
+ {
+ // A plugin doesn't agree with using the item, abort
+ return;
+ }
+ ItemHandler->OnItemUse(World, m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
+ }
+}
+
+
+
+
+
+void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler)
+{
+ if (a_BlockFace < 0)
+ {
+ // Clicked in air
+ return;
+ }
+
+ cWorld * World = m_Player->GetWorld();
+
+ BLOCKTYPE ClickedBlock;
+ NIBBLETYPE ClickedBlockMeta;
+ BLOCKTYPE EquippedBlock = (BLOCKTYPE)(m_Player->GetEquippedItem().m_ItemType);
+ 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 & 0x07)) && // 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, don't move them anywhere
+ }
+ else
+ {
+ // Check if the block ignores build collision (water, grass etc.):
+ cBlockHandler * Handler = cBlockHandler::GetBlockHandler(ClickedBlock);
+ if (Handler->DoesIgnoreBuildCollision())
+ {
+ Handler->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+
+ BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision())
+ {
+ 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;
+ }
+
+ BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+
+ // 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())
+ {
+ // Tried to place a block *into* another?
+ // Happens when you place a block aiming at side of block like torch or stem
+ 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_BlockY, m_Player);
+ 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);
+ return;
+ }
+
+ // The actual block placement:
+ World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
+ if (m_Player->GetGameMode() != gmCreative)
+ {
+ m_Player->GetInventory().RemoveOneEquippedItem();
+ }
+ NewBlock->OnPlacedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
+
+ // Step sound with 0.8f pitch is used as block placement sound
+ World->BroadcastSoundEffect(NewBlock->GetStepSound(), a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.8f);
+ 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
+
+ // If a command, perform it:
+ AString Message(a_Message);
+ if (cRoot::Get()->GetServer()->Command(*this, Message))
+ {
+ return;
+ }
+
+ // Not a command, broadcast as a simple message:
+ AString Msg;
+ Printf(Msg, "<%s%s%s> %s",
+ m_Player->GetColor().c_str(),
+ m_Player->GetName().c_str(),
+ cChatColor::White.c_str(),
+ Message.c_str()
+ );
+ m_Player->GetWorld()->BroadcastChat(Msg);
+}
+
+
+
+
+
+void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround)
+{
+ if ((m_Player == NULL) || (m_State != csPlaying))
+ {
+ return;
+ }
+
+ m_Player->SetRotation (a_Rotation);
+ m_Player->SetHeadYaw (a_Rotation);
+ m_Player->SetPitch (a_Pitch);
+ m_Player->SetTouchGround(a_IsOnGround);
+}
+
+
+
+
+
+void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround)
+{
+ if ((m_Player == NULL) || (m_State != csPlaying))
+ {
+ // The client hasn't been spawned yet and sends nonsense, we know better
+ return;
+ }
+
+ /*
+ // TODO: Invalid stance check
+ if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65))
+ {
+ LOGD("Invalid stance");
+ SendPlayerMoveLook();
+ return;
+ }
+ */
+
+ m_Player->MoveTo(Vector3d(a_PosX, a_PosY, a_PosZ));
+ m_Player->SetStance (a_Stance);
+ m_Player->SetTouchGround(a_IsOnGround);
+ m_Player->SetHeadYaw (a_Rotation);
+ m_Player->SetRotation (a_Rotation);
+ m_Player->SetPitch (a_Pitch);
+}
+
+
+
+
+
+void cClientHandle::HandleAnimation(char a_Animation)
+{
+ if (cPluginManager::Get()->CallHookPlayerAnimation(*m_Player, a_Animation))
+ {
+ // Plugin disagrees, bail out
+ return;
+ }
+
+ m_Player->GetWorld()->BroadcastPlayerAnimation(*m_Player, a_Animation, this);
+}
+
+
+
+
+
+void cClientHandle::HandleSlotSelected(short a_SlotNum)
+{
+ m_Player->GetInventory().SetEquippedSlotNum(a_SlotNum);
+ m_Player->GetWorld()->BroadcastEntityEquipment(*m_Player, 0, m_Player->GetInventory().GetEquippedItem(), this);
+}
+
+
+
+
+
+void cClientHandle::HandleSteerVehicle(float a_Forward, float a_Sideways)
+{
+ m_Player->SteerVehicle(a_Forward, a_Sideways);
+}
+
+
+
+
+
+void cClientHandle::HandleWindowClose(char a_WindowID)
+{
+ m_Player->CloseWindowIfID(a_WindowID);
+}
+
+
+
+
+
+void cClientHandle::HandleWindowClick(char a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem)
+{
+ LOGD("WindowClick: WinID %d, SlotNum %d, action: %s, Item %s x %d",
+ a_WindowID, a_SlotNum, ClickActionToString(a_ClickAction),
+ ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount
+ );
+
+ cWindow * Window = m_Player->GetWindow();
+ if (Window == NULL)
+ {
+ LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str());
+ return;
+ }
+
+ Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_ClickAction, a_HeldItem);
+}
+
+
+
+
+
+void cClientHandle::HandleUpdateSign(
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ const AString & a_Line1, const AString & a_Line2,
+ const AString & a_Line3, const AString & a_Line4
+)
+{
+ cWorld * World = m_Player->GetWorld();
+ World->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player);
+}
+
+
+
+
+
+void cClientHandle::HandleUseEntity(int a_TargetEntityID, bool a_IsLeftClick)
+{
+ // TODO: Let plugins interfere via a hook
+
+ // If it is a right click, call the entity's OnRightClicked() handler:
+ if (!a_IsLeftClick)
+ {
+ class cRclkEntity : public cEntityCallback
+ {
+ cPlayer & m_Player;
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ if (cPluginManager::Get()->CallHookPlayerRightClickingEntity(m_Player, *a_Entity))
+ {
+ return false;
+ }
+ a_Entity->OnRightClicked(m_Player);
+ return false;
+ }
+ public:
+ cRclkEntity(cPlayer & a_Player) : m_Player(a_Player) {}
+ } Callback (*m_Player);
+
+ cWorld * World = m_Player->GetWorld();
+ World->DoWithEntityByID(a_TargetEntityID, Callback);
+ return;
+ }
+
+ // If it is a left click, attack the entity:
+ class cDamageEntity : public cEntityCallback
+ {
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ if (!a_Entity->GetWorld()->IsPVPEnabled())
+ {
+ // PVP is disabled, disallow players hurting other players:
+ if (a_Entity->IsPlayer())
+ {
+ // Player is hurting another player which is not allowed when PVP is disabled so ignore it
+ return true;
+ }
+ }
+ a_Entity->TakeDamage(*m_Attacker);
+ return false;
+ }
+ public:
+ cPawn * m_Attacker;
+ } Callback;
+
+ Callback.m_Attacker = m_Player;
+
+ cWorld * World = m_Player->GetWorld();
+ if (World->DoWithEntityByID(a_TargetEntityID, Callback))
+ {
+ // Any kind of an attack implies food exhaustion
+ m_Player->AddFoodExhaustion(0.3);
+ }
+}
+
+
+
+
+
+void cClientHandle::HandleRespawn(void)
+{
+ if (m_Player == NULL)
+ {
+ Destroy();
+ return;
+ }
+ m_Player->Respawn();
+ cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
+}
+
+
+
+
+
+void cClientHandle::HandleDisconnect(const AString & a_Reason)
+{
+ LOGD("Received d/c packet from \"%s\" with reason \"%s\"", m_Username.c_str(), a_Reason.c_str());
+ if (!cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, a_Reason))
+ {
+ AString DisconnectMessage;
+ Printf(DisconnectMessage, "%s disconnected: %s", m_Username.c_str(), a_Reason.c_str());
+ m_Player->GetWorld()->BroadcastChat(DisconnectMessage, this);
+ }
+ m_HasSentDC = true;
+ Destroy();
+}
+
+
+
+
+
+void cClientHandle::HandleKeepAlive(int a_KeepAliveID)
+{
+ if (a_KeepAliveID == m_PingID)
+ {
+ cTimer t1;
+ m_Ping = (short)((t1.GetNowTime() - m_PingStartTime) / 2);
+ }
+}
+
+
+
+
+
+bool cClientHandle::HandleHandshake(const AString & a_Username)
+{
+ if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(this, a_Username))
+ {
+ if (cRoot::Get()->GetServer()->GetNumPlayers() >= cRoot::Get()->GetServer()->GetMaxPlayers())
+ {
+ Kick("The server is currently full :(-- Try again later");
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+
+
+void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID)
+{
+ if (a_EntityID != m_Player->GetUniqueID())
+ {
+ // We should only receive entity actions from the entity that is performing the action
+ return;
+ }
+
+ switch (a_ActionID)
+ {
+ case 1: // crouch
+ {
+ m_Player->SetCrouch(true);
+ break;
+ }
+ case 2: // uncrouch
+ {
+ m_Player->SetCrouch(false);
+ break;
+ }
+ case 3: // Leave bed
+ {
+ m_Player->GetWorld()->BroadcastPlayerAnimation(*m_Player, 3);
+ break;
+ }
+ case 4: // Start sprinting
+ {
+ m_Player->SetSprint(true);
+ break;
+ }
+ case 5: // Stop sprinting
+ {
+ m_Player->SetSprint(false);
+ SendPlayerMaxSpeed();
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cClientHandle::HandleUnmount(void)
+{
+ if (m_Player == NULL)
+ {
+ return;
+ }
+ m_Player->Detach();
+}
+
+
+
+
+
+void cClientHandle::HandleTabCompletion(const AString & a_Text)
+{
+ AStringVector Results;
+ m_Player->GetWorld()->TabCompleteUserName(a_Text, Results);
+ cRoot::Get()->GetPluginManager()->TabCompleteCommand(a_Text, Results, m_Player);
+ if (Results.empty())
+ {
+ return;
+ }
+ std::sort(Results.begin(), Results.end());
+ SendTabCompletionResults(Results);
+}
+
+
+
+
+
+void cClientHandle::SendData(const char * a_Data, int a_Size)
+{
+ if (m_HasSentDC)
+ {
+ // This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31)
+ return;
+ }
+
+ {
+ cCSLock Lock(m_CSOutgoingData);
+
+ // _X 2012_09_06: We need an overflow buffer, usually when streaming the initial chunks
+ if (m_OutgoingDataOverflow.empty())
+ {
+ // No queued overflow data; if this packet fits into the ringbuffer, put it in, otherwise put it in the overflow buffer:
+ int CanFit = m_OutgoingData.GetFreeSpace();
+ if (CanFit > a_Size)
+ {
+ CanFit = a_Size;
+ }
+ if (CanFit > 0)
+ {
+ m_OutgoingData.Write(a_Data, CanFit);
+ }
+ if (a_Size > CanFit)
+ {
+ m_OutgoingDataOverflow.append(a_Data + CanFit, a_Size - CanFit);
+ }
+ }
+ else
+ {
+ // There is a queued overflow. Append to it, then send as much from its front as possible
+ m_OutgoingDataOverflow.append(a_Data, a_Size);
+ int CanFit = m_OutgoingData.GetFreeSpace();
+ if (CanFit > 128)
+ {
+ // No point in moving the data over if it's not large enough - too much effort for too little an effect
+ m_OutgoingData.Write(m_OutgoingDataOverflow.data(), CanFit);
+ m_OutgoingDataOverflow.erase(0, CanFit);
+ }
+ }
+ } // Lock(m_CSOutgoingData)
+
+ // Notify SocketThreads that we have something to write:
+ cRoot::Get()->GetServer()->NotifyClientWrite(this);
+}
+
+
+
+
+
+void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket)
+{
+ ASSERT(m_Player != NULL);
+
+ if (a_SendRespawnPacket)
+ {
+ SendRespawn();
+ }
+
+ cWorld * World = m_Player->GetWorld();
+
+ // Remove all associated chunks:
+ cChunkCoordsList Chunks;
+ {
+ cCSLock Lock(m_CSChunkLists);
+ std::swap(Chunks, m_LoadedChunks);
+ m_ChunksToSend.clear();
+ }
+ for (cChunkCoordsList::iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr)
+ {
+ World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
+ m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
+ } // for itr - Chunks[]
+
+ // Do NOT stream new chunks, the new world runs its own tick thread and may deadlock
+ // Instead, the chunks will be streamed when the client is moved to the new world's Tick list,
+ // by setting state to csAuthenticated
+ m_State = csAuthenticated;
+ m_LastStreamedChunkX = 0x7fffffff;
+ m_LastStreamedChunkZ = 0x7fffffff;
+ m_HasSentPlayerChunk = false;
+}
+
+
+
+
+
+bool cClientHandle::CheckBlockInteractionsRate(void)
+{
+ ASSERT(m_Player != NULL);
+ ASSERT(m_Player->GetWorld() != NULL);
+ /*
+ // TODO: _X 2012_11_01: This needs a total re-thinking and rewriting
+ int LastActionCnt = m_Player->GetLastBlockActionCnt();
+ if ((m_Player->GetWorld()->GetTime() - m_Player->GetLastBlockActionTime()) < 0.1)
+ {
+ // Limit the number of block interactions per tick
+ m_Player->SetLastBlockActionTime(); //Player tried to interact with a block. Reset last block interation time.
+ m_Player->SetLastBlockActionCnt(LastActionCnt + 1);
+ if (m_Player->GetLastBlockActionCnt() > MAXBLOCKCHANGEINTERACTIONS)
+ {
+ // Kick if more than MAXBLOCKCHANGEINTERACTIONS per tick
+ LOGWARN("Player %s tried to interact with a block too quickly! (could indicate bot) Was Kicked.", m_Username.c_str());
+ Kick("You're a baaaaaad boy!");
+ return false;
+ }
+ }
+ else
+ {
+ m_Player->SetLastBlockActionCnt(0); // Reset count
+ m_Player->SetLastBlockActionTime(); // Player tried to interact with a block. Reset last block interation time.
+ }
+ */
+ return true;
+}
+
+
+
+
+
+void cClientHandle::Tick(float a_Dt)
+{
+ // Handle clients that are waiting for final close while destroyed:
+ if (m_State == csDestroyedWaiting)
+ {
+ m_TicksSinceDestruction += 1; // This field is misused for the timeout counting
+ if (m_TicksSinceDestruction > TICKS_BEFORE_CLOSE)
+ {
+ m_State = csDestroyed;
+ }
+ return;
+ }
+
+ // Process received network data:
+ AString IncomingData;
+ {
+ cCSLock Lock(m_CSIncomingData);
+ std::swap(IncomingData, m_IncomingData);
+ }
+ m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
+
+ if (m_State == csAuthenticated)
+ {
+ StreamChunks();
+ m_State = csDownloadingWorld;
+ }
+
+ m_TimeSinceLastPacket += a_Dt;
+ if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out
+ {
+ SendDisconnect("Nooooo!! You timed out! D: Come back!");
+ Destroy();
+ }
+
+ if (m_Player == NULL)
+ {
+ return;
+ }
+
+ // If the chunk the player's in was just sent, spawn the player:
+ if (m_HasSentPlayerChunk && (m_State != csPlaying) && !IsDestroying())
+ {
+ if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player))
+ {
+ // Broadcast that this player has joined the game! Yay~
+ m_Player->GetWorld()->BroadcastChat(m_Username + " joined the game!", this);
+ }
+ m_Protocol->SendPlayerMoveLook();
+ m_State = csPlaying;
+ }
+
+ // Send a ping packet:
+ cTimer t1;
+ if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()))
+ {
+ m_PingID++;
+ m_PingStartTime = t1.GetNowTime();
+ m_Protocol->SendKeepAlive(m_PingID);
+ m_LastPingTime = m_PingStartTime;
+ }
+
+ // Handle block break animation:
+ if (m_BlockDigAnimStage > -1)
+ {
+ int lastAnimVal = m_BlockDigAnimStage;
+ m_BlockDigAnimStage += (int)(m_BlockDigAnimSpeed * a_Dt);
+ if (m_BlockDigAnimStage > 9000)
+ {
+ m_BlockDigAnimStage = 9000;
+ }
+ if (m_BlockDigAnimStage / 1000 != lastAnimVal / 1000)
+ {
+ m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, (char)(m_BlockDigAnimStage / 1000), this);
+ }
+ }
+
+ // Update the explosion statistics:
+ m_CurrentExplosionTick = (m_CurrentExplosionTick + 1) % ARRAYCOUNT(m_NumExplosionsPerTick);
+ m_RunningSumExplosions -= m_NumExplosionsPerTick[m_CurrentExplosionTick];
+ m_NumExplosionsPerTick[m_CurrentExplosionTick] = 0;
+}
+
+
+
+
+
+void cClientHandle::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ m_Protocol->SendAttachEntity(a_Entity, a_Vehicle);
+}
+
+
+
+
+
+void cClientHandle::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
+{
+ m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
+}
+
+
+
+
+
+void cClientHandle::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage)
+{
+ m_Protocol->SendBlockBreakAnim(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage);
+}
+
+
+
+
+
+void cClientHandle::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
+{
+ ASSERT(!a_Changes.empty()); // We don't want to be sending empty change packets!
+
+ m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes);
+}
+
+
+
+
+
+void cClientHandle::SendChat(const AString & a_Message)
+{
+ m_Protocol->SendChat(a_Message);
+}
+
+
+
+
+
+void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
+{
+ ASSERT(m_Player != NULL);
+
+ // Check chunks being sent, erase them from m_ChunksToSend:
+ bool Found = false;
+ {
+ cCSLock Lock(m_CSChunkLists);
+ for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end(); ++itr)
+ {
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
+ {
+ m_ChunksToSend.erase(itr);
+ Found = true;
+ break;
+ }
+ } // for itr - m_ChunksToSend[]
+ }
+ if (!Found)
+ {
+ // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it
+ // It's not a big issue anyway, just means that some chunks may be compressed several times
+ // LOGD("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ());
+ return;
+ }
+
+ m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer);
+
+ // If it is the chunk the player's in, make them spawn (in the tick thread):
+ if ((m_State == csAuthenticated) || (m_State == csDownloadingWorld))
+ {
+ if ((a_ChunkX == m_Player->GetChunkX()) && (a_ChunkZ == m_Player->GetChunkZ()))
+ {
+ m_HasSentPlayerChunk = true;
+ }
+ }
+}
+
+
+
+
+
+void cClientHandle::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
+{
+ m_Protocol->SendCollectPickup(a_Pickup, a_Player);
+}
+
+
+
+
+
+void cClientHandle::SendDestroyEntity(const cEntity & a_Entity)
+{
+ m_Protocol->SendDestroyEntity(a_Entity);
+}
+
+
+
+
+
+void cClientHandle::SendDisconnect(const AString & a_Reason)
+{
+ if (!m_HasSentDC)
+ {
+ LOGD("Sending a DC: \"%s\"", StripColorCodes(a_Reason).c_str());
+ m_Protocol->SendDisconnect(a_Reason);
+ m_HasSentDC = true;
+ }
+}
+
+
+
+
+
+void cClientHandle::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
+{
+ m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cClientHandle::SendEntityHeadLook(const cEntity & a_Entity)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
+
+ m_Protocol->SendEntityHeadLook(a_Entity);
+}
+
+
+
+
+
+void cClientHandle::SendEntityLook(const cEntity & a_Entity)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
+
+ m_Protocol->SendEntityLook(a_Entity);
+}
+
+
+
+
+
+void cClientHandle::SendEntityMetadata(const cEntity & a_Entity)
+{
+ m_Protocol->SendEntityMetadata(a_Entity);
+}
+
+
+
+
+
+void cClientHandle::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
+
+ m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+void cClientHandle::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
+
+ m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+void cClientHandle::SendEntityStatus(const cEntity & a_Entity, char a_Status)
+{
+ m_Protocol->SendEntityStatus(a_Entity, a_Status);
+}
+
+
+
+
+
+void cClientHandle::SendEntityVelocity(const cEntity & a_Entity)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
+
+ m_Protocol->SendEntityVelocity(a_Entity);
+}
+
+
+
+
+
+void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
+{
+ if (
+ (m_NumExplosionsPerTick[m_CurrentExplosionTick] > MAX_EXPLOSIONS_PER_TICK) || // Too many explosions in this tick
+ (m_RunningSumExplosions > MAX_RUNNING_SUM_EXPLOSIONS) // Too many explosions in the recent history
+ )
+ {
+ LOGD("Dropped %u explosions", a_BlocksAffected.size());
+ return;
+ }
+
+ // Update the statistics:
+ m_NumExplosionsPerTick[m_CurrentExplosionTick] += a_BlocksAffected.size();
+ m_RunningSumExplosions += a_BlocksAffected.size();
+
+ m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion);
+}
+
+
+
+
+
+void cClientHandle::SendGameMode(eGameMode a_GameMode)
+{
+ m_Protocol->SendGameMode(a_GameMode);
+}
+
+
+
+
+
+void cClientHandle::SendHealth(void)
+{
+ m_Protocol->SendHealth();
+}
+
+
+
+
+
+void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
+{
+ m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ m_Protocol->SendPickupSpawn(a_Pickup);
+}
+
+
+
+
+
+void cClientHandle::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
+{
+ m_Protocol->SendPlayerAnimation(a_Player, a_Animation);
+}
+
+
+
+
+
+void cClientHandle::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
+{
+ m_Protocol->SendPlayerListItem(a_Player, a_IsOnline);
+}
+
+
+
+
+
+void cClientHandle::SendPlayerMaxSpeed(void)
+{
+ m_Protocol->SendPlayerMaxSpeed();
+}
+
+
+
+
+
+void cClientHandle::SendPlayerMoveLook(void)
+{
+ /*
+ LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d",
+ m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0
+ );
+ */
+ m_Protocol->SendPlayerMoveLook();
+}
+
+
+
+
+
+void cClientHandle::SendPlayerPosition(void)
+{
+ m_Protocol->SendPlayerPosition();
+}
+
+
+
+
+
+void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player)
+{
+ if (a_Player.GetUniqueID() == m_Player->GetUniqueID())
+ {
+ // Do NOT send this packet to myself
+ return;
+ }
+
+ LOGD("Spawning player \"%s\" on client \"%s\" @ %s",
+ a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str()
+ );
+
+ m_Protocol->SendPlayerSpawn(a_Player);
+}
+
+
+
+
+
+void cClientHandle::SendRespawn(void)
+{
+ m_Protocol->SendRespawn();
+}
+
+
+
+
+
+void cClientHandle::SendExperience(void)
+{
+ m_Protocol->SendExperience();
+}
+
+
+
+
+
+void cClientHandle::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
+{
+ m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch);
+}
+
+
+
+
+
+void cClientHandle::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data);
+}
+
+
+
+
+
+void cClientHandle::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
+{
+ m_Protocol->SendSpawnFallingBlock(a_FallingBlock);
+}
+
+
+
+
+
+void cClientHandle::SendSpawnMob(const cMonster & a_Mob)
+{
+ m_Protocol->SendSpawnMob(a_Mob);
+}
+
+
+
+
+
+void cClientHandle::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
+{
+ m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch);
+}
+
+
+
+
+
+void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) // VehicleSubType is specific to Minecarts
+{
+ m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType);
+}
+
+
+
+
+
+void cClientHandle::SendTabCompletionResults(const AStringVector & a_Results)
+{
+ m_Protocol->SendTabCompletionResults(a_Results);
+}
+
+
+
+
+
+void cClientHandle::SendTeleportEntity(const cEntity & a_Entity)
+{
+ m_Protocol->SendTeleportEntity(a_Entity);
+}
+
+
+
+
+
+void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cClientHandle::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
+{
+ m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay);
+}
+
+
+
+
+
+void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+{
+ m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cClientHandle::SendUpdateSign(
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4
+)
+{
+ m_Protocol->SendUpdateSign(
+ a_BlockX, a_BlockY, a_BlockZ,
+ a_Line1, a_Line2, a_Line3, a_Line4
+ );
+}
+
+
+
+
+
+void cClientHandle::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
+{
+ m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+void cClientHandle::SendWeather(eWeather a_Weather)
+{
+ m_Protocol->SendWeather(a_Weather);
+}
+
+
+
+
+
+void cClientHandle::SendWholeInventory(const cWindow & a_Window)
+{
+ m_Protocol->SendWholeInventory(a_Window);
+}
+
+
+
+
+
+void cClientHandle::SendWindowClose(const cWindow & a_Window)
+{
+ m_Protocol->SendWindowClose(a_Window);
+}
+
+
+
+
+
+void cClientHandle::SendWindowOpen(const cWindow & a_Window)
+{
+ m_Protocol->SendWindowOpen(a_Window);
+}
+
+
+
+
+
+void cClientHandle::SendWindowProperty(const cWindow & a_Window, int a_Property, int a_Value)
+{
+ m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value);
+}
+
+
+
+
+
+const AString & cClientHandle::GetUsername(void) const
+{
+ return m_Username;
+}
+
+
+
+
+
+void cClientHandle::SetUsername( const AString & a_Username )
+{
+ m_Username = a_Username;
+}
+
+
+
+
+
+void cClientHandle::SetViewDistance(int a_ViewDistance)
+{
+ if (a_ViewDistance < MIN_VIEW_DISTANCE)
+ {
+ a_ViewDistance = MIN_VIEW_DISTANCE;
+ }
+ if (a_ViewDistance > MAX_VIEW_DISTANCE)
+ {
+ a_ViewDistance = MAX_VIEW_DISTANCE;
+ }
+ m_ViewDistance = a_ViewDistance;
+
+ // Need to re-stream chunks for the change to become apparent:
+ StreamChunks();
+}
+
+
+
+
+
+bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ if (m_State >= csDestroying)
+ {
+ return false;
+ }
+
+ cCSLock Lock(m_CSChunkLists);
+ return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)) != m_ChunksToSend.end());
+}
+
+
+
+
+
+void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ)
+{
+ if (m_State >= csDestroying)
+ {
+ return;
+ }
+
+ LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, this);
+ cCSLock Lock(m_CSChunkLists);
+ if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)) == m_ChunksToSend.end())
+ {
+ m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
+ }
+}
+
+
+
+
+
+void cClientHandle::PacketBufferFull(void)
+{
+ // Too much data in the incoming queue, the server is probably too busy, kick the client:
+ LOGERROR("Too much data in queue for client \"%s\" @ %s, kicking them.", m_Username.c_str(), m_IPString.c_str());
+ SendDisconnect("Server busy");
+ Destroy();
+}
+
+
+
+
+
+void cClientHandle::PacketUnknown(unsigned char a_PacketType)
+{
+ LOGERROR("Unknown packet type 0x%02x from client \"%s\" @ %s", a_PacketType, m_Username.c_str(), m_IPString.c_str());
+
+ AString Reason;
+ Printf(Reason, "Unknown [C->S] PacketType: 0x%02x", a_PacketType);
+ SendDisconnect(Reason);
+ Destroy();
+}
+
+
+
+
+
+void cClientHandle::PacketError(unsigned char a_PacketType)
+{
+ LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username.c_str());
+ SendDisconnect("Protocol error");
+ Destroy();
+}
+
+
+
+
+
+void cClientHandle::DataReceived(const char * a_Data, int a_Size)
+{
+ // Data is received from the client, store it in the buffer to be processed by the Tick thread:
+ m_TimeSinceLastPacket = 0;
+ cCSLock Lock(m_CSIncomingData);
+ m_IncomingData.append(a_Data, a_Size);
+}
+
+
+
+
+
+void cClientHandle::GetOutgoingData(AString & a_Data)
+{
+ // Data can be sent to client
+ {
+ cCSLock Lock(m_CSOutgoingData);
+ m_OutgoingData.ReadAll(a_Data);
+ m_OutgoingData.CommitRead();
+ a_Data.append(m_OutgoingDataOverflow);
+ m_OutgoingDataOverflow.clear();
+ }
+
+ // Disconnect player after all packets have been sent
+ if (m_HasSentDC && a_Data.empty())
+ {
+ Destroy();
+ }
+}
+
+
+
+
+
+void cClientHandle::SocketClosed(void)
+{
+ // The socket has been closed for any reason
+
+ LOGD("Client \"%s\" @ %s disconnected", m_Username.c_str(), m_IPString.c_str());
+ Destroy();
+}
+
+
+
+
+
+
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
new file mode 100644
index 000000000..b887bb11a
--- /dev/null
+++ b/src/ClientHandle.h
@@ -0,0 +1,332 @@
+
+// cClientHandle.h
+
+// Interfaces to the cClientHandle class representing a client connected to this server. The client need not be a player yet
+
+
+
+
+
+#pragma once
+#ifndef CCLIENTHANDLE_H_INCLUDED
+#define CCLIENTHANDLE_H_INCLUDED
+
+#include "Defines.h"
+#include "Vector3d.h"
+#include "OSSupport/SocketThreads.h"
+#include "ChunkDef.h"
+#include "ByteBuffer.h"
+
+
+
+
+
+class cChunkDataSerializer;
+class cInventory;
+class cMonster;
+class cPawn;
+class cPickup;
+class cPlayer;
+class cProtocol;
+class cRedstone;
+class cWindow;
+class cFallingBlock;
+class cItemHandler;
+class cWorld;
+
+
+
+
+
+class cClientHandle : // tolua_export
+ public cSocketThreads::cCallback
+{ // tolua_export
+public:
+ enum ENUM_PRIORITY
+ {
+ E_PRIORITY_LOW,
+ E_PRIORITY_NORMAL
+ };
+
+ static const int MAXBLOCKCHANGEINTERACTIONS = 20; // 5 didn't help, 10 still doesn't work in Creative, 20 seems to have done the trick
+
+#if defined(ANDROID_NDK)
+ static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini)
+#else
+ static const int DEFAULT_VIEW_DISTANCE = 10;
+#endif
+ static const int MAX_VIEW_DISTANCE = 15;
+ static const int MIN_VIEW_DISTANCE = 3;
+
+ /// How many ticks should be checked for a running average of explosions, for limiting purposes
+ static const int NUM_CHECK_EXPLOSIONS_TICKS = 20;
+
+ cClientHandle(const cSocket * a_Socket, int a_ViewDistance);
+ virtual ~cClientHandle();
+
+ const AString & GetIPString(void) const { return m_IPString; }
+
+ cPlayer* GetPlayer() { return m_Player; } // tolua_export
+
+ void Kick(const AString & a_Reason); // tolua_export
+ void Authenticate(void); // Called by cAuthenticator when the user passes authentication
+
+ void StreamChunks(void);
+
+ // Removes the client from all chunks. Used when switching worlds or destroying the player
+ void RemoveFromAllChunks(void);
+
+ inline bool IsLoggedIn(void) const { return (m_State >= csAuthenticating); }
+
+ void Tick(float a_Dt);
+
+ void Destroy(void);
+
+ bool IsPlaying (void) const { return (m_State == csPlaying); }
+ bool IsDestroyed (void) const { return (m_State == csDestroyed); }
+ bool IsDestroying(void) const { return (m_State == csDestroying); }
+
+ // The following functions send the various packets:
+ // (Please keep these alpha-sorted)
+ void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
+ void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType);
+ void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage);
+ void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export
+ void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes);
+ void SendChat (const AString & a_Message);
+ void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer);
+ void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player);
+ void SendDestroyEntity (const cEntity & a_Entity);
+ void SendDisconnect (const AString & a_Reason);
+ void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ);
+ void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item);
+ void SendEntityHeadLook (const cEntity & a_Entity);
+ void SendEntityLook (const cEntity & a_Entity);
+ void SendEntityMetadata (const cEntity & a_Entity);
+ void SendEntityProperties (const cEntity & a_Entity);
+ void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ);
+ void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ);
+ void SendEntityStatus (const cEntity & a_Entity, char a_Status);
+ void SendEntityVelocity (const cEntity & a_Entity);
+ void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion);
+ void SendGameMode (eGameMode a_GameMode);
+ void SendHealth (void);
+ void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item);
+ void SendPickupSpawn (const cPickup & a_Pickup);
+ void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation);
+ void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline);
+ void SendPlayerMaxSpeed (void); ///< Informs the client of the maximum player speed (1.6.1+)
+ void SendPlayerMoveLook (void);
+ void SendPlayerPosition (void);
+ void SendPlayerSpawn (const cPlayer & a_Player);
+ void SendRespawn (void);
+ void SendExperience (void);
+ void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8
+ void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data);
+ void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock);
+ void SendSpawnMob (const cMonster & a_Mob);
+ void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch);
+ void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType = 0);
+ void SendTabCompletionResults(const AStringVector & a_Results);
+ void SendTeleportEntity (const cEntity & a_Entity);
+ void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ);
+ void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay);
+ void SendUnloadChunk (int a_ChunkX, int a_ChunkZ);
+ void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
+ void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ );
+ void SendWeather (eWeather a_Weather);
+ void SendWholeInventory (const cWindow & a_Window);
+ void SendWindowClose (const cWindow & a_Window);
+ void SendWindowOpen (const cWindow & a_Window);
+ void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value);
+
+ const AString & GetUsername(void) const; // tolua_export
+ void SetUsername( const AString & a_Username ); // tolua_export
+
+ inline short GetPing(void) const { return m_Ping; } // tolua_export
+
+ void SetViewDistance(int a_ViewDistance); // tolua_export
+ int GetViewDistance(void) const { return m_ViewDistance; } // tolua_export
+
+ int GetUniqueID() const { return m_UniqueID; } // tolua_export
+
+ /// Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend)
+ bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend)
+ void AddWantedChunk(int a_ChunkX, int a_ChunkZ);
+
+ // Calls that cProtocol descendants use to report state:
+ void PacketBufferFull(void);
+ void PacketUnknown(unsigned char a_PacketType);
+ void PacketError(unsigned char a_PacketType);
+
+ // Calls that cProtocol descendants use for handling packets:
+ void HandleAnimation (char a_Animation);
+ void HandleChat (const AString & a_Message);
+ void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem);
+ void HandleDisconnect (const AString & a_Reason);
+ void HandleEntityAction (int a_EntityID, char a_ActionID);
+ bool HandleHandshake (const AString & a_Username);
+ void HandleKeepAlive (int a_KeepAliveID);
+ void HandleLeftClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
+ void HandlePing (void);
+ void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround);
+ void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); // While m_bPositionConfirmed (normal gameplay)
+ void HandlePlayerPos (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround);
+ void HandleRespawn (void);
+ void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem);
+ void HandleSlotSelected (short a_SlotNum);
+ void HandleSteerVehicle (float Forward, float Sideways);
+ void HandleTabCompletion (const AString & a_Text);
+ void HandleUpdateSign (
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ const AString & a_Line1, const AString & a_Line2,
+ const AString & a_Line3, const AString & a_Line4
+ );
+ void HandleUnmount (void);
+ void HandleUseEntity (int a_TargetEntityID, bool a_IsLeftClick);
+ void HandleWindowClick (char a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem);
+ void HandleWindowClose (char a_WindowID);
+
+ /** Called when the protocol has finished logging the user in.
+ Return true to allow the user in; false to kick them.
+ */
+ bool HandleLogin(int a_ProtocolVersion, const AString & a_Username);
+
+ void SendData(const char * a_Data, int a_Size);
+
+ /// Called when the player moves into a different world; queues sreaming the new chunks
+ void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket);
+
+ /// 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, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler);
+
+private:
+
+ int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 )
+
+ static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. 2 is the minimum, since foliage is generated 1 step behind chunk terrain generation
+
+ AString m_IPString;
+
+ int m_ProtocolVersion;
+ AString m_Username;
+ AString m_Password;
+
+ cCriticalSection m_CSChunkLists;
+ cChunkCoordsList m_LoadedChunks; // Chunks that the player belongs to
+ cChunkCoordsList m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them)
+
+ cProtocol * m_Protocol;
+
+ cCriticalSection m_CSIncomingData;
+ AString m_IncomingData;
+
+ cCriticalSection m_CSOutgoingData;
+ cByteBuffer m_OutgoingData;
+ AString m_OutgoingDataOverflow; ///< For data that didn't fit into the m_OutgoingData ringbuffer temporarily
+
+ Vector3d m_ConfirmPosition;
+
+ cPlayer * m_Player;
+
+ bool m_HasSentDC; ///< True if a D/C packet has been sent in either direction
+
+ // Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk
+ int m_LastStreamedChunkX;
+ int m_LastStreamedChunkZ;
+
+ /// Seconds since the last packet data was received (updated in Tick(), reset in DataReceived())
+ float m_TimeSinceLastPacket;
+
+ short m_Ping;
+ int m_PingID;
+ long long m_PingStartTime;
+ long long m_LastPingTime;
+ static const unsigned short PING_TIME_MS = 1000; //minecraft sends 1 per 20 ticks (1 second or every 1000 ms)
+
+ // Values required for block dig animation
+ int m_BlockDigAnimStage; // Current stage of the animation; -1 if not digging
+ int m_BlockDigAnimSpeed; // Current speed of the animation (units ???)
+ int m_BlockDigAnimX;
+ int m_BlockDigAnimY;
+ int m_BlockDigAnimZ;
+
+ // To avoid dig/aim bug in the client, store the last position given in a DIG_START packet and compare to that when processing the DIG_FINISH packet:
+ bool m_HasStartedDigging;
+ int m_LastDigBlockX;
+ int m_LastDigBlockY;
+ int m_LastDigBlockZ;
+
+ /// Used while csDestroyedWaiting for counting the ticks until the connection is closed
+ int m_TicksSinceDestruction;
+
+ enum eState
+ {
+ csConnected, ///< The client has just connected, waiting for their handshake / login
+ csAuthenticating, ///< The client has logged in, waiting for external authentication
+ csAuthenticated, ///< The client has been authenticated, will start streaming chunks in the next tick
+ csDownloadingWorld, ///< The client is waiting for chunks, we're waiting for the loader to provide and send them
+ csConfirmingPos, ///< The client has been sent the position packet, waiting for them to repeat the position back
+ csPlaying, ///< Normal gameplay
+ csDestroying, ///< The client is being destroyed, don't queue any more packets / don't add to chunks
+ csDestroyedWaiting, ///< The client has been destroyed, but is still kept so that the Kick packet is delivered (#31)
+ csDestroyed, ///< The client has been destroyed, the destructor is to be called from the owner thread
+
+ // TODO: Add Kicking here as well
+ } ;
+
+ eState m_State;
+
+ /// m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads
+ cCriticalSection m_CSDestroyingState;
+
+ bool m_bKeepThreadGoing;
+
+ /// If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded()
+ bool m_ShouldCheckDownloaded;
+
+ /// Stores the recent history of the number of explosions per tick
+ int m_NumExplosionsPerTick[NUM_CHECK_EXPLOSIONS_TICKS];
+
+ /// Points to the current tick in the m_NumExplosionsPerTick[] array
+ int m_CurrentExplosionTick;
+
+ /// Running sum of m_NumExplosionsPerTick[]
+ int m_RunningSumExplosions;
+
+ static int s_ClientCount;
+ int m_UniqueID;
+
+ /// Set to true when the chunk where the player is is sent to the client. Used for spawning the player
+ bool m_HasSentPlayerChunk;
+
+
+
+ /// Returns true if the rate block interactions is within a reasonable limit (bot protection)
+ bool CheckBlockInteractionsRate(void);
+
+ /// Adds a single chunk to be streamed to the client; used by StreamChunks()
+ void StreamChunk(int a_ChunkX, int a_ChunkZ);
+
+ /// Handles the DIG_STARTED dig packet:
+ void HandleBlockDigStarted (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta);
+
+ /// Handles the DIG_FINISHED dig packet:
+ void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta);
+
+ // cSocketThreads::cCallback overrides:
+ virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
+ virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
+ virtual void SocketClosed (void) override; // The socket has been closed for any reason
+}; // tolua_export
+
+
+
+
+#endif // CCLIENTHANDLE_H_INCLUDED
+
+
+
+
diff --git a/src/CommandOutput.cpp b/src/CommandOutput.cpp
new file mode 100644
index 000000000..c221682a1
--- /dev/null
+++ b/src/CommandOutput.cpp
@@ -0,0 +1,71 @@
+
+// CommandOutput.cpp
+
+// Implements the various classes that process command output
+
+#include "Globals.h"
+#include "CommandOutput.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCommandOutputCallback:
+
+void cCommandOutputCallback::Out(const char * a_Fmt, ...)
+{
+ AString Output;
+ va_list args;
+ va_start(args, a_Fmt);
+ AppendVPrintf(Output, a_Fmt, args);
+ va_end(args);
+ Output.append("\n");
+ Out(Output);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cLogCommandOutputCallback:
+
+void cLogCommandOutputCallback::Out(const AString & a_Text)
+{
+ m_Buffer.append(a_Text);
+}
+
+
+
+
+
+void cLogCommandOutputCallback::Finished(void)
+{
+ // Log each line separately:
+ size_t len = m_Buffer.length();
+ size_t last = 0;
+ for (size_t i = 0; i < len; i++)
+ {
+ switch (m_Buffer[i])
+ {
+ case '\n':
+ {
+ LOG(m_Buffer.substr(last, i - last).c_str());
+ last = i + 1;
+ break;
+ }
+ }
+ } // for i - m_Buffer[]
+ if (last < len)
+ {
+ LOG(m_Buffer.substr(last).c_str());
+ }
+
+ // Clear the buffer for the next command output:
+ m_Buffer.clear();
+}
+
+
+
+
diff --git a/src/CommandOutput.h b/src/CommandOutput.h
new file mode 100644
index 000000000..bdf675238
--- /dev/null
+++ b/src/CommandOutput.h
@@ -0,0 +1,82 @@
+
+// CommandOutput.h
+
+// Declares various classes that process command output
+
+
+
+
+
+/** Interface for a callback that receives command output
+The Out() function is called for any output the command has produced.
+Descendants override that function to provide specific processing of the output.
+*/
+class cCommandOutputCallback
+{
+public:
+ virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses
+
+ /// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n"
+ void Out(const char * a_Fmt, ...);
+
+ /// Called when the command wants to output anything; may be called multiple times
+ virtual void Out(const AString & a_Text) = 0;
+
+ /// Called when the command processing has been finished
+ virtual void Finished(void) {};
+} ;
+
+
+
+
+
+/// Class that discards all command output
+class cNullCommandOutputCallback :
+ public cCommandOutputCallback
+{
+ // cCommandOutputCallback overrides:
+ virtual void Out(const AString & a_Text) override
+ {
+ // Do nothing
+ }
+} ;
+
+
+
+
+
+
+/// Sends all command output to a log, line by line, when the command finishes processing
+class cLogCommandOutputCallback :
+ public cCommandOutputCallback
+{
+public:
+ // cCommandOutputCallback overrides:
+ virtual void Out(const AString & a_Text) override;
+ virtual void Finished(void) override;
+
+protected:
+ /// Output is stored here until the command finishes processing
+ AString m_Buffer;
+} ;
+
+
+
+
+
+/// Sends all command output to a log, line by line; deletes self when command finishes processing
+class cLogCommandDeleteSelfOutputCallback :
+ public cLogCommandOutputCallback
+{
+ typedef cLogCommandOutputCallback super;
+
+ virtual void Finished(void) override
+ {
+ super::Finished();
+ delete this;
+ }
+} ;
+
+
+
+
diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp
new file mode 100644
index 000000000..9dc471781
--- /dev/null
+++ b/src/CraftingRecipes.cpp
@@ -0,0 +1,770 @@
+
+// CraftingRecipes.cpp
+
+// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
+
+#include "Globals.h"
+#include "CraftingRecipes.h"
+#include "Root.h"
+#include "PluginManager.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCraftingGrid:
+
+cCraftingGrid::cCraftingGrid(int a_Width, int a_Height) :
+ m_Width(a_Width),
+ m_Height(a_Height),
+ m_Items(new cItem[a_Width * a_Height])
+{
+}
+
+
+
+
+
+cCraftingGrid::cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height) :
+ m_Width(a_Width),
+ m_Height(a_Height),
+ m_Items(new cItem[a_Width * a_Height])
+{
+ for (int i = a_Width * a_Height - 1; i >= 0; i--)
+ {
+ m_Items[i] = a_Items[i];
+ }
+}
+
+
+
+
+
+cCraftingGrid::cCraftingGrid(const cCraftingGrid & a_Original) :
+ m_Width(a_Original.m_Width),
+ m_Height(a_Original.m_Height),
+ m_Items(new cItem[a_Original.m_Width * a_Original.m_Height])
+{
+ for (int i = m_Width * m_Height - 1; i >= 0; i--)
+ {
+ m_Items[i] = a_Original.m_Items[i];
+ }
+}
+
+
+
+
+
+cCraftingGrid::~cCraftingGrid()
+{
+ delete[] m_Items;
+}
+
+
+
+
+
+cItem & cCraftingGrid::GetItem(int x, int y) const
+{
+ // Accessible through scripting, must verify parameters:
+ if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height))
+ {
+ LOGERROR("Attempted to get an invalid item from a crafting grid: (%d, %d), grid dimensions: (%d, %d).",
+ x, y, m_Width, m_Height
+ );
+ return m_Items[0];
+ }
+ return m_Items[x + m_Width * y];
+}
+
+
+
+
+
+void cCraftingGrid::SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth)
+{
+ // Accessible through scripting, must verify parameters:
+ if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height))
+ {
+ LOGERROR("Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).",
+ x, y, m_Width, m_Height
+ );
+ return;
+ }
+
+ m_Items[x + m_Width * y] = cItem(a_ItemType, a_ItemCount, a_ItemHealth);
+}
+
+
+
+
+
+void cCraftingGrid::SetItem(int x, int y, const cItem & a_Item)
+{
+ // Accessible through scripting, must verify parameters:
+ if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height))
+ {
+ LOGERROR("Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).",
+ x, y, m_Width, m_Height
+ );
+ return;
+ }
+
+ m_Items[x + m_Width * y] = a_Item;
+}
+
+
+
+
+
+void cCraftingGrid::Clear(void)
+{
+ for (int y = 0; y < m_Height; y++) for (int x = 0; x < m_Width; x++)
+ {
+ m_Items[x + m_Width * y].Empty();
+ }
+}
+
+
+
+
+
+void cCraftingGrid::ConsumeGrid(const cCraftingGrid & a_Grid)
+{
+ if ((a_Grid.m_Width != m_Width) || (a_Grid.m_Height != m_Height))
+ {
+ LOGWARNING("Consuming a grid of different dimensions: (%d, %d) vs (%d, %d)",
+ a_Grid.m_Width, a_Grid.m_Height, m_Width, m_Height
+ );
+ }
+ int MinX = std::min(a_Grid.m_Width, m_Width);
+ int MinY = std::min(a_Grid.m_Height, m_Height);
+ for (int y = 0; y < MinY; y++) for (int x = 0; x < MinX; x++)
+ {
+ int ThatIdx = x + a_Grid.m_Width * y;
+ if (a_Grid.m_Items[ThatIdx].IsEmpty())
+ {
+ continue;
+ }
+ int ThisIdx = x + m_Width * y;
+ if (a_Grid.m_Items[ThatIdx].m_ItemType != m_Items[ThisIdx].m_ItemType)
+ {
+ LOGWARNING("Consuming incompatible grids: item at (%d, %d) is %d in grid and %d in ingredients. Item not consumed.",
+ x, y, m_Items[ThisIdx].m_ItemType, a_Grid.m_Items[ThatIdx].m_ItemType
+ );
+ continue;
+ }
+ char NumWantedItems = a_Grid.m_Items[ThatIdx].m_ItemCount;
+ if (NumWantedItems > m_Items[ThisIdx].m_ItemCount)
+ {
+ LOGWARNING("Consuming more items than there actually are in slot (%d, %d), item %d (want %d, have %d). Item zeroed out.",
+ x, y, m_Items[ThisIdx].m_ItemType,
+ NumWantedItems, m_Items[ThisIdx].m_ItemCount
+ );
+ NumWantedItems = m_Items[ThisIdx].m_ItemCount;
+ }
+ m_Items[ThisIdx].m_ItemCount -= NumWantedItems;
+ if (m_Items[ThisIdx].m_ItemCount == 0)
+ {
+ m_Items[ThisIdx].Clear();
+ }
+ } // for x, for y
+}
+
+
+
+
+
+void cCraftingGrid::CopyToItems(cItem * a_Items) const
+{
+ for (int i = m_Height * m_Width - 1; i >= 0; i--)
+ {
+ a_Items[i] = m_Items[i];
+ } // for x, for y
+}
+
+
+
+
+
+void cCraftingGrid::Dump(void)
+{
+ for (int y = 0; y < m_Height; y++) for (int x = 0; x < m_Width; x++)
+ {
+ int idx = x + m_Width * y;
+ LOGD("Slot (%d, %d): Type %d, health %d, count %d",
+ x, y, m_Items[idx].m_ItemType, m_Items[idx].m_ItemDamage, m_Items[idx].m_ItemCount
+ );
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCraftingRecipe:
+
+cCraftingRecipe::cCraftingRecipe(const cCraftingGrid & a_CraftingGrid) :
+ m_Ingredients(a_CraftingGrid)
+{
+}
+
+
+
+
+
+void cCraftingRecipe::Clear(void)
+{
+ m_Ingredients.Clear();
+ m_Result.Clear();
+}
+
+
+
+
+
+void cCraftingRecipe::SetResult(ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth)
+{
+ m_Result = cItem(a_ItemType, a_ItemCount, a_ItemHealth);
+}
+
+
+
+
+
+void cCraftingRecipe::ConsumeIngredients(cCraftingGrid & a_CraftingGrid)
+{
+ a_CraftingGrid.ConsumeGrid(m_Ingredients);
+}
+
+
+
+
+
+void cCraftingRecipe::Dump(void)
+{
+ LOGD("Recipe ingredients:");
+ m_Ingredients.Dump();
+ LOGD("Result: Type %d, health %d, count %d",
+ m_Result.m_ItemType, m_Result.m_ItemDamage, m_Result.m_ItemCount
+ );
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCraftingRecipes:
+
+cCraftingRecipes::cCraftingRecipes(void)
+{
+ LoadRecipes();
+}
+
+
+
+
+
+cCraftingRecipes::~cCraftingRecipes()
+{
+ ClearRecipes();
+}
+
+
+
+
+
+void cCraftingRecipes::GetRecipe(const cPlayer * a_Player, const cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe)
+{
+ // Allow plugins to intercept recipes using a pre-craft hook:
+ if (cRoot::Get()->GetPluginManager()->CallHookPreCrafting(a_Player, &a_CraftingGrid, &a_Recipe))
+ {
+ return;
+ }
+
+ // Built-in recipes:
+ std::auto_ptr<cRecipe> Recipe(FindRecipe(a_CraftingGrid.GetItems(), a_CraftingGrid.GetWidth(), a_CraftingGrid.GetHeight()));
+ a_Recipe.Clear();
+ if (Recipe.get() == NULL)
+ {
+ // Allow plugins to intercept a no-recipe-found situation:
+ cRoot::Get()->GetPluginManager()->CallHookCraftingNoRecipe(a_Player, &a_CraftingGrid, &a_Recipe);
+ return;
+ }
+ for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr)
+ {
+ a_Recipe.SetIngredient(itr->x, itr->y, itr->m_Item);
+ } // for itr
+ a_Recipe.SetResult(Recipe->m_Result);
+
+ // Allow plugins to intercept recipes after they are processed:
+ cRoot::Get()->GetPluginManager()->CallHookPostCrafting(a_Player, &a_CraftingGrid, &a_Recipe);
+}
+
+
+
+
+
+void cCraftingRecipes::LoadRecipes(void)
+{
+ LOGD("Loading crafting recipes from crafting.txt...");
+ ClearRecipes();
+
+ // Load the crafting.txt file:
+ cFile f;
+ if (!f.Open("crafting.txt", cFile::fmRead))
+ {
+ LOGWARNING("Cannot open file \"crafting.txt\", no crafting recipes will be available!");
+ return;
+ }
+ AString Everything;
+ f.ReadRestOfFile(Everything);
+ f.Close();
+
+ // Split it into lines, then process each line as a single recipe:
+ AStringVector Split = StringSplit(Everything, "\n");
+ int LineNum = 1;
+ for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr, ++LineNum)
+ {
+ // Remove anything after a '#' sign and trim away the whitespace:
+ AString Recipe = TrimString(itr->substr(0, itr->find('#')));
+ if (Recipe.empty())
+ {
+ // Empty recipe
+ continue;
+ }
+ AddRecipeLine(LineNum, Recipe);
+ } // for itr - Split[]
+ LOG("Loaded %d crafting recipes", m_Recipes.size());
+}
+
+
+
+
+void cCraftingRecipes::ClearRecipes(void)
+{
+ for (cRecipes::iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr)
+ {
+ delete *itr;
+ }
+ m_Recipes.clear();
+}
+
+
+
+
+
+void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine)
+{
+ AStringVector Sides = StringSplit(a_RecipeLine, "=");
+ if (Sides.size() != 2)
+ {
+ LOGWARNING("crafting.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
+ LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str());
+ return;
+ }
+
+ std::auto_ptr<cCraftingRecipes::cRecipe> Recipe(new cCraftingRecipes::cRecipe);
+
+ // Parse the result:
+ AStringVector ResultSplit = StringSplit(Sides[0], ",");
+ if (ResultSplit.empty())
+ {
+ LOGWARNING("crafting.txt: line %d: Result is empty, ignoring the recipe.", a_LineNum);
+ LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str());
+ return;
+ }
+ if (!ParseItem(ResultSplit[0], Recipe->m_Result))
+ {
+ LOGWARNING("crafting.txt: line %d: Cannot parse result item, ignoring the recipe.", a_LineNum);
+ LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str());
+ return;
+ }
+ if (ResultSplit.size() > 1)
+ {
+ Recipe->m_Result.m_ItemCount = atoi(ResultSplit[1].c_str());
+ if (Recipe->m_Result.m_ItemCount == 0)
+ {
+ LOGWARNING("crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum);
+ LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str());
+ return;
+ }
+ }
+ else
+ {
+ Recipe->m_Result.m_ItemCount = 1;
+ }
+
+ // Parse each ingredient:
+ AStringVector Ingredients = StringSplit(Sides[1], "|");
+ int Num = 1;
+ for (AStringVector::const_iterator itr = Ingredients.begin(); itr != Ingredients.end(); ++itr, ++Num)
+ {
+ if (!ParseIngredient(*itr, Recipe.get()))
+ {
+ LOGWARNING("crafting.txt: line %d: Cannot parse ingredient #%d, ignoring the recipe.", a_LineNum, Num);
+ LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str());
+ return;
+ }
+ } // for itr - Ingredients[]
+
+ NormalizeIngredients(Recipe.get());
+
+ m_Recipes.push_back(Recipe.release());
+}
+
+
+
+
+
+bool cCraftingRecipes::ParseItem(const AString & a_String, cItem & a_Item)
+{
+ // The caller provides error logging
+
+ AStringVector Split = StringSplit(a_String, "^");
+ if (Split.empty())
+ {
+ return false;
+ }
+
+ if (!StringToItem(Split[0], a_Item))
+ {
+ return false;
+ }
+
+ if (Split.size() > 1)
+ {
+ AString Damage = TrimString(Split[1]);
+ a_Item.m_ItemDamage = atoi(Damage.c_str());
+ if ((a_Item.m_ItemDamage == 0) && (Damage.compare("0") != 0))
+ {
+ // Parsing the number failed
+ return false;
+ }
+ }
+
+ // Success
+ return true;
+}
+
+
+
+
+
+bool cCraftingRecipes::ParseIngredient(const AString & a_String, cRecipe * a_Recipe)
+{
+ // a_String is in this format: "ItemType^damage, X:Y, X:Y, X:Y..."
+ AStringVector Split = StringSplit(a_String, ",");
+ if (Split.size() < 2)
+ {
+ // Not enough split items
+ return false;
+ }
+ cItem Item;
+ if (!ParseItem(Split[0], Item))
+ {
+ return false;
+ }
+ Item.m_ItemCount = 1;
+
+ cCraftingRecipes::cRecipeSlots TempSlots;
+ for (AStringVector::const_iterator itr = Split.begin() + 1; itr != Split.end(); ++itr)
+ {
+ // Parse the coords in the split item:
+ AStringVector Coords = StringSplit(*itr, ":");
+ if ((Coords.size() == 1) && (TrimString(Coords[0]) == "*"))
+ {
+ cCraftingRecipes::cRecipeSlot Slot;
+ Slot.m_Item = Item;
+ Slot.x = -1;
+ Slot.y = -1;
+ TempSlots.push_back(Slot);
+ continue;
+ }
+ if (Coords.size() != 2)
+ {
+ return false;
+ }
+ Coords[0] = TrimString(Coords[0]);
+ Coords[1] = TrimString(Coords[1]);
+ if (Coords[0].empty() || Coords[1].empty())
+ {
+ return false;
+ }
+ cCraftingRecipes::cRecipeSlot Slot;
+ Slot.m_Item = Item;
+ switch (Coords[0][0])
+ {
+ case '1': Slot.x = 0; break;
+ case '2': Slot.x = 1; break;
+ case '3': Slot.x = 2; break;
+ case '*': Slot.x = -1; break;
+ default:
+ {
+ return false;
+ }
+ }
+ switch (Coords[1][0])
+ {
+ case '1': Slot.y = 0; break;
+ case '2': Slot.y = 1; break;
+ case '3': Slot.y = 2; break;
+ case '*': Slot.y = -1; break;
+ default:
+ {
+ return false;
+ }
+ }
+ TempSlots.push_back(Slot);
+ } // for itr - Split[]
+
+ // Append the ingredients:
+ a_Recipe->m_Ingredients.insert(a_Recipe->m_Ingredients.end(), TempSlots.begin(), TempSlots.end());
+ return true;
+}
+
+
+
+
+
+void cCraftingRecipes::NormalizeIngredients(cCraftingRecipes::cRecipe * a_Recipe)
+{
+ // Calculate the minimum coords for ingredients, excluding the "anywhere" items:
+ int MinX = MAX_GRID_WIDTH, MaxX = 0;
+ int MinY = MAX_GRID_HEIGHT, MaxY = 0;
+ for (cRecipeSlots::const_iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr)
+ {
+ if (itr->x >= 0)
+ {
+ MinX = std::min(itr->x, MinX);
+ MaxX = std::max(itr->x, MaxX);
+ }
+ if (itr->y >= 0)
+ {
+ MinY = std::min(itr->y, MinY);
+ MaxY = std::max(itr->y, MaxY);
+ }
+ } // for itr - a_Recipe->m_Ingredients[]
+
+ // Move ingredients so that the minimum coords are 0:0
+ for (cRecipeSlots::iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr)
+ {
+ if (itr->x >= 0)
+ {
+ itr->x -= MinX;
+ }
+ if (itr->y >= 0)
+ {
+ itr->y -= MinY;
+ }
+ } // for itr - a_Recipe->m_Ingredients[]
+ a_Recipe->m_Width = std::max(MaxX - MinX + 1, 1);
+ a_Recipe->m_Height = std::max(MaxY - MinY + 1, 1);
+
+ // TODO: Compress two same ingredients with the same coords into a single ingredient with increased item count
+}
+
+
+
+
+
+cCraftingRecipes::cRecipe * cCraftingRecipes::FindRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight)
+{
+ ASSERT(a_GridWidth <= MAX_GRID_WIDTH);
+ ASSERT(a_GridHeight <= MAX_GRID_HEIGHT);
+
+ // Get the real bounds of the crafting grid:
+ int GridLeft = MAX_GRID_WIDTH, GridTop = MAX_GRID_HEIGHT;
+ int GridRight = 0, GridBottom = 0;
+ for (int y = 0; y < a_GridHeight; y++ ) for(int x = 0; x < a_GridWidth; x++)
+ {
+ if (!a_CraftingGrid[x + y * a_GridWidth].IsEmpty())
+ {
+ GridRight = std::max(x, GridRight);
+ GridBottom = std::max(y, GridBottom);
+ GridLeft = std::min(x, GridLeft);
+ GridTop = std::min(y, GridTop);
+ }
+ }
+ int GridWidth = GridRight - GridLeft + 1;
+ int GridHeight = GridBottom - GridTop + 1;
+
+ // Search in the possibly minimized grid, but keep the stride:
+ const cItem * Grid = a_CraftingGrid + GridLeft + (a_GridWidth * GridTop);
+ cRecipe * Recipe = FindRecipeCropped(Grid, GridWidth, GridHeight, a_GridWidth);
+ if (Recipe == NULL)
+ {
+ return NULL;
+ }
+
+ // A recipe has been found, move it to correspond to the original crafting grid:
+ for (cRecipeSlots::iterator itrS = Recipe->m_Ingredients.begin(); itrS != Recipe->m_Ingredients.end(); ++itrS)
+ {
+ itrS->x += GridLeft;
+ itrS->y += GridTop;
+ } // for itrS - Recipe->m_Ingredients[]
+
+ return Recipe;
+}
+
+
+
+
+
+cCraftingRecipes::cRecipe * cCraftingRecipes::FindRecipeCropped(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride)
+{
+ for (cRecipes::const_iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr)
+ {
+ // Both the crafting grid and the recipes are normalized. The only variable possible is the "anywhere" items.
+ // This still means that the "anywhere" item may be the one that is offsetting the grid contents to the right or downwards, so we need to check all possible positions.
+ // E. g. recipe "A, * | B, 1:1 | ..." still needs to check grid for B at 2:2 (in case A was in grid's 1:1)
+ // Calculate the maximum offsets for this recipe relative to the grid size, and iterate through all combinations of offsets.
+ // Also, this calculation automatically filters out recipes that are too large for the current grid - the loop won't be entered at all.
+
+ int MaxOfsX = a_GridWidth - (*itr)->m_Width;
+ int MaxOfsY = a_GridHeight - (*itr)->m_Height;
+ for (int x = 0; x <= MaxOfsX; x++) for (int y = 0; y <= MaxOfsY; y++)
+ {
+ cRecipe * Recipe = MatchRecipe(a_CraftingGrid, a_GridWidth, a_GridHeight, a_GridStride, *itr, x, y);
+ if (Recipe != NULL)
+ {
+ return Recipe;
+ }
+ } // for y, for x
+ } // for itr - m_Recipes[]
+
+ // No matching recipe found
+ return NULL;
+}
+
+
+
+
+
+cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY)
+{
+ // Check the regular items first:
+ bool HasMatched[MAX_GRID_WIDTH][MAX_GRID_HEIGHT];
+ memset(HasMatched, 0, sizeof(HasMatched));
+ for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS)
+ {
+ if ((itrS->x < 0) || (itrS->y < 0))
+ {
+ // "Anywhere" item, process later
+ continue;
+ }
+ ASSERT(itrS->x + a_OffsetX < a_GridWidth);
+ ASSERT(itrS->y + a_OffsetY < a_GridHeight);
+ int GridID = (itrS->x + a_OffsetX) + a_GridStride * (itrS->y + a_OffsetY);
+ if (
+ (itrS->x >= a_GridWidth) ||
+ (itrS->y >= a_GridHeight) ||
+ (itrS->m_Item.m_ItemType != a_CraftingGrid[GridID].m_ItemType) || // same item type?
+ (itrS->m_Item.m_ItemCount > a_CraftingGrid[GridID].m_ItemCount) || // not enough items
+ (
+ (itrS->m_Item.m_ItemDamage > 0) && // should compare damage values?
+ (itrS->m_Item.m_ItemDamage != a_CraftingGrid[GridID].m_ItemDamage)
+ )
+ )
+ {
+ // Doesn't match
+ return NULL;
+ }
+ HasMatched[itrS->x + a_OffsetX][itrS->y + a_OffsetY] = true;
+ } // for itrS - Recipe->m_Ingredients[]
+
+ // Process the "Anywhere" items now, and only in the cells that haven't matched yet
+ // The "anywhere" items are processed on a first-come-first-served basis.
+ // Do not use a recipe with one horizontal and one vertical "anywhere" ("*:1, 1:*") as it may not match properly!
+ cRecipeSlots MatchedSlots; // Stores the slots of "anywhere" items that have matched, with the match coords
+ for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS)
+ {
+ if ((itrS->x >= 0) && (itrS->y >= 0))
+ {
+ // Regular item, already processed
+ continue;
+ }
+ int StartX = 0, EndX = a_GridWidth - 1;
+ int StartY = 0, EndY = a_GridHeight - 1;
+ if (itrS->x >= 0)
+ {
+ StartX = itrS->x;
+ EndX = itrS->x;
+ }
+ else if (itrS->y >= 0)
+ {
+ StartY = itrS->y;
+ EndY = itrS->y;
+ }
+ bool Found = false;
+ for (int x = StartX; x <= EndX; x++)
+ {
+ for (int y = StartY; y <= EndY; y++)
+ {
+ if (HasMatched[x][y])
+ {
+ // Already matched some other item
+ continue;
+ }
+ int GridIdx = x + a_GridStride * y;
+ if (
+ (a_CraftingGrid[GridIdx].m_ItemType == itrS->m_Item.m_ItemType) &&
+ (
+ (itrS->m_Item.m_ItemDamage < 0) || // doesn't want damage comparison
+ (itrS->m_Item.m_ItemDamage == a_CraftingGrid[GridIdx].m_ItemDamage) // the damage matches
+ )
+ )
+ {
+ HasMatched[x][y] = true;
+ Found = true;
+ MatchedSlots.push_back(*itrS);
+ MatchedSlots.back().x = x;
+ MatchedSlots.back().y = y;
+ break;
+ }
+ } // for y
+ if (Found)
+ {
+ break;
+ }
+ } // for x
+ if (!Found)
+ {
+ return NULL;
+ }
+ } // for itrS - a_Recipe->m_Ingredients[]
+
+ // Check if the whole grid has matched:
+ for (int x = 0; x < a_GridWidth; x++) for (int y = 0; y < a_GridHeight; y++)
+ {
+ if (!HasMatched[x][y] && !a_CraftingGrid[x + a_GridStride * y].IsEmpty())
+ {
+ // There's an unmatched item in the grid
+ return NULL;
+ }
+ } // for y, for x
+
+ // The recipe has matched. Create a copy of the recipe and set its coords to match the crafting grid:
+ std::auto_ptr<cRecipe> Recipe(new cRecipe);
+ Recipe->m_Result = a_Recipe->m_Result;
+ Recipe->m_Width = a_Recipe->m_Width;
+ Recipe->m_Height = a_Recipe->m_Height;
+ for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS)
+ {
+ if ((itrS->x < 0) || (itrS->y < 0))
+ {
+ // "Anywhere" item, process later
+ continue;
+ }
+ Recipe->m_Ingredients.push_back(*itrS);
+ }
+ Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end());
+ return Recipe.release();
+}
+
+
+
+
diff --git a/src/CraftingRecipes.h b/src/CraftingRecipes.h
new file mode 100644
index 000000000..9d92cbfab
--- /dev/null
+++ b/src/CraftingRecipes.h
@@ -0,0 +1,172 @@
+
+// CraftingRecipes.h
+
+// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
+
+
+
+
+#pragma once
+
+#include "Item.h"
+
+
+
+
+
+// fwd: cPlayer.h
+class cPlayer;
+
+
+
+
+
+class cCraftingGrid // tolua_export
+{ // tolua_export
+public:
+ cCraftingGrid(const cCraftingGrid & a_Original);
+ cCraftingGrid(int a_Width, int a_Height); // tolua_export
+ cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height);
+ ~cCraftingGrid();
+
+ // tolua_begin
+ int GetWidth (void) const {return m_Width; }
+ int GetHeight(void) const {return m_Height; }
+ cItem & GetItem (int x, int y) const;
+ void SetItem (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth);
+ void SetItem (int x, int y, const cItem & a_Item);
+ void Clear (void);
+
+ /// Removes items in a_Grid from m_Items[] (used by cCraftingRecipe::ConsumeIngredients())
+ void ConsumeGrid(const cCraftingGrid & a_Grid);
+
+ /// Dumps the entire crafting grid using LOGD()
+ void Dump(void);
+
+ // tolua_end
+
+ cItem * GetItems(void) const {return m_Items; }
+
+ /// Copies internal contents into the item array specified. Assumes that the array has the same dimensions as self
+ void CopyToItems(cItem * a_Items) const;
+
+protected:
+
+ int m_Width;
+ int m_Height;
+ cItem * m_Items;
+} ; // tolua_export
+
+
+
+
+
+class cCraftingRecipe // tolua_export
+{ // tolua_export
+public:
+ cCraftingRecipe(const cCraftingGrid & a_CraftingGrid);
+
+ // tolua_begin
+ void Clear (void);
+ int GetIngredientsWidth (void) const {return m_Ingredients.GetWidth(); }
+ int GetIngredientsHeight(void) const {return m_Ingredients.GetHeight(); }
+ cItem & GetIngredient (int x, int y) const {return m_Ingredients.GetItem(x, y); }
+ const cItem & GetResult (void) const {return m_Result; }
+ void SetResult (ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth);
+ void SetResult (const cItem & a_Item)
+ {
+ m_Result = a_Item;
+ }
+
+ void SetIngredient (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth)
+ {
+ m_Ingredients.SetItem(x, y, a_ItemType, a_ItemCount, a_ItemHealth);
+ }
+
+ void SetIngredient (int x, int y, const cItem & a_Item)
+ {
+ m_Ingredients.SetItem(x, y, a_Item);
+ }
+
+ /// Consumes ingredients from the crafting grid specified
+ void ConsumeIngredients(cCraftingGrid & a_CraftingGrid);
+
+ /// Dumps the entire recipe using LOGD()
+ void Dump(void);
+ // tolua_end
+
+protected:
+
+ cCraftingGrid m_Ingredients; // Adjusted to correspond to the input crafting grid!
+ cItem m_Result;
+} ; // tolua_export
+
+
+
+
+
+class cCraftingRecipes
+{
+public:
+ static const int MAX_GRID_WIDTH = 3;
+ static const int MAX_GRID_HEIGHT = 3;
+
+ cCraftingRecipes(void);
+ ~cCraftingRecipes();
+
+ /// Returns the recipe for current crafting grid. Doesn't modify the grid. Clears a_Recipe if no recipe found.
+ void GetRecipe(const cPlayer * a_Player, const cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe);
+
+protected:
+
+ struct cRecipeSlot
+ {
+ cItem m_Item;
+ int x, y; // 1..3, or -1 for "any"
+ } ;
+ typedef std::vector<cRecipeSlot> cRecipeSlots;
+
+ /** A single recipe, stored. Each recipe is normalized right after parsing (NormalizeIngredients())
+ A normalized recipe starts at (0,0)
+ */
+ struct cRecipe
+ {
+ cRecipeSlots m_Ingredients;
+ cItem m_Result;
+
+ // Size of the regular items in the recipe; "anywhere" items are excluded:
+ int m_Width;
+ int m_Height;
+ } ;
+ typedef std::vector<cRecipe *> cRecipes;
+
+ cRecipes m_Recipes;
+
+ void LoadRecipes(void);
+ void ClearRecipes(void);
+
+ /// Parses the recipe line and adds it into m_Recipes. a_LineNum is used for diagnostic warnings only
+ void AddRecipeLine(int a_LineNum, const AString & a_RecipeLine);
+
+ /// Parses an item string in the format "<ItemType>[^<Damage>]", returns true if successful.
+ bool ParseItem(const AString & a_String, cItem & a_Item);
+
+ /// Parses one ingredient and adds it to the specified recipe. Returns true if successful.
+ bool ParseIngredient(const AString & a_String, cRecipe * a_Recipe);
+
+ /// Moves the recipe to top-left corner, sets its MinWidth / MinHeight
+ void NormalizeIngredients(cRecipe * a_Recipe);
+
+ /// Finds a recipe matching the crafting grid. Returns a newly allocated recipe (with all its coords set) or NULL if not found. Caller must delete return value!
+ cRecipe * FindRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight);
+
+ /// Same as FindRecipe, but the grid is guaranteed to be of minimal dimensions needed
+ cRecipe * FindRecipeCropped(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride);
+
+ /// Checks if the grid matches the specified recipe, offset by the specified offsets. Returns a matched cRecipe * if so, or NULL if not matching. Caller must delete the return value!
+ cRecipe * MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY);
+} ;
+
+
+
+
diff --git a/src/Cuboid.cpp b/src/Cuboid.cpp
new file mode 100644
index 000000000..ea6f7c453
--- /dev/null
+++ b/src/Cuboid.cpp
@@ -0,0 +1,117 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Cuboid.h"
+
+
+
+
+
+/// Returns true if the two specified intervals have a non-empty union
+static bool DoIntervalsIntersect(int a_Min1, int a_Max1, int a_Min2, int a_Max2)
+{
+ return (
+ ((a_Min1 >= a_Min2) && (a_Min1 <= a_Max2)) || // Start of first interval is within the second interval
+ ((a_Max1 >= a_Min2) && (a_Max1 <= a_Max2)) || // End of first interval is within the second interval
+ ((a_Min2 >= a_Min1) && (a_Min2 <= a_Max1)) // Start of second interval is within the first interval
+ );
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCuboid:
+
+void cCuboid::Assign(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2)
+{
+ p1.x = a_X1;
+ p1.y = a_Y1;
+ p1.z = a_Z1;
+ p2.x = a_X2;
+ p2.y = a_Y2;
+ p2.z = a_Z2;
+}
+
+
+
+
+
+void cCuboid::Sort(void)
+{
+ if (p1.x > p2.x)
+ {
+ std::swap(p1.x, p2.x);
+ }
+ if (p1.y > p2.y)
+ {
+ std::swap(p1.y, p2.y);
+ }
+ if (p1.z > p2.z)
+ {
+ std::swap(p1.z, p2.z);
+ }
+}
+
+
+
+
+
+bool cCuboid::DoesIntersect(const cCuboid & a_Other) const
+{
+ // In order for cuboids to intersect, each of their coord intervals need to intersect
+ return (
+ DoIntervalsIntersect(p1.x, p2.x, a_Other.p1.x, a_Other.p2.x) &&
+ DoIntervalsIntersect(p1.y, p2.y, a_Other.p1.y, a_Other.p2.y) &&
+ DoIntervalsIntersect(p1.z, p2.z, a_Other.p1.z, a_Other.p2.z)
+ );
+}
+
+
+
+
+
+bool cCuboid::IsCompletelyInside(const cCuboid & a_Outer) const
+{
+ return (
+ (p1.x >= a_Outer.p1.x) &&
+ (p2.x <= a_Outer.p2.x) &&
+ (p1.y >= a_Outer.p1.y) &&
+ (p2.y <= a_Outer.p2.y) &&
+ (p1.z >= a_Outer.p1.z) &&
+ (p2.z <= a_Outer.p2.z)
+ );
+}
+
+
+
+
+
+void cCuboid::Move(int a_OfsX, int a_OfsY, int a_OfsZ)
+{
+ p1.x += a_OfsX;
+ p1.y += a_OfsY;
+ p1.z += a_OfsZ;
+ p2.x += a_OfsX;
+ p2.y += a_OfsY;
+ p2.z += a_OfsZ;
+}
+
+
+
+
+
+
+bool cCuboid::IsSorted(void) const
+{
+ return (
+ (p1.x <= p2.x) &&
+ (p1.y <= p2.y) &&
+ (p1.z <= p2.z)
+ );
+}
+
+
+
+
diff --git a/src/Cuboid.h b/src/Cuboid.h
new file mode 100644
index 000000000..44db7b98e
--- /dev/null
+++ b/src/Cuboid.h
@@ -0,0 +1,75 @@
+
+#pragma once
+
+#include "Vector3i.h"
+#include "Vector3d.h"
+
+
+
+
+
+// tolua_begin
+class cCuboid
+{
+public:
+ // p1 is expected to have the smaller of the coords; Sort() swaps coords to match this
+ Vector3i p1, p2;
+
+ cCuboid(void) {}
+ cCuboid(const cCuboid & a_Cuboid ) : p1(a_Cuboid.p1), p2(a_Cuboid.p2) {}
+ cCuboid(const Vector3i & a_p1, const Vector3i & a_p2) : p1(a_p1), p2(a_p2) {}
+ cCuboid(int a_X1, int a_Y1, int a_Z1) : p1(a_X1, a_Y1, a_Z1), p2(a_X1, a_Y1, a_Z1) {}
+ cCuboid(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2) : p1(a_X1, a_Y1, a_Z1), p2(a_X2, a_Y2, a_Z2) {}
+
+ void Assign(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2);
+
+ void Sort(void);
+
+ int DifX(void) const { return p2.x - p1.x; }
+ int DifY(void) const { return p2.y - p1.y; }
+ int DifZ(void) const { return p2.z - p1.z; }
+
+ /// Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive.
+ bool DoesIntersect(const cCuboid & a_Other) const;
+
+ bool IsInside(const Vector3i & v) const
+ {
+ return (
+ (v.x >= p1.x) && (v.x <= p2.x) &&
+ (v.y >= p1.y) && (v.y <= p2.y) &&
+ (v.z >= p1.z) && (v.z <= p2.z)
+ );
+ }
+
+ bool IsInside(int a_X, int a_Y, int a_Z) const
+ {
+ return (
+ (a_X >= p1.x) && (a_X <= p2.x) &&
+ (a_Y >= p1.y) && (a_Y <= p2.y) &&
+ (a_Z >= p1.z) && (a_Z <= p2.z)
+ );
+ }
+
+ bool IsInside( const Vector3d & v ) const
+ {
+ return (
+ (v.x >= p1.x) && (v.x <= p2.x) &&
+ (v.y >= p1.y) && (v.y <= p2.y) &&
+ (v.z >= p1.z) && (v.z <= p2.z)
+ );
+ }
+
+ /// Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords)
+ bool IsCompletelyInside(const cCuboid & a_Outer) const;
+
+ /// Moves the cuboid by the specified offsets in each direction
+ void Move(int a_OfsX, int a_OfsY, int a_OfsZ);
+
+ /// Returns true if the coords are properly sorted (lesser in p1, greater in p2)
+ bool IsSorted(void) const;
+} ;
+// tolua_end
+
+
+
+
diff --git a/src/DeadlockDetect.cpp b/src/DeadlockDetect.cpp
new file mode 100644
index 000000000..c774c9dce
--- /dev/null
+++ b/src/DeadlockDetect.cpp
@@ -0,0 +1,147 @@
+
+// DeadlockDetect.cpp
+
+// Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one
+
+#include "Globals.h"
+#include "DeadlockDetect.h"
+#include "Root.h"
+#include "World.h"
+
+
+
+
+
+/// Number of milliseconds per cycle
+const int CYCLE_MILLISECONDS = 100;
+
+/// When the number of cycles for the same world age hits this value, it is considered a deadlock
+const int NUM_CYCLES_LIMIT = 200; // 200 = twenty seconds
+
+
+
+
+
+cDeadlockDetect::cDeadlockDetect(void) :
+ super("DeadlockDetect")
+{
+}
+
+
+
+
+
+bool cDeadlockDetect::Start(void)
+{
+ // Read the initial world data:
+ class cFillIn :
+ public cWorldListCallback
+ {
+ public:
+ cFillIn(cDeadlockDetect * a_Detect) :
+ m_Detect(a_Detect)
+ {
+ }
+
+ virtual bool Item(cWorld * a_World) override
+ {
+ m_Detect->SetWorldAge(a_World->GetName(), a_World->GetWorldAge());
+ return false;
+ }
+
+ protected:
+ cDeadlockDetect * m_Detect;
+ } FillIn(this);
+ cRoot::Get()->ForEachWorld(FillIn);
+ return super::Start();
+}
+
+
+
+
+
+void cDeadlockDetect::Execute(void)
+{
+ // Loop until the signal to terminate:
+ while (!m_ShouldTerminate)
+ {
+ // Check the world ages:
+ class cChecker :
+ public cWorldListCallback
+ {
+ public:
+ cChecker(cDeadlockDetect * a_Detect) :
+ m_Detect(a_Detect)
+ {
+ }
+
+ protected:
+ cDeadlockDetect * m_Detect;
+
+ virtual bool Item(cWorld * a_World) override
+ {
+ m_Detect->CheckWorldAge(a_World->GetName(), a_World->GetWorldAge());
+ return false;
+ }
+ } Checker(this);
+ cRoot::Get()->ForEachWorld(Checker);
+
+ cSleep::MilliSleep(CYCLE_MILLISECONDS);
+ } // while (should run)
+}
+
+
+
+
+
+void cDeadlockDetect::SetWorldAge(const AString & a_WorldName, Int64 a_Age)
+{
+ m_WorldAges[a_WorldName].m_Age = a_Age;
+ m_WorldAges[a_WorldName].m_NumCyclesSame = 0;
+}
+
+
+
+
+
+void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
+{
+ WorldAges::iterator itr = m_WorldAges.find(a_WorldName);
+ if (itr == m_WorldAges.end())
+ {
+ ASSERT(!"Unknown world in cDeadlockDetect");
+ return;
+ }
+ if (itr->second.m_Age == a_Age)
+ {
+ itr->second.m_NumCyclesSame += 1;
+ if (itr->second.m_NumCyclesSame > NUM_CYCLES_LIMIT)
+ {
+ DeadlockDetected();
+ return;
+ }
+ }
+ else
+ {
+ itr->second.m_Age = a_Age;
+ itr->second.m_NumCyclesSame = 0;
+ }
+}
+
+
+
+
+
+void cDeadlockDetect::DeadlockDetected(void)
+{
+ ASSERT(!"Deadlock detected");
+
+ // TODO: Make a crashdump / coredump
+
+ // Crash the server intentionally:
+ *((volatile int *)0) = 0;
+}
+
+
+
+
diff --git a/src/DeadlockDetect.h b/src/DeadlockDetect.h
new file mode 100644
index 000000000..2559c3fff
--- /dev/null
+++ b/src/DeadlockDetect.h
@@ -0,0 +1,65 @@
+
+// DeadlockDetect.h
+
+// Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one
+
+/*
+This class simply monitors each world's m_WorldAge, which is expected to grow on each tick.
+If the world age doesn't grow for several seconds, it's either because the server is super-overloaded,
+or because the world tick thread hangs in a deadlock. We presume the latter and therefore kill the server.
+Once we learn to write crashdumps programmatically, we should do so just before killing, to enable debugging.
+*/
+
+
+
+#pragma once
+
+#include "OSSupport/IsThread.h"
+
+
+
+
+
+class cDeadlockDetect :
+ public cIsThread
+{
+ typedef cIsThread super;
+
+public:
+ cDeadlockDetect(void);
+
+ /// Starts the detection. Hides cIsThread's Start, because we need some initialization
+ bool Start(void);
+
+protected:
+ struct sWorldAge
+ {
+ /// Last m_WorldAge that has been detected in this world
+ Int64 m_Age;
+
+ /// Number of cycles for which the age has been the same
+ int m_NumCyclesSame;
+ } ;
+
+ /// Maps world name -> sWorldAge
+ typedef std::map<AString, sWorldAge> WorldAges;
+
+ WorldAges m_WorldAges;
+
+
+ // cIsThread overrides:
+ virtual void Execute(void) override;
+
+ /// Sets the initial world age
+ void SetWorldAge(const AString & a_WorldName, Int64 a_Age);
+
+ /// Checks if the world's age has changed, updates the world's stats; calls DeadlockDetected() if deadlock detected
+ void CheckWorldAge(const AString & a_WorldName, Int64 a_Age);
+
+ /// Called when a deadlock is detected. Aborts the server.
+ void DeadlockDetected(void);
+} ;
+
+
+
+
diff --git a/src/Defines.h b/src/Defines.h
new file mode 100644
index 000000000..06410cab4
--- /dev/null
+++ b/src/Defines.h
@@ -0,0 +1,562 @@
+
+#pragma once
+
+
+
+
+
+typedef unsigned char Byte;
+
+/// List of slot numbers, used for inventory-painting
+typedef std::vector<int> cSlotNums;
+
+
+
+
+
+
+// tolua_begin
+
+/// How much light do the blocks emit on their own?
+extern unsigned char g_BlockLightValue[];
+
+/// How much light do the block consume?
+extern unsigned char g_BlockSpreadLightFalloff[];
+
+/// Is a block completely transparent? (light doesn't get decreased(?))
+extern bool g_BlockTransparent[];
+
+/// Is a block destroyed after a single hit?
+extern bool g_BlockOneHitDig[];
+
+/// Can a piston break this block?
+extern bool g_BlockPistonBreakable[256];
+
+/// Can this block hold snow atop?
+extern bool g_BlockIsSnowable[256];
+
+/// Does this block require a tool to drop?
+extern bool g_BlockRequiresSpecialTool[256];
+
+/// Is this block solid (player cannot walk through)?
+extern bool g_BlockIsSolid[256];
+
+/// Can torches be placed on this block?
+extern bool g_BlockIsTorchPlaceable[256];
+
+/// Experience Orb setup
+enum
+{
+ //open to suggestion on naming convention here :)
+ MAX_EXPERIENCE_ORB_SIZE = 2000
+} ;
+
+
+
+
+
+/// Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc
+enum eBlockFace
+{
+ BLOCK_FACE_NONE = -1, // Interacting with no block face - swinging the item in the air
+ BLOCK_FACE_XM = 4, // Interacting with the X- face of the block
+ BLOCK_FACE_XP = 5, // Interacting with the X+ face of the block
+ BLOCK_FACE_YM = 0, // Interacting with the Y- face of the block
+ BLOCK_FACE_YP = 1, // Interacting with the Y+ face of the block
+ BLOCK_FACE_ZM = 2, // Interacting with the Z- face of the block
+ BLOCK_FACE_ZP = 3, // Interacting with the Z+ face of the block
+
+ // Synonyms using the (deprecated) world directions:
+ BLOCK_FACE_BOTTOM = BLOCK_FACE_YM, // Interacting with the bottom face of the block
+ BLOCK_FACE_TOP = BLOCK_FACE_YP, // Interacting with the top face of the block
+ BLOCK_FACE_NORTH = BLOCK_FACE_ZM, // Interacting with the northern face of the block
+ BLOCK_FACE_SOUTH = BLOCK_FACE_ZP, // Interacting with the southern face of the block
+ BLOCK_FACE_WEST = BLOCK_FACE_XM, // Interacting with the western face of the block
+ BLOCK_FACE_EAST = BLOCK_FACE_XP, // Interacting with the eastern face of the block
+} ;
+
+
+
+
+
+/// PlayerDigging status constants
+enum
+{
+ DIG_STATUS_STARTED = 0,
+ DIG_STATUS_CANCELLED = 1,
+ DIG_STATUS_FINISHED = 2,
+ DIG_STATUS_DROP_HELD = 4,
+ DIG_STATUS_SHOOT_EAT = 5,
+} ;
+
+
+
+
+
+/// Individual actions sent in the WindowClick packet
+enum eClickAction
+{
+ // Sorted by occurrence in the 1.5 protocol
+ caLeftClick,
+ caRightClick,
+ caShiftLeftClick,
+ caShiftRightClick,
+ caNumber1,
+ caNumber2,
+ caNumber3,
+ caNumber4,
+ caNumber5,
+ caNumber6,
+ caNumber7,
+ caNumber8,
+ caNumber9,
+ caMiddleClick,
+ caDropKey,
+ caCtrlDropKey,
+ caLeftClickOutside,
+ caRightClickOutside,
+ caLeftClickOutsideHoldNothing,
+ caRightClickOutsideHoldNothing,
+ caLeftPaintBegin,
+ caRightPaintBegin,
+ caLeftPaintProgress,
+ caRightPaintProgress,
+ caLeftPaintEnd,
+ caRightPaintEnd,
+ caDblClick,
+ // Add new actions here
+ caUnknown = 255,
+
+ // Keep this list in sync with ClickActionToString() function below!
+} ;
+
+
+
+
+
+enum eGameMode
+{
+ eGameMode_NotSet = -1,
+ eGameMode_Survival = 0,
+ eGameMode_Creative = 1,
+ eGameMode_Adventure = 2,
+
+ // Easier-to-use synonyms:
+ gmNotSet = eGameMode_NotSet,
+ gmSurvival = eGameMode_Survival,
+ gmCreative = eGameMode_Creative,
+ gmAdventure = eGameMode_Adventure,
+
+ // These two are used to check GameMode for validity when converting from integers.
+ gmMax, // Gets automatically assigned
+ gmMin = 0,
+} ;
+
+
+
+
+
+enum eWeather
+{
+ eWeather_Sunny = 0,
+ eWeather_Rain = 1,
+ eWeather_ThunderStorm = 2,
+
+ // Easier-to-use synonyms:
+ wSunny = eWeather_Sunny,
+ wRain = eWeather_Rain,
+ wThunderstorm = eWeather_ThunderStorm,
+ wStorm = wThunderstorm,
+} ;
+
+
+
+
+
+inline const char * ClickActionToString(eClickAction a_ClickAction)
+{
+ switch (a_ClickAction)
+ {
+ case caLeftClick: return "caLeftClick";
+ case caRightClick: return "caRightClick";
+ case caShiftLeftClick: return "caShiftLeftClick";
+ case caShiftRightClick: return "caShiftRightClick";
+ case caNumber1: return "caNumber1";
+ case caNumber2: return "caNumber2";
+ case caNumber3: return "caNumber3";
+ case caNumber4: return "caNumber4";
+ case caNumber5: return "caNumber5";
+ case caNumber6: return "caNumber6";
+ case caNumber7: return "caNumber7";
+ case caNumber8: return "caNumber8";
+ case caNumber9: return "caNumber9";
+ case caMiddleClick: return "caMiddleClick";
+ case caDropKey: return "caDropKey";
+ case caCtrlDropKey: return "caCtrlDropKey";
+ case caLeftClickOutside: return "caLeftClickOutside";
+ case caRightClickOutside: return "caRightClickOutside";
+ case caLeftClickOutsideHoldNothing: return "caLeftClickOutsideHoldNothing";
+ case caRightClickOutsideHoldNothing: return "caRightClickOutsideHoldNothing";
+ case caLeftPaintBegin: return "caLeftPaintBegin";
+ case caRightPaintBegin: return "caRightPaintBegin";
+ case caLeftPaintProgress: return "caLeftPaintProgress";
+ case caRightPaintProgress: return "caRightPaintProgress";
+ case caLeftPaintEnd: return "caLeftPaintEnd";
+ case caRightPaintEnd: return "caRightPaintEnd";
+ case caDblClick: return "caDblClick";
+
+ case caUnknown: return "caUnknown";
+ }
+ ASSERT(!"Unknown click action");
+ return "caUnknown";
+}
+
+
+
+
+
+inline bool IsValidBlock(int a_BlockType)
+{
+ if (
+ (a_BlockType > -1) &&
+ (a_BlockType <= E_BLOCK_MAX_TYPE_ID)
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+inline bool IsValidItem(int a_ItemType)
+{
+ if (
+ ((a_ItemType >= E_ITEM_FIRST) && (a_ItemType <= E_ITEM_MAX_CONSECUTIVE_TYPE_ID)) || // Basic items range
+ ((a_ItemType >= E_ITEM_FIRST_DISC) && (a_ItemType <= E_ITEM_LAST_DISC)) // Music discs' special range
+ )
+ {
+ return true;
+ }
+
+ if (a_ItemType == 0)
+ {
+ return false;
+ }
+
+ return IsValidBlock(a_ItemType);
+}
+
+// tolua_end
+
+
+
+
+
+inline bool IsBlockWater(BLOCKTYPE a_BlockType)
+{
+ return ((a_BlockType == E_BLOCK_WATER) || (a_BlockType == E_BLOCK_STATIONARY_WATER));
+}
+
+
+
+
+
+inline bool IsBlockLava(BLOCKTYPE a_BlockType)
+{
+ return ((a_BlockType == E_BLOCK_LAVA) || (a_BlockType == E_BLOCK_STATIONARY_LAVA));
+}
+
+
+
+
+
+inline bool IsBlockLiquid(BLOCKTYPE a_BlockType)
+{
+ return IsBlockWater(a_BlockType) || IsBlockLava(a_BlockType);
+}
+
+
+
+
+
+inline bool IsBlockTypeOfDirt(BLOCKTYPE a_BlockType)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_FARMLAND:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+inline void AddFaceDirection(int & a_BlockX, int & a_BlockY, int & a_BlockZ, char a_BlockFace, bool a_bInverse = false) // tolua_export
+{ // tolua_export
+ if (!a_bInverse)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_YP: a_BlockY++; break;
+ case BLOCK_FACE_YM: a_BlockY--; break;
+ case BLOCK_FACE_ZM: a_BlockZ--; break;
+ case BLOCK_FACE_ZP: a_BlockZ++; break;
+ case BLOCK_FACE_XP: a_BlockX++; break;
+ case BLOCK_FACE_XM: a_BlockX--; break;
+ default:
+ {
+ LOGWARNING("%s: Unknown face: %d", __FUNCTION__, a_BlockFace);
+ ASSERT(!"AddFaceDirection(): Unknown face");
+ break;
+ }
+ }
+ }
+ else
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_YP: a_BlockY--; break;
+ case BLOCK_FACE_YM: a_BlockY++; break;
+ case BLOCK_FACE_ZM: a_BlockZ++; break;
+ case BLOCK_FACE_ZP: a_BlockZ--; break;
+ case BLOCK_FACE_XP: a_BlockX--; break;
+ case BLOCK_FACE_XM: a_BlockX++; break;
+ default:
+ {
+ LOGWARNING("%s: Unknown inv face: %d", __FUNCTION__, a_BlockFace);
+ ASSERT(!"AddFaceDirection(): Unknown face");
+ break;
+ }
+ }
+ }
+} // tolua_export
+
+
+
+
+
+inline void AddFaceDirection(int & a_BlockX, unsigned char & a_BlockY, int & a_BlockZ, char a_BlockFace, bool a_bInverse = false)
+{
+ int Y = a_BlockY;
+ AddFaceDirection(a_BlockX, Y, a_BlockZ, a_BlockFace, a_bInverse);
+ if (Y < 0)
+ {
+ a_BlockY = 0;
+ }
+ else if (Y > 255)
+ {
+ a_BlockY = 255;
+ }
+ else
+ {
+ a_BlockY = (unsigned char)Y;
+ }
+}
+
+
+
+
+
+#define PI 3.14159265358979323846264338327950288419716939937510582097494459072381640628620899862803482534211706798f
+
+inline void EulerToVector(double a_Pan, double a_Pitch, double & a_X, double & a_Y, double & a_Z)
+{
+ // a_X = sinf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI );
+ // a_Y = -sinf ( a_Pitch / 180 * PI );
+ // a_Z = -cosf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI );
+ a_X = cos(a_Pan / 180 * PI) * cos(a_Pitch / 180 * PI);
+ a_Y = sin(a_Pan / 180 * PI) * cos(a_Pitch / 180 * PI);
+ a_Z = sin(a_Pitch / 180 * PI);
+}
+
+
+
+
+
+inline void VectorToEuler(double a_X, double a_Y, double a_Z, double & a_Pan, double & a_Pitch)
+{
+ if (a_X != 0)
+ {
+ a_Pan = atan2(a_Z, a_X) * 180 / PI - 90;
+ }
+ else
+ {
+ a_Pan = 0;
+ }
+ a_Pitch = atan2(a_Y, sqrt((a_X * a_X) + (a_Z * a_Z))) * 180 / PI;
+}
+
+
+
+
+
+inline float GetSignf(float a_Val)
+{
+ return (a_Val < 0.f) ? -1.f : 1.f;
+}
+
+
+
+
+
+inline float GetSpecialSignf( float a_Val )
+{
+ return (a_Val <= 0.f) ? -1.f : 1.f;
+}
+
+
+
+
+// tolua_begin
+namespace ItemCategory
+{
+ inline bool IsPickaxe(short a_ItemID)
+ {
+ return (a_ItemID == E_ITEM_WOODEN_PICKAXE)
+ || (a_ItemID == E_ITEM_STONE_PICKAXE)
+ || (a_ItemID == E_ITEM_IRON_PICKAXE)
+ || (a_ItemID == E_ITEM_GOLD_PICKAXE)
+ || (a_ItemID == E_ITEM_DIAMOND_PICKAXE);
+ }
+
+
+
+ inline bool IsAxe(short a_ItemID)
+ {
+ return (a_ItemID == E_ITEM_WOODEN_AXE)
+ || (a_ItemID == E_ITEM_STONE_AXE)
+ || (a_ItemID == E_ITEM_IRON_AXE)
+ || (a_ItemID == E_ITEM_GOLD_AXE)
+ || (a_ItemID == E_ITEM_DIAMOND_AXE);
+ }
+
+
+
+ inline bool IsSword(short a_ItemID)
+ {
+ return (a_ItemID == E_ITEM_WOODEN_SWORD)
+ || (a_ItemID == E_ITEM_STONE_SWORD)
+ || (a_ItemID == E_ITEM_IRON_SWORD)
+ || (a_ItemID == E_ITEM_GOLD_SWORD)
+ || (a_ItemID == E_ITEM_DIAMOND_SWORD);
+ }
+
+
+
+ inline bool IsHoe(short a_ItemID)
+ {
+ return (a_ItemID == E_ITEM_WOODEN_HOE)
+ || (a_ItemID == E_ITEM_STONE_HOE)
+ || (a_ItemID == E_ITEM_IRON_HOE)
+ || (a_ItemID == E_ITEM_GOLD_HOE)
+ || (a_ItemID == E_ITEM_DIAMOND_HOE);
+ }
+
+
+
+ inline bool IsShovel(short a_ItemID)
+ {
+ return (a_ItemID == E_ITEM_WOODEN_SHOVEL)
+ || (a_ItemID == E_ITEM_STONE_SHOVEL)
+ || (a_ItemID == E_ITEM_IRON_SHOVEL)
+ || (a_ItemID == E_ITEM_GOLD_SHOVEL)
+ || (a_ItemID == E_ITEM_DIAMOND_SHOVEL);
+ }
+
+
+
+ inline bool IsTool(short a_ItemID)
+ {
+ return IsPickaxe( a_ItemID )
+ || IsAxe ( a_ItemID )
+ || IsSword ( a_ItemID )
+ || IsHoe ( a_ItemID )
+ || IsShovel ( a_ItemID );
+ }
+
+
+
+ inline bool IsHelmet(short a_ItemType)
+ {
+ return (
+ (a_ItemType == E_ITEM_LEATHER_CAP) ||
+ (a_ItemType == E_ITEM_GOLD_HELMET) ||
+ (a_ItemType == E_ITEM_CHAIN_HELMET) ||
+ (a_ItemType == E_ITEM_IRON_HELMET) ||
+ (a_ItemType == E_ITEM_DIAMOND_HELMET)
+ );
+ }
+
+
+
+ inline bool IsChestPlate(short a_ItemType)
+ {
+ return (
+ (a_ItemType == E_ITEM_LEATHER_TUNIC) ||
+ (a_ItemType == E_ITEM_GOLD_CHESTPLATE) ||
+ (a_ItemType == E_ITEM_CHAIN_CHESTPLATE) ||
+ (a_ItemType == E_ITEM_IRON_CHESTPLATE) ||
+ (a_ItemType == E_ITEM_DIAMOND_CHESTPLATE)
+ );
+ }
+
+
+
+ inline bool IsLeggings(short a_ItemType)
+ {
+ return (
+ (a_ItemType == E_ITEM_LEATHER_PANTS) ||
+ (a_ItemType == E_ITEM_GOLD_LEGGINGS) ||
+ (a_ItemType == E_ITEM_CHAIN_LEGGINGS) ||
+ (a_ItemType == E_ITEM_IRON_LEGGINGS) ||
+ (a_ItemType == E_ITEM_DIAMOND_LEGGINGS)
+ );
+ }
+
+
+
+ inline bool IsBoots(short a_ItemType)
+ {
+ return (
+ (a_ItemType == E_ITEM_LEATHER_BOOTS) ||
+ (a_ItemType == E_ITEM_GOLD_BOOTS) ||
+ (a_ItemType == E_ITEM_CHAIN_BOOTS) ||
+ (a_ItemType == E_ITEM_IRON_BOOTS) ||
+ (a_ItemType == E_ITEM_DIAMOND_BOOTS)
+ );
+ }
+
+
+
+ inline bool IsArmor(short a_ItemType)
+ {
+ return (
+ IsHelmet(a_ItemType) ||
+ IsChestPlate(a_ItemType) ||
+ IsLeggings(a_ItemType) ||
+ IsBoots(a_ItemType)
+ );
+ }
+}
+// tolua_end
+
+
+inline bool BlockRequiresSpecialTool(BLOCKTYPE a_BlockType)
+{
+ if(!IsValidBlock(a_BlockType)) return false;
+ return g_BlockRequiresSpecialTool[a_BlockType];
+}
+
+
+
+
+
+
diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp
new file mode 100644
index 000000000..6b53d0b52
--- /dev/null
+++ b/src/Enchantments.cpp
@@ -0,0 +1,299 @@
+// Enchantments.cpp
+
+// Implements the cEnchantments class representing a storage for item enchantments and stored-enchantments
+
+#include "Globals.h"
+#include "Enchantments.h"
+#include "WorldStorage/FastNBT.h"
+
+
+
+
+
+cEnchantments::cEnchantments(void)
+{
+ // Nothing needed yet, but the constructor needs to be declared and impemented in order to be usable
+}
+
+
+
+
+
+cEnchantments::cEnchantments(const AString & a_StringSpec)
+{
+ AddFromString(a_StringSpec);
+}
+
+
+
+
+
+void cEnchantments::AddFromString(const AString & a_StringSpec)
+{
+ // Add enchantments in the stringspec; if a specified enchantment already exists, overwrites it
+
+ // Split the StringSpec into separate declarations, each in the form "id=lvl":
+ AStringVector Decls = StringSplit(a_StringSpec, ";");
+ for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr)
+ {
+ // Split each declaration into the id and lvl part:
+ if (itr->empty())
+ {
+ // The decl is empty (may happen if there's an extra semicolon at the end), ignore silently
+ continue;
+ }
+ AStringVector Split = StringSplitAndTrim(*itr, "=");
+ if (Split.size() != 2)
+ {
+ // Malformed decl
+ LOG("%s: Malformed enchantment decl: \"%s\", skipping.", __FUNCTION__, itr->c_str());
+ continue;
+ }
+ int id = atoi(Split[0].c_str());
+ if ((id == 0) && (Split[0] != "0"))
+ {
+ id = StringToEnchantmentID(Split[0]);
+ }
+ int lvl = atoi(Split[1].c_str());
+ if (
+ ((id <= 0) && (Split[0] != "0")) ||
+ ((lvl == 0) && (Split[1] != "0"))
+ )
+ {
+ // Numbers failed to parse
+ LOG("%s: Failed to parse enchantment declaration for numbers: \"%s\" and \"%s\", skipping.",
+ __FUNCTION__, Split[0].c_str(), Split[1].c_str()
+ );
+ continue;
+ }
+ SetLevel(id, lvl);
+ } // for itr - Decls[]
+}
+
+
+
+
+
+AString cEnchantments::ToString(void) const
+{
+ // Serialize all the enchantments into a string
+ AString res;
+ for (cEnchantments::cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr)
+ {
+ AppendPrintf(res, "%d=%d;", itr->first, itr->second);
+ } // for itr - m_Enchantments[]
+ return res;
+}
+
+
+
+
+
+int cEnchantments::GetLevel(int a_EnchantmentID) const
+{
+ // Return the level for the specified enchantment; 0 if not stored
+ cMap::const_iterator itr = m_Enchantments.find(a_EnchantmentID);
+ if (itr != m_Enchantments.end())
+ {
+ return itr->second;
+ }
+
+ // Not stored, return zero
+ return 0;
+}
+
+
+
+
+
+void cEnchantments::SetLevel(int a_EnchantmentID, int a_Level)
+{
+ // Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0
+ if (a_Level == 0)
+ {
+ // Delete enchantment, if present:
+ cMap::iterator itr = m_Enchantments.find(a_EnchantmentID);
+ if (itr != m_Enchantments.end())
+ {
+ m_Enchantments.erase(itr);
+ }
+ }
+ else
+ {
+ // Add / overwrite enchantment
+ m_Enchantments[a_EnchantmentID] = a_Level;
+ }
+}
+
+
+
+
+
+
+void cEnchantments::Clear(void)
+{
+ m_Enchantments.clear();
+}
+
+
+
+
+
+bool cEnchantments::IsEmpty(void) const
+{
+ return m_Enchantments.empty();
+}
+
+
+
+
+
+int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName)
+{
+ struct
+ {
+ int m_Value;
+ const char * m_Name;
+ } EnchantmentNames[] =
+ {
+ { enchProtection, "Protection"},
+ { enchFireProtection, "FireProtection"},
+ { enchFeatherFalling, "FeatherFalling"},
+ { enchBlastProtection, "BlastProtection"},
+ { enchProjectileProtection, "ProjectileProtection"},
+ { enchRespiration, "Respiration"},
+ { enchAquaAffinity, "AquaAffinity"},
+ { enchThorns, "Thorns"},
+ { enchSharpness, "Sharpness"},
+ { enchSmite, "Smite"},
+ { enchBaneOfArthropods, "BaneOfArthropods"},
+ { enchKnockback, "Knockback"},
+ { enchFireAspect, "FireAspect"},
+ { enchLooting, "Looting"},
+ { enchEfficiency, "Efficiency"},
+ { enchSilkTouch, "SilkTouch"},
+ { enchUnbreaking, "Unbreaking"},
+ { enchFortune, "Fortune"},
+ { enchPower, "Power"},
+ { enchPunch, "Punch"},
+ { enchFlame, "Flame"},
+ { enchInfinity, "Infinity"},
+ { enchLuckOfTheSea, "LuckOfTheSea"},
+ { enchLure, "Lure"},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(EnchantmentNames); i++)
+ {
+ if (NoCaseCompare(EnchantmentNames[i].m_Name, a_EnchantmentName) == 0)
+ {
+ return EnchantmentNames[i].m_Value;
+ }
+ } // for i - EnchantmentNames[]
+ return -1;
+}
+
+
+
+
+
+bool cEnchantments::operator ==(const cEnchantments & a_Other) const
+{
+ return m_Enchantments == a_Other.m_Enchantments;
+}
+
+
+
+
+
+bool cEnchantments::operator !=(const cEnchantments & a_Other) const
+{
+ return m_Enchantments != a_Other.m_Enchantments;
+}
+
+
+
+
+
+void cEnchantments::WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const
+{
+ // Write the enchantments into the specified NBT writer
+ // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments")
+
+ a_Writer.BeginList(a_ListTagName, TAG_Compound);
+ for (cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr)
+ {
+ a_Writer.BeginCompound("");
+ a_Writer.AddShort("id", itr->first);
+ a_Writer.AddShort("lvl", itr->second);
+ a_Writer.EndCompound();
+ } // for itr - m_Enchantments[]
+ a_Writer.EndList();
+}
+
+
+
+
+
+void cEnchantments::ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx)
+{
+ // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments)
+
+ // Verify that the tag is a list:
+ if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List)
+ {
+ LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed",
+ __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx)
+ );
+ ASSERT(!"Bad EnchListTag type");
+ return;
+ }
+
+ // Verify that the list is of Compounds:
+ if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound)
+ {
+ LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed",
+ __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx)
+ );
+ ASSERT(!"Bad EnchListTag children type");
+ return;
+ }
+
+ Clear();
+
+ // Iterate over all the compound children, parse an enchantment from each:
+ for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag))
+ {
+ // tag is the compound inside the "ench" list tag
+ ASSERT(a_NBT.GetType(tag) == TAG_Compound);
+
+ // Search for the id and lvl tags' values:
+ int id = -1, lvl = -1;
+ for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch))
+ {
+ if (a_NBT.GetType(ch) != TAG_Short)
+ {
+ continue;
+ }
+ if (a_NBT.GetName(ch) == "id")
+ {
+ id = a_NBT.GetShort(ch);
+ }
+ else if (a_NBT.GetName(ch) == "lvl")
+ {
+ lvl = a_NBT.GetShort(ch);
+ }
+ } // for ch - children of the compound tag
+
+ if ((id == -1) || (lvl <= 0))
+ {
+ // Failed to parse either the id or the lvl, skip this compound
+ continue;
+ }
+
+ // Store the enchantment:
+ m_Enchantments[id] = lvl;
+ } // for tag - children of the ench list tag
+}
+
+
+
+
diff --git a/src/Enchantments.h b/src/Enchantments.h
new file mode 100644
index 000000000..7581b87b5
--- /dev/null
+++ b/src/Enchantments.h
@@ -0,0 +1,115 @@
+// Enchantments.h
+
+// Declares the cEnchantments class representing a storage for item enchantments and stored-enchantments
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd: WorldStorage/FastNBT.h
+class cFastNBTWriter;
+class cParsedNBT;
+
+
+
+
+
+// tolua_begin
+
+/** Class that stores item enchantments or stored-enchantments
+The enchantments may be serialized to a stringspec and read back from such stringspec.
+The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end,
+mapping each enchantment's id onto its level. ID may be either a number or the enchantment name.
+Level value of 0 means no such enchantment, and it will not be stored in the m_Enchantments.
+Serialization will never put zero-level enchantments into the stringspec and will always use numeric IDs.
+*/
+class cEnchantments
+{
+public:
+ /// Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs )
+ enum
+ {
+ enchProtection = 0,
+ enchFireProtection = 1,
+ enchFeatherFalling = 2,
+ enchBlastProtection = 3,
+ enchProjectileProtection = 4,
+ enchRespiration = 5,
+ enchAquaAffinity = 6,
+ enchThorns = 7,
+ enchSharpness = 16,
+ enchSmite = 17,
+ enchBaneOfArthropods = 18,
+ enchKnockback = 19,
+ enchFireAspect = 20,
+ enchLooting = 21,
+ enchEfficiency = 32,
+ enchSilkTouch = 33,
+ enchUnbreaking = 34,
+ enchFortune = 35,
+ enchPower = 48,
+ enchPunch = 49,
+ enchFlame = 50,
+ enchInfinity = 51,
+ enchLuckOfTheSea = 61,
+ enchLure = 62,
+ } ;
+
+ /// Creates an empty enchantments container
+ cEnchantments(void);
+
+ /// Creates an enchantments container filled with enchantments parsed from stringspec
+ cEnchantments(const AString & a_StringSpec);
+
+ /// Adds enchantments in the stringspec; if a specified enchantment already exists, overwrites it
+ void AddFromString(const AString & a_StringSpec);
+
+ /// Serializes all the enchantments into a string
+ AString ToString(void) const;
+
+ /// Returns the level for the specified enchantment; 0 if not stored
+ int GetLevel(int a_EnchantmentID) const;
+
+ /// Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0
+ void SetLevel(int a_EnchantmentID, int a_Level);
+
+ /// Removes all enchantments
+ void Clear(void);
+
+ /// Returns true if there are no enchantments
+ bool IsEmpty(void) const;
+
+ /// Converts enchantment name to the numeric representation; returns -1 if enchantment name not found; case insensitive
+ static int StringToEnchantmentID(const AString & a_EnchantmentName);
+
+ /// Returns true if a_Other contains exactly the same enchantments and levels
+ bool operator ==(const cEnchantments & a_Other) const;
+
+ // tolua_end
+
+ /// Returns true if a_Other doesn't contain exactly the same enchantments and levels
+ bool operator !=(const cEnchantments & a_Other) const;
+
+ /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments")
+ void WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const;
+
+ /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments)
+ void ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx);
+
+protected:
+ /// Maps enchantment ID -> enchantment level
+ typedef std::map<int, int> cMap;
+
+ /// Currently stored enchantments
+ cMap m_Enchantments;
+} ; // tolua_export
+
+
+
+
diff --git a/src/Endianness.h b/src/Endianness.h
new file mode 100644
index 000000000..86eb369f5
--- /dev/null
+++ b/src/Endianness.h
@@ -0,0 +1,70 @@
+
+#pragma once
+
+
+
+
+
+// Changes endianness
+inline unsigned long long HostToNetwork8(const void* a_Value )
+{
+ unsigned long long __HostToNetwork8;
+ memcpy( &__HostToNetwork8, a_Value, sizeof( __HostToNetwork8 ) );
+ __HostToNetwork8 = (( ( (unsigned long long)htonl((u_long)__HostToNetwork8) ) << 32) + htonl(__HostToNetwork8 >> 32));
+ return __HostToNetwork8;
+}
+
+
+
+
+
+inline unsigned int HostToNetwork4(const void* a_Value )
+{
+ unsigned int __HostToNetwork4;
+ memcpy( &__HostToNetwork4, a_Value, sizeof( __HostToNetwork4 ) );
+ __HostToNetwork4 = ntohl( __HostToNetwork4 );
+ return __HostToNetwork4;
+}
+
+
+
+
+
+inline double NetworkToHostDouble8(const void* a_Value )
+{
+#define ntohll(x) ((((unsigned long long)ntohl((u_long)x)) << 32) + ntohl(x >> 32))
+ unsigned long long buf = 0;//(*(unsigned long long*)a_Value);
+ memcpy( &buf, a_Value, 8 );
+ buf = ntohll(buf);
+ double x;
+ memcpy(&x, &buf, sizeof(double));
+ return x;
+}
+
+
+
+
+
+inline long long NetworkToHostLong8(const void * a_Value )
+{
+ unsigned long long buf = *(unsigned long long*)a_Value;
+ buf = ntohll(buf);
+ return *reinterpret_cast<long long *>(&buf);
+}
+
+
+
+
+
+inline float NetworkToHostFloat4(const void* a_Value )
+{
+ u_long buf = *(u_long*)a_Value;
+ buf = ntohl( buf );
+ float x = 0;
+ memcpy( &x, &buf, sizeof(float) );
+ return x;
+}
+
+
+
+
diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp
new file mode 100644
index 000000000..56e766dd4
--- /dev/null
+++ b/src/Entities/Boat.cpp
@@ -0,0 +1,87 @@
+
+// Boat.cpp
+
+// Implements the cBoat class representing a boat in the world
+
+#include "Globals.h"
+#include "Boat.h"
+#include "../World.h"
+#include "../ClientHandle.h"
+#include "Player.h"
+
+
+
+
+
+cBoat::cBoat(double a_X, double a_Y, double a_Z) :
+ super(etBoat, a_X, a_Y, a_Z, 0.98, 0.7)
+{
+ SetMass(20.f);
+ SetMaxHealth(6);
+ SetHealth(6);
+}
+
+
+
+
+void cBoat::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ a_ClientHandle.SendSpawnVehicle(*this, 1);
+}
+
+
+
+
+
+void cBoat::DoTakeDamage(TakeDamageInfo & TDI)
+{
+ super::DoTakeDamage(TDI);
+
+ if (GetHealth() == 0)
+ {
+ Destroy(true);
+ }
+}
+
+
+
+
+
+void cBoat::OnRightClicked(cPlayer & a_Player)
+{
+ if (m_Attachee != NULL)
+ {
+ if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ {
+ // This player is already sitting in, they want out.
+ a_Player.Detach();
+ return;
+ }
+
+ if (m_Attachee->IsPlayer())
+ {
+ // Another player is already sitting in here, cannot attach
+ return;
+ }
+
+ // Detach whatever is sitting in this boat now:
+ m_Attachee->Detach();
+ }
+
+ // Attach the player to this boat
+ a_Player.AttachTo(this);
+}
+
+
+
+
+
+void cBoat::HandlePhysics(float a_Dt, cChunk & a_Chunk)
+{
+ super::HandlePhysics(a_Dt, a_Chunk);
+ BroadcastMovementUpdate();
+}
+
+
+
+
diff --git a/src/Entities/Boat.h b/src/Entities/Boat.h
new file mode 100644
index 000000000..8c51ab86c
--- /dev/null
+++ b/src/Entities/Boat.h
@@ -0,0 +1,37 @@
+
+// Boat.h
+
+// Declares the cBoat class representing a boat in the world
+
+
+
+
+
+#pragma once
+
+#include "Entity.h"
+
+
+
+
+
+class cBoat :
+ public cEntity
+{
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(cBoat);
+
+ // cEntity overrides:
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
+ virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
+
+ cBoat(double a_X, double a_Y, double a_Z);
+} ;
+
+
+
+
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
new file mode 100644
index 000000000..3bea7bc01
--- /dev/null
+++ b/src/Entities/Entity.cpp
@@ -0,0 +1,1450 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Entity.h"
+#include "../World.h"
+#include "../Server.h"
+#include "../Root.h"
+#include "../Vector3d.h"
+#include "../Matrix4f.h"
+#include "../ReferenceManager.h"
+#include "../ClientHandle.h"
+#include "../Chunk.h"
+#include "../Simulator/FluidSimulator.h"
+#include "../PluginManager.h"
+#include "../Tracer.h"
+#include "Minecart.h"
+
+
+
+
+
+int cEntity::m_EntityCount = 0;
+cCriticalSection cEntity::m_CSCount;
+
+
+
+
+
+cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height)
+ : m_UniqueID(0)
+ , m_Health(1)
+ , m_MaxHealth(1)
+ , m_AttachedTo(NULL)
+ , m_Attachee(NULL)
+ , m_Referencers(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCERS))
+ , m_References(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCES))
+ , m_HeadYaw( 0.0 )
+ , m_Rot(0.0, 0.0, 0.0)
+ , m_Pos(a_X, a_Y, a_Z)
+ , m_Mass (0.001) //Default 1g
+ , m_bDirtyHead(true)
+ , m_bDirtyOrientation(true)
+ , m_bDirtyPosition(true)
+ , m_bDirtySpeed(true)
+ , m_bOnGround( false )
+ , m_Gravity( -9.81f )
+ , m_IsInitialized(false)
+ , m_LastPosX( 0.0 )
+ , m_LastPosY( 0.0 )
+ , m_LastPosZ( 0.0 )
+ , m_TimeLastTeleportPacket(0)
+ , m_TimeLastMoveReltPacket(0)
+ , m_TimeLastSpeedPacket(0)
+ , m_EntityType(a_EntityType)
+ , m_World(NULL)
+ , m_TicksSinceLastBurnDamage(0)
+ , m_TicksSinceLastLavaDamage(0)
+ , m_TicksSinceLastFireDamage(0)
+ , m_TicksSinceLastVoidDamage(0)
+ , m_TicksLeftBurning(0)
+ , m_WaterSpeed(0, 0, 0)
+ , m_Width(a_Width)
+ , m_Height(a_Height)
+{
+ cCSLock Lock(m_CSCount);
+ m_EntityCount++;
+ m_UniqueID = m_EntityCount;
+}
+
+
+
+
+
+cEntity::~cEntity()
+{
+ ASSERT(!m_World->HasEntity(m_UniqueID)); // Before deleting, the entity needs to have been removed from the world
+
+ LOGD("Deleting entity %d at pos {%.2f, %.2f, %.2f} ~ [%d, %d]; ptr %p",
+ m_UniqueID,
+ m_Pos.x, m_Pos.y, m_Pos.z,
+ (int)(m_Pos.x / cChunkDef::Width), (int)(m_Pos.z / cChunkDef::Width),
+ this
+ );
+
+ if (m_AttachedTo != NULL)
+ {
+ Detach();
+ }
+ if (m_Attachee != NULL)
+ {
+ m_Attachee->Detach();
+ }
+
+ if (m_IsInitialized)
+ {
+ LOGWARNING("ERROR: Entity deallocated without being destroyed");
+ ASSERT(!"Entity deallocated without being destroyed or unlinked");
+ }
+ delete m_Referencers;
+ delete m_References;
+}
+
+
+
+
+
+const char * cEntity::GetClass(void) const
+{
+ return "cEntity";
+}
+
+
+
+
+
+const char * cEntity::GetClassStatic(void)
+{
+ return "cEntity";
+}
+
+
+
+
+
+const char * cEntity::GetParentClass(void) const
+{
+ return "";
+}
+
+
+
+
+
+bool cEntity::Initialize(cWorld * a_World)
+{
+ if (cPluginManager::Get()->CallHookSpawningEntity(*a_World, *this))
+ {
+ return false;
+ }
+
+ LOGD("Initializing entity #%d (%s) at {%.02f, %.02f, %.02f}",
+ m_UniqueID, GetClass(), m_Pos.x, m_Pos.y, m_Pos.z
+ );
+ m_IsInitialized = true;
+ m_World = a_World;
+ m_World->AddEntity(this);
+
+ cPluginManager::Get()->CallHookSpawnedEntity(*a_World, *this);
+
+ // Spawn the entity on the clients:
+ a_World->BroadcastSpawnEntity(*this);
+
+ return true;
+}
+
+
+
+
+
+void cEntity::WrapHeadYaw(void)
+{
+ while (m_HeadYaw > 180.f) m_HeadYaw -= 360.f; // Wrap it
+ while (m_HeadYaw < -180.f) m_HeadYaw += 360.f;
+}
+
+
+
+
+
+void cEntity::WrapRotation(void)
+{
+ while (m_Rot.x > 180.f) m_Rot.x -= 360.f; // Wrap it
+ while (m_Rot.x < -180.f) m_Rot.x += 360.f;
+ while (m_Rot.y > 180.f) m_Rot.y -= 360.f;
+ while (m_Rot.y < -180.f) m_Rot.y += 360.f;
+}
+
+
+
+
+void cEntity::WrapSpeed(void)
+{
+ // There shoudn't be a need for flipping the flag on because this function is called
+ // after any update, so the flag is already turned on
+ if (m_Speed.x > 78.0f) m_Speed.x = 78.0f;
+ else if (m_Speed.x < -78.0f) m_Speed.x = -78.0f;
+ if (m_Speed.y > 78.0f) m_Speed.y = 78.0f;
+ else if (m_Speed.y < -78.0f) m_Speed.y = -78.0f;
+ if (m_Speed.z > 78.0f) m_Speed.z = 78.0f;
+ else if (m_Speed.z < -78.0f) m_Speed.z = -78.0f;
+}
+
+
+
+
+
+void cEntity::Destroy(bool a_ShouldBroadcast)
+{
+ if (!m_IsInitialized)
+ {
+ return;
+ }
+
+ if (a_ShouldBroadcast)
+ {
+ m_World->BroadcastDestroyEntity(*this);
+ }
+
+ m_IsInitialized = false;
+
+ Destroyed();
+}
+
+
+
+
+
+void cEntity::TakeDamage(cEntity & a_Attacker)
+{
+ int RawDamage = a_Attacker.GetRawDamageAgainst(*this);
+
+ TakeDamage(dtAttack, &a_Attacker, RawDamage, a_Attacker.GetKnockbackAmountAgainst(*this));
+}
+
+
+
+
+
+void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount)
+{
+ int FinalDamage = a_RawDamage - GetArmorCoverAgainst(a_Attacker, a_DamageType, a_RawDamage);
+ cEntity::TakeDamage(a_DamageType, a_Attacker, a_RawDamage, FinalDamage, a_KnockbackAmount);
+}
+
+
+
+
+
+void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount)
+{
+ TakeDamageInfo TDI;
+ TDI.DamageType = a_DamageType;
+ TDI.Attacker = a_Attacker;
+ TDI.RawDamage = a_RawDamage;
+ TDI.FinalDamage = a_FinalDamage;
+ Vector3d Heading;
+ Heading.x = sin(GetRotation());
+ Heading.y = 0.4; // TODO: adjust the amount of "up" knockback when testing
+ Heading.z = cos(GetRotation());
+ TDI.Knockback = Heading * a_KnockbackAmount;
+ DoTakeDamage(TDI);
+}
+
+
+
+
+
+void cEntity::SetRotationFromSpeed(void)
+{
+ const double EPS = 0.0000001;
+ if ((abs(m_Speed.x) < EPS) && (abs(m_Speed.z) < EPS))
+ {
+ // atan2() may overflow or is undefined, pick any number
+ SetRotation(0);
+ return;
+ }
+ SetRotation(atan2(m_Speed.x, m_Speed.z) * 180 / PI);
+}
+
+
+
+
+
+void cEntity::SetPitchFromSpeed(void)
+{
+ const double EPS = 0.0000001;
+ double xz = sqrt(m_Speed.x * m_Speed.x + m_Speed.z * m_Speed.z); // Speed XZ-plane component
+ if ((abs(xz) < EPS) && (abs(m_Speed.y) < EPS))
+ {
+ // atan2() may overflow or is undefined, pick any number
+ SetPitch(0);
+ return;
+ }
+ SetPitch(atan2(m_Speed.y, xz) * 180 / PI);
+}
+
+
+
+
+
+void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI))
+ {
+ return;
+ }
+
+ if (m_Health <= 0)
+ {
+ // Can't take damage if already dead
+ return;
+ }
+
+ m_Health -= (short)a_TDI.FinalDamage;
+
+ // TODO: Apply damage to armor
+
+ if (m_Health < 0)
+ {
+ m_Health = 0;
+ }
+
+ m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT);
+
+ if (m_Health <= 0)
+ {
+ KilledBy(a_TDI.Attacker);
+ }
+}
+
+
+
+
+
+int cEntity::GetRawDamageAgainst(const cEntity & a_Receiver)
+{
+ // Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items
+ // Ref: http://www.minecraftwiki.net/wiki/Damage#Dealing_damage as of 2012_12_20
+ switch (this->GetEquippedWeapon().m_ItemType)
+ {
+ case E_ITEM_WOODEN_SWORD: return 4;
+ case E_ITEM_GOLD_SWORD: return 4;
+ case E_ITEM_STONE_SWORD: return 5;
+ case E_ITEM_IRON_SWORD: return 6;
+ case E_ITEM_DIAMOND_SWORD: return 7;
+
+ case E_ITEM_WOODEN_AXE: return 3;
+ case E_ITEM_GOLD_AXE: return 3;
+ case E_ITEM_STONE_AXE: return 4;
+ case E_ITEM_IRON_AXE: return 5;
+ case E_ITEM_DIAMOND_AXE: return 6;
+
+ case E_ITEM_WOODEN_PICKAXE: return 2;
+ case E_ITEM_GOLD_PICKAXE: return 2;
+ case E_ITEM_STONE_PICKAXE: return 3;
+ case E_ITEM_IRON_PICKAXE: return 4;
+ case E_ITEM_DIAMOND_PICKAXE: return 5;
+
+ case E_ITEM_WOODEN_SHOVEL: return 1;
+ case E_ITEM_GOLD_SHOVEL: return 1;
+ case E_ITEM_STONE_SHOVEL: return 2;
+ case E_ITEM_IRON_SHOVEL: return 3;
+ case E_ITEM_DIAMOND_SHOVEL: return 4;
+ }
+ // All other equipped items give a damage of 1:
+ return 1;
+}
+
+
+
+
+
+int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage)
+{
+ // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
+
+ // Filter out damage types that are not protected by armor:
+ // Ref.: http://www.minecraftwiki.net/wiki/Armor#Effects as of 2012_12_20
+ switch (a_DamageType)
+ {
+ case dtOnFire:
+ case dtSuffocating:
+ case dtDrowning: // TODO: This one could be a special case - in various MC versions (PC vs XBox) it is and isn't armor-protected
+ case dtStarving:
+ case dtInVoid:
+ case dtPoisoning:
+ case dtPotionOfHarming:
+ case dtFalling:
+ case dtLightning:
+ {
+ return 0;
+ }
+ }
+
+ // Add up all armor points:
+ // Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20
+ int ArmorValue = 0;
+ switch (GetEquippedHelmet().m_ItemType)
+ {
+ case E_ITEM_LEATHER_CAP: ArmorValue += 1; break;
+ case E_ITEM_GOLD_HELMET: ArmorValue += 2; break;
+ case E_ITEM_CHAIN_HELMET: ArmorValue += 2; break;
+ case E_ITEM_IRON_HELMET: ArmorValue += 2; break;
+ case E_ITEM_DIAMOND_HELMET: ArmorValue += 3; break;
+ }
+ switch (GetEquippedChestplate().m_ItemType)
+ {
+ case E_ITEM_LEATHER_TUNIC: ArmorValue += 3; break;
+ case E_ITEM_GOLD_CHESTPLATE: ArmorValue += 5; break;
+ case E_ITEM_CHAIN_CHESTPLATE: ArmorValue += 5; break;
+ case E_ITEM_IRON_CHESTPLATE: ArmorValue += 6; break;
+ case E_ITEM_DIAMOND_CHESTPLATE: ArmorValue += 8; break;
+ }
+ switch (GetEquippedLeggings().m_ItemType)
+ {
+ case E_ITEM_LEATHER_PANTS: ArmorValue += 2; break;
+ case E_ITEM_GOLD_LEGGINGS: ArmorValue += 3; break;
+ case E_ITEM_CHAIN_LEGGINGS: ArmorValue += 4; break;
+ case E_ITEM_IRON_LEGGINGS: ArmorValue += 5; break;
+ case E_ITEM_DIAMOND_LEGGINGS: ArmorValue += 6; break;
+ }
+ switch (GetEquippedBoots().m_ItemType)
+ {
+ case E_ITEM_LEATHER_BOOTS: ArmorValue += 1; break;
+ case E_ITEM_GOLD_BOOTS: ArmorValue += 1; break;
+ case E_ITEM_CHAIN_BOOTS: ArmorValue += 1; break;
+ case E_ITEM_IRON_BOOTS: ArmorValue += 2; break;
+ case E_ITEM_DIAMOND_BOOTS: ArmorValue += 3; break;
+ }
+
+ // TODO: Special armor cases, such as wool, saddles, dog's collar
+ // Ref.: http://www.minecraftwiki.net/wiki/Armor#Mob_armor as of 2012_12_20
+
+ // Now ArmorValue is in [0, 20] range, which corresponds to [0, 80%] protection. Calculate the hitpoints from that:
+ return a_Damage * (ArmorValue * 4) / 100;
+}
+
+
+
+
+
+double cEntity::GetKnockbackAmountAgainst(const cEntity & a_Receiver)
+{
+ // Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit
+
+ // TODO: Enchantments
+ return 1;
+}
+
+
+
+
+
+void cEntity::KilledBy(cEntity * a_Killer)
+{
+ m_Health = 0;
+
+ cRoot::Get()->GetPluginManager()->CallHookKilling(*this, a_Killer);
+
+ if (m_Health > 0)
+ {
+ // Plugin wants to 'unkill' the pawn. Abort
+ return;
+ }
+
+ // Drop loot:
+ cItems Drops;
+ GetDrops(Drops, a_Killer);
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ());
+
+ m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_DEAD);
+}
+
+
+
+
+
+void cEntity::Heal(int a_HitPoints)
+{
+ m_Health += a_HitPoints;
+ if (m_Health > m_MaxHealth)
+ {
+ m_Health = m_MaxHealth;
+ }
+}
+
+
+
+
+
+void cEntity::SetHealth(int a_Health)
+{
+ m_Health = std::max(0, std::min(m_MaxHealth, a_Health));
+}
+
+
+
+
+
+void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ if (m_AttachedTo != NULL)
+ {
+ if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5)
+ {
+ SetPosition(m_AttachedTo->GetPosition());
+ }
+ }
+ else
+ {
+ if (a_Chunk.IsValid())
+ {
+ HandlePhysics(a_Dt, a_Chunk);
+ }
+ }
+ if (a_Chunk.IsValid())
+ {
+ TickBurning(a_Chunk);
+ }
+ if ((a_Chunk.IsValid()) && (GetPosY() < -46))
+ {
+ TickInVoid(a_Chunk);
+ }
+ else { m_TicksSinceLastVoidDamage = 0; }
+}
+
+
+
+
+
+void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
+{
+ // TODO Add collision detection with entities.
+ a_Dt /= 1000; // Convert from msec to sec
+ Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ());
+ Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ());
+ int BlockX = (int) floor(NextPos.x);
+ int BlockY = (int) floor(NextPos.y);
+ int BlockZ = (int) floor(NextPos.z);
+
+ if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
+ {
+ // Outside of the world
+
+ cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
+ // See if we can commit our changes. If not, we will discard them.
+ if (NextChunk != NULL)
+ {
+ SetSpeed(NextSpeed);
+ NextPos += (NextSpeed * a_Dt);
+ SetPosition(NextPos);
+ }
+ return;
+ }
+
+ // Make sure we got the correct chunk and a valid one. No one ever knows...
+ cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
+ if (NextChunk != NULL)
+ {
+ int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
+ int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
+ BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ );
+ BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
+ if (!g_BlockIsSolid[BlockIn]) // Making sure we are not inside a solid block
+ {
+ if (m_bOnGround) // check if it's still on the ground
+ {
+ if (!g_BlockIsSolid[BlockBelow]) // Check if block below is air or water.
+ {
+ m_bOnGround = false;
+ }
+ }
+ }
+ else
+ {
+ // Push out entity.
+ BLOCKTYPE GotBlock;
+
+ static const struct
+ {
+ int x, y, z;
+ } gCrossCoords[] =
+ {
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ } ;
+
+ bool IsNoAirSurrounding = true;
+ for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ {
+ if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock))
+ {
+ // The pickup is too close to an unloaded chunk, bail out of any physics handling
+ return;
+ }
+ if (!g_BlockIsSolid[GotBlock])
+ {
+ NextPos.x += gCrossCoords[i].x;
+ NextPos.z += gCrossCoords[i].z;
+ IsNoAirSurrounding = false;
+ break;
+ }
+ } // for i - gCrossCoords[]
+
+ if (IsNoAirSurrounding)
+ {
+ NextPos.y += 0.5;
+ }
+
+ m_bOnGround = true;
+
+ LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}",
+ m_UniqueID, GetClass(), BlockX, BlockY, BlockZ
+ );
+ }
+
+ if (!m_bOnGround)
+ {
+ float fallspeed;
+ if (IsBlockWater(BlockIn))
+ {
+ fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water.
+ }
+ else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts
+ {
+ fallspeed = 0;
+ m_bOnGround = true;
+ }
+ else if (BlockIn == E_BLOCK_COBWEB)
+ {
+ NextSpeed.y *= 0.05; // Reduce overall falling speed
+ fallspeed = 0; // No falling.
+ }
+ else
+ {
+ // Normal gravity
+ fallspeed = m_Gravity * a_Dt;
+ }
+ NextSpeed.y += fallspeed;
+ }
+ else
+ {
+ if (IsMinecart())
+ {
+ if (!IsBlockRail(BlockBelow))
+ {
+ // Friction if minecart is off track, otherwise, Minecart.cpp handles this
+ if (NextSpeed.SqrLength() > 0.0004f)
+ {
+ NextSpeed.x *= 0.7f / (1 + a_Dt);
+ if (fabs(NextSpeed.x) < 0.05)
+ {
+ NextSpeed.x = 0;
+ }
+ NextSpeed.z *= 0.7f / (1 + a_Dt);
+ if (fabs(NextSpeed.z) < 0.05)
+ {
+ NextSpeed.z = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Friction for non-minecarts
+ if (NextSpeed.SqrLength() > 0.0004f)
+ {
+ NextSpeed.x *= 0.7f / (1 + a_Dt);
+ if (fabs(NextSpeed.x) < 0.05)
+ {
+ NextSpeed.x = 0;
+ }
+ NextSpeed.z *= 0.7f / (1 + a_Dt);
+ if (fabs(NextSpeed.z) < 0.05)
+ {
+ NextSpeed.z = 0;
+ }
+ }
+ }
+ }
+
+ // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
+ // might have different speed modifiers according to terrain.
+ if (BlockIn == E_BLOCK_COBWEB)
+ {
+ NextSpeed.x *= 0.25;
+ NextSpeed.z *= 0.25;
+ }
+
+ //Get water direction
+ Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ);
+
+ m_WaterSpeed *= 0.9f; //Reduce speed each tick
+
+ switch(WaterDir)
+ {
+ case X_PLUS:
+ m_WaterSpeed.x = 0.2f;
+ m_bOnGround = false;
+ break;
+ case X_MINUS:
+ m_WaterSpeed.x = -0.2f;
+ m_bOnGround = false;
+ break;
+ case Z_PLUS:
+ m_WaterSpeed.z = 0.2f;
+ m_bOnGround = false;
+ break;
+ case Z_MINUS:
+ m_WaterSpeed.z = -0.2f;
+ m_bOnGround = false;
+ break;
+
+ default:
+ break;
+ }
+
+ if (fabs(m_WaterSpeed.x) < 0.05)
+ {
+ m_WaterSpeed.x = 0;
+ }
+
+ if (fabs(m_WaterSpeed.z) < 0.05)
+ {
+ m_WaterSpeed.z = 0;
+ }
+
+ NextSpeed += m_WaterSpeed;
+
+ if( NextSpeed.SqrLength() > 0.f )
+ {
+ cTracer Tracer( GetWorld() );
+ int Ret = Tracer.Trace( NextPos, NextSpeed, 2 );
+ if( Ret ) // Oh noez! we hit something
+ {
+ // Set to hit position
+ if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() )
+ {
+ if( Ret == 1 )
+ {
+ if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f;
+ if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f;
+ if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f;
+
+ if( Tracer.HitNormal.y > 0 ) // means on ground
+ {
+ m_bOnGround = true;
+ }
+ }
+ NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z);
+ NextPos.x += Tracer.HitNormal.x * 0.3f;
+ NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot
+ NextPos.z += Tracer.HitNormal.z * 0.3f;
+ }
+ else
+ {
+ NextPos += (NextSpeed * a_Dt);
+ }
+ }
+ else
+ {
+ // We didn't hit anything, so move =]
+ NextPos += (NextSpeed * a_Dt);
+ }
+ }
+ BlockX = (int) floor(NextPos.x);
+ BlockZ = (int) floor(NextPos.z);
+ NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ);
+ // See if we can commit our changes. If not, we will discard them.
+ if (NextChunk != NULL)
+ {
+ if (NextPos.x != GetPosX()) SetPosX(NextPos.x);
+ if (NextPos.y != GetPosY()) SetPosY(NextPos.y);
+ if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z);
+ if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x);
+ if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y);
+ if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z);
+ }
+ }
+}
+
+
+
+
+
+void cEntity::TickBurning(cChunk & a_Chunk)
+{
+ // Remember the current burning state:
+ bool HasBeenBurning = (m_TicksLeftBurning > 0);
+
+ // Do the burning damage:
+ if (m_TicksLeftBurning > 0)
+ {
+ m_TicksSinceLastBurnDamage++;
+ if (m_TicksSinceLastBurnDamage >= BURN_TICKS_PER_DAMAGE)
+ {
+ TakeDamage(dtOnFire, NULL, BURN_DAMAGE, 0);
+ m_TicksSinceLastBurnDamage = 0;
+ }
+ m_TicksLeftBurning--;
+ }
+
+ // Update the burning times, based on surroundings:
+ int MinRelX = (int)floor(GetPosX() - m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int MaxRelX = (int)floor(GetPosX() + m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int MinRelZ = (int)floor(GetPosZ() - m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ int MaxRelZ = (int)floor(GetPosZ() + m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ int MinY = std::max(0, std::min(cChunkDef::Height - 1, (int)floor(GetPosY())));
+ int MaxY = std::max(0, std::min(cChunkDef::Height - 1, (int)ceil (GetPosY() + m_Height)));
+ bool HasWater = false;
+ bool HasLava = false;
+ bool HasFire = false;
+
+ for (int x = MinRelX; x <= MaxRelX; x++)
+ {
+ for (int z = MinRelZ; z <= MaxRelZ; z++)
+ {
+ int RelX = x;
+ int RelZ = z;
+ cChunk * CurChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelX, RelZ);
+ if (CurChunk == NULL)
+ {
+ continue;
+ }
+ for (int y = MinY; y <= MaxY; y++)
+ {
+ switch (CurChunk->GetBlock(RelX, y, RelZ))
+ {
+ case E_BLOCK_FIRE:
+ {
+ HasFire = true;
+ break;
+ }
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ HasLava = true;
+ break;
+ }
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_WATER:
+ {
+ HasWater = true;
+ break;
+ }
+ } // switch (BlockType)
+ } // for y
+ } // for z
+ } // for x
+
+ if (HasWater)
+ {
+ // Extinguish the fire
+ m_TicksLeftBurning = 0;
+ }
+
+ if (HasLava)
+ {
+ // Burn:
+ m_TicksLeftBurning = BURN_TICKS;
+
+ // Periodically damage:
+ m_TicksSinceLastLavaDamage++;
+ if (m_TicksSinceLastLavaDamage >= LAVA_TICKS_PER_DAMAGE)
+ {
+ TakeDamage(dtLavaContact, NULL, LAVA_DAMAGE, 0);
+ m_TicksSinceLastLavaDamage = 0;
+ }
+ }
+ else
+ {
+ m_TicksSinceLastLavaDamage = 0;
+ }
+
+ if (HasFire)
+ {
+ // Burn:
+ m_TicksLeftBurning = BURN_TICKS;
+
+ // Periodically damage:
+ m_TicksSinceLastFireDamage++;
+ if (m_TicksSinceLastFireDamage >= FIRE_TICKS_PER_DAMAGE)
+ {
+ TakeDamage(dtFireContact, NULL, FIRE_DAMAGE, 0);
+ m_TicksSinceLastFireDamage = 0;
+ }
+ }
+ else
+ {
+ m_TicksSinceLastFireDamage = 0;
+ }
+
+ // If just started / finished burning, notify descendants:
+ if ((m_TicksLeftBurning > 0) && !HasBeenBurning)
+ {
+ OnStartedBurning();
+ }
+ else if ((m_TicksLeftBurning <= 0) && HasBeenBurning)
+ {
+ OnFinishedBurning();
+ }
+}
+
+
+
+
+
+void cEntity::TickInVoid(cChunk & a_Chunk)
+{
+ if (m_TicksSinceLastVoidDamage == 20)
+ {
+ TakeDamage(dtInVoid, NULL, 2, 0);
+ m_TicksSinceLastVoidDamage = 0;
+ }
+ else
+ {
+ m_TicksSinceLastVoidDamage++;
+ }
+}
+
+
+
+
+
+/// Called when the entity starts burning
+void cEntity::OnStartedBurning(void)
+{
+ // Broadcast the change:
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+/// Called when the entity finishes burning
+void cEntity::OnFinishedBurning(void)
+{
+ // Broadcast the change:
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+/// Sets the maximum value for the health
+void cEntity::SetMaxHealth(int a_MaxHealth)
+{
+ m_MaxHealth = a_MaxHealth;
+
+ // Reset health, if too high:
+ if (m_Health > a_MaxHealth)
+ {
+ m_Health = a_MaxHealth;
+ }
+}
+
+
+
+
+
+/// Puts the entity on fire for the specified amount of ticks
+void cEntity::StartBurning(int a_TicksLeftBurning)
+{
+ if (m_TicksLeftBurning > 0)
+ {
+ // Already burning, top up the ticks left burning and bail out:
+ m_TicksLeftBurning = std::max(m_TicksLeftBurning, a_TicksLeftBurning);
+ return;
+ }
+
+ m_TicksLeftBurning = a_TicksLeftBurning;
+ OnStartedBurning();
+}
+
+
+
+
+
+/// Stops the entity from burning, resets all burning timers
+void cEntity::StopBurning(void)
+{
+ bool HasBeenBurning = (m_TicksLeftBurning > 0);
+ m_TicksLeftBurning = 0;
+ m_TicksSinceLastBurnDamage = 0;
+ m_TicksSinceLastFireDamage = 0;
+ m_TicksSinceLastLavaDamage = 0;
+
+ // Notify if the entity has stopped burning
+ if (HasBeenBurning)
+ {
+ OnFinishedBurning();
+ }
+}
+
+
+
+
+
+void cEntity::TeleportToEntity(cEntity & a_Entity)
+{
+ TeleportToCoords(a_Entity.GetPosX(), a_Entity.GetPosY(), a_Entity.GetPosZ());
+}
+
+
+
+
+
+void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
+{
+ SetPosition(a_PosX, a_PosY, a_PosZ);
+ m_World->BroadcastTeleportEntity(*this);
+}
+
+
+
+
+
+void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude)
+{
+ //We need to keep updating the clients when there is movement or if there was a change in speed and after 2 ticks
+ if( (m_Speed.SqrLength() > 0.0004f || m_bDirtySpeed) && (m_World->GetWorldAge() - m_TimeLastSpeedPacket >= 2))
+ {
+ m_World->BroadcastEntityVelocity(*this,a_Exclude);
+ m_bDirtySpeed = false;
+ m_TimeLastSpeedPacket = m_World->GetWorldAge();
+ }
+
+ //Have to process position related packets this every two ticks
+ if (m_World->GetWorldAge() % 2 == 0)
+ {
+ int DiffX = (int) (floor(GetPosX() * 32.0) - floor(m_LastPosX * 32.0));
+ int DiffY = (int) (floor(GetPosY() * 32.0) - floor(m_LastPosY * 32.0));
+ int DiffZ = (int) (floor(GetPosZ() * 32.0) - floor(m_LastPosZ * 32.0));
+ Int64 DiffTeleportPacket = m_World->GetWorldAge() - m_TimeLastTeleportPacket;
+ // 4 blocks is max Relative So if the Diff is greater than 127 or. Send an absolute position every 20 seconds
+ if (DiffTeleportPacket >= 400 ||
+ ((DiffX > 127) || (DiffX < -128) ||
+ (DiffY > 127) || (DiffY < -128) ||
+ (DiffZ > 127) || (DiffZ < -128)))
+ {
+ //
+ m_World->BroadcastTeleportEntity(*this,a_Exclude);
+ m_TimeLastTeleportPacket = m_World->GetWorldAge();
+ m_TimeLastMoveReltPacket = m_TimeLastTeleportPacket; //Must synchronize.
+ m_LastPosX = GetPosX();
+ m_LastPosY = GetPosY();
+ m_LastPosZ = GetPosZ();
+ m_bDirtyPosition = false;
+ m_bDirtyOrientation = false;
+ }
+ else
+ {
+ Int64 DiffMoveRelPacket = m_World->GetWorldAge() - m_TimeLastMoveReltPacket;
+ //if the change is big enough.
+ if ((abs(DiffX) >= 4 || abs(DiffY) >= 4 || abs(DiffZ) >= 4 || DiffMoveRelPacket >= 60) && m_bDirtyPosition)
+ {
+ if (m_bDirtyOrientation)
+ {
+ m_World->BroadcastEntityRelMoveLook(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude);
+ m_bDirtyOrientation = false;
+ }
+ else
+ {
+ m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude);
+ }
+ m_LastPosX = GetPosX();
+ m_LastPosY = GetPosY();
+ m_LastPosZ = GetPosZ();
+ m_bDirtyPosition = false;
+ m_TimeLastMoveReltPacket = m_World->GetWorldAge();
+ }
+ else
+ {
+ if (m_bDirtyOrientation)
+ {
+ m_World->BroadcastEntityLook(*this,a_Exclude);
+ m_bDirtyOrientation = false;
+ }
+ }
+ }
+ if (m_bDirtyHead)
+ {
+ m_World->BroadcastEntityHeadLook(*this,a_Exclude);
+ m_bDirtyHead = false;
+ }
+ }
+}
+
+
+
+
+
+void cEntity::AttachTo(cEntity * a_AttachTo)
+{
+ if (m_AttachedTo == a_AttachTo)
+ {
+ // Already attached to that entity, nothing to do here
+ return;
+ }
+
+ // Detach from any previous entity:
+ Detach();
+
+ // Attach to the new entity:
+ m_AttachedTo = a_AttachTo;
+ a_AttachTo->m_Attachee = this;
+ m_World->BroadcastAttachEntity(*this, a_AttachTo);
+}
+
+
+
+
+
+void cEntity::Detach(void)
+{
+ if (m_AttachedTo == NULL)
+ {
+ // Attached to no entity, our work is done
+ return;
+ }
+ m_AttachedTo->m_Attachee = NULL;
+ m_AttachedTo = NULL;
+ m_World->BroadcastAttachEntity(*this, NULL);
+}
+
+
+
+
+
+bool cEntity::IsA(const char * a_ClassName) const
+{
+ return (strcmp(a_ClassName, "cEntity") == 0);
+}
+
+
+
+
+
+void cEntity::SetRot(const Vector3f & a_Rot)
+{
+ m_Rot = a_Rot;
+ m_bDirtyOrientation = true;
+}
+
+
+
+
+
+void cEntity::SetHeadYaw(double a_HeadYaw)
+{
+ m_HeadYaw = a_HeadYaw;
+ m_bDirtyHead = true;
+ WrapHeadYaw();
+}
+
+
+
+
+
+void cEntity::SetHeight(double a_Height)
+{
+ m_Height = a_Height;
+}
+
+
+
+
+
+void cEntity::SetMass(double a_Mass)
+{
+ if (a_Mass > 0)
+ {
+ m_Mass = a_Mass;
+ }
+ else
+ {
+ // Make sure that mass is not zero. 1g is the default because we
+ // have to choose a number. It's perfectly legal to have a mass
+ // less than 1g as long as is NOT equal or less than zero.
+ m_Mass = 0.001;
+ }
+}
+
+
+
+
+
+void cEntity::SetYaw(double a_Yaw)
+{
+ m_Rot.x = a_Yaw;
+ m_bDirtyOrientation = true;
+ WrapRotation();
+}
+
+
+
+
+
+void cEntity::SetPitch(double a_Pitch)
+{
+ m_Rot.y = a_Pitch;
+ m_bDirtyOrientation = true;
+ WrapRotation();
+}
+
+
+
+
+
+void cEntity::SetRoll(double a_Roll)
+{
+ m_Rot.z = a_Roll;
+ m_bDirtyOrientation = true;
+}
+
+
+
+
+
+void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
+{
+ m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ);
+ m_bDirtySpeed = true;
+ WrapSpeed();
+}
+
+
+
+
+void cEntity::SetSpeedX(double a_SpeedX)
+{
+ m_Speed.x = a_SpeedX;
+ m_bDirtySpeed = true;
+ WrapSpeed();
+}
+
+
+
+
+void cEntity::SetSpeedY(double a_SpeedY)
+{
+ m_Speed.y = a_SpeedY;
+ m_bDirtySpeed = true;
+ WrapSpeed();
+}
+
+
+
+
+void cEntity::SetSpeedZ(double a_SpeedZ)
+{
+ m_Speed.z = a_SpeedZ;
+ m_bDirtySpeed = true;
+ WrapSpeed();
+}
+
+
+
+
+
+void cEntity::SetWidth(double a_Width)
+{
+ m_Width = a_Width;
+}
+
+
+
+
+
+void cEntity::AddPosX(double a_AddPosX)
+{
+ m_Pos.x += a_AddPosX;
+ m_bDirtyPosition = true;
+}
+
+
+
+
+void cEntity::AddPosY(double a_AddPosY)
+{
+ m_Pos.y += a_AddPosY;
+ m_bDirtyPosition = true;
+}
+
+
+
+
+void cEntity::AddPosZ(double a_AddPosZ)
+{
+ m_Pos.z += a_AddPosZ;
+ m_bDirtyPosition = true;
+}
+
+
+
+
+void cEntity::AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ)
+{
+ m_Pos.x += a_AddPosX;
+ m_Pos.y += a_AddPosY;
+ m_Pos.z += a_AddPosZ;
+ m_bDirtyPosition = true;
+}
+
+
+
+
+void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ)
+{
+ m_Speed.x += a_AddSpeedX;
+ m_Speed.y += a_AddSpeedY;
+ m_Speed.z += a_AddSpeedZ;
+ m_bDirtySpeed = true;
+ WrapSpeed();
+}
+
+
+
+
+
+void cEntity::AddSpeedX(double a_AddSpeedX)
+{
+ m_Speed.x += a_AddSpeedX;
+ m_bDirtySpeed = true;
+ WrapSpeed();
+}
+
+
+
+
+
+void cEntity::AddSpeedY(double a_AddSpeedY)
+{
+ m_Speed.y += a_AddSpeedY;
+ m_bDirtySpeed = true;
+ WrapSpeed();
+}
+
+
+
+
+
+void cEntity::AddSpeedZ(double a_AddSpeedZ)
+{
+ m_Speed.z += a_AddSpeedZ;
+ m_bDirtySpeed = true;
+ WrapSpeed();
+}
+
+
+
+
+
+void cEntity::SteerVehicle(float a_Forward, float a_Sideways)
+{
+ if (m_AttachedTo == NULL)
+ {
+ return;
+ }
+ if ((a_Forward != 0) || (a_Sideways != 0))
+ {
+ Vector3d LookVector = GetLookVector();
+ double AddSpeedX = LookVector.x * a_Forward + LookVector.z * a_Sideways;
+ double AddSpeedZ = LookVector.z * a_Forward - LookVector.x * a_Sideways;
+ m_AttachedTo->AddSpeed(AddSpeedX, 0, AddSpeedZ);
+ }
+}
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// Get look vector (this is NOT a rotation!)
+Vector3d cEntity::GetLookVector(void) const
+{
+ Matrix4d m;
+ m.Init(Vector3f(), 0, m_Rot.x, -m_Rot.y);
+ Vector3d Look = m.Transform(Vector3d(0, 0, 1));
+ return Look;
+}
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// Set position
+void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ)
+{
+ m_Pos.Set(a_PosX, a_PosY, a_PosZ);
+ m_bDirtyPosition = true;
+}
+
+
+
+
+
+void cEntity::SetPosX(double a_PosX)
+{
+ m_Pos.x = a_PosX;
+ m_bDirtyPosition = true;
+}
+
+
+
+
+
+void cEntity::SetPosY(double a_PosY)
+{
+ m_Pos.y = a_PosY;
+ m_bDirtyPosition = true;
+}
+
+
+
+
+
+void cEntity::SetPosZ(double a_PosZ)
+{
+ m_Pos.z = a_PosZ;
+ m_bDirtyPosition = true;
+}
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// Reference stuffs
+void cEntity::AddReference(cEntity * & a_EntityPtr)
+{
+ m_References->AddReference(a_EntityPtr);
+ a_EntityPtr->ReferencedBy(a_EntityPtr);
+}
+
+
+
+
+
+void cEntity::ReferencedBy(cEntity * & a_EntityPtr)
+{
+ m_Referencers->AddReference(a_EntityPtr);
+}
+
+
+
+
+
+void cEntity::Dereference(cEntity * & a_EntityPtr)
+{
+ m_Referencers->Dereference(a_EntityPtr);
+}
+
+
+
+
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
new file mode 100644
index 000000000..dafda7826
--- /dev/null
+++ b/src/Entities/Entity.h
@@ -0,0 +1,445 @@
+
+#pragma once
+
+#include "../Item.h"
+#include "../Vector3d.h"
+#include "../Vector3f.h"
+
+
+
+
+
+// Place this macro in the public section of each cEntity descendant class and you're done :)
+#define CLASS_PROTODEF(classname) \
+ virtual bool IsA(const char * a_ClassName) const override\
+ { \
+ return ((strcmp(a_ClassName, #classname) == 0) || super::IsA(a_ClassName)); \
+ } \
+ virtual const char * GetClass(void) const override \
+ { \
+ return #classname; \
+ } \
+ static const char * GetClassStatic(void) \
+ { \
+ return #classname; \
+ } \
+ virtual const char * GetParentClass(void) const override \
+ { \
+ return super::GetClass(); \
+ }
+
+
+
+
+
+class cWorld;
+class cReferenceManager;
+class cClientHandle;
+class cPlayer;
+class cChunk;
+
+
+
+
+
+// tolua_begin
+struct TakeDamageInfo
+{
+ eDamageType DamageType; // Where does the damage come from? Being hit / on fire / contact with cactus / ...
+ cEntity * Attacker; // The attacking entity; valid only for dtAttack
+ int RawDamage; // What damage would the receiver get without any armor. Usually: attacker mob type + weapons
+ int FinalDamage; // What actual damage will be received. Usually: m_RawDamage minus armor
+ Vector3d Knockback; // The amount and direction of knockback received from the damage
+ // TODO: Effects - list of effects that the hit is causing. Unknown representation yet
+} ;
+// tolua_end
+
+
+
+
+
+// tolua_begin
+class cEntity
+{
+public:
+
+ enum eEntityType
+ {
+ etEntity, // For all other types
+ etPlayer,
+ etPickup,
+ etMonster,
+ etFallingBlock,
+ etMinecart,
+ etBoat,
+ etTNT,
+ etProjectile,
+
+ // Common variations
+ etMob = etMonster, // DEPRECATED, use etMonster instead!
+ } ;
+
+ // tolua_end
+
+ enum
+ {
+ ENTITY_STATUS_HURT = 2,
+ ENTITY_STATUS_DEAD = 3,
+ ENTITY_STATUS_WOLF_TAMING = 6,
+ ENTITY_STATUS_WOLF_TAMED = 7,
+ ENTITY_STATUS_WOLF_SHAKING = 8,
+ ENTITY_STATUS_EATING_ACCEPTED = 9,
+ ENTITY_STATUS_SHEEP_EATING = 10,
+ ENTITY_STATUS_GOLEM_ROSING = 11,
+ ENTITY_STATUS_VILLAGER_HEARTS = 12,
+ ENTITY_STATUS_VILLAGER_ANGRY = 13,
+ ENTITY_STATUS_VILLAGER_HAPPY = 14,
+ ENTITY_STATUS_WITCH_MAGICKING = 15,
+ // It seems 16 (zombie conversion) is now done with metadata
+ ENTITY_STATUS_FIREWORK_EXPLODE= 17,
+ } ;
+
+ enum
+ {
+ FIRE_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in fire
+ FIRE_DAMAGE = 1, ///< How much damage to deal when standing in fire
+ LAVA_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in lava
+ LAVA_DAMAGE = 5, ///< How much damage to deal when standing in lava
+ BURN_TICKS_PER_DAMAGE = 20, ///< How many ticks to wait between damaging an entity when it is burning
+ BURN_DAMAGE = 1, ///< How much damage to deal when the entity is burning
+ BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire
+ } ;
+
+ cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
+ virtual ~cEntity();
+
+ /// Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed)
+ virtual bool Initialize(cWorld * a_World);
+
+ // tolua_begin
+
+ eEntityType GetEntityType(void) const { return m_EntityType; }
+
+ bool IsPlayer (void) const { return (m_EntityType == etPlayer); }
+ bool IsPickup (void) const { return (m_EntityType == etPickup); }
+ bool IsMob (void) const { return (m_EntityType == etMonster); }
+ bool IsFallingBlock(void) const { return (m_EntityType == etFallingBlock); }
+ bool IsMinecart (void) const { return (m_EntityType == etMinecart); }
+ bool IsBoat (void) const { return (m_EntityType == etBoat); }
+ bool IsTNT (void) const { return (m_EntityType == etTNT); }
+ bool IsProjectile (void) const { return (m_EntityType == etProjectile); }
+
+ /// Returns true if the entity is of the specified class or a subclass (cPawn's IsA("cEntity") returns true)
+ virtual bool IsA(const char * a_ClassName) const;
+
+ /// Returns the topmost class name for the object
+ virtual const char * GetClass(void) const;
+
+ // Returns the class name of this class
+ static const char * GetClassStatic(void);
+
+ /// Returns the topmost class's parent class name for the object. cEntity returns an empty string (no parent).
+ virtual const char * GetParentClass(void) const;
+
+ cWorld * GetWorld(void) const { return m_World; }
+
+ double GetHeadYaw (void) const { return m_HeadYaw; }
+ double GetHeight (void) const { return m_Height; }
+ double GetMass (void) const { return m_Mass; }
+ const Vector3d & GetPosition (void) const { return m_Pos; }
+ double GetPosX (void) const { return m_Pos.x; }
+ double GetPosY (void) const { return m_Pos.y; }
+ double GetPosZ (void) const { return m_Pos.z; }
+ const Vector3d & GetRot (void) const { return m_Rot; }
+ double GetRotation (void) const { return m_Rot.x; } // OBSOLETE, use GetYaw() instead
+ double GetYaw (void) const { return m_Rot.x; }
+ double GetPitch (void) const { return m_Rot.y; }
+ double GetRoll (void) const { return m_Rot.z; }
+ Vector3d GetLookVector(void) const;
+ const Vector3d & GetSpeed (void) const { return m_Speed; }
+ double GetSpeedX (void) const { return m_Speed.x; }
+ double GetSpeedY (void) const { return m_Speed.y; }
+ double GetSpeedZ (void) const { return m_Speed.z; }
+ double GetWidth (void) const { return m_Width; }
+
+ int GetChunkX(void) const {return (int)floor(m_Pos.x / cChunkDef::Width); }
+ int GetChunkZ(void) const {return (int)floor(m_Pos.z / cChunkDef::Width); }
+
+ void SetHeadYaw (double a_HeadYaw);
+ void SetHeight (double a_Height);
+ void SetMass (double a_Mass);
+ void SetPosX (double a_PosX);
+ void SetPosY (double a_PosY);
+ void SetPosZ (double a_PosZ);
+ void SetPosition(double a_PosX, double a_PosY, double a_PosZ);
+ void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); }
+ void SetRot (const Vector3f & a_Rot);
+ void SetRotation(double a_Rotation) { SetYaw(a_Rotation); } // OBSOLETE, use SetYaw() instead
+ void SetYaw (double a_Yaw);
+ void SetPitch (double a_Pitch);
+ void SetRoll (double a_Roll);
+ void SetSpeed (double a_SpeedX, double a_SpeedY, double a_SpeedZ);
+ void SetSpeed (const Vector3d & a_Speed) { SetSpeed(a_Speed.x, a_Speed.y, a_Speed.z); }
+ void SetSpeedX (double a_SpeedX);
+ void SetSpeedY (double a_SpeedY);
+ void SetSpeedZ (double a_SpeedZ);
+ void SetWidth (double a_Width);
+
+ void AddPosX (double a_AddPosX);
+ void AddPosY (double a_AddPosY);
+ void AddPosZ (double a_AddPosZ);
+ void AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ);
+ void AddPosition(const Vector3d & a_AddPos) { AddPosition(a_AddPos.x,a_AddPos.y,a_AddPos.z);}
+ void AddSpeed (double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ);
+ void AddSpeed (const Vector3d & a_AddSpeed) { AddSpeed(a_AddSpeed.x,a_AddSpeed.y,a_AddSpeed.z);}
+ void AddSpeedX (double a_AddSpeedX);
+ void AddSpeedY (double a_AddSpeedY);
+ void AddSpeedZ (double a_AddSpeedZ);
+
+ void SteerVehicle(float a_Forward, float a_Sideways);
+
+ inline int GetUniqueID(void) const { return m_UniqueID; }
+ inline bool IsDestroyed(void) const { return !m_IsInitialized; }
+
+ /// Schedules the entity for destroying; if a_ShouldBroadcast is set to true, broadcasts the DestroyEntity packet
+ void Destroy(bool a_ShouldBroadcast = true);
+
+ /// Makes this pawn take damage from an attack by a_Attacker. Damage values are calculated automatically and DoTakeDamage() called
+ void TakeDamage(cEntity & a_Attacker);
+
+ /// Makes this entity take the specified damage. The final damage is calculated using current armor, then DoTakeDamage() called
+ void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount);
+
+ /// Makes this entity take the specified damage. The values are packed into a TDI, knockback calculated, then sent through DoTakeDamage()
+ void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount);
+
+ float GetGravity(void) const { return m_Gravity; }
+
+ void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; }
+
+ /// Sets the rotation to match the speed vector (entity goes "face-forward")
+ void SetRotationFromSpeed(void);
+
+ /// Sets the pitch to match the speed vector (entity gies "face-forward")
+ void SetPitchFromSpeed(void);
+
+ // tolua_end
+
+ /// Makes this entity take damage specified in the a_TDI. The TDI is sent through plugins first, then applied
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI);
+
+ // tolua_begin
+
+ /// Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items
+ virtual int GetRawDamageAgainst(const cEntity & a_Receiver);
+
+ /// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
+ virtual int GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_RawDamage);
+
+ /// Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit
+ virtual double GetKnockbackAmountAgainst(const cEntity & a_Receiver);
+
+ /// Returns the curently equipped weapon; empty item if none
+ virtual cItem GetEquippedWeapon(void) const { return cItem(); }
+
+ /// Returns the currently equipped helmet; empty item if none
+ virtual cItem GetEquippedHelmet(void) const { return cItem(); }
+
+ /// Returns the currently equipped chestplate; empty item if none
+ virtual cItem GetEquippedChestplate(void) const { return cItem(); }
+
+ /// Returns the currently equipped leggings; empty item if none
+ virtual cItem GetEquippedLeggings(void) const { return cItem(); }
+
+ /// Returns the currently equipped boots; empty item if none
+ virtual cItem GetEquippedBoots(void) const { return cItem(); }
+
+ /// Called when the health drops below zero. a_Killer may be NULL (environmental damage)
+ virtual void KilledBy(cEntity * a_Killer);
+
+ /// Heals the specified amount of HPs
+ void Heal(int a_HitPoints);
+
+ /// Returns the health of this entity
+ int GetHealth(void) const { return m_Health; }
+
+ /// Sets the health of this entity; doesn't broadcast any hurt animation
+ void SetHealth(int a_Health);
+
+ // tolua_end
+
+ virtual void Tick(float 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);
+
+ /// Updates the state related to this entity being on fire
+ virtual void TickBurning(cChunk & a_Chunk);
+
+ /// Handles when the entity is in the void
+ virtual void TickInVoid(cChunk & a_Chunk);
+
+ /// Called when the entity starts burning
+ virtual void OnStartedBurning(void);
+
+ /// Called when the entity finishes burning
+ virtual void OnFinishedBurning(void);
+
+ // tolua_begin
+
+ /// Sets the maximum value for the health
+ void SetMaxHealth(int a_MaxHealth);
+
+ int GetMaxHealth(void) const { return m_MaxHealth; }
+
+ /// Puts the entity on fire for the specified amount of ticks
+ void StartBurning(int a_TicksLeftBurning);
+
+ /// Stops the entity from burning, resets all burning timers
+ void StopBurning(void);
+
+ // tolua_end
+
+ /** Descendants override this function to send a command to the specified client to spawn the entity on the client.
+ To spawn on all eligible clients, use cChunkMap::BroadcastSpawnEntity()
+ */
+ virtual void SpawnOn(cClientHandle & a_Client) = 0;
+
+ // tolua_begin
+
+ /// Teleports to the entity specified
+ virtual void TeleportToEntity(cEntity & a_Entity);
+
+ /// Teleports to the coordinates specified
+ virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ);
+
+ // tolua_end
+
+ /// Updates clients of changes in the entity.
+ virtual void BroadcastMovementUpdate(const cClientHandle * a_Exclude = NULL);
+
+ /// Attaches to the specified entity; detaches from any previous one first
+ void AttachTo(cEntity * a_AttachTo);
+
+ /// Detaches from the currently attached entity, if any
+ void Detach(void);
+
+ /// Makes sure head yaw is not over the specified range.
+ void WrapHeadYaw();
+
+ /// Makes sure rotation is not over the specified range.
+ void WrapRotation();
+
+ /// Makes speed is not over 20. Max speed is 20 blocks / second
+ void WrapSpeed();
+
+ // tolua_begin
+
+ // COMMON metadata flags; descendants may override the defaults:
+ virtual bool IsOnFire (void) const {return (m_TicksLeftBurning > 0); }
+ virtual bool IsCrouched (void) const {return false; }
+ virtual bool IsRiding (void) const {return false; }
+ virtual bool IsSprinting(void) const {return false; }
+ virtual bool IsRclking (void) const {return false; }
+ virtual bool IsInvisible(void) const {return false; }
+
+ // tolua_end
+
+ /// Called when the specified player right-clicks this entity
+ virtual void OnRightClicked(cPlayer & a_Player) {};
+
+ /// Returns the list of drops for this pawn when it is killed. May check a_Killer for special handling (sword of looting etc.). Called from KilledBy().
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) {}
+
+protected:
+ static cCriticalSection m_CSCount;
+ static int m_EntityCount;
+
+ int m_UniqueID;
+
+ int m_Health;
+ int m_MaxHealth;
+
+ /// The entity to which this entity is attached (vehicle), NULL if none
+ cEntity * m_AttachedTo;
+
+ /// The entity which is attached to this entity (rider), NULL if none
+ cEntity * m_Attachee;
+
+ cReferenceManager* m_Referencers;
+ cReferenceManager* m_References;
+
+ // Flags that signal that we haven't updated the clients with the latest.
+ bool m_bDirtyHead;
+ bool m_bDirtyOrientation;
+ bool m_bDirtyPosition;
+ bool m_bDirtySpeed;
+
+ bool m_bOnGround;
+ float m_Gravity;
+
+ // Last Position.
+ double m_LastPosX, m_LastPosY, m_LastPosZ;
+
+ // This variables keep track of the last time a packet was sent
+ Int64 m_TimeLastTeleportPacket,m_TimeLastMoveReltPacket,m_TimeLastSpeedPacket; // In ticks
+
+ bool m_IsInitialized; // Is set to true when it's initialized, until it's destroyed (Initialize() till Destroy() )
+
+ eEntityType m_EntityType;
+
+ cWorld * m_World;
+
+ /// Time, in ticks, since the last damage dealt by being on fire. Valid only if on fire (IsOnFire())
+ int m_TicksSinceLastBurnDamage;
+
+ /// Time, in ticks, since the last damage dealt by standing in lava. Reset to zero when moving out of lava.
+ int m_TicksSinceLastLavaDamage;
+
+ /// Time, in ticks, since the last damage dealt by standing in fire. Reset to zero when moving out of fire.
+ int m_TicksSinceLastFireDamage;
+
+ /// Time, in ticks, until the entity extinguishes its fire
+ int m_TicksLeftBurning;
+
+ /// Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void.
+ int m_TicksSinceLastVoidDamage;
+
+ virtual void Destroyed(void) {} // Called after the entity has been destroyed
+
+ void SetWorld(cWorld * a_World) { m_World = a_World; }
+
+ friend class cReferenceManager;
+ void AddReference( cEntity*& a_EntityPtr );
+ void ReferencedBy( cEntity*& a_EntityPtr );
+ void Dereference( cEntity*& a_EntityPtr );
+
+private:
+ // Measured in degrees (MAX 360°)
+ double m_HeadYaw;
+ // Measured in meter/second (m/s)
+ Vector3d m_Speed;
+ // Measured in degrees (MAX 360°)
+ Vector3d m_Rot;
+
+ /// Position of the entity's XZ center and Y bottom
+ Vector3d m_Pos;
+
+ // Measured in meter / second
+ Vector3d m_WaterSpeed;
+
+ // Measured in Kilograms (Kg)
+ double m_Mass;
+
+ /// Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter.
+ double m_Width;
+
+ /// Height of the entity (Y axis)
+ double m_Height;
+} ; // tolua_export
+
+typedef std::list<cEntity *> cEntityList;
+
+
+
+
diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp
new file mode 100644
index 000000000..9fcd9ac80
--- /dev/null
+++ b/src/Entities/FallingBlock.cpp
@@ -0,0 +1,93 @@
+#include "Globals.h"
+
+#include "FallingBlock.h"
+#include "../World.h"
+#include "../ClientHandle.h"
+#include "../Simulator/SandSimulator.h"
+#include "../Chunk.h"
+
+
+
+
+
+cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
+ super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f, 0.98, 0.98),
+ m_BlockType(a_BlockType),
+ m_BlockMeta(a_BlockMeta),
+ m_OriginalPosition(a_BlockPosition)
+{
+}
+
+
+
+
+
+void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ a_ClientHandle.SendSpawnFallingBlock(*this);
+}
+
+
+
+
+
+void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ float MilliDt = a_Dt * 0.001f;
+ AddSpeedY(MilliDt * -9.8f);
+ AddPosY(GetSpeedY() * MilliDt);
+
+ // GetWorld()->BroadcastTeleportEntity(*this); // Test position
+
+ int BlockX = m_OriginalPosition.x;
+ int BlockY = (int)(GetPosY() - 0.5);
+ int BlockZ = m_OriginalPosition.z;
+
+ if (BlockY < 0)
+ {
+ // Fallen out of this world, just continue falling until out of sight, then destroy:
+ if (BlockY < 100)
+ {
+ Destroy(true);
+ }
+ return;
+ }
+
+ if (BlockY >= cChunkDef::Height)
+ {
+ // Above the world, just wait for it to fall back down
+ return;
+ }
+
+ int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
+ BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx);
+ NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx);
+ if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta))
+ {
+ // Fallen onto a block that breaks this into pickups (e. g. half-slab)
+ // Must finish the fall with coords one below the block:
+ cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta);
+ Destroy(true);
+ return;
+ }
+ else if (!cSandSimulator::CanContinueFallThrough(BlockBelow))
+ {
+ // Fallen onto a solid block
+ /*
+ LOGD(
+ "Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.",
+ BlockX, BlockY, BlockZ,
+ BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width,
+ ItemTypeToString(BlockBelow).c_str()
+ );
+ */
+
+ cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta);
+ Destroy(true);
+ return;
+ }
+}
+
+
+
+
diff --git a/src/Entities/FallingBlock.h b/src/Entities/FallingBlock.h
new file mode 100644
index 000000000..5ba9909bb
--- /dev/null
+++ b/src/Entities/FallingBlock.h
@@ -0,0 +1,43 @@
+
+#pragma once
+
+#include "Entity.h"
+
+
+
+
+class cPlayer;
+class cItem;
+
+
+
+
+
+
+class cFallingBlock :
+ public cEntity
+{
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(cFallingBlock);
+
+ /// Creates a new falling block. a_BlockPosition is expected in world coords
+ cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ BLOCKTYPE GetBlockType(void) const { return m_BlockType; }
+ NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; }
+
+ // cEntity overrides:
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+private:
+ BLOCKTYPE m_BlockType;
+ NIBBLETYPE m_BlockMeta;
+ Vector3i m_OriginalPosition; // Position where the falling block has started, in world coords
+} ;
+
+
+
+
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
new file mode 100644
index 000000000..f75e23d8b
--- /dev/null
+++ b/src/Entities/Minecart.cpp
@@ -0,0 +1,541 @@
+
+// Minecart.cpp
+
+// Implements the cMinecart class representing a minecart in the world
+// Indiana Jones!
+
+#include "Globals.h"
+#include "Minecart.h"
+#include "../World.h"
+#include "../ClientHandle.h"
+#include "../Chunk.h"
+#include "Player.h"
+
+
+
+
+
+cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
+ super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7),
+ m_Payload(a_Payload),
+ m_LastDamage(0)
+{
+ SetMass(20.f);
+ SetMaxHealth(6);
+ SetHealth(6);
+}
+
+
+
+
+void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ char SubType = 0;
+ switch (m_Payload)
+ {
+ case mpNone: SubType = 0; break;
+ case mpChest: SubType = 1; break;
+ case mpFurnace: SubType = 2; break;
+ case mpTNT: SubType = 3; break;
+ case mpHopper: SubType = 5; break;
+ default:
+ {
+ ASSERT(!"Unknown payload, cannot spawn on client");
+ return;
+ }
+ }
+ a_ClientHandle.SendSpawnVehicle(*this, 10, SubType); // 10 = Minecarts, SubType = What type of Minecart
+}
+
+
+
+
+
+void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
+{
+ int PosY = (int)floor(GetPosY());
+ if ((PosY <= 0) || (PosY >= cChunkDef::Height))
+ {
+ // Outside the world, just process normal falling physics
+ super::HandlePhysics(a_Dt, a_Chunk);
+ BroadcastMovementUpdate();
+ return;
+ }
+
+ int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
+ if (Chunk == NULL)
+ {
+ // Inside an unloaded chunk, bail out all processing
+ return;
+ }
+ BLOCKTYPE BelowType = Chunk->GetBlock(RelPosX, PosY - 1, RelPosZ);
+ BLOCKTYPE InsideType = Chunk->GetBlock(RelPosX, PosY, RelPosZ);
+
+ if (IsBlockRail(BelowType))
+ {
+ HandleRailPhysics(a_Dt, *Chunk);
+ }
+ else
+ {
+ if (IsBlockRail(InsideType))
+ {
+ SetPosY(PosY + 1);
+ HandleRailPhysics(a_Dt, *Chunk);
+ }
+ else
+ {
+ super::HandlePhysics(a_Dt, *Chunk);
+ BroadcastMovementUpdate();
+ }
+ }
+}
+
+
+
+
+
+static const double MAX_SPEED = 8;
+static const double MAX_SPEED_NEGATIVE = (0 - MAX_SPEED);
+
+void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk)
+{
+
+ super::HandlePhysics(a_Dt, a_Chunk); // Main physics handling
+
+ /*
+ NOTE: Please bear in mind that taking away from negatives make them even more negative,
+ adding to negatives make them positive, etc.
+ */
+
+ // Get block meta below the cart
+ int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ);
+ double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed
+
+ switch (BelowMeta)
+ {
+ case E_META_RAIL_ZM_ZP: // NORTHSOUTH
+ {
+ SetRotation(270);
+ SpeedY = 0; // Don't move vertically as on ground
+ SpeedX = 0; // Correct diagonal movement from curved rails
+
+ if (SpeedZ != 0) // Don't do anything if cart is stationary
+ {
+ if (SpeedZ > 0)
+ {
+ // Going SOUTH, slow down
+ SpeedZ = SpeedZ - 0.1;
+ }
+ else
+ {
+ // Going NORTH, slow down
+ SpeedZ = SpeedZ + 0.1;
+ }
+ }
+ break;
+ }
+
+ case E_META_RAIL_XM_XP: // EASTWEST
+ {
+ SetRotation(180);
+ SpeedY = 0;
+ SpeedZ = 0;
+
+ if (SpeedX != 0)
+ {
+ if (SpeedX > 0)
+ {
+ SpeedX = SpeedX - 0.1;
+ }
+ else
+ {
+ SpeedX = SpeedX + 0.1;
+ }
+ }
+ break;
+ }
+
+ case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH
+ {
+ SetRotation(270);
+ SetPosY(floor(GetPosY()) + 0.2); // It seems it doesn't work without levitation :/
+ SpeedX = 0;
+
+ if (SpeedZ >= 0)
+ {
+ // SpeedZ POSITIVE, going SOUTH
+ if (SpeedZ <= MAX_SPEED) // Speed limit
+ {
+ SpeedZ = SpeedZ + 0.5; // Speed up
+ SpeedY = (0 - SpeedZ); // Downward movement is negative (0 minus positive numbers is negative)
+ }
+ else
+ {
+ SpeedZ = MAX_SPEED; // Enforce speed limit
+ SpeedY = (0 - SpeedZ);
+ }
+ }
+ else
+ {
+ // SpeedZ NEGATIVE, going NORTH
+ SpeedZ = SpeedZ + 0.4; // Slow down
+ SpeedY = (0 - SpeedZ); // Upward movement is positive (0 minus negative number is positive number)
+ }
+ break;
+ }
+
+ case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH
+ {
+ SetRotation(270);
+ SetPosY(floor(GetPosY()) + 0.2);
+ SpeedX = 0;
+
+ if (SpeedZ > 0)
+ {
+ // SpeedZ POSITIVE, going SOUTH
+ SpeedZ = SpeedZ - 0.4; // Slow down
+ SpeedY = SpeedZ; // Upward movement positive
+ }
+ else
+ {
+ if (SpeedZ >= MAX_SPEED_NEGATIVE) // Speed limit
+ {
+ // SpeedZ NEGATIVE, going NORTH
+ SpeedZ = SpeedZ - 0.5; // Speed up
+ SpeedY = SpeedZ; // Downward movement negative
+ }
+ else
+ {
+ SpeedZ = MAX_SPEED_NEGATIVE; // Enforce speed limit
+ SpeedY = SpeedZ;
+ }
+ }
+ break;
+ }
+
+ case E_META_RAIL_ASCEND_XM: // ASCEND EAST
+ {
+ SetRotation(180);
+ SetPosY(floor(GetPosY()) + 0.2);
+ SpeedZ = 0;
+
+ if (SpeedX >= 0)
+ {
+ if (SpeedX <= MAX_SPEED)
+ {
+ SpeedX = SpeedX + 0.5;
+ SpeedY = (0 - SpeedX);
+ }
+ else
+ {
+ SpeedX = MAX_SPEED;
+ SpeedY = (0 - SpeedX);
+ }
+ }
+ else
+ {
+ SpeedX = SpeedX + 0.4;
+ SpeedY = (0 - SpeedX);
+ }
+ break;
+ }
+
+ case E_META_RAIL_ASCEND_XP: // ASCEND WEST
+ {
+ SetRotation(180);
+ SetPosY(floor(GetPosY()) + 0.2);
+ SpeedZ = 0;
+
+ if (SpeedX > 0)
+ {
+ SpeedX = SpeedX - 0.4;
+ SpeedY = SpeedX;
+ }
+ else
+ {
+ if (SpeedX >= MAX_SPEED_NEGATIVE)
+ {
+ SpeedX = SpeedX - 0.5;
+ SpeedY = SpeedX;
+ }
+ else
+ {
+ SpeedX = MAX_SPEED_NEGATIVE;
+ SpeedY = SpeedX;
+ }
+ }
+ break;
+ }
+
+ case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST
+ {
+ SetRotation(315); // Set correct rotation server side
+ SetPosY(floor(GetPosY()) + 0.2); // Levitate dat cart
+
+ if (SpeedZ > 0) // Cart moving south
+ {
+ SpeedX = (0 - SpeedZ); // Diagonally move southwest (which will make cart hit a southwest rail)
+ }
+ else if (SpeedX > 0) // Cart moving east
+ {
+ SpeedZ = (0 - SpeedX); // Diagonally move northeast
+ }
+ break;
+ }
+
+ case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST
+ {
+ SetRotation(225);
+ SetPosY(floor(GetPosY()) + 0.2);
+
+ if (SpeedZ > 0)
+ {
+ SpeedX = SpeedZ;
+ }
+ else if (SpeedX < 0)
+ {
+ SpeedZ = SpeedX;
+ }
+ break;
+ }
+
+ case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST
+ {
+ SetRotation(135);
+ SetPosY(floor(GetPosY()) + 0.2);
+
+ if (SpeedZ < 0)
+ {
+ SpeedX = SpeedZ;
+ }
+ else if (SpeedX > 0)
+ {
+ SpeedZ = SpeedX;
+ }
+ break;
+ }
+
+ case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST
+ {
+ SetRotation(45);
+ SetPosY(floor(GetPosY()) + 0.2);
+
+ if (SpeedZ < 0)
+ {
+ SpeedX = (0 - SpeedZ);
+ }
+ else if (SpeedX < 0)
+ {
+ SpeedZ = (0 - SpeedX);
+ }
+ break;
+ }
+
+ default:
+ {
+ ASSERT(!"Unhandled rail meta!"); // Dun dun DUN!
+ break;
+ }
+ }
+
+ // Set speed to speed variables
+ SetSpeedX(SpeedX);
+ SetSpeedY(SpeedY);
+ SetSpeedZ(SpeedZ);
+
+
+ // Broadcast position to client
+ BroadcastMovementUpdate();
+}
+
+
+
+
+
+void cMinecart::DoTakeDamage(TakeDamageInfo & TDI)
+{
+ m_LastDamage = TDI.FinalDamage;
+ super::DoTakeDamage(TDI);
+
+ m_World->BroadcastEntityMetadata(*this);
+
+ if (GetHealth() <= 0)
+ {
+ Destroy(true);
+
+ cItems Drops;
+ switch (m_Payload)
+ {
+ case mpNone:
+ {
+ Drops.push_back(cItem(E_ITEM_MINECART, 1, 0));
+ break;
+ }
+ case mpChest:
+ {
+ Drops.push_back(cItem(E_ITEM_CHEST_MINECART, 1, 0));
+ break;
+ }
+ case mpFurnace:
+ {
+ Drops.push_back(cItem(E_ITEM_FURNACE_MINECART, 1, 0));
+ break;
+ }
+ case mpTNT:
+ {
+ Drops.push_back(cItem(E_ITEM_MINECART_WITH_TNT, 1, 0));
+ break;
+ }
+ case mpHopper:
+ {
+ Drops.push_back(cItem(E_ITEM_MINECART_WITH_HOPPER, 1, 0));
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled minecart type when spawning pickup!");
+ return;
+ }
+ }
+
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ());
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cEmptyMinecart:
+
+cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) :
+ super(mpNone, a_X, a_Y, a_Z)
+{
+}
+
+
+
+
+
+void cEmptyMinecart::OnRightClicked(cPlayer & a_Player)
+{
+ if (m_Attachee != NULL)
+ {
+ if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ {
+ // This player is already sitting in, they want out.
+ a_Player.Detach();
+ return;
+ }
+
+ if (m_Attachee->IsPlayer())
+ {
+ // Another player is already sitting in here, cannot attach
+ return;
+ }
+
+ // Detach whatever is sitting in this minecart now:
+ m_Attachee->Detach();
+ }
+
+ // Attach the player to this minecart
+ a_Player.AttachTo(this);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMinecartWithChest:
+
+cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) :
+ super(mpChest, a_X, a_Y, a_Z)
+{
+}
+
+
+
+
+
+void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item)
+{
+ ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items)));
+
+ m_Items[a_Idx] = a_Item;
+}
+
+
+
+
+
+void cMinecartWithChest::OnRightClicked(cPlayer & a_Player)
+{
+ // Show the chest UI window to the player
+ // TODO
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMinecartWithFurnace:
+
+cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) :
+ super(mpFurnace, a_X, a_Y, a_Z),
+ m_IsFueled(false)
+{
+}
+
+
+
+
+
+void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player)
+{
+ if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_COAL)
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+
+ m_IsFueled = true;
+ m_World->BroadcastEntityMetadata(*this);
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMinecartWithTNT:
+
+cMinecartWithTNT::cMinecartWithTNT(double a_X, double a_Y, double a_Z) :
+ super(mpTNT, a_X, a_Y, a_Z)
+{
+}
+
+// TODO: Make it activate when passing over activator rail
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMinecartWithHopper:
+
+cMinecartWithHopper::cMinecartWithHopper(double a_X, double a_Y, double a_Z) :
+ super(mpHopper, a_X, a_Y, a_Z)
+{
+}
+
+// TODO: Make it suck up blocks and travel further than any other cart and physics and put and take blocks
+// AND AVARYTHING!! \ No newline at end of file
diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h
new file mode 100644
index 000000000..b1b48be4e
--- /dev/null
+++ b/src/Entities/Minecart.h
@@ -0,0 +1,169 @@
+
+// Minecart.h
+
+// Declares the cMinecart class representing a minecart in the world
+
+
+
+
+
+#pragma once
+
+#include "Entity.h"
+
+
+
+
+
+inline bool IsBlockRail(BLOCKTYPE a_BlockType)
+ {
+ return (
+ (a_BlockType == E_BLOCK_RAIL) ||
+ (a_BlockType == E_BLOCK_ACTIVATOR_RAIL) ||
+ (a_BlockType == E_BLOCK_DETECTOR_RAIL) ||
+ (a_BlockType == E_BLOCK_POWERED_RAIL)
+ ) ;
+ }
+
+
+
+
+
+class cMinecart :
+ public cEntity
+{
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(cMinecart);
+
+ enum ePayload
+ {
+ mpNone, // Empty minecart, ridable by player or mobs
+ mpChest, // Minecart-with-chest, can store a grid of 3*8 items
+ mpFurnace, // Minecart-with-furnace, can be powered
+ mpTNT, // Minecart-with-TNT, can be blown up with activator rail
+ mpHopper, // Minecart-with-hopper, can be hopper
+ // TODO: Spawner minecarts, (and possibly any block in a minecart with NBT editing)
+ } ;
+
+ // cEntity overrides:
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
+ virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
+
+ int LastDamage(void) const { return m_LastDamage; }
+ void HandleRailPhysics(float a_Dt, cChunk & a_Chunk);
+ ePayload GetPayload(void) const { return m_Payload; }
+
+protected:
+ ePayload m_Payload;
+
+ cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z);
+
+ int m_LastDamage;
+
+} ;
+
+
+
+
+
+class cEmptyMinecart :
+ public cMinecart
+{
+ typedef cMinecart super;
+
+public:
+ CLASS_PROTODEF(cEmptyMinecart);
+
+ cEmptyMinecart(double a_X, double a_Y, double a_Z);
+
+ // cEntity overrides:
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+} ;
+
+
+
+
+
+class cMinecartWithChest :
+ public cMinecart
+{
+ typedef cMinecart super;
+
+public:
+ CLASS_PROTODEF(cMinecartWithChest);
+
+ /// Number of item slots in the chest
+ static const int NumSlots = 9 * 3;
+
+ cMinecartWithChest(double a_X, double a_Y, double a_Z);
+
+ const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; }
+ cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; }
+
+ void SetSlot(int a_Idx, const cItem & a_Item);
+
+protected:
+
+ /// The chest contents:
+ cItem m_Items[NumSlots];
+
+ // cEntity overrides:
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+} ;
+
+
+
+
+
+class cMinecartWithFurnace :
+ public cMinecart
+{
+ typedef cMinecart super;
+
+public:
+ CLASS_PROTODEF(cMinecartWithFurnace);
+
+ cMinecartWithFurnace(double a_X, double a_Y, double a_Z);
+
+ // cEntity overrides:
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ bool IsFueled (void) const { return m_IsFueled; }
+
+private:
+
+ bool m_IsFueled;
+
+} ;
+
+
+
+
+
+class cMinecartWithTNT :
+ public cMinecart
+{
+ typedef cMinecart super;
+
+public:
+ CLASS_PROTODEF(cMinecartWithTNT);
+
+ cMinecartWithTNT(double a_X, double a_Y, double a_Z);
+} ;
+
+
+
+
+
+class cMinecartWithHopper :
+ public cMinecart
+{
+ typedef cMinecart super;
+
+public:
+ CLASS_PROTODEF(cMinecartWithHopper);
+
+ cMinecartWithHopper(double a_X, double a_Y, double a_Z);
+} ; \ No newline at end of file
diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp
new file mode 100644
index 000000000..fffefd538
--- /dev/null
+++ b/src/Entities/Pawn.cpp
@@ -0,0 +1,19 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Pawn.h"
+
+
+
+
+
+cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height)
+ : cEntity(a_EntityType, 0, 0, 0, a_Width, a_Height)
+ , m_bBurnable(true)
+{
+}
+
+
+
+
+
diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h
new file mode 100644
index 000000000..e76337d86
--- /dev/null
+++ b/src/Entities/Pawn.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "Entity.h"
+
+
+
+
+
+// tolua_begin
+class cPawn :
+ public cEntity
+{
+ // tolua_end
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(cPawn);
+
+ cPawn(eEntityType a_EntityType, double a_Width, double a_Height);
+
+protected:
+ bool m_bBurnable;
+} ; // tolua_export
+
+
+
+
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
new file mode 100644
index 000000000..f8aae9703
--- /dev/null
+++ b/src/Entities/Pickup.cpp
@@ -0,0 +1,166 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#ifndef _WIN32
+#include <cstdlib>
+#endif
+
+#include "Pickup.h"
+#include "../ClientHandle.h"
+#include "../Inventory.h"
+#include "../World.h"
+#include "../Simulator/FluidSimulator.h"
+#include "../Server.h"
+#include "Player.h"
+#include "../PluginManager.h"
+#include "../Item.h"
+#include "../Root.h"
+#include "../Chunk.h"
+
+#include "../Vector3d.h"
+#include "../Vector3f.h"
+
+
+
+
+
+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_Item(a_Item)
+ , m_bCollected( false )
+ , m_bIsPlayerCreated( IsPlayerCreated )
+{
+ SetGravity(-10.5f);
+ SetMaxHealth(5);
+ SetHealth(5);
+ SetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ);
+}
+
+
+
+
+
+void cPickup::SpawnOn(cClientHandle & a_Client)
+{
+ a_Client.SendPickupSpawn(*this);
+}
+
+
+
+
+
+void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+ BroadcastMovementUpdate(); //Notify clients of position
+
+ m_Timer += a_Dt;
+
+ if (!m_bCollected)
+ {
+ int BlockY = (int) floor(GetPosY());
+ if ((BlockY >= 0) && (BlockY < cChunkDef::Height)) // Don't do anything except for falling when outside the world
+ {
+ int BlockX = (int) floor(GetPosX());
+ int BlockZ = (int) floor(GetPosZ());
+ // Position might have changed due to physics. So we have to make sure we have the correct chunk.
+ cChunk * CurrentChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
+ if (CurrentChunk != NULL) // Make sure the chunk is loaded
+ {
+ int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width);
+ int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width);
+
+ // If the pickup is on the bottommost block position, make it think the void is made of air: (#131)
+ BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
+ BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ);
+
+ if (
+ IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) ||
+ IsBlockLava(BlockIn) || (BlockIn == E_BLOCK_FIRE)
+ )
+ {
+ 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.
+ if (m_Timer > 500.f)
+ {
+ Destroy(true);
+ return;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (m_Timer > 500.f) // 0.5 second
+ {
+ Destroy(true);
+ return;
+ }
+ }
+
+ if (m_Timer > 1000 * 60 * 5) // 5 minutes
+ {
+ Destroy(true);
+ return;
+ }
+
+ if (GetPosY() < -8) // Out of this world and no more visible!
+ {
+ Destroy(true);
+ return;
+ }
+}
+
+
+
+
+
+bool cPickup::CollectedBy(cPlayer * a_Dest)
+{
+ ASSERT(a_Dest != NULL);
+
+ if (m_bCollected)
+ {
+ // LOG("Pickup %d cannot be collected by \"%s\", because it has already been collected.", m_UniqueID, a_Dest->GetName().c_str());
+ return false; // It's already collected!
+ }
+
+ // Two seconds if player created the pickup (vomiting), half a second if anything else
+ if (m_Timer < (m_bIsPlayerCreated ? 2000.f : 500.f))
+ {
+ // 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
+ }
+
+ if (cRoot::Get()->GetPluginManager()->CallHookCollectingPickup(a_Dest, *this))
+ {
+ // LOG("Pickup %d cannot be collected by \"%s\", because a plugin has said no.", m_UniqueID, a_Dest->GetName().c_str());
+ return false;
+ }
+
+ int NumAdded = a_Dest->GetInventory().AddItem(m_Item);
+ if (NumAdded > 0)
+ {
+ m_Item.m_ItemCount -= NumAdded;
+ m_World->BroadcastCollectPickup(*this, *a_Dest);
+ // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
+ m_World->BroadcastSoundEffect("random.pop",(int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+ if (m_Item.m_ItemCount == 0)
+ {
+ // All of the pickup has been collected, schedule the pickup for destroying
+ m_bCollected = true;
+ }
+ m_Timer = 0;
+ return true;
+ }
+
+ // LOG("Pickup %d cannot be collected by \"%s\", because there's no space in the inventory.", a_Dest->GetName().c_str(), m_UniqueID);
+ return false;
+}
+
+
+
+
diff --git a/src/Entities/Pickup.h b/src/Entities/Pickup.h
new file mode 100644
index 000000000..d39eda298
--- /dev/null
+++ b/src/Entities/Pickup.h
@@ -0,0 +1,64 @@
+
+#pragma once
+
+#include "Entity.h"
+#include "../Item.h"
+
+
+
+
+
+class cPlayer;
+
+
+
+
+
+// tolua_begin
+class cPickup :
+ public cEntity
+{
+ // tolua_end
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(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); // tolua_export
+
+ cItem & GetItem(void) {return m_Item; } // tolua_export
+ const cItem & GetItem(void) const {return m_Item; }
+
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+
+ bool CollectedBy(cPlayer * a_Dest); // tolua_export
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+ /// Returns the number of ticks that this entity has existed
+ int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export
+
+ /// Returns true if the pickup has already been collected
+ bool IsCollected(void) const { return m_bCollected; } // tolua_export
+
+ /// Returns true if created by player (i.e. vomiting), used for determining picking-up delay time
+ bool IsPlayerCreated(void) const { return m_bIsPlayerCreated; } // tolua_export
+
+private:
+ Vector3d m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;)
+
+ Vector3d m_WaterSpeed;
+
+ /// The number of ticks that the entity has existed / timer between collect and destroy; in msec
+ float m_Timer;
+
+ cItem m_Item;
+
+ bool m_bCollected;
+
+ bool m_bIsPlayerCreated;
+}; // tolua_export
+
+
+
+
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
new file mode 100644
index 000000000..f4ddd979c
--- /dev/null
+++ b/src/Entities/Player.cpp
@@ -0,0 +1,1765 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Player.h"
+#include "../Server.h"
+#include "../ClientHandle.h"
+#include "../UI/Window.h"
+#include "../UI/WindowOwner.h"
+#include "../World.h"
+#include "Pickup.h"
+#include "../PluginManager.h"
+#include "../BlockEntities/BlockEntity.h"
+#include "../GroupManager.h"
+#include "../Group.h"
+#include "../ChatColor.h"
+#include "../Item.h"
+#include "../Tracer.h"
+#include "../Root.h"
+#include "../OSSupport/Timer.h"
+#include "../MersenneTwister.h"
+#include "../Chunk.h"
+#include "../Items/ItemHandler.h"
+
+#include "../Vector3d.h"
+#include "../Vector3f.h"
+
+#include "lib/iniFile/iniFile.h"
+#include "lib/jsoncpp/include/json/json.h"
+
+#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x))
+
+
+
+
+
+
+cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
+ : super(etPlayer, 0.6, 1.8)
+ , m_GameMode(eGameMode_NotSet)
+ , m_IP("")
+ , m_LastBlockActionTime( 0 )
+ , m_LastBlockActionCnt( 0 )
+ , m_AirLevel( MAX_AIR_LEVEL )
+ , m_AirTickTimer( DROWNING_TICKS )
+ , m_bVisible( true )
+ , m_LastGroundHeight( 0 )
+ , m_bTouchGround( false )
+ , m_Stance( 0.0 )
+ , m_Inventory(*this)
+ , m_CurrentWindow(NULL)
+ , m_InventoryWindow(NULL)
+ , m_TimeLastPickupCheck( 0.f )
+ , m_Color('-')
+ , m_ClientHandle( a_Client )
+ , m_FoodLevel(MAX_FOOD_LEVEL)
+ , m_FoodSaturationLevel(5)
+ , m_FoodTickTimer(0)
+ , m_FoodExhaustionLevel(0)
+ , m_FoodPoisonedTicksRemaining(0)
+ , m_NormalMaxSpeed(0.1)
+ , m_SprintingMaxSpeed(0.13)
+ , m_IsCrouched(false)
+ , m_IsSprinting(false)
+ , m_IsSwimming(false)
+ , m_IsSubmerged(false)
+ , m_EatingFinishTick(-1)
+ , m_IsChargingBow(false)
+ , m_BowCharge(0)
+ , m_CurrentXp(0)
+ , m_LifetimeTotalXp(0)
+ , m_bDirtyExperience(false)
+{
+ LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d",
+ a_PlayerName.c_str(), a_Client->GetIPString().c_str(),
+ this, GetUniqueID()
+ );
+
+ m_InventoryWindow = new cInventoryWindow(*this);
+ m_CurrentWindow = m_InventoryWindow;
+ m_InventoryWindow->OpenedByPlayer(*this);
+
+ SetMaxHealth(MAX_HEALTH);
+ m_Health = MAX_HEALTH;
+
+ cTimer t1;
+ m_LastPlayerListTime = t1.GetNowTime();
+
+ m_TimeLastTeleportPacket = 0;
+ m_TimeLastPickupCheck = 0;
+
+ m_PlayerName = a_PlayerName;
+ m_bDirtyPosition = true; // So chunks are streamed to player at spawn
+
+ if (!LoadFromDisk())
+ {
+ m_Inventory.Clear();
+ SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX());
+ SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY());
+ SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ());
+
+ LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}",
+ a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
+ );
+ }
+ m_LastJumpHeight = (float)(GetPosY());
+ m_LastGroundHeight = (float)(GetPosY());
+ m_Stance = GetPosY() + 1.62;
+
+ cRoot::Get()->GetServer()->PlayerCreated(this);
+}
+
+
+
+
+
+cPlayer::~cPlayer(void)
+{
+ LOGD("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID());
+
+ // Notify the server that the player is being destroyed
+ cRoot::Get()->GetServer()->PlayerDestroying(this);
+
+ SaveToDisk();
+
+ m_World->RemovePlayer( this );
+
+ m_ClientHandle = NULL;
+
+ delete m_InventoryWindow;
+
+ LOGD("Player %p deleted", this);
+}
+
+
+
+
+
+bool cPlayer::Initialize(cWorld * a_World)
+{
+ ASSERT(a_World != NULL);
+
+ if (super::Initialize(a_World))
+ {
+ // Remove the client handle from the server, it will be ticked from this object from now on
+ if (m_ClientHandle != NULL)
+ {
+ cRoot::Get()->GetServer()->ClientMovedToWorld(m_ClientHandle);
+ }
+
+ GetWorld()->AddPlayer(this);
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+void cPlayer::Destroyed()
+{
+ CloseWindow(false);
+
+ m_ClientHandle = NULL;
+}
+
+
+
+
+
+void cPlayer::SpawnOn(cClientHandle & a_Client)
+{
+ if (!m_bVisible || (m_ClientHandle == (&a_Client)))
+ {
+ return;
+ }
+ a_Client.SendPlayerSpawn(*this);
+ a_Client.SendEntityHeadLook(*this);
+ a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() );
+ a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() );
+ a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() );
+ a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() );
+ a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() );
+}
+
+
+
+
+
+void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ if (m_ClientHandle != NULL)
+ {
+ if (m_ClientHandle->IsDestroyed())
+ {
+ // This should not happen, because destroying a client will remove it from the world, but just in case
+ m_ClientHandle = NULL;
+ return;
+ }
+
+ if (!m_ClientHandle->IsPlaying())
+ {
+ // We're not yet in the game, ignore everything
+ return;
+ }
+ }
+
+ if (!a_Chunk.IsValid())
+ {
+ // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83)
+ return;
+ }
+
+ super::Tick(a_Dt, a_Chunk);
+
+ // Set player swimming state
+ SetSwimState(a_Chunk);
+
+ // Handle air drowning stuff
+ HandleAir();
+
+ // Handle charging the bow:
+ if (m_IsChargingBow)
+ {
+ m_BowCharge += 1;
+ }
+
+ //handle updating experience
+ if (m_bDirtyExperience)
+ {
+ SendExperience();
+ }
+
+ if (m_bDirtyPosition)
+ {
+ // Apply food exhaustion from movement:
+ ApplyFoodExhaustionFromMovement();
+
+ cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this);
+ BroadcastMovementUpdate(m_ClientHandle);
+ m_ClientHandle->StreamChunks();
+ }
+ else
+ {
+ BroadcastMovementUpdate(m_ClientHandle);
+ }
+
+ if (m_Health > 0) // make sure player is alive
+ {
+ m_World->CollectPickupsByPlayer(this);
+
+ if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge()))
+ {
+ FinishEating();
+ }
+
+ HandleFood();
+ }
+
+ // Send Player List (Once per m_LastPlayerListTime/1000 ms)
+ cTimer t1;
+ if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime())
+ {
+ m_World->SendPlayerList(this);
+ m_LastPlayerListTime = t1.GetNowTime();
+ }
+}
+
+
+
+
+
+short cPlayer::CalcLevelFromXp(short a_XpTotal)
+{
+ //level 0 to 15
+ if(a_XpTotal <= XP_TO_LEVEL15)
+ {
+ return a_XpTotal / XP_PER_LEVEL_TO15;
+ }
+
+ //level 30+
+ if(a_XpTotal > XP_TO_LEVEL30)
+ {
+ return (short) (151.5 + sqrt( 22952.25 - (14 * (2220 - a_XpTotal)))) / 7;
+ }
+
+ //level 16 to 30
+ return (short) ( 29.5 + sqrt( 870.25 - (6 * ( 360 - a_XpTotal )))) / 3;
+}
+
+
+
+
+
+short cPlayer::XpForLevel(short a_Level)
+{
+ //level 0 to 15
+ if(a_Level <= 15)
+ {
+ return a_Level * XP_PER_LEVEL_TO15;
+ }
+
+ //level 30+
+ if(a_Level >= 31)
+ {
+ return (short) ( (3.5 * a_Level * a_Level) - (151.5 * a_Level) + 2220 );
+ }
+
+ //level 16 to 30
+ return (short) ( (1.5 * a_Level * a_Level) - (29.5 * a_Level) + 360 );
+}
+
+
+
+
+
+short cPlayer::GetXpLevel()
+{
+ return CalcLevelFromXp(m_CurrentXp);
+}
+
+
+
+
+
+float cPlayer::GetXpPercentage()
+{
+ short int currentLevel = CalcLevelFromXp(m_CurrentXp);
+ short int currentLevel_XpBase = XpForLevel(currentLevel);
+
+ return (float)(m_CurrentXp - currentLevel_XpBase) /
+ (float)(XpForLevel(1+currentLevel) - currentLevel_XpBase);
+}
+
+
+
+
+
+bool cPlayer::SetCurrentExperience(short int a_CurrentXp)
+{
+ if(!(a_CurrentXp >= 0) || (a_CurrentXp > (SHRT_MAX - m_LifetimeTotalXp)))
+ {
+ LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp);
+ return false; //oops, they gave us a dodgey number
+ }
+
+ m_CurrentXp = a_CurrentXp;
+
+ // Set experience to be updated
+ m_bDirtyExperience = true;
+
+ return true;
+}
+
+
+
+
+
+short cPlayer::DeltaExperience(short a_Xp_delta)
+{
+ if (a_Xp_delta > (SHRT_MAX - m_CurrentXp))
+ {
+ // Value was bad, abort and report
+ LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.",
+ a_Xp_delta);
+ return -1; // Should we instead just return the current Xp?
+ }
+
+ m_CurrentXp += a_Xp_delta;
+
+ // Make sure they didn't subtract too much
+ if (m_CurrentXp < 0)
+ {
+ m_CurrentXp = 0;
+ }
+
+ // Update total for score calculation
+ if (a_Xp_delta > 0)
+ {
+ m_LifetimeTotalXp += a_Xp_delta;
+ }
+
+ LOGD("Player \"%s\" gained/lost %d experience, total is now: %d",
+ m_PlayerName.c_str(), a_Xp_delta, m_CurrentXp);
+
+ // Set experience to be updated
+ m_bDirtyExperience = true;
+
+ return m_CurrentXp;
+}
+
+
+
+
+
+void cPlayer::StartChargingBow(void)
+{
+ LOGD("Player \"%s\" started charging their bow", m_PlayerName.c_str());
+ m_IsChargingBow = true;
+ m_BowCharge = 0;
+}
+
+
+
+
+
+int cPlayer::FinishChargingBow(void)
+{
+ LOGD("Player \"%s\" finished charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge);
+ int res = m_BowCharge;
+ m_IsChargingBow = false;
+ m_BowCharge = 0;
+ return res;
+}
+
+
+
+
+
+void cPlayer::CancelChargingBow(void)
+{
+ LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge);
+ m_IsChargingBow = false;
+ m_BowCharge = 0;
+}
+
+
+
+
+
+void cPlayer::SetTouchGround(bool a_bTouchGround)
+{
+ m_bTouchGround = a_bTouchGround;
+
+ if (!m_bTouchGround)
+ {
+ if (GetPosY() > m_LastJumpHeight)
+ {
+ m_LastJumpHeight = (float)GetPosY();
+ }
+ cWorld * World = GetWorld();
+ if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height))
+ {
+ BLOCKTYPE BlockType = World->GetBlock(float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ()));
+ if (BlockType != E_BLOCK_AIR)
+ {
+ m_bTouchGround = true;
+ }
+ if (
+ (BlockType == E_BLOCK_WATER) ||
+ (BlockType == E_BLOCK_STATIONARY_WATER) ||
+ (BlockType == E_BLOCK_LADDER) ||
+ (BlockType == E_BLOCK_VINES)
+ )
+ {
+ m_LastGroundHeight = (float)GetPosY();
+ }
+ }
+ }
+ else
+ {
+ float Dist = (float)(m_LastGroundHeight - floor(GetPosY()));
+ int Damage = (int)(Dist - 3.f);
+ if (m_LastJumpHeight > m_LastGroundHeight) Damage++;
+ m_LastJumpHeight = (float)GetPosY();
+
+ if ((Damage > 0) && (!IsGameModeCreative()))
+ {
+ TakeDamage(dtFalling, NULL, Damage, Damage, 0);
+ }
+
+ m_LastGroundHeight = (float)GetPosY();
+ }
+}
+
+
+
+
+
+void cPlayer::Heal(int a_Health)
+{
+ super::Heal(a_Health);
+ SendHealth();
+}
+
+
+
+
+
+void cPlayer::SetFoodLevel(int a_FoodLevel)
+{
+ m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL));
+ SendHealth();
+}
+
+
+
+
+
+void cPlayer::SetFoodSaturationLevel(double a_FoodSaturationLevel)
+{
+ m_FoodSaturationLevel = std::max(0.0, std::min(a_FoodSaturationLevel, (double)m_FoodLevel));
+}
+
+
+
+
+
+void cPlayer::SetFoodTickTimer(int a_FoodTickTimer)
+{
+ m_FoodTickTimer = a_FoodTickTimer;
+}
+
+
+
+
+
+void cPlayer::SetFoodExhaustionLevel(double a_FoodExhaustionLevel)
+{
+ m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodExhaustionLevel, 4.0));
+}
+
+
+
+
+
+void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining)
+{
+ m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining;
+}
+
+
+
+
+
+bool cPlayer::Feed(int a_Food, double a_Saturation)
+{
+ if (m_FoodLevel >= MAX_FOOD_LEVEL)
+ {
+ return false;
+ }
+
+ m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL);
+ m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel);
+
+ SendHealth();
+ return true;
+}
+
+
+
+
+
+void cPlayer::FoodPoison(int a_NumTicks)
+{
+ bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0);
+ m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks);
+ if (!HasBeenFoodPoisoned)
+ {
+ // TODO: Send the poisoning indication to the client - how?
+ SendHealth();
+ }
+}
+
+
+
+
+
+void cPlayer::StartEating(void)
+{
+ // Set the timer:
+ m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS;
+
+ // Send the packets:
+ m_World->BroadcastPlayerAnimation(*this, 5);
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cPlayer::FinishEating(void)
+{
+ // Reset the timer:
+ m_EatingFinishTick = -1;
+
+ // Send the packets:
+ m_ClientHandle->SendEntityStatus(*this, ENTITY_STATUS_EATING_ACCEPTED);
+ m_World->BroadcastPlayerAnimation(*this, 0);
+ m_World->BroadcastEntityMetadata(*this);
+
+ // consume the item:
+ cItem Item(GetEquippedItem());
+ Item.m_ItemCount = 1;
+ cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Item.m_ItemType);
+ if (!ItemHandler->EatItem(this, &Item))
+ {
+ return;
+ }
+ ItemHandler->OnFoodEaten(m_World, this, &Item);
+
+ GetInventory().RemoveOneEquippedItem();
+
+ //if the food is mushroom soup, return a bowl to the inventory
+ if( Item.m_ItemType == E_ITEM_MUSHROOM_SOUP ) {
+ cItem emptyBowl(E_ITEM_BOWL, 1, 0, "");
+ GetInventory().AddItem(emptyBowl, true, true);
+ }
+}
+
+
+
+
+
+void cPlayer::AbortEating(void)
+{
+ m_EatingFinishTick = -1;
+ m_World->BroadcastPlayerAnimation(*this, 0);
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cPlayer::SendHealth(void)
+{
+ if (m_ClientHandle != NULL)
+ {
+ m_ClientHandle->SendHealth();
+ }
+}
+
+
+
+
+
+void cPlayer::SendExperience(void)
+{
+ if (m_ClientHandle != NULL)
+ {
+ m_ClientHandle->SendExperience();
+ m_bDirtyExperience = false;
+ }
+}
+
+
+
+
+
+void cPlayer::ClearInventoryPaintSlots(void)
+{
+ // Clear the list of slots that are being inventory-painted. Used by cWindow only
+ m_InventoryPaintSlots.clear();
+}
+
+
+
+
+
+void cPlayer::AddInventoryPaintSlot(int a_SlotNum)
+{
+ // Add a slot to the list for inventory painting. Used by cWindow only
+ m_InventoryPaintSlots.push_back(a_SlotNum);
+}
+
+
+
+
+
+const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const
+{
+ // Return the list of slots currently stored for inventory painting. Used by cWindow only
+ return m_InventoryPaintSlots;
+}
+
+
+
+
+
+double cPlayer::GetMaxSpeed(void) const
+{
+ return m_IsSprinting ? m_SprintingMaxSpeed : m_NormalMaxSpeed;
+}
+
+
+
+
+
+void cPlayer::SetNormalMaxSpeed(double a_Speed)
+{
+ m_NormalMaxSpeed = a_Speed;
+ if (!m_IsSprinting)
+ {
+ m_ClientHandle->SendPlayerMaxSpeed();
+ }
+}
+
+
+
+
+
+void cPlayer::SetSprintingMaxSpeed(double a_Speed)
+{
+ m_SprintingMaxSpeed = a_Speed;
+ if (m_IsSprinting)
+ {
+ m_ClientHandle->SendPlayerMaxSpeed();
+ }
+}
+
+
+
+
+
+void cPlayer::SetCrouch(bool a_IsCrouched)
+{
+ // Set the crouch status, broadcast to all visible players
+
+ if (a_IsCrouched == m_IsCrouched)
+ {
+ // No change
+ return;
+ }
+ m_IsCrouched = a_IsCrouched;
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cPlayer::SetSprint(bool a_IsSprinting)
+{
+ if (a_IsSprinting == m_IsSprinting)
+ {
+ // No change
+ return;
+ }
+
+ m_IsSprinting = a_IsSprinting;
+ m_ClientHandle->SendPlayerMaxSpeed();
+}
+
+
+
+
+
+void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ if (a_TDI.DamageType != dtInVoid)
+ {
+ if (IsGameModeCreative())
+ {
+ // No damage / health in creative mode
+ return;
+ }
+ }
+
+ super::DoTakeDamage(a_TDI);
+
+ // Any kind of damage adds food exhaustion
+ AddFoodExhaustion(0.3f);
+
+ SendHealth();
+}
+
+
+
+
+
+void cPlayer::KilledBy(cEntity * a_Killer)
+{
+ super::KilledBy(a_Killer);
+
+ if (m_Health > 0)
+ {
+ return; // not dead yet =]
+ }
+
+ m_bVisible = false; // So new clients don't see the player
+
+ // Puke out all the items
+ cItems Pickups;
+ m_Inventory.CopyToItems(Pickups);
+ m_Inventory.Clear();
+ m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
+ SaveToDisk(); // Save it, yeah the world is a tough place !
+}
+
+
+
+
+
+void cPlayer::Respawn(void)
+{
+ m_Health = GetMaxHealth();
+
+ // Reset food level:
+ m_FoodLevel = MAX_FOOD_LEVEL;
+ m_FoodSaturationLevel = 5;
+
+ // Reset Experience
+ m_CurrentXp = 0;
+ m_LifetimeTotalXp = 0;
+ // ToDo: send score to client? How?
+
+ m_ClientHandle->SendRespawn();
+
+ // Extinguish the fire:
+ StopBurning();
+
+ TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ());
+
+ SetVisible(true);
+}
+
+
+
+
+
+double cPlayer::GetEyeHeight(void) const
+{
+ return m_Stance;
+}
+
+
+
+
+Vector3d cPlayer::GetEyePosition(void) const
+{
+ return Vector3d( GetPosX(), m_Stance, GetPosZ() );
+}
+
+
+
+
+
+bool cPlayer::IsGameModeCreative(void) const
+{
+ return (m_GameMode == gmCreative) || // Either the player is explicitly in Creative
+ ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Creative
+}
+
+
+
+
+
+bool cPlayer::IsGameModeSurvival(void) const
+{
+ return (m_GameMode == gmSurvival) || // Either the player is explicitly in Survival
+ ((m_GameMode == gmNotSet) && m_World->IsGameModeSurvival()); // or they inherit from the world and the world is Survival
+}
+
+
+
+
+
+bool cPlayer::IsGameModeAdventure(void) const
+{
+ return (m_GameMode == gmCreative) || // Either the player is explicitly in Adventure
+ ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Adventure
+}
+
+
+
+
+
+void cPlayer::OpenWindow(cWindow * a_Window)
+{
+ if (a_Window != m_CurrentWindow)
+ {
+ CloseWindow(false);
+ }
+ a_Window->OpenedByPlayer(*this);
+ m_CurrentWindow = a_Window;
+ a_Window->SendWholeWindow(*GetClientHandle());
+}
+
+
+
+
+
+void cPlayer::CloseWindow(bool a_CanRefuse)
+{
+ if (m_CurrentWindow == NULL)
+ {
+ m_CurrentWindow = m_InventoryWindow;
+ return;
+ }
+
+ if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse)
+ {
+ // Close accepted, go back to inventory window (the default):
+ m_CurrentWindow = m_InventoryWindow;
+ }
+ else
+ {
+ // Re-open the window
+ m_CurrentWindow->OpenedByPlayer(*this);
+ m_CurrentWindow->SendWholeWindow(*GetClientHandle());
+ }
+}
+
+
+
+
+
+void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse)
+{
+ if ((m_CurrentWindow == NULL) || (m_CurrentWindow->GetWindowID() != a_WindowID))
+ {
+ return;
+ }
+ CloseWindow();
+}
+
+
+
+
+
+void cPlayer::SetLastBlockActionTime()
+{
+ if (m_World != NULL)
+ {
+ m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f;
+ }
+}
+
+
+
+
+
+void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt )
+{
+ m_LastBlockActionCnt = a_LastBlockActionCnt;
+}
+
+
+
+
+
+void cPlayer::SetGameMode(eGameMode a_GameMode)
+{
+ if ((a_GameMode < gmMin) || (a_GameMode >= gmMax))
+ {
+ LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode);
+ return;
+ }
+
+ if (m_GameMode == a_GameMode)
+ {
+ // Gamemode already set
+ return;
+ }
+
+ m_GameMode = a_GameMode;
+ m_ClientHandle->SendGameMode(a_GameMode);
+}
+
+
+
+
+
+void cPlayer::LoginSetGameMode( eGameMode a_GameMode )
+{
+ m_GameMode = a_GameMode;
+}
+
+
+
+
+
+void cPlayer::SetIP(const AString & a_IP)
+{
+ m_IP = a_IP;
+}
+
+
+
+
+
+void cPlayer::SendMessage(const AString & a_Message)
+{
+ m_ClientHandle->SendChat(a_Message);
+}
+
+
+
+
+
+void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
+{
+ SetPosition( a_PosX, a_PosY, a_PosZ );
+ m_LastGroundHeight = (float)a_PosY;
+
+ m_World->BroadcastTeleportEntity(*this, GetClientHandle());
+ m_ClientHandle->SendPlayerMoveLook();
+}
+
+
+
+
+
+Vector3d cPlayer::GetThrowStartPos(void) const
+{
+ Vector3d res = GetEyePosition();
+
+ // Adjust the position to be just outside the player's bounding box:
+ res.x += 0.16 * cos(GetPitch());
+ res.y += -0.1;
+ res.z += 0.16 * sin(GetPitch());
+
+ return res;
+}
+
+
+
+
+
+Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const
+{
+ Vector3d res = GetLookVector();
+ res.Normalize();
+
+ // TODO: Add a slight random change (+-0.0075 in each direction)
+
+ return res * a_SpeedCoeff;
+}
+
+
+
+
+
+void cPlayer::MoveTo( const Vector3d & a_NewPos )
+{
+ if ((a_NewPos.y < -990) && (GetPosY() > -100))
+ {
+ // When attached to an entity, the client sends position packets with weird coords:
+ // Y = -999 and X, Z = attempting to create speed, usually up to 0.03
+ // We cannot test m_AttachedTo, because when deattaching, the server thinks the client is already deattached while
+ // the client may still send more of these nonsensical packets.
+ if (m_AttachedTo != NULL)
+ {
+ Vector3d AddSpeed(a_NewPos);
+ AddSpeed.y = 0;
+ m_AttachedTo->AddSpeed(AddSpeed);
+ }
+ return;
+ }
+
+ // TODO: should do some checks to see if player is not moving through terrain
+ // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
+
+ SetPosition( a_NewPos );
+ SetStance(a_NewPos.y + 1.62);
+}
+
+
+
+
+
+void cPlayer::SetVisible(bool a_bVisible)
+{
+ if (a_bVisible && !m_bVisible) // Make visible
+ {
+ m_bVisible = true;
+ m_World->BroadcastSpawnEntity(*this);
+ }
+ if (!a_bVisible && m_bVisible)
+ {
+ m_bVisible = false;
+ m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients
+ }
+}
+
+
+
+
+
+void cPlayer::AddToGroup( const AString & a_GroupName )
+{
+ cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName );
+ m_Groups.push_back( Group );
+ LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName.c_str() );
+ ResolveGroups();
+ ResolvePermissions();
+}
+
+
+
+
+
+void cPlayer::RemoveFromGroup( const AString & a_GroupName )
+{
+ bool bRemoved = false;
+ for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr )
+ {
+ if( (*itr)->GetName().compare(a_GroupName ) == 0 )
+ {
+ m_Groups.erase( itr );
+ bRemoved = true;
+ break;
+ }
+ }
+
+ if( bRemoved )
+ {
+ LOGD("Removed %s from group %s", m_PlayerName.c_str(), a_GroupName.c_str() );
+ ResolveGroups();
+ ResolvePermissions();
+ }
+ else
+ {
+ LOGWARN("Tried to remove %s from group %s but was not in that group", m_PlayerName.c_str(), a_GroupName.c_str() );
+ }
+}
+
+
+
+
+
+bool cPlayer::CanUseCommand( const AString & a_Command )
+{
+ for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr )
+ {
+ if( (*itr)->HasCommand( a_Command ) ) return true;
+ }
+ return false;
+}
+
+
+
+
+
+bool cPlayer::HasPermission(const AString & a_Permission)
+{
+ if (a_Permission.empty())
+ {
+ // Empty permission request is always granted
+ return true;
+ }
+
+ AStringVector Split = StringSplit( a_Permission, "." );
+ PermissionMap Possibilities = m_ResolvedPermissions;
+ // Now search the namespaces
+ while( Possibilities.begin() != Possibilities.end() )
+ {
+ PermissionMap::iterator itr = Possibilities.begin();
+ if( itr->second )
+ {
+ AStringVector OtherSplit = StringSplit( itr->first, "." );
+ if( OtherSplit.size() <= Split.size() )
+ {
+ unsigned int i;
+ for( i = 0; i < OtherSplit.size(); ++i )
+ {
+ if( OtherSplit[i].compare( Split[i] ) != 0 )
+ {
+ if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard!
+ break;
+ }
+ }
+ if( i == Split.size() ) return true;
+ }
+ }
+ Possibilities.erase( itr );
+ }
+
+ // Nothing that matched :(
+ return false;
+}
+
+
+
+
+
+bool cPlayer::IsInGroup( const AString & a_Group )
+{
+ for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr )
+ {
+ if( a_Group.compare( (*itr)->GetName().c_str() ) == 0 )
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+void cPlayer::ResolvePermissions()
+{
+ m_ResolvedPermissions.clear(); // Start with an empty map yo~
+
+ // Copy all player specific permissions into the resolved permissions map
+ for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr )
+ {
+ m_ResolvedPermissions[ itr->first ] = itr->second;
+ }
+
+ for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr )
+ {
+ const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions();
+ for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr )
+ {
+ m_ResolvedPermissions[ itr->first ] = itr->second;
+ }
+ }
+}
+
+
+
+
+
+void cPlayer::ResolveGroups()
+{
+ // Clear resolved groups first
+ m_ResolvedGroups.clear();
+
+ // Get a complete resolved list of all groups the player is in
+ std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates
+ GroupList ToIterate;
+ for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr )
+ {
+ ToIterate.push_back( *GroupItr );
+ }
+ while( ToIterate.begin() != ToIterate.end() )
+ {
+ cGroup* CurrentGroup = *ToIterate.begin();
+ if( AllGroups.find( CurrentGroup ) != AllGroups.end() )
+ {
+ LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!",
+ m_PlayerName.c_str(), CurrentGroup->GetName().c_str()
+ );
+ }
+ else
+ {
+ AllGroups[ CurrentGroup ] = true;
+ m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list
+ const cGroup::GroupList & Inherits = CurrentGroup->GetInherits();
+ for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr )
+ {
+ if( AllGroups.find( *itr ) != AllGroups.end() )
+ {
+ LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() );
+ continue;
+ }
+ ToIterate.push_back( *itr );
+ }
+ }
+ ToIterate.erase( ToIterate.begin() );
+ }
+}
+
+
+
+
+
+AString cPlayer::GetColor(void) const
+{
+ if ( m_Color != '-' )
+ {
+ return cChatColor::MakeColor( m_Color );
+ }
+
+ if ( m_Groups.size() < 1 )
+ {
+ return cChatColor::White;
+ }
+
+ return (*m_Groups.begin())->GetColor();
+}
+
+
+
+
+
+void cPlayer::TossItem(
+ bool a_bDraggingItem,
+ char a_Amount /* = 1 */,
+ short a_CreateType /* = 0 */,
+ short a_CreateHealth /* = 0 */
+)
+{
+ cItems Drops;
+ if (a_CreateType != 0)
+ {
+ // Just create item without touching the inventory (used in creative mode)
+ Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth));
+ }
+ else
+ {
+ // Drop an item from the inventory:
+ if (a_bDraggingItem)
+ {
+ cItem & Item = GetDraggingItem();
+ if (!Item.IsEmpty())
+ {
+ char OriginalItemAmount = Item.m_ItemCount;
+ Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount);
+ Drops.push_back(Item);
+ if (OriginalItemAmount > a_Amount)
+ {
+ Item.m_ItemCount = OriginalItemAmount - (char)a_Amount;
+ }
+ else
+ {
+ Item.Empty();
+ }
+ }
+ }
+ else
+ {
+ // Else drop equipped item
+ cItem DroppedItem(GetInventory().GetEquippedItem());
+ if (!DroppedItem.IsEmpty())
+ {
+ if (GetInventory().RemoveOneEquippedItem())
+ {
+ DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again
+ Drops.push_back(DroppedItem);
+ }
+ }
+ }
+ }
+ double vX = 0, vY = 0, vZ = 0;
+ EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY);
+ vY = -vY * 2 + 1.f;
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player
+}
+
+
+
+
+
+bool cPlayer::MoveToWorld(const char * a_WorldName)
+{
+ cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
+ if (World == NULL)
+ {
+ LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName);
+ return false;
+ }
+
+ eDimension OldDimension = m_World->GetDimension();
+
+ // Remove all links to the old world
+ m_World->RemovePlayer(this);
+ m_ClientHandle->RemoveFromAllChunks();
+ m_World->RemoveEntity(this);
+
+ // If the dimension is different, we can send the respawn packet
+ // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
+ m_ClientHandle->MoveToWorld(*World, (OldDimension != World->GetDimension()));
+
+ // Add player to all the necessary parts of the new world
+ SetWorld(World);
+ World->AddEntity(this);
+ World->AddPlayer(this);
+
+ return true;
+}
+
+
+
+
+
+void cPlayer::LoadPermissionsFromDisk()
+{
+ m_Groups.clear();
+ m_Permissions.clear();
+
+ cIniFile IniFile;
+ if (IniFile.ReadFile("users.ini"))
+ {
+ std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", "");
+ if (!Groups.empty())
+ {
+ AStringVector Split = StringSplit( Groups, "," );
+ for( unsigned int i = 0; i < Split.size(); i++ )
+ {
+ AddToGroup( Split[i].c_str() );
+ }
+ }
+ else
+ {
+ AddToGroup("Default");
+ }
+
+ m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0];
+ }
+ else
+ {
+ LOGWARN("Failed to read the users.ini file. The player will be added only to the Default group.");
+ AddToGroup("Default");
+ }
+ ResolvePermissions();
+}
+
+
+
+
+bool cPlayer::LoadFromDisk()
+{
+ LoadPermissionsFromDisk();
+
+ // Log player permissions, cause it's what the cool kids do
+ LOGINFO("Player %s has permissions:", m_PlayerName.c_str() );
+ for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr )
+ {
+ if( itr->second ) LOGINFO("%s", itr->first.c_str() );
+ }
+
+ AString SourceFile;
+ Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() );
+
+ cFile f;
+ if (!f.Open(SourceFile, cFile::fmRead))
+ {
+ // This is a new player whom we haven't seen yet, bail out, let them have the defaults
+ return false;
+ }
+
+ AString buffer;
+ if (f.ReadRestOfFile(buffer) != f.GetSize())
+ {
+ LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str());
+ return false;
+ }
+ f.Close(); //cool kids play nice
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(buffer, root, false))
+ {
+ LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str());
+ }
+
+ Json::Value & JSON_PlayerPosition = root["position"];
+ if (JSON_PlayerPosition.size() == 3)
+ {
+ SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble());
+ SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble());
+ SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble());
+ m_LastPosX = GetPosX();
+ m_LastPosY = GetPosY();
+ m_LastPosZ = GetPosZ();
+ m_LastFoodPos = GetPosition();
+ }
+
+ Json::Value & JSON_PlayerRotation = root["rotation"];
+ if (JSON_PlayerRotation.size() == 3)
+ {
+ SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble());
+ SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble());
+ SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble());
+ }
+
+ m_Health = root.get("health", 0).asInt();
+ m_AirLevel = root.get("air", MAX_AIR_LEVEL).asInt();
+ m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt();
+ m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble();
+ m_FoodTickTimer = root.get("foodTickTimer", 0).asInt();
+ m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble();
+ m_LifetimeTotalXp = (short) root.get("xpTotal", 0).asInt();
+ m_CurrentXp = (short) root.get("xpCurrent", 0).asInt();
+
+ //SetExperience(root.get("experience", 0).asInt());
+
+ m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt();
+
+ m_Inventory.LoadFromJson(root["inventory"]);
+
+ m_LoadedWorldName = root.get("world", "world").asString();
+
+ LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"",
+ m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str()
+ );
+
+ return true;
+}
+
+
+
+
+
+bool cPlayer::SaveToDisk()
+{
+ cFile::CreateFolder(FILE_IO_PREFIX + AString("players"));
+
+ // create the JSON data
+ Json::Value JSON_PlayerPosition;
+ JSON_PlayerPosition.append(Json::Value(GetPosX()));
+ JSON_PlayerPosition.append(Json::Value(GetPosY()));
+ JSON_PlayerPosition.append(Json::Value(GetPosZ()));
+
+ Json::Value JSON_PlayerRotation;
+ JSON_PlayerRotation.append(Json::Value(GetRotation()));
+ JSON_PlayerRotation.append(Json::Value(GetPitch()));
+ JSON_PlayerRotation.append(Json::Value(GetRoll()));
+
+ Json::Value JSON_Inventory;
+ m_Inventory.SaveToJson(JSON_Inventory);
+
+ Json::Value root;
+ root["position"] = JSON_PlayerPosition;
+ root["rotation"] = JSON_PlayerRotation;
+ root["inventory"] = JSON_Inventory;
+ root["health"] = m_Health;
+ root["xpTotal"] = m_LifetimeTotalXp;
+ root["xpCurrent"] = m_CurrentXp;
+ root["air"] = m_AirLevel;
+ root["food"] = m_FoodLevel;
+ root["foodSaturation"] = m_FoodSaturationLevel;
+ root["foodTickTimer"] = m_FoodTickTimer;
+ root["foodExhaustion"] = m_FoodExhaustionLevel;
+ root["world"] = GetWorld()->GetName();
+
+ if (m_GameMode == GetWorld()->GetGameMode())
+ {
+ root["gamemode"] = (int) eGameMode_NotSet;
+ }
+ else
+ {
+ root["gamemode"] = (int) m_GameMode;
+ }
+
+ Json::StyledWriter writer;
+ std::string JsonData = writer.write(root);
+
+ AString SourceFile;
+ Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() );
+
+ cFile f;
+ if (!f.Open(SourceFile, cFile::fmWrite))
+ {
+ LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str());
+ return false;
+ }
+ if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size())
+ {
+ LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str());
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+cPlayer::StringList cPlayer::GetResolvedPermissions()
+{
+ StringList Permissions;
+
+ const PermissionMap& ResolvedPermissions = m_ResolvedPermissions;
+ for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr )
+ {
+ if( itr->second ) Permissions.push_back( itr->first );
+ }
+
+ return Permissions;
+}
+
+
+
+
+
+void cPlayer::UseEquippedItem(void)
+{
+ if (IsGameModeCreative()) // No damage in creative
+ {
+ return;
+ }
+
+ GetInventory().DamageEquippedItem();
+}
+
+
+
+
+
+void cPlayer::SetSwimState(cChunk & a_Chunk)
+{
+ int RelY = (int)floor(m_LastPosY + 0.1);
+ if ((RelY < 0) || (RelY >= cChunkDef::Height - 1))
+ {
+ m_IsSwimming = false;
+ m_IsSubmerged = false;
+ return;
+ }
+
+ BLOCKTYPE BlockIn;
+ int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width;
+
+ // Check if the player is swimming:
+ // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk
+ if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn))
+ {
+ // This sometimes happens on Linux machines
+ // Ref.: http://forum.mc-server.org/showthread.php?tid=1244
+ LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}",
+ RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ()
+ );
+ m_IsSwimming = false;
+ m_IsSubmerged = false;
+ return;
+ }
+ m_IsSwimming = IsBlockWater(BlockIn);
+
+ // Check if the player is submerged:
+ VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn));
+ m_IsSubmerged = IsBlockWater(BlockIn);
+}
+
+
+
+
+
+void cPlayer::HandleAir(void)
+{
+ // Ref.: http://www.minecraftwiki.net/wiki/Chunk_format
+ // see if the player is /submerged/ water (block above is water)
+ // Get the type of block the player's standing in:
+
+ if (IsSubmerged())
+ {
+ // either reduce air level or damage player
+ if (m_AirLevel < 1)
+ {
+ if (m_AirTickTimer < 1)
+ {
+ // damage player
+ TakeDamage(dtDrowning, NULL, 1, 1, 0);
+ // reset timer
+ m_AirTickTimer = DROWNING_TICKS;
+ }
+ else
+ {
+ m_AirTickTimer -= 1;
+ }
+ }
+ else
+ {
+ // reduce air supply
+ m_AirLevel -= 1;
+ }
+ }
+ else
+ {
+ // set the air back to maximum
+ m_AirLevel = MAX_AIR_LEVEL;
+ m_AirTickTimer = DROWNING_TICKS;
+ }
+}
+
+
+
+
+
+void cPlayer::HandleFood(void)
+{
+ // Ref.: http://www.minecraftwiki.net/wiki/Hunger
+
+ // Remember the food level before processing, for later comparison
+ int LastFoodLevel = m_FoodLevel;
+
+ // Heal or damage, based on the food level, using the m_FoodTickTimer:
+ if ((m_FoodLevel > 17) || (m_FoodLevel <= 0))
+ {
+ m_FoodTickTimer++;
+ if (m_FoodTickTimer >= 80)
+ {
+ m_FoodTickTimer = 0;
+
+ if (m_FoodLevel >= 17)
+ {
+ // Regenerate health from food, incur 3 pts of food exhaustion:
+ Heal(1);
+ m_FoodExhaustionLevel += 3;
+ }
+ else if (m_FoodLevel <= 0)
+ {
+ // Damage from starving
+ TakeDamage(dtStarving, NULL, 1, 1, 0);
+ }
+ }
+ }
+
+ // Apply food poisoning food exhaustion:
+ if (m_FoodPoisonedTicksRemaining > 0)
+ {
+ m_FoodPoisonedTicksRemaining--;
+ m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick
+ }
+
+ // Apply food exhaustion that has accumulated:
+ if (m_FoodExhaustionLevel >= 4)
+ {
+ m_FoodExhaustionLevel -= 4;
+
+ if (m_FoodSaturationLevel >= 1)
+ {
+ m_FoodSaturationLevel -= 1;
+ }
+ else
+ {
+ m_FoodLevel = std::max(m_FoodLevel - 1, 0);
+ }
+ }
+
+ if (m_FoodLevel != LastFoodLevel)
+ {
+ SendHealth();
+ }
+}
+
+
+
+
+
+void cPlayer::ApplyFoodExhaustionFromMovement()
+{
+ if (IsGameModeCreative())
+ {
+ return;
+ }
+
+ // Calculate the distance travelled, update the last pos:
+ Vector3d Movement(GetPosition() - m_LastFoodPos);
+ Movement.y = 0; // Only take XZ movement into account
+ m_LastFoodPos = GetPosition();
+
+ // If riding anything, apply no food exhaustion
+ if (m_AttachedTo != NULL)
+ {
+ return;
+ }
+
+ // Apply the exhaustion based on distance travelled:
+ double BaseExhaustion = Movement.Length();
+ if (IsSprinting())
+ {
+ // 0.1 pt per meter sprinted
+ BaseExhaustion = BaseExhaustion * 0.1;
+ }
+ else if (IsSwimming())
+ {
+ // 0.015 pt per meter swum
+ BaseExhaustion = BaseExhaustion * 0.015;
+ }
+ else
+ {
+ // 0.01 pt per meter walked / sneaked
+ BaseExhaustion = BaseExhaustion * 0.01;
+ }
+ m_FoodExhaustionLevel += BaseExhaustion;
+}
+
+
+
+
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
new file mode 100644
index 000000000..44cab7d74
--- /dev/null
+++ b/src/Entities/Player.h
@@ -0,0 +1,456 @@
+
+#pragma once
+
+#include "Pawn.h"
+#include "../Inventory.h"
+#include "../Defines.h"
+#include "../World.h"
+
+
+
+
+
+class cGroup;
+class cWindow;
+class cClientHandle;
+
+
+
+
+
+// tolua_begin
+class cPlayer :
+ public cPawn
+{
+ typedef cPawn super;
+
+public:
+ enum
+ {
+ MAX_HEALTH = 20,
+ MAX_FOOD_LEVEL = 20,
+ EATING_TICKS = 30, ///< Number of ticks it takes to eat an item
+ MAX_AIR_LEVEL = 300,
+ DROWNING_TICKS = 10, //number of ticks per heart of damage
+ } ;
+ // tolua_end
+
+ CLASS_PROTODEF(cPlayer)
+
+
+ cPlayer(cClientHandle * a_Client, const AString & a_PlayerName);
+ virtual ~cPlayer();
+
+ virtual bool Initialize(cWorld * a_World) override;
+
+ virtual void SpawnOn(cClientHandle & a_Client) override;
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+ virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override { };
+
+ /// Returns the curently equipped weapon; empty item if none
+ virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); }
+
+ /// Returns the currently equipped helmet; empty item if nonte
+ virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); }
+
+ /// Returns the currently equipped chestplate; empty item if none
+ virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); }
+
+ /// Returns the currently equipped leggings; empty item if none
+ virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); }
+
+ /// Returns the currently equipped boots; empty item if none
+ virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); }
+
+
+ // tolua_begin
+
+ /** Sets the experience total
+ Returns true on success
+ "should" really only be called at init or player death, plugins excepted
+ */
+ bool SetCurrentExperience(short a_XpTotal);
+
+ /* changes Xp by Xp_delta, you "shouldn't" inc more than MAX_EXPERIENCE_ORB_SIZE
+ Wont't allow xp to go negative
+ Returns the new current experience, -1 on error
+ */
+ short DeltaExperience(short a_Xp_delta);
+
+ /// Gets the experience total - XpTotal for score on death
+ inline short GetXpLifetimeTotal(void) { return m_LifetimeTotalXp; }
+
+ /// Gets the currrent experience
+ inline short GetCurrentXp(void) { return m_CurrentXp; }
+
+ /// Gets the current level - XpLevel
+ short GetXpLevel(void);
+
+ /// Gets the experience bar percentage - XpP
+ float GetXpPercentage(void);
+
+ /// Caculates the amount of XP needed for a given level, ref: http://minecraft.gamepedia.com/XP
+ static short XpForLevel(short int a_Level);
+
+ /// inverse of XpForLevel, ref: http://minecraft.gamepedia.com/XP values are as per this with pre-calculations
+ static short CalcLevelFromXp(short int a_CurrentXp);
+
+ // tolua_end
+
+ /// Starts charging the equipped bow
+ void StartChargingBow(void);
+
+ /// Finishes charging the current bow. Returns the number of ticks for which the bow has been charged
+ int FinishChargingBow(void);
+
+ /// Cancels the current bow charging
+ void CancelChargingBow(void);
+
+ /// Returns true if the player is currently charging the bow
+ bool IsChargingBow(void) const { return m_IsChargingBow; }
+
+ void SetTouchGround( bool a_bTouchGround );
+ inline void SetStance( const double a_Stance ) { m_Stance = a_Stance; }
+ double GetEyeHeight(void) const; // tolua_export
+ Vector3d GetEyePosition(void) const; // tolua_export
+ inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export
+ inline const double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc.
+ inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export
+ inline const cInventory & GetInventory(void) const { return m_Inventory; }
+
+ inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export
+
+ virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override;
+
+ // tolua_begin
+
+ /// Returns the position where projectiles thrown by this player should start, player eye position + adjustment
+ Vector3d GetThrowStartPos(void) const;
+
+ /// Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff.
+ Vector3d GetThrowSpeed(double a_SpeedCoeff) const;
+
+ /// Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable
+ eGameMode GetGameMode(void) const { return m_GameMode; }
+
+ /// Returns the current effective gamemode (inherited gamemode is resolved before returning)
+ eGameMode GetEffectiveGameMode(void) const { return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; }
+
+ /** Sets the gamemode for the player.
+ The gamemode may be gmNotSet, in that case the player inherits the world's gamemode.
+ Updates the gamemode on the client (sends the packet)
+ */
+ void SetGameMode(eGameMode a_GameMode);
+
+ /// Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world
+ bool IsGameModeCreative(void) const;
+
+ /// Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world
+ bool IsGameModeSurvival(void) const;
+
+ /// Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world
+ bool IsGameModeAdventure(void) const;
+
+ AString GetIP(void) const { return m_IP; } // tolua_export
+
+ // tolua_end
+
+ void SetIP(const AString & a_IP);
+
+ float GetLastBlockActionTime() { return m_LastBlockActionTime; }
+ int GetLastBlockActionCnt() { return m_LastBlockActionCnt; }
+ void SetLastBlockActionCnt( int );
+ void SetLastBlockActionTime();
+
+ // Sets the current gamemode, doesn't check validity, doesn't send update packets to client
+ void LoginSetGameMode(eGameMode a_GameMode);
+
+ /// Tries to move to a new position, with attachment-related checks (y == -999)
+ void MoveTo(const Vector3d & a_NewPos); // tolua_export
+
+ cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export
+ const cWindow * GetWindow(void) const { return m_CurrentWindow; }
+
+ /// Opens the specified window; closes the current one first using CloseWindow()
+ void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp
+
+ // tolua_begin
+
+ /// Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true
+ void CloseWindow(bool a_CanRefuse = true);
+
+ /// Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow
+ void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true);
+
+ cClientHandle * GetClientHandle(void) const { return m_ClientHandle; }
+
+ void SendMessage(const AString & a_Message);
+
+ const AString & GetName(void) const { return m_PlayerName; }
+ void SetName(const AString & a_Name) { m_PlayerName = a_Name; }
+
+ // tolua_end
+
+ typedef std::list< cGroup* > GroupList;
+ typedef std::list< std::string > StringList;
+
+ /// Adds a player to existing group or creates a new group when it doesn't exist
+ void AddToGroup( const AString & a_GroupName ); // tolua_export
+
+ /// Removes a player from the group, resolves permissions and group inheritance (case sensitive)
+ void RemoveFromGroup( const AString & a_GroupName ); // tolua_export
+
+ bool CanUseCommand( const AString & a_Command ); // tolua_export
+ bool HasPermission( const AString & a_Permission ); // tolua_export
+ const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS <<
+ StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS <<
+ bool IsInGroup( const AString & a_Group ); // tolua_export
+
+ // tolua_begin
+
+ /// Returns the full color code to use for this player, based on their primary group or set in m_Color
+ AString GetColor(void) const;
+
+ void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0);
+
+ /// Heals the player by the specified amount of HPs (positive only); sends health update
+ void Heal(int a_Health);
+
+ int GetFoodLevel (void) const { return m_FoodLevel; }
+ double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; }
+ int GetFoodTickTimer (void) const { return m_FoodTickTimer; }
+ double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; }
+ int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; }
+
+ int GetAirLevel (void) const { return m_AirLevel; }
+
+ /// Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore
+ bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); }
+
+ void SetFoodLevel (int a_FoodLevel);
+ void SetFoodSaturationLevel (double a_FoodSaturationLevel);
+ void SetFoodTickTimer (int a_FoodTickTimer);
+ void SetFoodExhaustionLevel (double a_FoodExhaustionLevel);
+ void SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining);
+
+ /// Adds to FoodLevel and FoodSaturationLevel, returns true if any food has been consumed, false if player "full"
+ bool Feed(int a_Food, double a_Saturation);
+
+ /// Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values.
+ void AddFoodExhaustion(double a_Exhaustion)
+ {
+ m_FoodExhaustionLevel += a_Exhaustion;
+ }
+
+ /// Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two
+ void FoodPoison(int a_NumTicks);
+
+ /// Returns true if the player is currently in the process of eating the currently equipped item
+ bool IsEating(void) const { return (m_EatingFinishTick >= 0); }
+
+ // tolua_end
+
+ /// Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet
+ void StartEating(void);
+
+ /// Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets
+ void FinishEating(void);
+
+ /// Aborts the current eating operation
+ void AbortEating(void);
+
+ virtual void KilledBy(cEntity * a_Killer) override;
+
+ void Respawn(void); // tolua_export
+
+ void SetVisible( bool a_bVisible ); // tolua_export
+ bool IsVisible(void) const { return m_bVisible; } // tolua_export
+
+ bool MoveToWorld(const char * a_WorldName); // tolua_export
+
+ bool SaveToDisk(void);
+ bool LoadFromDisk(void);
+ void LoadPermissionsFromDisk(void); // tolua_export
+
+ const AString & GetLoadedWorldName() { return m_LoadedWorldName; }
+
+ void UseEquippedItem(void);
+
+ void SendHealth(void);
+
+ void SendExperience(void);
+
+ // In UI windows, the item that the player is dragging:
+ bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); }
+ cItem & GetDraggingItem(void) {return m_DraggingItem; }
+
+ // In UI windows, when inventory-painting:
+ /// Clears the list of slots that are being inventory-painted. To be used by cWindow only
+ void ClearInventoryPaintSlots(void);
+
+ /// Adds a slot to the list for inventory painting. To be used by cWindow only
+ void AddInventoryPaintSlot(int a_SlotNum);
+
+ /// Returns the list of slots currently stored for inventory painting. To be used by cWindow only
+ const cSlotNums & GetInventoryPaintSlots(void) const;
+
+ // tolua_begin
+
+ /// Returns the current maximum speed, as reported in the 1.6.1+ protocol (takes current sprinting state into account)
+ double GetMaxSpeed(void) const;
+
+ /// Gets the normal maximum speed, as reported in the 1.6.1+ protocol, in the protocol units
+ double GetNormalMaxSpeed(void) const { return m_NormalMaxSpeed; }
+
+ /// Gets the sprinting maximum speed, as reported in the 1.6.1+ protocol, in the protocol units
+ double GetSprintingMaxSpeed(void) const { return m_SprintingMaxSpeed; }
+
+ /// Sets the normal maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed.
+ void SetNormalMaxSpeed(double a_Speed);
+
+ /// Sets the sprinting maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed.
+ void SetSprintingMaxSpeed(double a_Speed);
+
+ /// Sets the crouch status, broadcasts to all visible players
+ void SetCrouch(bool a_IsCrouched);
+
+ /// Starts or stops sprinting, sends the max speed update to the client, if needed
+ void SetSprint(bool a_IsSprinting);
+
+ /// Returns whether the player is swimming or not
+ virtual bool IsSwimming(void) const{ return m_IsSwimming; }
+
+ /// Return whether the player is under water or not
+ virtual bool IsSubmerged(void) const{ return m_IsSubmerged; }
+
+ // tolua_end
+
+ // cEntity overrides:
+ virtual bool IsCrouched (void) const { return m_IsCrouched; }
+ virtual bool IsSprinting(void) const { return m_IsSprinting; }
+ virtual bool IsRclking (void) const { return IsEating(); }
+
+protected:
+ typedef std::map< std::string, bool > PermissionMap;
+ PermissionMap m_ResolvedPermissions;
+ PermissionMap m_Permissions;
+
+ GroupList m_ResolvedGroups;
+ GroupList m_Groups;
+
+ std::string m_PlayerName;
+ std::string m_LoadedWorldName;
+
+ /// Xp Level stuff
+ enum
+ {
+ XP_TO_LEVEL15 = 255,
+ XP_PER_LEVEL_TO15 = 17,
+ XP_TO_LEVEL30 = 825
+ } ;
+
+ /// Player's air level (for swimming)
+ int m_AirLevel;
+
+ /// used to time ticks between damage taken via drowning/suffocation
+ int m_AirTickTimer;
+
+ bool m_bVisible;
+
+ // Food-related variables:
+ /// Represents the food bar, one point equals half a "drumstick"
+ int m_FoodLevel;
+
+ /// "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel
+ double m_FoodSaturationLevel;
+
+ /// Count-up to the healing or damaging action, based on m_FoodLevel
+ int m_FoodTickTimer;
+
+ /// A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little
+ double m_FoodExhaustionLevel;
+
+ /// Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned
+ int m_FoodPoisonedTicksRemaining;
+
+ /// Last position that has been recorded for food-related processing:
+ Vector3d m_LastFoodPos;
+
+ float m_LastJumpHeight;
+ float m_LastGroundHeight;
+ bool m_bTouchGround;
+ double m_Stance;
+ cInventory m_Inventory;
+ cWindow * m_CurrentWindow;
+ cWindow * m_InventoryWindow;
+
+ float m_TimeLastPickupCheck;
+
+ void ResolvePermissions();
+
+ void ResolveGroups();
+ char m_Color;
+
+ float m_LastBlockActionTime;
+ int m_LastBlockActionCnt;
+ eGameMode m_GameMode;
+ std::string m_IP;
+
+ cItem m_DraggingItem;
+
+ long long m_LastPlayerListTime;
+ static const unsigned short PLAYER_LIST_TIME_MS = 1000; // 1000 = once per second
+
+ cClientHandle * m_ClientHandle;
+
+ cSlotNums m_InventoryPaintSlots;
+
+ /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is walking. 0.1 by default
+ double m_NormalMaxSpeed;
+
+ /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is sprinting. 0.13 by default
+ double m_SprintingMaxSpeed;
+
+ bool m_IsCrouched;
+ bool m_IsSprinting;
+
+ bool m_IsSwimming;
+ bool m_IsSubmerged;
+
+ /// The world tick in which eating will be finished. -1 if not eating
+ Int64 m_EatingFinishTick;
+
+ /// Player Xp level
+ short int m_LifetimeTotalXp;
+ short int m_CurrentXp;
+
+ // flag saying we need to send a xp update to client
+ bool m_bDirtyExperience;
+
+ bool m_IsChargingBow;
+ int m_BowCharge;
+
+
+ virtual void Destroyed(void);
+
+ /// Filters out damage for creative mode
+ virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
+
+ /// Called in each tick to handle food-related processing
+ void HandleFood(void);
+
+ /// Called in each tick to handle air-related processing i.e. drowning
+ void HandleAir();
+
+ /// Called once per tick to set IsSwimming and IsSubmerged
+ void SetSwimState(cChunk & a_Chunk);
+
+ /// Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block)
+ void ApplyFoodExhaustionFromMovement();
+} ; // tolua_export
+
+
+
+
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
new file mode 100644
index 000000000..fb25aea35
--- /dev/null
+++ b/src/Entities/ProjectileEntity.cpp
@@ -0,0 +1,855 @@
+
+// ProjectileEntity.cpp
+
+// Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types
+
+#include "Globals.h"
+#include "ProjectileEntity.h"
+#include "../ClientHandle.h"
+#include "Player.h"
+#include "../LineBlockTracer.h"
+#include "../BoundingBox.h"
+#include "../ChunkMap.h"
+#include "../Chunk.h"
+
+
+
+
+
+/// Converts an angle in radians into a byte representation used by the network protocol
+#define ANGLE_TO_PROTO(X) (Byte)(X * 255 / 360)
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProjectileTracerCallback:
+
+class cProjectileTracerCallback :
+ public cBlockTracer::cCallbacks
+{
+public:
+ cProjectileTracerCallback(cProjectileEntity * a_Projectile) :
+ m_Projectile(a_Projectile),
+ m_SlowdownCoeff(0.99) // Default slowdown when not in water
+ {
+ }
+
+ double GetSlowdownCoeff(void) const { return m_SlowdownCoeff; }
+
+protected:
+ cProjectileEntity * m_Projectile;
+ double m_SlowdownCoeff;
+
+ // cCallbacks overrides:
+ virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override
+ {
+ /*
+ // DEBUG:
+ LOGD("Hit block %d:%d at {%d, %d, %d} face %d, %s (%s)",
+ a_BlockType, a_BlockMeta,
+ a_BlockX, a_BlockY, a_BlockZ, a_EntryFace,
+ g_BlockIsSolid[a_BlockType] ? "solid" : "non-solid",
+ ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str()
+ );
+ */
+
+ if (g_BlockIsSolid[a_BlockType])
+ {
+ // The projectile hit a solid block
+ // Calculate the exact hit coords:
+ cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1);
+ Vector3d Line1 = m_Projectile->GetPosition();
+ Vector3d Line2 = Line1 + m_Projectile->GetSpeed();
+ double LineCoeff = 0;
+ char Face;
+ if (bb.CalcLineIntersection(Line1, Line2, LineCoeff, Face))
+ {
+ Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff;
+ m_Projectile->OnHitSolidBlock(Intersection, Face);
+ return true;
+ }
+ else
+ {
+ LOGD("WEIRD! block tracer reports a hit, but BBox tracer doesn't. Ignoring the hit.");
+ }
+ }
+
+ // Convey some special effects from special blocks:
+ switch (a_BlockType)
+ {
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ m_Projectile->StartBurning(30);
+ m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.9); // Slow down to 0.9* the speed each tick when moving through lava
+ break;
+ }
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ m_Projectile->StopBurning();
+ m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.8); // Slow down to 0.8* the speed each tick when moving through water
+ break;
+ }
+ } // switch (a_BlockType)
+
+ // Continue tracing
+ return false;
+ }
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProjectileEntityCollisionCallback:
+
+class cProjectileEntityCollisionCallback :
+ public cEntityCallback
+{
+public:
+ cProjectileEntityCollisionCallback(cProjectileEntity * a_Projectile, const Vector3d & a_Pos, const Vector3d & a_NextPos) :
+ m_Projectile(a_Projectile),
+ m_Pos(a_Pos),
+ m_NextPos(a_NextPos),
+ m_MinCoeff(1),
+ m_HitEntity(NULL)
+ {
+ }
+
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ if (
+ (a_Entity == m_Projectile) || // Do not check collisions with self
+ (a_Entity == m_Projectile->GetCreator()) // Do not check whoever shot the projectile
+ )
+ {
+ // TODO: Don't check creator only for the first 5 ticks
+ // so that arrows stuck in ground and dug up can hurt the player
+ return false;
+ }
+
+ cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight());
+
+ // Instead of colliding the bounding box with another bounding box in motion, we collide an enlarged bounding box with a hairline.
+ // The results should be good enough for our purposes
+ double LineCoeff;
+ char Face;
+ EntBox.Expand(m_Projectile->GetWidth() / 2, m_Projectile->GetHeight() / 2, m_Projectile->GetWidth() / 2);
+ if (!EntBox.CalcLineIntersection(m_Pos, m_NextPos, LineCoeff, Face))
+ {
+ // No intersection whatsoever
+ return false;
+ }
+
+ // TODO: Some entities don't interact with the projectiles (pickups, falling blocks)
+ // TODO: Allow plugins to interfere about which entities can be hit
+
+ if (LineCoeff < m_MinCoeff)
+ {
+ // The entity is closer than anything we've stored so far, replace it as the potential victim
+ m_MinCoeff = LineCoeff;
+ m_HitEntity = a_Entity;
+ }
+
+ // Don't break the enumeration, we want all the entities
+ return false;
+ }
+
+ /// Returns the nearest entity that was hit, after the enumeration has been completed
+ cEntity * GetHitEntity(void) const { return m_HitEntity; }
+
+ /// Returns the line coeff where the hit was encountered, after the enumeration has been completed
+ double GetMinCoeff(void) const { return m_MinCoeff; }
+
+ /// Returns true if the callback has encountered a true hit
+ bool HasHit(void) const { return (m_MinCoeff < 1); }
+
+protected:
+ cProjectileEntity * m_Projectile;
+ const Vector3d & m_Pos;
+ const Vector3d & m_NextPos;
+ double m_MinCoeff; // The coefficient of the nearest hit on the Pos line
+
+ // Although it's bad(tm) to store entity ptrs from a callback, we can afford it here, because the entire callback
+ // is processed inside the tick thread, so the entities won't be removed in between the calls and the final processing
+ cEntity * m_HitEntity; // The nearest hit entity
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProjectileEntity:
+
+cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) :
+ super(etProjectile, a_X, a_Y, a_Z, a_Width, a_Height),
+ m_ProjectileKind(a_Kind),
+ m_Creator(a_Creator),
+ m_IsInGround(false)
+{
+}
+
+
+
+
+
+cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) :
+ super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height),
+ m_ProjectileKind(a_Kind),
+ m_Creator(a_Creator),
+ m_IsInGround(false)
+{
+ SetSpeed(a_Speed);
+ SetRotationFromSpeed();
+ SetPitchFromSpeed();
+}
+
+
+
+
+
+cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed)
+{
+ Vector3d Speed;
+ if (a_Speed != NULL)
+ {
+ Speed = *a_Speed;
+ }
+
+ switch (a_Kind)
+ {
+ case pkArrow: return new cArrowEntity (a_Creator, a_X, a_Y, a_Z, Speed);
+ case pkEgg: return new cThrownEggEntity (a_Creator, a_X, a_Y, a_Z, Speed);
+ case pkEnderPearl: return new cThrownEnderPearlEntity(a_Creator, a_X, a_Y, a_Z, Speed);
+ case pkSnowball: return new cThrownSnowballEntity (a_Creator, a_X, a_Y, a_Z, Speed);
+ case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed);
+ case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed);
+ case pkExpBottle: return new cExpBottleEntity (a_Creator, a_X, a_Y, a_Z, Speed);
+ case pkFirework: return new cFireworkEntity (a_Creator, a_X, a_Y, a_Z );
+ // TODO: the rest
+ }
+
+ LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind);
+ return NULL;
+}
+
+
+
+
+
+void cProjectileEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+{
+ // Set the position based on what face was hit:
+ SetPosition(a_HitPos);
+ SetSpeed(0, 0, 0);
+
+ // DEBUG:
+ LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, hit solid block at face %d",
+ m_UniqueID,
+ a_HitPos.x, a_HitPos.y, a_HitPos.z,
+ a_HitFace
+ );
+
+ m_IsInGround = true;
+}
+
+
+
+
+
+AString cProjectileEntity::GetMCAClassName(void) const
+{
+ switch (m_ProjectileKind)
+ {
+ case pkArrow: return "Arrow";
+ case pkSnowball: return "Snowball";
+ case pkEgg: return "Egg";
+ case pkGhastFireball: return "Fireball";
+ case pkFireCharge: return "SmallFireball";
+ case pkEnderPearl: return "ThrownEnderPearl";
+ case pkExpBottle: return "ThrownExpBottle";
+ case pkSplashPotion: return "ThrownPotion";
+ case pkWitherSkull: return "WitherSkull";
+ case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this?
+ }
+ ASSERT(!"Unhandled projectile entity kind!");
+ return "";
+}
+
+
+
+
+
+void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if (GetProjectileKind() != pkArrow) // See cArrow::Tick
+ {
+ BroadcastMovementUpdate();
+ }
+}
+
+
+
+
+
+void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
+{
+ if (m_IsInGround)
+ {
+ // Already-grounded projectiles don't move at all
+ return;
+ }
+
+ Vector3d PerTickSpeed = GetSpeed() / 20;
+ Vector3d Pos = GetPosition();
+
+ // Trace the tick's worth of movement as a line:
+ Vector3d NextPos = Pos + PerTickSpeed;
+ cProjectileTracerCallback TracerCallback(this);
+ if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
+ {
+ // Something has been hit, abort all other processing
+ return;
+ }
+ // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff
+
+ // Test for entity collisions:
+ cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos);
+ a_Chunk.ForEachEntity(EntityCollisionCallback);
+ if (EntityCollisionCallback.HasHit())
+ {
+ // An entity was hit:
+ Vector3d HitPos = Pos + (NextPos - Pos) * EntityCollisionCallback.GetMinCoeff();
+
+ // DEBUG:
+ LOGD("Projectile %d has hit an entity %d (%s) at {%.02f, %.02f, %.02f} (coeff %.03f)",
+ m_UniqueID,
+ EntityCollisionCallback.GetHitEntity()->GetUniqueID(),
+ EntityCollisionCallback.GetHitEntity()->GetClass(),
+ HitPos.x, HitPos.y, HitPos.z,
+ EntityCollisionCallback.GetMinCoeff()
+ );
+
+ OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos);
+ }
+ // TODO: Test the entities in the neighboring chunks, too
+
+ // Update the position:
+ SetPosition(NextPos);
+
+ // Add slowdown and gravity effect to the speed:
+ Vector3d NewSpeed(GetSpeed());
+ NewSpeed.y += m_Gravity / 20;
+ NewSpeed *= TracerCallback.GetSlowdownCoeff();
+ SetSpeed(NewSpeed);
+ SetRotationFromSpeed();
+ SetPitchFromSpeed();
+
+ // DEBUG:
+ LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}",
+ m_UniqueID,
+ GetPosX(), GetPosY(), GetPosZ(),
+ GetSpeedX(), GetSpeedY(), GetSpeedZ(),
+ GetRotation(), GetPitch()
+ );
+}
+
+
+
+
+
+void cProjectileEntity::SpawnOn(cClientHandle & a_Client)
+{
+ // Default spawning - use the projectile kind to spawn an object:
+ a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetRotation()), ANGLE_TO_PROTO(GetPitch()));
+ a_Client.SendEntityMetadata(*this);
+}
+
+
+
+
+
+void cProjectileEntity::CollectedBy(cPlayer * a_Dest)
+{
+ // Overriden in arrow
+ UNUSED(a_Dest);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cArrowEntity:
+
+cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+ super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5),
+ m_PickupState(psNoPickup),
+ m_DamageCoeff(2),
+ m_IsCritical(false),
+ m_Timer(0),
+ m_bIsCollected(false),
+ m_HitBlockPos(Vector3i(0, 0, 0)),
+ m_HitGroundTimer(0)
+{
+ SetSpeed(a_Speed);
+ SetMass(0.1);
+ SetRotationFromSpeed();
+ SetPitchFromSpeed();
+ LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}",
+ m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(),
+ GetRotation(), GetPitch()
+ );
+}
+
+
+
+
+
+cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) :
+ super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5),
+ m_PickupState(psInSurvivalOrCreative),
+ m_DamageCoeff(2),
+ m_IsCritical((a_Force >= 1)),
+ m_Timer(0),
+ m_bIsCollected(false),
+ m_HitBlockPos(0, 0, 0),
+ m_HitGroundTimer(0)
+{
+}
+
+
+
+
+
+bool cArrowEntity::CanPickup(const cPlayer & a_Player) const
+{
+ switch (m_PickupState)
+ {
+ case psNoPickup: return false;
+ case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative());
+ case psInCreative: return a_Player.IsGameModeCreative();
+ }
+ ASSERT(!"Unhandled pickup state");
+ return false;
+}
+
+
+
+
+
+void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+{
+ if (a_HitFace == BLOCK_FACE_NONE) { return; }
+
+ super::OnHitSolidBlock(a_HitPos, a_HitFace);
+ int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z;
+
+ switch (a_HitFace)
+ {
+ case BLOCK_FACE_XM: // Strangely, bounding boxes / block tracers return the actual block for these two directions, so AddFace not needed
+ case BLOCK_FACE_YM:
+ {
+ break;
+ }
+ default: AddFaceDirection(a_X, a_Y, a_Z, a_HitFace, true);
+ }
+
+ m_HitBlockPos = Vector3i(a_X, a_Y, a_Z);
+
+ // Broadcast arrow hit sound
+ m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+}
+
+
+
+
+
+void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
+{
+ if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat())
+ {
+ // Not an entity that interacts with an arrow
+ return;
+ }
+
+ int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5);
+ if (m_IsCritical)
+ {
+ Damage += m_World->GetTickRandomNumber(Damage / 2 + 2);
+ }
+ a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
+
+ // Broadcast successful hit sound
+ m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+
+ Destroy();
+}
+
+
+
+
+
+void cArrowEntity::CollectedBy(cPlayer * a_Dest)
+{
+ if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest)))
+ {
+ int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW);
+ if (NumAdded > 0) // Only play effects if there was space in inventory
+ {
+ m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest);
+ // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
+ m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+ m_bIsCollected = true;
+ }
+ }
+}
+
+
+
+
+
+void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+ m_Timer += a_Dt;
+
+ if (m_bIsCollected)
+ {
+ if (m_Timer > 500.f) // 0.5 seconds
+ {
+ Destroy();
+ return;
+ }
+ }
+ else if (m_Timer > 1000 * 60 * 5) // 5 minutes
+ {
+ Destroy();
+ return;
+ }
+
+ if (m_IsInGround)
+ {
+ // When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL
+ // Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing)
+ // We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync
+ // Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position
+
+ if (m_HitGroundTimer != -1) // Sent a teleport already, don't do again
+ {
+ if (m_HitGroundTimer > 1000.f) // Send after a second, could be less, but just in case
+ {
+ m_World->BroadcastTeleportEntity(*this);
+ m_HitGroundTimer = -1;
+ }
+ else
+ {
+ m_HitGroundTimer += a_Dt;
+ }
+ }
+
+ int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width;
+ cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
+
+ if (Chunk == NULL)
+ {
+ // Inside an unloaded chunk, abort
+ return;
+ }
+
+ if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed?
+ {
+ m_IsInGround = false; // Yes, begin simulating physics again
+ }
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cThrownEggEntity:
+
+cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+ super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+{
+ SetSpeed(a_Speed);
+}
+
+
+
+
+
+void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+{
+ if (m_World->GetTickRandomNumber(7) == 1)
+ {
+ m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
+ }
+ else if (m_World->GetTickRandomNumber(32) == 1)
+ {
+ m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
+ m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
+ m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
+ m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
+ }
+ Destroy();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cThrownEnderPearlEntity :
+
+cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+ super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+{
+ SetSpeed(a_Speed);
+}
+
+
+
+
+
+void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+{
+ // Teleport the creator here, make them take 5 damage:
+ if (m_Creator != NULL)
+ {
+ // TODO: The coords might need some tweaking based on the block face
+ m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5);
+ m_Creator->TakeDamage(dtEnderPearl, this, 5, 0);
+ }
+
+ Destroy();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cThrownSnowballEntity :
+
+cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+ super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+{
+ SetSpeed(a_Speed);
+}
+
+
+
+
+
+void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+{
+ // TODO: Apply damage to certain mobs (blaze etc.) and anger all mobs
+
+ Destroy();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBottleOEnchantingEntity :
+
+cExpBottleEntity::cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+{
+ SetSpeed(a_Speed);
+}
+
+
+
+
+
+void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+{
+ // TODO: Spawn experience orbs
+
+ Destroy();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFireworkEntity :
+
+cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z) :
+super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+{
+}
+
+
+
+
+
+void cFireworkEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+{
+ if ((a_HitFace != BLOCK_FACE_BOTTOM) && (a_HitFace != BLOCK_FACE_NONE))
+ {
+ return;
+ }
+
+ SetSpeed(0, 0, 0);
+ SetPosition(GetPosX(), GetPosY() - 0.5, GetPosZ());
+
+ std::cout << a_HitPos.x << " " << a_HitPos.y << " " << a_HitPos.z << std::endl;
+
+ m_IsInGround = true;
+
+ BroadcastMovementUpdate();
+}
+
+
+
+
+
+void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
+{
+ if (m_IsInGround)
+ {
+ if (a_Chunk.GetBlock((int)GetPosX(), (int)GetPosY() + 1, (int)GetPosZ()) == E_BLOCK_AIR)
+ {
+ m_IsInGround = false;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ Vector3d PerTickSpeed = GetSpeed() / 20;
+ Vector3d Pos = GetPosition();
+
+ // Trace the tick's worth of movement as a line:
+ Vector3d NextPos = Pos + PerTickSpeed;
+ cProjectileTracerCallback TracerCallback(this);
+ if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
+ {
+ // Something has been hit, abort all other processing
+ return;
+ }
+ // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff
+
+ // Update the position:
+ SetPosition(NextPos);
+
+ // Add slowdown and gravity effect to the speed:
+ Vector3d NewSpeed(GetSpeed());
+ NewSpeed.y += 2;
+ NewSpeed *= TracerCallback.GetSlowdownCoeff();
+ SetSpeed(NewSpeed);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cGhastFireballEntity :
+
+cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+ super(pkGhastFireball, a_Creator, a_X, a_Y, a_Z, 1, 1)
+{
+ SetSpeed(a_Speed);
+ SetGravity(0);
+}
+
+
+
+
+
+void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ m_World->DoExplosionAt(1, a_BlockX, a_BlockY, a_BlockZ, true, esGhastFireball, this);
+}
+
+
+
+
+
+void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+{
+ Destroy();
+ Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
+}
+
+
+
+
+
+void cGhastFireballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
+{
+ Destroy();
+ Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFireChargeEntity :
+
+cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+ super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125)
+{
+ SetSpeed(a_Speed);
+ SetGravity(0);
+}
+
+
+
+
+
+void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR)
+ {
+ m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1);
+ }
+}
+
+
+
+
+
+void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+{
+ Destroy();
+ Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
+}
+
+
+
+
+
+void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
+{
+ Destroy();
+ Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
+
+ // TODO: Some entities are immune to hits
+ a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning
+}
+
+
+
+
diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h
new file mode 100644
index 000000000..959e81ae5
--- /dev/null
+++ b/src/Entities/ProjectileEntity.h
@@ -0,0 +1,382 @@
+
+// ProjectileEntity.h
+
+// Declares the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types
+
+
+
+
+
+#pragma once
+
+#include "Entity.h"
+
+
+
+
+
+// tolua_begin
+
+class cProjectileEntity :
+ public cEntity
+{
+ typedef cEntity super;
+
+public:
+ /// The kind of the projectile. The numbers correspond to the network type ID used for spawning via the 0x17 packet.
+ enum eKind
+ {
+ pkArrow = 60,
+ pkSnowball = 61,
+ pkEgg = 62,
+ pkGhastFireball = 63,
+ pkFireCharge = 64,
+ pkEnderPearl = 65,
+ pkExpBottle = 75,
+ pkSplashPotion = 73,
+ pkFirework = 76,
+ pkWitherSkull = 66,
+ pkFishingFloat = 90,
+ } ;
+
+ // tolua_end
+
+ CLASS_PROTODEF(cProjectileEntity);
+
+ cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
+ cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height);
+
+ static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed = NULL);
+
+ /// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace);
+
+ /// Called by the physics blocktracer when the entity hits another entity
+ virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) {}
+
+ /// Called by Chunk when the projectile is eligible for player collection
+ virtual void CollectedBy(cPlayer * a_Dest);
+
+ // tolua_begin
+
+ /// Returns the kind of the projectile (fast class identification)
+ eKind GetProjectileKind(void) const { return m_ProjectileKind; }
+
+ /// Returns the entity who created this projectile; may be NULL
+ cEntity * GetCreator(void) { return m_Creator; }
+
+ /// Returns the string that is used as the entity type (class name) in MCA files
+ AString GetMCAClassName(void) const;
+
+ /// Returns true if the projectile has hit the ground and is stuck there
+ bool IsInGround(void) const { return m_IsInGround; }
+
+ // tolua_end
+
+ /// Sets the internal InGround flag. To be used by MCA loader only!
+ void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; }
+
+protected:
+ eKind m_ProjectileKind;
+
+ /// The entity who has created this projectile; may be NULL (e. g. for dispensers)
+ cEntity * m_Creator;
+
+ /// True if the projectile has hit the ground and is stuck there
+ 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 SpawnOn(cClientHandle & a_Client) override;
+
+ // tolua_begin
+} ;
+
+
+
+
+
+class cArrowEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+ /// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field
+ enum ePickupState
+ {
+ psNoPickup = 0,
+ psInSurvivalOrCreative = 1,
+ psInCreative = 2,
+ } ;
+
+ // tolua_end
+
+ CLASS_PROTODEF(cArrowEntity);
+
+ /// Creates a new arrow with psNoPickup state and default damage modifier coeff
+ cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+ /// Creates a new arrow as shot by a player, initializes it from the player object
+ cArrowEntity(cPlayer & a_Player, double a_Force);
+
+ // tolua_begin
+
+ /// Returns whether the arrow can be picked up by players
+ ePickupState GetPickupState(void) const { return m_PickupState; }
+
+ /// Sets a new pickup state
+ void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; }
+
+ /// Returns the damage modifier coeff.
+ double GetDamageCoeff(void) const { return m_DamageCoeff; }
+
+ /// Sets the damage modifier coeff
+ void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; }
+
+ /// Returns true if the specified player can pick the arrow up
+ bool CanPickup(const cPlayer & a_Player) const;
+
+ /// Returns true if the arrow is set as critical
+ bool IsCritical(void) const { return m_IsCritical; }
+
+ /// Sets the IsCritical flag
+ void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; }
+
+ // tolua_end
+
+protected:
+
+ /// Determines when the arrow can be picked up by players
+ ePickupState m_PickupState;
+
+ /// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow
+ double m_DamageCoeff;
+
+ /// If true, the arrow deals more damage
+ bool m_IsCritical;
+
+ /// Timer for pickup collection animation or five minute timeout
+ float m_Timer;
+
+ /// Timer for client arrow position confirmation via TeleportEntity
+ float m_HitGroundTimer;
+
+ /// If true, the arrow is in the process of being collected - don't go to anyone else
+ bool m_bIsCollected;
+
+ /// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air
+ Vector3i m_HitBlockPos;
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char 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;
+
+ // tolua_begin
+} ;
+
+
+
+
+
+class cThrownEggEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cThrownEggEntity);
+
+ cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+protected:
+
+ // tolua_end
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+
+ // tolua_begin
+
+} ;
+
+
+
+
+
+class cThrownEnderPearlEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cThrownEnderPearlEntity);
+
+ cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+protected:
+
+ // tolua_end
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+
+ // tolua_begin
+
+} ;
+
+
+
+
+
+class cThrownSnowballEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cThrownSnowballEntity);
+
+ cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+protected:
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+
+ // tolua_begin
+
+} ;
+
+
+
+
+
+class cExpBottleEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cExpBottleEntity);
+
+ cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+protected:
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+
+ // tolua_begin
+
+};
+
+
+
+
+
+class cFireworkEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cFireworkEntity);
+
+ cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z);
+
+protected:
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+ virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
+
+ // tolua_begin
+
+};
+
+
+
+
+
+class cGhastFireballEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cGhastFireballEntity);
+
+ cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+protected:
+
+ void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+ virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
+
+ // TODO: Deflecting the fireballs by arrow- or sword- hits
+
+ // tolua_begin
+
+} ;
+
+
+
+
+
+class cFireChargeEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cFireChargeEntity);
+
+ cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+protected:
+
+ void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+ virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
+
+ // tolua_begin
+
+} ;
+
+
+
+
+// tolua_end
+
+
+
diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp
new file mode 100644
index 000000000..339107b2e
--- /dev/null
+++ b/src/Entities/TNTEntity.cpp
@@ -0,0 +1,62 @@
+#include "Globals.h"
+
+#include "TNTEntity.h"
+#include "../World.h"
+#include "../ClientHandle.h"
+
+
+
+
+
+cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec) :
+ super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98),
+ m_Counter(0),
+ m_MaxFuseTime(a_FuseTimeInSec)
+{
+}
+
+
+
+
+
+cTNTEntity::cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec) :
+ super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98),
+ m_Counter(0),
+ m_MaxFuseTime(a_FuseTimeInSec)
+{
+}
+
+
+
+
+void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT
+ m_bDirtyPosition = false;
+ m_bDirtySpeed = false;
+ m_bDirtyOrientation = false;
+ m_bDirtyHead = false;
+}
+
+
+
+
+
+void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+ BroadcastMovementUpdate();
+ float delta_time = a_Dt / 1000; // Convert miliseconds to seconds
+ m_Counter += delta_time;
+ if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM
+ {
+ Destroy(true);
+ LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ());
+ m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this);
+ return;
+ }
+}
+
+
+
+
diff --git a/src/Entities/TNTEntity.h b/src/Entities/TNTEntity.h
new file mode 100644
index 000000000..eb5040e8a
--- /dev/null
+++ b/src/Entities/TNTEntity.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+#include "Entity.h"
+
+
+
+
+
+class cTNTEntity :
+ public cEntity
+{
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(cTNTEntity);
+
+ cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec);
+ cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec);
+
+ // cEntity overrides:
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+protected:
+ double m_Counter; ///< How much time has elapsed since the object was created, in seconds
+ double m_MaxFuseTime; ///< How long the fuse is, in seconds
+};
+
+
+
+
diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp
new file mode 100644
index 000000000..887e4426d
--- /dev/null
+++ b/src/FastRandom.cpp
@@ -0,0 +1,174 @@
+
+// FastRandom.cpp
+
+// Implements the cFastRandom class representing a fast random number generator
+
+#include "Globals.h"
+#include <time.h>
+#include "FastRandom.h"
+
+
+
+
+
+#if 0 && defined(_DEBUG)
+// Self-test
+// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs,
+// and if it performs well in terms of distribution (checked by avg, expected to be in the range midpoint
+class cFastRandomTest
+{
+public:
+ cFastRandomTest(void)
+ {
+ TestInts();
+ TestFloats();
+ }
+
+
+ void TestInts(void)
+ {
+ printf("Testing ints...\n");
+ cFastRandom rnd;
+ int sum = 0;
+ const int BUCKETS = 8;
+ int Counts[BUCKETS];
+ memset(Counts, 0, sizeof(Counts));
+ const int ITER = 10000;
+ for (int i = 0; i < ITER; i++)
+ {
+ int v = rnd.NextInt(1000);
+ ASSERT(v >= 0);
+ ASSERT(v < 1000);
+ Counts[v % BUCKETS]++;
+ sum += v;
+ }
+ double avg = (double)sum / ITER;
+ printf("avg: %f\n", avg);
+ for (int i = 0; i < BUCKETS; i++)
+ {
+ printf(" bucket %d: %d\n", i, Counts[i]);
+ }
+ }
+
+
+ void TestFloats(void)
+ {
+ printf("Testing floats...\n");
+ cFastRandom rnd;
+ float sum = 0;
+ const int BUCKETS = 8;
+ int Counts[BUCKETS];
+ memset(Counts, 0, sizeof(Counts));
+ const int ITER = 10000;
+ for (int i = 0; i < ITER; i++)
+ {
+ float v = rnd.NextFloat(1000);
+ ASSERT(v >= 0);
+ ASSERT(v <= 1000);
+ Counts[((int)v) % BUCKETS]++;
+ sum += v;
+ }
+ sum = sum / ITER;
+ printf("avg: %f\n", sum);
+ for (int i = 0; i < BUCKETS; i++)
+ {
+ printf(" bucket %d: %d\n", i, Counts[i]);
+ }
+ }
+} g_Test;
+
+#endif
+
+
+
+
+
+
+int cFastRandom::m_SeedCounter = 0;
+
+
+
+
+
+cFastRandom::cFastRandom(void) :
+ m_Seed(m_SeedCounter++)
+{
+}
+
+
+
+
+
+int cFastRandom::NextInt(int a_Range)
+{
+ ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges
+ ASSERT(a_Range > 0);
+
+ // Make the m_Counter operations as minimal as possible, to emulate atomicity
+ int Counter = m_Counter++;
+
+ // Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function:
+ int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57;
+ n = (n << 13) ^ n;
+ n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+ return ((n / 11) % a_Range);
+}
+
+
+
+
+
+int cFastRandom::NextInt(int a_Range, int a_Salt)
+{
+ ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges
+ ASSERT(a_Range > 0);
+
+ // Make the m_Counter operations as minimal as possible, to emulate atomicity
+ int Counter = m_Counter++;
+
+ // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
+ int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57;
+ n = (n << 13) ^ n;
+ n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+ return ((n / 11) % a_Range);
+}
+
+
+
+
+
+float cFastRandom::NextFloat(float a_Range)
+{
+ // Make the m_Counter operations as minimal as possible, to emulate atomicity
+ int Counter = m_Counter++;
+
+ // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
+ int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57;
+ n = (n << 13) ^ n;
+ n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+
+ // Convert the integer into float with the specified range:
+ return (((float)n / (float)0x7fffffff) * a_Range);
+}
+
+
+
+
+
+float cFastRandom::NextFloat(float a_Range, int a_Salt)
+{
+ // Make the m_Counter operations as minimal as possible, to emulate atomicity
+ int Counter = m_Counter++;
+
+ // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
+ int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57;
+ n = (n << 13) ^ n;
+ n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+
+ // Convert the integer into float with the specified range:
+ return (((float)n / (float)0x7fffffff) * a_Range);
+}
+
+
+
+
diff --git a/src/FastRandom.h b/src/FastRandom.h
new file mode 100644
index 000000000..bf70822cf
--- /dev/null
+++ b/src/FastRandom.h
@@ -0,0 +1,57 @@
+
+// FastRandom.h
+
+// Declares the cFastRandom class representing a fast random number generator
+
+/*
+The cFastRandom aims to provide a very fast, although not very cryptographically secure, random generator.
+It is fast to instantiate, fast to query next, and partially multi-thread-safe.
+It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may
+yield duplicate numbers in that case.
+
+Internally, this works similar to cNoise's integral noise generation, with some predefined inputs: the seed is
+taken from a global counter and the random is calculated using a counter that is incremented on each use (hence
+the multi-thread duplication). Two alternatives exists for each function, one that takes a range parameter,
+and another that takes an additional "salt" parameter; this salt is used as an additional input to the random,
+in order to avoid multi-thread duplication. If two threads both use the class at the same time with different
+salts, the values they get will be different.
+*/
+
+
+
+
+
+#pragma once
+
+
+
+
+
+class cFastRandom
+{
+public:
+ cFastRandom(void);
+
+ /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M
+ int NextInt(int a_Range);
+
+ /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness
+ int NextInt(int a_Range, int a_Salt);
+
+ /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M
+ float NextFloat(float a_Range);
+
+ /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness
+ float NextFloat(float a_Range, int a_Salt);
+
+protected:
+ int m_Seed;
+ int m_Counter;
+
+ /// Counter that is used to initialize the seed, incremented for each object created
+ static int m_SeedCounter;
+} ;
+
+
+
+
diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp
new file mode 100644
index 000000000..2e2276981
--- /dev/null
+++ b/src/FurnaceRecipe.cpp
@@ -0,0 +1,255 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "FurnaceRecipe.h"
+#include "Item.h"
+
+#include <fstream>
+#include <sstream>
+
+
+
+
+
+typedef std::list< cFurnaceRecipe::Recipe > RecipeList;
+typedef std::list< cFurnaceRecipe::Fuel > FuelList;
+
+
+
+
+
+struct cFurnaceRecipe::sFurnaceRecipeState
+{
+ RecipeList Recipes;
+ FuelList Fuel;
+};
+
+
+
+
+
+cFurnaceRecipe::cFurnaceRecipe()
+ : m_pState( new sFurnaceRecipeState )
+{
+ ReloadRecipes();
+}
+
+
+
+
+
+cFurnaceRecipe::~cFurnaceRecipe()
+{
+ ClearRecipes();
+ delete m_pState;
+}
+
+
+
+
+
+void cFurnaceRecipe::ReloadRecipes(void)
+{
+ ClearRecipes();
+ LOGD("Loading furnace recipes...");
+
+ std::ifstream f;
+ char a_File[] = "furnace.txt";
+ f.open(a_File, std::ios::in);
+ std::string input;
+
+ if (!f.good())
+ {
+ f.close();
+ LOG("Could not open the furnace recipes file \"%s\"", a_File);
+ return;
+ }
+
+ // TODO: Replace this messy parse with a high-level-structured one (ReadLine / ProcessLine)
+ bool bSyntaxError = false;
+ while (f.good())
+ {
+ char c;
+
+ //////////////////////////////////////////////////////////////////////////
+ // comments
+ f >> c;
+ f.unget();
+ if( c == '#' )
+ {
+ while( f.good() && c != '\n' )
+ {
+ f.get( c );
+ }
+ continue;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Line breaks
+ f.get( c );
+ while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); }
+ if (f.eof())
+ {
+ break;
+ }
+ f.unget();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Check for fuel
+ f >> c;
+ if( c == '!' ) // It's fuel :)
+ {
+ // Read item
+ int IItemID = 0, IItemCount = 0, IItemHealth = 0;
+ f >> IItemID;
+ f >> c; if( c != ':' ) { bSyntaxError = true; break; }
+ f >> IItemCount;
+
+ // Optional health
+ f >> c;
+ if( c != ':' )
+ f.unget();
+ else
+ {
+ f >> IItemHealth;
+ }
+
+ // Burn time
+ int BurnTime;
+ f >> c; if( c != '=' ) { bSyntaxError = true; break; }
+ f >> BurnTime;
+
+ // Add to fuel list
+ Fuel F;
+ F.In = new cItem( (ENUM_ITEM_ID) IItemID, (char)IItemCount, (short)IItemHealth );
+ F.BurnTime = BurnTime;
+ m_pState->Fuel.push_back( F );
+ continue;
+ }
+ f.unget();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Read items
+ int IItemID = 0, IItemCount = 0, IItemHealth = 0;
+ f >> IItemID;
+ f >> c; if( c != ':' ) { bSyntaxError = true; break; }
+ f >> IItemCount;
+
+ // Optional health
+ f >> c;
+ if( c != ':' )
+ f.unget();
+ else
+ {
+ f >> IItemHealth;
+ }
+
+ int CookTime;
+ f >> c; if( c != '@' ) { bSyntaxError = true; break; }
+ f >> CookTime;
+
+ int OItemID = 0, OItemCount = 0, OItemHealth = 0;
+ f >> c; if( c != '=' ) { bSyntaxError = true; break; }
+ f >> OItemID;
+ f >> c; if( c != ':' ) { bSyntaxError = true; break; }
+ f >> OItemCount;
+
+ // Optional health
+ f >> c;
+ if( c != ':' )
+ f.unget();
+ else
+ {
+ f >> OItemHealth;
+ }
+
+ // Add to recipe list
+ Recipe R;
+ R.In = new cItem( (ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth );
+ R.Out = new cItem( (ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth );
+ R.CookTime = CookTime;
+ m_pState->Recipes.push_back( R );
+ }
+ if (bSyntaxError)
+ {
+ LOGERROR("ERROR: FurnaceRecipe, syntax error" );
+ }
+ LOG("Loaded %u furnace recipes and %u fuels", m_pState->Recipes.size(), m_pState->Fuel.size());
+}
+
+
+
+
+
+void cFurnaceRecipe::ClearRecipes(void)
+{
+ for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
+ {
+ Recipe R = *itr;
+ delete R.In;
+ delete R.Out;
+ }
+ m_pState->Recipes.clear();
+
+ for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
+ {
+ Fuel F = *itr;
+ delete F.In;
+ }
+ m_pState->Fuel.clear();
+}
+
+
+
+
+
+const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
+{
+ const Recipe * BestRecipe = 0;
+ for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
+ {
+ const Recipe & R = *itr;
+ if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount))
+ {
+ if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount))
+ {
+ continue;
+ }
+ else
+ {
+ BestRecipe = &R;
+ }
+ }
+ }
+ return BestRecipe;
+}
+
+
+
+
+
+int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const
+{
+ int BestFuel = 0;
+ for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
+ {
+ const Fuel & F = *itr;
+ if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount))
+ {
+ if (BestFuel > 0 && (BestFuel > F.BurnTime))
+ {
+ continue;
+ }
+ else
+ {
+ BestFuel = F.BurnTime;
+ }
+ }
+ }
+ return BestFuel;
+}
+
+
+
+
diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h
new file mode 100644
index 000000000..2f91e9bcb
--- /dev/null
+++ b/src/FurnaceRecipe.h
@@ -0,0 +1,50 @@
+
+#pragma once
+
+
+
+
+
+class cItem;
+
+
+
+
+
+class cFurnaceRecipe
+{
+public:
+ cFurnaceRecipe(void);
+ ~cFurnaceRecipe();
+
+ void ReloadRecipes(void);
+
+ struct Fuel
+ {
+ cItem * In;
+ int BurnTime; ///< How long this fuel burns, in ticks
+ };
+
+ struct Recipe
+ {
+ cItem * In;
+ cItem * Out;
+ int CookTime; ///< How long this recipe takes to smelt, in ticks
+ };
+
+ /// Returns a recipe for the specified input, NULL if no recipe found
+ const Recipe * GetRecipeFrom(const cItem & a_Ingredient) const;
+
+ /// Returns the amount of time that the specified fuel burns, in ticks
+ int GetBurnTime(const cItem & a_Fuel) const;
+
+private:
+ void ClearRecipes(void);
+
+ struct sFurnaceRecipeState;
+ sFurnaceRecipeState * m_pState;
+};
+
+
+
+
diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp
new file mode 100644
index 000000000..4dff50918
--- /dev/null
+++ b/src/Generating/BioGen.cpp
@@ -0,0 +1,707 @@
+
+// BioGen.cpp
+
+// Implements the various biome generators
+
+#include "Globals.h"
+#include "BioGen.h"
+#include "lib/inifile/iniFile.h"
+#include "../LinearUpscale.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBioGenConstant:
+
+void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
+ {
+ a_BiomeMap[i] = m_Biome;
+ }
+}
+
+
+
+
+
+void cBioGenConstant::InitializeBiomeGen(cIniFile & a_IniFile)
+{
+ AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "Plains");
+ m_Biome = StringToBiome(Biome);
+ if (m_Biome == -1)
+ {
+ LOGWARN("[Generator]::ConstantBiome value \"%s\" not recognized, using \"Plains\".", Biome.c_str());
+ m_Biome = biPlains;
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBioGenCache:
+
+cBioGenCache::cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize) :
+ m_BioGenToCache(a_BioGenToCache),
+ m_CacheSize(a_CacheSize),
+ m_CacheOrder(new int[a_CacheSize]),
+ m_CacheData(new sCacheData[a_CacheSize]),
+ m_NumHits(0),
+ m_NumMisses(0),
+ m_TotalChain(0)
+{
+ for (int i = 0; i < m_CacheSize; i++)
+ {
+ m_CacheOrder[i] = i;
+ m_CacheData[i].m_ChunkX = 0x7fffffff;
+ m_CacheData[i].m_ChunkZ = 0x7fffffff;
+ }
+}
+
+
+
+
+
+cBioGenCache::~cBioGenCache()
+{
+ delete[] m_CacheData;
+ delete[] m_CacheOrder;
+}
+
+
+
+
+
+void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ if (((m_NumHits + m_NumMisses) % 1024) == 10)
+ {
+ LOGD("BioGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
+ LOGD("BioGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits);
+ }
+
+ for (int i = 0; i < m_CacheSize; i++)
+ {
+ if (
+ (m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) ||
+ (m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ)
+ )
+ {
+ continue;
+ }
+ // Found it in the cache
+ int Idx = m_CacheOrder[i];
+
+ // Move to front:
+ for (int j = i; j > 0; j--)
+ {
+ m_CacheOrder[j] = m_CacheOrder[j - 1];
+ }
+ m_CacheOrder[0] = Idx;
+
+ // Use the cached data:
+ memcpy(a_BiomeMap, m_CacheData[Idx].m_BiomeMap, sizeof(a_BiomeMap));
+
+ m_NumHits++;
+ m_TotalChain += i;
+ return;
+ } // for i - cache
+
+ // Not in the cache:
+ m_NumMisses++;
+ m_BioGenToCache->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
+
+ // Insert it as the first item in the MRU order:
+ int Idx = m_CacheOrder[m_CacheSize - 1];
+ for (int i = m_CacheSize - 1; i > 0; i--)
+ {
+ m_CacheOrder[i] = m_CacheOrder[i - 1];
+ } // for i - m_CacheOrder[]
+ m_CacheOrder[0] = Idx;
+ memcpy(m_CacheData[Idx].m_BiomeMap, a_BiomeMap, sizeof(a_BiomeMap));
+ m_CacheData[Idx].m_ChunkX = a_ChunkX;
+ m_CacheData[Idx].m_ChunkZ = a_ChunkZ;
+}
+
+
+
+
+
+void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile)
+{
+ super::InitializeBiomeGen(a_IniFile);
+ m_BioGenToCache->InitializeBiomeGen(a_IniFile);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBiomeGenList:
+
+void cBiomeGenList::InitializeBiomes(const AString & a_Biomes)
+{
+ AStringVector Split = StringSplit(a_Biomes, ",");
+
+ // Convert each string in the list into biome:
+ for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr)
+ {
+ AStringVector Split2 = StringSplit(*itr, ":");
+ if (Split2.size() < 1)
+ {
+ continue;
+ }
+ int Count = 1;
+ if (Split2.size() >= 2)
+ {
+ Count = atol(Split2[1].c_str());
+ if (Count <= 0)
+ {
+ LOGWARNING("Cannot decode biome count: \"%s\"; using 1.", Split2[1].c_str());
+ Count = 1;
+ }
+ }
+ EMCSBiome Biome = StringToBiome(Split2[0]);
+ if (Biome != -1)
+ {
+ for (int i = 0; i < Count; i++)
+ {
+ m_Biomes.push_back(Biome);
+ }
+ }
+ else
+ {
+ LOGWARNING("Cannot decode biome name: \"%s\"; skipping", Split2[0].c_str());
+ }
+ } // for itr - Split[]
+ if (!m_Biomes.empty())
+ {
+ m_BiomesCount = (int)m_Biomes.size();
+ return;
+ }
+
+ // There were no biomes, add default biomes:
+ static EMCSBiome Biomes[] =
+ {
+ biOcean,
+ biPlains,
+ biDesert,
+ biExtremeHills,
+ biForest,
+ biTaiga,
+ biSwampland,
+ biRiver,
+ biFrozenOcean,
+ biFrozenRiver,
+ biIcePlains,
+ biIceMountains,
+ biMushroomIsland,
+ biMushroomShore,
+ biBeach,
+ biDesertHills,
+ biForestHills,
+ biTaigaHills,
+ biExtremeHillsEdge,
+ biJungle,
+ biJungleHills,
+ } ;
+ m_Biomes.reserve(ARRAYCOUNT(Biomes));
+ for (int i = 0; i < ARRAYCOUNT(Biomes); i++)
+ {
+ m_Biomes.push_back(Biomes[i]);
+ }
+ m_BiomesCount = (int)m_Biomes.size();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBioGenCheckerboard:
+
+void cBioGenCheckerboard::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int Base = cChunkDef::Width * a_ChunkZ + z;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int Add = cChunkDef::Width * a_ChunkX + x;
+ a_BiomeMap[x + cChunkDef::Width * z] = m_Biomes[(Base / m_BiomeSize + Add / m_BiomeSize) % m_BiomesCount];
+ }
+ }
+}
+
+
+
+
+
+void cBioGenCheckerboard::InitializeBiomeGen(cIniFile & a_IniFile)
+{
+ super::InitializeBiomeGen(a_IniFile);
+ AString Biomes = a_IniFile.GetValueSet ("Generator", "CheckerBoardBiomes", "");
+ m_BiomeSize = a_IniFile.GetValueSetI("Generator", "CheckerboardBiomeSize", 64);
+ m_BiomeSize = (m_BiomeSize < 8) ? 8 : m_BiomeSize;
+ InitializeBiomes(Biomes);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBioGenVoronoi :
+
+void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ int BaseZ = cChunkDef::Width * a_ChunkZ;
+ int BaseX = cChunkDef::Width * a_ChunkX;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int AbsoluteZ = BaseZ + z;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(BaseX + x, AbsoluteZ));
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cBioGenVoronoi::InitializeBiomeGen(cIniFile & a_IniFile)
+{
+ super::InitializeBiomeGen(a_IniFile);
+ m_CellSize = a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 64);
+ AString Biomes = a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", "");
+ InitializeBiomes(Biomes);
+}
+
+
+
+
+
+EMCSBiome cBioGenVoronoi::VoronoiBiome(int a_BlockX, int a_BlockZ)
+{
+ int CellX = a_BlockX / m_CellSize;
+ int CellZ = a_BlockZ / m_CellSize;
+
+ // Note that Noise values need to be divided by 8 to gain a uniform modulo-2^n distribution
+
+ // Get 5x5 neighboring cell seeds, compare distance to each. Return the biome in the minumim-distance cell
+ int MinDist = m_CellSize * m_CellSize * 16; // There has to be a cell closer than this
+ EMCSBiome res = biPlains; // Will be overriden
+ for (int x = CellX - 2; x <= CellX + 2; x++)
+ {
+ int BaseX = x * m_CellSize;
+ for (int z = CellZ - 2; z < CellZ + 2; z++)
+ {
+ int OffsetX = (m_Noise.IntNoise3DInt(x, 16 * x + 32 * z, z) / 8) % m_CellSize;
+ int OffsetZ = (m_Noise.IntNoise3DInt(x, 32 * x - 16 * z, z) / 8) % m_CellSize;
+ int SeedX = BaseX + OffsetX;
+ int SeedZ = z * m_CellSize + OffsetZ;
+
+ int Dist = (SeedX - a_BlockX) * (SeedX - a_BlockX) + (SeedZ - a_BlockZ) * (SeedZ - a_BlockZ);
+ if (Dist < MinDist)
+ {
+ MinDist = Dist;
+ res = m_Biomes[(m_Noise.IntNoise3DInt(x, x - z + 1000, z) / 8) % m_BiomesCount];
+ }
+ } // for z
+ } // for x
+
+ return res;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBioGenDistortedVoronoi:
+
+void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ int BaseZ = cChunkDef::Width * a_ChunkZ;
+ int BaseX = cChunkDef::Width * a_ChunkX;
+
+ // Distortions for linear interpolation:
+ int DistortX[cChunkDef::Width + 1][cChunkDef::Width + 1];
+ int DistortZ[cChunkDef::Width + 1][cChunkDef::Width + 1];
+ for (int x = 0; x <= 4; x++) for (int z = 0; z <= 4; z++)
+ {
+ Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z]);
+ }
+
+ LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4);
+ LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4);
+
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int AbsoluteZ = BaseZ + z;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ // Distort(BaseX + x, AbsoluteZ, DistX, DistZ);
+ cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(DistortX[x][z], DistortZ[x][z]));
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cBioGenDistortedVoronoi::InitializeBiomeGen(cIniFile & a_IniFile)
+{
+ // Do NOT call super::InitializeBiomeGen(), as it would try to read Voronoi params instead of DistortedVoronoi params
+ m_CellSize = a_IniFile.GetValueSetI("Generator", "DistortedVoronoiCellSize", 96);
+ AString Biomes = a_IniFile.GetValueSet ("Generator", "DistortedVoronoiBiomes", "");
+ InitializeBiomes(Biomes);
+}
+
+
+
+
+void cBioGenDistortedVoronoi::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ)
+{
+ double NoiseX = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 1000);
+ NoiseX += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 2000);
+ NoiseX += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 3000);
+ double NoiseZ = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 4000);
+ NoiseZ += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 5000);
+ NoiseZ += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 6000);
+
+ a_DistortedX = a_BlockX + (int)(m_CellSize * 0.5 * NoiseX);
+ a_DistortedZ = a_BlockZ + (int)(m_CellSize * 0.5 * NoiseZ);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBioGenMultiStepMap :
+
+cBioGenMultiStepMap::cBioGenMultiStepMap(int a_Seed) :
+ m_Noise1(a_Seed + 1000),
+ m_Noise2(a_Seed + 2000),
+ m_Noise3(a_Seed + 3000),
+ m_Noise4(a_Seed + 4000),
+ m_Noise5(a_Seed + 5000),
+ m_Noise6(a_Seed + 6000),
+ m_Seed(a_Seed),
+ m_OceanCellSize(384),
+ m_MushroomIslandSize(64),
+ m_RiverCellSize(384),
+ m_RiverWidthThreshold(0.125),
+ m_LandBiomesSize(1024)
+{
+}
+
+
+
+
+
+void cBioGenMultiStepMap::InitializeBiomeGen(cIniFile & a_IniFile)
+{
+ m_OceanCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapOceanCellSize", m_OceanCellSize);
+ m_MushroomIslandSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapMushroomIslandSize", m_MushroomIslandSize);
+ m_RiverCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapRiverCellSize", m_RiverCellSize);
+ m_RiverWidthThreshold = a_IniFile.GetValueSetF("Generator", "MultiStepMapRiverWidth", m_RiverWidthThreshold);
+ m_LandBiomesSize = (float)a_IniFile.GetValueSetI("Generator", "MultiStepMapLandBiomeSize", (int)m_LandBiomesSize);
+}
+
+
+
+
+
+void cBioGenMultiStepMap::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ DecideOceanLandMushroom(a_ChunkX, a_ChunkZ, a_BiomeMap);
+ AddRivers(a_ChunkX, a_ChunkZ, a_BiomeMap);
+ ApplyTemperatureHumidity(a_ChunkX, a_ChunkZ, a_BiomeMap);
+}
+
+
+
+
+
+void cBioGenMultiStepMap::DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ // Distorted Voronoi over 3 biomes, with mushroom having only a special occurence.
+
+ // Prepare a distortion lookup table, by distorting a 5x5 area and using that as 1:4 zoom (linear interpolate):
+ int BaseZ = cChunkDef::Width * a_ChunkZ;
+ int BaseX = cChunkDef::Width * a_ChunkX;
+ int DistortX[cChunkDef::Width + 1][cChunkDef::Width + 1];
+ int DistortZ[cChunkDef::Width + 1][cChunkDef::Width + 1];
+ int DistortSize = m_OceanCellSize / 2;
+ for (int x = 0; x <= 4; x++) for (int z = 0; z <= 4; z++)
+ {
+ Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z], DistortSize);
+ }
+ LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4);
+ LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4);
+
+ // Prepare a 9x9 area of neighboring cell seeds
+ // (assuming that 7x7 cell area is larger than a chunk being generated)
+ const int NEIGHBORHOOD_SIZE = 4; // How many seeds in each direction to check
+ int CellX = BaseX / m_OceanCellSize;
+ int CellZ = BaseZ / m_OceanCellSize;
+ int SeedX[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1];
+ int SeedZ[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1];
+ EMCSBiome SeedV[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1];
+ for (int xc = 0; xc < 2 * NEIGHBORHOOD_SIZE + 1; xc++)
+ {
+ int RealCellX = xc + CellX - NEIGHBORHOOD_SIZE;
+ int CellBlockX = RealCellX * m_OceanCellSize;
+ for (int zc = 0; zc < 2 * NEIGHBORHOOD_SIZE + 1; zc++)
+ {
+ int RealCellZ = zc + CellZ - NEIGHBORHOOD_SIZE;
+ int CellBlockZ = RealCellZ * m_OceanCellSize;
+ int OffsetX = (m_Noise2.IntNoise3DInt(RealCellX, 16 * RealCellX + 32 * RealCellZ, RealCellZ) / 8) % m_OceanCellSize;
+ int OffsetZ = (m_Noise4.IntNoise3DInt(RealCellX, 32 * RealCellX - 16 * RealCellZ, RealCellZ) / 8) % m_OceanCellSize;
+ SeedX[xc][zc] = CellBlockX + OffsetX;
+ SeedZ[xc][zc] = CellBlockZ + OffsetZ;
+ SeedV[xc][zc] = (((m_Noise6.IntNoise3DInt(RealCellX, RealCellX - RealCellZ + 1000, RealCellZ) / 11) % 256) > 90) ? biOcean : ((EMCSBiome)(-1));
+ } // for z
+ } // for x
+
+ for (int xc = 1; xc < 2 * NEIGHBORHOOD_SIZE; xc++) for (int zc = 1; zc < 2 * NEIGHBORHOOD_SIZE; zc++)
+ {
+ if (
+ (SeedV[xc ][zc] == biOcean) &&
+ (SeedV[xc - 1][zc] == biOcean) &&
+ (SeedV[xc + 1][zc] == biOcean) &&
+ (SeedV[xc ][zc - 1] == biOcean) &&
+ (SeedV[xc ][zc + 1] == biOcean) &&
+ (SeedV[xc - 1][zc - 1] == biOcean) &&
+ (SeedV[xc + 1][zc - 1] == biOcean) &&
+ (SeedV[xc - 1][zc + 1] == biOcean) &&
+ (SeedV[xc + 1][zc + 1] == biOcean)
+ )
+ {
+ SeedV[xc][zc] = biMushroomIsland;
+ }
+ }
+
+ // For each column find the nearest distorted cell and use its value as the biome:
+ int MushroomOceanThreshold = m_OceanCellSize * m_OceanCellSize * m_MushroomIslandSize / 1024;
+ int MushroomShoreThreshold = m_OceanCellSize * m_OceanCellSize * m_MushroomIslandSize / 2048;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int AbsoluteZ = DistortZ[x][z];
+ int AbsoluteX = DistortX[x][z];
+ int MinDist = m_OceanCellSize * m_OceanCellSize * 16; // There has to be a cell closer than this
+ EMCSBiome Biome = biPlains;
+ // Find the nearest cell seed:
+ for (int xs = 1; xs < 2 * NEIGHBORHOOD_SIZE; xs++) for (int zs = 1; zs < 2 * NEIGHBORHOOD_SIZE; zs++)
+ {
+ int Dist = (SeedX[xs][zs] - AbsoluteX) * (SeedX[xs][zs] - AbsoluteX) + (SeedZ[xs][zs] - AbsoluteZ) * (SeedZ[xs][zs] - AbsoluteZ);
+ if (Dist >= MinDist)
+ {
+ continue;
+ }
+ MinDist = Dist;
+ Biome = SeedV[xs][zs];
+ // Shrink mushroom biome and add a shore:
+ if (Biome == biMushroomIsland)
+ {
+ if (Dist > MushroomOceanThreshold)
+ {
+ Biome = biOcean;
+ }
+ else if (Dist > MushroomShoreThreshold)
+ {
+ Biome = biMushroomShore;
+ }
+ }
+ } // for zs, xs
+
+ cChunkDef::SetBiome(a_BiomeMap, x, z, Biome);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cBioGenMultiStepMap::AddRivers(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ float NoiseCoordZ = (float)(a_ChunkZ * cChunkDef::Width + z) / m_RiverCellSize;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ if (cChunkDef::GetBiome(a_BiomeMap, x, z) != -1)
+ {
+ // Biome already set, skip this column
+ continue;
+ }
+
+ float NoiseCoordX = (float)(a_ChunkX * cChunkDef::Width + x) / m_RiverCellSize;
+
+ double Noise = m_Noise1.CubicNoise2D( NoiseCoordX, NoiseCoordZ);
+ Noise += 0.5 * m_Noise3.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ);
+ Noise += 0.1 * m_Noise5.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ);
+
+ if ((Noise > 0) && (Noise < m_RiverWidthThreshold))
+ {
+ cChunkDef::SetBiome(a_BiomeMap, x, z, biRiver);
+ }
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cBioGenMultiStepMap::ApplyTemperatureHumidity(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ IntMap TemperatureMap;
+ IntMap HumidityMap;
+ BuildTemperatureHumidityMaps(a_ChunkX, a_ChunkZ, TemperatureMap, HumidityMap);
+
+ FreezeWaterBiomes(a_BiomeMap, TemperatureMap);
+ DecideLandBiomes(a_BiomeMap, TemperatureMap, HumidityMap);
+}
+
+
+
+
+
+void cBioGenMultiStepMap::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ, int a_CellSize)
+{
+ double NoiseX = m_Noise3.CubicNoise2D( (float)a_BlockX / a_CellSize, (float)a_BlockZ / a_CellSize);
+ NoiseX += 0.5 * m_Noise2.CubicNoise2D(2 * (float)a_BlockX / a_CellSize, 2 * (float)a_BlockZ / a_CellSize);
+ NoiseX += 0.1 * m_Noise1.CubicNoise2D(16 * (float)a_BlockX / a_CellSize, 16 * (float)a_BlockZ / a_CellSize);
+ double NoiseZ = m_Noise6.CubicNoise2D( (float)a_BlockX / a_CellSize, (float)a_BlockZ / a_CellSize);
+ NoiseZ += 0.5 * m_Noise5.CubicNoise2D(2 * (float)a_BlockX / a_CellSize, 2 * (float)a_BlockZ / a_CellSize);
+ NoiseZ += 0.1 * m_Noise4.CubicNoise2D(16 * (float)a_BlockX / a_CellSize, 16 * (float)a_BlockZ / a_CellSize);
+
+ a_DistortedX = a_BlockX + (int)(a_CellSize * 0.5 * NoiseX);
+ a_DistortedZ = a_BlockZ + (int)(a_CellSize * 0.5 * NoiseZ);
+}
+
+
+
+
+
+void cBioGenMultiStepMap::BuildTemperatureHumidityMaps(int a_ChunkX, int a_ChunkZ, IntMap & a_TemperatureMap, IntMap & a_HumidityMap)
+{
+ // Linear interpolation over 8x8 blocks; use double for better precision:
+ DblMap TemperatureMap;
+ DblMap HumidityMap;
+ for (int z = 0; z < 17; z += 8)
+ {
+ float NoiseCoordZ = (float)(a_ChunkZ * cChunkDef::Width + z) / m_LandBiomesSize;
+ for (int x = 0; x < 17; x += 8)
+ {
+ float NoiseCoordX = (float)(a_ChunkX * cChunkDef::Width + x) / m_LandBiomesSize;
+
+ double NoiseT = m_Noise1.CubicNoise2D( NoiseCoordX, NoiseCoordZ);
+ NoiseT += 0.5 * m_Noise2.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ);
+ NoiseT += 0.1 * m_Noise3.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ);
+ TemperatureMap[x + 17 * z] = NoiseT;
+
+ double NoiseH = m_Noise4.CubicNoise2D( NoiseCoordX, NoiseCoordZ);
+ NoiseH += 0.5 * m_Noise5.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ);
+ NoiseH += 0.1 * m_Noise6.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ);
+ HumidityMap[x + 17 * z] = NoiseH;
+ } // for x
+ } // for z
+ LinearUpscale2DArrayInPlace(TemperatureMap, 17, 17, 8, 8);
+ LinearUpscale2DArrayInPlace(HumidityMap, 17, 17, 8, 8);
+
+ // Re-map into integral values in [0 .. 255] range:
+ for (int idx = 0; idx < ARRAYCOUNT(a_TemperatureMap); idx++)
+ {
+ a_TemperatureMap[idx] = std::max(0, std::min(255, (int)(128 + TemperatureMap[idx] * 128)));
+ a_HumidityMap[idx] = std::max(0, std::min(255, (int)(128 + HumidityMap[idx] * 128)));
+ }
+}
+
+
+
+
+
+void cBioGenMultiStepMap::DecideLandBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap, const IntMap & a_HumidityMap)
+{
+ static const EMCSBiome BiomeMap[] =
+ {
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ /* 0 */ biTundra, biTundra, biTundra, biTundra, biPlains, biPlains, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesert, biDesert, biDesert, biDesert,
+ /* 1 */ biTundra, biTundra, biTundra, biTundra, biPlains, biPlains, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesert, biDesert, biDesert, biDesert,
+ /* 2 */ biTundra, biTundra, biTundra, biTundra, biPlains, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesertHills, biDesertHills, biDesert, biDesert,
+ /* 3 */ biTundra, biTundra, biTundra, biTundra, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesertHills, biDesertHills, biDesert, biDesert,
+ /* 4 */ biTundra, biTundra, biIceMountains, biIceMountains, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biForestHills, biForestHills, biExtremeHills, biExtremeHills, biDesertHills, biDesert,
+ /* 5 */ biTundra, biTundra, biIceMountains, biIceMountains, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biForestHills, biForestHills, biExtremeHills, biExtremeHills, biDesertHills, biDesert,
+ /* 6 */ biTundra, biTundra, biIceMountains, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains,
+ /* 7 */ biTundra, biTundra, biIceMountains, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains,
+ /* 8 */ biTundra, biTundra, biTaiga, biTaiga, biForest, biForest, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains,
+ /* 9 */ biTundra, biTundra, biTaiga, biTaiga, biForest, biForest, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains,
+ /* 10 */ biTaiga, biTaiga, biTaiga, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
+ /* 11 */ biTaiga, biTaiga, biIceMountains, biIceMountains, biExtremeHills, biForestHills, biForest, biForest, biForest, biForest, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
+ /* 12 */ biTaiga, biTaiga, biIceMountains, biIceMountains, biExtremeHills, biJungleHills, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
+ /* 13 */ biTaiga, biTaiga, biTaiga, biIceMountains, biJungleHills, biJungleHills, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
+ /* 14 */ biTaiga, biTaiga, biTaiga, biTaiga, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
+ /* 15 */ biTaiga, biTaiga, biTaiga, biTaiga, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland,
+ } ;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int idxZ = 17 * z;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ if (cChunkDef::GetBiome(a_BiomeMap, x, z) != -1)
+ {
+ // Already set before
+ continue;
+ }
+ int idx = idxZ + x;
+ int Temperature = a_TemperatureMap[idx] / 16; // -> [0..15] range
+ int Humidity = a_HumidityMap[idx] / 16; // -> [0..15] range
+ cChunkDef::SetBiome(a_BiomeMap, x, z, BiomeMap[Temperature + 16 * Humidity]);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cBioGenMultiStepMap::FreezeWaterBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap)
+{
+ int idx = 0;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++, idx++)
+ {
+ if (a_TemperatureMap[idx] > 4 * 16)
+ {
+ // Not frozen
+ continue;
+ }
+ switch (cChunkDef::GetBiome(a_BiomeMap, x, z))
+ {
+ case biRiver: cChunkDef::SetBiome(a_BiomeMap, x, z, biFrozenRiver); break;
+ case biOcean: cChunkDef::SetBiome(a_BiomeMap, x, z, biFrozenOcean); break;
+ }
+ } // for x
+ idx += 1;
+ } // for z
+}
+
+
+
+
diff --git a/src/Generating/BioGen.h b/src/Generating/BioGen.h
new file mode 100644
index 000000000..bc70bfab2
--- /dev/null
+++ b/src/Generating/BioGen.h
@@ -0,0 +1,230 @@
+
+// BioGen.h
+
+/*
+Interfaces to the various biome generators:
+ - cBioGenConstant
+ - cBioGenCheckerboard
+ - cBioGenDistortedVoronoi
+*/
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cBioGenConstant :
+ public cBiomeGen
+{
+public:
+ cBioGenConstant(void) : m_Biome(biPlains) {}
+
+protected:
+
+ EMCSBiome m_Biome;
+
+ // cBiomeGen overrides:
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
+/// A simple cache that stores N most recently generated chunks' biomes; N being settable upon creation
+class cBioGenCache :
+ public cBiomeGen
+{
+ typedef cBiomeGen super;
+
+public:
+ cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize); // Doesn't take ownership of a_BioGenToCache
+ ~cBioGenCache();
+
+protected:
+
+ cBiomeGen * m_BioGenToCache;
+
+ struct sCacheData
+ {
+ int m_ChunkX;
+ int m_ChunkZ;
+ cChunkDef::BiomeMap m_BiomeMap;
+ } ;
+
+ // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
+ int m_CacheSize;
+ int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
+ sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
+
+ // Cache statistics
+ int m_NumHits;
+ int m_NumMisses;
+ int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
+
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
+/// Base class for generators that use a list of available biomes. This class takes care of the list.
+class cBiomeGenList :
+ public cBiomeGen
+{
+ typedef cBiomeGen super;
+
+protected:
+ // List of biomes that the generator is allowed to generate:
+ typedef std::vector<EMCSBiome> EMCSBiomes;
+ EMCSBiomes m_Biomes;
+ int m_BiomesCount; // Pulled out of m_Biomes for faster access
+
+ /// Parses the INI file setting string into m_Biomes.
+ void InitializeBiomes(const AString & a_Biomes);
+} ;
+
+
+
+
+
+class cBioGenCheckerboard :
+ public cBiomeGenList
+{
+ typedef cBiomeGenList super;
+
+protected:
+ int m_BiomeSize;
+
+ // cBiomeGen overrides:
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
+class cBioGenVoronoi :
+ public cBiomeGenList
+{
+ typedef cBiomeGenList super;
+
+public:
+ cBioGenVoronoi(int a_Seed) :
+ m_Noise(a_Seed)
+ {
+ }
+
+protected:
+ int m_CellSize;
+
+ cNoise m_Noise;
+
+ // cBiomeGen overrides:
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
+
+ EMCSBiome VoronoiBiome(int a_BlockX, int a_BlockZ);
+} ;
+
+
+
+
+
+class cBioGenDistortedVoronoi :
+ public cBioGenVoronoi
+{
+ typedef cBioGenVoronoi super;
+public:
+ cBioGenDistortedVoronoi(int a_Seed) :
+ cBioGenVoronoi(a_Seed)
+ {
+ }
+
+protected:
+ // cBiomeGen overrides:
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
+
+ /// Distorts the coords using a Perlin-like noise
+ void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ);
+} ;
+
+
+
+
+
+class cBioGenMultiStepMap :
+ public cBiomeGen
+{
+ typedef cBiomeGen super;
+
+public:
+ cBioGenMultiStepMap(int a_Seed);
+
+protected:
+ // Noises used for composing the perlin-noise:
+ cNoise m_Noise1;
+ cNoise m_Noise2;
+ cNoise m_Noise3;
+ cNoise m_Noise4;
+ cNoise m_Noise5;
+ cNoise m_Noise6;
+
+ int m_Seed;
+ int m_OceanCellSize;
+ int m_MushroomIslandSize;
+ int m_RiverCellSize;
+ double m_RiverWidthThreshold;
+ float m_LandBiomesSize;
+
+ typedef int IntMap[17 * 17]; // x + 17 * z, expected trimmed into [0..255] range
+ typedef double DblMap[17 * 17]; // x + 17 * z, expected trimmed into [0..1] range
+
+ // cBiomeGen overrides:
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
+
+ /** Step 1: Decides between ocean, land and mushroom, using a DistVoronoi with special conditions and post-processing for mushroom islands
+ Sets biomes to biOcean, -1 (i.e. land), biMushroomIsland or biMushroomShore
+ */
+ void DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
+
+ /** Step 2: Add rivers to the land
+ Flips some "-1" biomes into biRiver
+ */
+ void AddRivers(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
+
+ /** Step 3: Decide land biomes using a temperature / humidity map; freeze ocean / river in low temperatures.
+ Flips all remaining "-1" biomes into land biomes. Also flips some biOcean and biRiver into biFrozenOcean, biFrozenRiver, based on temp map.
+ */
+ void ApplyTemperatureHumidity(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
+
+ /// Distorts the coords using a Perlin-like noise, with a specified cell-size
+ void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ, int a_CellSize);
+
+ /// Builds two Perlin-noise maps, one for temperature, the other for humidity. Trims both into [0..255] range
+ void BuildTemperatureHumidityMaps(int a_ChunkX, int a_ChunkZ, IntMap & a_TemperatureMap, IntMap & a_HumidityMap);
+
+ /// Flips all remaining "-1" biomes into land biomes using the two maps
+ void DecideLandBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap, const IntMap & a_HumidityMap);
+
+ /// Flips biOcean and biRiver into biFrozenOcean and biFrozenRiver if the temperature is too low
+ void FreezeWaterBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap);
+} ;
+
+
+
+
diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp
new file mode 100644
index 000000000..df45bb4c2
--- /dev/null
+++ b/src/Generating/Caves.cpp
@@ -0,0 +1,968 @@
+
+// Caves.cpp
+
+// Implements the various cave structure generators:
+// - cStructGenWormNestCaves
+// - cStructGenDualRidgeCaves
+// - cStructGenMarbleCaves
+// - cStructGenNetherCaves
+
+/*
+WormNestCave generator:
+Caves are generated in "nests" - groups of tunnels generated from a single point.
+For each chunk, all the nests that could intersect it are generated.
+For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...)
+Then each tunnel is randomized by inserting points in between its ends.
+Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other.
+When the tunnels are ready, they are simply carved into the chunk, one by one.
+To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it.
+
+MarbleCaves generator:
+For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept.
+Problem with this is the amount of CPU work needed for each chunk.
+Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground.
+
+DualRidgeCaves generator:
+Instead of evaluating a single noise function, two different noise functions are multiplied. This produces
+regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the noise functions need to be
+reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best.
+*/
+
+#include "Globals.h"
+#include "Caves.h"
+
+
+
+
+
+/// How many nests in each direction are generated for a given chunk. Must be an even number
+#define NEIGHBORHOOD_SIZE 8
+
+
+
+
+
+const int MIN_RADIUS = 3;
+const int MAX_RADIUS = 8;
+
+
+
+
+
+struct cCaveDefPoint
+{
+ int m_BlockX;
+ int m_BlockY;
+ int m_BlockZ;
+ int m_Radius;
+
+ cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) :
+ m_BlockX(a_BlockX),
+ m_BlockY(a_BlockY),
+ m_BlockZ(a_BlockZ),
+ m_Radius(a_Radius)
+ {
+ }
+} ;
+
+typedef std::vector<cCaveDefPoint> cCaveDefPoints;
+
+
+
+
+
+/// A single non-branching tunnel of a WormNestCave
+class cCaveTunnel
+{
+ // The bounding box, including the radii around defpoints:
+ int m_MinBlockX, m_MaxBlockX;
+ int m_MinBlockY, m_MaxBlockY;
+ int m_MinBlockZ, m_MaxBlockZ;
+
+ /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise
+ void Randomize(cNoise & a_Noise);
+
+ /// Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (segments too short)
+ bool RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst);
+
+ /// Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true
+ void Smooth(void);
+
+ /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block
+ void FinishLinear(void);
+
+ /// Calculates the bounding box of the points present
+ void CalcBoundingBox(void);
+
+public:
+ cCaveDefPoints m_Points;
+
+ cCaveTunnel(
+ int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius,
+ int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius,
+ cNoise & a_Noise
+ );
+
+ /// Carves the tunnel into the chunk specified
+ void ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+ );
+
+ #ifdef _DEBUG
+ AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
+ #endif // _DEBUG
+} ;
+
+typedef std::vector<cCaveTunnel *> cCaveTunnels;
+
+
+
+
+
+/// A collection of connected tunnels, possibly branching.
+class cStructGenWormNestCaves::cCaveSystem
+{
+public:
+ // The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk()
+ int m_BlockX;
+ int m_BlockZ;
+
+ cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise);
+ ~cCaveSystem();
+
+ /// Carves the cave system into the chunk specified
+ void ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+ );
+
+ #ifdef _DEBUG
+ AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
+ #endif // _DEBUG
+
+protected:
+ int m_Size;
+ cCaveTunnels m_Tunnels;
+
+ void Clear(void);
+
+ /// Generates a_Segment successive tunnels, with possible branches. Generates the same output for the same [x, y, z, a_Segments]
+ void GenerateTunnelsFromPoint(
+ int a_OriginX, int a_OriginY, int a_OriginZ,
+ cNoise & a_Noise, int a_Segments
+ );
+
+ /// Returns a radius based on the location provided.
+ int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ);
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCaveTunnel:
+
+cCaveTunnel::cCaveTunnel(
+ int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius,
+ int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius,
+ cNoise & a_Noise
+)
+{
+ m_Points.push_back(cCaveDefPoint(a_BlockStartX, a_BlockStartY, a_BlockStartZ, a_StartRadius));
+ m_Points.push_back(cCaveDefPoint(a_BlockEndX, a_BlockEndY, a_BlockEndZ, a_EndRadius));
+
+ if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0))
+ {
+ // Don't bother detailing this cave, it's under the world anyway
+ return;
+ }
+
+ Randomize(a_Noise);
+ Smooth();
+
+ // We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data:
+ CalcBoundingBox();
+
+ FinishLinear();
+}
+
+
+
+
+
+void cCaveTunnel::Randomize(cNoise & a_Noise)
+{
+ // Repeat 4 times:
+ for (int i = 0; i < 4; i++)
+ {
+ // For each already present point, insert a point in between it and its predecessor, shifted randomly.
+ int PrevX = m_Points.front().m_BlockX;
+ int PrevY = m_Points.front().m_BlockY;
+ int PrevZ = m_Points.front().m_BlockZ;
+ int PrevR = m_Points.front().m_Radius;
+ cCaveDefPoints Pts;
+ Pts.reserve(m_Points.size() * 2 + 1);
+ Pts.push_back(m_Points.front());
+ for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
+ {
+ int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11;
+ int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX);
+ len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY);
+ len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ);
+ len = 3 * (int)sqrt((double)len) / 4;
+ int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1));
+ Random /= 4;
+ int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2);
+ Random /= 256;
+ int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4);
+ Random /= 256;
+ int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2);
+ Pts.push_back(cCaveDefPoint(x, y, z, Rad));
+ Pts.push_back(*itr);
+ PrevX = itr->m_BlockX;
+ PrevY = itr->m_BlockY;
+ PrevZ = itr->m_BlockZ;
+ PrevR = itr->m_Radius;
+ }
+ std::swap(Pts, m_Points);
+ }
+}
+
+
+
+
+
+bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst)
+{
+ // Smoothing: for each line segment, add points on its 1/4 lengths
+ bool res = false;
+ int Num = a_Src.size() - 2; // this many intermediary points
+ a_Dst.clear();
+ a_Dst.reserve(Num * 2 + 2);
+ cCaveDefPoints::const_iterator itr = a_Src.begin() + 1;
+ a_Dst.push_back(a_Src.front());
+ int PrevX = a_Src.front().m_BlockX;
+ int PrevY = a_Src.front().m_BlockY;
+ int PrevZ = a_Src.front().m_BlockZ;
+ int PrevR = a_Src.front().m_Radius;
+ for (int i = 0; i <= Num; ++i, ++itr)
+ {
+ int dx = itr->m_BlockX - PrevX;
+ int dy = itr->m_BlockY - PrevY;
+ int dz = itr->m_BlockZ - PrevZ;
+ if (abs(dx) + abs(dz) + abs(dy) < 6)
+ {
+ // Too short a segment to smooth-subdivide into quarters
+ PrevX = itr->m_BlockX;
+ PrevY = itr->m_BlockY;
+ PrevZ = itr->m_BlockZ;
+ PrevR = itr->m_Radius;
+ continue;
+ }
+ int dr = itr->m_Radius - PrevR;
+ int Rad1 = std::max(PrevR + 1 * dr / 4, 1);
+ int Rad2 = std::max(PrevR + 3 * dr / 4, 1);
+ a_Dst.push_back(cCaveDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1));
+ a_Dst.push_back(cCaveDefPoint(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2));
+ PrevX = itr->m_BlockX;
+ PrevY = itr->m_BlockY;
+ PrevZ = itr->m_BlockZ;
+ PrevR = itr->m_Radius;
+ res = true;
+ }
+ a_Dst.push_back(a_Src.back());
+ return res && (a_Src.size() < a_Dst.size());
+}
+
+
+
+
+
+void cCaveTunnel::Smooth(void)
+{
+ cCaveDefPoints Pts;
+ while (true)
+ {
+ if (!RefineDefPoints(m_Points, Pts))
+ {
+ std::swap(Pts, m_Points);
+ return;
+ }
+ if (!RefineDefPoints(Pts, m_Points))
+ {
+ return;
+ }
+ }
+}
+
+
+
+
+
+void cCaveTunnel::FinishLinear(void)
+{
+ // For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints
+ cCaveDefPoints Pts;
+ std::swap(Pts, m_Points);
+
+ m_Points.reserve(Pts.size() * 3);
+ int PrevX = Pts.front().m_BlockX;
+ int PrevY = Pts.front().m_BlockY;
+ int PrevZ = Pts.front().m_BlockZ;
+ for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
+ {
+ int x1 = itr->m_BlockX;
+ int y1 = itr->m_BlockY;
+ int z1 = itr->m_BlockZ;
+ int dx = abs(x1 - PrevX);
+ int dy = abs(y1 - PrevY);
+ int dz = abs(z1 - PrevZ);
+ int sx = (PrevX < x1) ? 1 : -1;
+ int sy = (PrevY < y1) ? 1 : -1;
+ int sz = (PrevZ < z1) ? 1 : -1;
+ int R = itr->m_Radius;
+
+ if (dx >= std::max(dy, dz)) // x dominant
+ {
+ int yd = dy - dx / 2;
+ int zd = dz - dx / 2;
+
+ while (true)
+ {
+ m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
+
+ if (PrevX == x1)
+ {
+ break;
+ }
+
+ if (yd >= 0) // move along y
+ {
+ PrevY += sy;
+ yd -= dx;
+ }
+
+ if (zd >= 0) // move along z
+ {
+ PrevZ += sz;
+ zd -= dx;
+ }
+
+ // move along x
+ PrevX += sx;
+ yd += dy;
+ zd += dz;
+ }
+ }
+ else if (dy >= std::max(dx, dz)) // y dominant
+ {
+ int xd = dx - dy / 2;
+ int zd = dz - dy / 2;
+
+ while (true)
+ {
+ m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
+
+ if (PrevY == y1)
+ {
+ break;
+ }
+
+ if (xd >= 0) // move along x
+ {
+ PrevX += sx;
+ xd -= dy;
+ }
+
+ if (zd >= 0) // move along z
+ {
+ PrevZ += sz;
+ zd -= dy;
+ }
+
+ // move along y
+ PrevY += sy;
+ xd += dx;
+ zd += dz;
+ }
+ }
+ else
+ {
+ // z dominant
+ ASSERT(dz >= std::max(dx, dy));
+ int xd = dx - dz / 2;
+ int yd = dy - dz / 2;
+
+ while (true)
+ {
+ m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
+
+ if (PrevZ == z1)
+ {
+ break;
+ }
+
+ if (xd >= 0) // move along x
+ {
+ PrevX += sx;
+ xd -= dz;
+ }
+
+ if (yd >= 0) // move along y
+ {
+ PrevY += sy;
+ yd -= dz;
+ }
+
+ // move along z
+ PrevZ += sz;
+ xd += dx;
+ yd += dy;
+ }
+ } // if (which dimension is dominant)
+ } // for itr
+}
+
+
+
+
+
+void cCaveTunnel::CalcBoundingBox(void)
+{
+ m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX;
+ m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY;
+ m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ;
+ for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
+ {
+ m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius);
+ m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius);
+ m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius);
+ m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius);
+ m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius);
+ m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius);
+ } // for itr - m_Points[]
+}
+
+
+
+
+
+void cCaveTunnel::ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+)
+{
+ int BaseX = a_ChunkX * cChunkDef::Width;
+ int BaseZ = a_ChunkZ * cChunkDef::Width;
+ if (
+ (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) ||
+ (BaseZ > m_MaxBlockZ) || (BaseZ + cChunkDef::Width < m_MinBlockZ)
+ )
+ {
+ // Tunnel does not intersect the chunk at all, bail out
+ return;
+ }
+
+ int BlockStartX = a_ChunkX * cChunkDef::Width;
+ int BlockStartZ = a_ChunkZ * cChunkDef::Width;
+ int BlockEndX = BlockStartX + cChunkDef::Width;
+ int BlockEndZ = BlockStartZ + cChunkDef::Width;
+ for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
+ {
+ if (
+ (itr->m_BlockX + itr->m_Radius < BlockStartX) ||
+ (itr->m_BlockX - itr->m_Radius > BlockEndX) ||
+ (itr->m_BlockZ + itr->m_Radius < BlockStartZ) ||
+ (itr->m_BlockZ - itr->m_Radius > BlockEndZ)
+ )
+ {
+ // Cannot intersect, bail out early
+ continue;
+ }
+
+ // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom:
+ int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
+ int DifY = itr->m_BlockY;
+ int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
+ int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1);
+ int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, (int)(cChunkDef::Height));
+ int SqRad = itr->m_Radius * itr->m_Radius;
+ for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int y = Bottom; y <= Top; y++)
+ {
+ int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z);
+ if (4 * SqDist <= SqRad)
+ {
+ switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z))
+ {
+ // Only carve out these specific block types
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_STONE:
+ case E_BLOCK_COBBLESTONE:
+ case E_BLOCK_GRAVEL:
+ case E_BLOCK_SAND:
+ case E_BLOCK_SANDSTONE:
+ case E_BLOCK_NETHERRACK:
+ case E_BLOCK_COAL_ORE:
+ case E_BLOCK_IRON_ORE:
+ case E_BLOCK_GOLD_ORE:
+ case E_BLOCK_DIAMOND_ORE:
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_REDSTONE_ORE_GLOWING:
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
+ break;
+ }
+ default: break;
+ }
+ }
+ } // for y
+ } // for x, z
+ } // for itr - m_Points[]
+
+ /*
+ #ifdef _DEBUG
+ // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave:
+ for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
+ {
+ int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
+ int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
+ if (
+ (DifX >= 0) && (DifX < cChunkDef::Width) &&
+ (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) &&
+ (DifZ >= 0) && (DifZ < cChunkDef::Width)
+ )
+ {
+ cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE);
+ }
+ } // for itr - m_Points[]
+ #endif // _DEBUG
+ //*/
+}
+
+
+
+
+
+#ifdef _DEBUG
+AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
+{
+ AString SVG;
+ SVG.reserve(m_Points.size() * 20 + 200);
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color);
+ char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L"
+ for (cCaveDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr)
+ {
+ AppendPrintf(SVG, "%c %d,%d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ);
+ Prefix = 'L';
+ }
+ SVG.append("\"/>\n");
+ return SVG;
+}
+#endif // _DEBUG
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenWormNestCaves::cCaveSystem:
+
+cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) :
+ m_BlockX(a_BlockX),
+ m_BlockZ(a_BlockZ),
+ m_Size(a_Size)
+{
+ int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3;
+ for (int i = 0; i < Num; i++)
+ {
+ int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset;
+ int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset;
+ int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20;
+
+ // Generate three branches from the origin point:
+ // The tunnels generated depend on X, Y, Z and Branches,
+ // for the same set of numbers it generates the same offsets!
+ // That's why we add a +1 to X in the third line
+ GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3);
+ GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2);
+ GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3);
+ }
+}
+
+
+
+
+
+cStructGenWormNestCaves::cCaveSystem::~cCaveSystem()
+{
+ Clear();
+}
+
+
+
+
+
+
+void cStructGenWormNestCaves::cCaveSystem::ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+)
+{
+ for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
+ {
+ (*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap);
+ } // for itr - m_Tunnels[]
+}
+
+
+
+
+
+#ifdef _DEBUG
+AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
+{
+ AString SVG;
+ SVG.reserve(512 * 1024);
+ for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
+ {
+ SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ));
+ } // for itr - m_Tunnels[]
+
+ // Base point highlight:
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
+ );
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
+ );
+
+ // A gray line from the base point to the first point of the ravine, for identification:
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ,
+ a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX,
+ a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ
+ );
+
+ // Offset guides:
+ if (a_OffsetX > 0)
+ {
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n",
+ a_OffsetX, a_OffsetX
+ );
+ }
+ if (a_OffsetZ > 0)
+ {
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n",
+ a_OffsetZ, a_OffsetZ
+ );
+ }
+
+ return SVG;
+}
+#endif // _DEBUG
+
+
+
+
+
+void cStructGenWormNestCaves::cCaveSystem::Clear(void)
+{
+ for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ m_Tunnels.clear();
+}
+
+
+
+
+
+void cStructGenWormNestCaves::cCaveSystem::GenerateTunnelsFromPoint(
+ int a_OriginX, int a_OriginY, int a_OriginZ,
+ cNoise & a_Noise, int a_NumSegments
+)
+{
+ int DoubleSize = m_Size * 2;
+ int Radius = GetRadius(a_Noise, a_OriginX + a_OriginY, a_OriginY + a_OriginZ, a_OriginZ + a_OriginX);
+ for (int i = a_NumSegments - 1; i >= 0; --i)
+ {
+ int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2;
+ int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4;
+ int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2;
+ int EndR = GetRadius(a_Noise, a_OriginX + 7 * i, a_OriginY + 11 * i, a_OriginZ + a_OriginX);
+ m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, Radius, EndX, EndY, EndZ, EndR, a_Noise));
+ GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i);
+ a_OriginX = EndX;
+ a_OriginY = EndY;
+ a_OriginZ = EndZ;
+ Radius = EndR;
+ } // for i - a_NumSegments
+}
+
+
+
+
+
+int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ)
+{
+ // Instead of a flat distribution noise function, we need to shape it, so that most caves are smallish and only a few select are large
+ int rnd = a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ) / 11;
+ /*
+ // Not good enough:
+ // The algorithm of choice: emulate gauss-distribution noise by adding 3 flat noises, then fold it in half using absolute value.
+ // To save on processing, use one random value and extract 3 bytes to be separately added as the gaussian noise
+ int sum = (rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff);
+ // sum is now a gaussian-distribution noise within [0 .. 767], with center at 384.
+ // We want mapping 384 -> 3, 0 -> 19, 768 -> 19, so divide by 24 to get [0 .. 31] with center at 16, then use abs() to fold around the center
+ int res = 3 + abs((sum / 24) - 16);
+ */
+
+ // Algorithm of choice: random value in the range of zero to random value - heavily towards zero
+ int res = MIN_RADIUS + (rnd >> 8) % ((rnd % (MAX_RADIUS - MIN_RADIUS)) + 1);
+ return res;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenWormNestCaves:
+
+cStructGenWormNestCaves::~cStructGenWormNestCaves()
+{
+ ClearCache();
+}
+
+
+
+
+
+void cStructGenWormNestCaves::ClearCache(void)
+{
+ for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ } // for itr - m_Cache[]
+ m_Cache.clear();
+}
+
+
+
+
+
+void cStructGenWormNestCaves::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+ cCaveSystems Caves;
+ GetCavesForChunk(ChunkX, ChunkZ, Caves);
+ for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr)
+ {
+ (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
+ } // for itr - Caves[]
+}
+
+
+
+
+
+void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves)
+{
+ int BaseX = a_ChunkX * cChunkDef::Width / m_Grid;
+ int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid;
+ if (BaseX < 0)
+ {
+ --BaseX;
+ }
+ if (BaseZ < 0)
+ {
+ --BaseZ;
+ }
+ BaseX -= NEIGHBORHOOD_SIZE / 2;
+ BaseZ -= NEIGHBORHOOD_SIZE / 2;
+
+ // Walk the cache, move each cave system that we want into a_Caves:
+ int StartX = BaseX * m_Grid;
+ int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid;
+ int StartZ = BaseZ * m_Grid;
+ int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid;
+ for (cCaveSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
+ {
+ if (
+ ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
+ ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
+ )
+ {
+ // want
+ a_Caves.push_back(*itr);
+ itr = m_Cache.erase(itr);
+ }
+ else
+ {
+ // don't want
+ ++itr;
+ }
+ } // for itr - m_Cache[]
+
+ for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
+ {
+ int RealX = (BaseX + x) * m_Grid;
+ for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
+ {
+ int RealZ = (BaseZ + z) * m_Grid;
+ bool Found = false;
+ for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
+ {
+ if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
+ {
+ Found = true;
+ break;
+ }
+ }
+ if (!Found)
+ {
+ a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise));
+ }
+ }
+ }
+
+ // Copy a_Caves into m_Cache to the beginning:
+ cCaveSystems CavesCopy(a_Caves);
+ m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end());
+
+ // Trim the cache if it's too long:
+ if (m_Cache.size() > 100)
+ {
+ cCaveSystems::iterator itr = m_Cache.begin();
+ std::advance(itr, 100);
+ for (cCaveSystems::iterator end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ itr = m_Cache.begin();
+ std::advance(itr, 100);
+ m_Cache.erase(itr, m_Cache.end());
+ }
+
+ /*
+ // Uncomment this block for debugging the caves' shapes in 2D using an SVG export
+ #ifdef _DEBUG
+ AString SVG;
+ SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
+ SVG.reserve(2 * 1024 * 1024);
+ for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
+ {
+ int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid);
+ Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid);
+ SVG.append((*itr)->ExportAsSVG(Color, 512, 512));
+ }
+ SVG.append("</svg>\n");
+
+ AString fnam;
+ Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
+ cFile File(fnam, cFile::fmWrite);
+ File.Write(SVG.c_str(), SVG.size());
+ #endif // _DEBUG
+ //*/
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenMarbleCaves:
+
+static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise )
+{
+ static const float PI_2 = 1.57079633f;
+ float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f )) * 4;
+
+ oct1 = oct1 * oct1 * oct1;
+ if (oct1 < 0.f) oct1 = PI_2;
+ if (oct1 > PI_2) oct1 = PI_2;
+
+ return oct1;
+}
+
+
+
+
+
+void cStructGenMarbleCaves::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ cNoise Noise(m_Seed);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z);
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x);
+
+ int Top = a_ChunkDesc.GetHeight(x, z);
+ for (int y = 1; y < Top; ++y )
+ {
+ if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_STONE)
+ {
+ continue;
+ }
+
+ const float yy = (float)y;
+ const float WaveNoise = 1;
+ if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenDualRidgeCaves:
+
+void cStructGenDualRidgeCaves::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10;
+
+ int Top = a_ChunkDesc.GetHeight(x, z);
+ for (int y = 1; y <= Top; ++y)
+ {
+ const float yy = (float)y / 10;
+ float n1 = m_Noise1.CubicNoise3D(xx, yy, zz);
+ float n2 = m_Noise2.CubicNoise3D(xx, yy, zz);
+ float n3 = m_Noise1.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4;
+ float n4 = m_Noise2.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4;
+ if ((abs(n1 + n3) * abs(n2 + n4)) > m_Threshold)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
diff --git a/src/Generating/Caves.h b/src/Generating/Caves.h
new file mode 100644
index 000000000..70cf6fe8c
--- /dev/null
+++ b/src/Generating/Caves.h
@@ -0,0 +1,102 @@
+
+// Caves.h
+
+// Interfaces to the various cave structure generators:
+// - cStructGenWormNestCaves
+// - cStructGenMarbleCaves
+// - cStructGenNetherCaves
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cStructGenMarbleCaves :
+ public cStructureGen
+{
+public:
+ cStructGenMarbleCaves(int a_Seed) : m_Seed(a_Seed) {}
+
+protected:
+
+ int m_Seed;
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cStructGenDualRidgeCaves :
+ public cStructureGen
+{
+public:
+ cStructGenDualRidgeCaves(int a_Seed, float a_Threshold) :
+ m_Noise1(a_Seed),
+ m_Noise2(2 * a_Seed + 19999),
+ m_Seed(a_Seed),
+ m_Threshold(a_Threshold)
+ {
+ }
+
+protected:
+ cNoise m_Noise1;
+ cNoise m_Noise2;
+ int m_Seed;
+ float m_Threshold;
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cStructGenWormNestCaves :
+ public cStructureGen
+{
+public:
+ cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) :
+ m_Noise(a_Seed),
+ m_Size(a_Size),
+ m_Grid(a_Grid),
+ m_MaxOffset(a_MaxOffset)
+ {
+ }
+
+ ~cStructGenWormNestCaves();
+
+protected:
+ class cCaveSystem; // fwd: Caves.cpp
+ typedef std::list<cCaveSystem *> cCaveSystems;
+
+ cNoise m_Noise;
+ int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel
+ int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to
+ int m_Grid; // average spacing of the nests
+ cCaveSystems m_Cache;
+
+ /// Clears everything from the cache
+ void ClearCache(void);
+
+ /// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function.
+ void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves);
+
+ // cStructGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
diff --git a/src/Generating/ChunkDesc.cpp b/src/Generating/ChunkDesc.cpp
new file mode 100644
index 000000000..6050430fd
--- /dev/null
+++ b/src/Generating/ChunkDesc.cpp
@@ -0,0 +1,605 @@
+
+// ChunkDesc.cpp
+
+// Implements the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING.
+
+#include "Globals.h"
+#include "ChunkDesc.h"
+#include "../BlockArea.h"
+#include "../Cuboid.h"
+#include "../Noise.h"
+#include "../BlockEntities/BlockEntity.h"
+
+
+
+
+
+cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) :
+ m_ChunkX(a_ChunkX),
+ m_ChunkZ(a_ChunkZ),
+ m_bUseDefaultBiomes(true),
+ m_bUseDefaultHeight(true),
+ m_bUseDefaultComposition(true),
+ m_bUseDefaultStructures(true),
+ m_bUseDefaultFinish(true)
+{
+ m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width);
+ /*
+ memset(m_BlockTypes, 0, sizeof(cChunkDef::BlockTypes));
+ memset(m_BlockMeta, 0, sizeof(cChunkDef::BlockNibbles));
+ */
+ memset(m_BiomeMap, 0, sizeof(cChunkDef::BiomeMap));
+ memset(m_HeightMap, 0, sizeof(cChunkDef::HeightMap));
+}
+
+
+
+
+
+cChunkDesc::~cChunkDesc()
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+void cChunkDesc::SetChunkCoords(int a_ChunkX, int a_ChunkZ)
+{
+ m_ChunkX = a_ChunkX;
+ m_ChunkZ = a_ChunkZ;
+}
+
+
+
+
+
+void cChunkDesc::FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ m_BlockArea.Fill(cBlockArea::baTypes | cBlockArea::baMetas, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cChunkDesc::SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ m_BlockArea.SetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cChunkDesc::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
+{
+ m_BlockArea.GetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cChunkDesc::SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
+{
+ cChunkDef::SetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ, a_BlockType);
+}
+
+
+
+
+
+BLOCKTYPE cChunkDesc::GetBlockType(int a_RelX, int a_RelY, int a_RelZ)
+{
+ return cChunkDef::GetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+NIBBLETYPE cChunkDesc::GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ)
+{
+ return m_BlockArea.GetRelBlockMeta(a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+void cChunkDesc::SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta)
+{
+ m_BlockArea.SetRelBlockMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta);
+}
+
+
+
+
+
+void cChunkDesc::SetBiome(int a_RelX, int a_RelZ, int a_BiomeID)
+{
+ cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, (EMCSBiome)a_BiomeID);
+}
+
+
+
+
+EMCSBiome cChunkDesc::GetBiome(int a_RelX, int a_RelZ)
+{
+ return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ);
+}
+
+
+
+
+
+void cChunkDesc::SetHeight(int a_RelX, int a_RelZ, int a_Height)
+{
+ cChunkDef::SetHeight(m_HeightMap, a_RelX, a_RelZ, a_Height);
+}
+
+
+
+
+
+int cChunkDesc::GetHeight(int a_RelX, int a_RelZ)
+{
+ return cChunkDef::GetHeight(m_HeightMap, a_RelX, a_RelZ);
+}
+
+
+
+
+
+void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes)
+{
+ m_bUseDefaultBiomes = a_bUseDefaultBiomes;
+}
+
+
+
+
+
+bool cChunkDesc::IsUsingDefaultBiomes(void) const
+{
+ return m_bUseDefaultBiomes;
+}
+
+
+
+
+
+void cChunkDesc::SetUseDefaultHeight(bool a_bUseDefaultHeight)
+{
+ m_bUseDefaultHeight = a_bUseDefaultHeight;
+}
+
+
+
+
+
+bool cChunkDesc::IsUsingDefaultHeight(void) const
+{
+ return m_bUseDefaultHeight;
+}
+
+
+
+
+
+void cChunkDesc::SetUseDefaultComposition(bool a_bUseDefaultComposition)
+{
+ m_bUseDefaultComposition = a_bUseDefaultComposition;
+}
+
+
+
+
+
+bool cChunkDesc::IsUsingDefaultComposition(void) const
+{
+ return m_bUseDefaultComposition;
+}
+
+
+
+
+
+void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures)
+{
+ m_bUseDefaultStructures = a_bUseDefaultStructures;
+}
+
+
+
+
+
+bool cChunkDesc::IsUsingDefaultStructures(void) const
+{
+ return m_bUseDefaultStructures;
+}
+
+
+
+
+
+void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish)
+{
+ m_bUseDefaultFinish = a_bUseDefaultFinish;
+}
+
+
+
+
+
+bool cChunkDesc::IsUsingDefaultFinish(void) const
+{
+ return m_bUseDefaultFinish;
+}
+
+
+
+
+void cChunkDesc::WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy)
+{
+ m_BlockArea.Merge(a_BlockArea, a_RelX, a_RelY, a_RelZ, a_MergeStrategy);
+}
+
+
+
+
+
+void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ)
+{
+ // Normalize the coords:
+ if (a_MinRelX > a_MaxRelX)
+ {
+ std::swap(a_MinRelX, a_MaxRelX);
+ }
+ if (a_MinRelY > a_MaxRelY)
+ {
+ std::swap(a_MinRelY, a_MaxRelY);
+ }
+ if (a_MinRelZ > a_MaxRelZ)
+ {
+ std::swap(a_MinRelZ, a_MaxRelZ);
+ }
+
+ // Include the Max coords:
+ a_MaxRelX += 1;
+ a_MaxRelY += 1;
+ a_MaxRelZ += 1;
+
+ // Check coords validity:
+ if (a_MinRelX < 0)
+ {
+ LOGWARNING("%s: MinRelX less than zero, adjusting to zero", __FUNCTION__);
+ a_MinRelX = 0;
+ }
+ else if (a_MinRelX >= cChunkDef::Width)
+ {
+ LOGWARNING("%s: MinRelX more than chunk width, adjusting to chunk width", __FUNCTION__);
+ a_MinRelX = cChunkDef::Width - 1;
+ }
+ if (a_MaxRelX < 0)
+ {
+ LOGWARNING("%s: MaxRelX less than zero, adjusting to zero", __FUNCTION__);
+ a_MaxRelX = 0;
+ }
+ else if (a_MinRelX >= cChunkDef::Width)
+ {
+ LOGWARNING("%s: MaxRelX more than chunk width, adjusting to chunk width", __FUNCTION__);
+ a_MaxRelX = cChunkDef::Width - 1;
+ }
+
+ if (a_MinRelY < 0)
+ {
+ LOGWARNING("%s: MinRelY less than zero, adjusting to zero", __FUNCTION__);
+ a_MinRelY = 0;
+ }
+ else if (a_MinRelY >= cChunkDef::Height)
+ {
+ LOGWARNING("%s: MinRelY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MinRelY = cChunkDef::Height - 1;
+ }
+ if (a_MaxRelY < 0)
+ {
+ LOGWARNING("%s: MaxRelY less than zero, adjusting to zero", __FUNCTION__);
+ a_MaxRelY = 0;
+ }
+ else if (a_MinRelY >= cChunkDef::Height)
+ {
+ LOGWARNING("%s: MaxRelY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MaxRelY = cChunkDef::Height - 1;
+ }
+
+ if (a_MinRelZ < 0)
+ {
+ LOGWARNING("%s: MinRelZ less than zero, adjusting to zero", __FUNCTION__);
+ a_MinRelZ = 0;
+ }
+ else if (a_MinRelZ >= cChunkDef::Width)
+ {
+ LOGWARNING("%s: MinRelZ more than chunk width, adjusting to chunk width", __FUNCTION__);
+ a_MinRelZ = cChunkDef::Width - 1;
+ }
+ if (a_MaxRelZ < 0)
+ {
+ LOGWARNING("%s: MaxRelZ less than zero, adjusting to zero", __FUNCTION__);
+ a_MaxRelZ = 0;
+ }
+ else if (a_MinRelZ >= cChunkDef::Width)
+ {
+ LOGWARNING("%s: MaxRelZ more than chunk width, adjusting to chunk width", __FUNCTION__);
+ a_MaxRelZ = cChunkDef::Width - 1;
+ }
+
+ // Prepare the block area:
+ int SizeX = a_MaxRelX - a_MinRelX;
+ int SizeY = a_MaxRelY - a_MinRelY;
+ int SizeZ = a_MaxRelZ - a_MinRelZ;
+ a_Dest.Clear();
+ a_Dest.m_OriginX = m_ChunkX * cChunkDef::Width + a_MinRelX;
+ a_Dest.m_OriginY = a_MinRelY;
+ a_Dest.m_OriginZ = m_ChunkZ * cChunkDef::Width + a_MinRelZ;
+ a_Dest.SetSize(SizeX, SizeY, SizeZ, cBlockArea::baTypes | cBlockArea::baMetas);
+
+ for (int y = 0; y < SizeY; y++)
+ {
+ int CDY = a_MinRelY + y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int CDZ = a_MinRelZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int CDX = a_MinRelX + x;
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ GetBlockTypeMeta(CDX, CDY, CDZ, BlockType, BlockMeta);
+ a_Dest.SetRelBlockTypeMeta(x, y, z, BlockType, BlockMeta);
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const
+{
+ HEIGHTTYPE MaxHeight = m_HeightMap[0];
+ for (unsigned int i = 1; i < ARRAYCOUNT(m_HeightMap); i++)
+ {
+ if (m_HeightMap[i] > MaxHeight)
+ {
+ MaxHeight = m_HeightMap[i];
+ }
+ }
+ return MaxHeight;
+}
+
+
+
+
+
+void cChunkDesc::FillRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
+)
+{
+ int MinX = std::max(a_MinX, 0);
+ int MinY = std::max(a_MinY, 0);
+ int MinZ = std::max(a_MinZ, 0);
+ int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
+ int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
+ int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
+
+ for (int y = MinY; y <= MaxY; y++)
+ {
+ for (int z = MinZ; z <= MaxZ; z++)
+ {
+ for (int x = MinX; x <= MaxX; x++)
+ {
+ SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta);
+ }
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cChunkDesc::ReplaceRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta,
+ BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
+)
+{
+ int MinX = std::max(a_MinX, 0);
+ int MinY = std::max(a_MinY, 0);
+ int MinZ = std::max(a_MinZ, 0);
+ int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
+ int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
+ int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
+
+ for (int y = MinY; y <= MaxY; y++)
+ {
+ for (int z = MinZ; z <= MaxZ; z++)
+ {
+ for (int x = MinX; x <= MaxX; x++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ GetBlockTypeMeta(x, y, z, BlockType, BlockMeta);
+ if ((BlockType == a_SrcType) && (BlockMeta == a_SrcMeta))
+ {
+ SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta);
+ }
+ }
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cChunkDesc::FloorRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
+)
+{
+ int MinX = std::max(a_MinX, 0);
+ int MinY = std::max(a_MinY, 0);
+ int MinZ = std::max(a_MinZ, 0);
+ int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
+ int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
+ int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
+
+ for (int y = MinY; y <= MaxY; y++)
+ {
+ for (int z = MinZ; z <= MaxZ; z++)
+ {
+ for (int x = MinX; x <= MaxX; x++)
+ {
+ switch (GetBlockType(x, y, z))
+ {
+ case E_BLOCK_AIR:
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta);
+ break;
+ }
+ } // switch (GetBlockType)
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cChunkDesc::RandomFillRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ int a_RandomSeed, int a_ChanceOutOf10k
+)
+{
+ cNoise Noise(a_RandomSeed);
+ int MinX = std::max(a_MinX, 0);
+ int MinY = std::max(a_MinY, 0);
+ int MinZ = std::max(a_MinZ, 0);
+ int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
+ int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
+ int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
+
+ for (int y = MinY; y <= MaxY; y++)
+ {
+ for (int z = MinZ; z <= MaxZ; z++)
+ {
+ for (int x = MinX; x <= MaxX; x++)
+ {
+ int rnd = (Noise.IntNoise3DInt(x, y, z) / 7) % 10000;
+ if (rnd <= a_ChanceOutOf10k)
+ {
+ SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta);
+ }
+ }
+ } // for z
+ } // for y
+}
+
+
+
+
+
+cBlockEntity * cChunkDesc::GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ)
+{
+ int AbsX = a_RelX + m_ChunkX * cChunkDef::Width;
+ int AbsZ = a_RelZ + m_ChunkZ * cChunkDef::Width;
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr)
+ {
+ if (((*itr)->GetPosX() == AbsX) && ((*itr)->GetPosY() == a_RelY) && ((*itr)->GetPosZ() == AbsZ))
+ {
+ // Already in the list:
+ if ((*itr)->GetBlockType() != GetBlockType(a_RelX, a_RelY, a_RelZ))
+ {
+ // Wrong type, the block type has been overwritten. Erase and create new:
+ m_BlockEntities.erase(itr);
+ break;
+ }
+ // Correct type, already present. Return it:
+ return *itr;
+ }
+ } // for itr - m_BlockEntities[]
+
+ // The block entity is not created yet, try to create it and add to list:
+ cBlockEntity * be = cBlockEntity::CreateByBlockType(GetBlockType(a_RelX, a_RelY, a_RelZ), GetBlockMeta(a_RelX, a_RelY, a_RelZ), AbsX, a_RelY, AbsZ);
+ if (be == NULL)
+ {
+ // No block entity for this block type
+ return NULL;
+ }
+ m_BlockEntities.push_back(be);
+ return be;
+}
+
+
+
+
+
+void cChunkDesc::CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas)
+{
+ const NIBBLETYPE * AreaMetas = m_BlockArea.GetBlockMetas();
+ for (unsigned int i = 0; i < ARRAYCOUNT(a_DestMetas); i++)
+ {
+ a_DestMetas[i] = AreaMetas[2 * i] | (AreaMetas[2 * i + 1] << 4);
+ }
+}
+
+
+
+
+
+#ifdef _DEBUG
+
+void cChunkDesc::VerifyHeightmap(void)
+{
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int y = cChunkDef::Height - 1; y > 0; y--)
+ {
+ BLOCKTYPE BlockType = GetBlockType(x, y, z);
+ if (BlockType != E_BLOCK_AIR)
+ {
+ int Height = GetHeight(x, z);
+ ASSERT(Height == y);
+ break;
+ }
+ } // for y
+ } // for z
+ } // for x
+}
+
+#endif // _DEBUG
+
+
+
+
+
diff --git a/src/Generating/ChunkDesc.h b/src/Generating/ChunkDesc.h
new file mode 100644
index 000000000..e130c463f
--- /dev/null
+++ b/src/Generating/ChunkDesc.h
@@ -0,0 +1,217 @@
+
+// ChunkDesc.h
+
+// Declares the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING.
+
+
+
+
+
+#pragma once
+
+#include "../BlockArea.h"
+#include "../ChunkDef.h"
+#include "../Cuboid.h"
+
+
+
+
+
+// fwd: ../BlockArea.h
+class cBlockArea;
+
+
+
+
+
+// tolua_begin
+class cChunkDesc
+{
+public:
+ // tolua_end
+
+ /// Uncompressed block metas, 1 meta per byte
+ typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks];
+
+ cChunkDesc(int a_ChunkX, int a_ChunkZ);
+ ~cChunkDesc();
+
+ void SetChunkCoords(int a_ChunkX, int a_ChunkZ);
+
+ // tolua_begin
+
+ int GetChunkX(void) const { return m_ChunkX; }
+ int GetChunkZ(void) const { return m_ChunkZ; }
+
+ void FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ void SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
+
+ void SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType);
+ BLOCKTYPE GetBlockType(int a_RelX, int a_RelY, int a_RelZ);
+
+ void SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta);
+ NIBBLETYPE GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ);
+
+ void SetBiome(int a_RelX, int a_RelZ, int a_BiomeID);
+ EMCSBiome GetBiome(int a_RelX, int a_RelZ);
+
+ void SetHeight(int a_RelX, int a_RelZ, int a_Height);
+ int GetHeight(int a_RelX, int a_RelZ);
+
+ // Default generation:
+ void SetUseDefaultBiomes(bool a_bUseDefaultBiomes);
+ bool IsUsingDefaultBiomes(void) const;
+ void SetUseDefaultHeight(bool a_bUseDefaultHeight);
+ bool IsUsingDefaultHeight(void) const;
+ void SetUseDefaultComposition(bool a_bUseDefaultComposition);
+ bool IsUsingDefaultComposition(void) const;
+ void SetUseDefaultStructures(bool a_bUseDefaultStructures);
+ bool IsUsingDefaultStructures(void) const;
+ void SetUseDefaultFinish(bool a_bUseDefaultFinish);
+ bool IsUsingDefaultFinish(void) const;
+
+ /// Writes the block area into the chunk, with its origin set at the specified relative coords. Area's data overwrite everything in the chunk.
+ void WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy = cBlockArea::msOverwrite);
+
+ /// Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas
+ void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ);
+
+ /// Returns the maximum height value in the heightmap
+ HEIGHTTYPE GetMaxHeight(void) const;
+
+ /// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk
+ void FillRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
+ );
+
+ /// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk
+ void FillRelCuboid(const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+ {
+ FillRelCuboid(
+ a_RelCuboid.p1.x, a_RelCuboid.p2.x,
+ a_RelCuboid.p1.y, a_RelCuboid.p2.y,
+ a_RelCuboid.p1.z, a_RelCuboid.p2.z,
+ a_BlockType, a_BlockMeta
+ );
+ }
+
+ /// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk
+ void ReplaceRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta,
+ BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
+ );
+
+ /// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk
+ void ReplaceRelCuboid(
+ const cCuboid & a_RelCuboid,
+ BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta,
+ BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
+ )
+ {
+ ReplaceRelCuboid(
+ a_RelCuboid.p1.x, a_RelCuboid.p2.x,
+ a_RelCuboid.p1.y, a_RelCuboid.p2.y,
+ a_RelCuboid.p1.z, a_RelCuboid.p2.z,
+ a_SrcType, a_SrcMeta,
+ a_DstType, a_DstMeta
+ );
+ }
+
+ /// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk
+ void FloorRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
+ );
+
+ /// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk
+ void FloorRelCuboid(
+ const cCuboid & a_RelCuboid,
+ BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
+ )
+ {
+ FloorRelCuboid(
+ a_RelCuboid.p1.x, a_RelCuboid.p2.x,
+ a_RelCuboid.p1.y, a_RelCuboid.p2.y,
+ a_RelCuboid.p1.z, a_RelCuboid.p2.z,
+ a_DstType, a_DstMeta
+ );
+ }
+
+ /// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk
+ void RandomFillRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ int a_RandomSeed, int a_ChanceOutOf10k
+ );
+
+ /// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk
+ void RandomFillRelCuboid(
+ const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ int a_RandomSeed, int a_ChanceOutOf10k
+ )
+ {
+ RandomFillRelCuboid(
+ a_RelCuboid.p1.x, a_RelCuboid.p2.x,
+ a_RelCuboid.p1.y, a_RelCuboid.p2.y,
+ a_RelCuboid.p1.z, a_RelCuboid.p2.z,
+ a_BlockType, a_BlockMeta,
+ a_RandomSeed, a_ChanceOutOf10k
+ );
+ }
+
+ /// Returns the block entity at the specified coords.
+ /// If there is no block entity at those coords, tries to create one, based on the block type
+ /// If the blocktype doesn't support a block entity, returns NULL.
+ cBlockEntity * GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ);
+
+ // tolua_end
+
+ // Accessors used by cChunkGenerator::Generator descendants:
+ inline cChunkDef::BiomeMap & GetBiomeMap (void) { return m_BiomeMap; }
+ inline cChunkDef::BlockTypes & GetBlockTypes (void) { return *((cChunkDef::BlockTypes *)m_BlockArea.GetBlockTypes()); }
+ // CANNOT, different compression!
+ // inline cChunkDef::BlockNibbles & GetBlockMetas (void) { return *((cChunkDef::BlockNibbles *)m_BlockArea.GetBlockMetas()); }
+ inline BlockNibbleBytes & GetBlockMetasUncompressed(void) { return *((BlockNibbleBytes *)m_BlockArea.GetBlockMetas()); }
+ inline cChunkDef::HeightMap & GetHeightMap (void) { return m_HeightMap; }
+ inline cEntityList & GetEntities (void) { return m_Entities; }
+ inline cBlockEntityList & GetBlockEntities (void) { return m_BlockEntities; }
+
+ /// Compresses the metas from the BlockArea format (1 meta per byte) into regular format (2 metas per byte)
+ void CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas);
+
+ #ifdef _DEBUG
+ /// Verifies that the heightmap corresponds to blocktype contents; if not, asserts on that column
+ void VerifyHeightmap(void);
+ #endif // _DEBUG
+
+private:
+ int m_ChunkX;
+ int m_ChunkZ;
+
+ cChunkDef::BiomeMap m_BiomeMap;
+ cBlockArea m_BlockArea;
+ cChunkDef::HeightMap m_HeightMap;
+ cEntityList m_Entities; // Individual entities are NOT owned by this object!
+ cBlockEntityList m_BlockEntities; // Individual block entities are NOT owned by this object!
+
+ bool m_bUseDefaultBiomes;
+ bool m_bUseDefaultHeight;
+ bool m_bUseDefaultComposition;
+ bool m_bUseDefaultStructures;
+ bool m_bUseDefaultFinish;
+} ; // tolua_export
+
+
+
+
diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp
new file mode 100644
index 000000000..cb993bb05
--- /dev/null
+++ b/src/Generating/ChunkGenerator.cpp
@@ -0,0 +1,329 @@
+
+#include "Globals.h"
+
+#include "ChunkGenerator.h"
+#include "../World.h"
+#include "lib/iniFile/iniFile.h"
+#include "../Root.h"
+#include "../PluginManager.h"
+#include "ChunkDesc.h"
+#include "ComposableGenerator.h"
+#include "Noise3DGenerator.h"
+
+
+
+
+
+/// If the generation queue size exceeds this number, a warning will be output
+const unsigned int QUEUE_WARNING_LIMIT = 1000;
+
+/// If the generation queue size exceeds this number, chunks with no clients will be skipped
+const unsigned int QUEUE_SKIP_LIMIT = 500;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cChunkGenerator:
+
+cChunkGenerator::cChunkGenerator(void) :
+ super("cChunkGenerator"),
+ m_World(NULL),
+ m_Generator(NULL)
+{
+}
+
+
+
+
+
+cChunkGenerator::~cChunkGenerator()
+{
+ Stop();
+}
+
+
+
+
+
+bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile)
+{
+ MTRand rnd;
+ m_World = a_World;
+ m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", rnd.randInt());
+ AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable");
+
+ if (NoCaseCompare(GeneratorName, "Noise3D") == 0)
+ {
+ m_Generator = new cNoise3DGenerator(*this);
+ }
+ else
+ {
+ if (NoCaseCompare(GeneratorName, "composable") != 0)
+ {
+ LOGWARN("[Generator]::Generator value \"%s\" not recognized, using \"Composable\".", GeneratorName.c_str());
+ }
+ m_Generator = new cComposableGenerator(*this);
+ }
+
+ if (m_Generator == NULL)
+ {
+ LOGERROR("Generator could not start, aborting the server");
+ return false;
+ }
+
+ m_Generator->Initialize(a_World, a_IniFile);
+
+ return super::Start();
+}
+
+
+
+
+
+void cChunkGenerator::Stop(void)
+{
+ m_ShouldTerminate = true;
+ m_Event.Set();
+ m_evtRemoved.Set(); // Wake up anybody waiting for empty queue
+ Wait();
+
+ delete m_Generator;
+ m_Generator = NULL;
+}
+
+
+
+
+
+void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ {
+ cCSLock Lock(m_CS);
+
+ // Check if it is already in the queue:
+ for (cChunkCoordsList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
+ {
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
+ {
+ // Already in the queue, bail out
+ return;
+ }
+ } // for itr - m_Queue[]
+
+ // Add to queue, issue a warning if too many:
+ if (m_Queue.size() >= QUEUE_WARNING_LIMIT)
+ {
+ LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (%i)", a_ChunkX, a_ChunkZ, m_Queue.size());
+ }
+ m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
+ }
+
+ m_Event.Set();
+}
+
+
+
+
+
+void cChunkGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ if (m_Generator != NULL)
+ {
+ m_Generator->GenerateBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
+ }
+}
+
+
+
+
+
+void cChunkGenerator::WaitForQueueEmpty(void)
+{
+ cCSLock Lock(m_CS);
+ while (!m_ShouldTerminate && !m_Queue.empty())
+ {
+ cCSUnlock Unlock(Lock);
+ m_evtRemoved.Wait();
+ }
+}
+
+
+
+
+
+int cChunkGenerator::GetQueueLength(void)
+{
+ cCSLock Lock(m_CS);
+ return (int)m_Queue.size();
+}
+
+
+
+
+
+EMCSBiome cChunkGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
+{
+ ASSERT(m_Generator != NULL);
+ return m_Generator->GetBiomeAt(a_BlockX, a_BlockZ);
+}
+
+
+
+
+
+BLOCKTYPE cChunkGenerator::GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default)
+{
+ AString BlockType = a_IniFile.GetValueSet(a_SectionName, a_ValueName, a_Default);
+ BLOCKTYPE Block = BlockStringToType(BlockType);
+ if (Block < 0)
+ {
+ LOGWARN("[&s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(),a_Default.c_str());
+ return BlockStringToType(a_Default);
+ }
+ return Block;
+}
+
+
+
+
+
+void cChunkGenerator::Execute(void)
+{
+ // To be able to display performance information, the generator counts the chunks generated.
+ // When the queue gets empty, the count is reset, so that waiting for the queue is not counted into the total time.
+ int NumChunksGenerated = 0; // Number of chunks generated since the queue was last empty
+ clock_t GenerationStart = clock(); // Clock tick when the queue started to fill
+ clock_t LastReportTick = clock(); // Clock tick of the last report made (so that performance isn't reported too often)
+
+ while (!m_ShouldTerminate)
+ {
+ cCSLock Lock(m_CS);
+ while (m_Queue.size() == 0)
+ {
+ if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC))
+ {
+ LOG("Chunk generator performance: %.2f ch/s (%d ch total)",
+ (double)NumChunksGenerated * CLOCKS_PER_SEC/ (clock() - GenerationStart),
+ NumChunksGenerated
+ );
+ }
+ cCSUnlock Unlock(Lock);
+ m_Event.Wait();
+ if (m_ShouldTerminate)
+ {
+ return;
+ }
+ NumChunksGenerated = 0;
+ GenerationStart = clock();
+ LastReportTick = clock();
+ }
+
+ cChunkCoords coords = m_Queue.front(); // Get next coord from queue
+ m_Queue.erase( m_Queue.begin() ); // Remove coordinate from queue
+ bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
+ Lock.Unlock(); // Unlock ASAP
+ m_evtRemoved.Set();
+
+ // Display perf info once in a while:
+ if ((NumChunksGenerated > 16) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC))
+ {
+ LOG("Chunk generator performance: %.2f ch/s (%d ch total)",
+ (double)NumChunksGenerated * CLOCKS_PER_SEC / (clock() - GenerationStart),
+ NumChunksGenerated
+ );
+ LastReportTick = clock();
+ }
+
+ // Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set
+ if ((coords.m_ChunkY == 0) && m_World->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
+ {
+ LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ);
+ // Already generated, ignore request
+ continue;
+ }
+
+ if (SkipEnabled && !m_World->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ))
+ {
+ LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
+ continue;
+ }
+
+ LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
+ DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
+
+ // Save the chunk right after generating, so that we don't have to generate it again on next run
+ m_World->GetStorage().QueueSaveChunk(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
+
+ NumChunksGenerated++;
+ } // while (!bStop)
+}
+
+
+
+
+void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ);
+ cRoot::Get()->GetPluginManager()->CallHookChunkGenerating(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc);
+ m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc);
+ cRoot::Get()->GetPluginManager()->CallHookChunkGenerated(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc);
+
+ #ifdef _DEBUG
+ // Verify that the generator has produced valid data:
+ ChunkDesc.VerifyHeightmap();
+ #endif
+
+ cChunkDef::BlockNibbles BlockMetas;
+ ChunkDesc.CompressBlockMetas(BlockMetas);
+
+ m_World->SetChunkData(
+ a_ChunkX, a_ChunkZ,
+ ChunkDesc.GetBlockTypes(), BlockMetas,
+ NULL, NULL, // We don't have lighting, chunk will be lighted when needed
+ &ChunkDesc.GetHeightMap(), &ChunkDesc.GetBiomeMap(),
+ ChunkDesc.GetEntities(), ChunkDesc.GetBlockEntities(),
+ true
+ );
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cChunkGenerator::cGenerator:
+
+cChunkGenerator::cGenerator::cGenerator(cChunkGenerator & a_ChunkGenerator) :
+ m_ChunkGenerator(a_ChunkGenerator)
+{
+}
+
+
+
+
+
+void cChunkGenerator::cGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
+{
+ m_World = a_World;
+ UNUSED(a_IniFile);
+}
+
+
+
+
+
+EMCSBiome cChunkGenerator::cGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
+{
+ cChunkDef::BiomeMap Biomes;
+ int Y = 0;
+ int ChunkX, ChunkZ;
+ cWorld::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, Y, ChunkZ);
+ GenerateBiomes(ChunkX, ChunkZ, Biomes);
+ return cChunkDef::GetBiome(Biomes, a_BlockX, a_BlockZ);
+}
+
+
+
+
diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h
new file mode 100644
index 000000000..2d3bb8082
--- /dev/null
+++ b/src/Generating/ChunkGenerator.h
@@ -0,0 +1,113 @@
+
+// ChunkGenerator.h
+
+// Interfaces to the cChunkGenerator class representing the thread that generates chunks
+
+/*
+The object takes requests for generating chunks and processes them in a separate thread one by one.
+The requests are not added to the queue if there is already a request with the same coords
+Before generating, the thread checks if the chunk hasn't been already generated.
+It is theoretically possible to have multiple generator threads by having multiple instances of this object,
+but then it MAY happen that the chunk is generated twice.
+If the generator queue is overloaded, the generator skips chunks with no clients in them
+*/
+
+
+
+
+
+#pragma once
+
+#include "../OSSupport/IsThread.h"
+#include "../ChunkDef.h"
+
+
+
+
+
+// fwd:
+class cWorld;
+class cIniFile;
+class cChunkDesc;
+
+
+
+
+
+class cChunkGenerator :
+ cIsThread
+{
+ typedef cIsThread super;
+
+public:
+ /// The interface that a class has to implement to become a generator
+ class cGenerator
+ {
+ public:
+ cGenerator(cChunkGenerator & a_ChunkGenerator);
+ virtual ~cGenerator() {} ; // Force a virtual destructor
+
+ /// Called to initialize the generator on server startup.
+ virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile);
+
+ /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
+ virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
+
+ /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome. Default implementation uses GenerateBiomes().
+ virtual EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
+
+ /// Called in a separate thread to do the actual chunk generation. Generator should generate into a_ChunkDesc.
+ virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) = 0;
+
+ protected:
+ cChunkGenerator & m_ChunkGenerator;
+ cWorld * m_World;
+ } ;
+
+
+ cChunkGenerator (void);
+ ~cChunkGenerator();
+
+ bool Start(cWorld * a_World, cIniFile & a_IniFile);
+ void Stop(void);
+
+ /// Queues the chunk for generation; removes duplicate requests
+ void QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
+ void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
+
+ void WaitForQueueEmpty(void);
+
+ int GetQueueLength(void);
+
+ int GetSeed(void) const { return m_Seed; }
+
+ /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome
+ EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
+
+ /// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure.
+ static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default);
+
+private:
+
+ cWorld * m_World;
+
+ int m_Seed;
+
+ cCriticalSection m_CS;
+ cChunkCoordsList m_Queue;
+ cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate
+ cEvent m_evtRemoved; ///< Set when an item is removed from the queue
+
+ cGenerator * m_Generator; ///< The actual generator engine used to generate chunks
+
+ // cIsThread override:
+ virtual void Execute(void) override;
+
+ void DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+};
+
+
+
+
diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp
new file mode 100644
index 000000000..7e1ade147
--- /dev/null
+++ b/src/Generating/CompoGen.cpp
@@ -0,0 +1,634 @@
+
+// CompoGen.cpp
+
+/* Implements the various terrain composition generators:
+ - cCompoGenSameBlock
+ - cCompoGenDebugBiomes
+ - cCompoGenClassic
+*/
+
+#include "Globals.h"
+#include "CompoGen.h"
+#include "../BlockID.h"
+#include "../Item.h"
+#include "../LinearUpscale.h"
+#include "lib/inifile/iniFile.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompoGenSameBlock:
+
+void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int Start;
+ if (m_IsBedrocked)
+ {
+ a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
+ Start = 1;
+ }
+ else
+ {
+ Start = 0;
+ }
+ for (int y = a_ChunkDesc.GetHeight(x, z); y >= Start; y--)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, m_BlockType);
+ } // for y
+ } // for z
+ } // for x
+}
+
+
+
+
+
+void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile)
+{
+ m_BlockType = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "SameBlockType", "stone").m_ItemType);
+ m_IsBedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompoGenDebugBiomes:
+
+void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ static BLOCKTYPE Blocks[] =
+ {
+ E_BLOCK_STONE,
+ E_BLOCK_COBBLESTONE,
+ E_BLOCK_LOG,
+ E_BLOCK_PLANKS,
+ E_BLOCK_SANDSTONE,
+ E_BLOCK_WOOL,
+ E_BLOCK_COAL_ORE,
+ E_BLOCK_IRON_ORE,
+ E_BLOCK_GOLD_ORE,
+ E_BLOCK_DIAMOND_ORE,
+ E_BLOCK_LAPIS_ORE,
+ E_BLOCK_REDSTONE_ORE,
+ E_BLOCK_IRON_BLOCK,
+ E_BLOCK_GOLD_BLOCK,
+ E_BLOCK_DIAMOND_BLOCK,
+ E_BLOCK_LAPIS_BLOCK,
+ E_BLOCK_BRICK,
+ E_BLOCK_MOSSY_COBBLESTONE,
+ E_BLOCK_OBSIDIAN,
+ E_BLOCK_NETHERRACK,
+ E_BLOCK_SOULSAND,
+ E_BLOCK_NETHER_BRICK,
+ E_BLOCK_BEDROCK,
+ } ;
+
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ BLOCKTYPE BlockType = Blocks[a_ChunkDesc.GetBiome(x, z)];
+ for (int y = a_ChunkDesc.GetHeight(x, z); y >= 0; y--)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, BlockType);
+ } // for y
+ } // for z
+ } // for x
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompoGenClassic:
+
+cCompoGenClassic::cCompoGenClassic(void) :
+ m_SeaLevel(60),
+ m_BeachHeight(2),
+ m_BeachDepth(4),
+ m_BlockTop(E_BLOCK_GRASS),
+ m_BlockMiddle(E_BLOCK_DIRT),
+ m_BlockBottom(E_BLOCK_STONE),
+ m_BlockBeach(E_BLOCK_SAND),
+ m_BlockBeachBottom(E_BLOCK_SANDSTONE),
+ m_BlockSea(E_BLOCK_STATIONARY_WATER)
+{
+}
+
+
+
+
+
+void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ /* The classic composition means:
+ - 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight
+ - 3 sand and a 1 sandstone, rest stone if between sealevel and sealevel + beachheight
+ - water from waterlevel to height, then 3 sand, 1 sandstone, the rest stone, if water depth < beachdepth
+ - water from waterlevel, then 3 dirt, the rest stone otherwise
+ - bedrock at the bottom
+ */
+
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+
+ // The patterns to use for different situations, must be same length!
+ const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ;
+ const BLOCKTYPE PatternBeach[] = {m_BlockBeach, m_BlockBeach, m_BlockBeach, m_BlockBeachBottom} ;
+ const BLOCKTYPE PatternOcean[] = {m_BlockMiddle, m_BlockMiddle, m_BlockMiddle, m_BlockBottom} ;
+ static int PatternLength = ARRAYCOUNT(PatternGround);
+ ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternBeach));
+ ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternOcean));
+
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int Height = a_ChunkDesc.GetHeight(x, z);
+ const BLOCKTYPE * Pattern;
+ if (Height > m_SeaLevel + m_BeachHeight)
+ {
+ Pattern = PatternGround;
+ }
+ else if (Height > m_SeaLevel - m_BeachDepth)
+ {
+ Pattern = PatternBeach;
+ }
+ else
+ {
+ Pattern = PatternOcean;
+ }
+
+ // Fill water from sealevel down to height (if any):
+ for (int y = m_SeaLevel; y >= Height; --y)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, m_BlockSea);
+ }
+
+ // Fill from height till the bottom:
+ for (int y = Height; y >= 1; y--)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (Height - y < PatternLength) ? Pattern[Height - y] : m_BlockBottom);
+ }
+
+ // The last layer is always bedrock:
+ a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile)
+{
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", m_SeaLevel);
+ m_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight);
+ m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth);
+ m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType);
+ m_BlockMiddle = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt").m_ItemType);
+ m_BlockBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBottom", "stone").m_ItemType);
+ m_BlockBeach = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeach", "sand").m_ItemType);
+ m_BlockBeachBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone").m_ItemType);
+ m_BlockSea = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater").m_ItemType);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompoGenBiomal:
+
+void cCompoGenBiomal::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+
+ /*
+ _X 2013_04_22:
+ There's no point in generating the whole cubic noise at once, because the noise values are used in
+ only about 20 % of the cases, so the speed gained by precalculating is lost by precalculating too much data
+ */
+
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int Height = a_ChunkDesc.GetHeight(x, z);
+ if (Height > m_SeaLevel)
+ {
+ switch (a_ChunkDesc.GetBiome(x, z))
+ {
+ case biOcean:
+ case biPlains:
+ case biExtremeHills:
+ case biForest:
+ case biTaiga:
+ case biSwampland:
+ case biRiver:
+ case biFrozenOcean:
+ case biFrozenRiver:
+ case biIcePlains:
+ case biIceMountains:
+ case biForestHills:
+ case biTaigaHills:
+ case biExtremeHillsEdge:
+ case biJungle:
+ case biJungleHills:
+ {
+ FillColumnGrass(x, z, Height, a_ChunkDesc.GetBlockTypes());
+ break;
+ }
+ case biDesertHills:
+ case biDesert:
+ case biBeach:
+ {
+ FillColumnSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
+ break;
+ }
+ case biMushroomIsland:
+ case biMushroomShore:
+ {
+ FillColumnMycelium(x, z, Height, a_ChunkDesc.GetBlockTypes());
+ break;
+ }
+ default:
+ {
+ // TODO
+ ASSERT(!"CompoGenBiomal: Biome not implemented yet!");
+ break;
+ }
+ }
+ }
+ else
+ {
+ switch (a_ChunkDesc.GetBiome(x, z))
+ {
+ case biDesert:
+ case biBeach:
+ {
+ // Fill with water, sand, sandstone and stone
+ FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
+ break;
+ }
+ default:
+ {
+ // Fill with water, sand/dirt/clay mix and stone
+ if (m_Noise.CubicNoise2D(0.3f * (cChunkDef::Width * ChunkX + x), 0.3f * (cChunkDef::Width * ChunkZ + z)) < 0)
+ {
+ FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
+ }
+ else
+ {
+ FillColumnWaterDirt(x, z, Height, a_ChunkDesc.GetBlockTypes());
+ }
+ break;
+ }
+ } // switch (biome)
+ a_ChunkDesc.SetHeight(x, z, m_SeaLevel + 1);
+ } // else (under water)
+ a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cCompoGenBiomal::InitializeCompoGen(cIniFile & a_IniFile)
+{
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", m_SeaLevel) - 1;
+}
+
+
+
+
+
+void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
+{
+ BLOCKTYPE Pattern[] =
+ {
+ E_BLOCK_GRASS,
+ E_BLOCK_DIRT,
+ E_BLOCK_DIRT,
+ E_BLOCK_DIRT,
+ } ;
+ FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
+
+ for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
+ }
+}
+
+
+
+
+
+void cCompoGenBiomal::FillColumnSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
+{
+ BLOCKTYPE Pattern[] =
+ {
+ E_BLOCK_SAND,
+ E_BLOCK_SAND,
+ E_BLOCK_SAND,
+ E_BLOCK_SANDSTONE,
+ } ;
+ FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
+
+ for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
+ }
+}
+
+
+
+
+
+
+void cCompoGenBiomal::FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
+{
+ BLOCKTYPE Pattern[] =
+ {
+ E_BLOCK_MYCELIUM,
+ E_BLOCK_DIRT,
+ E_BLOCK_DIRT,
+ E_BLOCK_DIRT,
+ } ;
+ FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
+
+ for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
+ }
+}
+
+
+
+
+
+void cCompoGenBiomal::FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
+{
+ FillColumnSand(a_RelX, a_RelZ, a_Height, a_BlockTypes);
+ for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
+ }
+}
+
+
+
+
+
+void cCompoGenBiomal::FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
+{
+ // Dirt
+ BLOCKTYPE Pattern[] =
+ {
+ E_BLOCK_DIRT,
+ E_BLOCK_DIRT,
+ E_BLOCK_DIRT,
+ E_BLOCK_DIRT,
+ } ;
+ FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
+
+ for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
+ }
+ for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
+ }
+}
+
+
+
+
+
+
+void cCompoGenBiomal::FillColumnPattern(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize)
+{
+ for (int y = a_Height, idx = 0; (y >= 0) && (idx < a_PatternSize); y--, idx++)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, a_Pattern[idx]);
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompoGenNether:
+
+cCompoGenNether::cCompoGenNether(int a_Seed) :
+ m_Noise1(a_Seed + 10),
+ m_Noise2(a_Seed * a_Seed * 10 + a_Seed * 1000 + 6000),
+ m_Threshold(0)
+{
+}
+
+
+
+
+
+void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
+
+ const int SEGMENT_HEIGHT = 8;
+ const int INTERPOL_X = 16; // Must be a divisor of 16
+ const int INTERPOL_Z = 16; // Must be a divisor of 16
+ // Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately.
+ // Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them
+ // Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it.
+
+ int FloorBuf1[17 * 17];
+ int FloorBuf2[17 * 17];
+ int * FloorHi = FloorBuf1;
+ int * FloorLo = FloorBuf2;
+ int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+
+ // Interpolate the lowest floor:
+ for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++)
+ {
+ FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
+ m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) *
+ m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) /
+ 256;
+ } // for x, z - FloorLo[]
+ LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z);
+
+ // Interpolate segments:
+ for (int Segment = 0; Segment < MaxHeight; Segment += SEGMENT_HEIGHT)
+ {
+ // First update the high floor:
+ for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++)
+ {
+ FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
+ m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) *
+ m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) /
+ 256;
+ } // for x, z - FloorLo[]
+ LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z);
+
+ // Interpolate between FloorLo and FloorHi:
+ for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++)
+ {
+ int Lo = FloorLo[x + 17 * z] / 256;
+ int Hi = FloorHi[x + 17 * z] / 256;
+ for (int y = 0; y < SEGMENT_HEIGHT; y++)
+ {
+ int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT;
+ a_ChunkDesc.SetBlockType(x, y + Segment, z, (Val < m_Threshold) ? E_BLOCK_NETHERRACK : E_BLOCK_AIR);
+ }
+ }
+
+ // Swap the floors:
+ std::swap(FloorLo, FloorHi);
+ }
+
+ // Bedrock at the bottom and at the top:
+ for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++)
+ {
+ a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
+ a_ChunkDesc.SetBlockType(x, a_ChunkDesc.GetHeight(x, z), z, E_BLOCK_BEDROCK);
+ }
+}
+
+
+
+
+
+void cCompoGenNether::InitializeCompoGen(cIniFile & a_IniFile)
+{
+ m_Threshold = a_IniFile.GetValueSetI("Generator", "NetherThreshold", m_Threshold);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompoGenCache:
+
+cCompoGenCache::cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize) :
+ m_Underlying(a_Underlying),
+ m_CacheSize(a_CacheSize),
+ m_CacheOrder(new int[a_CacheSize]),
+ m_CacheData(new sCacheData[a_CacheSize]),
+ m_NumHits(0),
+ m_NumMisses(0),
+ m_TotalChain(0)
+{
+ for (int i = 0; i < m_CacheSize; i++)
+ {
+ m_CacheOrder[i] = i;
+ m_CacheData[i].m_ChunkX = 0x7fffffff;
+ m_CacheData[i].m_ChunkZ = 0x7fffffff;
+ }
+}
+
+
+
+
+
+cCompoGenCache::~cCompoGenCache()
+{
+ delete[] m_CacheData;
+ delete[] m_CacheOrder;
+}
+
+
+
+
+
+void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ #ifdef _DEBUG
+ if (((m_NumHits + m_NumMisses) % 1024) == 10)
+ {
+ LOGD("CompoGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
+ LOGD("CompoGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits);
+ }
+ #endif // _DEBUG
+
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+
+ for (int i = 0; i < m_CacheSize; i++)
+ {
+ if (
+ (m_CacheData[m_CacheOrder[i]].m_ChunkX != ChunkX) ||
+ (m_CacheData[m_CacheOrder[i]].m_ChunkZ != ChunkZ)
+ )
+ {
+ continue;
+ }
+ // Found it in the cache
+ int Idx = m_CacheOrder[i];
+
+ // Move to front:
+ for (int j = i; j > 0; j--)
+ {
+ m_CacheOrder[j] = m_CacheOrder[j - 1];
+ }
+ m_CacheOrder[0] = Idx;
+
+ // Use the cached data:
+ memcpy(a_ChunkDesc.GetBlockTypes(), m_CacheData[Idx].m_BlockTypes, sizeof(a_ChunkDesc.GetBlockTypes()));
+ memcpy(a_ChunkDesc.GetBlockMetasUncompressed(), m_CacheData[Idx].m_BlockMetas, sizeof(a_ChunkDesc.GetBlockMetasUncompressed()));
+
+ m_NumHits++;
+ m_TotalChain += i;
+ return;
+ } // for i - cache
+
+ // Not in the cache:
+ m_NumMisses++;
+ m_Underlying.ComposeTerrain(a_ChunkDesc);
+
+ // Insert it as the first item in the MRU order:
+ int Idx = m_CacheOrder[m_CacheSize - 1];
+ for (int i = m_CacheSize - 1; i > 0; i--)
+ {
+ m_CacheOrder[i] = m_CacheOrder[i - 1];
+ } // for i - m_CacheOrder[]
+ m_CacheOrder[0] = Idx;
+ memcpy(m_CacheData[Idx].m_BlockTypes, a_ChunkDesc.GetBlockTypes(), sizeof(a_ChunkDesc.GetBlockTypes()));
+ memcpy(m_CacheData[Idx].m_BlockMetas, a_ChunkDesc.GetBlockMetasUncompressed(), sizeof(a_ChunkDesc.GetBlockMetasUncompressed()));
+ m_CacheData[Idx].m_ChunkX = ChunkX;
+ m_CacheData[Idx].m_ChunkZ = ChunkZ;
+}
+
+
+
+
+
+void cCompoGenCache::InitializeCompoGen(cIniFile & a_IniFile)
+{
+ m_Underlying.InitializeCompoGen(a_IniFile);
+}
+
+
+
+
diff --git a/src/Generating/CompoGen.h b/src/Generating/CompoGen.h
new file mode 100644
index 000000000..2ee286b06
--- /dev/null
+++ b/src/Generating/CompoGen.h
@@ -0,0 +1,182 @@
+
+// CompoGen.h
+
+/* Interfaces to the various terrain composition generators:
+ - cCompoGenSameBlock
+ - cCompoGenDebugBiomes
+ - cCompoGenClassic
+ - cCompoGenBiomal
+ - cCompoGenNether
+ - cCompoGenCache
+*/
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cCompoGenSameBlock :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenSameBlock(void) :
+ m_BlockType(E_BLOCK_STONE),
+ m_IsBedrocked(true)
+ {}
+
+protected:
+
+ BLOCKTYPE m_BlockType;
+ bool m_IsBedrocked;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
+class cCompoGenDebugBiomes :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenDebugBiomes(void) {}
+
+protected:
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cCompoGenClassic :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenClassic(void);
+
+protected:
+
+ int m_SeaLevel;
+ int m_BeachHeight;
+ int m_BeachDepth;
+ BLOCKTYPE m_BlockTop;
+ BLOCKTYPE m_BlockMiddle;
+ BLOCKTYPE m_BlockBottom;
+ BLOCKTYPE m_BlockBeach;
+ BLOCKTYPE m_BlockBeachBottom;
+ BLOCKTYPE m_BlockSea;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
+class cCompoGenBiomal :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenBiomal(int a_Seed) :
+ m_Noise(a_Seed + 1000),
+ m_SeaLevel(62)
+ {
+ }
+
+protected:
+
+ cNoise m_Noise;
+ int m_SeaLevel;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
+
+ void FillColumnGrass (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
+ void FillColumnSand (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
+ void FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
+ void FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
+ void FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
+
+ void FillColumnPattern (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize);
+} ;
+
+
+
+
+
+class cCompoGenNether :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenNether(int a_Seed);
+
+protected:
+ cNoise m_Noise1;
+ cNoise m_Noise2;
+
+ int m_Threshold;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
+/// Caches most-recently-used chunk composition of another composition generator. Caches only the types and metas
+class cCompoGenCache :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize); // Doesn't take ownership of a_Underlying
+ ~cCompoGenCache();
+
+ // cTerrainCompositionGen override:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
+
+protected:
+
+ cTerrainCompositionGen & m_Underlying;
+
+ struct sCacheData
+ {
+ int m_ChunkX;
+ int m_ChunkZ;
+ cChunkDef::BlockTypes m_BlockTypes;
+ cChunkDesc::BlockNibbleBytes m_BlockMetas; // The metas are uncompressed, 1 meta per byte
+ } ;
+
+ // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
+ int m_CacheSize;
+ int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
+ sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
+
+ // Cache statistics
+ int m_NumHits;
+ int m_NumMisses;
+ int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
+} ;
+
+
+
+
diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp
new file mode 100644
index 000000000..496ada52d
--- /dev/null
+++ b/src/Generating/ComposableGenerator.cpp
@@ -0,0 +1,501 @@
+
+// ComposableGenerator.cpp
+
+// Implements the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks
+
+#include "Globals.h"
+
+#include "ComposableGenerator.h"
+#include "../World.h"
+#include "lib/iniFile/iniFile.h"
+#include "../Root.h"
+
+// Individual composed algorithms:
+#include "BioGen.h"
+#include "HeiGen.h"
+#include "CompoGen.h"
+#include "StructGen.h"
+#include "FinishGen.h"
+
+#include "Caves.h"
+#include "DistortedHeightmap.h"
+#include "EndGen.h"
+#include "MineShafts.h"
+#include "Noise3DGenerator.h"
+#include "Ravines.h"
+
+
+
+
+
+
+
+
+
+
+cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
+ super(a_ChunkGenerator),
+ m_BiomeGen(NULL),
+ m_HeightGen(NULL),
+ m_CompositionGen(NULL),
+ m_UnderlyingBiomeGen(NULL),
+ m_UnderlyingHeightGen(NULL),
+ m_UnderlyingCompositionGen(NULL)
+{
+}
+
+
+
+
+
+cComposableGenerator::~cComposableGenerator()
+{
+ // Delete the generating composition:
+ for (cFinishGenList::const_iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr)
+ {
+ delete *itr;
+ }
+ m_FinishGens.clear();
+ for (cStructureGenList::const_iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr)
+ {
+ delete *itr;
+ }
+ m_StructureGens.clear();
+
+ delete m_CompositionGen;
+ m_CompositionGen = NULL;
+ delete m_HeightGen;
+ m_HeightGen = NULL;
+ delete m_BiomeGen;
+ m_BiomeGen = NULL;
+ delete m_UnderlyingCompositionGen;
+ m_UnderlyingCompositionGen = NULL;
+ delete m_UnderlyingHeightGen;
+ m_UnderlyingHeightGen = NULL;
+ delete m_UnderlyingBiomeGen;
+ m_UnderlyingBiomeGen = NULL;
+}
+
+
+
+
+
+void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
+{
+ super::Initialize(a_World, a_IniFile);
+
+ InitBiomeGen(a_IniFile);
+ InitHeightGen(a_IniFile);
+ InitCompositionGen(a_IniFile);
+ InitStructureGens(a_IniFile);
+ InitFinishGens(a_IniFile);
+}
+
+
+
+
+
+void cComposableGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ if (m_BiomeGen != NULL) // Quick fix for generator deinitializing before the world storage finishes loading
+ {
+ m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
+ }
+}
+
+
+
+
+
+void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
+{
+ if (a_ChunkDesc.IsUsingDefaultBiomes())
+ {
+ m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap());
+ }
+
+ if (a_ChunkDesc.IsUsingDefaultHeight())
+ {
+ m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap());
+ }
+
+ if (a_ChunkDesc.IsUsingDefaultComposition())
+ {
+ m_CompositionGen->ComposeTerrain(a_ChunkDesc);
+ }
+
+ if (a_ChunkDesc.IsUsingDefaultStructures())
+ {
+ for (cStructureGenList::iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr)
+ {
+ (*itr)->GenStructures(a_ChunkDesc);
+ } // for itr - m_StructureGens[]
+ }
+
+ if (a_ChunkDesc.IsUsingDefaultFinish())
+ {
+ for (cFinishGenList::iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr)
+ {
+ (*itr)->GenFinish(a_ChunkDesc);
+ } // for itr - m_FinishGens[]
+ }
+}
+
+
+
+
+
+void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
+{
+ AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", "");
+ if (BiomeGenName.empty())
+ {
+ LOGWARN("[Generator] BiomeGen value not set in world.ini, using \"MultiStepMap\".");
+ BiomeGenName = "MultiStepMap";
+ }
+
+ int Seed = m_ChunkGenerator.GetSeed();
+ bool CacheOffByDefault = false;
+ if (NoCaseCompare(BiomeGenName, "constant") == 0)
+ {
+ m_BiomeGen = new cBioGenConstant;
+ CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :)
+ }
+ else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0)
+ {
+ m_BiomeGen = new cBioGenCheckerboard;
+ CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data
+ }
+ else if (NoCaseCompare(BiomeGenName, "voronoi") == 0)
+ {
+ m_BiomeGen = new cBioGenVoronoi(Seed);
+ }
+ else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0)
+ {
+ m_BiomeGen = new cBioGenDistortedVoronoi(Seed);
+ }
+ else
+ {
+ if (NoCaseCompare(BiomeGenName, "multistepmap") != 0)
+ {
+ LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str());
+ }
+ m_BiomeGen = new cBioGenMultiStepMap(Seed);
+
+ /*
+ // Performance-testing:
+ LOGINFO("Measuring performance of cBioGenMultiStepMap...");
+ clock_t BeginTick = clock();
+ for (int x = 0; x < 5000; x++)
+ {
+ cChunkDef::BiomeMap Biomes;
+ m_BiomeGen->GenBiomes(x * 5, x * 5, Biomes);
+ }
+ clock_t Duration = clock() - BeginTick;
+ LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
+ //*/
+ }
+
+ // Add a cache, if requested:
+ int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64);
+ if (CacheSize > 0)
+ {
+ if (CacheSize < 4)
+ {
+ LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d",
+ CacheSize, 4
+ );
+ CacheSize = 4;
+ }
+ LOGD("Using a cache for biomegen of size %d.", CacheSize);
+ m_UnderlyingBiomeGen = m_BiomeGen;
+ m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize);
+ }
+ m_BiomeGen->InitializeBiomeGen(a_IniFile);
+}
+
+
+
+
+
+void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
+{
+ AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", "");
+ if (HeightGenName.empty())
+ {
+ LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\".");
+ HeightGenName = "Biomal";
+ }
+
+ int Seed = m_ChunkGenerator.GetSeed();
+ bool CacheOffByDefault = false;
+ if (NoCaseCompare(HeightGenName, "flat") == 0)
+ {
+ m_HeightGen = new cHeiGenFlat;
+ CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
+ }
+ else if (NoCaseCompare(HeightGenName, "classic") == 0)
+ {
+ m_HeightGen = new cHeiGenClassic(Seed);
+ }
+ else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
+ {
+ m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen);
+ }
+ else if (NoCaseCompare(HeightGenName, "End") == 0)
+ {
+ m_HeightGen = new cEndGen(Seed);
+ }
+ else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
+ {
+ m_HeightGen = new cNoise3DComposable(Seed);
+ }
+ else // "biomal" or <not found>
+ {
+ if (NoCaseCompare(HeightGenName, "biomal") != 0)
+ {
+ LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
+ }
+ m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen);
+
+ /*
+ // Performance-testing:
+ LOGINFO("Measuring performance of cHeiGenBiomal...");
+ clock_t BeginTick = clock();
+ for (int x = 0; x < 500; x++)
+ {
+ cChunkDef::HeightMap Heights;
+ m_HeightGen->GenHeightMap(x * 5, x * 5, Heights);
+ }
+ clock_t Duration = clock() - BeginTick;
+ LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
+ //*/
+ }
+
+ // Read the settings:
+ m_HeightGen->InitializeHeightGen(a_IniFile);
+
+ // Add a cache, if requested:
+ int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64);
+ if (CacheSize > 0)
+ {
+ if (CacheSize < 4)
+ {
+ LOGWARNING("Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d",
+ CacheSize, 4
+ );
+ CacheSize = 4;
+ }
+ LOGD("Using a cache for Heightgen of size %d.", CacheSize);
+ m_UnderlyingHeightGen = m_HeightGen;
+ m_HeightGen = new cHeiGenCache(*m_UnderlyingHeightGen, CacheSize);
+ }
+}
+
+
+
+
+
+void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile)
+{
+ int Seed = m_ChunkGenerator.GetSeed();
+ AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
+ if (CompoGenName.empty())
+ {
+ LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\".");
+ CompoGenName = "Biomal";
+ }
+ if (NoCaseCompare(CompoGenName, "sameblock") == 0)
+ {
+ m_CompositionGen = new cCompoGenSameBlock;
+ }
+ else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
+ {
+ m_CompositionGen = new cCompoGenDebugBiomes;
+ }
+ else if (NoCaseCompare(CompoGenName, "classic") == 0)
+ {
+ m_CompositionGen = new cCompoGenClassic;
+ }
+ else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
+ {
+ m_CompositionGen = new cDistortedHeightmap(Seed, *m_BiomeGen);
+ }
+ else if (NoCaseCompare(CompoGenName, "end") == 0)
+ {
+ m_CompositionGen = new cEndGen(Seed);
+ }
+ else if (NoCaseCompare(CompoGenName, "nether") == 0)
+ {
+ m_CompositionGen = new cCompoGenNether(Seed);
+ }
+ else if (NoCaseCompare(CompoGenName, "Noise3D") == 0)
+ {
+ m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed());
+ }
+ else
+ {
+ if (NoCaseCompare(CompoGenName, "biomal") != 0)
+ {
+ LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str());
+ }
+ m_CompositionGen = new cCompoGenBiomal(Seed);
+
+ /*
+ // Performance-testing:
+ LOGINFO("Measuring performance of cCompoGenBiomal...");
+ clock_t BeginTick = clock();
+ for (int x = 0; x < 500; x++)
+ {
+ cChunkDesc Desc(200 + x * 8, 200 + x * 8);
+ m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap());
+ m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap());
+ m_CompositionGen->ComposeTerrain(Desc);
+ }
+ clock_t Duration = clock() - BeginTick;
+ LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
+ //*/
+ }
+
+ // Read the settings from the ini file:
+ m_CompositionGen->InitializeCompoGen(a_IniFile);
+
+ int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64);
+ if (CompoGenCacheSize > 1)
+ {
+ m_UnderlyingCompositionGen = m_CompositionGen;
+ m_CompositionGen = new cCompoGenCache(*m_UnderlyingCompositionGen, 32);
+ }
+}
+
+
+
+
+
+void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile)
+{
+ AString Structures = a_IniFile.GetValueSet("Generator", "Structures", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees");
+
+ int Seed = m_ChunkGenerator.GetSeed();
+ AStringVector Str = StringSplitAndTrim(Structures, ",");
+ for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
+ {
+ if (NoCaseCompare(*itr, "DualRidgeCaves") == 0)
+ {
+ float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3);
+ m_StructureGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold));
+ }
+ else if (NoCaseCompare(*itr, "DirectOverhangs") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenDirectOverhangs(Seed));
+ }
+ else if (NoCaseCompare(*itr, "DistortedMembraneOverhangs") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenDistortedMembraneOverhangs(Seed));
+ }
+ else if (NoCaseCompare(*itr, "LavaLakes") == 0)
+ {
+ int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10);
+ m_StructureGens.push_back(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, *m_HeightGen, Probability));
+ }
+ else if (NoCaseCompare(*itr, "MarbleCaves") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenMarbleCaves(Seed));
+ }
+ else if (NoCaseCompare(*itr, "MineShafts") == 0)
+ {
+ int GridSize = a_IniFile.GetValueSetI("Generator", "MineShaftsGridSize", 512);
+ int MaxSystemSize = a_IniFile.GetValueSetI("Generator", "MineShaftsMaxSystemSize", 160);
+ int ChanceCorridor = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCorridor", 600);
+ int ChanceCrossing = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCrossing", 200);
+ int ChanceStaircase = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceStaircase", 200);
+ m_StructureGens.push_back(new cStructGenMineShafts(
+ Seed, GridSize, MaxSystemSize,
+ ChanceCorridor, ChanceCrossing, ChanceStaircase
+ ));
+ }
+ else if (NoCaseCompare(*itr, "OreNests") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenOreNests(Seed));
+ }
+ else if (NoCaseCompare(*itr, "Ravines") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenRavines(Seed, 128));
+ }
+ else if (NoCaseCompare(*itr, "Trees") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen));
+ }
+ else if (NoCaseCompare(*itr, "WaterLakes") == 0)
+ {
+ int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
+ m_StructureGens.push_back(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, *m_HeightGen, Probability));
+ }
+ else if (NoCaseCompare(*itr, "WormNestCaves") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenWormNestCaves(Seed));
+ }
+ else
+ {
+ LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str());
+ }
+ } // for itr - Str[]
+}
+
+
+
+
+
+void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
+{
+ int Seed = m_ChunkGenerator.GetSeed();
+ AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator");
+
+ AStringVector Str = StringSplitAndTrim(Structures, ",");
+ for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
+ {
+ // Finishers, alpha-sorted:
+ if (NoCaseCompare(*itr, "BottomLava") == 0)
+ {
+ int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10;
+ int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
+ m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel));
+ }
+ else if (NoCaseCompare(*itr, "DeadBushes") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, biDesert, 2, E_BLOCK_SAND, E_BLOCK_SAND));
+ }
+ else if (NoCaseCompare(*itr, "Ice") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenIce);
+ }
+ else if (NoCaseCompare(*itr, "LavaSprings") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World));
+ }
+ else if (NoCaseCompare(*itr, "Lilypads") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER));
+ }
+ else if (NoCaseCompare(*itr, "PreSimulator") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenPreSimulator);
+ }
+ else if (NoCaseCompare(*itr, "Snow") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenSnow);
+ }
+ else if (NoCaseCompare(*itr, "SprinkleFoliage") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenSprinkleFoliage(Seed));
+ }
+ else if (NoCaseCompare(*itr, "WaterSprings") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World));
+ }
+ } // for itr - Str[]
+}
+
+
+
+
diff --git a/src/Generating/ComposableGenerator.h b/src/Generating/ComposableGenerator.h
new file mode 100644
index 000000000..d5e33a439
--- /dev/null
+++ b/src/Generating/ComposableGenerator.h
@@ -0,0 +1,181 @@
+
+// ComposableGenerator.h
+
+// Declares the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks
+
+/*
+Generating works by composing several algorithms:
+Biome, TerrainHeight, TerrainComposition, Ores, Structures and SmallFoliage
+Each algorithm may be chosen from a pool of available algorithms in the same class and combined with others,
+based on user's preferences in the world.ini.
+See http://forum.mc-server.org/showthread.php?tid=409 for details.
+*/
+
+
+
+
+
+#pragma once
+
+#include "ChunkGenerator.h"
+#include "ChunkDesc.h"
+
+
+
+
+
+// fwd: Noise3DGenerator.h
+class cNoise3DComposable;
+
+// fwd: DistortedHeightmap.h
+class cDistortedHeightmap;
+
+
+
+
+
+/** The interface that a biome generator must implement
+A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output.
+The output array is sequenced in the same way as the MapChunk packet's biome data.
+*/
+class cBiomeGen
+{
+public:
+ virtual ~cBiomeGen() {} // Force a virtual destructor in descendants
+
+ /// Generates biomes for the given chunk
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
+
+ /// Reads parameters from the ini file, prepares generator for use.
+ virtual void InitializeBiomeGen(cIniFile & a_IniFile) {}
+} ;
+
+
+
+
+
+/** The interface that a terrain height generator must implement
+A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk.
+The output array is sequenced in the same way as the BiomeGen's biome data.
+The generator may request biome information from the underlying BiomeGen, it may even request information for
+other chunks than the one it's currently generating (possibly neighbors - for averaging)
+*/
+class cTerrainHeightGen
+{
+public:
+ virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
+
+ /// Generates heightmap for the given chunk
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
+
+ /// Reads parameters from the ini file, prepares generator for use.
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) {}
+} ;
+
+
+
+
+
+/** The interface that a terrain composition generator must implement
+Terrain composition takes chunk coords on input and outputs the blockdata for that entire chunk, along with
+the list of entities. It is supposed to make use of the underlying TerrainHeightGen and BiomeGen for that purpose,
+but it may request information for other chunks than the one it's currently generating from them.
+*/
+class cTerrainCompositionGen
+{
+public:
+ virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants
+
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0;
+
+ /// Reads parameters from the ini file, prepares generator for use.
+ virtual void InitializeCompoGen(cIniFile & a_IniFile) {}
+} ;
+
+
+
+
+
+/** The interface that a structure generator must implement
+Structures are generated after the terrain composition took place. It should modify the blocktype data to account
+for whatever structures the generator is generating.
+Note that ores are considered structures too, at least from the interface point of view.
+Also note that a worldgenerator may contain multiple structure generators, one for each type of structure
+*/
+class cStructureGen
+{
+public:
+ virtual ~cStructureGen() {} // Force a virtual destructor in descendants
+
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) = 0;
+} ;
+
+typedef std::list<cStructureGen *> cStructureGenList;
+
+
+
+
+
+/** The interface that a finisher must implement
+Finisher implements small additions after all structures have been generated.
+*/
+class cFinishGen
+{
+public:
+ virtual ~cFinishGen() {} // Force a virtual destructor in descendants
+
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) = 0;
+} ;
+
+typedef std::list<cFinishGen *> cFinishGenList;
+
+
+
+
+
+class cComposableGenerator :
+ public cChunkGenerator::cGenerator
+{
+ typedef cChunkGenerator::cGenerator super;
+
+public:
+ cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
+ virtual ~cComposableGenerator();
+
+ virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
+ virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
+
+protected:
+ // The generation composition:
+ cBiomeGen * m_BiomeGen;
+ cTerrainHeightGen * m_HeightGen;
+ cTerrainCompositionGen * m_CompositionGen;
+ cStructureGenList m_StructureGens;
+ cFinishGenList m_FinishGens;
+
+ // Generators underlying the caches:
+ cBiomeGen * m_UnderlyingBiomeGen;
+ cTerrainHeightGen * m_UnderlyingHeightGen;
+ cTerrainCompositionGen * m_UnderlyingCompositionGen;
+
+
+ /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly
+ void InitBiomeGen(cIniFile & a_IniFile);
+
+ /// Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly
+ void InitHeightGen(cIniFile & a_IniFile);
+
+ /// Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly
+ void InitCompositionGen(cIniFile & a_IniFile);
+
+ /// Reads the structures to generate from the ini and initializes m_StructureGens accordingly
+ void InitStructureGens(cIniFile & a_IniFile);
+
+ /// Reads the finishers from the ini and initializes m_FinishGens accordingly
+ void InitFinishGens(cIniFile & a_IniFile);
+} ;
+
+
+
+
diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp
new file mode 100644
index 000000000..6fbe7c1d4
--- /dev/null
+++ b/src/Generating/DistortedHeightmap.cpp
@@ -0,0 +1,444 @@
+
+// DistortedHeightmap.cpp
+
+// Implements the cDistortedHeightmap class representing the height and composition generator capable of overhangs
+
+#include "Globals.h"
+
+#include "DistortedHeightmap.h"
+#include "../OSSupport/File.h"
+#include "lib/inifile/iniFile.h"
+#include "../LinearUpscale.h"
+
+
+
+
+
+/** This table assigns a relative maximum overhang size in each direction to biomes.
+Both numbers indicate a number which will multiply the noise value for each coord;
+this means that you can have different-sized overhangs in each direction.
+Usually you'd want to keep both numbers the same.
+The numbers are "relative", not absolute maximum; overhangs of a slightly larger size are possible
+due to the way that noise is calculated.
+*/
+const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[biNumBiomes] =
+{
+ /* Biome | AmpX | AmpZ */
+ /* biOcean */ { 1.5f, 1.5f},
+ /* biPlains */ { 0.5f, 0.5f},
+ /* biDesert */ { 0.5f, 0.5f},
+ /* biExtremeHills */ {16.0f, 16.0f},
+ /* biForest */ { 3.0f, 3.0f},
+ /* biTaiga */ { 1.5f, 1.5f},
+
+ /* biSwampland */ { 0.0f, 0.0f},
+ /* biRiver */ { 0.0f, 0.0f},
+ /* biNether */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing
+ /* biSky */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing
+ /* biFrozenOcean */ { 0.0f, 0.0f},
+ /* biFrozenRiver */ { 0.0f, 0.0f},
+ /* biIcePlains */ { 0.0f, 0.0f},
+ /* biIceMountains */ { 8.0f, 8.0f},
+ /* biMushroomIsland */ { 4.0f, 4.0f},
+ /* biMushroomShore */ { 0.0f, 0.0f},
+ /* biBeach */ { 0.0f, 0.0f},
+ /* biDesertHills */ { 5.0f, 5.0f},
+ /* biForestHills */ { 6.0f, 6.0f},
+ /* biTaigaHills */ { 8.0f, 8.0f},
+ /* biExtremeHillsEdge */ { 7.0f, 7.0f},
+ /* biJungle */ { 0.0f, 0.0f},
+ /* biJungleHills */ { 8.0f, 8.0f},
+} ;
+
+
+
+
+
+cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) :
+ m_NoiseDistortX(a_Seed + 1000),
+ m_NoiseDistortZ(a_Seed + 2000),
+ m_OceanFloorSelect(a_Seed + 3000),
+ m_BiomeGen(a_BiomeGen),
+ m_UnderlyingHeiGen(a_Seed, a_BiomeGen),
+ m_HeightGen(m_UnderlyingHeiGen, 64)
+{
+ m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
+ m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
+ m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
+
+ m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
+ m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
+ m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
+}
+
+
+
+
+
+void cDistortedHeightmap::Initialize(cIniFile & a_IniFile)
+{
+ if (m_IsInitialized)
+ {
+ return;
+ }
+
+ // Read the params from the INI file:
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62);
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10);
+
+ m_IsInitialized = true;
+}
+
+
+
+
+
+void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ)
+{
+ if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ))
+ {
+ return;
+ }
+ m_CurChunkX = a_ChunkX;
+ m_CurChunkZ = a_ChunkZ;
+
+
+ m_HeightGen.GenHeightMap(a_ChunkX, a_ChunkZ, m_CurChunkHeights);
+ UpdateDistortAmps();
+ GenerateHeightArray();
+}
+
+
+
+
+
+void cDistortedHeightmap::GenerateHeightArray(void)
+{
+ // Generate distortion noise:
+ NOISE_DATATYPE DistortNoiseX[DIM_X * DIM_Y * DIM_Z];
+ NOISE_DATATYPE DistortNoiseZ[DIM_X * DIM_Y * DIM_Z];
+ NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z];
+ NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_CurChunkX + 1) * cChunkDef::Width - 1)) / m_FrequencyX;
+ NOISE_DATATYPE StartY = 0;
+ NOISE_DATATYPE EndY = ((NOISE_DATATYPE)(257)) / m_FrequencyY;
+ NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width)) / m_FrequencyZ;
+ NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_CurChunkZ + 1) * cChunkDef::Width - 1)) / m_FrequencyZ;
+
+ m_NoiseDistortX.Generate3D(DistortNoiseX, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace);
+ m_NoiseDistortZ.Generate3D(DistortNoiseZ, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace);
+
+ // The distorted heightmap, before linear upscaling
+ NOISE_DATATYPE DistHei[DIM_X * DIM_Y * DIM_Z];
+
+ // Distort the heightmap using the distortion:
+ for (int z = 0; z < DIM_Z; z++)
+ {
+ int AmpIdx = z * DIM_X;
+ for (int y = 0; y < DIM_Y; y++)
+ {
+ int NoiseArrayIdx = z * DIM_X * DIM_Y + y * DIM_X;
+ for (int x = 0; x < DIM_X; x++)
+ {
+ NOISE_DATATYPE DistX = DistortNoiseX[NoiseArrayIdx + x] * m_DistortAmpX[AmpIdx + x];
+ NOISE_DATATYPE DistZ = DistortNoiseZ[NoiseArrayIdx + x] * m_DistortAmpZ[AmpIdx + x];
+ DistX += (NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x * INTERPOL_X);
+ DistZ += (NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z * INTERPOL_Z);
+ // Adding 0.5 helps alleviate the interpolation artifacts
+ DistHei[NoiseArrayIdx + x] = (NOISE_DATATYPE)GetHeightmapAt(DistX, DistZ) + (NOISE_DATATYPE)0.5;
+ }
+ }
+ }
+
+ // Upscale the distorted heightmap into full dimensions:
+ LinearUpscale3DArray(
+ DistHei, DIM_X, DIM_Y, DIM_Z,
+ m_DistortedHeightmap, INTERPOL_X, INTERPOL_Y, INTERPOL_Z
+ );
+
+ // DEBUG: Debug3DNoise(m_DistortedHeightmap, 17, 257, 17, Printf("DistortedHeightmap_%d_%d", m_CurChunkX, m_CurChunkZ));
+}
+
+
+
+
+
+void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ PrepareState(a_ChunkX, a_ChunkZ);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int NoiseArrayIdx = x + 17 * 257 * z;
+ cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1);
+ for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--)
+ {
+ int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
+ if (y < HeightMapHeight)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, y);
+ break;
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile)
+{
+ Initialize(a_IniFile);
+}
+
+
+
+
+
+void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ // Frequencies for the ocean floor selecting noise:
+ NOISE_DATATYPE FrequencyX = 3;
+ NOISE_DATATYPE FrequencyZ = 3;
+
+ // Prepare the internal state for generating this chunk:
+ PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
+
+ // Compose:
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int NoiseArrayIdx = x + 17 * 257 * z;
+ int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
+ bool HasHadWater = false;
+ for (int y = LastAir - 1; y > 0; y--)
+ {
+ int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
+
+ if (y >= HeightMapHeight)
+ {
+ // "air" part
+ LastAir = y;
+ if (y < m_SeaLevel)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
+ HasHadWater = true;
+ }
+ continue;
+ }
+ // "ground" part:
+ if (y < LastAir - 4)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
+ continue;
+ }
+ if (HasHadWater)
+ {
+ // Decide between clay, sand and dirt
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x)) / FrequencyX;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z)) / FrequencyZ;
+ NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
+ if (Val < -0.95)
+ {
+ // Clay:
+ switch (LastAir - y)
+ {
+ case 0:
+ case 1:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_CLAY);
+ break;
+ }
+ case 2:
+ case 3:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
+ break;
+ }
+ case 4:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SANDSTONE);
+ break;
+ }
+ } // switch (floor depth)
+ }
+ else if (Val < 0)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND);
+ }
+ else
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_DIRT);
+ }
+ }
+ else
+ {
+ switch (a_ChunkDesc.GetBiome(x, z))
+ {
+ case biOcean:
+ case biPlains:
+ case biExtremeHills:
+ case biForest:
+ case biTaiga:
+ case biSwampland:
+ case biRiver:
+ case biFrozenOcean:
+ case biFrozenRiver:
+ case biIcePlains:
+ case biIceMountains:
+ case biForestHills:
+ case biTaigaHills:
+ case biExtremeHillsEdge:
+ case biJungle:
+ case biJungleHills:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
+ break;
+ }
+ case biDesertHills:
+ case biDesert:
+ case biBeach:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND);
+ break;
+ }
+ case biMushroomIsland:
+ case biMushroomShore:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_MYCELIUM : E_BLOCK_DIRT);
+ break;
+ }
+ }
+ }
+ } // for y
+ a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cDistortedHeightmap::InitializeCompoGen(cIniFile & a_IniFile)
+{
+ Initialize(a_IniFile);
+}
+
+
+
+
+
+int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z)
+{
+ int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16);
+ int ChunkZ = (int)floor(a_Z / (NOISE_DATATYPE)16);
+ int RelX = (int)(a_X - (NOISE_DATATYPE)ChunkX * cChunkDef::Width);
+ int RelZ = (int)(a_Z - (NOISE_DATATYPE)ChunkZ * cChunkDef::Width);
+
+ // If we're withing the same chunk, return the pre-cached heightmap:
+ if ((ChunkX == m_CurChunkX) && (ChunkZ == m_CurChunkZ))
+ {
+ return cChunkDef::GetHeight(m_CurChunkHeights, RelX, RelZ);
+ }
+
+ // Ask the cache:
+ HEIGHTTYPE res = 0;
+ if (m_HeightGen.GetHeightAt(ChunkX, ChunkZ, RelX, RelZ, res))
+ {
+ // The height was in the cache
+ return res;
+ }
+
+ // The height is not in the cache, generate full heightmap and get it there:
+ cChunkDef::HeightMap Heightmap;
+ m_HeightGen.GenHeightMap(ChunkX, ChunkZ, Heightmap);
+ return cChunkDef::GetHeight(Heightmap, RelX, RelZ);
+}
+
+
+
+
+
+void cDistortedHeightmap::UpdateDistortAmps(void)
+{
+ BiomeNeighbors Biomes;
+ for (int z = -1; z <= 1; z++)
+ {
+ for (int x = -1; x <= 1; x++)
+ {
+ m_BiomeGen.GenBiomes(m_CurChunkX + x, m_CurChunkZ + z, Biomes[x + 1][z + 1]);
+ } // for x
+ } // for z
+
+ for (int z = 0; z < DIM_Z; z++)
+ {
+ for (int x = 0; x < DIM_Z; x++)
+ {
+ GetDistortAmpsAt(Biomes, x * INTERPOL_X, z * INTERPOL_Z, m_DistortAmpX[x + DIM_X * z], m_DistortAmpZ[x + DIM_X * z]);
+ }
+ }
+}
+
+
+
+
+
+void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ)
+{
+ // Sum up how many biomes of each type there are in the neighborhood:
+ int BiomeCounts[biNumBiomes];
+ memset(BiomeCounts, 0, sizeof(BiomeCounts));
+ int Sum = 0;
+ for (int z = -8; z <= 8; z++)
+ {
+ int FinalZ = a_RelZ + z + cChunkDef::Width;
+ int IdxZ = FinalZ / cChunkDef::Width;
+ int ModZ = FinalZ % cChunkDef::Width;
+ int WeightZ = 9 - abs(z);
+ for (int x = -8; x <= 8; x++)
+ {
+ int FinalX = a_RelX + x + cChunkDef::Width;
+ int IdxX = FinalX / cChunkDef::Width;
+ int ModX = FinalX % cChunkDef::Width;
+ EMCSBiome Biome = cChunkDef::GetBiome(a_Neighbors[IdxX][IdxZ], ModX, ModZ);
+ if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts)))
+ {
+ continue;
+ }
+ int WeightX = 9 - abs(x);
+ BiomeCounts[Biome] += WeightX + WeightZ;
+ Sum += WeightX + WeightZ;
+ } // for x
+ } // for z
+
+ if (Sum <= 0)
+ {
+ // No known biome around? Weird. Return a bogus value:
+ ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around");
+ a_DistortAmpX = 16;
+ a_DistortAmpZ = 16;
+ }
+
+ // For each biome type that has a nonzero count, calc its amps and add it:
+ NOISE_DATATYPE AmpX = 0;
+ NOISE_DATATYPE AmpZ = 0;
+ for (unsigned int i = 0; i < ARRAYCOUNT(BiomeCounts); i++)
+ {
+ AmpX += BiomeCounts[i] * m_GenParam[i].m_DistortAmpX;
+ AmpZ += BiomeCounts[i] * m_GenParam[i].m_DistortAmpZ;
+ }
+ a_DistortAmpX = AmpX / Sum;
+ a_DistortAmpZ = AmpZ / Sum;
+}
+
+
+
+
diff --git a/src/Generating/DistortedHeightmap.h b/src/Generating/DistortedHeightmap.h
new file mode 100644
index 000000000..6d7007375
--- /dev/null
+++ b/src/Generating/DistortedHeightmap.h
@@ -0,0 +1,108 @@
+
+// DistortedHeightmap.h
+
+// Declares the cDistortedHeightmap class representing the height and composition generator capable of overhangs
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "HeiGen.h"
+#include "../Noise.h"
+
+
+
+
+
+#define NOISE_SIZE_Y (257 + 32)
+
+
+
+
+
+class cDistortedHeightmap :
+ public cTerrainHeightGen,
+ public cTerrainCompositionGen
+{
+public:
+ cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen);
+
+protected:
+ typedef cChunkDef::BiomeMap BiomeNeighbors[3][3];
+
+ // Linear upscaling step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
+ static const int INTERPOL_X = 8;
+ static const int INTERPOL_Y = 4;
+ static const int INTERPOL_Z = 8;
+
+ // Linear upscaling buffer dimensions, calculated from the step sizes:
+ static const int DIM_X = 1 + (17 / INTERPOL_X);
+ static const int DIM_Y = 1 + (257 / INTERPOL_Y);
+ static const int DIM_Z = 1 + (17 / INTERPOL_Z);
+
+ cPerlinNoise m_NoiseDistortX;
+ cPerlinNoise m_NoiseDistortZ;
+ cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor
+
+ int m_SeaLevel;
+ NOISE_DATATYPE m_FrequencyX;
+ NOISE_DATATYPE m_FrequencyY;
+ NOISE_DATATYPE m_FrequencyZ;
+
+ int m_CurChunkX;
+ int m_CurChunkZ;
+ NOISE_DATATYPE m_DistortedHeightmap[17 * 257 * 17];
+
+ cBiomeGen & m_BiomeGen;
+ cHeiGenBiomal m_UnderlyingHeiGen; // This generator provides us with base heightmap (before distortion)
+ cHeiGenCache m_HeightGen; // Cache above m_UnderlyingHeiGen
+
+ /// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization.
+ cChunkDef::HeightMap m_CurChunkHeights;
+
+ // Per-biome terrain generator parameters:
+ struct sGenParam
+ {
+ NOISE_DATATYPE m_DistortAmpX;
+ NOISE_DATATYPE m_DistortAmpZ;
+ } ;
+ static const sGenParam m_GenParam[biNumBiomes];
+
+ // Distortion amplitudes for each direction, before linear upscaling
+ NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z];
+ NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z];
+
+ /// True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen)
+ bool m_IsInitialized;
+
+
+ /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap)
+ void PrepareState(int a_ChunkX, int a_ChunkZ);
+
+ /// Generates the m_DistortedHeightmap array for the current chunk
+ void GenerateHeightArray(void);
+
+ /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords
+ int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z);
+
+ /// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ
+ void UpdateDistortAmps(void);
+
+ /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes
+ void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ);
+
+ /// Reads the settings from the ini file. Skips reading if already initialized
+ void Initialize(cIniFile & a_IniFile);
+
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
+} ;
diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp
new file mode 100644
index 000000000..121ed4488
--- /dev/null
+++ b/src/Generating/EndGen.cpp
@@ -0,0 +1,217 @@
+
+// EndGen.cpp
+
+// Implements the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen
+
+#include "Globals.h"
+#include "EndGen.h"
+#include "lib/inifile/iniFile.h"
+#include "../LinearUpscale.h"
+
+
+
+
+
+enum
+{
+ // Interpolation cell size:
+ INTERPOL_X = 4,
+ INTERPOL_Y = 4,
+ INTERPOL_Z = 4,
+
+ // Size of chunk data, downscaled before interpolation:
+ DIM_X = 16 / INTERPOL_X + 1,
+ DIM_Y = 256 / INTERPOL_Y + 1,
+ DIM_Z = 16 / INTERPOL_Z + 1,
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cEndGen:
+
+cEndGen::cEndGen(int a_Seed) :
+ m_Seed(a_Seed),
+ m_IslandSizeX(256),
+ m_IslandSizeY(96),
+ m_IslandSizeZ(256),
+ m_FrequencyX(80),
+ m_FrequencyY(80),
+ m_FrequencyZ(80)
+{
+ m_Perlin.AddOctave(1, 1);
+ m_Perlin.AddOctave(2, 0.5);
+ m_Perlin.AddOctave(4, 0.25);
+}
+
+
+
+
+
+void cEndGen::Initialize(cIniFile & a_IniFile)
+{
+ m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX);
+ m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY);
+ m_IslandSizeZ = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeZ", m_IslandSizeZ);
+
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyX", m_FrequencyX);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyY", m_FrequencyY);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyZ", m_FrequencyZ);
+
+ // Recalculate the min and max chunk coords of the island
+ m_MaxChunkX = (m_IslandSizeX + cChunkDef::Width - 1) / cChunkDef::Width;
+ m_MinChunkX = -m_MaxChunkX;
+ m_MaxChunkZ = (m_IslandSizeZ + cChunkDef::Width - 1) / cChunkDef::Width;
+ m_MinChunkZ = -m_MaxChunkZ;
+}
+
+
+
+
+
+/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array)
+void cEndGen::PrepareState(int a_ChunkX, int a_ChunkZ)
+{
+ ASSERT(!IsChunkOutsideRange(a_ChunkX, a_ChunkZ)); // Should be filtered before calling this function
+
+ if ((m_LastChunkX == a_ChunkX) && (m_LastChunkZ == a_ChunkZ))
+ {
+ return;
+ }
+
+ m_LastChunkX = a_ChunkX;
+ m_LastChunkZ = a_ChunkZ;
+
+ GenerateNoiseArray();
+}
+
+
+
+
+
+/// Generates the m_NoiseArray array for the current chunk
+void cEndGen::GenerateNoiseArray(void)
+{
+ NOISE_DATATYPE NoiseData[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y]
+ NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y]
+
+ // Generate the downscaled noise:
+ NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_LastChunkX + 1) * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width)) / m_FrequencyZ;
+ NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_LastChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ;
+ NOISE_DATATYPE StartY = 0;
+ NOISE_DATATYPE EndY = ((NOISE_DATATYPE)257) / m_FrequencyY;
+ m_Perlin.Generate3D(NoiseData, DIM_X, DIM_Z, DIM_Y, StartX, EndX, StartZ, EndZ, StartY, EndY, Workspace);
+
+ // Add distance:
+ int idx = 0;
+ for (int y = 0; y < DIM_Y; y++)
+ {
+ NOISE_DATATYPE ValY = (NOISE_DATATYPE)(2 * INTERPOL_Y * y - m_IslandSizeY) / m_IslandSizeY;
+ ValY = ValY * ValY;
+ for (int z = 0; z < DIM_Z; z++)
+ {
+ NOISE_DATATYPE ValZ = (NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width + (z * cChunkDef::Width / (DIM_Z - 1))) / m_IslandSizeZ;
+ ValZ = ValZ * ValZ;
+ for (int x = 0; x < DIM_X; x++)
+ {
+ // NOISE_DATATYPE ValX = StartX + (EndX - StartX) * x / (DIM_X - 1);
+ NOISE_DATATYPE ValX = (NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width + (x * cChunkDef::Width / (DIM_X - 1))) / m_IslandSizeX;
+ ValX = ValX * ValX;
+ NoiseData[idx++] += ValX + ValZ + ValY;
+ } // for x
+ } // for z
+ } // for y
+
+ // Upscale into real chunk size:
+ LinearUpscale3DArray(NoiseData, DIM_X, DIM_Z, DIM_Y, m_NoiseArray, INTERPOL_X, INTERPOL_Z, INTERPOL_Y);
+}
+
+
+
+
+
+/// Returns true if the chunk is outside of the island's dimensions
+bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ)
+{
+ return (
+ (a_ChunkX < m_MinChunkX) || (a_ChunkX > m_MaxChunkX) ||
+ (a_ChunkZ < m_MinChunkZ) || (a_ChunkZ > m_MaxChunkZ)
+ );
+}
+
+
+
+
+
+void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ))
+ {
+ for (unsigned int i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
+ {
+ a_HeightMap[i] = 0;
+ }
+ return;
+ }
+
+ PrepareState(a_ChunkX, a_ChunkZ);
+
+ int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, MaxY);
+ for (int y = MaxY; y > 0; y--)
+ {
+ if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, y);
+ break;
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()))
+ {
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+ return;
+ }
+
+ PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
+
+ int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int y = MaxY; y > 0; y--)
+ {
+ if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0);
+ }
+ else
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0);
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
diff --git a/src/Generating/EndGen.h b/src/Generating/EndGen.h
new file mode 100644
index 000000000..4904a0e3d
--- /dev/null
+++ b/src/Generating/EndGen.h
@@ -0,0 +1,69 @@
+
+// EndGen.h
+
+// Declares the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cEndGen :
+ public cTerrainHeightGen,
+ public cTerrainCompositionGen
+{
+public:
+ cEndGen(int a_Seed);
+
+ void Initialize(cIniFile & a_IniFile);
+
+protected:
+
+ /// Seed for the noise
+ int m_Seed;
+
+ /// The Perlin noise used for generating
+ cPerlinNoise m_Perlin;
+
+ // XYZ size of the "island", in blocks:
+ int m_IslandSizeX;
+ int m_IslandSizeY;
+ int m_IslandSizeZ;
+
+ // XYZ Frequencies of the noise functions:
+ NOISE_DATATYPE m_FrequencyX;
+ NOISE_DATATYPE m_FrequencyY;
+ NOISE_DATATYPE m_FrequencyZ;
+
+ // Minimum and maximum chunk coords for chunks inside the island area. Chunks outside won't get calculated at all
+ int m_MinChunkX, m_MaxChunkX;
+ int m_MinChunkZ, m_MaxChunkZ;
+
+ // Noise array for the last chunk (in the noise range)
+ int m_LastChunkX;
+ int m_LastChunkZ;
+ NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
+
+ /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array)
+ void PrepareState(int a_ChunkX, int a_ChunkZ);
+
+ /// Generates the m_NoiseArray array for the current chunk
+ void GenerateNoiseArray(void);
+
+ /// Returns true if the chunk is outside of the island's dimensions
+ bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ);
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+} ;
diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp
new file mode 100644
index 000000000..8899e4bd0
--- /dev/null
+++ b/src/Generating/FinishGen.cpp
@@ -0,0 +1,664 @@
+
+// FinishGen.cpp
+
+/* Implements the various finishing generators:
+ - cFinishGenSnow
+ - cFinishGenIce
+ - cFinishGenSprinkleFoliage
+*/
+
+#include "Globals.h"
+
+#include "FinishGen.h"
+#include "../Noise.h"
+#include "../BlockID.h"
+#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway()
+#include "../World.h"
+
+
+
+
+
+#define DEF_NETHER_WATER_SPRINGS "0, 1; 255, 1"
+#define DEF_NETHER_LAVA_SPRINGS "0, 0; 30, 0; 31, 50; 120, 50; 127, 0"
+#define DEF_OVERWORLD_WATER_SPRINGS "0, 0; 10, 10; 11, 75; 16, 83; 20, 83; 24, 78; 32, 62; 40, 40; 44, 15; 48, 7; 56, 2; 64, 1; 255, 0"
+#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0"
+#define DEF_END_WATER_SPRINGS "0, 1; 255, 1"
+#define DEF_END_LAVA_SPRINGS "0, 1; 255, 1"
+
+
+
+
+
+static inline bool IsWater(BLOCKTYPE a_BlockType)
+{
+ return (a_BlockType == E_BLOCK_STATIONARY_WATER) || (a_BlockType == E_BLOCK_WATER);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenSprinkleFoliage:
+
+bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ)
+{
+ // We'll be doing comparison to neighbors, so require the coords to be 1 block away from the chunk edges:
+ if (
+ (a_RelX < 1) || (a_RelX >= cChunkDef::Width - 1) ||
+ (a_RelY < 1) || (a_RelY >= cChunkDef::Height - 2) ||
+ (a_RelZ < 1) || (a_RelZ >= cChunkDef::Width - 1)
+ )
+ {
+ return false;
+ }
+
+ // Only allow dirt, grass or sand below sugarcane:
+ switch (a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ))
+ {
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_SAND:
+ {
+ break;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+
+ // Water is required next to the block below the sugarcane:
+ if (
+ !IsWater(a_ChunkDesc.GetBlockType(a_RelX - 1, a_RelY, a_RelZ)) &&
+ !IsWater(a_ChunkDesc.GetBlockType(a_RelX + 1, a_RelY, a_RelZ)) &&
+ !IsWater(a_ChunkDesc.GetBlockType(a_RelX , a_RelY, a_RelZ - 1)) &&
+ !IsWater(a_ChunkDesc.GetBlockType(a_RelX , a_RelY, a_RelZ + 1))
+ )
+ {
+ return false;
+ }
+
+ // All conditions met, place a sugarcane here:
+ a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE);
+ return true;
+}
+
+
+
+
+
+void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ // Generate small foliage (1-block):
+
+ // TODO: Update heightmap with 1-block-tall foliage
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z;
+ const float zz = (float)BlockZ;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width + x;
+ if (((m_Noise.IntNoise2DInt(BlockX, BlockZ) / 8) % 128) < 124)
+ {
+ continue;
+ }
+ int Top = a_ChunkDesc.GetHeight(x, z);
+ if (Top > 250)
+ {
+ // Nothing grows above Y=250
+ continue;
+ }
+ if (a_ChunkDesc.GetBlockType(x, Top + 1, z) != E_BLOCK_AIR)
+ {
+ // Space already taken by something else, don't grow here
+ // WEIRD, since we're using heightmap, so there should NOT be anything above it
+ continue;
+ }
+
+ const float xx = (float)BlockX;
+ float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f );
+ float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f );
+ switch (a_ChunkDesc.GetBlockType(x, Top, z))
+ {
+ case E_BLOCK_GRASS:
+ {
+ float val3 = m_Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10 );
+ float val4 = m_Noise.CubicNoise2D(xx * 0.05f + 20, zz * 0.05f + 20 );
+ if (val1 + val2 > 0.2f)
+ {
+ a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_YELLOW_FLOWER);
+ }
+ else if (val2 + val3 > 0.2f)
+ {
+ a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_ROSE);
+ }
+ else if (val3 + val4 > 0.2f)
+ {
+ a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_MUSHROOM);
+ }
+ else if (val1 + val4 > 0.2f)
+ {
+ a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_BROWN_MUSHROOM);
+ }
+ else if (val1 + val2 + val3 + val4 < -0.1)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_TALL_GRASS, E_META_TALL_GRASS_GRASS);
+ }
+ else if (TryAddSugarcane(a_ChunkDesc, x, Top, z))
+ {
+ ++Top;
+ }
+ else if ((val1 > 0.5) && (val2 < -0.5))
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_PUMPKIN, (int)(val3 * 8) % 4);
+ }
+ break;
+ } // case E_BLOCK_GRASS
+
+ case E_BLOCK_SAND:
+ {
+ int y = Top + 1;
+ if (
+ (x > 0) && (x < cChunkDef::Width - 1) &&
+ (z > 0) && (z < cChunkDef::Width - 1) &&
+ (val1 + val2 > 0.5f) &&
+ (a_ChunkDesc.GetBlockType(x + 1, y, z) == E_BLOCK_AIR) &&
+ (a_ChunkDesc.GetBlockType(x - 1, y, z) == E_BLOCK_AIR) &&
+ (a_ChunkDesc.GetBlockType(x, y, z + 1) == E_BLOCK_AIR) &&
+ (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR)
+ )
+ {
+ a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS);
+ }
+ else if (TryAddSugarcane(a_ChunkDesc, x, Top, z))
+ {
+ ++Top;
+ }
+ break;
+ }
+ } // switch (TopBlock)
+ a_ChunkDesc.SetHeight(x, z, Top);
+ } // for y
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenSnow:
+
+void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ // Add a snow block in snowy biomes onto blocks that can be snowed over
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ switch (a_ChunkDesc.GetBiome(x, z))
+ {
+ case biIcePlains:
+ case biIceMountains:
+ case biTaiga:
+ case biTaigaHills:
+ case biFrozenRiver:
+ case biFrozenOcean:
+ {
+ int Height = a_ChunkDesc.GetHeight(x, z);
+ if (g_BlockIsSnowable[a_ChunkDesc.GetBlockType(x, Height, z)])
+ {
+ a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW);
+ a_ChunkDesc.SetHeight(x, z, Height + 1);
+ }
+ break;
+ }
+ }
+ }
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenIce:
+
+void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ // Turn surface water into ice in icy biomes
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ switch (a_ChunkDesc.GetBiome(x, z))
+ {
+ case biIcePlains:
+ case biIceMountains:
+ case biTaiga:
+ case biTaigaHills:
+ case biFrozenRiver:
+ case biFrozenOcean:
+ {
+ int Height = a_ChunkDesc.GetHeight(x, z);
+ switch (a_ChunkDesc.GetBlockType(x, Height, z))
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_ICE);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenLilypads:
+
+int cFinishGenSingleBiomeSingleTopBlock::GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap)
+{
+ int res = 0;
+ for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
+ {
+ if (a_BiomeMap[i] == m_Biome)
+ {
+ res++;
+ }
+ } // for i - a_BiomeMap[]
+ return m_Amount * res / 256;
+}
+
+
+
+
+
+void cFinishGenSingleBiomeSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ // Add Lilypads on top of water surface in Swampland
+
+ int NumToGen = GetNumToGen(a_ChunkDesc.GetBiomeMap());
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+ for (int i = 0; i < NumToGen; i++)
+ {
+ int x = (m_Noise.IntNoise3DInt(ChunkX + ChunkZ, ChunkZ, i) / 13) % cChunkDef::Width;
+ int z = (m_Noise.IntNoise3DInt(ChunkX - ChunkZ, i, ChunkZ) / 11) % cChunkDef::Width;
+
+ // Place the block at {x, z} if possible:
+ if (a_ChunkDesc.GetBiome(x, z) != m_Biome)
+ {
+ // Incorrect biome
+ continue;
+ }
+ int Height = a_ChunkDesc.GetHeight(x, z);
+ if (Height >= cChunkDef::Height)
+ {
+ // Too high up
+ continue;
+ }
+ if (a_ChunkDesc.GetBlockType(x, Height + 1, z) != E_BLOCK_AIR)
+ {
+ // Not an empty block
+ continue;
+ }
+ BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, Height, z);
+ if ((BlockBelow == m_AllowedBelow1) || (BlockBelow == m_AllowedBelow2))
+ {
+ a_ChunkDesc.SetBlockType(x, Height + 1, z, m_BlockType);
+ a_ChunkDesc.SetHeight(x, z, Height + 1);
+ }
+ } // for i
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenBottomLava:
+
+void cFinishGenBottomLava::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes();
+ for (int y = m_Level; y > 0; y--)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int Index = cChunkDef::MakeIndexNoCheck(x, y, z);
+ if (BlockTypes[Index] == E_BLOCK_AIR)
+ {
+ BlockTypes[Index] = E_BLOCK_STATIONARY_LAVA;
+ }
+ } // for x, for z
+ } // for y
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenPreSimulator:
+
+cFinishGenPreSimulator::cFinishGenPreSimulator(void)
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+void cFinishGenPreSimulator::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ CollapseSandGravel(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
+ StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
+ StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA);
+ // TODO: other operations
+}
+
+
+
+
+
+void cFinishGenPreSimulator::CollapseSandGravel(
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data
+)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int LastY = -1;
+ int HeightY = 0;
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z);
+ switch (Block)
+ {
+ default:
+ {
+ // Set the last block onto which stuff can fall to this height:
+ LastY = y;
+ HeightY = y;
+ break;
+ }
+ case E_BLOCK_AIR:
+ {
+ // Do nothing
+ break;
+ }
+ case E_BLOCK_FIRE:
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ // Do nothing, only remember this height as potentially highest
+ HeightY = y;
+ break;
+ }
+ case E_BLOCK_SAND:
+ case E_BLOCK_GRAVEL:
+ {
+ if (LastY < y - 1)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, LastY + 1, z, Block);
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
+ }
+ LastY++;
+ if (LastY > HeightY)
+ {
+ HeightY = LastY;
+ }
+ break;
+ }
+ } // switch (GetBlock)
+ } // for y
+ cChunkDef::SetHeight(a_HeightMap, x, z, HeightY);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cFinishGenPreSimulator::StationarizeFluid(
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read
+ BLOCKTYPE a_Fluid,
+ BLOCKTYPE a_StationaryFluid
+)
+{
+ // Turn fluid in the middle to stationary, unless it has air or washable block next to it:
+ for (int z = 1; z < cChunkDef::Width - 1; z++)
+ {
+ for (int x = 1; x < cChunkDef::Width - 1; x++)
+ {
+ for (int y = cChunkDef::GetHeight(a_HeightMap, x, z); y >= 0; y--)
+ {
+ BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z);
+ if ((Block != a_Fluid) && (Block != a_StationaryFluid))
+ {
+ continue;
+ }
+ static const struct
+ {
+ int x, y, z;
+ } Coords[] =
+ {
+ {1, 0, 0},
+ {-1, 0, 0},
+ {0, 0, 1},
+ {0, 0, -1},
+ {0, -1, 0}
+ } ;
+ BLOCKTYPE BlockToSet = a_StationaryFluid; // By default, don't simulate this block
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ if ((y == 0) && (Coords[i].y < 0))
+ {
+ continue;
+ }
+ BLOCKTYPE Neighbor = cChunkDef::GetBlock(a_BlockTypes, x + Coords[i].x, y + Coords[i].y, z + Coords[i].z);
+ if ((Neighbor == E_BLOCK_AIR) || cFluidSimulator::CanWashAway(Neighbor))
+ {
+ // There is an air / washable neighbor, simulate this block
+ BlockToSet = a_Fluid;
+ break;
+ }
+ } // for i - Coords[]
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, BlockToSet);
+ } // for y
+ } // for x
+ } // for z
+
+ // Turn fluid at the chunk edges into non-stationary fluid:
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int i = 0; i < cChunkDef::Width; i++) // i stands for both x and z here
+ {
+ if (cChunkDef::GetBlock(a_BlockTypes, 0, y, i) == a_StationaryFluid)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, 0, y, i, a_Fluid);
+ }
+ if (cChunkDef::GetBlock(a_BlockTypes, i, y, 0) == a_StationaryFluid)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, i, y, 0, a_Fluid);
+ }
+ if (cChunkDef::GetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i) == a_StationaryFluid)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i, a_Fluid);
+ }
+ if (cChunkDef::GetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1) == a_StationaryFluid)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1, a_Fluid);
+ }
+ }
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenFluidSprings:
+
+cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World) :
+ m_Noise(a_Seed + a_Fluid * 100), // Need to take fluid into account, otherwise water and lava springs generate next to each other
+ m_HeightDistribution(255),
+ m_Fluid(a_Fluid)
+{
+ bool IsWater = (a_Fluid == E_BLOCK_WATER);
+ AString SectionName = IsWater ? "WaterSprings" : "LavaSprings";
+ AString DefaultHeightDistribution;
+ int DefaultChance;
+ switch (a_World.GetDimension())
+ {
+ case dimNether:
+ {
+ DefaultHeightDistribution = IsWater ? DEF_NETHER_WATER_SPRINGS : DEF_NETHER_LAVA_SPRINGS;
+ DefaultChance = IsWater ? 0 : 15;
+ break;
+ }
+ case dimOverworld:
+ {
+ DefaultHeightDistribution = IsWater ? DEF_OVERWORLD_WATER_SPRINGS : DEF_OVERWORLD_LAVA_SPRINGS;
+ DefaultChance = IsWater ? 24 : 9;
+ break;
+ }
+ case dimEnd:
+ {
+ DefaultHeightDistribution = IsWater ? DEF_END_WATER_SPRINGS : DEF_END_LAVA_SPRINGS;
+ DefaultChance = 0;
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled world dimension");
+ break;
+ }
+ } // switch (dimension)
+ AString HeightDistribution = a_IniFile.GetValueSet(SectionName, "HeightDistribution", DefaultHeightDistribution);
+ if (!m_HeightDistribution.SetDefString(HeightDistribution) || (m_HeightDistribution.GetSum() <= 0))
+ {
+ LOGWARNING("[%sSprings]: HeightDistribution is invalid, using the default of \"%s\".",
+ (a_Fluid == E_BLOCK_WATER) ? "Water" : "Lava",
+ DefaultHeightDistribution.c_str()
+ );
+ m_HeightDistribution.SetDefString(DefaultHeightDistribution);
+ }
+ m_Chance = a_IniFile.GetValueSetI(SectionName, "Chance", DefaultChance);
+}
+
+
+
+
+
+void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ int ChanceRnd = (m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 512, 256 * a_ChunkDesc.GetChunkZ()) / 13) % 100;
+ if (ChanceRnd > m_Chance)
+ {
+ // Not in this chunk
+ return;
+ }
+
+ // Get the height at which to try:
+ int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11;
+ Height %= m_HeightDistribution.GetSum();
+ Height = m_HeightDistribution.MapValue(Height);
+
+ // Try adding the spring at the height, if unsuccessful, move lower:
+ for (int y = Height; y > 1; y--)
+ {
+ // TODO: randomize the order in which the coords are being checked
+ for (int z = 1; z < cChunkDef::Width - 1; z++)
+ {
+ for (int x = 1; x < cChunkDef::Width - 1; x++)
+ {
+ switch (a_ChunkDesc.GetBlockType(x, y, z))
+ {
+ case E_BLOCK_NETHERRACK:
+ case E_BLOCK_STONE:
+ {
+ if (TryPlaceSpring(a_ChunkDesc, x, y, z))
+ {
+ // Succeeded, bail out
+ return;
+ }
+ }
+ } // switch (BlockType)
+ } // for x
+ } // for y
+ } // for y
+}
+
+
+
+
+
+bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z)
+{
+ // In order to place a spring, it needs exactly one of the XZ neighbors or a below neighbor to be air
+ // Also, its neighbor on top of it must be non-air
+ if (a_ChunkDesc.GetBlockType(x, y + 1, z) == E_BLOCK_AIR)
+ {
+ return false;
+ }
+
+ static const struct
+ {
+ int x, y, z;
+ } Coords[] =
+ {
+ {-1, 0, 0},
+ { 1, 0, 0},
+ { 0, -1, 0},
+ { 0, 0, -1},
+ { 0, 0, 1},
+ } ;
+ int NumAirNeighbors = 0;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ switch (a_ChunkDesc.GetBlockType(x + Coords[i].x, y + Coords[i].y, z + Coords[i].z))
+ {
+ case E_BLOCK_AIR:
+ {
+ NumAirNeighbors += 1;
+ if (NumAirNeighbors > 1)
+ {
+ return false;
+ }
+ }
+ }
+ }
+ if (NumAirNeighbors == 0)
+ {
+ return false;
+ }
+
+ // Has exactly one air neighbor, place a spring:
+ a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0);
+ return true;
+}
+
+
+
+
diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h
new file mode 100644
index 000000000..ed7df5909
--- /dev/null
+++ b/src/Generating/FinishGen.h
@@ -0,0 +1,185 @@
+
+// FinishGen.h
+
+/* Interfaces to the various finishing generators:
+ - cFinishGenSnow
+ - cFinishGenIce
+ - cFinishGenSprinkleFoliage
+ - cFinishGenLilypads
+ - cFinishGenBottomLava
+ - cFinishGenPreSimulator
+ - cFinishGenDeadBushes
+*/
+
+
+
+
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+#include "../ProbabDistrib.h"
+
+
+
+
+
+class cFinishGenSnow :
+ public cFinishGen
+{
+protected:
+ // cFinishGen override:
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cFinishGenIce :
+ public cFinishGen
+{
+protected:
+ // cFinishGen override:
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cFinishGenSprinkleFoliage :
+ public cFinishGen
+{
+public:
+ cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {}
+
+protected:
+ cNoise m_Noise;
+ int m_Seed;
+
+ /// Tries to place sugarcane at the coords specified, returns true if successful
+ bool TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ);
+
+ // cFinishGen override:
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+/** This class adds a single top block in random positions in the specified biome on top of specified allowed blocks.
+Used for:
+- Lilypads finisher
+- DeadBushes finisher
+*/
+class cFinishGenSingleBiomeSingleTopBlock :
+ public cFinishGen
+{
+public:
+ cFinishGenSingleBiomeSingleTopBlock(
+ int a_Seed, BLOCKTYPE a_BlockType, EMCSBiome a_Biome, int a_Amount,
+ BLOCKTYPE a_AllowedBelow1, BLOCKTYPE a_AllowedBelow2
+ ) :
+ m_Noise(a_Seed),
+ m_BlockType(a_BlockType),
+ m_Biome(a_Biome),
+ m_Amount(a_Amount),
+ m_AllowedBelow1(a_AllowedBelow1),
+ m_AllowedBelow2(a_AllowedBelow2)
+ {
+ }
+
+protected:
+ cNoise m_Noise;
+ BLOCKTYPE m_BlockType;
+ EMCSBiome m_Biome;
+ int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns.
+ BLOCKTYPE m_AllowedBelow1; ///< First of the two blocktypes that are allowed below m_BlockType
+ BLOCKTYPE m_AllowedBelow2; ///< Second of the two blocktypes that are allowed below m_BlockType
+
+ int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap);
+
+ // cFinishGen override:
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cFinishGenBottomLava :
+ public cFinishGen
+{
+public:
+ cFinishGenBottomLava(int a_Level) :
+ m_Level(a_Level)
+ {
+ }
+
+protected:
+ int m_Level;
+
+ // cFinishGen override:
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cFinishGenPreSimulator :
+ public cFinishGen
+{
+public:
+ cFinishGenPreSimulator(void);
+
+protected:
+ // Drops hanging sand and gravel down to the ground, recalculates heightmap
+ void CollapseSandGravel(
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data
+ );
+
+ /** For each fluid block:
+ - if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top)
+ - all fluid on the chunk's edge is made flowing
+ */
+ void StationarizeFluid(
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read
+ BLOCKTYPE a_Fluid,
+ BLOCKTYPE a_StationaryFluid
+ );
+
+ // cFinishGen override:
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cFinishGenFluidSprings :
+ public cFinishGen
+{
+public:
+ cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World);
+
+protected:
+
+ cNoise m_Noise;
+ cProbabDistrib m_HeightDistribution;
+ BLOCKTYPE m_Fluid;
+ int m_Chance; ///< Chance, [0..100], that a spring will be generated in a chunk
+
+ // cFinishGen override:
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+
+ /// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful
+ bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z);
+} ;
+
+
+
+
diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp
new file mode 100644
index 000000000..9cc1fbbaa
--- /dev/null
+++ b/src/Generating/HeiGen.cpp
@@ -0,0 +1,390 @@
+
+// HeiGen.cpp
+
+// Implements the various terrain height generators
+
+#include "Globals.h"
+#include "HeiGen.h"
+#include "../LinearUpscale.h"
+#include "lib/inifile/iniFile.h"
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHeiGenFlat:
+
+void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
+ {
+ a_HeightMap[i] = m_Height;
+ }
+}
+
+
+
+
+
+void cHeiGenFlat::InitializeHeightGen(cIniFile & a_IniFile)
+{
+ m_Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", m_Height);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHeiGenCache:
+
+cHeiGenCache::cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize) :
+ m_HeiGenToCache(a_HeiGenToCache),
+ m_CacheSize(a_CacheSize),
+ m_CacheOrder(new int[a_CacheSize]),
+ m_CacheData(new sCacheData[a_CacheSize]),
+ m_NumHits(0),
+ m_NumMisses(0),
+ m_TotalChain(0)
+{
+ for (int i = 0; i < m_CacheSize; i++)
+ {
+ m_CacheOrder[i] = i;
+ m_CacheData[i].m_ChunkX = 0x7fffffff;
+ m_CacheData[i].m_ChunkZ = 0x7fffffff;
+ }
+}
+
+
+
+
+
+cHeiGenCache::~cHeiGenCache()
+{
+ delete[] m_CacheData;
+ delete[] m_CacheOrder;
+}
+
+
+
+
+
+void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ /*
+ if (((m_NumHits + m_NumMisses) % 1024) == 10)
+ {
+ LOGD("HeiGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
+ LOGD("HeiGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits);
+ }
+ //*/
+
+ for (int i = 0; i < m_CacheSize; i++)
+ {
+ if (
+ (m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) ||
+ (m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ)
+ )
+ {
+ continue;
+ }
+ // Found it in the cache
+ int Idx = m_CacheOrder[i];
+
+ // Move to front:
+ for (int j = i; j > 0; j--)
+ {
+ m_CacheOrder[j] = m_CacheOrder[j - 1];
+ }
+ m_CacheOrder[0] = Idx;
+
+ // Use the cached data:
+ memcpy(a_HeightMap, m_CacheData[Idx].m_HeightMap, sizeof(a_HeightMap));
+
+ m_NumHits++;
+ m_TotalChain += i;
+ return;
+ } // for i - cache
+
+ // Not in the cache:
+ m_NumMisses++;
+ m_HeiGenToCache.GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap);
+
+ // Insert it as the first item in the MRU order:
+ int Idx = m_CacheOrder[m_CacheSize - 1];
+ for (int i = m_CacheSize - 1; i > 0; i--)
+ {
+ m_CacheOrder[i] = m_CacheOrder[i - 1];
+ } // for i - m_CacheOrder[]
+ m_CacheOrder[0] = Idx;
+ memcpy(m_CacheData[Idx].m_HeightMap, a_HeightMap, sizeof(a_HeightMap));
+ m_CacheData[Idx].m_ChunkX = a_ChunkX;
+ m_CacheData[Idx].m_ChunkZ = a_ChunkZ;
+}
+
+
+
+
+
+void cHeiGenCache::InitializeHeightGen(cIniFile & a_IniFile)
+{
+ m_HeiGenToCache.InitializeHeightGen(a_IniFile);
+}
+
+
+
+
+
+bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height)
+{
+ for (int i = 0; i < m_CacheSize; i++)
+ {
+ if ((m_CacheData[i].m_ChunkX == a_ChunkX) && (m_CacheData[i].m_ChunkZ == a_ChunkZ))
+ {
+ a_Height = cChunkDef::GetHeight(m_CacheData[i].m_HeightMap, a_RelX, a_RelZ);
+ return true;
+ }
+ } // for i - m_CacheData[]
+ return false;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHeiGenClassic:
+
+cHeiGenClassic::cHeiGenClassic(int a_Seed) :
+ m_Seed(a_Seed),
+ m_Noise(a_Seed)
+{
+}
+
+
+
+
+
+float cHeiGenClassic::GetNoise(float x, float y)
+{
+ float oct1 = m_Noise.CubicNoise2D(x * m_HeightFreq1, y * m_HeightFreq1) * m_HeightAmp1;
+ float oct2 = m_Noise.CubicNoise2D(x * m_HeightFreq2, y * m_HeightFreq2) * m_HeightAmp2;
+ float oct3 = m_Noise.CubicNoise2D(x * m_HeightFreq3, y * m_HeightFreq3) * m_HeightAmp3;
+
+ float height = m_Noise.CubicNoise2D(x * 0.1f, y * 0.1f ) * 2;
+
+ float flatness = ((m_Noise.CubicNoise2D(x * 0.5f, y * 0.5f) + 1.f) * 0.5f) * 1.1f; // 0 ... 1.5
+ flatness *= flatness * flatness;
+
+ return (oct1 + oct2 + oct3) * flatness + height;
+}
+
+
+
+
+
+void cHeiGenClassic::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ const float zz = (float)(a_ChunkZ * cChunkDef::Width + z);
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ const float xx = (float)(a_ChunkX * cChunkDef::Width + x);
+
+ int hei = 64 + (int)(GetNoise(xx * 0.05f, zz * 0.05f) * 16);
+ if (hei < 10)
+ {
+ hei = 10;
+ }
+ if (hei > 250)
+ {
+ hei = 250;
+ }
+ cChunkDef::SetHeight(a_HeightMap, x , z, hei);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cHeiGenClassic::InitializeHeightGen(cIniFile & a_IniFile)
+{
+ m_HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1);
+ m_HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0);
+ m_HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0);
+ m_HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0);
+ m_HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5);
+ m_HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHeiGenBiomal:
+
+const cHeiGenBiomal::sGenParam cHeiGenBiomal::m_GenParam[biNumBiomes] =
+{
+ /* Fast-changing | Middle-changing | Slow-changing |*/
+ /* Biome | Freq1 | Amp1 | Freq2 | Amp2 | Freq3 | Amp3 | BaseHeight */
+ /* biOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40},
+ /* biPlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68},
+ /* biDesert */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68},
+ /* biExtremeHills */ { 0.2f, 4.0f, 0.05f, 20.0f, 0.01f, 16.0f, 100},
+ /* biForest */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70},
+ /* biTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70},
+ /* biSwampland */ { 0.1f, 1.1f, 0.05f, 1.5f, 0.02f, 2.5f, 61.5},
+ /* biRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56},
+ /* biNether */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing
+ /* biSky */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing
+ /* biFrozenOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40},
+ /* biFrozenRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56},
+ /* biIcePlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68},
+ /* biIceMountains */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80},
+ /* biMushroomIsland */ { 0.1f, 2.0f, 0.05f, 8.0f, 0.01f, 6.0f, 80},
+ /* biMushroomShore */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 64},
+ /* biBeach */ { 0.1f, 0.5f, 0.05f, 1.0f, 0.01f, 1.0f, 64},
+ /* biDesertHills */ { 0.2f, 2.0f, 0.05f, 5.0f, 0.01f, 4.0f, 75},
+ /* biForestHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80},
+ /* biTaigaHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80},
+ /* biExtremeHillsEdge */ { 0.2f, 3.0f, 0.05f, 16.0f, 0.01f, 12.0f, 80},
+ /* biJungle */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70},
+ /* biJungleHills */ { 0.2f, 3.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80},
+} ;
+
+
+
+
+
+void cHeiGenBiomal::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ // Generate a 3x3 chunk area of biomes around this chunk:
+ BiomeNeighbors Biomes;
+ for (int z = -1; z <= 1; z++)
+ {
+ for (int x = -1; x <= 1; x++)
+ {
+ m_BiomeGen.GenBiomes(a_ChunkX + x, a_ChunkZ + z, Biomes[x + 1][z + 1]);
+ } // for x
+ } // for z
+
+ /*
+ _X 2013_04_22:
+ There's no point in precalculating the entire perlin noise arrays, too many values are calculated uselessly,
+ resulting in speed DEcrease.
+ */
+
+ //*
+ // Linearly interpolate 4x4 blocks of heightmap:
+ // Must be done on a floating point datatype, else the results are ugly!
+ const int STEPZ = 4; // Must be a divisor of 16
+ const int STEPX = 4; // Must be a divisor of 16
+ NOISE_DATATYPE Height[17 * 17];
+ for (int z = 0; z < 17; z += STEPZ)
+ {
+ for (int x = 0; x < 17; x += STEPX)
+ {
+ Height[x + 17 * z] = GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes);
+ }
+ }
+ LinearUpscale2DArrayInPlace(Height, 17, 17, STEPX, STEPZ);
+
+ // Copy into the heightmap
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, (int)Height[x + 17 * z]);
+ }
+ }
+ //*/
+
+ /*
+ // For each height, go through neighboring biomes and add up their idea of height:
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes));
+ } // for x
+ }
+ //*/
+}
+
+
+
+
+
+void cHeiGenBiomal::InitializeHeightGen(cIniFile & a_IniFile)
+{
+ // No user-settable params
+}
+
+
+
+
+
+NOISE_DATATYPE cHeiGenBiomal::GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const cHeiGenBiomal::BiomeNeighbors & a_BiomeNeighbors)
+{
+ // Sum up how many biomes of each type there are in the neighborhood:
+ int BiomeCounts[biNumBiomes];
+ memset(BiomeCounts, 0, sizeof(BiomeCounts));
+ int Sum = 0;
+ for (int z = -8; z <= 8; z++)
+ {
+ int FinalZ = a_RelZ + z + cChunkDef::Width;
+ int IdxZ = FinalZ / cChunkDef::Width;
+ int ModZ = FinalZ % cChunkDef::Width;
+ int WeightZ = 9 - abs(z);
+ for (int x = -8; x <= 8; x++)
+ {
+ int FinalX = a_RelX + x + cChunkDef::Width;
+ int IdxX = FinalX / cChunkDef::Width;
+ int ModX = FinalX % cChunkDef::Width;
+ EMCSBiome Biome = cChunkDef::GetBiome(a_BiomeNeighbors[IdxX][IdxZ], ModX, ModZ);
+ if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts)))
+ {
+ continue;
+ }
+ int WeightX = 9 - abs(x);
+ BiomeCounts[Biome] += WeightX + WeightZ;
+ Sum += WeightX + WeightZ;
+ } // for x
+ } // for z
+
+ // For each biome type that has a nonzero count, calc its height and add it:
+ if (Sum > 0)
+ {
+ NOISE_DATATYPE Height = 0;
+ int BlockX = a_ChunkX * cChunkDef::Width + a_RelX;
+ int BlockZ = a_ChunkZ * cChunkDef::Width + a_RelZ;
+ for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++)
+ {
+ if (BiomeCounts[i] == 0)
+ {
+ continue;
+ }
+ NOISE_DATATYPE oct1 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq1, BlockZ * m_GenParam[i].m_HeightFreq1) * m_GenParam[i].m_HeightAmp1;
+ NOISE_DATATYPE oct2 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq2, BlockZ * m_GenParam[i].m_HeightFreq2) * m_GenParam[i].m_HeightAmp2;
+ NOISE_DATATYPE oct3 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq3, BlockZ * m_GenParam[i].m_HeightFreq3) * m_GenParam[i].m_HeightAmp3;
+ Height += BiomeCounts[i] * (m_GenParam[i].m_BaseHeight + oct1 + oct2 + oct3);
+ }
+ NOISE_DATATYPE res = Height / Sum;
+ return std::min((NOISE_DATATYPE)250, std::max(res, (NOISE_DATATYPE)5));
+ }
+
+ // No known biome around? Weird. Return a bogus value:
+ ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around");
+ return 5;
+}
+
+
+
+
+
diff --git a/src/Generating/HeiGen.h b/src/Generating/HeiGen.h
new file mode 100644
index 000000000..1b246c70a
--- /dev/null
+++ b/src/Generating/HeiGen.h
@@ -0,0 +1,145 @@
+
+// HeiGen.h
+
+/*
+Interfaces to the various height generators:
+ - cHeiGenFlat
+ - cHeiGenClassic
+ - cHeiGenBiomal
+*/
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cHeiGenFlat :
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenFlat(void) : m_Height(5) {}
+
+protected:
+
+ int m_Height;
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
+/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation
+class cHeiGenCache :
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize); // Doesn't take ownership of a_HeiGenToCache
+ ~cHeiGenCache();
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
+
+ /// Retrieves height at the specified point in the cache, returns true if found, false if not found
+ bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height);
+
+protected:
+
+ cTerrainHeightGen & m_HeiGenToCache;
+
+ struct sCacheData
+ {
+ int m_ChunkX;
+ int m_ChunkZ;
+ cChunkDef::HeightMap m_HeightMap;
+ } ;
+
+ // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
+ int m_CacheSize;
+ int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
+ sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
+
+ // Cache statistics
+ int m_NumHits;
+ int m_NumMisses;
+ int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
+} ;
+
+
+
+
+
+class cHeiGenClassic :
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenClassic(int a_Seed);
+
+protected:
+
+ int m_Seed;
+ cNoise m_Noise;
+ float m_HeightFreq1, m_HeightAmp1;
+ float m_HeightFreq2, m_HeightAmp2;
+ float m_HeightFreq3, m_HeightAmp3;
+
+ float GetNoise(float x, float y);
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
+class cHeiGenBiomal :
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenBiomal(int a_Seed, cBiomeGen & a_BiomeGen) :
+ m_Noise(a_Seed),
+ m_BiomeGen(a_BiomeGen)
+ {
+ }
+
+protected:
+
+ typedef cChunkDef::BiomeMap BiomeNeighbors[3][3];
+
+ cNoise m_Noise;
+ cBiomeGen & m_BiomeGen;
+
+ // Per-biome terrain generator parameters:
+ struct sGenParam
+ {
+ float m_HeightFreq1, m_HeightAmp1;
+ float m_HeightFreq2, m_HeightAmp2;
+ float m_HeightFreq3, m_HeightAmp3;
+ float m_BaseHeight;
+ } ;
+ static const sGenParam m_GenParam[biNumBiomes];
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
+
+ NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors);
+} ;
+
+
+
+
diff --git a/src/Generating/MineShafts.cpp b/src/Generating/MineShafts.cpp
new file mode 100644
index 000000000..f42240e55
--- /dev/null
+++ b/src/Generating/MineShafts.cpp
@@ -0,0 +1,1424 @@
+
+// MineShafts.cpp
+
+// Implements the cStructGenMineShafts class representing the structure generator for abandoned mineshafts
+
+/*
+Algorithm:
+The cStructGenMineShafts::cMineShaftSystem class is the main controller, which knows what mineshaft
+classes there are and their random weights. It gets asked to produce a new class everytime a connection is to be made.
+The cMineShaft class is a base class for each mineshaft structure.
+Each cMineShaft descendant knows how large it is, how to imprint itself into the chunk data and where to connect to
+other descendants. Its PivotPoint is always a walkable column. Its Direction determines in which direction the structure
+is facing.
+
+The generation starts with the central dirt room, from there corridors, crossings and staircases are added
+in a depth-first processing. Each of the descendants will branch randomly, if not beyond the allowed recursion level
+*/
+
+#include "Globals.h"
+#include "MineShafts.h"
+#include "../Cuboid.h"
+#include "../BlockEntities/ChestEntity.h"
+
+
+
+
+
+static const int NEIGHBORHOOD_SIZE = 3;
+
+
+
+
+
+class cMineShaft abstract
+{
+public:
+ enum eKind
+ {
+ mskDirtRoom,
+ mskCorridor,
+ mskCrossing,
+ mskStaircase,
+ } ;
+
+
+ enum eDirection
+ {
+ dirXP,
+ dirZP,
+ dirXM,
+ dirZM,
+ } ;
+
+
+ cStructGenMineShafts::cMineShaftSystem & m_ParentSystem;
+ eKind m_Kind;
+ cCuboid m_BoundingBox;
+
+
+ cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind) :
+ m_ParentSystem(a_ParentSystem),
+ m_Kind(a_Kind)
+ {
+ }
+
+ cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind, const cCuboid & a_BoundingBox) :
+ m_ParentSystem(a_ParentSystem),
+ m_Kind(a_Kind),
+ m_BoundingBox(a_BoundingBox)
+ {
+ }
+
+ /// Returns true if this mineshaft intersects the specified cuboid
+ bool DoesIntersect(const cCuboid & a_Other)
+ {
+ return m_BoundingBox.DoesIntersect(a_Other);
+ }
+
+ /** If recursion level is not too large, appends more branches to the parent system,
+ using exit points specific to this class.
+ */
+ virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) = 0;
+
+ /// Imprints this shape into the specified chunk's data
+ virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) = 0;
+} ;
+
+typedef std::vector<cMineShaft *> cMineShafts;
+
+
+
+
+
+class cMineShaftDirtRoom :
+ public cMineShaft
+{
+ typedef cMineShaft super;
+
+public:
+ cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise);
+
+ // cMineShaft overrides:
+ virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
+ virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cMineShaftCorridor :
+ public cMineShaft
+{
+ typedef cMineShaft super;
+
+public:
+ /** Creates a new Corridor attached to the specified pivot point and direction.
+ Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit.
+ May return NULL if cannot fit.
+ */
+ static cMineShaft * CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+ );
+
+protected:
+ static const int MAX_SEGMENTS = 5;
+
+ int m_NumSegments;
+ eDirection m_Direction;
+ bool m_HasFullBeam[MAX_SEGMENTS]; ///< If true, segment at that index has a full beam support (planks in the top center block)
+ int m_ChestPosition; ///< If <0, no chest; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction
+ int m_SpawnerPosition; ///< If <0, no spawner; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction
+ bool m_HasTracks; ///< If true, random tracks will be placed on the floor
+
+ cMineShaftCorridor(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction,
+ cNoise & a_Noise
+ );
+
+ // cMineShaft overrides:
+ virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
+ virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
+
+ /// Places a chest, if the corridor has one
+ void PlaceChest(cChunkDesc & a_ChunkDesc);
+
+ /// If this corridor has tracks, places them randomly
+ void PlaceTracks(cChunkDesc & a_ChunkDesc);
+
+ /// If this corridor has a spawner, places the spawner
+ void PlaceSpawner(cChunkDesc & a_ChunkDesc);
+
+ /// Randomly places torches around the central beam block
+ void PlaceTorches(cChunkDesc & a_ChunkDesc);
+} ;
+
+
+
+
+
+class cMineShaftCrossing :
+ public cMineShaft
+{
+ typedef cMineShaft super;
+
+public:
+ /** Creates a new Crossing attached to the specified pivot point and direction.
+ Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit.
+ May return NULL if cannot fit.
+ */
+ static cMineShaft * CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+ );
+
+protected:
+ cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox);
+
+ // cMineShaft overrides:
+ virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
+ virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cMineShaftStaircase :
+ public cMineShaft
+{
+ typedef cMineShaft super;
+
+public:
+ enum eSlope
+ {
+ sUp,
+ sDown,
+ } ;
+
+ /** Creates a new Staircase attached to the specified pivot point and direction.
+ Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit.
+ May return NULL if cannot fit.
+ */
+ static cMineShaft * CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+ );
+
+protected:
+ eDirection m_Direction;
+ eSlope m_Slope;
+
+
+ cMineShaftStaircase(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ const cCuboid & a_BoundingBox,
+ eDirection a_Direction,
+ eSlope a_Slope
+ );
+
+ // cMineShaft overrides:
+ virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
+ virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cStructGenMineShafts::cMineShaftSystem
+{
+public:
+ int m_BlockX, m_BlockZ; ///< The pivot point on which the system is generated
+ int m_GridSize; ///< Maximum offset of the dirtroom from grid center, * 2, in each direction
+ int m_MaxRecursion; ///< Maximum recursion level (initialized from cStructGenMineShafts::m_MaxRecursion)
+ int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor
+ int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor
+ int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing
+ int m_ChanceChest; ///< Chance [0 .. 250] that a corridor has a chest in it
+ int m_ChanceSpawner; ///< Chance [0 .. 250] that a corridor has a spawner in it
+ int m_ChanceTorch; ///< Chance [0 .. 10k] for a torch appearing attached to a corridor's beam
+ cMineShafts m_MineShafts; ///< List of cMineShaft descendants that comprise this system
+ cCuboid m_BoundingBox; ///< Bounding box into which all of the components need to fit
+
+ /// Creates and generates the entire system
+ cMineShaftSystem(
+ int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise,
+ int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase
+ );
+
+ ~cMineShaftSystem();
+
+ /// Carves the system into the chunk data
+ void ProcessChunk(cChunkDesc & a_Chunk);
+
+ /** Creates new cMineShaft descendant connected at the specified point, heading the specified direction,
+ if it fits, appends it to the list and calls its AppendBranches()
+ */
+ void AppendBranch(
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ cMineShaft::eDirection a_Direction, cNoise & a_Noise,
+ int a_RecursionLevel
+ );
+
+ /// Returns true if none of the objects in m_MineShafts intersect with the specified bounding box and the bounding box is valid
+ bool CanAppend(const cCuboid & a_BoundingBox);
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenMineShafts::cMineShaftSystem:
+
+cStructGenMineShafts::cMineShaftSystem::cMineShaftSystem(
+ int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise,
+ int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase
+) :
+ m_BlockX(a_BlockX),
+ m_BlockZ(a_BlockZ),
+ m_GridSize(a_GridSize),
+ m_MaxRecursion(8), // TODO: settable
+ m_ProbLevelCorridor(a_ProbLevelCorridor),
+ m_ProbLevelCrossing(a_ProbLevelCrossing),
+ m_ProbLevelStaircase(a_ProbLevelStaircase + 1),
+ m_ChanceChest(12), // TODO: settable
+ m_ChanceSpawner(12), // TODO: settable
+ m_ChanceTorch(1000) // TODO: settable
+{
+ m_MineShafts.reserve(100);
+
+ cMineShaft * Start = new cMineShaftDirtRoom(*this, a_Noise);
+ m_MineShafts.push_back(Start);
+
+ m_BoundingBox.Assign(
+ Start->m_BoundingBox.p1.x - a_MaxSystemSize / 2, 2, Start->m_BoundingBox.p1.z - a_MaxSystemSize / 2,
+ Start->m_BoundingBox.p2.x + a_MaxSystemSize / 2, 50, Start->m_BoundingBox.p2.z + a_MaxSystemSize / 2
+ );
+
+ Start->AppendBranches(0, a_Noise);
+
+ for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
+ {
+ ASSERT((*itr)->m_BoundingBox.IsSorted());
+ } // for itr - m_MineShafts[]
+}
+
+
+
+
+
+cStructGenMineShafts::cMineShaftSystem::~cMineShaftSystem()
+{
+ for (cMineShafts::iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ } // for itr - m_MineShafts[]
+ m_MineShafts.clear();
+}
+
+
+
+
+
+void cStructGenMineShafts::cMineShaftSystem::ProcessChunk(cChunkDesc & a_Chunk)
+{
+ for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
+ {
+ (*itr)->ProcessChunk(a_Chunk);
+ } // for itr - m_MineShafts[]
+}
+
+
+
+
+
+void cStructGenMineShafts::cMineShaftSystem::AppendBranch(
+ int a_PivotX, int a_PivotY, int a_PivotZ,
+ cMineShaft::eDirection a_Direction, cNoise & a_Noise,
+ int a_RecursionLevel
+)
+{
+ if (a_RecursionLevel > m_MaxRecursion)
+ {
+ return;
+ }
+
+ cMineShaft * Next = NULL;
+ int rnd = (a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_RecursionLevel * 16, a_PivotZ) / 13) % m_ProbLevelStaircase;
+ if (rnd < m_ProbLevelCorridor)
+ {
+ Next = cMineShaftCorridor::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise);
+ }
+ else if (rnd < m_ProbLevelCrossing)
+ {
+ Next = cMineShaftCrossing::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise);
+ }
+ else
+ {
+ Next = cMineShaftStaircase::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise);
+ }
+ if (Next == NULL)
+ {
+ return;
+ }
+ m_MineShafts.push_back(Next);
+ Next->AppendBranches(a_RecursionLevel + 1, a_Noise);
+}
+
+
+
+
+
+bool cStructGenMineShafts::cMineShaftSystem::CanAppend(const cCuboid & a_BoundingBox)
+{
+ if (!a_BoundingBox.IsCompletelyInside(m_BoundingBox))
+ {
+ // Too far away, or too low / too high
+ return false;
+ }
+
+ // Check intersections:
+ for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
+ {
+ if ((*itr)->DoesIntersect(a_BoundingBox))
+ {
+ return false;
+ }
+ } // for itr - m_MineShafts[]
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMineShaftDirtRoom:
+
+cMineShaftDirtRoom::cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise) :
+ super(a_Parent, mskDirtRoom)
+{
+ // Make the room of random size, min 10 x 4 x 10; max 18 x 12 x 18:
+ int rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 0, a_Parent.m_BlockZ) / 7;
+ int OfsX = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2;
+ rnd >>= 12;
+ int OfsZ = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2;
+ rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 1000, a_Parent.m_BlockZ) / 11;
+ m_BoundingBox.p1.x = a_Parent.m_BlockX + OfsX;
+ m_BoundingBox.p2.x = m_BoundingBox.p1.x + 10 + (rnd % 8);
+ rnd >>= 4;
+ m_BoundingBox.p1.z = a_Parent.m_BlockZ + OfsZ;
+ m_BoundingBox.p2.z = m_BoundingBox.p1.z + 10 + (rnd % 8);
+ rnd >>= 4;
+ m_BoundingBox.p1.y = 20;
+ m_BoundingBox.p2.y = 24 + rnd % 8;
+}
+
+
+
+
+
+void cMineShaftDirtRoom::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
+{
+ int Height = m_BoundingBox.DifY() - 3;
+ for (int x = m_BoundingBox.p1.x + 1; x < m_BoundingBox.p2.x; x += 4)
+ {
+ int rnd = a_Noise.IntNoise3DInt(x, a_RecursionLevel, m_BoundingBox.p1.z) / 7;
+ m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
+ rnd >>= 4;
+ m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
+ }
+
+ for (int z = m_BoundingBox.p1.z + 1; z < m_BoundingBox.p2.z; z += 4)
+ {
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, a_RecursionLevel, z) / 13;
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXM, a_Noise, a_RecursionLevel);
+ rnd >>= 4;
+ m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXP, a_Noise, a_RecursionLevel);
+ }
+}
+
+
+
+
+
+void cMineShaftDirtRoom::ProcessChunk(cChunkDesc & a_ChunkDesc)
+{
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ if (
+ (m_BoundingBox.p1.x > BlockX + cChunkDef::Width) ||
+ (m_BoundingBox.p1.z > BlockZ + cChunkDef::Width) ||
+ (m_BoundingBox.p2.x < BlockX) ||
+ (m_BoundingBox.p2.z < BlockZ)
+ )
+ {
+ // Early bailout - cannot intersect this chunk
+ return;
+ }
+
+ // Chunk-relative coords of the boundaries:
+ int MinX = std::max(BlockX, m_BoundingBox.p1.x) - BlockX;
+ int MaxX = std::min(BlockX + cChunkDef::Width, m_BoundingBox.p2.x + 1) - BlockX;
+ int MinZ = std::max(BlockZ, m_BoundingBox.p1.z) - BlockZ;
+ int MaxZ = std::min(BlockZ + cChunkDef::Width, m_BoundingBox.p2.z + 1) - BlockZ;
+
+ // Carve the room out:
+ for (int z = MinZ; z < MaxZ; z++)
+ {
+ for (int x = MinX; x < MaxX; x++)
+ {
+ for (int y = m_BoundingBox.p1.y + 1; y < m_BoundingBox.p2.y; y++)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
+ }
+ if (a_ChunkDesc.GetBlockType(x, m_BoundingBox.p1.y, z) != E_BLOCK_AIR)
+ {
+ a_ChunkDesc.SetBlockType(x, m_BoundingBox.p1.y, z, E_BLOCK_DIRT);
+ }
+ } // for x
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMineShaftCorridor:
+
+cMineShaftCorridor::cMineShaftCorridor(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction,
+ cNoise & a_Noise
+) :
+ super(a_ParentSystem, mskCorridor, a_BoundingBox),
+ m_NumSegments(a_NumSegments),
+ m_Direction(a_Direction),
+ m_ChestPosition(-1),
+ m_SpawnerPosition(-1)
+{
+ int rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.x, a_BoundingBox.p1.y, a_BoundingBox.p1.z) / 7;
+ for (int i = 0; i < a_NumSegments; i++)
+ {
+ m_HasFullBeam[i] = (rnd % 4) < 3; // 75 % chance of full beam
+ rnd >>= 2;
+ }
+ m_HasTracks = ((rnd % 4) < 2); // 50 % chance of tracks
+
+ rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.z, a_BoundingBox.p1.x, a_BoundingBox.p1.y) / 7;
+ int ChestCheck = rnd % 250;
+ rnd >>= 8;
+ int SpawnerCheck = rnd % 250;
+ rnd >>= 8;
+ if (ChestCheck < a_ParentSystem.m_ChanceChest)
+ {
+ m_ChestPosition = rnd % (a_NumSegments * 5);
+ }
+ if ((a_NumSegments < 4) && (SpawnerCheck < a_ParentSystem.m_ChanceSpawner))
+ {
+ m_SpawnerPosition = rnd % (a_NumSegments * 5);
+ }
+}
+
+
+
+
+
+cMineShaft * cMineShaftCorridor::CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+)
+{
+ cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ);
+ BoundingBox.p2.y += 3;
+ int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7;
+ int NumSegments = 2 + (rnd) % (MAX_SEGMENTS - 1); // 2 .. MAX_SEGMENTS
+ switch (a_Direction)
+ {
+ case dirXP: BoundingBox.p2.x += NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break;
+ case dirXM: BoundingBox.p1.x -= NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break;
+ case dirZP: BoundingBox.p2.z += NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break;
+ case dirZM: BoundingBox.p1.z -= NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break;
+ }
+ if (!a_ParentSystem.CanAppend(BoundingBox))
+ {
+ return NULL;
+ }
+ return new cMineShaftCorridor(a_ParentSystem, BoundingBox, NumSegments, a_Direction, a_Noise);
+}
+
+
+
+
+
+void cMineShaftCorridor::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
+{
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 7;
+ // Prefer the same height, but allow for up to one block height displacement:
+ int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
+ switch (m_Direction)
+ {
+ case dirXM:
+ {
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel);
+ for (int i = m_NumSegments; i >= 0; i--)
+ {
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
+ int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
+ rnd >>= 6;
+ int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
+ }
+ break;
+ }
+
+ case dirXP:
+ {
+ m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel);
+ for (int i = m_NumSegments; i >= 0; i--)
+ {
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
+ int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
+ rnd >>= 6;
+ int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
+ }
+ break;
+ }
+
+ case dirZM:
+ {
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
+ for (int i = m_NumSegments; i >= 0; i--)
+ {
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
+ int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
+ rnd >>= 6;
+ int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel);
+ }
+ break;
+ }
+
+ case dirZP:
+ {
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
+ for (int i = m_NumSegments; i >= 0; i--)
+ {
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
+ int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
+ rnd >>= 6;
+ int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel);
+ }
+ break;
+ }
+ } // switch (m_Direction)
+}
+
+
+
+
+
+void cMineShaftCorridor::ProcessChunk(cChunkDesc & a_ChunkDesc)
+{
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ cCuboid RelBoundingBox(m_BoundingBox);
+ RelBoundingBox.Move(-BlockX, 0, -BlockZ);
+ RelBoundingBox.p1.y += 1;
+ RelBoundingBox.p2.y -= 1;
+ cCuboid Top(RelBoundingBox);
+ Top.p2.y += 1;
+ Top.p1.y = Top.p2.y;
+ a_ChunkDesc.FillRelCuboid(RelBoundingBox, E_BLOCK_AIR, 0);
+ a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_AIR, 0, (BlockX ^ (BlockZ + BlockX)), 8000);
+ if (m_SpawnerPosition >= 0)
+ {
+ // Cobwebs around the spider spawner
+ a_ChunkDesc.RandomFillRelCuboid(RelBoundingBox, E_BLOCK_COBWEB, 0, (BlockX ^ (BlockZ + BlockZ)), 8000);
+ a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, (BlockX ^ (BlockZ + BlockX)), 5000);
+ }
+ a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, (BlockX ^ (BlockZ + BlockX + 10)), 500);
+ RelBoundingBox.p1.y = m_BoundingBox.p1.y;
+ RelBoundingBox.p2.y = m_BoundingBox.p1.y;
+ a_ChunkDesc.FloorRelCuboid(RelBoundingBox, E_BLOCK_PLANKS, 0);
+ switch (m_Direction)
+ {
+ case dirXM:
+ case dirXP:
+ {
+ int y1 = m_BoundingBox.p1.y + 1;
+ int y2 = m_BoundingBox.p1.y + 2;
+ int y3 = m_BoundingBox.p1.y + 3;
+ int z1 = m_BoundingBox.p1.z - BlockZ;
+ int z2 = m_BoundingBox.p2.z - BlockZ;
+ for (int i = 0; i < m_NumSegments; i++)
+ {
+ int x = m_BoundingBox.p1.x + i * 5 + 2 - BlockX;
+ if ((x < 0) || (x >= cChunkDef::Width))
+ {
+ continue;
+ }
+ if ((z1 >= 0) && (z1 < cChunkDef::Width))
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, y1, z1, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x, y2, z1, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x, y3, z1, E_BLOCK_PLANKS, 0);
+ }
+ if ((z2 >= 0) && (z2 < cChunkDef::Width))
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, y1, z2, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x, y2, z2, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x, y3, z2, E_BLOCK_PLANKS, 0);
+ }
+ if ((z1 >= -1) && (z1 < cChunkDef::Width - 1) && m_HasFullBeam[i])
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, y3, z1 + 1, E_BLOCK_PLANKS, 0);
+ }
+ } // for i - NumSegments
+ break;
+ }
+
+ case dirZM:
+ case dirZP:
+ {
+ int y1 = m_BoundingBox.p1.y + 1;
+ int y2 = m_BoundingBox.p1.y + 2;
+ int y3 = m_BoundingBox.p1.y + 3;
+ int x1 = m_BoundingBox.p1.x - BlockX;
+ int x2 = m_BoundingBox.p2.x - BlockX;
+ for (int i = 0; i < m_NumSegments; i++)
+ {
+ int z = m_BoundingBox.p1.z + i * 5 + 2 - BlockZ;
+ if ((z < 0) || (z >= cChunkDef::Width))
+ {
+ continue;
+ }
+ if ((x1 >= 0) && (x1 < cChunkDef::Width))
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x1, y1, z, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x1, y2, z, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x1, y3, z, E_BLOCK_PLANKS, 0);
+ }
+ if ((x2 >= 0) && (x2 < cChunkDef::Width))
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x2, y1, z, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x2, y2, z, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x2, y3, z, E_BLOCK_PLANKS, 0);
+ }
+ if ((x1 >= -1) && (x1 < cChunkDef::Width - 1) && m_HasFullBeam[i])
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x1 + 1, y3, z, E_BLOCK_PLANKS, 0);
+ }
+ } // for i - NumSegments
+ break;
+ } // case dirZ?
+ } // for i
+
+ PlaceChest(a_ChunkDesc);
+ PlaceTracks(a_ChunkDesc);
+ PlaceSpawner(a_ChunkDesc); // (must be after Tracks!)
+ PlaceTorches(a_ChunkDesc);
+}
+
+
+
+
+
+void cMineShaftCorridor::PlaceChest(cChunkDesc & a_ChunkDesc)
+{
+ static const cLootProbab LootProbab[] =
+ {
+ // Item, MinAmount, MaxAmount, Weight
+ { cItem(E_ITEM_IRON), 1, 5, 10 },
+ { cItem(E_ITEM_GOLD), 1, 3, 5 },
+ { cItem(E_ITEM_REDSTONE_DUST), 4, 9, 5 },
+ { cItem(E_ITEM_DIAMOND), 1, 2, 3 },
+ { cItem(E_ITEM_DYE, 1, 4), 4, 9, 5 }, // lapis lazuli dye
+ { cItem(E_ITEM_COAL), 3, 8, 10 },
+ { cItem(E_ITEM_BREAD), 1, 3, 15 },
+ { cItem(E_ITEM_IRON_PICKAXE), 1, 1, 1 },
+ { cItem(E_BLOCK_MINECART_TRACKS), 4, 8, 1 },
+ { cItem(E_ITEM_MELON_SEEDS), 2, 4, 10 },
+ { cItem(E_ITEM_PUMPKIN_SEEDS), 2, 4, 10 },
+ } ;
+
+ if (m_ChestPosition < 0)
+ {
+ return;
+ }
+
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ int x, z;
+ NIBBLETYPE Meta = 0;
+ switch (m_Direction)
+ {
+ case dirXM:
+ case dirXP:
+ {
+ x = m_BoundingBox.p1.x + m_ChestPosition - BlockX;
+ z = m_BoundingBox.p1.z - BlockZ;
+ Meta = E_META_CHEST_FACING_ZP;
+ break;
+ }
+
+ case dirZM:
+ case dirZP:
+ default:
+ {
+ x = m_BoundingBox.p1.x - BlockX;
+ z = m_BoundingBox.p1.z + m_ChestPosition - BlockZ;
+ Meta = E_META_CHEST_FACING_XP;
+ break;
+ }
+ } // switch (Dir)
+
+ if (
+ (x >= 0) && (x < cChunkDef::Width) &&
+ (z >= 0) && (z < cChunkDef::Width)
+ )
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p1.y + 1, z, E_BLOCK_CHEST, Meta);
+ cChestEntity * ChestEntity = (cChestEntity *)a_ChunkDesc.GetBlockEntity(x, m_BoundingBox.p1.y + 1, z);
+ ASSERT((ChestEntity != NULL) && (ChestEntity->GetBlockType() == E_BLOCK_CHEST));
+ cNoise Noise(a_ChunkDesc.GetChunkX() ^ a_ChunkDesc.GetChunkZ());
+ int NumSlots = 3 + ((Noise.IntNoise3DInt(x, m_BoundingBox.p1.y, z) / 11) % 4);
+ int Seed = Noise.IntNoise2DInt(x, z);
+ ChestEntity->GetContents().GenerateRandomLootWithBooks(LootProbab, ARRAYCOUNT(LootProbab), NumSlots, Seed);
+ }
+}
+
+
+
+
+
+void cMineShaftCorridor::PlaceTracks(cChunkDesc & a_ChunkDesc)
+{
+ if (!m_HasTracks)
+ {
+ return;
+ }
+ cCuboid Box(m_BoundingBox);
+ Box.Move(-a_ChunkDesc.GetChunkX() * cChunkDef::Width, 1, -a_ChunkDesc.GetChunkZ() * cChunkDef::Width);
+ Box.p2.y = Box.p1.y;
+ Box.p1.x += 1;
+ Box.p2.x -= 1;
+ Box.p1.z += 1;
+ Box.p2.z -= 1;
+ NIBBLETYPE Meta = 0;
+ switch (m_Direction)
+ {
+ case dirXM:
+ case dirXP:
+ {
+ Meta = E_META_TRACKS_X;
+ break;
+ }
+
+ case dirZM:
+ case dirZP:
+ {
+ Meta = E_META_TRACKS_Z;
+ break;
+ }
+ } // switch (direction)
+ a_ChunkDesc.RandomFillRelCuboid(Box, E_BLOCK_MINECART_TRACKS, Meta, a_ChunkDesc.GetChunkX() + a_ChunkDesc.GetChunkZ(), 6000);
+}
+
+
+
+
+
+void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc)
+{
+ if (m_SpawnerPosition < 0)
+ {
+ // No spawner in this corridor
+ return;
+ }
+ int SpawnerRelX = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int SpawnerRelZ = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ switch (m_Direction)
+ {
+ case dirXM:
+ case dirXP:
+ {
+ SpawnerRelX += m_SpawnerPosition - 1;
+ break;
+ }
+ case dirZM:
+ case dirZP:
+ {
+ SpawnerRelZ += m_SpawnerPosition - 1;
+ break;
+ }
+ }
+ if (
+ (SpawnerRelX >= 0) && (SpawnerRelX < cChunkDef::Width) &&
+ (SpawnerRelZ >= 0) && (SpawnerRelZ < cChunkDef::Width)
+ )
+ {
+ a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0);
+ // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented
+ }
+}
+
+
+
+
+
+void cMineShaftCorridor::PlaceTorches(cChunkDesc & a_ChunkDesc)
+{
+ cNoise Noise(m_BoundingBox.p1.x);
+ switch (m_Direction)
+ {
+ case dirXM:
+ case dirXP:
+ {
+ int z = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ if ((z < 0) || (z >= cChunkDef::Width))
+ {
+ return;
+ }
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ for (int i = 0; i < m_NumSegments; i++)
+ {
+ if (!m_HasFullBeam[i])
+ {
+ continue;
+ }
+ int x = m_BoundingBox.p1.x + i * 5 + 1 - BlockX;
+ if ((x >= 0) && (x < cChunkDef::Width))
+ {
+ if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XP);
+ }
+ }
+ x += 2;
+ if ((x >= 0) && (x < cChunkDef::Width))
+ {
+ if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XM);
+ }
+ }
+ } // for i
+ break;
+ }
+
+ case dirZM:
+ case dirZP:
+ {
+ int x = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ if ((x < 0) || (x >= cChunkDef::Width))
+ {
+ return;
+ }
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ for (int i = 0; i < m_NumSegments; i++)
+ {
+ if (!m_HasFullBeam[i])
+ {
+ continue;
+ }
+ int z = m_BoundingBox.p1.z + i * 5 + 1 - BlockZ;
+ if ((z >= 0) && (z < cChunkDef::Width))
+ {
+ if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZP);
+ }
+ }
+ z += 2;
+ if ((z >= 0) && (z < cChunkDef::Width))
+ {
+ if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZM);
+ }
+ }
+ } // for i
+ break;
+ }
+ } // switch (direction)
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMineShaftCrossing:
+
+cMineShaftCrossing::cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox) :
+ super(a_ParentSystem, mskCrossing, a_BoundingBox)
+{
+}
+
+
+
+
+
+cMineShaft * cMineShaftCrossing::CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+)
+{
+ cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ);
+ int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7;
+ BoundingBox.p2.y += 3;
+ if ((rnd % 4) < 2)
+ {
+ // 2-level crossing:
+ BoundingBox.p2.y += 4;
+ rnd >>= 2;
+ if ((rnd % 4) < 2)
+ {
+ // This is the higher level:
+ BoundingBox.p1.y -= 4;
+ BoundingBox.p2.y -= 4;
+ }
+ }
+ rnd >>= 2;
+ switch (a_Direction)
+ {
+ case dirXP: BoundingBox.p2.x += 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break;
+ case dirXM: BoundingBox.p1.x -= 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break;
+ case dirZP: BoundingBox.p2.z += 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break;
+ case dirZM: BoundingBox.p1.z -= 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break;
+ }
+ if (!a_ParentSystem.CanAppend(BoundingBox))
+ {
+ return NULL;
+ }
+ return new cMineShaftCrossing(a_ParentSystem, BoundingBox);
+}
+
+
+
+
+
+void cMineShaftCrossing::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
+{
+ struct
+ {
+ int x, y, z;
+ eDirection dir;
+ } Exits[] =
+ {
+ // Bottom level:
+ {-1, 1, 2, dirXM},
+ { 2, 1, -1, dirZM},
+ { 5, 1, 2, dirXP},
+ { 2, 1, 5, dirZP},
+ // Top level:
+ {-1, 5, 2, dirXM},
+ { 2, 5, -1, dirZM},
+ { 5, 5, 2, dirXP},
+ { 2, 5, 5, dirZP},
+ } ;
+ for (unsigned int i = 0; i < ARRAYCOUNT(Exits); i++)
+ {
+ if (m_BoundingBox.p1.y + Exits[i].y >= m_BoundingBox.p2.y)
+ {
+ // This exit is not available (two-level exit on a one-level crossing)
+ continue;
+ }
+
+ int Height = m_BoundingBox.p1.y + Exits[i].y;
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Exits[i].x, Height, m_BoundingBox.p1.z + Exits[i].z, Exits[i].dir, a_Noise, a_RecursionLevel);
+ } // for i
+}
+
+
+
+
+
+void cMineShaftCrossing::ProcessChunk(cChunkDesc & a_ChunkDesc)
+{
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ cCuboid box(m_BoundingBox);
+ box.Move(-BlockX, 0, -BlockZ);
+ if ((box.p2.x < 0) || (box.p2.z < 0) || (box.p1.x >= cChunkDef::Width) || (box.p1.z > cChunkDef::Width))
+ {
+ // Does not intersect this chunk
+ return;
+ }
+ int Floor = box.p1.y + 1;
+ int Ceil = box.p2.y;
+
+ // The supports:
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0);
+
+ // The air in between:
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 2, box.p1.x + 2, Floor, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 2, box.p1.z + 2, E_BLOCK_AIR, 0);
+
+ // The air on the edges:
+ int Mid = Floor + 2;
+ a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p1.z, box.p1.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p2.z, box.p2.z, E_BLOCK_AIR, 0);
+ Mid += 2;
+ if (Mid < Ceil)
+ {
+ a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p1.z, box.p1.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p2.z, box.p2.z, E_BLOCK_AIR, 0);
+ }
+
+ // The floor, if needed:
+ box.p2.y = box.p1.y;
+ a_ChunkDesc.FloorRelCuboid(box, E_BLOCK_PLANKS, 0);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMineShaftStaircase:
+
+cMineShaftStaircase::cMineShaftStaircase(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ const cCuboid & a_BoundingBox,
+ eDirection a_Direction,
+ eSlope a_Slope
+) :
+ super(a_ParentSystem, mskStaircase, a_BoundingBox),
+ m_Direction(a_Direction),
+ m_Slope(a_Slope)
+{
+}
+
+
+
+
+
+cMineShaft * cMineShaftStaircase::CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+)
+{
+ int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7;
+ cCuboid Box;
+ switch (a_Direction)
+ {
+ case dirXM:
+ {
+ Box.Assign(a_PivotX - 7, a_PivotY - 1, a_PivotZ - 1, a_PivotX, a_PivotY + 6, a_PivotZ + 1);
+ break;
+ }
+ case dirXP:
+ {
+ Box.Assign(a_PivotX, a_PivotY - 1, a_PivotZ - 1, a_PivotX + 7, a_PivotY + 6, a_PivotZ + 1);
+ break;
+ }
+ case dirZM:
+ {
+ Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ - 7, a_PivotX + 1, a_PivotY + 6, a_PivotZ);
+ break;
+ }
+ case dirZP:
+ {
+ Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ, a_PivotX + 1, a_PivotY + 6, a_PivotZ + 7);
+ break;
+ }
+ }
+ eSlope Slope = sUp;
+ if ((rnd % 4) < 2) // 50 %
+ {
+ Slope = sDown;
+ Box.Move(0, -4, 0);
+ }
+ if (!a_ParentSystem.CanAppend(Box))
+ {
+ return NULL;
+ }
+ return new cMineShaftStaircase(a_ParentSystem, Box, a_Direction, Slope);
+}
+
+
+
+
+
+void cMineShaftStaircase::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
+{
+ int Height = m_BoundingBox.p1.y + ((m_Slope == sDown) ? 1 : 5);
+ switch (m_Direction)
+ {
+ case dirXM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); break;
+ case dirXP: m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); break;
+ case dirZM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); break;
+ case dirZP: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); break;
+ }
+}
+
+
+
+
+
+void cMineShaftStaircase::ProcessChunk(cChunkDesc & a_ChunkDesc)
+{
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ cCuboid RelB(m_BoundingBox);
+ RelB.Move(-BlockX, 0, -BlockZ);
+ if (
+ (RelB.p1.x >= cChunkDef::Width) ||
+ (RelB.p1.z >= cChunkDef::Width) ||
+ (RelB.p2.x < 0) ||
+ (RelB.p2.z < 0)
+ )
+ {
+ // No intersection between this staircase and this chunk
+ return;
+ }
+
+ int SFloor = RelB.p1.y + ((m_Slope == sDown) ? 5 : 1);
+ int DFloor = RelB.p1.y + ((m_Slope == sDown) ? 1 : 5);
+ int Add = (m_Slope == sDown) ? -1 : 1;
+ int InitAdd = (m_Slope == sDown) ? -1 : 0;
+ cCuboid Box;
+ switch (m_Direction)
+ {
+ case dirXM:
+ {
+ a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Assign(RelB.p2.x - 2, SFloor + InitAdd, RelB.p1.z, RelB.p2.x - 2, SFloor + 3 + InitAdd, RelB.p2.z);
+ for (int i = 0; i < 4; i++)
+ {
+ a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Move(-1, Add, 0);
+ }
+ break;
+ }
+
+ case dirXP:
+ {
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Assign(RelB.p1.x + 2, SFloor + InitAdd, RelB.p1.z, RelB.p1.x + 2, SFloor + 3 + InitAdd, RelB.p2.z);
+ for (int i = 0; i < 4; i++)
+ {
+ a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Move(1, Add, 0);
+ }
+ break;
+ }
+
+ case dirZM:
+ {
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0);
+ Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p2.z - 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p2.z - 2);
+ for (int i = 0; i < 4; i++)
+ {
+ a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Move(0, Add, -1);
+ }
+ break;
+ }
+
+ case dirZP:
+ {
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p1.z + 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p1.z + 2);
+ for (int i = 0; i < 4; i++)
+ {
+ a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Move(0, Add, 1);
+ }
+ break;
+ }
+
+ } // switch (m_Direction)
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenMineShafts:
+
+cStructGenMineShafts::cStructGenMineShafts(
+ int a_Seed, int a_GridSize, int a_MaxSystemSize,
+ int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase
+) :
+ m_Noise(a_Seed),
+ m_GridSize(a_GridSize),
+ m_MaxSystemSize(a_MaxSystemSize),
+ m_ProbLevelCorridor(std::max(0, a_ChanceCorridor)),
+ m_ProbLevelCrossing(std::max(0, a_ChanceCorridor + a_ChanceCrossing)),
+ m_ProbLevelStaircase(std::max(0, a_ChanceCorridor + a_ChanceCrossing + a_ChanceStaircase))
+{
+}
+
+
+
+
+
+cStructGenMineShafts::~cStructGenMineShafts()
+{
+ ClearCache();
+}
+
+
+
+
+
+void cStructGenMineShafts::ClearCache(void)
+{
+ for (cMineShaftSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ } // for itr - m_Cache[]
+ m_Cache.clear();
+}
+
+
+
+
+
+void cStructGenMineShafts::GetMineShaftSystemsForChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cStructGenMineShafts::cMineShaftSystems & a_MineShafts
+)
+{
+ int BaseX = a_ChunkX * cChunkDef::Width / m_GridSize;
+ int BaseZ = a_ChunkZ * cChunkDef::Width / m_GridSize;
+ if (BaseX < 0)
+ {
+ --BaseX;
+ }
+ if (BaseZ < 0)
+ {
+ --BaseZ;
+ }
+ BaseX -= NEIGHBORHOOD_SIZE / 2;
+ BaseZ -= NEIGHBORHOOD_SIZE / 2;
+
+ // Walk the cache, move each cave system that we want into a_Caves:
+ int StartX = BaseX * m_GridSize;
+ int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_GridSize;
+ int StartZ = BaseZ * m_GridSize;
+ int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_GridSize;
+ for (cMineShaftSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
+ {
+ if (
+ ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
+ ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
+ )
+ {
+ // want
+ a_MineShafts.push_back(*itr);
+ itr = m_Cache.erase(itr);
+ }
+ else
+ {
+ // don't want
+ ++itr;
+ }
+ } // for itr - m_Cache[]
+
+ for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
+ {
+ int RealX = (BaseX + x) * m_GridSize;
+ for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
+ {
+ int RealZ = (BaseZ + z) * m_GridSize;
+ bool Found = false;
+ for (cMineShaftSystems::const_iterator itr = a_MineShafts.begin(), end = a_MineShafts.end(); itr != end; ++itr)
+ {
+ if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
+ {
+ Found = true;
+ break;
+ }
+ } // for itr - a_Mineshafts
+ if (!Found)
+ {
+ a_MineShafts.push_back(new cMineShaftSystem(RealX, RealZ, m_GridSize, m_MaxSystemSize, m_Noise, m_ProbLevelCorridor, m_ProbLevelCrossing, m_ProbLevelStaircase));
+ }
+ } // for z
+ } // for x
+
+ // Copy a_MineShafts into m_Cache to the beginning:
+ cMineShaftSystems MineShaftsCopy(a_MineShafts);
+ m_Cache.splice(m_Cache.begin(), MineShaftsCopy, MineShaftsCopy.begin(), MineShaftsCopy.end());
+
+ // Trim the cache if it's too long:
+ if (m_Cache.size() > 100)
+ {
+ cMineShaftSystems::iterator itr = m_Cache.begin();
+ std::advance(itr, 100);
+ for (cMineShaftSystems::iterator end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ itr = m_Cache.begin();
+ std::advance(itr, 100);
+ m_Cache.erase(itr, m_Cache.end());
+ }
+}
+
+
+
+
+
+
+void cStructGenMineShafts::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+ cMineShaftSystems MineShafts;
+ GetMineShaftSystemsForChunk(ChunkX, ChunkZ, MineShafts);
+ for (cMineShaftSystems::const_iterator itr = MineShafts.begin(); itr != MineShafts.end(); ++itr)
+ {
+ (*itr)->ProcessChunk(a_ChunkDesc);
+ } // for itr - MineShafts[]
+}
+
+
+
+
diff --git a/src/Generating/MineShafts.h b/src/Generating/MineShafts.h
new file mode 100644
index 000000000..c53d3bc53
--- /dev/null
+++ b/src/Generating/MineShafts.h
@@ -0,0 +1,61 @@
+
+// MineShafts.h
+
+// Declares the cStructGenMineShafts class representing the structure generator for abandoned mineshafts
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cStructGenMineShafts :
+ public cStructureGen
+{
+public:
+ cStructGenMineShafts(
+ int a_Seed, int a_GridSize, int a_MaxSystemSize,
+ int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase
+ );
+
+ virtual ~cStructGenMineShafts();
+
+protected:
+ friend class cMineShaft;
+ friend class cMineShaftDirtRoom;
+ friend class cMineShaftCorridor;
+ friend class cMineShaftCrossing;
+ friend class cMineShaftStaircase;
+ class cMineShaftSystem; // fwd: MineShafts.cpp
+ typedef std::list<cMineShaftSystem *> cMineShaftSystems;
+
+ cNoise m_Noise;
+ int m_GridSize; ///< Average spacing of the systems
+ int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system
+ int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor
+ int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor
+ int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing
+ cMineShaftSystems m_Cache; ///< Cache of the most recently used systems. MoveToFront used.
+
+ /// Clears everything from the cache
+ void ClearCache(void);
+
+ /** Returns all systems that *may* intersect the given chunk.
+ All the systems are valid until the next call to this function (which may delete some of the pointers).
+ */
+ void GetMineShaftSystemsForChunk(int a_ChunkX, int a_ChunkZ, cMineShaftSystems & a_MineShaftSystems);
+
+ // cStructureGen overrides:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp
new file mode 100644
index 000000000..9760ca4ca
--- /dev/null
+++ b/src/Generating/Noise3DGenerator.cpp
@@ -0,0 +1,576 @@
+
+// Nosie3DGenerator.cpp
+
+// Generates terrain using 3D noise, rather than composing. Is a test.
+
+#include "Globals.h"
+#include "Noise3DGenerator.h"
+#include "../OSSupport/File.h"
+#include "lib/inifile/iniFile.h"
+#include "../LinearInterpolation.h"
+#include "../LinearUpscale.h"
+
+
+
+
+
+/*
+// Perform an automatic test of upscaling upon program start (use breakpoints to debug):
+
+class Test
+{
+public:
+ Test(void)
+ {
+ DoTest1();
+ DoTest2();
+ }
+
+
+ void DoTest1(void)
+ {
+ float In[3 * 3 * 3];
+ for (int i = 0; i < ARRAYCOUNT(In); i++)
+ {
+ In[i] = (float)(i % 5);
+ }
+ Debug3DNoise(In, 3, 3, 3, "Upscale3D in");
+ float Out[17 * 33 * 35];
+ LinearUpscale3DArray(In, 3, 3, 3, Out, 8, 16, 17);
+ Debug3DNoise(Out, 17, 33, 35, "Upscale3D test");
+ }
+
+
+ void DoTest2(void)
+ {
+ float In[3 * 3];
+ for (int i = 0; i < ARRAYCOUNT(In); i++)
+ {
+ In[i] = (float)(i % 5);
+ }
+ Debug2DNoise(In, 3, 3, "Upscale2D in");
+ float Out[17 * 33];
+ LinearUpscale2DArray(In, 3, 3, Out, 8, 16);
+ Debug2DNoise(Out, 17, 33, "Upscale2D test");
+ }
+
+} gTest;
+//*/
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cNoise3DGenerator:
+
+cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
+ super(a_ChunkGenerator),
+ m_Perlin(1000),
+ m_Cubic(1000)
+{
+ m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5);
+ m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1);
+ m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2);
+
+ #if 0
+ // DEBUG: Test the noise generation:
+ // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size
+ // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M
+ m_SeaLevel = 62;
+ m_HeightAmplification = 0;
+ m_MidPoint = 75;
+ m_FrequencyX = 4;
+ m_FrequencyY = 4;
+ m_FrequencyZ = 4;
+ m_AirThreshold = 0.5;
+
+ const int NumChunks = 4;
+ NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height];
+ for (int x = 0; x < NumChunks; x++)
+ {
+ GenerateNoiseArray(x, 5, Noise[x]);
+ }
+
+ // Save in XY cuts:
+ cFile f1;
+ if (f1.Open("Test_XY.grab", cFile::fmWrite))
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int i = 0; i < NumChunks; i++)
+ {
+ int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
+ unsigned char buf[cChunkDef::Width];
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
+ }
+ f1.Write(buf, cChunkDef::Width);
+ }
+ } // for y
+ } // for z
+ } // if (XY file open)
+
+ cFile f2;
+ if (f2.Open("Test_XZ.grab", cFile::fmWrite))
+ {
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int i = 0; i < NumChunks; i++)
+ {
+ int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
+ unsigned char buf[cChunkDef::Width];
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
+ }
+ f2.Write(buf, cChunkDef::Width);
+ }
+ } // for z
+ } // for y
+ } // if (XZ file open)
+ #endif // 0
+}
+
+
+
+
+
+cNoise3DGenerator::~cNoise3DGenerator()
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
+{
+ m_World = a_World;
+
+ // Params:
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
+ m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
+ m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8);
+ m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
+}
+
+
+
+
+
+void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ for (unsigned int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
+ {
+ a_BiomeMap[i] = biExtremeHills;
+ }
+}
+
+
+
+
+
+void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
+{
+ NOISE_DATATYPE Noise[17 * 257 * 17];
+ GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise);
+
+ // Output noise into chunk:
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ int idx = z * 17 * 257 + y * 17;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ NOISE_DATATYPE n = Noise[idx++];
+ BLOCKTYPE BlockType;
+ if (n > m_AirThreshold)
+ {
+ BlockType = (y > m_SeaLevel) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER;
+ }
+ else
+ {
+ BlockType = E_BLOCK_STONE;
+ }
+ a_ChunkDesc.SetBlockType(x, y, z, BlockType);
+ }
+ }
+ }
+
+ UpdateHeightmap(a_ChunkDesc);
+ ComposeTerrain (a_ChunkDesc);
+}
+
+
+
+
+
+void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise)
+{
+ NOISE_DATATYPE NoiseO[DIM_X * DIM_Y * DIM_Z]; // Output for the Perlin noise
+ NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash
+
+ // Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed"
+ NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX;
+ NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
+ NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ;
+ NOISE_DATATYPE StartY = 0;
+ NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY;
+
+ m_Perlin.Generate3D(NoiseO, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW);
+
+ // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ));
+
+ // Precalculate a "height" array:
+ NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source")
+ m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25);
+ for (unsigned int i = 0; i < ARRAYCOUNT(Height); i++)
+ {
+ Height[i] = abs(Height[i]) * m_HeightAmplification + 1;
+ }
+
+ // Modify the noise by height data:
+ for (int y = 0; y < DIM_Y; y++)
+ {
+ NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20;
+ AddHeight *= AddHeight * AddHeight;
+ for (int z = 0; z < DIM_Z; z++)
+ {
+ NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]);
+ for (int x = 0; x < DIM_X; x++)
+ {
+ CurRow[x] += AddHeight / Height[x + DIM_X * z];
+ }
+ }
+ }
+
+ // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ));
+
+ // Upscale the Perlin noise into full-blown chunk dimensions:
+ LinearUpscale3DArray(
+ NoiseO, DIM_X, DIM_Y, DIM_Z,
+ a_OutNoise, UPSCALE_X, UPSCALE_Y, UPSCALE_Z
+ );
+
+ // DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ));
+}
+
+
+
+
+
+void cNoise3DGenerator::UpdateHeightmap(cChunkDesc & a_ChunkDesc)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int y = cChunkDef::Height - 1; y > 0; y--)
+ {
+ if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR)
+ {
+ a_ChunkDesc.SetHeight(x, z, y);
+ break;
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ // Make basic terrain composition:
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
+ bool HasHadWater = false;
+ for (int y = LastAir - 1; y > 0; y--)
+ {
+ switch (a_ChunkDesc.GetBlockType(x, y, z))
+ {
+ case E_BLOCK_AIR:
+ {
+ LastAir = y;
+ break;
+ }
+ case E_BLOCK_STONE:
+ {
+ if (LastAir - y > 3)
+ {
+ break;
+ }
+ if (HasHadWater)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
+ }
+ else
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
+ }
+ break;
+ }
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ LastAir = y;
+ HasHadWater = true;
+ break;
+ }
+ } // switch (GetBlockType())
+ } // for y
+ a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cNoise3DComposable:
+
+cNoise3DComposable::cNoise3DComposable(int a_Seed) :
+ m_Noise1(a_Seed + 1000),
+ m_Noise2(a_Seed + 2000),
+ m_Noise3(a_Seed + 3000)
+{
+}
+
+
+
+
+
+void cNoise3DComposable::Initialize(cIniFile & a_IniFile)
+{
+ // Params:
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
+ m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
+ m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10);
+ m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
+}
+
+
+
+
+
+void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
+{
+ if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ))
+ {
+ // The noise for this chunk is already generated in m_Noise
+ return;
+ }
+ m_LastChunkX = a_ChunkX;
+ m_LastChunkZ = a_ChunkZ;
+
+ // Upscaling parameters:
+ const int UPSCALE_X = 8;
+ const int UPSCALE_Y = 4;
+ const int UPSCALE_Z = 8;
+
+ // Precalculate a "height" array:
+ NOISE_DATATYPE Height[17 * 17]; // x + 17 * z
+ for (int z = 0; z < 17; z += UPSCALE_Z)
+ {
+ NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
+ for (int x = 0; x < 17; x += UPSCALE_X)
+ {
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
+ NOISE_DATATYPE val = abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1;
+ Height[x + 17 * z] = val * val * val;
+ }
+ }
+
+ for (int y = 0; y < 257; y += UPSCALE_Y)
+ {
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY;
+ NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20;
+ AddHeight *= AddHeight * AddHeight;
+ NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
+ for (int z = 0; z < 17; z += UPSCALE_Z)
+ {
+ NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
+ for (int x = 0; x < 17; x += UPSCALE_X)
+ {
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
+ CurFloor[x + 17 * z] =
+ m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 +
+ m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) +
+ m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 +
+ AddHeight / Height[x + 17 * z];
+ }
+ }
+ // Linear-interpolate this XZ floor:
+ LinearUpscale2DArrayInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z);
+ }
+
+ // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis
+ for (int y = 1; y < cChunkDef::Height; y++)
+ {
+ if ((y % UPSCALE_Y) == 0)
+ {
+ // This is the interpolation source floor, already calculated
+ continue;
+ }
+ int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y;
+ int HiFloorY = LoFloorY + UPSCALE_Y;
+ NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]);
+ NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]);
+ NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
+ NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y;
+ int idx = 0;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio;
+ idx += 1;
+ }
+ idx += 1; // Skipping one X column
+ }
+ }
+
+ // The noise array is now fully interpolated
+ /*
+ // DEBUG: Output two images of the array, sliced by XY and XZ:
+ cFile f1;
+ if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ int idx = y * 17 * 17 + z * 17;
+ unsigned char buf[16];
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
+ }
+ f1.Write(buf, 16);
+ } // for y
+ } // for z
+ } // if (XY file open)
+
+ cFile f2;
+ if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
+ {
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int idx = y * 17 * 17 + z * 17;
+ unsigned char buf[16];
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
+ }
+ f2.Write(buf, 16);
+ } // for z
+ } // for y
+ } // if (XZ file open)
+ */
+}
+
+
+
+
+
+void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
+
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
+ for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
+ {
+ if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, y);
+ break;
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
+
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+
+ // Make basic terrain composition:
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
+ bool HasHadWater = false;
+ for (int y = LastAir; y < m_SeaLevel; y++)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
+ }
+ for (int y = LastAir - 1; y > 0; y--)
+ {
+ if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold)
+ {
+ // "air" part
+ LastAir = y;
+ if (y < m_SeaLevel)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
+ HasHadWater = true;
+ }
+ continue;
+ }
+ // "ground" part:
+ if (LastAir - y > 4)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
+ continue;
+ }
+ if (HasHadWater)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
+ }
+ else
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
+ }
+ } // for y
+ a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
+ } // for x
+ } // for z
+}
+
+
+
+
diff --git a/src/Generating/Noise3DGenerator.h b/src/Generating/Noise3DGenerator.h
new file mode 100644
index 000000000..0d211cddc
--- /dev/null
+++ b/src/Generating/Noise3DGenerator.h
@@ -0,0 +1,106 @@
+
+// Noise3DGenerator.h
+
+// Generates terrain using 3D noise, rather than composing. Is a test.
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cNoise3DGenerator :
+ public cChunkGenerator::cGenerator
+{
+ typedef cChunkGenerator::cGenerator super;
+
+public:
+ cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator);
+ virtual ~cNoise3DGenerator();
+
+ virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
+ virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
+
+protected:
+ // Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
+ static const int UPSCALE_X = 8;
+ static const int UPSCALE_Y = 4;
+ static const int UPSCALE_Z = 8;
+
+ // Linear interpolation buffer dimensions, calculated from the step sizes:
+ static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
+ static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
+ static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
+
+ cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition
+ cCubicNoise m_Cubic; // The noise used for heightmap directing
+
+ int m_SeaLevel;
+ NOISE_DATATYPE m_HeightAmplification;
+ NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
+ NOISE_DATATYPE m_FrequencyX;
+ NOISE_DATATYPE m_FrequencyY;
+ NOISE_DATATYPE m_FrequencyZ;
+ NOISE_DATATYPE m_AirThreshold;
+
+ /// Generates the 3D noise array used for terrain generation; a_Noise is of ChunkData-size
+ void GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise);
+
+ /// Updates heightmap based on the chunk's contents
+ void UpdateHeightmap(cChunkDesc & a_ChunkDesc);
+
+ /// Composes terrain - adds dirt, grass and sand
+ void ComposeTerrain(cChunkDesc & a_ChunkDesc);
+} ;
+
+
+
+
+
+class cNoise3DComposable :
+ public cTerrainHeightGen,
+ public cTerrainCompositionGen
+{
+public:
+ cNoise3DComposable(int a_Seed);
+
+ void Initialize(cIniFile & a_IniFile);
+
+protected:
+ cNoise m_Noise1;
+ cNoise m_Noise2;
+ cNoise m_Noise3;
+
+ int m_SeaLevel;
+ NOISE_DATATYPE m_HeightAmplification;
+ NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
+ NOISE_DATATYPE m_FrequencyX;
+ NOISE_DATATYPE m_FrequencyY;
+ NOISE_DATATYPE m_FrequencyZ;
+ NOISE_DATATYPE m_AirThreshold;
+
+ int m_LastChunkX;
+ int m_LastChunkZ;
+ NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
+
+
+ /// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given
+ void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
diff --git a/src/Generating/Ravines.cpp b/src/Generating/Ravines.cpp
new file mode 100644
index 000000000..6413b963b
--- /dev/null
+++ b/src/Generating/Ravines.cpp
@@ -0,0 +1,531 @@
+
+// Ravines.cpp
+
+// Implements the cStructGenRavines class representing the ravine structure generator
+
+#include "Globals.h"
+#include "Ravines.h"
+
+
+
+
+/// How many ravines in each direction are generated for a given chunk. Must be an even number
+static const int NEIGHBORHOOD_SIZE = 8;
+
+static const int NUM_RAVINE_POINTS = 4;
+
+
+
+
+
+struct cRavDefPoint
+{
+ int m_BlockX;
+ int m_BlockZ;
+ int m_Radius;
+ int m_Top;
+ int m_Bottom;
+
+ cRavDefPoint(int a_BlockX, int a_BlockZ, int a_Radius, int a_Top, int a_Bottom) :
+ m_BlockX(a_BlockX),
+ m_BlockZ(a_BlockZ),
+ m_Radius(a_Radius),
+ m_Top (a_Top),
+ m_Bottom(a_Bottom)
+ {
+ }
+} ;
+
+typedef std::vector<cRavDefPoint> cRavDefPoints;
+
+
+
+
+
+class cStructGenRavines::cRavine
+{
+ cRavDefPoints m_Points;
+
+ /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise
+ void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
+
+ /// Refines (adds and smooths) defpoints from a_Src into a_Dst
+ void RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst);
+
+ /// Does one round of smoothing, two passes of RefineDefPoints()
+ void Smooth(void);
+
+ /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block
+ void FinishLinear(void);
+
+public:
+ // Coords for which the ravine was generated (not necessarily the center)
+ int m_BlockX;
+ int m_BlockZ;
+
+ cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
+
+ /// Carves the ravine into the chunk specified
+ void ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+ );
+
+ #ifdef _DEBUG
+ /// Exports itself as a SVG line definition
+ AString ExportAsSVG(int a_Color, int a_OffsetX = 0, int a_OffsetZ = 0) const;
+ #endif // _DEBUG
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenRavines:
+
+cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) :
+ m_Noise(a_Seed),
+ m_Size(a_Size)
+{
+}
+
+
+
+
+
+cStructGenRavines::~cStructGenRavines()
+{
+ ClearCache();
+}
+
+
+
+
+
+void cStructGenRavines::ClearCache(void)
+{
+ for (cRavines::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ } // for itr - m_Cache[]
+ m_Cache.clear();
+}
+
+
+
+
+
+void cStructGenRavines::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+ cRavines Ravines;
+ GetRavinesForChunk(ChunkX, ChunkZ, Ravines);
+ for (cRavines::const_iterator itr = Ravines.begin(), end = Ravines.end(); itr != end; ++itr)
+ {
+ (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
+ } // for itr - Ravines[]
+}
+
+
+
+
+
+void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenRavines::cRavines & a_Ravines)
+{
+ int BaseX = a_ChunkX * cChunkDef::Width / m_Size;
+ int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size;
+ if (BaseX < 0)
+ {
+ --BaseX;
+ }
+ if (BaseZ < 0)
+ {
+ --BaseZ;
+ }
+ BaseX -= 4;
+ BaseZ -= 4;
+
+ // Walk the cache, move each ravine that we want into a_Ravines:
+ int StartX = BaseX * m_Size;
+ int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Size;
+ int StartZ = BaseZ * m_Size;
+ int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Size;
+ for (cRavines::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
+ {
+ if (
+ ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
+ ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
+ )
+ {
+ // want
+ a_Ravines.push_back(*itr);
+ itr = m_Cache.erase(itr);
+ }
+ else
+ {
+ // don't want
+ ++itr;
+ }
+ } // for itr - m_Cache[]
+
+ for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
+ {
+ int RealX = (BaseX + x) * m_Size;
+ for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
+ {
+ int RealZ = (BaseZ + z) * m_Size;
+ bool Found = false;
+ for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr)
+ {
+ if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
+ {
+ Found = true;
+ break;
+ }
+ }
+ if (!Found)
+ {
+ a_Ravines.push_back(new cRavine(RealX, RealZ, m_Size, m_Noise));
+ }
+ }
+ }
+
+ // Copy a_Ravines into m_Cache to the beginning:
+ cRavines RavinesCopy(a_Ravines);
+ m_Cache.splice(m_Cache.begin(), RavinesCopy, RavinesCopy.begin(), RavinesCopy.end());
+
+ // Trim the cache if it's too long:
+ if (m_Cache.size() > 100)
+ {
+ cRavines::iterator itr = m_Cache.begin();
+ std::advance(itr, 100);
+ for (cRavines::iterator end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ itr = m_Cache.begin();
+ std::advance(itr, 100);
+ m_Cache.erase(itr, m_Cache.end());
+ }
+
+ /*
+ #ifdef _DEBUG
+ // DEBUG: Export as SVG into a file specific for the chunk, for visual verification:
+ AString SVG;
+ SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
+ for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr)
+ {
+ SVG.append((*itr)->ExportAsSVG(0, 512, 512));
+ }
+ SVG.append("</svg>\n");
+
+ AString fnam;
+ Printf(fnam, "ravines\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
+ cFile File(fnam, cFile::fmWrite);
+ File.Write(SVG.c_str(), SVG.size());
+ #endif // _DEBUG
+ //*/
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenRavines::cRavine
+
+cStructGenRavines::cRavine::cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) :
+ m_BlockX(a_BlockX),
+ m_BlockZ(a_BlockZ)
+{
+ // Calculate the ravine shape-defining points:
+ GenerateBaseDefPoints(a_BlockX, a_BlockZ, a_Size, a_Noise);
+
+ // Smooth the ravine. A two passes are needed:
+ Smooth();
+ Smooth();
+
+ // Linearly interpolate the neighbors so that they're close enough together:
+ FinishLinear();
+}
+
+
+
+
+
+void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise)
+{
+ // Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size):
+ a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024;
+
+ // The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction
+ int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2;
+ int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2;
+ int CenterX = a_BlockX + OffsetX;
+ int CenterZ = a_BlockZ + OffsetZ;
+
+ // Get the base angle in which the ravine "axis" goes:
+ float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * 3.141592653);
+ float xc = sin(Angle);
+ float zc = cos(Angle);
+
+ // Calculate the definition points and radii:
+ int MaxRadius = (int)(sqrt(12.0 + ((a_Noise.IntNoise2DInt(61 * a_BlockX, 97 * a_BlockZ) / 13) % a_Size) / 16));
+ int Top = 32 + ((a_Noise.IntNoise2DInt(13 * a_BlockX, 17 * a_BlockZ) / 23) % 32);
+ int Bottom = 5 + ((a_Noise.IntNoise2DInt(17 * a_BlockX, 29 * a_BlockZ) / 13) % 32);
+ int Mid = (Top + Bottom) / 2;
+ int PointX = CenterX - (int)(xc * a_Size / 2);
+ int PointZ = CenterZ - (int)(zc * a_Size / 2);
+ m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, (Mid + Top) / 2, (Mid + Bottom) / 2));
+ for (int i = 1; i < NUM_RAVINE_POINTS - 1; i++)
+ {
+ int LineX = CenterX + (int)(xc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS);
+ int LineZ = CenterZ + (int)(zc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS);
+ // Amplitude is the amount of blocks that this point is away from the ravine "axis"
+ int Amplitude = (a_Noise.IntNoise3DInt(70 * a_BlockX, 20 * a_BlockZ + 31 * i, 10000 * i) / 9) % a_Size;
+ Amplitude = Amplitude / 4 - a_Size / 8; // Amplitude is in interval [-a_Size / 4, a_Size / 4]
+ int PointX = LineX + (int)(zc * Amplitude);
+ int PointZ = LineZ - (int)(xc * Amplitude);
+ int Radius = MaxRadius - abs(i - NUM_RAVINE_POINTS / 2); // TODO: better radius function
+ int ThisTop = Top + ((a_Noise.IntNoise3DInt(7 * a_BlockX, 19 * a_BlockZ, i * 31) / 13) % 8) - 4;
+ int ThisBottom = Bottom + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 7 * a_BlockZ, i * 31) / 13) % 8) - 4;
+ m_Points.push_back(cRavDefPoint(PointX, PointZ, Radius, ThisTop, ThisBottom));
+ } // for i - m_Points[]
+ PointX = CenterX + (int)(xc * a_Size / 2);
+ PointZ = CenterZ + (int)(zc * a_Size / 2);
+ m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, Mid, Mid));
+}
+
+
+
+
+
+void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst)
+{
+ // Smoothing: for each line segment, add points on its 1/4 lengths
+ int Num = a_Src.size() - 2; // this many intermediary points
+ a_Dst.clear();
+ a_Dst.reserve(Num * 2 + 2);
+ cRavDefPoints::const_iterator itr = a_Src.begin() + 1;
+ a_Dst.push_back(a_Src.front());
+ int PrevX = a_Src.front().m_BlockX;
+ int PrevZ = a_Src.front().m_BlockZ;
+ int PrevR = a_Src.front().m_Radius;
+ int PrevT = a_Src.front().m_Top;
+ int PrevB = a_Src.front().m_Bottom;
+ for (int i = 0; i <= Num; ++i, ++itr)
+ {
+ int dx = itr->m_BlockX - PrevX;
+ int dz = itr->m_BlockZ - PrevZ;
+ if (abs(dx) + abs(dz) < 4)
+ {
+ // Too short a segment to smooth-subdivide into quarters
+ continue;
+ }
+ int dr = itr->m_Radius - PrevR;
+ int dt = itr->m_Top - PrevT;
+ int db = itr->m_Bottom - PrevB;
+ int Rad1 = std::max(PrevR + 1 * dr / 4, 1);
+ int Rad2 = std::max(PrevR + 3 * dr / 4, 1);
+ a_Dst.push_back(cRavDefPoint(PrevX + 1 * dx / 4, PrevZ + 1 * dz / 4, Rad1, PrevT + 1 * dt / 4, PrevB + 1 * db / 4));
+ a_Dst.push_back(cRavDefPoint(PrevX + 3 * dx / 4, PrevZ + 3 * dz / 4, Rad2, PrevT + 3 * dt / 4, PrevB + 3 * db / 4));
+ PrevX = itr->m_BlockX;
+ PrevZ = itr->m_BlockZ;
+ PrevR = itr->m_Radius;
+ PrevT = itr->m_Top;
+ PrevB = itr->m_Bottom;
+ }
+ a_Dst.push_back(a_Src.back());
+}
+
+
+
+
+
+void cStructGenRavines::cRavine::Smooth(void)
+{
+ cRavDefPoints Pts;
+ RefineDefPoints(m_Points, Pts); // Refine m_Points -> Pts
+ RefineDefPoints(Pts, m_Points); // Refine Pts -> m_Points
+}
+
+
+
+
+
+void cStructGenRavines::cRavine::FinishLinear(void)
+{
+ // For each segment, use Bresenham's line algorithm to draw a "line" of defpoints
+ // _X 2012_07_20: I tried modifying this algorithm to produce "thick" lines (only one coord change per point)
+ // But the results were about the same as the original, so I disposed of it again - no need to use twice the count of points
+
+ cRavDefPoints Pts;
+ std::swap(Pts, m_Points);
+
+ m_Points.reserve(Pts.size() * 3);
+ int PrevX = Pts.front().m_BlockX;
+ int PrevZ = Pts.front().m_BlockZ;
+ for (cRavDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
+ {
+ int x1 = itr->m_BlockX;
+ int z1 = itr->m_BlockZ;
+ int dx = abs(x1 - PrevX);
+ int dz = abs(z1 - PrevZ);
+ int sx = (PrevX < x1) ? 1 : -1;
+ int sz = (PrevZ < z1) ? 1 : -1;
+ int err = dx - dz;
+ int R = itr->m_Radius;
+ int T = itr->m_Top;
+ int B = itr->m_Bottom;
+ while (true)
+ {
+ m_Points.push_back(cRavDefPoint(PrevX, PrevZ, R, T, B));
+ if ((PrevX == x1) && (PrevZ == z1))
+ {
+ break;
+ }
+ int e2 = 2 * err;
+ if (e2 > -dz)
+ {
+ err -= dz;
+ PrevX += sx;
+ }
+ if (e2 < dx)
+ {
+ err += dx;
+ PrevZ += sz;
+ }
+ } // while (true)
+ } // for itr
+}
+
+
+
+
+
+#ifdef _DEBUG
+AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
+{
+ AString SVG;
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color);
+ char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L"
+ for (cRavDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr)
+ {
+ AppendPrintf(SVG, "%c %d,%d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ);
+ Prefix = 'L';
+ }
+ SVG.append("\"/>\n");
+
+ // Base point highlight:
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
+ );
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
+ );
+
+ // A gray line from the base point to the first point of the ravine, for identification:
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ
+ );
+
+ // Offset guides:
+ if (a_OffsetX > 0)
+ {
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n",
+ a_OffsetX, a_OffsetX
+ );
+ }
+ if (a_OffsetZ > 0)
+ {
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n",
+ a_OffsetZ, a_OffsetZ
+ );
+ }
+ return SVG;
+}
+#endif // _DEBUG
+
+
+
+
+
+void cStructGenRavines::cRavine::ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+)
+{
+ int BlockStartX = a_ChunkX * cChunkDef::Width;
+ int BlockStartZ = a_ChunkZ * cChunkDef::Width;
+ int BlockEndX = BlockStartX + cChunkDef::Width;
+ int BlockEndZ = BlockStartZ + cChunkDef::Width;
+ for (cRavDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
+ {
+ if (
+ (itr->m_BlockX + itr->m_Radius < BlockStartX) ||
+ (itr->m_BlockX - itr->m_Radius > BlockEndX) ||
+ (itr->m_BlockZ + itr->m_Radius < BlockStartZ) ||
+ (itr->m_BlockZ - itr->m_Radius > BlockEndZ)
+ )
+ {
+ // Cannot intersect, bail out early
+ continue;
+ }
+
+ // Carve out a cylinder around the xz point, m_Radius in diameter, from Bottom to Top:
+ int RadiusSq = itr->m_Radius * itr->m_Radius; // instead of doing sqrt for each distance, we do sqr of the radius
+ int DifX = BlockStartX - itr->m_BlockX; // substitution for faster calc
+ int DifZ = BlockStartZ - itr->m_BlockZ; // substitution for faster calc
+ for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ #ifdef _DEBUG
+ // DEBUG: Make the ravine shapepoints visible on a single layer (so that we can see with Minutor what's going on)
+ if ((DifX + x == 0) && (DifZ + z == 0))
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, 4, z, E_BLOCK_LAPIS_ORE);
+ }
+ #endif // _DEBUG
+
+ int DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z);
+ if (DistSq <= RadiusSq)
+ {
+ int Top = std::min(itr->m_Top, (int)(cChunkDef::Height)); // Stupid gcc needs int cast
+ for (int y = std::max(itr->m_Bottom, 1); y <= Top; y++)
+ {
+ switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z))
+ {
+ // Only carve out these specific block types
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_STONE:
+ case E_BLOCK_COBBLESTONE:
+ case E_BLOCK_GRAVEL:
+ case E_BLOCK_SAND:
+ case E_BLOCK_SANDSTONE:
+ case E_BLOCK_NETHERRACK:
+ case E_BLOCK_COAL_ORE:
+ case E_BLOCK_IRON_ORE:
+ case E_BLOCK_GOLD_ORE:
+ case E_BLOCK_DIAMOND_ORE:
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_REDSTONE_ORE_GLOWING:
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
+ break;
+ }
+ default: break;
+ }
+ }
+ }
+ } // for x, z - a_BlockTypes
+ } // for itr - m_Points[]
+}
+
+
+
+
diff --git a/src/Generating/Ravines.h b/src/Generating/Ravines.h
new file mode 100644
index 000000000..05164a5b2
--- /dev/null
+++ b/src/Generating/Ravines.h
@@ -0,0 +1,46 @@
+
+// Ravines.h
+
+// Interfaces to the cStructGenRavines class representing the ravine structure generator
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cStructGenRavines :
+ public cStructureGen
+{
+public:
+ cStructGenRavines(int a_Seed, int a_Size);
+ ~cStructGenRavines();
+
+protected:
+ class cRavine; // fwd: Ravines.cpp
+ typedef std::list<cRavine *> cRavines;
+
+ cNoise m_Noise;
+ int m_Size; // Max size, in blocks, of the ravines generated
+ cRavines m_Cache;
+
+ /// Clears everything from the cache
+ void ClearCache(void);
+
+ /// Returns all ravines that *may* intersect the given chunk. All the ravines are valid until the next call to this function.
+ void GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cRavines & a_Ravines);
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp
new file mode 100644
index 000000000..2180261aa
--- /dev/null
+++ b/src/Generating/StructGen.cpp
@@ -0,0 +1,675 @@
+
+// StructGen.h
+
+#include "Globals.h"
+#include "StructGen.h"
+#include "../BlockID.h"
+#include "Trees.h"
+#include "../BlockArea.h"
+#include "../LinearUpscale.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenOreNests configuration:
+
+const int MAX_HEIGHT_COAL = 127;
+const int NUM_NESTS_COAL = 50;
+const int NEST_SIZE_COAL = 10;
+
+const int MAX_HEIGHT_IRON = 64;
+const int NUM_NESTS_IRON = 14;
+const int NEST_SIZE_IRON = 6;
+
+const int MAX_HEIGHT_REDSTONE = 16;
+const int NUM_NESTS_REDSTONE = 4;
+const int NEST_SIZE_REDSTONE = 6;
+
+const int MAX_HEIGHT_GOLD = 32;
+const int NUM_NESTS_GOLD = 2;
+const int NEST_SIZE_GOLD = 6;
+
+const int MAX_HEIGHT_DIAMOND = 15;
+const int NUM_NESTS_DIAMOND = 1;
+const int NEST_SIZE_DIAMOND = 4;
+
+const int MAX_HEIGHT_LAPIS = 30;
+const int NUM_NESTS_LAPIS = 2;
+const int NEST_SIZE_LAPIS = 5;
+
+const int MAX_HEIGHT_DIRT = 127;
+const int NUM_NESTS_DIRT = 20;
+const int NEST_SIZE_DIRT = 32;
+
+const int MAX_HEIGHT_GRAVEL = 70;
+const int NUM_NESTS_GRAVEL = 15;
+const int NEST_SIZE_GRAVEL = 32;
+
+
+
+
+
+template <typename T> T Clamp(T a_Value, T a_Min, T a_Max)
+{
+ return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value);
+}
+
+
+
+
+
+static bool SortTreeBlocks(const sSetBlock & a_First, const sSetBlock & a_Second)
+{
+ return (a_First.BlockType == E_BLOCK_LOG) && (a_Second.BlockType != E_BLOCK_LOG);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenTrees:
+
+void cStructGenTrees::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+
+ cChunkDesc WorkerDesc(ChunkX, ChunkZ);
+
+ // Generate trees:
+ for (int x = 0; x <= 2; x++)
+ {
+ int BaseX = ChunkX + x - 1;
+ for (int z = 0; z <= 2; z++)
+ {
+ int BaseZ = ChunkZ + z - 1;
+
+ cChunkDesc * Dest;
+
+ if ((x != 1) || (z != 1))
+ {
+ Dest = &WorkerDesc;
+ WorkerDesc.SetChunkCoords(BaseX, BaseZ);
+
+ m_BiomeGen->GenBiomes (BaseX, BaseZ, WorkerDesc.GetBiomeMap());
+ m_HeightGen->GenHeightMap (BaseX, BaseZ, WorkerDesc.GetHeightMap());
+ m_CompositionGen->ComposeTerrain(WorkerDesc);
+ // TODO: Free the entity lists
+ }
+ else
+ {
+ Dest = &a_ChunkDesc;
+ }
+
+ int NumTrees = GetNumTrees(BaseX, BaseZ, Dest->GetBiomeMap());
+
+ sSetBlockVector OutsideLogs, OutsideOther;
+ for (int i = 0; i < NumTrees; i++)
+ {
+ GenerateSingleTree(BaseX, BaseZ, i, *Dest, OutsideLogs, OutsideOther);
+ }
+
+ sSetBlockVector IgnoredOverflow;
+ IgnoredOverflow.reserve(OutsideOther.size());
+ ApplyTreeImage(ChunkX, ChunkZ, a_ChunkDesc, OutsideOther, IgnoredOverflow);
+ IgnoredOverflow.clear();
+ IgnoredOverflow.reserve(OutsideLogs.size());
+ ApplyTreeImage(ChunkX, ChunkZ, a_ChunkDesc, OutsideLogs, IgnoredOverflow);
+ } // for z
+ } // for x
+
+ // Update the heightmap:
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int y = cChunkDef::Height - 1; y >= 0; y--)
+ {
+ if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR)
+ {
+ a_ChunkDesc.SetHeight(x, z, y);
+ break;
+ }
+ } // for y
+ } // for z
+ } // for x
+}
+
+
+
+
+
+void cStructGenTrees::GenerateSingleTree(
+ int a_ChunkX, int a_ChunkZ, int a_Seq,
+ cChunkDesc & a_ChunkDesc,
+ sSetBlockVector & a_OutsideLogs,
+ sSetBlockVector & a_OutsideOther
+)
+{
+ int x = (m_Noise.IntNoise3DInt(a_ChunkX + a_ChunkZ, a_ChunkZ, a_Seq) / 19) % cChunkDef::Width;
+ int z = (m_Noise.IntNoise3DInt(a_ChunkX - a_ChunkZ, a_Seq, a_ChunkZ) / 19) % cChunkDef::Width;
+
+ int Height = a_ChunkDesc.GetHeight(x, z);
+
+ if ((Height <= 0) || (Height > 240))
+ {
+ return;
+ }
+
+ // Check the block underneath the tree:
+ BLOCKTYPE TopBlock = a_ChunkDesc.GetBlockType(x, Height, z);
+ if ((TopBlock != E_BLOCK_DIRT) && (TopBlock != E_BLOCK_GRASS) && (TopBlock != E_BLOCK_FARMLAND))
+ {
+ return;
+ }
+
+ sSetBlockVector TreeLogs, TreeOther;
+ GetTreeImageByBiome(
+ a_ChunkX * cChunkDef::Width + x, Height + 1, a_ChunkZ * cChunkDef::Width + z,
+ m_Noise, a_Seq,
+ a_ChunkDesc.GetBiome(x, z),
+ TreeLogs, TreeOther
+ );
+
+ // 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))
+ {
+ // Outside the chunk
+ continue;
+ }
+
+ BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z);
+ switch (Block)
+ {
+ CASE_TREE_ALLOWED_BLOCKS:
+ {
+ break;
+ }
+ default:
+ {
+ // There's something in the way, abort this tree altogether
+ return;
+ }
+ }
+ }
+
+ ApplyTreeImage(a_ChunkX, a_ChunkZ, a_ChunkDesc, TreeOther, a_OutsideOther);
+ ApplyTreeImage(a_ChunkX, a_ChunkZ, a_ChunkDesc, TreeLogs, a_OutsideLogs);
+}
+
+
+
+
+
+void cStructGenTrees::ApplyTreeImage(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDesc & a_ChunkDesc,
+ const sSetBlockVector & a_Image,
+ sSetBlockVector & a_Overflow
+)
+{
+ // 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))
+ {
+ // Inside this chunk, integrate into a_ChunkDesc:
+ switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z))
+ {
+ case E_BLOCK_LEAVES:
+ {
+ if (itr->BlockType != E_BLOCK_LOG)
+ {
+ break;
+ }
+ // fallthrough:
+ }
+ CASE_TREE_OVERWRITTEN_BLOCKS:
+ {
+ a_ChunkDesc.SetBlockTypeMeta(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ break;
+ }
+
+ } // switch (GetBlock())
+ continue;
+ }
+
+ // Outside the chunk, push into a_Overflow.
+ // Don't check if already present there, by separating logs and others we don't need the checks anymore:
+ a_Overflow.push_back(*itr);
+ }
+}
+
+
+
+
+
+int cStructGenTrees::GetNumTrees(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BiomeMap & a_Biomes
+)
+{
+ int NumTrees = 0;
+ for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int Add = 0;
+ switch (cChunkDef::GetBiome(a_Biomes, x, z))
+ {
+ case biPlains: Add = 1; break;
+ case biExtremeHills: Add = 3; break;
+ case biForest: Add = 30; break;
+ case biTaiga: Add = 30; break;
+ case biSwampland: Add = 8; break;
+ case biIcePlains: Add = 1; break;
+ case biIceMountains: Add = 1; break;
+ case biMushroomIsland: Add = 3; break;
+ case biMushroomShore: Add = 3; break;
+ case biForestHills: Add = 20; break;
+ case biTaigaHills: Add = 20; break;
+ case biExtremeHillsEdge: Add = 5; break;
+ case biJungle: Add = 120; break;
+ case biJungleHills: Add = 90; break;
+ }
+ NumTrees += Add;
+ }
+ return NumTrees / 1024;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenOreNests:
+
+void cStructGenOreNests::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+ cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes();
+ GenerateOre(ChunkX, ChunkZ, E_BLOCK_COAL_ORE, MAX_HEIGHT_COAL, NUM_NESTS_COAL, NEST_SIZE_COAL, BlockTypes, 1);
+ GenerateOre(ChunkX, ChunkZ, E_BLOCK_IRON_ORE, MAX_HEIGHT_IRON, NUM_NESTS_IRON, NEST_SIZE_IRON, BlockTypes, 2);
+ GenerateOre(ChunkX, ChunkZ, E_BLOCK_REDSTONE_ORE, MAX_HEIGHT_REDSTONE, NUM_NESTS_REDSTONE, NEST_SIZE_REDSTONE, BlockTypes, 3);
+ GenerateOre(ChunkX, ChunkZ, E_BLOCK_GOLD_ORE, MAX_HEIGHT_GOLD, NUM_NESTS_GOLD, NEST_SIZE_GOLD, BlockTypes, 4);
+ GenerateOre(ChunkX, ChunkZ, E_BLOCK_DIAMOND_ORE, MAX_HEIGHT_DIAMOND, NUM_NESTS_DIAMOND, NEST_SIZE_DIAMOND, BlockTypes, 5);
+ GenerateOre(ChunkX, ChunkZ, E_BLOCK_LAPIS_ORE, MAX_HEIGHT_LAPIS, NUM_NESTS_LAPIS, NEST_SIZE_LAPIS, BlockTypes, 6);
+ GenerateOre(ChunkX, ChunkZ, E_BLOCK_DIRT, MAX_HEIGHT_DIRT, NUM_NESTS_DIRT, NEST_SIZE_DIRT, BlockTypes, 10);
+ GenerateOre(ChunkX, ChunkZ, E_BLOCK_GRAVEL, MAX_HEIGHT_GRAVEL, NUM_NESTS_GRAVEL, NEST_SIZE_GRAVEL, BlockTypes, 11);
+}
+
+
+
+
+
+void cStructGenOreNests::GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq)
+{
+ // This function generates several "nests" of ore, each nest consisting of number of ore blocks relatively adjacent to each other.
+ // It does so by making a random XYZ walk and adding ore along the way in cuboids of different (random) sizes
+ // Only stone gets replaced with ore, all other blocks stay (so the nest can actually be smaller than specified).
+
+ for (int i = 0; i < a_NumNests; i++)
+ {
+ int rnd = m_Noise.IntNoise3DInt(a_ChunkX + i, a_Seq, a_ChunkZ + 64 * i) / 8;
+ int BaseX = rnd % cChunkDef::Width;
+ rnd /= cChunkDef::Width;
+ int BaseZ = rnd % cChunkDef::Width;
+ rnd /= cChunkDef::Width;
+ int BaseY = rnd % a_MaxHeight;
+ rnd /= a_MaxHeight;
+ int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1/4 larger
+ int Num = 0;
+ while (Num < NestSize)
+ {
+ // Put a cuboid around [BaseX, BaseY, BaseZ]
+ int rnd = m_Noise.IntNoise3DInt(a_ChunkX + 64 * i, 2 * a_Seq + Num, a_ChunkZ + 32 * i) / 8;
+ int xsize = rnd % 2;
+ int ysize = (rnd / 4) % 2;
+ int zsize = (rnd / 16) % 2;
+ rnd >>= 8;
+ for (int x = xsize; x >= 0; --x)
+ {
+ int BlockX = BaseX + x;
+ if ((BlockX < 0) || (BlockX >= cChunkDef::Width))
+ {
+ Num++; // So that the cycle finishes even if the base coords wander away from the chunk
+ continue;
+ }
+ for (int y = ysize; y >= 0; --y)
+ {
+ int BlockY = BaseY + y;
+ if ((BlockY < 0) || (BlockY >= cChunkDef::Height))
+ {
+ Num++; // So that the cycle finishes even if the base coords wander away from the chunk
+ continue;
+ }
+ for (int z = zsize; z >= 0; --z)
+ {
+ int BlockZ = BaseZ + z;
+ if ((BlockZ < 0) || (BlockZ >= cChunkDef::Width))
+ {
+ Num++; // So that the cycle finishes even if the base coords wander away from the chunk
+ continue;
+ }
+
+ int Index = cChunkDef::MakeIndexNoCheck(BlockX, BlockY, BlockZ);
+ if (a_BlockTypes[Index] == E_BLOCK_STONE)
+ {
+ a_BlockTypes[Index] = a_OreType;
+ }
+ Num++;
+ } // for z
+ } // for y
+ } // for x
+
+ // Move the base to a neighbor voxel
+ switch (rnd % 4)
+ {
+ case 0: BaseX--; break;
+ case 1: BaseX++; break;
+ }
+ switch ((rnd >> 3) % 4)
+ {
+ case 0: BaseY--; break;
+ case 1: BaseY++; break;
+ }
+ switch ((rnd >> 6) % 4)
+ {
+ case 0: BaseZ--; break;
+ case 1: BaseZ++; break;
+ }
+ } // while (Num < NumBlocks)
+ } // for i - NumNests
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenLakes:
+
+void cStructGenLakes::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+
+ for (int z = -1; z < 2; z++) for (int x = -1; x < 2; x++)
+ {
+ if (((m_Noise.IntNoise2DInt(ChunkX + x, ChunkZ + z) / 17) % 100) > m_Probability)
+ {
+ continue;
+ }
+
+ cBlockArea Lake;
+ CreateLakeImage(ChunkX + x, ChunkZ + z, Lake);
+
+ int OfsX = Lake.GetOriginX() + x * cChunkDef::Width;
+ int OfsZ = Lake.GetOriginZ() + z * cChunkDef::Width;
+
+ // Merge the lake into the current data
+ a_ChunkDesc.WriteBlockArea(Lake, OfsX, Lake.GetOriginY(), OfsZ, cBlockArea::msLake);
+ } // for x, z - neighbor chunks
+}
+
+
+
+
+
+void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake)
+{
+ a_Lake.Create(16, 8, 16);
+ a_Lake.Fill(cBlockArea::baTypes, E_BLOCK_SPONGE); // Sponge is the NOP blocktype for lake merging strategy
+
+ // Find the minimum height in this chunk:
+ cChunkDef::HeightMap HeightMap;
+ m_HeiGen.GenHeightMap(a_ChunkX, a_ChunkZ, HeightMap);
+ HEIGHTTYPE MinHeight = HeightMap[0];
+ for (int i = 1; i < ARRAYCOUNT(HeightMap); i++)
+ {
+ if (HeightMap[i] < MinHeight)
+ {
+ MinHeight = HeightMap[i];
+ }
+ }
+
+ // Make a random position in the chunk by using a random 16 block XZ offset and random height up to chunk's max height minus 6
+ MinHeight = std::max(MinHeight - 6, 2);
+ int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 128, a_ChunkZ) / 11;
+ // Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range
+ int OffsetX = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8;
+ Rnd >>= 12;
+ // Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range
+ int OffsetZ = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8;
+ Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 512, a_ChunkZ) / 13;
+ // Random height [1 .. MinHeight] with preference to center heights
+ int HeightY = 1 + (((Rnd & 0x1ff) % MinHeight) + (((Rnd >> 9) & 0x1ff) % MinHeight)) / 2;
+
+ a_Lake.SetOrigin(OffsetX, HeightY, OffsetZ);
+
+ // Hollow out a few bubbles inside the blockarea:
+ int NumBubbles = 4 + ((Rnd >> 18) & 0x03); // 4 .. 7 bubbles
+ BLOCKTYPE * BlockTypes = a_Lake.GetBlockTypes();
+ for (int i = 0; i < NumBubbles; i++)
+ {
+ int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, i, a_ChunkZ) / 13;
+ const int BubbleR = 2 + (Rnd & 0x03); // 2 .. 5
+ const int Range = 16 - 2 * BubbleR;
+ const int BubbleX = BubbleR + (Rnd % Range);
+ Rnd >>= 4;
+ const int BubbleY = 4 + (Rnd & 0x01); // 4 .. 5
+ Rnd >>= 1;
+ const int BubbleZ = BubbleR + (Rnd % Range);
+ Rnd >>= 4;
+ const int HalfR = BubbleR / 2; // 1 .. 2
+ const int RSquared = BubbleR * BubbleR;
+ for (int y = -HalfR; y <= HalfR; y++)
+ {
+ // BubbleY + y is in the [0, 7] bounds
+ int DistY = 4 * y * y / 3;
+ int IdxY = (BubbleY + y) * 16 * 16;
+ for (int z = -BubbleR; z <= BubbleR; z++)
+ {
+ int DistYZ = DistY + z * z;
+ if (DistYZ >= RSquared)
+ {
+ continue;
+ }
+ int IdxYZ = BubbleX + IdxY + (BubbleZ + z) * 16;
+ for (int x = -BubbleR; x <= BubbleR; x++)
+ {
+ if (x * x + DistYZ < RSquared)
+ {
+ BlockTypes[x + IdxYZ] = E_BLOCK_AIR;
+ }
+ } // for x
+ } // for z
+ } // for y
+ } // for i - bubbles
+
+ // Turn air in the bottom half into liquid:
+ for (int y = 0; y < 4; y++)
+ {
+ for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++)
+ {
+ if (BlockTypes[x + z * 16 + y * 16 * 16] == E_BLOCK_AIR)
+ {
+ BlockTypes[x + z * 16 + y * 16 * 16] = m_Fluid;
+ }
+ } // for z, x
+ } // for y
+
+ // TODO: Turn sponge next to lava into stone
+
+ // a_Lake.SaveToSchematicFile(Printf("Lake_%d_%d.schematic", a_ChunkX, a_ChunkZ));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenDirectOverhangs:
+
+cStructGenDirectOverhangs::cStructGenDirectOverhangs(int a_Seed) :
+ m_Noise1(a_Seed),
+ m_Noise2(a_Seed + 1000)
+{
+}
+
+
+
+
+
+void cStructGenDirectOverhangs::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ // If there is no column of the wanted biome, bail out:
+ if (!HasWantedBiome(a_ChunkDesc))
+ {
+ return;
+ }
+
+ HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
+
+ const int SEGMENT_HEIGHT = 8;
+ const int INTERPOL_X = 16; // Must be a divisor of 16
+ const int INTERPOL_Z = 16; // Must be a divisor of 16
+ // Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately.
+ // Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them
+ // Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it.
+
+ int FloorBuf1[17 * 17];
+ int FloorBuf2[17 * 17];
+ int * FloorHi = FloorBuf1;
+ int * FloorLo = FloorBuf2;
+ int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ int BaseY = 63;
+
+ // Interpolate the lowest floor:
+ for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++)
+ {
+ FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
+ m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) *
+ m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) /
+ 256;
+ } // for x, z - FloorLo[]
+ LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z);
+
+ // Interpolate segments:
+ for (int Segment = BaseY; Segment < MaxHeight; Segment += SEGMENT_HEIGHT)
+ {
+ // First update the high floor:
+ for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++)
+ {
+ FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
+ m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) *
+ m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) /
+ 256;
+ } // for x, z - FloorLo[]
+ LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z);
+
+ // Interpolate between FloorLo and FloorHi:
+ for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++)
+ {
+ switch (a_ChunkDesc.GetBiome(x, z))
+ {
+ case biExtremeHills:
+ case biExtremeHillsEdge:
+ {
+ int Lo = FloorLo[x + 17 * z] / 256;
+ int Hi = FloorHi[x + 17 * z] / 256;
+ for (int y = 0; y < SEGMENT_HEIGHT; y++)
+ {
+ int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT;
+ if (Val < 0)
+ {
+ a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_AIR);
+ }
+ } // for y
+ break;
+ }
+ } // switch (biome)
+ } // for z, x
+
+ // Swap the floors:
+ std::swap(FloorLo, FloorHi);
+ }
+}
+
+
+
+
+
+bool cStructGenDirectOverhangs::HasWantedBiome(cChunkDesc & a_ChunkDesc) const
+{
+ cChunkDef::BiomeMap & Biomes = a_ChunkDesc.GetBiomeMap();
+ for (int i = 0; i < ARRAYCOUNT(Biomes); i++)
+ {
+ switch (Biomes[i])
+ {
+ case biExtremeHills:
+ case biExtremeHillsEdge:
+ {
+ return true;
+ }
+ }
+ } // for i
+ return false;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenDistortedMembraneOverhangs:
+
+cStructGenDistortedMembraneOverhangs::cStructGenDistortedMembraneOverhangs(int a_Seed) :
+ m_NoiseX(a_Seed + 1000),
+ m_NoiseY(a_Seed + 2000),
+ m_NoiseZ(a_Seed + 3000),
+ m_NoiseH(a_Seed + 4000)
+{
+}
+
+
+
+
+
+void cStructGenDistortedMembraneOverhangs::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ const NOISE_DATATYPE Frequency = (NOISE_DATATYPE)16;
+ const NOISE_DATATYPE Amount = (NOISE_DATATYPE)1;
+ for (int y = 50; y < 128; y++)
+ {
+ NOISE_DATATYPE NoiseY = (NOISE_DATATYPE)y / 32;
+ // TODO: proper water level - where to get?
+ BLOCKTYPE ReplacementBlock = (y > 62) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z)) / Frequency;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x)) / Frequency;
+ NOISE_DATATYPE DistortX = m_NoiseX.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount;
+ NOISE_DATATYPE DistortY = m_NoiseY.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount;
+ NOISE_DATATYPE DistortZ = m_NoiseZ.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount;
+ int MembraneHeight = 96 - (int)((DistortY + m_NoiseH.CubicNoise2D(NoiseX + DistortX, NoiseZ + DistortZ)) * 30);
+ if (MembraneHeight < y)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, ReplacementBlock);
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
diff --git a/src/Generating/StructGen.h b/src/Generating/StructGen.h
new file mode 100644
index 000000000..853748bb8
--- /dev/null
+++ b/src/Generating/StructGen.h
@@ -0,0 +1,165 @@
+
+// StructGen.h
+
+/* Interfaces to the various structure generators:
+ - cStructGenTrees
+ - cStructGenMarbleCaves
+ - cStructGenOres
+*/
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cStructGenTrees :
+ public cStructureGen
+{
+public:
+ cStructGenTrees(int a_Seed, cBiomeGen * a_BiomeGen, cTerrainHeightGen * a_HeightGen, cTerrainCompositionGen * a_CompositionGen) :
+ m_Seed(a_Seed),
+ m_Noise(a_Seed),
+ m_BiomeGen(a_BiomeGen),
+ m_HeightGen(a_HeightGen),
+ m_CompositionGen(a_CompositionGen)
+ {}
+
+protected:
+
+ int m_Seed;
+ cNoise m_Noise;
+ cBiomeGen * m_BiomeGen;
+ cTerrainHeightGen * m_HeightGen;
+ cTerrainCompositionGen * m_CompositionGen;
+
+ /** Generates and applies an image of a single tree.
+ Parts of the tree inside the chunk are applied to a_BlockX.
+ Parts of the tree outside the chunk are stored in a_OutsideX
+ */
+ void GenerateSingleTree(
+ int a_ChunkX, int a_ChunkZ, int a_Seq,
+ cChunkDesc & a_ChunkDesc,
+ sSetBlockVector & a_OutsideLogs,
+ sSetBlockVector & a_OutsideOther
+ ) ;
+
+ /// Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow
+ void ApplyTreeImage(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDesc & a_ChunkDesc,
+ const sSetBlockVector & a_Image,
+ sSetBlockVector & a_Overflow
+ );
+
+ int GetNumTrees(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BiomeMap & a_Biomes
+ );
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cStructGenOreNests :
+ public cStructureGen
+{
+public:
+ cStructGenOreNests(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {}
+
+protected:
+ cNoise m_Noise;
+ int m_Seed;
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+
+ void GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq);
+} ;
+
+
+
+
+
+class cStructGenLakes :
+ public cStructureGen
+{
+public:
+ cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainHeightGen & a_HeiGen, int a_Probability) :
+ m_Noise(a_Seed),
+ m_Seed(a_Seed),
+ m_Fluid(a_Fluid),
+ m_HeiGen(a_HeiGen),
+ m_Probability(a_Probability)
+ {
+ }
+
+protected:
+ cNoise m_Noise;
+ int m_Seed;
+ BLOCKTYPE m_Fluid;
+ cTerrainHeightGen & m_HeiGen;
+ int m_Probability; ///< Chance, 0 .. 100, of a chunk having the lake
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+
+ /// Creates a lake image for the specified chunk into a_Lake
+ void CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake);
+} ;
+
+
+
+
+
+
+class cStructGenDirectOverhangs :
+ public cStructureGen
+{
+public:
+ cStructGenDirectOverhangs(int a_Seed);
+
+protected:
+ cNoise m_Noise1;
+ cNoise m_Noise2;
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+
+ bool HasWantedBiome(cChunkDesc & a_ChunkDesc) const;
+} ;
+
+
+
+
+
+class cStructGenDistortedMembraneOverhangs :
+ public cStructureGen
+{
+public:
+ cStructGenDistortedMembraneOverhangs(int a_Seed);
+
+protected:
+ cNoise m_NoiseX;
+ cNoise m_NoiseY;
+ cNoise m_NoiseZ;
+ cNoise m_NoiseH;
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp
new file mode 100644
index 000000000..7ca30c60f
--- /dev/null
+++ b/src/Generating/Trees.cpp
@@ -0,0 +1,684 @@
+
+// Trees.cpp
+
+// Implements helper functions used for generating trees
+
+#include "Globals.h"
+#include "Trees.h"
+#include "../BlockID.h"
+
+
+
+
+// DEBUG:
+int gTotalLargeJungleTrees = 0;
+int gOversizeLargeJungleTrees = 0;
+
+
+
+
+
+typedef struct
+{
+ int x, z;
+} sCoords;
+
+typedef struct
+{
+ int x, z;
+ NIBBLETYPE Meta;
+} sMetaCoords;
+
+static const sCoords Corners[] =
+{
+ {-1, -1},
+ {-1, 1},
+ {1, -1},
+ {1, 1},
+} ;
+
+// BigO = a big ring of blocks, used for generating horz slices of treetops, the number indicates the radius
+
+static const sCoords BigO1[] =
+{
+ {0, -1},
+ {-1, 0}, {1, 0},
+ {0, 1},
+} ;
+
+static const sCoords BigO2[] =
+{
+ {-1, -2}, {0, -2}, {1, -2},
+ {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1},
+ {-2, 0}, {-1, 0}, {1, 0}, {2, 0},
+ {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1},
+ {-1, 2}, {0, 2}, {1, 2},
+} ;
+
+static const sCoords BigO3[] =
+{
+ {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3},
+ {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2},
+ {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1},
+ {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0},
+ {-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1},
+ {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2},
+ {-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3},
+} ;
+
+static const sCoords BigO4[] = // Part of Big Jungle tree
+{
+ {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4},
+ {-3, -3}, {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, {3, -3},
+ {-4, -2}, {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, {4, -2},
+ {-4, -1}, {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, {4, -1},
+ {-4, 0}, {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0},
+ {-4, 1}, {-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1},
+ {-4, 2}, {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2},
+ {-3, 3}, {-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3}, {3, 3},
+ {-2, 4}, {-1, 4}, {0, 4}, {1, 4}, {2, 4},
+} ;
+
+
+
+
+
+typedef struct
+{
+ const sCoords * Coords;
+ size_t Count;
+} sCoordsArr;
+
+static const sCoordsArr BigOs[] =
+{
+ {BigO1, ARRAYCOUNT(BigO1)},
+ {BigO2, ARRAYCOUNT(BigO2)},
+ {BigO3, ARRAYCOUNT(BigO3)},
+ {BigO4, ARRAYCOUNT(BigO4)},
+} ;
+
+
+
+
+
+/// Pushes a specified layer of blocks of the same type around (x, h, z) into a_Blocks
+inline void PushCoordBlocks(int a_BlockX, int a_Height, int a_BlockZ, sSetBlockVector & a_Blocks, const sCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta)
+{
+ for (size_t i = 0; i < a_NumCoords; i++)
+ {
+ a_Blocks.push_back(sSetBlock(a_BlockX + a_Coords[i].x, a_Height, a_BlockZ + a_Coords[i].z, a_BlockType, a_Meta));
+ }
+}
+
+
+
+
+inline void PushCornerBlocks(int a_BlockX, int a_Height, int a_BlockZ, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, int a_CornersDist, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta)
+{
+ for (size_t i = 0; i < ARRAYCOUNT(Corners); i++)
+ {
+ int x = a_BlockX + Corners[i].x;
+ int z = a_BlockZ + Corners[i].z;
+ if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height, z + 64 * a_Seq) <= a_Chance)
+ {
+ a_Blocks.push_back(sSetBlock(x, a_Height, z, a_BlockType, a_Meta));
+ }
+ } // for i - Corners[]
+}
+
+
+
+
+
+inline void PushSomeColumns(int a_BlockX, int a_Height, int a_BlockZ, int a_ColumnHeight, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, const sMetaCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType)
+{
+ for (size_t i = 0; i < a_NumCoords; i++)
+ {
+ int x = a_BlockX + a_Coords[i].x;
+ int z = a_BlockZ + a_Coords[i].z;
+ if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height + i, z + 64 * a_Seq) <= a_Chance)
+ {
+ for (int j = 0; j < a_ColumnHeight; j++)
+ {
+ a_Blocks.push_back(sSetBlock(x, a_Height - j, z, a_BlockType, a_Coords[i].Meta));
+ }
+ }
+ } // for i - a_Coords[]
+}
+
+
+
+
+
+void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ switch (a_Biome)
+ {
+ case biPlains:
+ case biExtremeHills:
+ case biExtremeHillsEdge:
+ case biForest:
+ case biMushroomIsland:
+ case biMushroomShore:
+ case biForestHills:
+ {
+ // Apple or birch trees:
+ if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x5fffffff)
+ {
+ GetAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+ else
+ {
+ GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+ break;
+ }
+
+ case biTaiga:
+ case biIcePlains:
+ case biIceMountains:
+ case biTaigaHills:
+ {
+ // Conifers
+ GetConiferTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ break;
+ }
+
+ case biSwampland:
+ {
+ // Swamp trees:
+ GetSwampTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ break;
+ }
+
+ case biJungle:
+ case biJungleHills:
+ {
+ // Apple bushes, large jungle trees, small jungle trees
+ if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x6fffffff)
+ {
+ GetAppleBushImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+ else
+ {
+ GetJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+ }
+ }
+}
+
+
+
+
+
+void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000)
+ {
+ GetSmallAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+ else
+ {
+ GetLargeAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+}
+
+
+
+
+
+void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ /* Small apple tree has:
+ - a top plus (no log)
+ - optional BigO1 + random corners (log)
+ - 2 layers of BigO2 + random corners (log)
+ - 1 to 3 blocks of trunk
+ */
+
+ int Random = a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) >> 3;
+
+ int Heights[] = {1, 2, 2, 3} ;
+ int Height = 1 + Heights[Random & 3];
+ Random >>= 2;
+
+ // Pre-alloc so that we don't realloc too often later:
+ a_LogBlocks.reserve(Height + 5);
+ a_OtherBlocks.reserve(ARRAYCOUNT(BigO2) * 2 + ARRAYCOUNT(BigO1) + ARRAYCOUNT(Corners) * 3 + 3 + 5);
+
+ // Trunk:
+ for (int i = 0; i < Height; i++)
+ {
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
+ }
+ int Hei = a_BlockY + Height;
+
+ // 2 BigO2 + corners layers:
+ for (int i = 0; i < 2; i++)
+ {
+ PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x5000000 - i * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
+ Hei++;
+ } // for i - 2*
+
+ // Optional BigO1 + corners layer:
+ if ((Random & 1) == 0)
+ {
+ PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x6000000, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
+ Hei++;
+ }
+
+ // Top plus:
+ PushCoordBlocks(a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+}
+
+
+
+
+
+void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ // TODO
+}
+
+
+
+
+
+void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ int Height = 5 + (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) % 3);
+
+ // Prealloc, so that we don't realloc too often later:
+ a_LogBlocks.reserve(Height);
+ a_OtherBlocks.reserve(80);
+
+ // The entire trunk, out of logs:
+ for (int i = Height - 1; i >= 0; --i)
+ {
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_BIRCH));
+ }
+ int h = a_BlockY + Height;
+
+ // Top layer - just the Plus:
+ PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH)); // There's no log at this layer
+ h--;
+
+ // Second layer - log, Plus and maybe Corners:
+ PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ h--;
+
+ // Third and fourth layers - BigO2 and maybe 2*Corners:
+ for (int Row = 0; Row < 2; Row++)
+ {
+ PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x3fffffff + Row * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ h--;
+ } // for Row - 2*
+}
+
+
+
+
+
+void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ // Half chance for a spruce, half for a pine:
+ if (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) < 0x40000000)
+ {
+ GetSpruceTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+ else
+ {
+ GetPineTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+}
+
+
+
+
+
+void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ // Spruces have a top section with layer sizes of (0, 1, 0) or only (1, 0),
+ // then 1 - 3 sections of ascending sizes (1, 2) [most often], (1, 3) or (1, 2, 3)
+ // and an optional bottom section of size 1, followed by 1 - 3 clear trunk blocks
+
+ // We'll use bits from this number as partial random numbers; but the noise function has mod8 irregularities
+ // (each of the mod8 remainders has a very different chance of occurrence) - that's why we divide by 8
+ int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) / 8;
+
+ static const int sHeights[] = {1, 2, 2, 3};
+ int Height = sHeights[MyRandom & 3];
+ MyRandom >>= 2;
+
+ // Prealloc, so that we don't realloc too often later:
+ a_LogBlocks.reserve(Height);
+ a_OtherBlocks.reserve(180);
+
+ // Clear trunk blocks:
+ for (int i = 0; i < Height; i++)
+ {
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ }
+ Height += a_BlockY;
+
+ // Optional size-1 bottom leaves layer:
+ if ((MyRandom & 1) == 0)
+ {
+ PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ Height++;
+ }
+ MyRandom >>= 1;
+
+ // 1 to 3 sections of leaves layers:
+ static const int sNumSections[] = {1, 2, 2, 3};
+ int NumSections = sNumSections[MyRandom & 3];
+ MyRandom >>= 2;
+ for (int i = 0; i < NumSections; i++)
+ {
+ switch (MyRandom & 3) // SectionType; (1, 2) twice as often as the other two
+ {
+ case 0:
+ case 1:
+ {
+ PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ Height += 2;
+ break;
+ }
+ case 2:
+ {
+ PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ Height += 2;
+ break;
+ }
+ case 3:
+ {
+ PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ PushCoordBlocks(a_BlockX, Height + 2, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ Height += 3;
+ break;
+ }
+ } // switch (SectionType)
+ MyRandom >>= 2;
+ } // for i - Sections
+
+ if ((MyRandom & 1) == 0)
+ {
+ // (0, 1, 0) top:
+ a_LogBlocks.push_back (sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+ }
+ else
+ {
+ // (1, 0) top:
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+ PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+ }
+}
+
+
+
+
+
+void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ // Tall, little leaves on top. The top leaves are arranged in a shape of two cones joined by their bases.
+ // There can be one or two layers representing the cone bases (SameSizeMax)
+
+ int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8;
+ int TrunkHeight = 8 + (MyRandom % 3);
+ int SameSizeMax = ((MyRandom & 8) == 0) ? 1 : 0;
+ MyRandom >>= 3;
+ int NumLeavesLayers = 2 + (MyRandom % 3); // Number of layers that have leaves in them
+ if (NumLeavesLayers == 2)
+ {
+ SameSizeMax = 0;
+ }
+
+ // Pre-allocate the vector:
+ a_LogBlocks.reserve(TrunkHeight);
+ a_OtherBlocks.reserve(NumLeavesLayers * 25);
+
+ // The entire trunk, out of logs:
+ for (int i = TrunkHeight; i >= 0; --i)
+ {
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ }
+ int h = a_BlockY + TrunkHeight + 2;
+
+ // Top layer - just a single leaves block:
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+ h--;
+
+ // One more layer is above the trunk, push the central leaves:
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+
+ // Layers expanding in size, then collapsing again:
+ // LOGD("Generating %d layers of pine leaves, SameSizeMax = %d", NumLeavesLayers, SameSizeMax);
+ for (int i = 0; i < NumLeavesLayers; ++i)
+ {
+ int LayerSize = std::min(i, NumLeavesLayers - i + SameSizeMax - 1);
+ // LOGD("LayerSize %d: %d", i, LayerSize);
+ if (LayerSize < 0)
+ {
+ break;
+ }
+ ASSERT(LayerSize < ARRAYCOUNT(BigOs));
+ PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigOs[LayerSize].Coords, BigOs[LayerSize].Count, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ h--;
+ }
+}
+
+
+
+
+
+void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ // Vines are around the BigO3, but not in the corners; need proper meta for direction
+ static const sMetaCoords Vines[] =
+ {
+ {-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face
+ {-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face
+ {4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face
+ {-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, {-4, 2, 8}, // West face
+ } ;
+
+ int Height = 3 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8) % 3;
+
+ a_LogBlocks.reserve(Height);
+ a_OtherBlocks.reserve(2 * ARRAYCOUNT(BigO2) + 2 * ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 20);
+
+ for (int i = 0; i < Height; i++)
+ {
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
+ }
+ int hei = a_BlockY + Height - 2;
+
+ // Put vines around the lowermost leaves layer:
+ PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES);
+
+ // The lower two leaves layers are BigO3 with log in the middle and possibly corners:
+ for (int i = 0; i < 2; i++)
+ {
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ hei++;
+ } // for i - 2*
+
+ // The upper two leaves layers are BigO2 with leaves in the middle and possibly corners:
+ for (int i = 0; i < 2; i++)
+ {
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+ hei++;
+ } // for i - 2*
+}
+
+
+
+
+
+void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ a_OtherBlocks.reserve(3 + ARRAYCOUNT(BigO2) + ARRAYCOUNT(BigO1));
+
+ int hei = a_BlockY;
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE));
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ hei++;
+
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ hei++;
+
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+}
+
+
+
+
+
+void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000)
+ {
+ GetSmallJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+ else
+ {
+ GetLargeJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+}
+
+
+
+
+
+void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ // TODO: Generate proper jungle trees with branches
+
+ // Vines are around the BigO4, but not in the corners; need proper meta for direction
+ static const sMetaCoords Vines[] =
+ {
+ {-2, -5, 1}, {-1, -5, 1}, {0, -5, 1}, {1, -5, 1}, {2, -5, 1}, // North face
+ {-2, 5, 4}, {-1, 5, 4}, {0, 5, 4}, {1, 5, 4}, {2, 5, 4}, // South face
+ {5, -2, 2}, {5, -1, 2}, {5, 0, 2}, {5, 1, 2}, {5, 2, 2}, // East face
+ {-5, -2, 8}, {-5, -1, 8}, {-5, 0, 8}, {-5, 1, 8}, {-5, 2, 8}, // West face
+ // TODO: vines around the trunk, proper metas and height
+ } ;
+
+ int Height = 24 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 11) % 24;
+
+ a_LogBlocks.reserve(Height * 4);
+ a_OtherBlocks.reserve(2 * ARRAYCOUNT(BigO4) + ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 50);
+
+ for (int i = 0; i < Height; i++)
+ {
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE));
+ a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE));
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ + 1, E_BLOCK_LOG, E_META_LOG_JUNGLE));
+ a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ + 1, E_BLOCK_LOG, E_META_LOG_JUNGLE));
+ }
+ int hei = a_BlockY + Height - 2;
+
+ // Put vines around the lowermost leaves layer:
+ PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES);
+
+ // The lower two leaves layers are BigO4 with log in the middle and possibly corners:
+ for (int i = 0; i < 2; i++)
+ {
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO4, ARRAYCOUNT(BigO4), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
+ PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
+ hei++;
+ } // for i - 2*
+
+ // The top leaves layer is a BigO3 with leaves in the middle and possibly corners:
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
+ PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE));
+}
+
+
+
+
+
+void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ // Vines are around the BigO3, but not in the corners; need proper meta for direction
+ static const sMetaCoords Vines[] =
+ {
+ {-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face
+ {-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face
+ {4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face
+ {-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, // West face
+ // TODO: proper metas and height: {0, 1, 1}, {0, -1, 4}, {-1, 0, 2}, {1, 1, 8}, // Around the tunk
+ } ;
+
+ int Height = 7 + (a_Noise.IntNoise3DInt(a_BlockX + 5 * a_Seq, a_BlockY, a_BlockZ + 5 * a_Seq) / 5) % 3;
+
+ a_LogBlocks.reserve(Height);
+ a_OtherBlocks.reserve(
+ 2 * ARRAYCOUNT(BigO3) + // O3 layer, 2x
+ 2 * ARRAYCOUNT(BigO2) + // O2 layer, 2x
+ ARRAYCOUNT(BigO1) + 1 + // Plus on the top
+ Height * ARRAYCOUNT(Vines) + // Vines
+ 50 // some safety
+ );
+
+ for (int i = 0; i < Height; i++)
+ {
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE));
+ }
+ int hei = a_BlockY + Height - 3;
+
+ // Put vines around the lowermost leaves layer:
+ PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES);
+
+ // The lower two leaves layers are BigO3 with log in the middle and possibly corners:
+ for (int i = 0; i < 2; i++)
+ {
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
+ PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
+ hei++;
+ } // for i - 2*
+
+ // Two layers of BigO2 leaves, possibly with corners:
+ for (int i = 0; i < 1; i++)
+ {
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
+ PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
+ hei++;
+ } // for i - 2*
+
+ // Top plus, all leaves:
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE));
+}
+
+
+
+
diff --git a/src/Generating/Trees.h b/src/Generating/Trees.h
new file mode 100644
index 000000000..f5148ad6f
--- /dev/null
+++ b/src/Generating/Trees.h
@@ -0,0 +1,93 @@
+
+// Trees.h
+
+// Interfaces to helper functions used for generating trees
+
+/*
+Note that all of these functions must generate the same tree image for the same input (x, y, z, seq)
+ - cStructGenTrees depends on this
+To generate a random image for the (x, y, z) coords, pass an arbitrary value as (seq).
+Each function returns two arrays of blocks, "logs" and "other". The point is that logs are of higher priority,
+logs can overwrite others(leaves), but others shouldn't overwrite logs. This is an optimization for the generator.
+*/
+
+
+
+
+
+#pragma once
+
+#include "../ChunkDef.h"
+#include "../Noise.h"
+
+
+
+
+
+// Blocks that don't block tree growth:
+#define CASE_TREE_ALLOWED_BLOCKS \
+ case E_BLOCK_AIR: \
+ case E_BLOCK_LEAVES: \
+ case E_BLOCK_SNOW: \
+ case E_BLOCK_TALL_GRASS: \
+ case E_BLOCK_DEAD_BUSH: \
+ case E_BLOCK_SAPLING: \
+ case E_BLOCK_VINES
+
+// Blocks that a tree may overwrite when growing:
+#define CASE_TREE_OVERWRITTEN_BLOCKS \
+ case E_BLOCK_AIR: \
+ /* case E_BLOCK_LEAVES: LEAVES are a special case, they can be overwritten only by log. Handled in cChunkMap::ReplaceTreeBlocks(). */ \
+ case E_BLOCK_SNOW: \
+ case E_BLOCK_TALL_GRASS: \
+ case E_BLOCK_DEAD_BUSH: \
+ case E_BLOCK_SAPLING: \
+ case E_BLOCK_VINES
+
+
+
+
+
+/// Generates an image of a tree at the specified coords (lowest trunk block) in the specified biome
+void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a random apple tree
+void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a small (nonbranching) apple tree
+void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a large (branching) apple tree
+void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a random birch tree
+void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a random conifer tree
+void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a random spruce (short conifer, two layers of leaves)
+void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a random pine (tall conifer, little leaves at top)
+void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a random swampland tree
+void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a random apple bush (for jungles)
+void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a random jungle tree
+void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a large jungle tree (2x2 trunk)
+void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+/// Generates an image of a small jungle tree (1x1 trunk)
+void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+
+
+
+
+
diff --git a/src/Globals.cpp b/src/Globals.cpp
new file mode 100644
index 000000000..13c6ae709
--- /dev/null
+++ b/src/Globals.cpp
@@ -0,0 +1,10 @@
+
+// Globals.cpp
+
+// This file is used for precompiled header generation in MSVC environments
+
+#include "Globals.h"
+
+
+
+
diff --git a/src/Globals.h b/src/Globals.h
new file mode 100644
index 000000000..ef79e4cf1
--- /dev/null
+++ b/src/Globals.h
@@ -0,0 +1,227 @@
+
+// Globals.h
+
+// This file gets included from every module in the project, so that global symbols may be introduced easily
+// Also used for precompiled header generation in MSVC environments
+
+
+
+
+
+// Compiler-dependent stuff:
+#if defined(_MSC_VER)
+ // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether
+ #pragma warning(disable:4481)
+
+ // Disable some warnings that we don't care about:
+ #pragma warning(disable:4100)
+
+ #define OBSOLETE __declspec(deprecated)
+
+ // No alignment needed in MSVC
+ #define ALIGN_8
+ #define ALIGN_16
+
+#elif defined(__GNUC__)
+
+ // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)?
+ #define abstract
+
+ // TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class)
+ #define override
+
+ #define OBSOLETE __attribute__((deprecated))
+
+ #define ALIGN_8 __attribute__((aligned(8)))
+ #define ALIGN_16 __attribute__((aligned(16)))
+
+ // Some portability macros :)
+ #define stricmp strcasecmp
+
+#else
+
+ #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler"
+
+ /*
+ // Copy and uncomment this into another #elif section based on your compiler identification
+
+ // Explicitly mark classes as abstract (no instances can be created)
+ #define abstract
+
+ // Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class)
+ #define override
+
+ // Mark functions as obsolete, so that their usage results in a compile-time warning
+ #define OBSOLETE
+
+ // Mark types / variables for alignment. Do the platforms need it?
+ #define ALIGN_8
+ #define ALIGN_16
+ */
+
+#endif
+
+
+
+
+
+// Integral types with predefined sizes:
+typedef long long Int64;
+typedef int Int32;
+typedef short Int16;
+
+typedef unsigned long long UInt64;
+typedef unsigned int UInt32;
+typedef unsigned short UInt16;
+
+
+
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for any class that shouldn't allow copying itself
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName &); \
+ void operator=(const TypeName &)
+
+// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc
+#define UNUSED(X) (void)(X)
+
+
+
+
+// OS-dependent stuff:
+#ifdef _WIN32
+ #define WIN32_LEAN_AND_MEAN
+
+ #define _WIN32_WINNT 0x501 // We want to target WinXP and higher
+
+ #include <Windows.h>
+ #include <winsock2.h>
+ #include <Ws2tcpip.h> // IPv6 stuff
+
+ // Windows SDK defines min and max macros, messing up with our std::min and std::max usage
+ #undef min
+ #undef max
+
+ // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant
+ #ifdef GetFreeSpace
+ #undef GetFreeSpace
+ #endif // GetFreeSpace
+#else
+ #include <sys/types.h>
+ #include <sys/time.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <time.h>
+ #include <dirent.h>
+ #include <errno.h>
+ #include <iostream>
+
+ #include <cstdio>
+ #include <cstring>
+ #include <pthread.h>
+ #include <semaphore.h>
+ #include <errno.h>
+ #include <fcntl.h>
+#if !defined(ANDROID_NDK)
+ #include <tr1/memory>
+#endif
+#endif
+
+#if defined(ANDROID_NDK)
+ #define FILE_IO_PREFIX "/sdcard/mcserver/"
+#else
+ #define FILE_IO_PREFIX ""
+#endif
+
+
+
+
+
+// CRT stuff:
+#include <sys/stat.h>
+#include <assert.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdarg.h>
+
+
+
+
+
+// STL stuff:
+#include <vector>
+#include <list>
+#include <deque>
+#include <string>
+#include <map>
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <queue>
+
+
+
+
+
+// Common headers (part 1, without macros):
+#include "StringUtils.h"
+#include "OSSupport/Sleep.h"
+#include "OSSupport/CriticalSection.h"
+#include "OSSupport/Semaphore.h"
+#include "OSSupport/Event.h"
+#include "OSSupport/Thread.h"
+#include "OSSupport/File.h"
+#include "MCLogger.h"
+
+
+
+
+
+// Common definitions:
+
+/// Evaluates to the number of elements in an array (compile-time!)
+#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X)))
+
+/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)" )
+#define KiB * 1024
+#define MiB * 1024 * 1024
+
+/// Faster than (int)floorf((float)x / (float)div)
+#define FAST_FLOOR_DIV( x, div ) (((x) - (((x) < 0) ? ((div) - 1) : 0)) / (div))
+
+// Own version of assert() that writes failed assertions to the log for review
+#ifdef _DEBUG
+ #define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) )
+#else
+ #define ASSERT(x) ((void)0)
+#endif
+
+// Pretty much the same as ASSERT() but stays in Release builds
+#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) )
+
+
+
+
+
+/// A generic interface used mainly in ForEach() functions
+template <typename Type> class cItemCallback
+{
+public:
+ /// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating
+ virtual bool Item(Type * a_Type) = 0;
+} ;
+
+
+
+
+
+// Common headers (part 2, with macros):
+#include "ChunkDef.h"
+#include "BlockID.h"
+
+
+
+
diff --git a/src/Group.cpp b/src/Group.cpp
new file mode 100644
index 000000000..448d29d87
--- /dev/null
+++ b/src/Group.cpp
@@ -0,0 +1,37 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Group.h"
+
+void cGroup::AddCommand( std::string a_Command )
+{
+ m_Commands[ a_Command ] = true;
+}
+
+void cGroup::AddPermission( std::string a_Permission )
+{
+ m_Permissions[ a_Permission ] = true;
+}
+
+bool cGroup::HasCommand( std::string a_Command )
+{
+ if( m_Commands.find("*") != m_Commands.end() ) return true;
+
+ CommandMap::iterator itr = m_Commands.find( a_Command );
+ if( itr != m_Commands.end() )
+ {
+ if( itr->second ) return true;
+ }
+
+ for( GroupList::iterator itr = m_Inherits.begin(); itr != m_Inherits.end(); ++itr )
+ {
+ if( (*itr)->HasCommand( a_Command ) ) return true;
+ }
+ return false;
+}
+
+void cGroup::InheritFrom( cGroup* a_Group )
+{
+ m_Inherits.remove( a_Group );
+ m_Inherits.push_back( a_Group );
+} \ No newline at end of file
diff --git a/src/Group.h b/src/Group.h
new file mode 100644
index 000000000..65ee1a60a
--- /dev/null
+++ b/src/Group.h
@@ -0,0 +1,40 @@
+
+#pragma once
+
+
+
+
+
+class cGroup // tolua_export
+{ // tolua_export
+public: // tolua_export
+ cGroup() {}
+ ~cGroup() {}
+
+ void SetName( std::string a_Name ) { m_Name = a_Name; } // tolua_export
+ const std::string & GetName() const { return m_Name; } // tolua_export
+ void SetColor( std::string a_Color ) { m_Color = a_Color; } // tolua_export
+ void AddCommand( std::string a_Command ); // tolua_export
+ void AddPermission( std::string a_Permission ); // tolua_export
+ void InheritFrom( cGroup* a_Group ); // tolua_export
+
+ bool HasCommand( std::string a_Command ); // tolua_export
+
+ typedef std::map< std::string, bool > PermissionMap;
+ const PermissionMap & GetPermissions() const { return m_Permissions; }
+
+ typedef std::map< std::string, bool > CommandMap;
+ const CommandMap & GetCommands() const { return m_Commands; }
+
+ const AString & GetColor() const { return m_Color; } // tolua_export
+
+ typedef std::list< cGroup* > GroupList;
+ const GroupList & GetInherits() const { return m_Inherits; }
+private:
+ std::string m_Name;
+ std::string m_Color;
+
+ PermissionMap m_Permissions;
+ CommandMap m_Commands;
+ GroupList m_Inherits;
+};// tolua_export
diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp
new file mode 100644
index 000000000..df609f05b
--- /dev/null
+++ b/src/GroupManager.cpp
@@ -0,0 +1,122 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "GroupManager.h"
+#include "Group.h"
+#include "inifile/iniFile.h"
+#include "ChatColor.h"
+#include "Root.h"
+
+
+
+
+
+typedef std::map< AString, cGroup* > GroupMap;
+
+
+
+
+
+struct cGroupManager::sGroupManagerState
+{
+ GroupMap Groups;
+};
+
+
+
+
+
+cGroupManager::~cGroupManager()
+{
+ for( GroupMap::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr )
+ {
+ delete itr->second;
+ }
+ m_pState->Groups.clear();
+
+ delete m_pState;
+}
+
+
+
+
+
+cGroupManager::cGroupManager()
+ : m_pState( new sGroupManagerState )
+{
+ LOGD("-- Loading Groups --");
+ cIniFile IniFile;
+ if (!IniFile.ReadFile("groups.ini"))
+ {
+ LOGWARNING("groups.ini inaccessible, no groups are defined");
+ return;
+ }
+
+ unsigned int NumKeys = IniFile.GetNumKeys();
+ for( unsigned int i = 0; i < NumKeys; i++ )
+ {
+ std::string KeyName = IniFile.GetKeyName( i );
+ cGroup* Group = GetGroup( KeyName.c_str() );
+
+ LOGD("Loading group: %s", KeyName.c_str() );
+
+ Group->SetName( KeyName );
+ char Color = IniFile.GetValue( KeyName, "Color", "-" )[0];
+ if( Color != '-' )
+ Group->SetColor( cChatColor::MakeColor(Color) );
+ else
+ Group->SetColor( cChatColor::White );
+
+ std::string Commands = IniFile.GetValue( KeyName, "Commands", "" );
+ if( Commands.size() > 0 )
+ {
+ AStringVector Split = StringSplit( Commands, "," );
+ for( unsigned int i = 0; i < Split.size(); i++)
+ {
+ Group->AddCommand( Split[i] );
+ }
+ }
+
+ std::string Permissions = IniFile.GetValue( KeyName, "Permissions", "" );
+ if( Permissions.size() > 0 )
+ {
+ AStringVector Split = StringSplit( Permissions, "," );
+ for( unsigned int i = 0; i < Split.size(); i++)
+ {
+ Group->AddPermission( Split[i] );
+ }
+ }
+
+ std::string Groups = IniFile.GetValue( KeyName, "Inherits", "" );
+ if( Groups.size() > 0 )
+ {
+ AStringVector Split = StringSplit( Groups, "," );
+ for( unsigned int i = 0; i < Split.size(); i++)
+ {
+ Group->InheritFrom( GetGroup( Split[i].c_str() ) );
+ }
+ }
+ }
+ LOGD("-- Groups Successfully Loaded --");
+}
+
+
+
+
+
+cGroup* cGroupManager::GetGroup( const AString & a_Name )
+{
+ GroupMap::iterator itr = m_pState->Groups.find( a_Name );
+ if( itr != m_pState->Groups.end() )
+ {
+ return itr->second;
+ }
+
+ cGroup* Group = new cGroup();
+ m_pState->Groups[a_Name] = Group;
+
+ return Group;
+}
+
+
+
+
diff --git a/src/GroupManager.h b/src/GroupManager.h
new file mode 100644
index 000000000..d911f976c
--- /dev/null
+++ b/src/GroupManager.h
@@ -0,0 +1,30 @@
+
+#pragma once
+
+
+
+
+
+class cGroup;
+
+
+
+
+
+class cGroupManager
+{
+public:
+ cGroup * GetGroup(const AString & a_Name);
+
+private:
+ friend class cRoot;
+ cGroupManager();
+ ~cGroupManager();
+
+ struct sGroupManagerState;
+ sGroupManagerState * m_pState;
+} ;
+
+
+
+
diff --git a/src/HTTPServer/EnvelopeParser.cpp b/src/HTTPServer/EnvelopeParser.cpp
new file mode 100644
index 000000000..8dbe05f14
--- /dev/null
+++ b/src/HTTPServer/EnvelopeParser.cpp
@@ -0,0 +1,132 @@
+
+// EnvelopeParser.cpp
+
+// Implements the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME
+
+#include "Globals.h"
+#include "EnvelopeParser.h"
+
+
+
+
+
+cEnvelopeParser::cEnvelopeParser(cCallbacks & a_Callbacks) :
+ m_Callbacks(a_Callbacks),
+ m_IsInHeaders(true)
+{
+}
+
+
+
+
+
+int cEnvelopeParser::Parse(const char * a_Data, int a_Size)
+{
+ if (!m_IsInHeaders)
+ {
+ return 0;
+ }
+
+ // Start searching 1 char from the end of the already received data, if available:
+ size_t SearchStart = m_IncomingData.size();
+ SearchStart = (SearchStart > 1) ? SearchStart - 1 : 0;
+
+ m_IncomingData.append(a_Data, a_Size);
+
+ size_t idxCRLF = m_IncomingData.find("\r\n", SearchStart);
+ if (idxCRLF == AString::npos)
+ {
+ // Not a complete line yet, all input consumed:
+ return a_Size;
+ }
+
+ // Parse as many lines as found:
+ size_t Last = 0;
+ do
+ {
+ if (idxCRLF == Last)
+ {
+ // This was the last line of the data. Finish whatever value has been cached and return:
+ NotifyLast();
+ m_IsInHeaders = false;
+ return a_Size - (m_IncomingData.size() - idxCRLF) + 2;
+ }
+ if (!ParseLine(m_IncomingData.c_str() + Last, idxCRLF - Last))
+ {
+ // An error has occurred
+ m_IsInHeaders = false;
+ return -1;
+ }
+ Last = idxCRLF + 2;
+ idxCRLF = m_IncomingData.find("\r\n", idxCRLF + 2);
+ } while (idxCRLF != AString::npos);
+ m_IncomingData.erase(0, Last);
+
+ // Parsed all lines and still expecting more
+ return a_Size;
+}
+
+
+
+
+
+void cEnvelopeParser::Reset(void)
+{
+ m_IsInHeaders = true;
+ m_IncomingData.clear();
+ m_LastKey.clear();
+ m_LastValue.clear();
+}
+
+
+
+
+
+void cEnvelopeParser::NotifyLast(void)
+{
+ if (!m_LastKey.empty())
+ {
+ m_Callbacks.OnHeaderLine(m_LastKey, m_LastValue);
+ m_LastKey.clear();
+ }
+ m_LastValue.clear();
+}
+
+
+
+
+
+bool cEnvelopeParser::ParseLine(const char * a_Data, size_t a_Size)
+{
+ ASSERT(a_Size > 0);
+ if (a_Data[0] <= ' ')
+ {
+ // This line is a continuation for the previous line
+ if (m_LastKey.empty())
+ {
+ return false;
+ }
+ // Append, including the whitespace in a_Data[0]
+ m_LastValue.append(a_Data, a_Size);
+ return true;
+ }
+
+ // This is a line with a new key:
+ NotifyLast();
+ for (size_t i = 0; i < a_Size; i++)
+ {
+ if (a_Data[i] == ':')
+ {
+ m_LastKey.assign(a_Data, i);
+ m_LastValue.assign(a_Data + i + 2, a_Size - i - 2);
+ return true;
+ }
+ } // for i - a_Data[]
+
+ // No colon was found, key-less header??
+ return false;
+}
+
+
+
+
diff --git a/src/HTTPServer/EnvelopeParser.h b/src/HTTPServer/EnvelopeParser.h
new file mode 100644
index 000000000..6430fbebf
--- /dev/null
+++ b/src/HTTPServer/EnvelopeParser.h
@@ -0,0 +1,69 @@
+
+// EnvelopeParser.h
+
+// Declares the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME
+
+
+
+
+
+#pragma once
+
+
+
+
+
+class cEnvelopeParser
+{
+public:
+ class cCallbacks
+ {
+ public:
+ /// Called when a full header line is parsed
+ virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) = 0;
+ } ;
+
+
+ cEnvelopeParser(cCallbacks & a_Callbacks);
+
+ /** Parses the incoming data.
+ Returns the number of bytes consumed from the input. The bytes not consumed are not part of the envelope header
+ */
+ int Parse(const char * a_Data, int a_Size);
+
+ /// Makes the parser forget everything parsed so far, so that it can be reused for parsing another datastream
+ void Reset(void);
+
+ /// Returns true if more input is expected for the envelope header
+ bool IsInHeaders(void) const { return m_IsInHeaders; }
+
+ /// Sets the IsInHeaders flag; used by cMultipartParser to simplify the parser initial conditions
+ void SetIsInHeaders(bool a_IsInHeaders) { m_IsInHeaders = a_IsInHeaders; }
+
+public:
+ /// Callbacks to call for the various events
+ cCallbacks & m_Callbacks;
+
+ /// Set to true while the parser is still parsing the envelope headers. Once set to true, the parser will not consume any more data.
+ bool m_IsInHeaders;
+
+ /// Buffer for the incoming data until it is parsed
+ AString m_IncomingData;
+
+ /// Holds the last parsed key; used for line-wrapped values
+ AString m_LastKey;
+
+ /// Holds the last parsed value; used for line-wrapped values
+ AString m_LastValue;
+
+
+ /// Notifies the callback of the key/value stored in m_LastKey/m_LastValue, then erases them
+ void NotifyLast(void);
+
+ /// Parses one line of header data. Returns true if successful
+ bool ParseLine(const char * a_Data, size_t a_Size);
+} ;
+
+
+
+
diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp
new file mode 100644
index 000000000..68afdfc11
--- /dev/null
+++ b/src/HTTPServer/HTTPConnection.cpp
@@ -0,0 +1,247 @@
+
+// HTTPConnection.cpp
+
+// Implements the cHTTPConnection class representing a single persistent connection in the HTTP server.
+
+#include "Globals.h"
+#include "HTTPConnection.h"
+#include "HTTPMessage.h"
+#include "HTTPServer.h"
+
+
+
+
+
+cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) :
+ m_HTTPServer(a_HTTPServer),
+ m_State(wcsRecvHeaders),
+ m_CurrentRequest(NULL)
+{
+ // LOGD("HTTP: New connection at %p", this);
+}
+
+
+
+
+
+cHTTPConnection::~cHTTPConnection()
+{
+ // LOGD("HTTP: Del connection at %p", this);
+}
+
+
+
+
+
+void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response)
+{
+ AppendPrintf(m_OutgoingData, "%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str());
+ m_HTTPServer.NotifyConnectionWrite(*this);
+ m_State = wcsRecvHeaders;
+}
+
+
+
+
+
+void cHTTPConnection::SendNeedAuth(const AString & a_Realm)
+{
+ AppendPrintf(m_OutgoingData, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str());
+ m_HTTPServer.NotifyConnectionWrite(*this);
+ m_State = wcsRecvHeaders;
+}
+
+
+
+
+
+void cHTTPConnection::Send(const cHTTPResponse & a_Response)
+{
+ ASSERT(m_State = wcsRecvIdle);
+ a_Response.AppendToData(m_OutgoingData);
+ m_State = wcsSendingResp;
+ m_HTTPServer.NotifyConnectionWrite(*this);
+}
+
+
+
+
+
+void cHTTPConnection::Send(const void * a_Data, int a_Size)
+{
+ ASSERT(m_State == wcsSendingResp);
+ AppendPrintf(m_OutgoingData, "%x\r\n", a_Size);
+ m_OutgoingData.append((const char *)a_Data, a_Size);
+ m_OutgoingData.append("\r\n");
+ m_HTTPServer.NotifyConnectionWrite(*this);
+}
+
+
+
+
+
+void cHTTPConnection::FinishResponse(void)
+{
+ ASSERT(m_State == wcsSendingResp);
+ m_OutgoingData.append("0\r\n\r\n");
+ m_State = wcsRecvHeaders;
+ m_HTTPServer.NotifyConnectionWrite(*this);
+}
+
+
+
+
+
+void cHTTPConnection::AwaitNextRequest(void)
+{
+ switch (m_State)
+ {
+ case wcsRecvHeaders:
+ {
+ // Nothing has been received yet, or a special response was given (SendStatusAndReason() or SendNeedAuth() )
+ break;
+ }
+
+ case wcsRecvIdle:
+ {
+ // The client is waiting for a response, send an "Internal server error":
+ m_OutgoingData.append("HTTP/1.1 500 Internal Server Error\r\n\r\n");
+ m_HTTPServer.NotifyConnectionWrite(*this);
+ m_State = wcsRecvHeaders;
+ break;
+ }
+
+ case wcsSendingResp:
+ {
+ // The response headers have been sent, we need to terminate the response body:
+ m_OutgoingData.append("0\r\n\r\n");
+ m_State = wcsRecvHeaders;
+ break;
+ }
+
+ default:
+ {
+ ASSERT(!"Unhandled state recovery");
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cHTTPConnection::Terminate(void)
+{
+ if (m_CurrentRequest != NULL)
+ {
+ m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
+ }
+ m_HTTPServer.CloseConnection(*this);
+}
+
+
+
+
+
+void cHTTPConnection::DataReceived(const char * a_Data, int a_Size)
+{
+ switch (m_State)
+ {
+ case wcsRecvHeaders:
+ {
+ if (m_CurrentRequest == NULL)
+ {
+ m_CurrentRequest = new cHTTPRequest;
+ }
+
+ int BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size);
+ if (BytesConsumed < 0)
+ {
+ delete m_CurrentRequest;
+ m_CurrentRequest = NULL;
+ m_State = wcsInvalid;
+ m_HTTPServer.CloseConnection(*this);
+ return;
+ }
+ if (m_CurrentRequest->IsInHeaders())
+ {
+ // The request headers are not yet complete
+ return;
+ }
+
+ // The request has finished parsing its headers successfully, notify of it:
+ m_State = wcsRecvBody;
+ m_HTTPServer.NewRequest(*this, *m_CurrentRequest);
+ m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength();
+ if (m_CurrentRequestBodyRemaining < 0)
+ {
+ // The body length was not specified in the request, assume zero
+ m_CurrentRequestBodyRemaining = 0;
+ }
+
+ // Process the rest of the incoming data into the request body:
+ if (a_Size > BytesConsumed)
+ {
+ DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed);
+ }
+ else
+ {
+ DataReceived("", 0); // If the request has zero body length, let it be processed right-away
+ }
+ break;
+ }
+
+ case wcsRecvBody:
+ {
+ ASSERT(m_CurrentRequest != NULL);
+ if (m_CurrentRequestBodyRemaining > 0)
+ {
+ int BytesToConsume = std::min(m_CurrentRequestBodyRemaining, a_Size);
+ m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume);
+ m_CurrentRequestBodyRemaining -= BytesToConsume;
+ }
+ if (m_CurrentRequestBodyRemaining == 0)
+ {
+ m_State = wcsRecvIdle;
+ m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
+ delete m_CurrentRequest;
+ m_CurrentRequest = NULL;
+ }
+ break;
+ }
+
+ default:
+ {
+ // TODO: Should we be receiving data in this state?
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cHTTPConnection::GetOutgoingData(AString & a_Data)
+{
+ std::swap(a_Data, m_OutgoingData);
+}
+
+
+
+
+
+void cHTTPConnection::SocketClosed(void)
+{
+ if (m_CurrentRequest != NULL)
+ {
+ m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
+ }
+ m_HTTPServer.CloseConnection(*this);
+}
+
+
+
+
+
diff --git a/src/HTTPServer/HTTPConnection.h b/src/HTTPServer/HTTPConnection.h
new file mode 100644
index 000000000..14603bb70
--- /dev/null
+++ b/src/HTTPServer/HTTPConnection.h
@@ -0,0 +1,101 @@
+
+// HTTPConnection.h
+
+// Declares the cHTTPConnection class representing a single persistent connection in the HTTP server.
+
+
+
+
+
+#pragma once
+
+#include "../OSSupport/SocketThreads.h"
+
+
+
+
+
+// fwd:
+class cHTTPServer;
+class cHTTPResponse;
+class cHTTPRequest;
+
+
+
+
+
+class cHTTPConnection :
+ public cSocketThreads::cCallback
+{
+public:
+
+ enum eState
+ {
+ wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest is created if NULL)
+ wcsRecvBody, ///< Receiving request body (m_CurrentRequest is valid)
+ wcsRecvIdle, ///< Has received the entire body, waiting to send the response (m_CurrentRequest == NULL)
+ wcsSendingResp, ///< Sending response body (m_CurrentRequest == NULL)
+ wcsInvalid, ///< The request was malformed, the connection is closing
+ } ;
+
+ cHTTPConnection(cHTTPServer & a_HTTPServer);
+ ~cHTTPConnection();
+
+ /// Sends HTTP status code together with a_Reason (used for HTTP errors)
+ void SendStatusAndReason(int a_StatusCode, const AString & a_Reason);
+
+ /// Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm
+ void SendNeedAuth(const AString & a_Realm);
+
+ /// Sends the headers contained in a_Response
+ void Send(const cHTTPResponse & a_Response);
+
+ /// Sends the data as the response (may be called multiple times)
+ void Send(const void * a_Data, int a_Size);
+
+ /// Sends the data as the response (may be called multiple times)
+ void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); }
+
+ /// Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive)
+ void FinishResponse(void);
+
+ /// Resets the connection for a new request. Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd"
+ void AwaitNextRequest(void);
+
+ /// Terminates the connection; finishes any request being currently processed
+ void Terminate(void);
+
+protected:
+ typedef std::map<AString, AString> cNameValueMap;
+
+ /// The parent webserver that is to be notified of events on this connection
+ cHTTPServer & m_HTTPServer;
+
+ /// All the incoming data until the entire request header is parsed
+ AString m_IncomingHeaderData;
+
+ /// Status in which the request currently is
+ eState m_State;
+
+ /// Data that is queued for sending, once the socket becomes writable
+ AString m_OutgoingData;
+
+ /// The request being currently received (valid only between having parsed the headers and finishing receiving the body)
+ cHTTPRequest * m_CurrentRequest;
+
+ /// Number of bytes that remain to read for the complete body of the message to be received. Valid only in wcsRecvBody
+ int m_CurrentRequestBodyRemaining;
+
+
+ // cSocketThreads::cCallback overrides:
+ virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
+ virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
+ virtual void SocketClosed (void) override; // The socket has been closed for any reason
+} ;
+
+typedef std::vector<cHTTPConnection *> cHTTPConnections;
+
+
+
+
+
diff --git a/src/HTTPServer/HTTPFormParser.cpp b/src/HTTPServer/HTTPFormParser.cpp
new file mode 100644
index 000000000..596db424e
--- /dev/null
+++ b/src/HTTPServer/HTTPFormParser.cpp
@@ -0,0 +1,290 @@
+
+// HTTPFormParser.cpp
+
+// Implements the cHTTPFormParser class representing a parser for forms sent over HTTP
+
+#include "Globals.h"
+#include "HTTPFormParser.h"
+#include "HTTPMessage.h"
+#include "MultipartParser.h"
+#include "NameValueParser.h"
+
+
+
+
+
+cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks) :
+ m_Callbacks(a_Callbacks),
+ m_IsValid(true)
+{
+ if (a_Request.GetMethod() == "GET")
+ {
+ m_Kind = fpkURL;
+
+ // Directly parse the URL in the request:
+ const AString & URL = a_Request.GetURL();
+ size_t idxQM = URL.find('?');
+ if (idxQM != AString::npos)
+ {
+ Parse(URL.c_str() + idxQM + 1, URL.size() - idxQM - 1);
+ }
+ return;
+ }
+ if ((a_Request.GetMethod() == "POST") || (a_Request.GetMethod() == "PUT"))
+ {
+ if (strncmp(a_Request.GetContentType().c_str(), "application/x-www-form-urlencoded", 33) == 0)
+ {
+ m_Kind = fpkFormUrlEncoded;
+ return;
+ }
+ if (strncmp(a_Request.GetContentType().c_str(), "multipart/form-data", 19) == 0)
+ {
+ m_Kind = fpkMultipart;
+ BeginMultipart(a_Request);
+ return;
+ }
+ }
+ // Invalid method / content type combination, this is not a HTTP form
+ m_IsValid = false;
+}
+
+
+
+
+
+cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks) :
+ m_Callbacks(a_Callbacks),
+ m_Kind(a_Kind),
+ m_IsValid(true)
+{
+ Parse(a_Data, a_Size);
+}
+
+
+
+
+
+void cHTTPFormParser::Parse(const char * a_Data, int a_Size)
+{
+ if (!m_IsValid)
+ {
+ return;
+ }
+
+ switch (m_Kind)
+ {
+ case fpkURL:
+ case fpkFormUrlEncoded:
+ {
+ // This format is used for smaller forms (not file uploads), so we can delay parsing it until Finish()
+ m_IncomingData.append(a_Data, a_Size);
+ break;
+ }
+ case fpkMultipart:
+ {
+ ASSERT(m_MultipartParser.get() != NULL);
+ m_MultipartParser->Parse(a_Data, a_Size);
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled form kind");
+ break;
+ }
+ }
+}
+
+
+
+
+
+bool cHTTPFormParser::Finish(void)
+{
+ switch (m_Kind)
+ {
+ case fpkURL:
+ case fpkFormUrlEncoded:
+ {
+ // m_IncomingData has all the form data, parse it now:
+ ParseFormUrlEncoded();
+ break;
+ }
+ }
+ return (m_IsValid && m_IncomingData.empty());
+}
+
+
+
+
+
+bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request)
+{
+ const AString & ContentType = a_Request.GetContentType();
+ return (
+ (ContentType == "application/x-www-form-urlencoded") ||
+ (strncmp(ContentType.c_str(), "multipart/form-data", 19) == 0) ||
+ (
+ (a_Request.GetMethod() == "GET") &&
+ (a_Request.GetURL().find('?') != AString::npos)
+ )
+ );
+ return false;
+}
+
+
+
+
+
+void cHTTPFormParser::BeginMultipart(const cHTTPRequest & a_Request)
+{
+ ASSERT(m_MultipartParser.get() == NULL);
+ m_MultipartParser.reset(new cMultipartParser(a_Request.GetContentType(), *this));
+}
+
+
+
+
+
+void cHTTPFormParser::ParseFormUrlEncoded(void)
+{
+ // Parse m_IncomingData for all the variables; no more data is incoming, since this is called from Finish()
+ // This may not be the most performant version, but we don't care, the form data is small enough and we're not a full-fledged web server anyway
+ AStringVector Lines = StringSplit(m_IncomingData, "&");
+ for (AStringVector::iterator itr = Lines.begin(), end = Lines.end(); itr != end; ++itr)
+ {
+ AStringVector Components = StringSplit(*itr, "=");
+ switch (Components.size())
+ {
+ default:
+ {
+ // Neither name nor value, or too many "="s, mark this as invalid form:
+ m_IsValid = false;
+ return;
+ }
+ case 1:
+ {
+ // Only name present
+ (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = "";
+ break;
+ }
+ case 2:
+ {
+ // name=value format:
+ (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = URLDecode(ReplaceAllCharOccurrences(Components[1], '+', ' '));
+ break;
+ }
+ }
+ } // for itr - Lines[]
+ m_IncomingData.clear();
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartStart(void)
+{
+ m_CurrentPartFileName.clear();
+ m_CurrentPartName.clear();
+ m_IsCurrentPartFile = false;
+ m_FileHasBeenAnnounced = false;
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartHeader(const AString & a_Key, const AString & a_Value)
+{
+ if (NoCaseCompare(a_Key, "Content-Disposition") == 0)
+ {
+ size_t len = a_Value.size();
+ size_t ParamsStart = AString::npos;
+ for (size_t i = 0; i < len; ++i)
+ {
+ if (a_Value[i] > ' ')
+ {
+ if (strncmp(a_Value.c_str() + i, "form-data", 9) != 0)
+ {
+ // Content disposition is not "form-data", mark the whole form invalid
+ m_IsValid = false;
+ return;
+ }
+ ParamsStart = a_Value.find(';', i + 9);
+ break;
+ }
+ }
+ if (ParamsStart == AString::npos)
+ {
+ // There is data missing in the Content-Disposition field, mark the whole form invalid:
+ m_IsValid = false;
+ return;
+ }
+
+ // Parse the field name and optional filename from this header:
+ cNameValueParser Parser(a_Value.data() + ParamsStart, a_Value.size() - ParamsStart);
+ Parser.Finish();
+ m_CurrentPartName = Parser["name"];
+ if (!Parser.IsValid() || m_CurrentPartName.empty())
+ {
+ // The required parameter "name" is missing, mark the whole form invalid:
+ m_IsValid = false;
+ return;
+ }
+ m_CurrentPartFileName = Parser["filename"];
+ }
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartData(const char * a_Data, int a_Size)
+{
+ if (m_CurrentPartName.empty())
+ {
+ // Prologue, epilogue or invalid part
+ return;
+ }
+ if (m_CurrentPartFileName.empty())
+ {
+ // This is a variable, store it in the map
+ iterator itr = find(m_CurrentPartName);
+ if (itr == end())
+ {
+ (*this)[m_CurrentPartName] = AString(a_Data, a_Size);
+ }
+ else
+ {
+ itr->second.append(a_Data, a_Size);
+ }
+ }
+ else
+ {
+ // This is a file, pass it on through the callbacks
+ if (!m_FileHasBeenAnnounced)
+ {
+ m_Callbacks.OnFileStart(*this, m_CurrentPartFileName);
+ m_FileHasBeenAnnounced = true;
+ }
+ m_Callbacks.OnFileData(*this, a_Data, a_Size);
+ }
+}
+
+
+
+
+
+void cHTTPFormParser::OnPartEnd(void)
+{
+ if (m_FileHasBeenAnnounced)
+ {
+ m_Callbacks.OnFileEnd(*this);
+ }
+ m_CurrentPartName.clear();
+ m_CurrentPartFileName.clear();
+}
+
+
+
+
diff --git a/src/HTTPServer/HTTPFormParser.h b/src/HTTPServer/HTTPFormParser.h
new file mode 100644
index 000000000..a554ca5a4
--- /dev/null
+++ b/src/HTTPServer/HTTPFormParser.h
@@ -0,0 +1,112 @@
+
+// HTTPFormParser.h
+
+// Declares the cHTTPFormParser class representing a parser for forms sent over HTTP
+
+
+
+
+#pragma once
+
+#include "MultipartParser.h"
+
+
+
+
+
+// fwd:
+class cHTTPRequest;
+
+
+
+
+
+class cHTTPFormParser :
+ public std::map<AString, AString>,
+ public cMultipartParser::cCallbacks
+{
+public:
+ enum eKind
+ {
+ fpkURL, ///< The form has been transmitted as parameters to a GET request
+ fpkFormUrlEncoded, ///< The form has been POSTed or PUT, with Content-Type of "application/x-www-form-urlencoded"
+ fpkMultipart, ///< The form has been POSTed or PUT, with Content-Type of "multipart/form-data"
+ } ;
+
+ class cCallbacks
+ {
+ public:
+ /// Called when a new file part is encountered in the form data
+ virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) = 0;
+
+ /// Called when more file data has come for the current file in the form data
+ virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) = 0;
+
+ /// Called when the current file part has ended in the form data
+ virtual void OnFileEnd(cHTTPFormParser & a_Parser) = 0;
+ } ;
+
+
+ /// Creates a parser that is tied to a request and notifies of various events using a callback mechanism
+ cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks);
+
+ /// Creates a parser with the specified content type that reads data from a string
+ cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks);
+
+ /// Adds more data into the parser, as the request body is received
+ void Parse(const char * a_Data, int a_Size);
+
+ /** Notifies that there's no more data incoming and the parser should finish its parsing.
+ Returns true if parsing successful
+ */
+ bool Finish(void);
+
+ /// Returns true if the headers suggest the request has form data parseable by this class
+ static bool HasFormData(const cHTTPRequest & a_Request);
+
+protected:
+
+ /// The callbacks to call for incoming file data
+ cCallbacks & m_Callbacks;
+
+ /// The kind of the parser (decided in the constructor, used in Parse()
+ eKind m_Kind;
+
+ /// Buffer for the incoming data until it's parsed
+ AString m_IncomingData;
+
+ /// True if the information received so far is a valid form; set to false on first problem. Further parsing is skipped when false.
+ bool m_IsValid;
+
+ /// The parser for the multipart data, if used
+ std::auto_ptr<cMultipartParser> m_MultipartParser;
+
+ /// Name of the currently parsed part in multipart data
+ AString m_CurrentPartName;
+
+ /// True if the currently parsed part in multipart data is a file
+ bool m_IsCurrentPartFile;
+
+ /// Filename of the current parsed part in multipart data (for file uploads)
+ AString m_CurrentPartFileName;
+
+ /// Set to true after m_Callbacks.OnFileStart() has been called, reset to false on PartEnd
+ bool m_FileHasBeenAnnounced;
+
+
+ /// Sets up the object for parsing a fpkMultipart request
+ void BeginMultipart(const cHTTPRequest & a_Request);
+
+ /// Parses m_IncomingData as form-urlencoded data (fpkURL or fpkFormUrlEncoded kinds)
+ void ParseFormUrlEncoded(void);
+
+ // cMultipartParser::cCallbacks overrides:
+ virtual void OnPartStart (void) override;
+ virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override;
+ virtual void OnPartData (const char * a_Data, int a_Size) override;
+ virtual void OnPartEnd (void) override;
+} ;
+
+
+
+
diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp
new file mode 100644
index 000000000..ab23866e6
--- /dev/null
+++ b/src/HTTPServer/HTTPMessage.cpp
@@ -0,0 +1,279 @@
+
+// HTTPMessage.cpp
+
+// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes
+
+#include "Globals.h"
+#include "HTTPMessage.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHTTPMessage:
+
+cHTTPMessage::cHTTPMessage(eKind a_Kind) :
+ m_Kind(a_Kind),
+ m_ContentLength(-1)
+{
+}
+
+
+
+
+
+void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value)
+{
+ AString Key = a_Key;
+ StrToLower(Key);
+ cNameValueMap::iterator itr = m_Headers.find(Key);
+ if (itr == m_Headers.end())
+ {
+ m_Headers[Key] = a_Value;
+ }
+ else
+ {
+ // The header-field key is specified multiple times, combine into comma-separated list (RFC 2616 @ 4.2)
+ itr->second.append(", ");
+ itr->second.append(a_Value);
+ }
+
+ // Special processing for well-known headers:
+ if (Key == "content-type")
+ {
+ m_ContentType = m_Headers[Key];
+ }
+ else if (Key == "content-length")
+ {
+ m_ContentLength = atoi(m_Headers[Key].c_str());
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHTTPRequest:
+
+cHTTPRequest::cHTTPRequest(void) :
+ super(mkRequest),
+ m_EnvelopeParser(*this),
+ m_IsValid(true),
+ m_UserData(NULL),
+ m_HasAuth(false)
+{
+}
+
+
+
+
+
+int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size)
+{
+ if (!m_IsValid)
+ {
+ return -1;
+ }
+
+ if (m_Method.empty())
+ {
+ // The first line hasn't been processed yet
+ int res = ParseRequestLine(a_Data, a_Size);
+ if ((res < 0) || (res == a_Size))
+ {
+ return res;
+ }
+ int res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res);
+ if (res2 < 0)
+ {
+ m_IsValid = false;
+ return res2;
+ }
+ return res2 + res;
+ }
+
+ if (m_EnvelopeParser.IsInHeaders())
+ {
+ int res = m_EnvelopeParser.Parse(a_Data, a_Size);
+ if (res < 0)
+ {
+ m_IsValid = false;
+ }
+ return res;
+ }
+ return 0;
+}
+
+
+
+
+
+AString cHTTPRequest::GetBareURL(void) const
+{
+ size_t idxQM = m_URL.find('?');
+ if (idxQM != AString::npos)
+ {
+ return m_URL.substr(0, idxQM);
+ }
+ else
+ {
+ return m_URL;
+ }
+}
+
+
+
+
+
+int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size)
+{
+ m_IncomingHeaderData.append(a_Data, a_Size);
+ size_t IdxEnd = m_IncomingHeaderData.size();
+
+ // Ignore the initial CRLFs (HTTP spec's "should")
+ size_t LineStart = 0;
+ while (
+ (LineStart < IdxEnd) &&
+ (
+ (m_IncomingHeaderData[LineStart] == '\r') ||
+ (m_IncomingHeaderData[LineStart] == '\n')
+ )
+ )
+ {
+ LineStart++;
+ }
+ if (LineStart >= IdxEnd)
+ {
+ m_IsValid = false;
+ return -1;
+ }
+
+ int NumSpaces = 0;
+ size_t MethodEnd = 0;
+ size_t URLEnd = 0;
+ for (size_t i = LineStart; i < IdxEnd; i++)
+ {
+ switch (m_IncomingHeaderData[i])
+ {
+ case ' ':
+ {
+ switch (NumSpaces)
+ {
+ case 0:
+ {
+ MethodEnd = i;
+ break;
+ }
+ case 1:
+ {
+ URLEnd = i;
+ break;
+ }
+ default:
+ {
+ // Too many spaces in the request
+ m_IsValid = false;
+ return -1;
+ }
+ }
+ NumSpaces += 1;
+ break;
+ }
+ case '\n':
+ {
+ if ((i == 0) || (m_IncomingHeaderData[i - 1] != '\r') || (NumSpaces != 2) || (i < URLEnd + 7))
+ {
+ // LF too early, without a CR, without two preceeding spaces or too soon after the second space
+ m_IsValid = false;
+ return -1;
+ }
+ // Check that there's HTTP/version at the end
+ if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0)
+ {
+ m_IsValid = false;
+ return -1;
+ }
+ m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart);
+ m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1);
+ return i + 1;
+ }
+ } // switch (m_IncomingHeaderData[i])
+ } // for i - m_IncomingHeaderData[]
+
+ // CRLF hasn't been encountered yet, consider all data consumed
+ return a_Size;
+}
+
+
+
+
+
+void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value)
+{
+ if (
+ (NoCaseCompare(a_Key, "Authorization") == 0) &&
+ (strncmp(a_Value.c_str(), "Basic ", 6) == 0)
+ )
+ {
+ AString UserPass = Base64Decode(a_Value.substr(6));
+ size_t idxCol = UserPass.find(':');
+ if (idxCol != AString::npos)
+ {
+ m_AuthUsername = UserPass.substr(0, idxCol);
+ m_AuthPassword = UserPass.substr(idxCol + 1);
+ m_HasAuth = true;
+ }
+ }
+ AddHeader(a_Key, a_Value);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHTTPResponse:
+
+cHTTPResponse::cHTTPResponse(void) :
+ super(mkResponse)
+{
+}
+
+
+
+
+
+void cHTTPResponse::AppendToData(AString & a_DataStream) const
+{
+ a_DataStream.append("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: ");
+ a_DataStream.append(m_ContentType);
+ a_DataStream.append("\r\n");
+ for (cNameValueMap::const_iterator itr = m_Headers.begin(), end = m_Headers.end(); itr != end; ++itr)
+ {
+ if ((itr->first == "Content-Type") || (itr->first == "Content-Length"))
+ {
+ continue;
+ }
+ a_DataStream.append(itr->first);
+ a_DataStream.append(": ");
+ a_DataStream.append(itr->second);
+ a_DataStream.append("\r\n");
+ } // for itr - m_Headers[]
+ a_DataStream.append("\r\n");
+}
+
+
+
+
diff --git a/src/HTTPServer/HTTPMessage.h b/src/HTTPServer/HTTPMessage.h
new file mode 100644
index 000000000..f5284c535
--- /dev/null
+++ b/src/HTTPServer/HTTPMessage.h
@@ -0,0 +1,164 @@
+
+// HTTPMessage.h
+
+// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes
+
+
+
+
+
+#pragma once
+
+#include "EnvelopeParser.h"
+
+
+
+
+
+class cHTTPMessage
+{
+public:
+ enum
+ {
+ HTTP_OK = 200,
+ HTTP_BAD_REQUEST = 400,
+ } ;
+
+ enum eKind
+ {
+ mkRequest,
+ mkResponse,
+ } ;
+
+ cHTTPMessage(eKind a_Kind);
+
+ /// Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length
+ void AddHeader(const AString & a_Key, const AString & a_Value);
+
+ void SetContentType (const AString & a_ContentType) { m_ContentType = a_ContentType; }
+ void SetContentLength(int a_ContentLength) { m_ContentLength = a_ContentLength; }
+
+ const AString & GetContentType (void) const { return m_ContentType; }
+ int GetContentLength(void) const { return m_ContentLength; }
+
+protected:
+ typedef std::map<AString, AString> cNameValueMap;
+
+ eKind m_Kind;
+
+ cNameValueMap m_Headers;
+
+ /// Type of the content; parsed by AddHeader(), set directly by SetContentLength()
+ AString m_ContentType;
+
+ /// Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength()
+ int m_ContentLength;
+} ;
+
+
+
+
+
+class cHTTPRequest :
+ public cHTTPMessage,
+ protected cEnvelopeParser::cCallbacks
+{
+ typedef cHTTPMessage super;
+
+public:
+ cHTTPRequest(void);
+
+ /** Parses the request line and then headers from the received data.
+ Returns the number of bytes consumed or a negative number for error
+ */
+ int ParseHeaders(const char * a_Data, int a_Size);
+
+ /// Returns true if the request did contain a Content-Length header
+ bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); }
+
+ /// Returns the method used in the request
+ const AString & GetMethod(void) const { return m_Method; }
+
+ /// Returns the URL used in the request
+ const AString & GetURL(void) const { return m_URL; }
+
+ /// Returns the URL used in the request, without any parameters
+ AString GetBareURL(void) const;
+
+ /// Sets the UserData pointer that is stored within this request. The request doesn't touch this data (doesn't delete it)!
+ void SetUserData(void * a_UserData) { m_UserData = a_UserData; }
+
+ /// Retrieves the UserData pointer that has been stored within this request.
+ void * GetUserData(void) const { return m_UserData; }
+
+ /// Returns true if more data is expected for the request headers
+ bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); }
+
+ /// Returns true if the request did present auth data that was understood by the parser
+ bool HasAuth(void) const { return m_HasAuth; }
+
+ /// Returns the username that the request presented. Only valid if HasAuth() is true
+ const AString & GetAuthUsername(void) const { return m_AuthUsername; }
+
+ /// Returns the password that the request presented. Only valid if HasAuth() is true
+ const AString & GetAuthPassword(void) const { return m_AuthPassword; }
+
+protected:
+ /// Parser for the envelope data
+ cEnvelopeParser m_EnvelopeParser;
+
+ /// True if the data received so far is parsed successfully. When false, all further parsing is skipped
+ bool m_IsValid;
+
+ /// Bufferred incoming data, while parsing for the request line
+ AString m_IncomingHeaderData;
+
+ /// Method of the request (GET / PUT / POST / ...)
+ AString m_Method;
+
+ /// Full URL of the request
+ AString m_URL;
+
+ /// Data that the HTTPServer callbacks are allowed to store.
+ void * m_UserData;
+
+ /// Set to true if the request contains auth data that was understood by the parser
+ bool m_HasAuth;
+
+ /// The username used for auth
+ AString m_AuthUsername;
+
+ /// The password used for auth
+ AString m_AuthPassword;
+
+
+ /** Parses the incoming data for the first line (RequestLine)
+ Returns the number of bytes consumed, or -1 for an error
+ */
+ int ParseRequestLine(const char * a_Data, int a_Size);
+
+ // cEnvelopeParser::cCallbacks overrides:
+ virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override;
+} ;
+
+
+
+
+
+class cHTTPResponse :
+ public cHTTPMessage
+{
+ typedef cHTTPMessage super;
+
+public:
+ cHTTPResponse(void);
+
+ /** Appends the response to the specified datastream - response line and headers.
+ The body will be sent later directly through cConnection::Send()
+ */
+ void AppendToData(AString & a_DataStream) const;
+} ;
+
+
+
+
diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp
new file mode 100644
index 000000000..f6f5b0f8b
--- /dev/null
+++ b/src/HTTPServer/HTTPServer.cpp
@@ -0,0 +1,258 @@
+
+// HTTPServer.cpp
+
+// Implements the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing
+
+#include "Globals.h"
+#include "HTTPServer.h"
+#include "HTTPMessage.h"
+#include "HTTPConnection.h"
+#include "HTTPFormParser.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+
+
+
+
+class cDebugCallbacks :
+ public cHTTPServer::cCallbacks,
+ protected cHTTPFormParser::cCallbacks
+{
+ virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override
+ {
+ if (cHTTPFormParser::HasFormData(a_Request))
+ {
+ a_Request.SetUserData(new cHTTPFormParser(a_Request, *this));
+ }
+ }
+
+
+ virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override
+ {
+ cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData());
+ if (FormParser != NULL)
+ {
+ FormParser->Parse(a_Data, a_Size);
+ }
+ }
+
+
+ virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override
+ {
+ cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData());
+ if (FormParser != NULL)
+ {
+ if (FormParser->Finish())
+ {
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send("<html><body><table border=1 cellspacing=0><tr><th>Name</th><th>Value</th></tr>\r\n");
+ for (cHTTPFormParser::iterator itr = FormParser->begin(), end = FormParser->end(); itr != end; ++itr)
+ {
+ a_Connection.Send(Printf("<tr><td valign=\"top\"><pre>%s</pre></td><td valign=\"top\"><pre>%s</pre></td></tr>\r\n", itr->first.c_str(), itr->second.c_str()));
+ } // for itr - FormParser[]
+ a_Connection.Send("</table></body></html>");
+ return;
+ }
+
+ // Parsing failed:
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/plain");
+ a_Connection.Send(Resp);
+ a_Connection.Send("Form parsing failed");
+ return;
+ }
+
+ // Test the auth failure and success:
+ if (a_Request.GetURL() == "/auth")
+ {
+ if (!a_Request.HasAuth() || (a_Request.GetAuthUsername() != "a") || (a_Request.GetAuthPassword() != "b"))
+ {
+ a_Connection.SendNeedAuth("MCServer WebAdmin");
+ return;
+ }
+ }
+
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/plain");
+ a_Connection.Send(Resp);
+ a_Connection.Send("Hello, world");
+ }
+
+
+ virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override
+ {
+ // TODO
+ }
+
+
+ virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override
+ {
+ // TODO
+ }
+
+
+ virtual void OnFileEnd(cHTTPFormParser & a_Parser) override
+ {
+ // TODO
+ }
+
+} g_DebugCallbacks;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHTTPServer:
+
+cHTTPServer::cHTTPServer(void) :
+ m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer IPv4"),
+ m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer IPv6"),
+ m_Callbacks(NULL)
+{
+}
+
+
+
+
+
+cHTTPServer::~cHTTPServer()
+{
+ Stop();
+}
+
+
+
+
+
+bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6)
+{
+ bool HasAnyPort;
+ HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4);
+ HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort;
+ if (!HasAnyPort)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cHTTPServer::Start(cCallbacks & a_Callbacks)
+{
+ m_Callbacks = &a_Callbacks;
+ if (!m_ListenThreadIPv4.Start())
+ {
+ return false;
+ }
+ if (!m_ListenThreadIPv6.Start())
+ {
+ m_ListenThreadIPv4.Stop();
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+void cHTTPServer::Stop(void)
+{
+ m_ListenThreadIPv4.Stop();
+ m_ListenThreadIPv6.Stop();
+
+ // Drop all current connections:
+ cCSLock Lock(m_CSConnections);
+ while (!m_Connections.empty())
+ {
+ m_Connections.front()->Terminate();
+ } // for itr - m_Connections[]
+}
+
+
+
+
+
+void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket)
+{
+ cHTTPConnection * Connection = new cHTTPConnection(*this);
+ m_SocketThreads.AddClient(a_Socket, Connection);
+ cCSLock Lock(m_CSConnections);
+ m_Connections.push_back(Connection);
+}
+
+
+
+
+
+void cHTTPServer::CloseConnection(cHTTPConnection & a_Connection)
+{
+ m_SocketThreads.RemoveClient(&a_Connection);
+ cCSLock Lock(m_CSConnections);
+ for (cHTTPConnections::iterator itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
+ {
+ if (*itr == &a_Connection)
+ {
+ m_Connections.erase(itr);
+ break;
+ }
+ }
+ delete &a_Connection;
+}
+
+
+
+
+
+void cHTTPServer::NotifyConnectionWrite(cHTTPConnection & a_Connection)
+{
+ m_SocketThreads.NotifyWrite(&a_Connection);
+}
+
+
+
+
+
+void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ m_Callbacks->OnRequestBegun(a_Connection, a_Request);
+}
+
+
+
+
+
+void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size)
+{
+ m_Callbacks->OnRequestBody(a_Connection, a_Request, a_Data, a_Size);
+}
+
+
+
+
+
+void cHTTPServer::RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ m_Callbacks->OnRequestFinished(a_Connection, a_Request);
+ a_Connection.AwaitNextRequest();
+}
+
+
+
+
diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h
new file mode 100644
index 000000000..ed7a03519
--- /dev/null
+++ b/src/HTTPServer/HTTPServer.h
@@ -0,0 +1,101 @@
+
+// HTTPServer.h
+
+// Declares the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing
+
+
+
+
+
+#pragma once
+
+#include "../OSSupport/ListenThread.h"
+#include "../OSSupport/SocketThreads.h"
+#include "lib/inifile/iniFile.h"
+
+
+
+
+
+// fwd:
+class cHTTPMessage;
+class cHTTPRequest;
+class cHTTPResponse;
+class cHTTPConnection;
+
+typedef std::vector<cHTTPConnection *> cHTTPConnections;
+
+
+
+
+
+
+class cHTTPServer :
+ public cListenThread::cCallback
+{
+public:
+ class cCallbacks
+ {
+ public:
+ /** Called when a new request arrives over a connection and its headers have been parsed.
+ The request body needn't have arrived yet.
+ */
+ virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0;
+
+ /// Called when another part of request body has arrived.
+ virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) = 0;
+
+ /// Called when the request body has been fully received in previous calls to OnRequestBody()
+ virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0;
+ } ;
+
+ cHTTPServer(void);
+ ~cHTTPServer();
+
+ /// Initializes the server on the specified ports
+ bool Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6);
+
+ /// Starts the server and assigns the callbacks to use for incoming requests
+ bool Start(cCallbacks & a_Callbacks);
+
+ /// Stops the server, drops all current connections
+ void Stop(void);
+
+protected:
+ friend class cHTTPConnection;
+
+ cListenThread m_ListenThreadIPv4;
+ cListenThread m_ListenThreadIPv6;
+
+ cSocketThreads m_SocketThreads;
+
+ cCriticalSection m_CSConnections;
+ cHTTPConnections m_Connections; ///< All the connections that are currently being serviced
+
+ /// The callbacks to call for various events
+ cCallbacks * m_Callbacks;
+
+
+ // cListenThread::cCallback overrides:
+ virtual void OnConnectionAccepted(cSocket & a_Socket) override;
+
+ /// Called by cHTTPConnection to close the connection (presumably due to an error)
+ void CloseConnection(cHTTPConnection & a_Connection);
+
+ /// Called by cHTTPConnection to notify SocketThreads that there's data to be sent for the connection
+ void NotifyConnectionWrite(cHTTPConnection & a_Connection);
+
+ /// Called by cHTTPConnection when it finishes parsing the request header
+ void NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
+
+ /// Called by cHTTPConenction when it receives more data for the request body
+ void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size);
+
+ /// Called by cHTTPConnection when it detects that the request has finished (all of its body has been received)
+ void RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
+} ;
+
+
+
+
+
diff --git a/src/HTTPServer/MultipartParser.cpp b/src/HTTPServer/MultipartParser.cpp
new file mode 100644
index 000000000..b49f6ec07
--- /dev/null
+++ b/src/HTTPServer/MultipartParser.cpp
@@ -0,0 +1,256 @@
+
+// MultipartParser.cpp
+
+// Implements the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts
+
+#include "Globals.h"
+#include "MultipartParser.h"
+#include "NameValueParser.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// self-test:
+
+#if 0
+
+class cMultipartParserTest :
+ public cMultipartParser::cCallbacks
+{
+public:
+ cMultipartParserTest(void)
+ {
+ cMultipartParser Parser("multipart/mixed; boundary=\"MyBoundaryString\"; foo=bar", *this);
+ const char Data[] =
+"ThisIsIgnoredPrologue\r\n\
+--MyBoundaryString\r\n\
+\r\n\
+Body with confusing strings\r\n\
+--NotABoundary\r\n\
+--MyBoundaryStringWithPostfix\r\n\
+--\r\n\
+--MyBoundaryString\r\n\
+content-disposition: inline\r\n\
+\r\n\
+This is body\r\n\
+--MyBoundaryString\r\n\
+\r\n\
+Headerless body with trailing CRLF\r\n\
+\r\n\
+--MyBoundaryString--\r\n\
+ThisIsIgnoredEpilogue";
+ printf("Multipart parsing test commencing.\n");
+ Parser.Parse(Data, sizeof(Data) - 1);
+ // DEBUG: Check if the onscreen output corresponds with the data above
+ printf("Multipart parsing test finished\n");
+ }
+
+ virtual void OnPartStart(void) override
+ {
+ printf("Starting a new part\n");
+ }
+
+
+ virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override
+ {
+ printf(" Hdr: \"%s\"=\"%s\"\n", a_Key.c_str(), a_Value.c_str());
+ }
+
+
+ virtual void OnPartData(const char * a_Data, int a_Size) override
+ {
+ printf(" Data: %d bytes, \"%.*s\"\n", a_Size, a_Size, a_Data);
+ }
+
+
+ virtual void OnPartEnd(void) override
+ {
+ printf("Part end\n");
+ }
+} g_Test;
+
+#endif
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMultipartParser:
+
+
+cMultipartParser::cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks) :
+ m_Callbacks(a_Callbacks),
+ m_IsValid(true),
+ m_EnvelopeParser(*this),
+ m_HasHadData(false)
+{
+ static AString s_Multipart = "multipart/";
+
+ // Check that the content type is multipart:
+ AString ContentType(a_ContentType);
+ if (strncmp(ContentType.c_str(), "multipart/", 10) != 0)
+ {
+ m_IsValid = false;
+ return;
+ }
+ size_t idxSC = ContentType.find(';', 10);
+ if (idxSC == AString::npos)
+ {
+ m_IsValid = false;
+ return;
+ }
+
+ // Find the multipart boundary:
+ ContentType.erase(0, idxSC + 1);
+ cNameValueParser CTParser(ContentType.c_str(), ContentType.size());
+ CTParser.Finish();
+ if (!CTParser.IsValid())
+ {
+ m_IsValid = false;
+ return;
+ }
+ m_Boundary = CTParser["boundary"];
+ m_IsValid = !m_Boundary.empty();
+ if (!m_IsValid)
+ {
+ return;
+ }
+
+ // Set the envelope parser for parsing the body, so that our Parse() function parses the ignored prefix data as a body
+ m_EnvelopeParser.SetIsInHeaders(false);
+
+ // Append an initial CRLF to the incoming data, so that a body starting with the boundary line will get caught
+ m_IncomingData.assign("\r\n");
+
+ /*
+ m_Boundary = AString("\r\n--") + m_Boundary
+ m_BoundaryEnd = m_Boundary + "--\r\n";
+ m_Boundary = m_Boundary + "\r\n";
+ */
+}
+
+
+
+
+
+void cMultipartParser::Parse(const char * a_Data, int a_Size)
+{
+ // Skip parsing if invalid
+ if (!m_IsValid)
+ {
+ return;
+ }
+
+ // Append to buffer, then parse it:
+ m_IncomingData.append(a_Data, a_Size);
+ while (true)
+ {
+ if (m_EnvelopeParser.IsInHeaders())
+ {
+ int BytesConsumed = m_EnvelopeParser.Parse(m_IncomingData.data(), m_IncomingData.size());
+ if (BytesConsumed < 0)
+ {
+ m_IsValid = false;
+ return;
+ }
+ if ((BytesConsumed == a_Size) && m_EnvelopeParser.IsInHeaders())
+ {
+ // All the incoming data has been consumed and still waiting for more
+ return;
+ }
+ m_IncomingData.erase(0, BytesConsumed);
+ }
+
+ // Search for boundary / boundary end:
+ size_t idxBoundary = m_IncomingData.find("\r\n--");
+ if (idxBoundary == AString::npos)
+ {
+ // Boundary string start not present, present as much data to the part callback as possible
+ if (m_IncomingData.size() > m_Boundary.size() + 8)
+ {
+ size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8;
+ m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport);
+ m_IncomingData.erase(0, BytesToReport);
+ }
+ return;
+ }
+ if (idxBoundary > 0)
+ {
+ m_Callbacks.OnPartData(m_IncomingData.data(), idxBoundary);
+ m_IncomingData.erase(0, idxBoundary);
+ }
+ idxBoundary = 4;
+ size_t LineEnd = m_IncomingData.find("\r\n", idxBoundary);
+ if (LineEnd == AString::npos)
+ {
+ // Not a complete line yet, present as much data to the part callback as possible
+ if (m_IncomingData.size() > m_Boundary.size() + 8)
+ {
+ size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8;
+ m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport);
+ m_IncomingData.erase(0, BytesToReport);
+ }
+ return;
+ }
+ if (
+ (LineEnd - idxBoundary != m_Boundary.size()) && // Line length not equal to boundary
+ (LineEnd - idxBoundary != m_Boundary.size() + 2) // Line length not equal to boundary end
+ )
+ {
+ // Got a line, but it's not a boundary, report it as data:
+ m_Callbacks.OnPartData(m_IncomingData.data(), LineEnd);
+ m_IncomingData.erase(0, LineEnd);
+ continue;
+ }
+
+ if (strncmp(m_IncomingData.c_str() + idxBoundary, m_Boundary.c_str(), m_Boundary.size()) == 0)
+ {
+ // Boundary or BoundaryEnd found:
+ m_Callbacks.OnPartEnd();
+ size_t idxSlash = idxBoundary + m_Boundary.size();
+ if ((m_IncomingData[idxSlash] == '-') && (m_IncomingData[idxSlash + 1] == '-'))
+ {
+ // This was the last part
+ m_Callbacks.OnPartData(m_IncomingData.data() + idxSlash + 4, m_IncomingData.size() - idxSlash - 4);
+ m_IncomingData.clear();
+ return;
+ }
+ m_Callbacks.OnPartStart();
+ m_IncomingData.erase(0, LineEnd + 2);
+
+ // Keep parsing for the headers that may have come with this data:
+ m_EnvelopeParser.Reset();
+ continue;
+ }
+
+ // It's a line, but not a boundary. It can be fully sent to the data receiver, since a boundary cannot cross lines
+ m_Callbacks.OnPartData(m_IncomingData.c_str(), LineEnd);
+ m_IncomingData.erase(0, LineEnd);
+ } // while (true)
+}
+
+
+
+
+
+void cMultipartParser::OnHeaderLine(const AString & a_Key, const AString & a_Value)
+{
+ m_Callbacks.OnPartHeader(a_Key, a_Value);
+}
+
+
+
+
diff --git a/src/HTTPServer/MultipartParser.h b/src/HTTPServer/MultipartParser.h
new file mode 100644
index 000000000..d853929ed
--- /dev/null
+++ b/src/HTTPServer/MultipartParser.h
@@ -0,0 +1,76 @@
+
+// MultipartParser.h
+
+// Declares the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts
+
+
+
+
+
+#pragma once
+
+#include "EnvelopeParser.h"
+
+
+
+
+
+class cMultipartParser :
+ protected cEnvelopeParser::cCallbacks
+{
+public:
+ class cCallbacks
+ {
+ public:
+ /// Called when a new part starts
+ virtual void OnPartStart(void) = 0;
+
+ /// Called when a complete header line is received for a part
+ virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) = 0;
+
+ /// Called when body for a part is received
+ virtual void OnPartData(const char * a_Data, int a_Size) = 0;
+
+ /// Called when the current part ends
+ virtual void OnPartEnd(void) = 0;
+ } ;
+
+ /// Creates the parser, expects to find the boundary in a_ContentType
+ cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks);
+
+ /// Parses more incoming data
+ void Parse(const char * a_Data, int a_Size);
+
+protected:
+ /// The callbacks to call for various parsing events
+ cCallbacks & m_Callbacks;
+
+ /// True if the data parsed so far is valid; if false, further parsing is skipped
+ bool m_IsValid;
+
+ /// Parser for each part's envelope
+ cEnvelopeParser m_EnvelopeParser;
+
+ /// Buffer for the incoming data until it is parsed
+ AString m_IncomingData;
+
+ /// The boundary, excluding both the initial "--" and the terminating CRLF
+ AString m_Boundary;
+
+ /// Set to true if some data for the current part has already been signalized to m_Callbacks. Used for proper CRLF inserting.
+ bool m_HasHadData;
+
+
+ /// Parse one line of incoming data. The CRLF has already been stripped from a_Data / a_Size
+ void ParseLine(const char * a_Data, int a_Size);
+
+ /// Parse one line of incoming data in the headers section of a part. The CRLF has already been stripped from a_Data / a_Size
+ void ParseHeaderLine(const char * a_Data, int a_Size);
+
+ // cEnvelopeParser overrides:
+ virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override;
+} ;
+
+
+
+
diff --git a/src/HTTPServer/NameValueParser.cpp b/src/HTTPServer/NameValueParser.cpp
new file mode 100644
index 000000000..a27f07d19
--- /dev/null
+++ b/src/HTTPServer/NameValueParser.cpp
@@ -0,0 +1,412 @@
+
+// NameValueParser.cpp
+
+// Implements the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap
+
+#include "Globals.h"
+#include "NameValueParser.h"
+
+
+
+
+
+
+// DEBUG: Self-test
+
+#if 0
+
+class cNameValueParserTest
+{
+public:
+ cNameValueParserTest(void)
+ {
+ const char Data[] = " Name1=Value1;Name2 = Value 2; Name3 =\"Value 3\"; Name4 =\'Value 4\'; Name5=\"Confusing; isn\'t it?\"";
+
+ // Now try parsing char-by-char, to debug transitions across datachunk boundaries:
+ cNameValueParser Parser2;
+ for (int i = 0; i < sizeof(Data) - 1; i++)
+ {
+ Parser2.Parse(Data + i, 1);
+ }
+ Parser2.Finish();
+
+ // Parse as a single chunk of data:
+ cNameValueParser Parser(Data, sizeof(Data) - 1);
+
+ // Use the debugger to inspect the Parser variable
+
+ // Check that the two parsers have the same content:
+ for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr)
+ {
+ ASSERT(Parser2[itr->first] == itr->second);
+ } // for itr - Parser[]
+
+ // Try parsing in 2-char chunks:
+ cNameValueParser Parser3;
+ for (int i = 0; i < sizeof(Data) - 2; i += 2)
+ {
+ Parser3.Parse(Data + i, 2);
+ }
+ if ((sizeof(Data) % 2) == 0) // There are even number of chars, including the NUL, so the data has an odd length. Parse one more char
+ {
+ Parser3.Parse(Data + sizeof(Data) - 2, 1);
+ }
+ Parser3.Finish();
+
+ // Check that the third parser has the same content:
+ for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr)
+ {
+ ASSERT(Parser3[itr->first] == itr->second);
+ } // for itr - Parser[]
+
+ printf("cNameValueParserTest done");
+ }
+} g_Test;
+
+#endif
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cNameValueParser:
+
+cNameValueParser::cNameValueParser(bool a_AllowsKeyOnly) :
+ m_State(psKeySpace),
+ m_AllowsKeyOnly(a_AllowsKeyOnly)
+{
+}
+
+
+
+
+
+cNameValueParser::cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly) :
+ m_State(psKeySpace),
+ m_AllowsKeyOnly(a_AllowsKeyOnly)
+{
+ Parse(a_Data, a_Size);
+}
+
+
+
+
+
+void cNameValueParser::Parse(const char * a_Data, int a_Size)
+{
+ ASSERT(m_State != psFinished); // Calling Parse() after Finish() is wrong!
+
+ if ((m_State == psInvalid) || (m_State == psFinished))
+ {
+ return;
+ }
+ int Last = 0;
+ for (int i = 0; i < a_Size;)
+ {
+ switch (m_State)
+ {
+ case psKeySpace:
+ {
+ // Skip whitespace until a non-whitespace is found, then start the key:
+ while ((i < a_Size) && (a_Data[i] <= ' '))
+ {
+ i++;
+ }
+ if ((i < a_Size) && (a_Data[i] > ' '))
+ {
+ m_State = psKey;
+ Last = i;
+ }
+ break;
+ }
+
+ case psKey:
+ {
+ // Read the key until whitespace or an equal sign:
+ while (i < a_Size)
+ {
+ if (a_Data[i] == '=')
+ {
+ m_CurrentKey.append(a_Data + Last, i - Last);
+ i++;
+ Last = i;
+ m_State = psEqual;
+ break;
+ }
+ else if (a_Data[i] <= ' ')
+ {
+ m_CurrentKey.append(a_Data + Last, i - Last);
+ i++;
+ Last = i;
+ m_State = psEqualSpace;
+ break;
+ }
+ else if (a_Data[i] == ';')
+ {
+ if (!m_AllowsKeyOnly)
+ {
+ m_State = psInvalid;
+ return;
+ }
+ m_CurrentKey.append(a_Data + Last, i - Last);
+ i++;
+ Last = i;
+ (*this)[m_CurrentKey] = "";
+ m_CurrentKey.clear();
+ m_State = psKeySpace;
+ break;
+ }
+ else if ((a_Data[i] == '\"') || (a_Data[i] == '\''))
+ {
+ m_State = psInvalid;
+ return;
+ }
+ i++;
+ } // while (i < a_Size)
+ if (i == a_Size)
+ {
+ // Still the key, ran out of data to parse, store the part of the key parsed so far:
+ m_CurrentKey.append(a_Data + Last, a_Size - Last);
+ return;
+ }
+ break;
+ }
+
+ case psEqualSpace:
+ {
+ // The space before the expected equal sign; the current key is already assigned
+ while (i < a_Size)
+ {
+ if (a_Data[i] == '=')
+ {
+ m_State = psEqual;
+ i++;
+ Last = i;
+ break;
+ }
+ else if (a_Data[i] == ';')
+ {
+ // Key-only
+ if (!m_AllowsKeyOnly)
+ {
+ m_State = psInvalid;
+ return;
+ }
+ i++;
+ Last = i;
+ (*this)[m_CurrentKey] = "";
+ m_CurrentKey.clear();
+ m_State = psKeySpace;
+ break;
+ }
+ else if (a_Data[i] > ' ')
+ {
+ m_State = psInvalid;
+ return;
+ }
+ i++;
+ } // while (i < a_Size)
+ break;
+ } // case psEqualSpace
+
+ case psEqual:
+ {
+ // just parsed the equal-sign
+ while (i < a_Size)
+ {
+ if (a_Data[i] == ';')
+ {
+ if (!m_AllowsKeyOnly)
+ {
+ m_State = psInvalid;
+ return;
+ }
+ i++;
+ Last = i;
+ (*this)[m_CurrentKey] = "";
+ m_CurrentKey.clear();
+ m_State = psKeySpace;
+ break;
+ }
+ else if (a_Data[i] == '\"')
+ {
+ i++;
+ Last = i;
+ m_State = psValueInDQuotes;
+ break;
+ }
+ else if (a_Data[i] == '\'')
+ {
+ i++;
+ Last = i;
+ m_State = psValueInSQuotes;
+ break;
+ }
+ else
+ {
+ m_CurrentValue.push_back(a_Data[i]);
+ i++;
+ Last = i;
+ m_State = psValueRaw;
+ break;
+ }
+ i++;
+ } // while (i < a_Size)
+ break;
+ } // case psEqual
+
+ case psValueInDQuotes:
+ {
+ while (i < a_Size)
+ {
+ if (a_Data[i] == '\"')
+ {
+ m_CurrentValue.append(a_Data + Last, i - Last);
+ (*this)[m_CurrentKey] = m_CurrentValue;
+ m_CurrentKey.clear();
+ m_CurrentValue.clear();
+ m_State = psAfterValue;
+ i++;
+ Last = i;
+ break;
+ }
+ i++;
+ } // while (i < a_Size)
+ if (i == a_Size)
+ {
+ m_CurrentValue.append(a_Data + Last, a_Size - Last);
+ }
+ break;
+ } // case psValueInDQuotes
+
+ case psValueInSQuotes:
+ {
+ while (i < a_Size)
+ {
+ if (a_Data[i] == '\'')
+ {
+ m_CurrentValue.append(a_Data + Last, i - Last);
+ (*this)[m_CurrentKey] = m_CurrentValue;
+ m_CurrentKey.clear();
+ m_CurrentValue.clear();
+ m_State = psAfterValue;
+ i++;
+ Last = i;
+ break;
+ }
+ i++;
+ } // while (i < a_Size)
+ if (i == a_Size)
+ {
+ m_CurrentValue.append(a_Data + Last, a_Size - Last);
+ }
+ break;
+ } // case psValueInSQuotes
+
+ case psValueRaw:
+ {
+ while (i < a_Size)
+ {
+ if (a_Data[i] == ';')
+ {
+ m_CurrentValue.append(a_Data + Last, i - Last);
+ (*this)[m_CurrentKey] = m_CurrentValue;
+ m_CurrentKey.clear();
+ m_CurrentValue.clear();
+ m_State = psKeySpace;
+ i++;
+ Last = i;
+ break;
+ }
+ i++;
+ }
+ if (i == a_Size)
+ {
+ m_CurrentValue.append(a_Data + Last, a_Size - Last);
+ }
+ break;
+ } // case psValueRaw
+
+ case psAfterValue:
+ {
+ // Between the closing DQuote or SQuote and the terminating semicolon
+ while (i < a_Size)
+ {
+ if (a_Data[i] == ';')
+ {
+ m_State = psKeySpace;
+ i++;
+ Last = i;
+ break;
+ }
+ else if (a_Data[i] < ' ')
+ {
+ i++;
+ continue;
+ }
+ m_State = psInvalid;
+ return;
+ } // while (i < a_Size)
+ break;
+ }
+ } // switch (m_State)
+ } // for i - a_Data[]
+}
+
+
+
+
+
+bool cNameValueParser::Finish(void)
+{
+ switch (m_State)
+ {
+ case psInvalid:
+ {
+ return false;
+ }
+ case psFinished:
+ {
+ return true;
+ }
+ case psKey:
+ case psEqualSpace:
+ case psEqual:
+ {
+ if ((m_AllowsKeyOnly) && !m_CurrentKey.empty())
+ {
+ (*this)[m_CurrentKey] = "";
+ m_State = psFinished;
+ return true;
+ }
+ m_State = psInvalid;
+ return false;
+ }
+ case psValueRaw:
+ {
+ (*this)[m_CurrentKey] = m_CurrentValue;
+ m_State = psFinished;
+ return true;
+ }
+ case psValueInDQuotes:
+ case psValueInSQuotes:
+ {
+ // Missing the terminating quotes, this is an error
+ m_State = psInvalid;
+ return false;
+ }
+ case psKeySpace:
+ case psAfterValue:
+ {
+ m_State = psFinished;
+ return true;
+ }
+ }
+ ASSERT(!"Unhandled parser state!");
+ return false;
+}
+
+
+
+
diff --git a/src/HTTPServer/NameValueParser.h b/src/HTTPServer/NameValueParser.h
new file mode 100644
index 000000000..07dc0b942
--- /dev/null
+++ b/src/HTTPServer/NameValueParser.h
@@ -0,0 +1,70 @@
+
+// NameValueParser.h
+
+// Declares the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap
+
+
+
+
+
+#pragma once
+
+
+
+
+
+class cNameValueParser :
+ public std::map<AString, AString>
+{
+public:
+ /// Creates an empty parser
+ cNameValueParser(bool a_AllowsKeyOnly = true);
+
+ /// Creates an empty parser, then parses the data given. Doesn't call Finish(), so more data can be parsed later
+ cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly = true);
+
+ /// Parses the data given
+ void Parse(const char * a_Data, int a_Size);
+
+ /// Notifies the parser that no more data will be coming. Returns true if the parser state is valid
+ bool Finish(void);
+
+ /// Returns true if the data parsed so far was valid
+ bool IsValid(void) const { return (m_State != psInvalid); }
+
+ /// Returns true if the parser expects no more data
+ bool IsFinished(void) const { return ((m_State == psInvalid) || (m_State == psFinished)); }
+
+protected:
+ enum eState
+ {
+ psKeySpace, ///< Parsing the space in front of the next key
+ psKey, ///< Currently adding more chars to the key in m_CurrentKey
+ psEqualSpace, ///< Space after m_CurrentKey
+ psEqual, ///< Just parsed the = sign after a name
+ psValueInSQuotes, ///< Just parsed a Single-quote sign after the Equal sign
+ psValueInDQuotes, ///< Just parsed a Double-quote sign after the Equal sign
+ psValueRaw, ///< Just parsed a raw value without a quote
+ psAfterValue, ///< Just finished parsing the value, waiting for semicolon or data end
+ psInvalid, ///< The parser has encountered an invalid input; further parsing is skipped
+ psFinished, ///< The parser has already been instructed to finish and doesn't expect any more data
+ } ;
+
+ /// The current state of the parser
+ eState m_State;
+
+ /// If true, the parser will accept keys without an equal sign and the value
+ bool m_AllowsKeyOnly;
+
+ /// Buffer for the current Key
+ AString m_CurrentKey;
+
+ /// Buffer for the current Value;
+ AString m_CurrentValue;
+
+
+} ;
+
+
+
+
diff --git a/src/Inventory.cpp b/src/Inventory.cpp
new file mode 100644
index 000000000..b9c7f4aaf
--- /dev/null
+++ b/src/Inventory.cpp
@@ -0,0 +1,682 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Inventory.h"
+#include "Entities/Player.h"
+#include "ClientHandle.h"
+#include "UI/Window.h"
+#include "Item.h"
+#include "Root.h"
+#include "World.h"
+
+#include "jsoncpp/include/json/json.h"
+
+#include "Items/ItemHandler.h"
+
+
+
+
+
+cInventory::cInventory(cPlayer & a_Owner) :
+ m_ArmorSlots (1, 4), // 1 x 4 slots
+ m_InventorySlots(9, 3), // 9 x 3 slots
+ m_HotbarSlots (9, 1), // 9 x 1 slots
+ m_Owner(a_Owner)
+{
+ // Ask each ItemGrid to report changes to us:
+ m_ArmorSlots.AddListener(*this);
+ m_InventorySlots.AddListener(*this);
+ m_HotbarSlots.AddListener(*this);
+
+ SetEquippedSlotNum(0);
+}
+
+
+
+
+
+void cInventory::Clear(void)
+{
+ m_ArmorSlots.Clear();
+ m_InventorySlots.Clear();
+ m_HotbarSlots.Clear();
+}
+
+
+
+
+
+int cInventory::HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots)
+{
+ return HowManyCanFit(a_ItemStack, 0, invNumSlots - 1, a_ConsiderEmptySlots);
+}
+
+
+
+
+
+int cInventory::HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots)
+{
+ if ((a_BeginSlotNum < 0) || (a_BeginSlotNum >= invNumSlots))
+ {
+ LOGWARNING("%s: Bad BeginSlotNum, got %d, there are %d slots; correcting to 0.", __FUNCTION__, a_BeginSlotNum, invNumSlots - 1);
+ a_BeginSlotNum = 0;
+ }
+ if ((a_EndSlotNum < 0) || (a_EndSlotNum >= invNumSlots))
+ {
+ LOGWARNING("%s: Bad EndSlotNum, got %d, there are %d slots; correcting to %d.", __FUNCTION__, a_BeginSlotNum, invNumSlots, invNumSlots - 1);
+ a_EndSlotNum = invNumSlots - 1;
+ }
+ if (a_BeginSlotNum > a_EndSlotNum)
+ {
+ std::swap(a_BeginSlotNum, a_EndSlotNum);
+ }
+
+ char NumLeft = a_ItemStack.m_ItemCount;
+ int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize();
+ for (int i = a_BeginSlotNum; i <= a_EndSlotNum; i++)
+ {
+ const cItem & Slot = GetSlot(i);
+ if (Slot.IsEmpty())
+ {
+ NumLeft -= MaxStack;
+ }
+ else if (Slot.IsStackableWith(a_ItemStack))
+ {
+ NumLeft -= MaxStack - Slot.m_ItemCount;
+ }
+ if (NumLeft <= 0)
+ {
+ // All items fit
+ return a_ItemStack.m_ItemCount;
+ }
+ } // for i - m_Slots[]
+ return a_ItemStack.m_ItemCount - NumLeft;
+}
+
+
+
+
+
+int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst)
+{
+ cItem ToAdd(a_Item);
+ int res = 0;
+ if (ItemCategory::IsArmor(a_Item.m_ItemType))
+ {
+ res = m_ArmorSlots.AddItem(ToAdd, a_AllowNewStacks);
+ ToAdd.m_ItemCount -= res;
+ if (ToAdd.m_ItemCount == 0)
+ {
+ return res;
+ }
+ }
+
+ res += m_HotbarSlots.AddItem(ToAdd, a_AllowNewStacks, a_tryToFillEquippedFirst ? m_EquippedSlotNum : -1);
+ ToAdd.m_ItemCount = a_Item.m_ItemCount - res;
+ if (ToAdd.m_ItemCount == 0)
+ {
+ return res;
+ }
+
+ res += m_InventorySlots.AddItem(ToAdd, a_AllowNewStacks);
+ return res;
+}
+
+
+
+
+
+int cInventory::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst)
+{
+ int TotalAdded = 0;
+ for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();)
+ {
+ int NumAdded = AddItem(*itr, a_AllowNewStacks, a_tryToFillEquippedFirst);
+ if (itr->m_ItemCount == NumAdded)
+ {
+ itr = a_ItemStackList.erase(itr);
+ }
+ else
+ {
+ itr->m_ItemCount -= NumAdded;
+ ++itr;
+ }
+ TotalAdded += NumAdded;
+ }
+ return TotalAdded;
+}
+
+
+
+
+
+bool cInventory::RemoveOneEquippedItem(void)
+{
+ if (m_HotbarSlots.GetSlot(m_EquippedSlotNum).IsEmpty())
+ {
+ return false;
+ }
+
+ m_HotbarSlots.ChangeSlotCount(m_EquippedSlotNum, -1);
+ return true;
+}
+
+
+
+
+
+int cInventory::HowManyItems(const cItem & a_Item)
+{
+ return
+ m_ArmorSlots.HowManyItems(a_Item) +
+ m_InventorySlots.HowManyItems(a_Item) +
+ m_HotbarSlots.HowManyItems(a_Item);
+}
+
+
+
+
+
+bool cInventory::HasItems(const cItem & a_ItemStack)
+{
+ int CurrentlyHave = HowManyItems(a_ItemStack);
+ return (CurrentlyHave >= a_ItemStack.m_ItemCount);
+}
+
+
+
+
+
+void cInventory::SetSlot(int a_SlotNum, const cItem & a_Item)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots))
+ {
+ LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Ignoring.", __FUNCTION__, a_SlotNum, invNumSlots - 1);
+ return;
+ }
+
+ int GridSlotNum = 0;
+ cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum);
+ if (Grid == NULL)
+ {
+ LOGWARNING("%s(%d): requesting an invalid itemgrid. Ignoring.", __FUNCTION__, a_SlotNum);
+ return;
+ }
+ Grid->SetSlot(GridSlotNum, a_Item);
+}
+
+
+
+
+
+void cInventory::SetArmorSlot(int a_ArmorSlotNum, const cItem & a_Item)
+{
+ m_ArmorSlots.SetSlot(a_ArmorSlotNum, a_Item);
+}
+
+
+
+
+
+void cInventory::SetInventorySlot(int a_InventorySlotNum, const cItem & a_Item)
+{
+ m_InventorySlots.SetSlot(a_InventorySlotNum, a_Item);
+}
+
+
+
+
+
+void cInventory::SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item)
+{
+ m_HotbarSlots.SetSlot(a_HotBarSlotNum, a_Item);
+}
+
+
+
+
+
+const cItem & cInventory::GetSlot(int a_SlotNum) const
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots))
+ {
+ LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first inventory slot instead.", __FUNCTION__, a_SlotNum, invNumSlots - 1);
+ return m_InventorySlots.GetSlot(0);
+ }
+ int GridSlotNum = 0;
+ const cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum);
+ if (Grid == NULL)
+ {
+ // Something went wrong, but we don't know what. We must return a value, so return the first inventory slot
+ LOGWARNING("%s(%d): requesting an invalid ItemGrid, returning the first inventory slot instead.", __FUNCTION__, a_SlotNum);
+ return m_InventorySlots.GetSlot(0);
+ }
+ return Grid->GetSlot(GridSlotNum);
+}
+
+
+
+
+
+const cItem & cInventory::GetArmorSlot(int a_ArmorSlotNum) const
+{
+ if ((a_ArmorSlotNum < 0) || (a_ArmorSlotNum >= invArmorCount))
+ {
+ LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_ArmorSlotNum, invArmorCount - 1);
+ return m_ArmorSlots.GetSlot(0);
+ }
+ return m_ArmorSlots.GetSlot(a_ArmorSlotNum);
+}
+
+
+
+
+
+const cItem & cInventory::GetInventorySlot(int a_InventorySlotNum) const
+{
+ if ((a_InventorySlotNum < 0) || (a_InventorySlotNum >= invInventoryCount))
+ {
+ LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_InventorySlotNum, invInventoryCount - 1);
+ return m_InventorySlots.GetSlot(0);
+ }
+ return m_InventorySlots.GetSlot(a_InventorySlotNum);
+}
+
+
+
+
+
+const cItem & cInventory::GetHotbarSlot(int a_SlotNum) const
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= invHotbarCount))
+ {
+ LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_SlotNum, invHotbarCount - 1);
+ return m_HotbarSlots.GetSlot(0);
+ }
+ return m_HotbarSlots.GetSlot(a_SlotNum);
+}
+
+
+
+
+
+const cItem & cInventory::GetEquippedItem(void) const
+{
+ return GetHotbarSlot(m_EquippedSlotNum);
+}
+
+
+
+
+
+void cInventory::SetEquippedSlotNum(int a_SlotNum)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= invHotbarCount))
+ {
+ LOGWARNING("%s: requesting invalid slot index: %d out of %d. Setting 0 instead.", __FUNCTION__, a_SlotNum, invHotbarCount - 1);
+ m_EquippedSlotNum = 0;
+ }
+ else
+ {
+ m_EquippedSlotNum = a_SlotNum;
+ }
+}
+
+
+
+
+
+bool cInventory::DamageEquippedItem(short a_Amount)
+{
+ return DamageItem(invHotbarOffset + m_EquippedSlotNum, a_Amount);
+}
+
+
+
+
+
+int cInventory::ChangeSlotCount(int a_SlotNum, int a_AddToCount)
+{
+ int GridSlotNum = 0;
+ cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum);
+ if (Grid == NULL)
+ {
+ LOGWARNING("%s: invalid slot number, expected 0 .. %d, got %d; ignoring", __FUNCTION__, invNumSlots, a_SlotNum);
+ return -1;
+ }
+ return Grid->ChangeSlotCount(GridSlotNum, a_AddToCount);
+}
+
+
+
+
+
+bool cInventory::DamageItem(int a_SlotNum, short a_Amount)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots))
+ {
+ LOGWARNING("%s: requesting an invalid slot index: %d out of %d", __FUNCTION__, a_SlotNum, invNumSlots - 1);
+ return false;
+ }
+
+ int GridSlotNum = 0;
+ cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum);
+ if (Grid == NULL)
+ {
+ LOGWARNING("%s(%d, %d): requesting an invalid grid, ignoring.", __FUNCTION__, a_SlotNum, a_Amount);
+ return false;
+ }
+ if (!Grid->DamageItem(GridSlotNum, a_Amount))
+ {
+ // The item has been damaged, but did not break yet
+ return false;
+ }
+
+ // The item has broken, remove it:
+ Grid->EmptySlot(GridSlotNum);
+ return true;
+}
+
+
+
+
+
+void cInventory::CopyToItems(cItems & a_Items)
+{
+ m_ArmorSlots.CopyToItems(a_Items);
+ m_InventorySlots.CopyToItems(a_Items);
+ m_HotbarSlots.CopyToItems(a_Items);
+}
+
+
+
+
+
+void cInventory::SendSlot(int a_SlotNum)
+{
+ cItem Item(GetSlot(a_SlotNum));
+ if (Item.IsEmpty())
+ {
+ // Sanitize items that are not completely empty (ie. count == 0, but type != empty)
+ Item.Empty();
+ }
+ m_Owner.GetClientHandle()->SendInventorySlot(0, a_SlotNum + 5, Item); // Slots in the client are numbered "+ 5" because of crafting grid and result
+}
+
+
+
+
+
+/*
+int cInventory::MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int a_BeginSlot, int a_EndSlot)
+{
+ int res = 0;
+ for (int i = a_BeginSlot; i <= a_EndSlot; i++)
+ {
+ if (
+ m_Slots[i].IsEmpty() ||
+ ((m_Slots[i].m_ItemType == a_ItemType) && (m_Slots[i].m_ItemDamage == a_ItemDamage))
+ )
+ {
+ int MaxCount = ItemHandler(a_ItemType)->GetMaxStackSize();
+ ASSERT(m_Slots[i].m_ItemCount <= MaxCount);
+ int NumToMove = std::min(a_Count, MaxCount - m_Slots[i].m_ItemCount);
+ m_Slots[i].m_ItemCount += NumToMove;
+ m_Slots[i].m_ItemDamage = a_ItemDamage;
+ m_Slots[i].m_ItemType = a_ItemType;
+ SendSlot(i);
+ res += NumToMove;
+ a_Count -= NumToMove;
+ if (a_Count <= 0)
+ {
+ // No more items to distribute
+ return res;
+ }
+ }
+ } // for i - m_Slots[]
+ // No more space to distribute to
+ return res;
+}
+*/
+
+
+
+
+
+int cInventory::ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum)
+{
+ switch (a_ArmorSlotNum)
+ {
+ case 0: return 4; // Helmet
+ case 1: return 3; // Chestplate
+ case 2: return 2; // Leggings
+ case 3: return 1; // Boots
+ }
+ LOGWARN("%s: invalid armor slot number: %d", __FUNCTION__, a_ArmorSlotNum);
+ return 0;
+}
+
+
+
+
+
+#if 0
+bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode /* = 0 */ )
+{
+ // Fill already present stacks
+ if( a_Mode < 2 )
+ {
+ int MaxStackSize = cItemHandler::GetItemHandler(a_Item.m_ItemType)->GetMaxStackSize();
+ for(int i = 0; i < a_Size; i++)
+ {
+ if( m_Slots[i + a_Offset].m_ItemType == a_Item.m_ItemType && m_Slots[i + a_Offset].m_ItemCount < MaxStackSize && m_Slots[i + a_Offset].m_ItemDamage == a_Item.m_ItemDamage )
+ {
+ int NumFree = MaxStackSize - m_Slots[i + a_Offset].m_ItemCount;
+ if( NumFree >= a_Item.m_ItemCount )
+ {
+
+ //printf("1. Adding %i items ( free: %i )\n", a_Item.m_ItemCount, NumFree );
+ m_Slots[i + a_Offset].m_ItemCount += a_Item.m_ItemCount;
+ a_Item.m_ItemCount = 0;
+ a_bChangedSlots[i + a_Offset] = true;
+ break;
+ }
+ else
+ {
+ //printf("2. Adding %i items\n", NumFree );
+ m_Slots[i + a_Offset].m_ItemCount += (char)NumFree;
+ a_Item.m_ItemCount -= (char)NumFree;
+ a_bChangedSlots[i + a_Offset] = true;
+ }
+ }
+ }
+ }
+
+ if( a_Mode > 0 )
+ {
+ // If we got more left, find first empty slot
+ for(int i = 0; i < a_Size && a_Item.m_ItemCount > 0; i++)
+ {
+ if( m_Slots[i + a_Offset].m_ItemType == -1 )
+ {
+ m_Slots[i + a_Offset] = a_Item;
+ a_Item.m_ItemCount = 0;
+ a_bChangedSlots[i + a_Offset] = true;
+ }
+ }
+ }
+
+ return true;
+}
+#endif
+
+
+
+
+
+void cInventory::SaveToJson(Json::Value & a_Value)
+{
+ // The JSON originally included the 4 crafting slots and the result, so we have to put empty items there, too:
+ cItem EmptyItem;
+ Json::Value EmptyItemJson;
+ EmptyItem.GetJson(EmptyItemJson);
+ for (int i = 0; i < 5; i++)
+ {
+ a_Value.append(EmptyItemJson);
+ }
+
+ // The 4 armor slots follow:
+ for (int i = 0; i < invArmorCount; i++)
+ {
+ Json::Value JSON_Item;
+ m_ArmorSlots.GetSlot(i).GetJson(JSON_Item);
+ a_Value.append(JSON_Item);
+ }
+
+ // Next comes the main inventory:
+ for (int i = 0; i < invInventoryCount; i++)
+ {
+ Json::Value JSON_Item;
+ m_InventorySlots.GetSlot(i).GetJson(JSON_Item);
+ a_Value.append(JSON_Item);
+ }
+
+ // The hotbar is the last:
+ for (int i = 0; i < invHotbarCount; i++)
+ {
+ Json::Value JSON_Item;
+ m_HotbarSlots.GetSlot(i).GetJson(JSON_Item);
+ a_Value.append(JSON_Item);
+ }
+}
+
+
+
+
+
+bool cInventory::LoadFromJson(Json::Value & a_Value)
+{
+ int SlotIdx = 0;
+
+ for (Json::Value::iterator itr = a_Value.begin(); itr != a_Value.end(); ++itr, SlotIdx++)
+ {
+ cItem Item;
+ Item.FromJson(*itr);
+
+ // The JSON originally included the 4 crafting slots and the result slot, so we need to skip the first 5 items:
+ if (SlotIdx < 5)
+ {
+ continue;
+ }
+
+ // If we loaded all the slots, stop now, even if the JSON has more:
+ if (SlotIdx - 5 >= invNumSlots)
+ {
+ break;
+ }
+
+ int GridSlotNum = 0;
+ cItemGrid * Grid = GetGridForSlotNum(SlotIdx - 5, GridSlotNum);
+ ASSERT(Grid != NULL);
+ Grid->SetSlot(GridSlotNum, Item);
+ } // for itr - a_Value[]
+ return true;
+}
+
+
+
+
+
+const cItemGrid * cInventory::GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const
+{
+ ASSERT(a_SlotNum >= 0);
+
+ if (a_SlotNum < invArmorCount)
+ {
+ a_GridSlotNum = a_SlotNum;
+ return &m_ArmorSlots;
+ }
+ a_SlotNum -= invArmorCount;
+ if (a_SlotNum < invInventoryCount)
+ {
+ a_GridSlotNum = a_SlotNum;
+ return &m_InventorySlots;
+ }
+ a_GridSlotNum = a_SlotNum - invInventoryCount;
+ return &m_HotbarSlots;
+}
+
+
+
+
+
+cItemGrid * cInventory::GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum)
+{
+ ASSERT(a_SlotNum >= 0);
+
+ if (a_SlotNum < invArmorCount)
+ {
+ a_GridSlotNum = a_SlotNum;
+ return &m_ArmorSlots;
+ }
+ a_SlotNum -= invArmorCount;
+ if (a_SlotNum < invInventoryCount)
+ {
+ a_GridSlotNum = a_SlotNum;
+ return &m_InventorySlots;
+ }
+ a_GridSlotNum = a_SlotNum - invInventoryCount;
+ return &m_HotbarSlots;
+}
+
+
+
+
+
+void cInventory::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ // Send the neccessary updates to whoever needs them
+
+ if (m_Owner.IsDestroyed())
+ {
+ // Owner is not (yet) valid, skip for now
+ return;
+ }
+
+ // Armor update needs broadcast to other players:
+ cWorld * World = m_Owner.GetWorld();
+ if ((a_ItemGrid == &m_ArmorSlots) && (World != NULL))
+ {
+ World->BroadcastEntityEquipment(
+ m_Owner, ArmorSlotNumToEntityEquipmentID(a_SlotNum),
+ m_ArmorSlots.GetSlot(a_SlotNum), m_Owner.GetClientHandle()
+ );
+ }
+
+ // Convert the grid-local a_SlotNum to our global SlotNum:
+ int Base = 0;
+ if (a_ItemGrid == &m_ArmorSlots)
+ {
+ Base = invArmorOffset;
+ }
+ else if (a_ItemGrid == &m_InventorySlots)
+ {
+ Base = invInventoryOffset;
+ }
+ else if (a_ItemGrid == &m_HotbarSlots)
+ {
+ Base = invHotbarOffset;
+ }
+ else
+ {
+ ASSERT(!"Unknown ItemGrid calling OnSlotChanged()");
+ return;
+ }
+
+ SendSlot(Base + a_SlotNum);
+}
+
+
+
+
diff --git a/src/Inventory.h b/src/Inventory.h
new file mode 100644
index 000000000..3c6a19de8
--- /dev/null
+++ b/src/Inventory.h
@@ -0,0 +1,182 @@
+
+#pragma once
+
+#include "ItemGrid.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+};
+
+class cClientHandle;
+class cPlayer;
+
+
+
+
+// tolua_begin
+
+/** This class represents the player's inventory
+The slots are divided into three areas:
+- armor slots (1 x 4)
+- inventory slots (9 x 3)
+- hotbar slots (9 x 1)
+The generic GetSlot(), SetSlot() and HowManyCanFit() functions take the index of the slots,
+as if armor slots, inventory slots and then hotbar slots were put one after another.
+You can use the invArmorOffset, invInventoryOffset and invHotbarOffset constants.
+*/
+
+class cInventory :
+ public cItemGrid::cListener
+{
+public:
+
+ // Counts and offsets to individual parts of the inventory, as used by GetSlot() / SetSlot() / HowManyCanFit():
+ enum
+ {
+ invArmorCount = 4,
+ invInventoryCount = 9 * 3,
+ invHotbarCount = 9,
+
+ invArmorOffset = 0,
+ invInventoryOffset = invArmorOffset + invArmorCount,
+ invHotbarOffset = invInventoryOffset + invInventoryCount,
+ invNumSlots = invHotbarOffset + invHotbarCount
+ } ;
+
+ // tolua_end
+
+ cInventory(cPlayer & a_Owner);
+
+ // tolua_begin
+
+ /// Removes all items from the entire inventory
+ void Clear(void);
+
+ /// Returns number of items out of a_ItemStack that can fit in the storage
+ int HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots);
+
+ /// Returns how many items of the specified type would fit into the slot range specified
+ int HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots);
+
+ /** Adds as many items out of a_ItemStack as can fit.
+ If a_AllowNewStacks is set to false, only existing stacks can be topped up;
+ if a_AllowNewStacks is set to true, empty slots can be used for the rest.
+ If a_tryToFillEquippedFirst is set to true, the currently equipped slot will be used first (if empty or
+ compatible with added items)
+ if a_tryToFillEquippedFirst is set to false, the regular order applies.
+ Returns the number of items that fit.
+ */
+ int AddItem(const cItem & a_ItemStack, bool a_AllowNewStacks = true, bool a_tryToFillEquippedFirst = false);
+
+ /** Same as AddItem, but works on an entire list of item stacks.
+ The a_ItemStackList is modified to reflect the leftover items.
+ If a_AllowNewStacks is set to false, only existing stacks can be topped up;
+ if a_AllowNewStacks is set to true, empty slots can be used for the rest.
+ If a_tryToFillEquippedFirst is set to true, the currently equipped slot will be used first (if empty or
+ compatible with added items)
+ if a_tryToFillEquippedFirst is set to false, the regular order applies.
+ Returns the total number of items that fit.
+ */
+ int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst);
+
+ /// Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed
+ bool RemoveOneEquippedItem(void);
+
+ /// Returns the number of items of type a_Item that are stored
+ int HowManyItems(const cItem & a_Item);
+
+ /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack
+ bool HasItems(const cItem & a_ItemStack);
+
+ /// Returns the cItemGrid object representing the armor slots
+ cItemGrid & GetArmorGrid(void) { return m_ArmorSlots; }
+
+ /// Returns the cItemGrid object representing the main inventory slots
+ cItemGrid & GetInventoryGrid(void) { return m_InventorySlots; }
+
+ /// Returns the cItemGrid object representing the hotbar slots
+ cItemGrid & GetHotbarGrid(void) { return m_HotbarSlots; }
+
+ /// Returns the player associated with this inventory
+ cPlayer & GetOwner(void) { return m_Owner; }
+
+ /// Copies the non-empty slots into a_ItemStacks; preserves the original a_Items contents
+ void CopyToItems(cItems & a_Items);
+
+ // tolua_end
+
+ /// Returns the player associated with this inventory (const version)
+ const cPlayer & GetOwner(void) const { return m_Owner; }
+
+ // tolua_begin
+
+ const cItem & GetSlot(int a_SlotNum) const;
+ const cItem & GetArmorSlot(int a_ArmorSlotNum) const;
+ const cItem & GetInventorySlot(int a_InventorySlotNum) const;
+ const cItem & GetHotbarSlot(int a_HotBarSlotNum) const;
+ const cItem & GetEquippedItem(void) const;
+ void SetSlot(int a_SlotNum, const cItem & a_Item);
+ void SetArmorSlot(int a_ArmorSlotNum, const cItem & a_Item);
+ void SetInventorySlot(int a_InventorySlotNum, const cItem & a_Item);
+ void SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item);
+
+ void SetEquippedSlotNum(int a_SlotNum);
+ int GetEquippedSlotNum(void) { return m_EquippedSlotNum; }
+
+ /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot.
+ If the slot is empty, ignores the call.
+ Returns the new count, or -1 if the slot number is invalid.
+ */
+ int ChangeSlotCount(int a_SlotNum, int a_AddToCount);
+
+ /// Adds the specified damage to the specified item; deletes the item and returns true if the item broke.
+ bool DamageItem(int a_SlotNum, short a_Amount);
+
+ /// Adds the specified damage to the currently held item; deletes the item and returns true if the item broke.
+ bool DamageEquippedItem(short a_Amount = 1);
+
+ const cItem & GetEquippedHelmet (void) const { return m_ArmorSlots.GetSlot(0); }
+ const cItem & GetEquippedChestplate(void) const { return m_ArmorSlots.GetSlot(1); }
+ const cItem & GetEquippedLeggings (void) const { return m_ArmorSlots.GetSlot(2); }
+ const cItem & GetEquippedBoots (void) const { return m_ArmorSlots.GetSlot(3); }
+
+ // tolua_end
+
+ /// Sends the slot contents to the owner
+ void SendSlot(int a_SlotNum);
+
+ /// Converts an armor slot number into the ID for the EntityEquipment packet
+ static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum);
+
+ void SaveToJson(Json::Value & a_Value);
+ bool LoadFromJson(Json::Value & a_Value);
+
+protected:
+ bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0 );
+
+ cItemGrid m_ArmorSlots;
+ cItemGrid m_InventorySlots;
+ cItemGrid m_HotbarSlots;
+
+ int m_EquippedSlotNum;
+
+ cPlayer & m_Owner;
+
+ /// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum
+ const cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const;
+
+ /// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum
+ cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum);
+
+ // cItemGrid::cListener override:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+}; // tolua_export
+
+
+
+
diff --git a/src/Item.cpp b/src/Item.cpp
new file mode 100644
index 000000000..37277e8c8
--- /dev/null
+++ b/src/Item.cpp
@@ -0,0 +1,261 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Item.h"
+#include "jsoncpp/include/json/json.h"
+#include "Items/ItemHandler.h"
+
+
+
+
+
+cItem cItem::CopyOne(void) const
+{
+ cItem res(*this);
+ res.m_ItemCount = 1;
+ return res;
+}
+
+
+
+
+
+cItem & cItem::AddCount(char a_AmountToAdd)
+{
+ m_ItemCount += a_AmountToAdd;
+ if (m_ItemCount <= 0)
+ {
+ Empty();
+ }
+ return *this;
+}
+
+
+
+
+
+short cItem::GetMaxDamage(void) const
+{
+ switch (m_ItemType)
+ {
+ case E_ITEM_BOW: return 384;
+ case E_ITEM_DIAMOND_AXE: return 1563;
+ case E_ITEM_DIAMOND_HOE: return 1563;
+ case E_ITEM_DIAMOND_PICKAXE: return 1563;
+ case E_ITEM_DIAMOND_SHOVEL: return 1563;
+ case E_ITEM_DIAMOND_SWORD: return 1563;
+ case E_ITEM_FLINT_AND_STEEL: return 65;
+ case E_ITEM_GOLD_AXE: return 32;
+ case E_ITEM_GOLD_HOE: return 32;
+ case E_ITEM_GOLD_PICKAXE: return 32;
+ case E_ITEM_GOLD_SHOVEL: return 32;
+ case E_ITEM_GOLD_SWORD: return 32;
+ case E_ITEM_IRON_AXE: return 251;
+ case E_ITEM_IRON_HOE: return 251;
+ case E_ITEM_IRON_PICKAXE: return 251;
+ case E_ITEM_IRON_SHOVEL: return 251;
+ case E_ITEM_IRON_SWORD: return 251;
+ case E_ITEM_SHEARS: return 251;
+ case E_ITEM_STONE_AXE: return 132;
+ case E_ITEM_STONE_HOE: return 132;
+ case E_ITEM_STONE_PICKAXE: return 132;
+ case E_ITEM_STONE_SHOVEL: return 132;
+ case E_ITEM_STONE_SWORD: return 132;
+ case E_ITEM_WOODEN_AXE: return 60;
+ case E_ITEM_WOODEN_HOE: return 60;
+ case E_ITEM_WOODEN_PICKAXE: return 60;
+ case E_ITEM_WOODEN_SHOVEL: return 60;
+ case E_ITEM_WOODEN_SWORD: return 60;
+ }
+ return 0;
+}
+
+
+
+
+
+bool cItem::DamageItem(short a_Amount)
+{
+ short MaxDamage = GetMaxDamage();
+ if (MaxDamage == 0)
+ {
+ // Item doesn't have damage
+ return false;
+ }
+
+ m_ItemDamage += a_Amount;
+ return (m_ItemDamage >= MaxDamage);
+}
+
+
+
+
+
+bool cItem::IsStackableWith(const cItem & a_OtherStack) const
+{
+ if (a_OtherStack.m_ItemType != m_ItemType)
+ {
+ return false;
+ }
+ if (a_OtherStack.m_ItemDamage != m_ItemDamage)
+ {
+ return false;
+ }
+ if (a_OtherStack.m_Enchantments != m_Enchantments)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cItem::IsFullStack(void) const
+{
+ return (m_ItemCount >= ItemHandler(m_ItemType)->GetMaxStackSize());
+}
+
+
+
+
+
+char cItem::GetMaxStackSize(void) const
+{
+ return ItemHandler(m_ItemType)->GetMaxStackSize();
+}
+
+
+
+
+
+/// Returns the cItemHandler responsible for this item type
+cItemHandler * cItem::GetHandler(void) const
+{
+ return ItemHandler(m_ItemType);
+}
+
+
+
+
+
+void cItem::GetJson(Json::Value & a_OutValue) const
+{
+ a_OutValue["ID"] = m_ItemType;
+ if (m_ItemType > 0)
+ {
+ a_OutValue["Count"] = m_ItemCount;
+ a_OutValue["Health"] = m_ItemDamage;
+ AString Enchantments(m_Enchantments.ToString());
+ if (!Enchantments.empty())
+ {
+ a_OutValue["ench"] = Enchantments;
+ }
+ }
+}
+
+
+
+
+
+void cItem::FromJson(const Json::Value & a_Value)
+{
+ m_ItemType = (ENUM_ITEM_ID)a_Value.get("ID", -1 ).asInt();
+ if (m_ItemType > 0)
+ {
+ m_ItemCount = (char)a_Value.get("Count", -1 ).asInt();
+ m_ItemDamage = (short)a_Value.get("Health", -1 ).asInt();
+ m_Enchantments.Clear();
+ m_Enchantments.AddFromString(a_Value.get("ench", "").asString());
+ }
+}
+
+
+
+
+
+bool cItem::IsEnchantable(short item)
+{
+ if ((item >= 256) && (item <= 259))
+ return true;
+ if ((item >= 267) && (item <= 279))
+ return true;
+ if ((item >= 283) && (item <= 286))
+ return true;
+ if ((item >= 290) && (item <= 294))
+ return true;
+ if ((item >= 298) && (item <= 317))
+ return true;
+ if ((item >= 290) && (item <= 294))
+ return true;
+
+ if ((item == 346) || (item == 359) || (item == 261))
+ return true;
+
+ return false;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cItems:
+
+cItem * cItems::Get(int a_Idx)
+{
+ if ((a_Idx < 0) || (a_Idx >= (int)size()))
+ {
+ LOGWARNING("cItems: Attempt to get an out-of-bounds item at index %d; there are currently %d items. Returning a nil.", a_Idx, size());
+ return NULL;
+ }
+ return &at(a_Idx);
+}
+
+
+
+
+
+void cItems::Set(int a_Idx, const cItem & a_Item)
+{
+ if ((a_Idx < 0) || (a_Idx >= (int)size()))
+ {
+ LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently %d items. Not setting.", a_Idx, size());
+ return;
+ }
+ at(a_Idx) = a_Item;
+}
+
+
+
+
+
+void cItems::Delete(int a_Idx)
+{
+ if ((a_Idx < 0) || (a_Idx >= (int)size()))
+ {
+ LOGWARNING("cItems: Attempt to delete an item at an out-of-bounds index %d; there are currently %d items. Ignoring.", a_Idx, size());
+ return;
+ }
+ erase(begin() + a_Idx);
+}
+
+
+
+
+
+void cItems::Set(int a_Idx, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage)
+{
+ if ((a_Idx < 0) || (a_Idx >= (int)size()))
+ {
+ LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently %d items. Not setting.", a_Idx, size());
+ return;
+ }
+ at(a_Idx) = cItem(a_ItemType, a_ItemCount, a_ItemDamage);
+}
+
+
+
+
diff --git a/src/Item.h b/src/Item.h
new file mode 100644
index 000000000..c60d0542c
--- /dev/null
+++ b/src/Item.h
@@ -0,0 +1,210 @@
+
+// Item.h
+
+// Declares the cItem class representing an item (in the inventory sense)
+
+
+
+
+
+#pragma once
+
+#include "Defines.h"
+#include "Enchantments.h"
+
+
+
+
+
+// fwd:
+class cItemHandler;
+
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
+// tolua_begin
+class cItem
+{
+public:
+ /// Creates an empty item
+ cItem(void) :
+ m_ItemType(E_ITEM_EMPTY),
+ m_ItemCount(0),
+ m_ItemDamage(0)
+ {
+ }
+
+
+ /// Creates an item of the specified type, by default 1 piece with no damage and no enchantments
+ cItem(
+ short a_ItemType,
+ char a_ItemCount = 1,
+ short a_ItemDamage = 0,
+ const AString & a_Enchantments = ""
+ ) :
+ m_ItemType (a_ItemType),
+ m_ItemCount (a_ItemCount),
+ m_ItemDamage (a_ItemDamage),
+ m_Enchantments(a_Enchantments)
+ {
+ if (!IsValidItem(m_ItemType))
+ {
+ if (m_ItemType != E_BLOCK_AIR)
+ {
+ LOGWARNING("%s: creating an invalid item type (%d), resetting to empty.", __FUNCTION__, a_ItemType);
+ }
+ Empty();
+ }
+ }
+
+
+ /// Creates an exact copy of the item
+ cItem(const cItem & a_CopyFrom) :
+ m_ItemType (a_CopyFrom.m_ItemType),
+ m_ItemCount (a_CopyFrom.m_ItemCount),
+ m_ItemDamage (a_CopyFrom.m_ItemDamage),
+ m_Enchantments(a_CopyFrom.m_Enchantments)
+ {
+ }
+
+
+ void Empty(void)
+ {
+ m_ItemType = E_ITEM_EMPTY;
+ m_ItemCount = 0;
+ m_ItemDamage = 0;
+ m_Enchantments.Clear();
+ }
+
+
+ void Clear(void)
+ {
+ m_ItemType = E_ITEM_EMPTY;
+ m_ItemCount = 0;
+ m_ItemDamage = 0;
+ }
+
+
+ bool IsEmpty(void) const
+ {
+ return ((m_ItemType <= 0) || (m_ItemCount <= 0));
+ }
+
+
+ bool IsEqual(const cItem & a_Item) const
+ {
+ return (
+ IsSameType(a_Item) &&
+ (m_ItemDamage == a_Item.m_ItemDamage) &&
+ (m_Enchantments == a_Item.m_Enchantments)
+ );
+ }
+
+
+ bool IsSameType(const cItem & a_Item) const
+ {
+ return (m_ItemType == a_Item.m_ItemType) || (IsEmpty() && a_Item.IsEmpty());
+ }
+
+
+ /// Returns a copy of this item with m_ItemCount set to 1. Useful to preserve enchantments etc. on stacked items
+ cItem CopyOne(void) const;
+
+ /// Adds the specified count to this object and returns the reference to self (useful for chaining)
+ cItem & AddCount(char a_AmountToAdd);
+
+ /// Returns the maximum damage value that this item can have; zero if damage is not applied
+ short GetMaxDamage(void) const;
+
+ /// Damages a weapon / tool. Returns true when damage reaches max value and the item should be destroyed
+ bool DamageItem(short a_Amount = 1);
+
+ inline bool IsDamageable(void) const { return (GetMaxDamage() > 0); }
+
+ /// Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.) ItemCounts are ignored!
+ bool IsStackableWith(const cItem & a_OtherStack) const;
+
+ /// Returns true if the item is stacked up to its maximum stacking.
+ bool IsFullStack(void) const;
+
+ /// Returns the maximum amount of stacked items of this type.
+ char GetMaxStackSize(void) const;
+
+ // tolua_end
+
+ /// Returns the cItemHandler responsible for this item type
+ cItemHandler * GetHandler(void) const;
+
+ /// Saves the item data into JSON representation
+ void GetJson(Json::Value & a_OutValue) const;
+
+ /// Loads the item data from JSON representation
+ void FromJson(const Json::Value & a_Value);
+
+ /// Returns true if the specified item type is enchantable (as per 1.2.5 protocol requirements)
+ static bool IsEnchantable(short a_ItemType);
+
+ // tolua_begin
+
+ short m_ItemType;
+ char m_ItemCount;
+ short m_ItemDamage;
+ cEnchantments m_Enchantments;
+};
+// tolua_end
+
+
+
+
+
+/** This class bridges a vector of cItem for safe access via Lua. It checks boundaries for all accesses
+Note that this class is zero-indexed!
+*/
+class cItems // tolua_export
+ : public std::vector<cItem>
+{ // tolua_export
+public:
+ // tolua_begin
+
+ /// Need a Lua-accessible constructor
+ cItems(void) {}
+
+ cItem * Get (int a_Idx);
+ void Set (int a_Idx, const cItem & a_Item);
+ void Add (const cItem & a_Item) {push_back(a_Item); }
+ void Delete(int a_Idx);
+ void Clear (void) {clear(); }
+ int Size (void) {return size(); }
+ void Set (int a_Idx, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage);
+
+ void Add (ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage)
+ {
+ push_back(cItem(a_ItemType, a_ItemCount, a_ItemDamage));
+ }
+
+ // tolua_end
+} ; // tolua_export
+
+
+
+
+
+/// Used to store loot probability tables
+class cLootProbab
+{
+public:
+ cItem m_Item;
+ int m_MinAmount;
+ int m_MaxAmount;
+ int m_Weight;
+} ;
+
+
+
+
diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp
new file mode 100644
index 000000000..e9b86173e
--- /dev/null
+++ b/src/ItemGrid.cpp
@@ -0,0 +1,665 @@
+
+// ItemGrid.cpp
+
+// Implements the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.)
+
+#include "Globals.h"
+#include "ItemGrid.h"
+#include "Items/ItemHandler.h"
+#include "Noise.h"
+
+
+
+
+
+cItemGrid::cItemGrid(int a_Width, int a_Height) :
+ m_Width(a_Width),
+ m_Height(a_Height),
+ m_NumSlots(a_Width * a_Height),
+ m_Slots(new cItem[a_Width * a_Height]),
+ m_IsInTriggerListeners(false)
+{
+}
+
+
+
+
+
+cItemGrid::~cItemGrid()
+{
+ delete[] m_Slots;
+}
+
+
+
+
+
+int cItemGrid::GetSlotNum(int a_X, int a_Y) const
+{
+ if (
+ (a_X < 0) || (a_X >= m_Width) ||
+ (a_Y < 0) || (a_Y >= m_Height)
+ )
+ {
+ LOGWARNING("%s: coords out of range: (%d, %d) in grid of size (%d, %d)",
+ __FUNCTION__, a_X, a_Y, m_Width, m_Height
+ );
+ return -1;
+ }
+ return a_X + m_Width * a_Y;
+}
+
+
+
+
+
+void cItemGrid::GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: SlotNum out of range: %d in grid of range %d",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ a_X = -1;
+ a_Y = -1;
+ return;
+ }
+ a_X = a_SlotNum % m_Width;
+ a_Y = a_SlotNum / m_Width;
+}
+
+
+
+
+
+const cItem & cItemGrid::GetSlot(int a_X, int a_Y) const
+{
+ return GetSlot(GetSlotNum(a_X, a_Y));
+}
+
+
+
+
+
+const cItem & cItemGrid::GetSlot(int a_SlotNum) const
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number, %d out of %d slots",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return m_Slots[0];
+ }
+ return m_Slots[a_SlotNum];
+}
+
+
+
+
+
+void cItemGrid::SetSlot(int a_X, int a_Y, const cItem & a_Item)
+{
+ SetSlot(GetSlotNum(a_X, a_Y), a_Item);
+}
+
+
+
+
+
+void cItemGrid::SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage)
+{
+ SetSlot(GetSlotNum(a_X, a_Y), cItem(a_ItemType, a_ItemCount, a_ItemDamage));
+}
+
+
+
+
+
+void cItemGrid::SetSlot(int a_SlotNum, const cItem & a_Item)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number %d out of %d slots",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return;
+ }
+ m_Slots[a_SlotNum] = a_Item;
+ TriggerListeners(a_SlotNum);
+}
+
+
+
+
+
+void cItemGrid::SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage)
+{
+ SetSlot(a_SlotNum, cItem(a_ItemType, a_ItemCount, a_ItemDamage));
+}
+
+
+
+
+
+void cItemGrid::EmptySlot(int a_X, int a_Y)
+{
+ EmptySlot(GetSlotNum(a_X, a_Y));
+}
+
+
+
+
+
+void cItemGrid::EmptySlot(int a_SlotNum)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number %d out of %d slots",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return;
+ }
+
+ // Check if already empty:
+ if (m_Slots[a_SlotNum].IsEmpty())
+ {
+ return;
+ }
+
+ // Empty and notify
+ m_Slots[a_SlotNum].Empty();
+ TriggerListeners(a_SlotNum);
+}
+
+
+
+
+
+bool cItemGrid::IsSlotEmpty(int a_SlotNum) const
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number %d out of %d slots",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return true;
+ }
+ return m_Slots[a_SlotNum].IsEmpty();
+}
+
+
+
+
+
+bool cItemGrid::IsSlotEmpty(int a_X, int a_Y) const
+{
+ return IsSlotEmpty(GetSlotNum(a_X, a_Y));
+}
+
+
+
+
+
+void cItemGrid::Clear(void)
+{
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ m_Slots[i].Empty();
+ TriggerListeners(i);
+ }
+}
+
+
+
+
+
+int cItemGrid::HowManyCanFit(const cItem & a_ItemStack, bool a_AllowNewStacks)
+{
+ char NumLeft = a_ItemStack.m_ItemCount;
+ int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize();
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ if (m_Slots[i].IsEmpty())
+ {
+ if (a_AllowNewStacks)
+ {
+ NumLeft -= MaxStack;
+ }
+ }
+ else if (m_Slots[i].IsStackableWith(a_ItemStack))
+ {
+ NumLeft -= MaxStack - m_Slots[i].m_ItemCount;
+ }
+ if (NumLeft <= 0)
+ {
+ // All items fit
+ return a_ItemStack.m_ItemCount;
+ }
+ } // for i - m_Slots[]
+ return a_ItemStack.m_ItemCount - NumLeft;
+}
+
+
+
+
+
+int cItemGrid::AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack)
+{
+ int PrevCount = 0;
+ if (m_Slots[a_Slot].IsEmpty())
+ {
+ m_Slots[a_Slot] = a_ItemStack;
+ PrevCount = 0;
+ }
+ else
+ {
+ PrevCount = m_Slots[a_Slot].m_ItemCount;
+ }
+ m_Slots[a_Slot].m_ItemCount = std::min(a_MaxStack, PrevCount + a_Num);
+ int toReturn = m_Slots[a_Slot].m_ItemCount - PrevCount;
+ TriggerListeners(a_Slot);
+ return toReturn;
+}
+
+
+
+
+
+int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_PrioritarySlot)
+{
+ int NumLeft = a_ItemStack.m_ItemCount;
+ int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize();
+
+ // Try prioritarySlot first:
+ if (
+ (a_PrioritarySlot != -1) &&
+ (
+ m_Slots[a_PrioritarySlot].IsEmpty() ||
+ m_Slots[a_PrioritarySlot].IsStackableWith(a_ItemStack)
+ )
+ )
+ {
+ NumLeft -= AddItemToSlot(a_ItemStack, a_PrioritarySlot, NumLeft, MaxStack);
+ }
+
+ // Scan existing stacks:
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ if (m_Slots[i].IsStackableWith(a_ItemStack))
+ {
+ NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack);
+ }
+ if (NumLeft <= 0)
+ {
+ // All items fit
+ return a_ItemStack.m_ItemCount;
+ }
+ } // for i - m_Slots[]
+
+ if (!a_AllowNewStacks)
+ {
+ return (a_ItemStack.m_ItemCount - NumLeft);
+ }
+
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ if (m_Slots[i].IsEmpty())
+ {
+ NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack);
+ }
+ if (NumLeft <= 0)
+ {
+ // All items fit
+ return a_ItemStack.m_ItemCount;
+ }
+ } // for i - m_Slots[]
+ return (a_ItemStack.m_ItemCount - NumLeft);
+}
+
+
+
+
+
+int cItemGrid::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, int a_PrioritarySlot)
+{
+ int TotalAdded = 0;
+ for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();)
+ {
+ int NumAdded = AddItem(*itr, a_AllowNewStacks, a_PrioritarySlot);
+ if (itr->m_ItemCount == NumAdded)
+ {
+ itr = a_ItemStackList.erase(itr);
+ }
+ else
+ {
+ itr->m_ItemCount -= NumAdded;
+ ++itr;
+ }
+ TotalAdded += NumAdded;
+ }
+ return TotalAdded;
+}
+
+
+
+
+
+int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning -1",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return -1;
+ }
+
+ if (m_Slots[a_SlotNum].IsEmpty())
+ {
+ // The item is empty, it's not gonna change
+ return 0;
+ }
+
+ if (m_Slots[a_SlotNum].m_ItemCount <= -a_AddToCount)
+ {
+ // Trying to remove more items than there already are, make the item empty
+ m_Slots[a_SlotNum].Empty();
+ TriggerListeners(a_SlotNum);
+ return 0;
+ }
+
+ m_Slots[a_SlotNum].m_ItemCount += a_AddToCount;
+ TriggerListeners(a_SlotNum);
+ return m_Slots[a_SlotNum].m_ItemCount;
+}
+
+
+
+
+
+int cItemGrid::ChangeSlotCount(int a_X, int a_Y, int a_AddToCount)
+{
+ return ChangeSlotCount(GetSlotNum(a_X, a_Y), a_AddToCount);
+}
+
+
+
+
+
+cItem cItemGrid::RemoveOneItem(int a_SlotNum)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning empty item",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return cItem();
+ }
+
+ // If the slot is empty, return an empty item
+ if (m_Slots[a_SlotNum].IsEmpty())
+ {
+ return cItem();
+ }
+
+ // Make a copy of the item in slot, set count to 1 and remove one from the slot
+ cItem res = m_Slots[a_SlotNum];
+ res.m_ItemCount = 1;
+ m_Slots[a_SlotNum].m_ItemCount -= 1;
+
+ // Emptying the slot correctly if appropriate
+ if (m_Slots[a_SlotNum].m_ItemCount == 0)
+ {
+ m_Slots[a_SlotNum].Empty();
+ }
+
+ // Notify everyone of the change
+ TriggerListeners(a_SlotNum);
+
+ // Return the stored one item
+ return res;
+}
+
+
+
+
+
+cItem cItemGrid::RemoveOneItem(int a_X, int a_Y)
+{
+ return RemoveOneItem(GetSlotNum(a_X, a_Y));
+}
+
+
+
+
+
+int cItemGrid::HowManyItems(const cItem & a_Item)
+{
+ int res = 0;
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ if (m_Slots[i].IsStackableWith(a_Item))
+ {
+ res += m_Slots[i].m_ItemCount;
+ }
+ }
+ return res;
+}
+
+
+
+
+
+bool cItemGrid::HasItems(const cItem & a_ItemStack)
+{
+ int CurrentlyHave = HowManyItems(a_ItemStack);
+ return (CurrentlyHave >= a_ItemStack.m_ItemCount);
+}
+
+
+
+
+
+int cItemGrid::GetFirstEmptySlot(void) const
+{
+ return GetNextEmptySlot(-1);
+}
+
+
+
+
+
+int cItemGrid::GetFirstUsedSlot(void) const
+{
+ return GetNextUsedSlot(-1);
+}
+
+
+
+
+
+int cItemGrid::GetLastEmptySlot(void) const
+{
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ if (m_Slots[i].IsEmpty())
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+
+
+int cItemGrid::GetLastUsedSlot(void) const
+{
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ if (!m_Slots[i].IsEmpty())
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+
+
+int cItemGrid::GetNextEmptySlot(int a_StartFrom) const
+{
+ for (int i = a_StartFrom + 1; i < m_NumSlots; i++)
+ {
+ if (m_Slots[i].IsEmpty())
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+
+
+int cItemGrid::GetNextUsedSlot(int a_StartFrom) const
+{
+ for (int i = a_StartFrom + 1; i < m_NumSlots; i++)
+ {
+ if (!m_Slots[i].IsEmpty())
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+
+
+void cItemGrid::CopyToItems(cItems & a_Items) const
+{
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ if (!m_Slots[i].IsEmpty())
+ {
+ a_Items.push_back(m_Slots[i]);
+ }
+ } // for i - m_Slots[]
+}
+
+
+
+
+
+bool cItemGrid::DamageItem(int a_SlotNum, short a_Amount)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: invalid slot number %d out of %d slots, ignoring.", __FUNCTION__, a_SlotNum, m_NumSlots);
+ return false;
+ }
+ return m_Slots[a_SlotNum].DamageItem(a_Amount);
+}
+
+
+
+
+
+bool cItemGrid::DamageItem(int a_X, int a_Y, short a_Amount)
+{
+ return DamageItem(GetSlotNum(a_X, a_Y), a_Amount);
+}
+
+
+
+
+
+void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed)
+{
+ // Calculate the total weight:
+ int TotalProbab = 1;
+ for (int i = 0; i < a_CountLootProbabs; i++)
+ {
+ TotalProbab += a_LootProbabs[i].m_Weight;
+ }
+
+ // Pick the loot items:
+ cNoise Noise(a_Seed);
+ for (int i = 0; i < a_NumSlots; i++)
+ {
+ int Rnd = (Noise.IntNoise1DInt(i) / 7);
+ int LootRnd = Rnd % TotalProbab;
+ Rnd >>= 8;
+ cItem CurrentLoot = cItem(E_ITEM_BOOK, 1, 0); // TODO: enchantment
+ for (int j = 0; j < a_CountLootProbabs; j++)
+ {
+ LootRnd -= a_LootProbabs[i].m_Weight;
+ if (LootRnd < 0)
+ {
+ CurrentLoot = a_LootProbabs[i].m_Item;
+ CurrentLoot.m_ItemCount = a_LootProbabs[i].m_MinAmount + (Rnd % (a_LootProbabs[i].m_MaxAmount - a_LootProbabs[i].m_MinAmount));
+ Rnd >>= 8;
+ break;
+ }
+ } // for j - a_LootProbabs[]
+ SetSlot(Rnd % m_NumSlots, CurrentLoot);
+ } // for i - NumSlots
+}
+
+
+
+
+
+void cItemGrid::AddListener(cListener & a_Listener)
+{
+ cCSLock Lock(m_CSListeners);
+ ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners()
+ m_Listeners.push_back(&a_Listener);
+}
+
+
+
+
+
+void cItemGrid::RemoveListener(cListener & a_Listener)
+{
+ cCSLock Lock(m_CSListeners);
+ ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners()
+ for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr)
+ {
+ if (*itr == &a_Listener)
+ {
+ m_Listeners.erase(itr);
+ return;
+ }
+ } // for itr - m_Listeners[]
+}
+
+
+
+
+
+void cItemGrid::TriggerListeners(int a_SlotNum)
+{
+ cListeners Listeners;
+ {
+ cCSLock Lock(m_CSListeners);
+ m_IsInTriggerListeners = true;
+ Listeners = m_Listeners;
+ }
+ for (cListeners::iterator itr = Listeners.begin(), end = Listeners.end(); itr != end; ++itr)
+ {
+ (*itr)->OnSlotChanged(this, a_SlotNum);
+ } // for itr - m_Listeners[]
+ m_IsInTriggerListeners = false;
+}
+
+
+
+
diff --git a/src/ItemGrid.h b/src/ItemGrid.h
new file mode 100644
index 000000000..a4af523cf
--- /dev/null
+++ b/src/ItemGrid.h
@@ -0,0 +1,191 @@
+
+// ItemGrid.h
+
+// Declares the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.)
+
+
+
+
+#pragma once
+
+#include "Item.h"
+
+
+
+
+
+// tolua_begin
+class cItemGrid
+{
+public:
+ // tolua_end
+
+ /// This class is used as a callback for when a slot changes
+ class cListener
+ {
+ public:
+ /// Called whenever a slot changes
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) = 0;
+ } ;
+ typedef std::vector<cListener *> cListeners;
+
+ cItemGrid(int a_Width, int a_Height);
+
+ ~cItemGrid();
+
+ // tolua_begin
+ int GetWidth (void) const { return m_Width; }
+ int GetHeight (void) const { return m_Height; }
+ int GetNumSlots(void) const { return m_NumSlots; }
+
+ /// Converts XY coords into slot number; returns -1 on invalid coords
+ int GetSlotNum(int a_X, int a_Y) const;
+
+ // tolua_end
+
+ /// Converts slot number into XY coords; sets coords to -1 on invalid slot number. Exported in ManualBindings.cpp
+ void GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const;
+
+ // tolua_begin
+
+ // Retrieve slots by coords or slot number; Logs warning and returns the first slot on invalid coords / slotnum
+ const cItem & GetSlot(int a_X, int a_Y) const;
+ const cItem & GetSlot(int a_SlotNum) const;
+
+ // Set slot by coords or slot number; Logs warning and doesn't set on invalid coords / slotnum
+ void SetSlot(int a_X, int a_Y, const cItem & a_Item);
+ void SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage);
+ void SetSlot(int a_SlotNum, const cItem & a_Item);
+ void SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage);
+
+ // Empty the specified slot; Logs warning and doesn't set on invalid coords / slotnum
+ void EmptySlot(int a_X, int a_Y);
+ void EmptySlot(int a_SlotNum);
+
+ /// Returns true if the specified slot is empty or the slot doesn't exist
+ bool IsSlotEmpty(int a_SlotNum) const;
+
+ /// Returns true if the specified slot is empty or the slot doesn't exist
+ bool IsSlotEmpty(int a_X, int a_Y) const;
+
+ /// Sets all items as empty
+ void Clear(void);
+
+ /// Returns number of items out of a_ItemStack that can fit in the storage
+ int HowManyCanFit(const cItem & a_ItemStack, bool a_AllowNewStacks = true);
+
+ /** Adds as many items out of a_ItemStack as can fit.
+ If a_AllowNewStacks is set to false, only existing stacks can be topped up;
+ if a_AllowNewStacks is set to true, empty slots can be used for the rest.
+ If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in
+ first (if empty or compatible with added items)
+ if a_PrioritarySlot is set to -1, regular order apply
+ Returns the number of items that fit.
+ */
+ int AddItem(cItem & a_ItemStack, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1);
+
+ /** Same as AddItem, but works on an entire list of item stacks.
+ The a_ItemStackList is modified to reflect the leftover items.
+ If a_AllowNewStacks is set to false, only existing stacks can be topped up;
+ if a_AllowNewStacks is set to true, empty slots can be used for the rest.
+ If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in
+ first (if empty or compatible with added items)
+ if a_PrioritarySlot is set to -1, regular order apply
+ Returns the total number of items that fit.
+ */
+ int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1);
+
+ /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot.
+ If the slot is empty, ignores the call.
+ Returns the new count.
+ */
+ int ChangeSlotCount(int a_SlotNum, int a_AddToCount);
+
+ /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot.
+ If the slot is empty, ignores the call.
+ Returns the new count.
+ */
+ int ChangeSlotCount(int a_X, int a_Y, int a_AddToCount);
+
+ /** Removes one item from the stack in the specified slot, and returns it.
+ If the slot was empty, returns an empty item
+ */
+ cItem RemoveOneItem(int a_SlotNum);
+
+ /** Removes one item from the stack in the specified slot, and returns it.
+ If the slot was empty, returns an empty item
+ */
+ cItem RemoveOneItem(int a_X, int a_Y);
+
+ /// Returns the number of items of type a_Item that are stored
+ int HowManyItems(const cItem & a_Item);
+
+ /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack
+ bool HasItems(const cItem & a_ItemStack);
+
+ /// Returns the index of the first empty slot; -1 if all full
+ int GetFirstEmptySlot(void) const;
+
+ /// Returns the index of the first non-empty slot; -1 if all empty
+ int GetFirstUsedSlot(void) const;
+
+ /// Returns the index of the last empty slot; -1 if all full
+ int GetLastEmptySlot(void) const;
+
+ /// Returns the index of the last used slot; -1 if all empty
+ int GetLastUsedSlot(void) const;
+
+ /// Returns the index of the first empty slot following a_StartFrom (a_StartFrom is not checked)
+ int GetNextEmptySlot(int a_StartFrom) const;
+
+ /// Returns the index of the first used slot following a_StartFrom (a_StartFrom is not checked)
+ int GetNextUsedSlot(int a_StartFrom) const;
+
+ /// Copies the contents into a cItems object; preserves the original a_Items contents
+ void CopyToItems(cItems & a_Items) const;
+
+ /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact)
+ bool DamageItem(int a_SlotNum, short a_Amount);
+
+ /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact)
+ bool DamageItem(int a_X, int a_Y, short a_Amount);
+
+ // tolua_end
+
+
+ /** Generates random loot from the specified loot probability table, with a chance of enchanted books added.
+ A total of a_NumSlots are taken by the loot.
+ Cannot export to Lua due to raw array a_LootProbabs. TODO: Make this exportable / export through ManualBindings.cpp with a Lua table as LootProbabs
+ */
+ void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed);
+
+ /// Adds a callback that gets called whenever a slot changes. Must not be called from within the listener callback!
+ void AddListener(cListener & a_Listener);
+
+ /// Removes a slot-change-callback. Must not be called from within the listener callback!
+ void RemoveListener(cListener & a_Listener);
+
+ // tolua_begin
+
+protected:
+ int m_Width;
+ int m_Height;
+ int m_NumSlots; // m_Width * m_Height, for easier validity checking in the access functions
+ cItem * m_Slots; // x + m_Width * y
+
+ cListeners m_Listeners; ///< Listeners which should be notified on slot changes; the pointers are not owned by this object
+ cCriticalSection m_CSListeners; ///< CS that guards the m_Listeners against multi-thread access
+ bool m_IsInTriggerListeners; ///< Set to true while TriggerListeners is running, to detect attempts to manipulate listener list while triggerring
+
+ /// Calls all m_Listeners for the specified slot number
+ void TriggerListeners(int a_SlotNum);
+
+ /** Adds up to a_Num items out of a_ItemStack, as many as can fit, in specified slot
+ Returns the number of items that did fit.
+ */
+ int AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack);
+} ;
+// tolua_end
+
+
+
diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h
new file mode 100644
index 000000000..ab4182eea
--- /dev/null
+++ b/src/Items/ItemBed.h
@@ -0,0 +1,56 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Blocks/BlockBed.h"
+
+
+
+
+
+class cItemBedHandler :
+ public cItemHandler
+{
+public:
+ cItemBedHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ if (a_BlockFace != BLOCK_FACE_TOP)
+ {
+ // Can only be placed on the floor
+ return false;
+ }
+
+ a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetRotation());
+
+ // 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)
+ {
+ return false;
+ }
+
+ a_BlockType = E_BLOCK_BED;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemBoat.h b/src/Items/ItemBoat.h
new file mode 100644
index 000000000..6e3395f1d
--- /dev/null
+++ b/src/Items/ItemBoat.h
@@ -0,0 +1,54 @@
+
+// ItemBoat.h
+
+// Declares the various boat ItemHandlers
+
+
+
+
+
+#pragma once
+
+#include "../Entities/Boat.h"
+
+
+
+
+
+class cItemBoatHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemBoatHandler(int a_ItemType) :
+ super(a_ItemType)
+ {
+ }
+
+
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ if (a_Dir < 0)
+ {
+ return false;
+ }
+
+ double x = (double)a_BlockX + 0.5;
+ double y = (double)a_BlockY + 0.5;
+ double z = (double)a_BlockZ + 0.5;
+
+ cBoat * Boat = NULL;
+
+ Boat = new cBoat (x, y, z);
+ Boat->Initialize(a_World);
+
+ return true;
+ }
+
+} ;
+
+
+
+
diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h
new file mode 100644
index 000000000..d533c21fd
--- /dev/null
+++ b/src/Items/ItemBow.h
@@ -0,0 +1,87 @@
+
+// ItemBow.h
+
+// Declares the cItemBowHandler class representing the itemhandler for bows
+
+
+
+
+
+#pragma once
+
+#include "../Entities/ProjectileEntity.h"
+
+
+
+
+
+class cItemBowHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemBowHandler(void) :
+ super(E_ITEM_BOW)
+ {
+ }
+
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ ASSERT(a_Player != NULL);
+
+ // Check if the player has an arrow in the inventory, or is in Creative:
+ if (!(a_Player->IsGameModeCreative() || a_Player->GetInventory().HasItems(cItem(E_ITEM_ARROW))))
+ {
+ return false;
+ }
+
+ a_Player->StartChargingBow();
+ return true;
+ }
+
+
+ virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
+ {
+ // Actual shot - produce the arrow with speed based on the ticks that the bow was charged
+ ASSERT(a_Player != NULL);
+
+ int BowCharge = a_Player->FinishChargingBow();
+ double Force = (double)BowCharge / 20;
+ Force = (Force * Force + 2 * Force) / 3; // This formula is used by the 1.6.2 client
+ if (Force < 0.1)
+ {
+ // Too little force, ignore the shot
+ return;
+ }
+ if (Force > 1)
+ {
+ Force = 1;
+ }
+
+ // Create the arrow entity:
+ cArrowEntity * Arrow = new cArrowEntity(*a_Player, Force * 2);
+ if (Arrow == NULL)
+ {
+ return;
+ }
+ if (!Arrow->Initialize(a_Player->GetWorld()))
+ {
+ delete Arrow;
+ return;
+ }
+ a_Player->GetWorld()->BroadcastSpawnEntity(*Arrow);
+ a_Player->GetWorld()->BroadcastSoundEffect("random.bow", (int)a_Player->GetPosX() * 8, (int)a_Player->GetPosY() * 8, (int)a_Player->GetPosZ() * 8, 0.5, (float)Force);
+
+ if (!a_Player->IsGameModeCreative())
+ {
+ a_Player->UseEquippedItem();
+ }
+ }
+} ;
+
+
+
+
+
diff --git a/src/Items/ItemBrewingStand.h b/src/Items/ItemBrewingStand.h
new file mode 100644
index 000000000..4ff14d4b4
--- /dev/null
+++ b/src/Items/ItemBrewingStand.h
@@ -0,0 +1,41 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemBrewingStandHandler :
+ public cItemHandler
+{
+public:
+ cItemBrewingStandHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = E_BLOCK_BREWING_STAND;
+ a_BlockMeta = 0;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h
new file mode 100644
index 000000000..fa3d48da1
--- /dev/null
+++ b/src/Items/ItemBucket.h
@@ -0,0 +1,160 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Simulator/FluidSimulator.h"
+#include "../Blocks/BlockHandler.h"
+
+
+
+
+
+class cItemBucketHandler :
+ public cItemHandler
+{
+public:
+ cItemBucketHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ switch (m_ItemType)
+ {
+ case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir);
+ case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_LAVA);
+ case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_WATER);
+ default:
+ {
+ ASSERT(!"Unhandled ItemType");
+ return false;
+ }
+ }
+ }
+
+
+
+ bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+ {
+ if (a_BlockFace < 0)
+ {
+ return false;
+ }
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ BLOCKTYPE ClickedBlock;
+ NIBBLETYPE ClickedMeta;
+ a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedMeta);
+ LOGD("Bucket Clicked BlockType %d, meta %d", ClickedBlock, ClickedMeta);
+ if (ClickedMeta != 0)
+ {
+ // Not a source block
+ return false;
+ }
+
+ if (a_Player->GetGameMode() == gmCreative)
+ {
+ // In creative mode don't modify the inventory, just remove the fluid:
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
+ return true;
+ }
+
+ ENUM_ITEM_ID NewItem = E_ITEM_EMPTY;
+ switch (ClickedBlock)
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ NewItem = E_ITEM_WATER_BUCKET;
+ break;
+ }
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ NewItem = E_ITEM_LAVA_BUCKET;
+ break;
+ }
+
+ default: return false;
+ }
+
+ // Remove the bucket from the inventory
+ if (!a_Player->GetInventory().RemoveOneEquippedItem())
+ {
+ LOG("Clicked with an empty bucket, but cannot remove one from the inventory? WTF?");
+ ASSERT(!"Inventory bucket mismatch");
+ return true;
+ }
+
+ // Give new bucket, filled with fluid:
+ cItem Item(NewItem, 1);
+ a_Player->GetInventory().AddItem(Item, true, true);
+
+ // Remove water / lava block
+ a_Player->GetWorld()->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
+ return true;
+ }
+
+
+ bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_FluidBlock)
+ {
+ if (a_BlockFace < 0)
+ {
+ return false;
+ }
+
+ BLOCKTYPE CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ bool CanWashAway = cFluidSimulator::CanWashAway(CurrentBlock);
+ if (!CanWashAway)
+ {
+ // The block pointed at cannot be washed away, so put fluid on top of it / on its sides
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ if (
+ !CanWashAway &&
+ (CurrentBlock != E_BLOCK_AIR) &&
+ (CurrentBlock != E_BLOCK_WATER) &&
+ (CurrentBlock != E_BLOCK_STATIONARY_WATER) &&
+ (CurrentBlock != E_BLOCK_LAVA) &&
+ (CurrentBlock != E_BLOCK_STATIONARY_LAVA)
+ )
+ {
+ // Cannot place water here
+ return false;
+ }
+
+ if (a_Player->GetGameMode() != gmCreative)
+ {
+ // Remove fluid bucket, add empty bucket:
+ if (!a_Player->GetInventory().RemoveOneEquippedItem())
+ {
+ LOG("Clicked with a full bucket, but cannot remove one from the inventory? WTF?");
+ ASSERT(!"Inventory bucket mismatch");
+ return false;
+ }
+ cItem Item(E_ITEM_BUCKET, 1);
+ if (!a_Player->GetInventory().AddItem(Item,true,true))
+ {
+ return false;
+ }
+ }
+
+ // Wash away anything that was there prior to placing:
+ if (CanWashAway)
+ {
+ cBlockHandler * Handler = BlockHandler(CurrentBlock);
+ if (Handler->DoesDropOnUnsuitable())
+ {
+ Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FluidBlock, 0);
+
+ return true;
+ }
+
+};
diff --git a/src/Items/ItemCauldron.h b/src/Items/ItemCauldron.h
new file mode 100644
index 000000000..8b2ddc29f
--- /dev/null
+++ b/src/Items/ItemCauldron.h
@@ -0,0 +1,41 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemCauldronHandler :
+ public cItemHandler
+{
+public:
+ cItemCauldronHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = E_BLOCK_CAULDRON;
+ a_BlockMeta = 0;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemCloth.h b/src/Items/ItemCloth.h
new file mode 100644
index 000000000..aca27a299
--- /dev/null
+++ b/src/Items/ItemCloth.h
@@ -0,0 +1,23 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemClothHandler :
+ public cItemHandler
+{
+public:
+ cItemClothHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemComparator.h b/src/Items/ItemComparator.h
new file mode 100644
index 000000000..3fbb7603d
--- /dev/null
+++ b/src/Items/ItemComparator.h
@@ -0,0 +1,40 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../Blocks/BlockRedstoneRepeater.h"
+
+
+
+
+
+class cItemComparatorHandler :
+ public cItemHandler
+{
+public:
+ cItemComparatorHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = E_BLOCK_INACTIVE_COMPARATOR;
+ a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation());
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h
new file mode 100644
index 000000000..72ea0beed
--- /dev/null
+++ b/src/Items/ItemDoor.h
@@ -0,0 +1,45 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cItemDoorHandler :
+ public cItemHandler
+{
+public:
+ cItemDoorHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = (m_ItemType == E_ITEM_WOODEN_DOOR) ? E_BLOCK_WOODEN_DOOR : E_BLOCK_IRON_DOOR;
+ return BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta(
+ a_World, a_Player,
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
+ a_CursorX, a_CursorY, a_CursorZ,
+ a_BlockType, a_BlockMeta
+ );
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h
new file mode 100644
index 000000000..99b8d2543
--- /dev/null
+++ b/src/Items/ItemDye.h
@@ -0,0 +1,44 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cItemDyeHandler :
+ public cItemHandler
+{
+public:
+ cItemDyeHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ // TODO: Handle coloring the sheep, too (OnItemUseOnEntity maybe)
+
+ // Handle growing the plants:
+ if (a_Item.m_ItemDamage == E_META_DYE_WHITE)
+ {
+ if (a_World->GrowRipePlant(a_BlockX, a_BlockY, a_BlockZ, true))
+ {
+ if (a_Player->GetGameMode() != gmCreative)
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemFlowerPot.h b/src/Items/ItemFlowerPot.h
new file mode 100644
index 000000000..befa2ff21
--- /dev/null
+++ b/src/Items/ItemFlowerPot.h
@@ -0,0 +1,41 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemFlowerPotHandler :
+ public cItemHandler
+{
+public:
+ cItemFlowerPotHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = E_BLOCK_FLOWER_POT;
+ a_BlockMeta = 0;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemFood.h b/src/Items/ItemFood.h
new file mode 100644
index 000000000..2ae572331
--- /dev/null
+++ b/src/Items/ItemFood.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemFoodHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemFoodHandler(int a_ItemType)
+ : super(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsFood(void) override
+ {
+ return true;
+ }
+
+
+ virtual FoodInfo GetFoodInfo(void) override
+ {
+ switch(m_ItemType)
+ {
+ // Please keep alpha-sorted.
+ case E_ITEM_BAKED_POTATO: return FoodInfo(6, 7.2);
+ case E_ITEM_BREAD: return FoodInfo(5, 6);
+ case E_ITEM_CARROT: return FoodInfo(4, 4.8);
+ case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2);
+ case E_ITEM_COOKED_FISH: return FoodInfo(5, 6);
+ case E_ITEM_COOKED_PORKCHOP: return FoodInfo(8, 12.8);
+ case E_ITEM_COOKIE: return FoodInfo(2, 0.4);
+ case E_ITEM_GOLDEN_APPLE: return FoodInfo(4, 9.6);
+ case E_ITEM_GOLDEN_CARROT: return FoodInfo(6, 14.4);
+ case E_ITEM_MELON_SLICE: return FoodInfo(2, 1.2);
+ case E_ITEM_POISONOUS_POTATO: return FoodInfo(2, 1.2, 60);
+ case E_ITEM_POTATO: return FoodInfo(1, 0.6);
+ case E_ITEM_PUMPKIN_PIE: return FoodInfo(8, 4.8);
+ case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8);
+ case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2, 30);
+ case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2);
+ case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8);
+ case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4);
+ case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8, 80);
+ case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2, 100);
+ case E_ITEM_STEAK: return FoodInfo(8, 12.8);
+ case E_ITEM_MUSHROOM_SOUP: return FoodInfo(6, 7.2);
+ }
+ LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType);
+ return FoodInfo(0, 0.f);
+ }
+
+};
+
+
+
+
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
new file mode 100644
index 000000000..92ba94999
--- /dev/null
+++ b/src/Items/ItemHandler.cpp
@@ -0,0 +1,511 @@
+
+#include "Globals.h"
+#include "ItemHandler.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+#include "../FastRandom.h"
+
+// Handlers:
+#include "ItemBed.h"
+#include "ItemBoat.h"
+#include "ItemBow.h"
+#include "ItemBrewingStand.h"
+#include "ItemBucket.h"
+#include "ItemCauldron.h"
+#include "ItemCloth.h"
+#include "ItemComparator.h"
+#include "ItemDoor.h"
+#include "ItemDye.h"
+#include "ItemFlowerPot.h"
+#include "ItemFood.h"
+#include "ItemHoe.h"
+#include "ItemLeaves.h"
+#include "ItemLighter.h"
+#include "ItemMinecart.h"
+#include "ItemPickaxe.h"
+#include "ItemThrowable.h"
+#include "ItemRedstoneDust.h"
+#include "ItemRedstoneRepeater.h"
+#include "ItemSapling.h"
+#include "ItemSeeds.h"
+#include "ItemShears.h"
+#include "ItemShovel.h"
+#include "ItemSign.h"
+#include "ItemSpawnEgg.h"
+#include "ItemSugarcane.h"
+#include "ItemSword.h"
+
+#include "../Blocks/BlockHandler.h"
+
+
+
+
+
+bool cItemHandler::m_HandlerInitialized = false;
+cItemHandler * cItemHandler::m_ItemHandler[2268];
+
+
+
+
+
+cItemHandler * cItemHandler::GetItemHandler(int a_ItemType)
+{
+ if (a_ItemType < 0)
+ {
+ // Either nothing (-1), or bad value, both cases should return the air handler
+ if (a_ItemType < -1)
+ {
+ ASSERT(!"Bad item type");
+ }
+ a_ItemType = 0;
+ }
+
+ if (!m_HandlerInitialized)
+ {
+ // We need to initialize
+ memset(m_ItemHandler, 0, sizeof(m_ItemHandler));
+ m_HandlerInitialized = true;
+ }
+ if (m_ItemHandler[a_ItemType] == NULL)
+ {
+ m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType);
+ }
+ return m_ItemHandler[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_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_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_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_ENDER_PEARL: return new cItemEnderPearlHandler();
+ case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
+ case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
+ case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
+ 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_SNOWBALL: return new cItemSnowballHandler();
+ case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
+ case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType);
+
+ case E_ITEM_WOODEN_HOE:
+ case E_ITEM_STONE_HOE:
+ case E_ITEM_IRON_HOE:
+ case E_ITEM_GOLD_HOE:
+ case E_ITEM_DIAMOND_HOE:
+ {
+ return new cItemHoeHandler(a_ItemType);
+ }
+
+ case E_ITEM_WOODEN_PICKAXE:
+ case E_ITEM_STONE_PICKAXE:
+ case E_ITEM_IRON_PICKAXE:
+ case E_ITEM_GOLD_PICKAXE:
+ case E_ITEM_DIAMOND_PICKAXE:
+ {
+ return new cItemPickaxeHandler(a_ItemType);
+ }
+
+ case E_ITEM_WOODEN_SHOVEL:
+ case E_ITEM_STONE_SHOVEL:
+ case E_ITEM_IRON_SHOVEL:
+ case E_ITEM_GOLD_SHOVEL:
+ case E_ITEM_DIAMOND_SHOVEL:
+ {
+ return new cItemShovelHandler(a_ItemType);
+ }
+
+ case E_ITEM_WOODEN_SWORD:
+ case E_ITEM_STONE_SWORD:
+ case E_ITEM_IRON_SWORD:
+ case E_ITEM_GOLD_SWORD:
+ case E_ITEM_DIAMOND_SWORD:
+ {
+ return new cItemSwordHandler(a_ItemType);
+ }
+
+ case E_ITEM_BUCKET:
+ case E_ITEM_WATER_BUCKET:
+ case E_ITEM_LAVA_BUCKET:
+ {
+ return new cItemBucketHandler(a_ItemType);
+ }
+
+ case E_ITEM_CARROT:
+ case E_ITEM_MELON_SEEDS:
+ case E_ITEM_POTATO:
+ case E_ITEM_PUMPKIN_SEEDS:
+ case E_ITEM_SEEDS:
+ {
+ return new cItemSeedsHandler(a_ItemType);
+ }
+
+ case E_ITEM_IRON_DOOR:
+ case E_ITEM_WOODEN_DOOR:
+ {
+ return new cItemDoorHandler(a_ItemType);
+ }
+
+ case E_ITEM_MINECART:
+ case E_ITEM_CHEST_MINECART:
+ case E_ITEM_FURNACE_MINECART:
+ case E_ITEM_MINECART_WITH_TNT:
+ case E_ITEM_MINECART_WITH_HOPPER:
+ {
+ return new cItemMinecartHandler(a_ItemType);
+ }
+
+ // Food:
+ case E_ITEM_BREAD:
+ case E_ITEM_COOKIE:
+ case E_ITEM_MELON_SLICE:
+ case E_ITEM_RAW_CHICKEN:
+ case E_ITEM_COOKED_CHICKEN:
+ case E_ITEM_RAW_BEEF:
+ case E_ITEM_RAW_PORKCHOP:
+ case E_ITEM_STEAK:
+ case E_ITEM_COOKED_PORKCHOP:
+ case E_ITEM_RAW_FISH:
+ case E_ITEM_COOKED_FISH:
+ case E_ITEM_RED_APPLE:
+ case E_ITEM_GOLDEN_APPLE:
+ case E_ITEM_ROTTEN_FLESH:
+ case E_ITEM_MUSHROOM_SOUP:
+ case E_ITEM_SPIDER_EYE:
+ {
+ return new cItemFoodHandler(a_ItemType);
+ }
+ }
+}
+
+
+
+
+
+void cItemHandler::Deinit()
+{
+ for(int i = 0; i < 2267; i++)
+ {
+ delete m_ItemHandler[i];
+ }
+ memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); // Don't leave any dangling pointers around, just in case
+ m_HandlerInitialized = false;
+}
+
+
+
+
+
+cItemHandler::cItemHandler(int a_ItemType)
+{
+ m_ItemType = a_ItemType;
+}
+
+
+
+
+
+bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir)
+{
+ return false;
+}
+
+
+
+
+
+bool cItemHandler::OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir)
+{
+ return false;
+}
+
+
+
+
+
+void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block);
+
+ if (a_Player->IsGameModeSurvival())
+ {
+ if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block))
+ {
+ Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+
+ a_Player->UseEquippedItem();
+}
+
+
+
+
+
+void cItemHandler::OnFoodEaten(cWorld * a_World, cPlayer * a_Player, cItem * a_Item)
+{
+
+}
+
+
+
+
+
+char cItemHandler::GetMaxStackSize(void)
+{
+ if (m_ItemType < 256)
+ {
+ // All blocks can stack up to 64
+ return 64;
+ }
+
+ switch (m_ItemType) //sorted by id
+ {
+ case E_ITEM_ARROW: return 64;
+ case E_ITEM_BAKED_POTATO: return 64;
+ case E_ITEM_BLAZE_POWDER: return 64;
+ case E_ITEM_BLAZE_ROD: return 64;
+ case E_ITEM_BONE: return 64;
+ case E_ITEM_BOOK: return 64;
+ case E_ITEM_BOTTLE_O_ENCHANTING: return 64;
+ case E_ITEM_BOWL: return 64;
+ case E_ITEM_BREAD: return 64;
+ case E_ITEM_BREWING_STAND: return 64;
+ case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3
+ case E_ITEM_CARROT: return 64;
+ case E_ITEM_CAULDRON: return 64;
+ case E_ITEM_CLAY: return 64;
+ case E_ITEM_CLAY_BRICK: return 64;
+ case E_ITEM_CLOCK: return 64;
+ case E_ITEM_COAL: return 64;
+ case E_ITEM_COMPARATOR: return 64;
+ case E_ITEM_COMPASS: return 64;
+ case E_ITEM_COOKED_CHICKEN: return 64;
+ case E_ITEM_COOKED_FISH: return 64;
+ case E_ITEM_COOKED_PORKCHOP: return 64;
+ case E_ITEM_COOKIE: return 64;
+ case E_ITEM_DIAMOND: return 64;
+ case E_ITEM_DYE: return 64;
+ case E_ITEM_EGG: return 16;
+ case E_ITEM_EMERALD: return 64;
+ case E_ITEM_ENDER_PEARL: return 16;
+ case E_ITEM_EYE_OF_ENDER: return 64;
+ case E_ITEM_FEATHER: return 64;
+ case E_ITEM_FERMENTED_SPIDER_EYE: return 64;
+ case E_ITEM_FIRE_CHARGE: return 64;
+ case E_ITEM_FIREWORK_ROCKET: return 64;
+ case E_ITEM_FIREWORK_STAR: return 64;
+ case E_ITEM_FLINT: return 64;
+ case E_ITEM_FLOWER_POT: return 64;
+ case E_ITEM_GHAST_TEAR: return 64;
+ case E_ITEM_GLASS_BOTTLE: return 64;
+ case E_ITEM_GLISTERING_MELON: return 64;
+ case E_ITEM_GLOWSTONE_DUST: return 64;
+ case E_ITEM_GOLD: return 64;
+ case E_ITEM_GOLDEN_APPLE: return 64;
+ case E_ITEM_GOLDEN_CARROT: return 64;
+ case E_ITEM_GOLD_NUGGET: return 64;
+ case E_ITEM_GUNPOWDER: return 64;
+ case E_ITEM_HEAD: return 64;
+ case E_ITEM_IRON: return 64;
+ case E_ITEM_LEATHER: return 64;
+ case E_ITEM_MAGMA_CREAM: return 64;
+ case E_ITEM_MAP: return 64;
+ case E_ITEM_MELON_SEEDS: return 64;
+ case E_ITEM_MELON_SLICE: return 64;
+ case E_ITEM_NETHER_BRICK: return 64;
+ case E_ITEM_NETHER_WART: return 64;
+ case E_ITEM_PAINTINGS: return 64;
+ case E_ITEM_PAPER: return 64;
+ case E_ITEM_POISONOUS_POTATO: return 64;
+ case E_ITEM_POTATO: return 64;
+ case E_ITEM_PUMPKIN_PIE: return 64;
+ case E_ITEM_PUMPKIN_SEEDS: return 64;
+ case E_ITEM_RAW_BEEF: return 64;
+ case E_ITEM_RAW_CHICKEN: return 64;
+ case E_ITEM_RAW_FISH: return 64;
+ case E_ITEM_RAW_PORKCHOP: return 64;
+ case E_ITEM_RED_APPLE: return 64;
+ case E_ITEM_REDSTONE_DUST: return 64;
+ case E_ITEM_REDSTONE_REPEATER: return 64;
+ case E_ITEM_ROTTEN_FLESH: return 64;
+ case E_ITEM_SEEDS: return 64;
+ case E_ITEM_SIGN: return 16;
+ case E_ITEM_SLIMEBALL: return 64;
+ case E_ITEM_SNOWBALL: return 16;
+ case E_ITEM_SPAWN_EGG: return 64;
+ case E_ITEM_SPIDER_EYE: return 64;
+ case E_ITEM_STEAK: return 64;
+ case E_ITEM_STICK: return 64;
+ case E_ITEM_STRING: return 64;
+ case E_ITEM_SUGAR: return 64;
+ case E_ITEM_SUGAR_CANE: return 64;
+ case E_ITEM_WHEAT: return 64;
+ }
+ // By default items don't stack:
+ return 1;
+}
+
+
+
+
+
+bool cItemHandler::IsTool()
+{
+ // TODO: Rewrite this to list all tools specifically
+ return
+ (m_ItemType >= 256 && m_ItemType <= 259)
+ || (m_ItemType == 261)
+ || (m_ItemType >= 267 && m_ItemType <= 279)
+ || (m_ItemType >= 283 && m_ItemType <= 286)
+ || (m_ItemType >= 290 && m_ItemType <= 294)
+ || (m_ItemType >= 256 && m_ItemType <= 259)
+ || (m_ItemType == 325)
+ || (m_ItemType == 346);
+}
+
+
+
+
+
+bool cItemHandler::IsFood(void)
+{
+ switch (m_ItemType)
+ {
+ case E_ITEM_RED_APPLE:
+ case E_ITEM_GOLDEN_APPLE:
+ case E_ITEM_MUSHROOM_SOUP:
+ case E_ITEM_BREAD:
+ case E_ITEM_RAW_PORKCHOP:
+ case E_ITEM_COOKED_PORKCHOP:
+ case E_ITEM_MILK:
+ case E_ITEM_RAW_FISH:
+ case E_ITEM_COOKED_FISH:
+ case E_ITEM_COOKIE:
+ case E_ITEM_MELON_SLICE:
+ case E_ITEM_RAW_BEEF:
+ case E_ITEM_STEAK:
+ case E_ITEM_RAW_CHICKEN:
+ case E_ITEM_COOKED_CHICKEN:
+ case E_ITEM_ROTTEN_FLESH:
+ case E_ITEM_SPIDER_EYE:
+ case E_ITEM_CARROT:
+ case E_ITEM_POTATO:
+ case E_ITEM_BAKED_POTATO:
+ case E_ITEM_POISONOUS_POTATO:
+ {
+ return true;
+ }
+ } // switch (m_ItemType)
+ return false;
+}
+
+
+
+
+
+bool cItemHandler::IsPlaceable(void)
+{
+ // We can place any block that has a corresponding E_BLOCK_TYPE:
+ return (m_ItemType >= 1) && (m_ItemType <= E_BLOCK_MAX_TYPE_ID);
+}
+
+
+
+
+
+bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
+{
+ return false;
+}
+
+
+
+
+
+bool cItemHandler::GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+)
+{
+ ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers
+
+ if (m_ItemType > 256)
+ {
+ LOGERROR("%s: Item %d has no valid block!", __FUNCTION__, m_ItemType);
+ return false;
+ }
+
+ cBlockHandler * BlockH = BlockHandler(m_ItemType);
+ return BlockH->GetPlacementBlockTypeMeta(
+ a_World, a_Player,
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
+ a_CursorX, a_CursorY, a_CursorZ,
+ a_BlockType, a_BlockMeta
+ );
+}
+
+
+
+
+
+bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item)
+{
+ FoodInfo Info = GetFoodInfo();
+
+ if ((Info.FoodLevel > 0) || (Info.Saturation > 0.f))
+ {
+ bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation);
+
+ // If consumed and there's chance of foodpoisoning, do it:
+ if (Success && (Info.PoisonChance > 0))
+ {
+ cFastRandom r1;
+ if ((r1.NextInt(100, a_Player->GetUniqueID()) - Info.PoisonChance) <= 0)
+ {
+ a_Player->FoodPoison(300);
+ }
+ }
+
+ return Success;
+ }
+
+ return false;
+}
+
+
+
+
+
+cItemHandler::FoodInfo cItemHandler::GetFoodInfo()
+{
+ return FoodInfo(0, 0.f);
+}
+
+
+
+
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
new file mode 100644
index 000000000..e39bb054b
--- /dev/null
+++ b/src/Items/ItemHandler.h
@@ -0,0 +1,99 @@
+
+#pragma once
+
+#include "../Defines.h"
+#include "../Item.h"
+
+
+
+
+
+// fwd:
+class cWorld;
+class cPlayer;
+
+
+
+
+
+class cItemHandler
+{
+public:
+ cItemHandler(int a_ItemType);
+
+ /// 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, char a_Dir);
+
+ /// Called when the client sends the SHOOT status in the lclk packet
+ virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) {}
+
+ /// Called while the player diggs a block using this item
+ virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace);
+
+ /// Called when the player destroys a block using this item. This also calls the drop function for the destroyed block
+ virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_X, int a_Y, int a_Z);
+
+ /// Called after the player has eaten this item.
+ virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item);
+
+ /// Returns the maximum stack size for a given item
+ virtual char GetMaxStackSize(void);
+
+ struct FoodInfo
+ {
+ int FoodLevel;
+ double Saturation;
+ int PoisonChance; // 0 - 100, in percent. 0 = no chance of poisoning, 100 = sure poisoning
+
+ FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) :
+ FoodLevel(a_FoodLevel),
+ Saturation(a_Saturation),
+ PoisonChance(a_PoisonChance)
+ {
+ }
+ } ;
+
+ /// Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance)
+ virtual FoodInfo GetFoodInfo();
+
+ /// Lets the player eat a selected item. Returns true if the player ate the item
+ virtual bool EatItem(cPlayer *a_Player, cItem *a_Item);
+
+ /// Indicates if this item is a tool
+ virtual bool IsTool(void);
+
+ /// Indicates if this item is food
+ virtual bool IsFood(void);
+
+ /// Blocks simply get placed
+ virtual bool IsPlaceable(void);
+
+ /** 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, char 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
+ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType);
+
+ static cItemHandler * GetItemHandler(int a_ItemType);
+ static cItemHandler * GetItemHandler(const cItem & a_Item) { return GetItemHandler(a_Item.m_ItemType); }
+
+ static void Deinit();
+
+protected:
+ int m_ItemType;
+ static cItemHandler *CreateItemHandler(int m_ItemType);
+
+ static cItemHandler * m_ItemHandler[E_ITEM_LAST + 1];
+ static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized
+};
+
+//Short function
+inline cItemHandler *ItemHandler(int a_ItemType) { return cItemHandler::GetItemHandler(a_ItemType); }
diff --git a/src/Items/ItemHoe.h b/src/Items/ItemHoe.h
new file mode 100644
index 000000000..7b6b3e6ac
--- /dev/null
+++ b/src/Items/ItemHoe.h
@@ -0,0 +1,40 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cItemHoeHandler :
+ public cItemHandler
+{
+public:
+ cItemHoeHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+
+ if ((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS))
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, 0);
+
+ a_Player->UseEquippedItem();
+ return true;
+
+ }
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemLeaves.h b/src/Items/ItemLeaves.h
new file mode 100644
index 000000000..60222eaa9
--- /dev/null
+++ b/src/Items/ItemLeaves.h
@@ -0,0 +1,41 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemLeavesHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemLeavesHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ bool res = super::GetPlacementBlockTypeMeta(
+ a_World, a_Player,
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
+ a_CursorX, a_CursorY, a_CursorZ,
+ a_BlockType, a_BlockMeta
+ );
+ a_BlockMeta = a_BlockMeta | 0x4; //0x4 bit set means this is a player-placed leaves block, not to be decayed
+ return res;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemLighter.h b/src/Items/ItemLighter.h
new file mode 100644
index 000000000..4281a2d0c
--- /dev/null
+++ b/src/Items/ItemLighter.h
@@ -0,0 +1,59 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+#include "../Entities/TNTEntity.h"
+
+
+
+
+
+class cItemLighterHandler :
+ public cItemHandler
+{
+public:
+ cItemLighterHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
+ {
+ if (a_BlockFace < 0)
+ {
+ return false;
+ }
+
+ a_Player->UseEquippedItem();
+
+ switch (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))
+ {
+ case E_BLOCK_TNT:
+ {
+ // Activate the TNT:
+ a_World->BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f);
+ a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom
+ a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0);
+ break;
+ }
+ default:
+ {
+ // Light a fire next to/on top of the block if air:
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR)
+ {
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0);
+ break;
+ }
+ }
+ }
+
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemMinecart.h b/src/Items/ItemMinecart.h
new file mode 100644
index 000000000..f8eb31a49
--- /dev/null
+++ b/src/Items/ItemMinecart.h
@@ -0,0 +1,82 @@
+
+// ItemMinecart.h
+
+// Declares the various minecart ItemHandlers
+
+
+
+
+
+#pragma once
+
+#include "../Entities/Minecart.h"
+
+
+
+
+
+class cItemMinecartHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemMinecartHandler(int a_ItemType) :
+ super(a_ItemType)
+ {
+ }
+
+
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ if (a_Dir < 0)
+ {
+ return false;
+ }
+
+ // Check that there's rail in there:
+ BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ switch (Block)
+ {
+ case E_BLOCK_MINECART_TRACKS:
+ case E_BLOCK_POWERED_RAIL:
+ case E_BLOCK_DETECTOR_RAIL:
+ case E_BLOCK_ACTIVATOR_RAIL:
+ {
+ // These are allowed
+ break;
+ }
+ default:
+ {
+ LOGD("Used minecart on an unsuitable block %d (%s)", Block, ItemTypeToString(Block).c_str());
+ return false;
+ }
+ }
+
+ double x = (double)a_BlockX + 0.5;
+ double y = (double)a_BlockY + 0.5;
+ double z = (double)a_BlockZ + 0.5;
+ cMinecart * Minecart = NULL;
+ switch (m_ItemType)
+ {
+ case E_ITEM_MINECART: Minecart = new cEmptyMinecart (x, y, z); break;
+ case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (x, y, z); break;
+ case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace (x, y, z); break;
+ case E_ITEM_MINECART_WITH_TNT: Minecart = new cMinecartWithTNT (x, y, z); break;
+ case E_ITEM_MINECART_WITH_HOPPER: Minecart = new cMinecartWithHopper (x, y, z); break;
+ default:
+ {
+ ASSERT(!"Unhandled minecart item");
+ return false;
+ }
+ } // switch (m_ItemType)
+ Minecart->Initialize(a_World);
+ return true;
+ }
+
+} ;
+
+
+
+
diff --git a/src/Items/ItemPickaxe.h b/src/Items/ItemPickaxe.h
new file mode 100644
index 000000000..bde7f0905
--- /dev/null
+++ b/src/Items/ItemPickaxe.h
@@ -0,0 +1,92 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+class cItemPickaxeHandler :
+ public cItemHandler
+{
+public:
+ cItemPickaxeHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ char PickaxeLevel()
+ {
+ switch(m_ItemType)
+ {
+ case E_ITEM_WOODEN_PICKAXE:
+ case E_ITEM_GOLD_PICKAXE:
+ return 1;
+ case E_ITEM_STONE_PICKAXE:
+ return 2;
+ case E_ITEM_IRON_PICKAXE:
+ return 3;
+ case E_ITEM_DIAMOND_PICKAXE:
+ return 4;
+ default:
+ return 0;
+ }
+ }
+
+ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
+ {
+ switch(a_BlockType)
+ {
+ case E_BLOCK_OBSIDIAN:
+ {
+ return PickaxeLevel() >= 4;
+ }
+
+ case E_BLOCK_DIAMOND_BLOCK:
+ case E_BLOCK_DIAMOND_ORE:
+ case E_BLOCK_GOLD_BLOCK:
+ case E_BLOCK_GOLD_ORE:
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_REDSTONE_ORE_GLOWING:
+ case E_BLOCK_EMERALD_ORE:
+ {
+ return PickaxeLevel() >= 3;
+ }
+
+ case E_BLOCK_IRON_BLOCK:
+ case E_BLOCK_IRON_ORE:
+ case E_BLOCK_LAPIS_ORE:
+ case E_BLOCK_LAPIS_BLOCK:
+ {
+ return PickaxeLevel() >= 2;
+ }
+
+ case E_BLOCK_COAL_ORE:
+ case E_BLOCK_STONE:
+ case E_BLOCK_COBBLESTONE:
+ case E_BLOCK_END_STONE:
+ case E_BLOCK_MOSSY_COBBLESTONE:
+ case E_BLOCK_SANDSTONE_STAIRS:
+ case E_BLOCK_SANDSTONE:
+ case E_BLOCK_STONE_BRICKS:
+ case E_BLOCK_NETHER_BRICK:
+ case E_BLOCK_NETHERRACK:
+ case E_BLOCK_STONE_SLAB:
+ case E_BLOCK_DOUBLE_STONE_SLAB:
+ case E_BLOCK_STONE_PRESSURE_PLATE:
+ case E_BLOCK_BRICK:
+ case E_BLOCK_COBBLESTONE_STAIRS:
+ case E_BLOCK_STONE_BRICK_STAIRS:
+ case E_BLOCK_NETHER_BRICK_STAIRS:
+ case E_BLOCK_CAULDRON:
+ {
+ return PickaxeLevel() >= 1;
+ }
+ }
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemRedstoneDust.h b/src/Items/ItemRedstoneDust.h
new file mode 100644
index 000000000..b7860b187
--- /dev/null
+++ b/src/Items/ItemRedstoneDust.h
@@ -0,0 +1,38 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemRedstoneDustHandler : public cItemHandler
+{
+public:
+ cItemRedstoneDustHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = E_BLOCK_REDSTONE_WIRE;
+ a_BlockMeta = 0;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemRedstoneRepeater.h b/src/Items/ItemRedstoneRepeater.h
new file mode 100644
index 000000000..f69f24eb8
--- /dev/null
+++ b/src/Items/ItemRedstoneRepeater.h
@@ -0,0 +1,40 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../Blocks/BlockRedstoneRepeater.h"
+
+
+
+
+
+class cItemRedstoneRepeaterHandler :
+ public cItemHandler
+{
+public:
+ cItemRedstoneRepeaterHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool IsPlaceable() override
+ {
+ return true;
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF;
+ a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation());
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemSapling.h b/src/Items/ItemSapling.h
new file mode 100644
index 000000000..dc0810a45
--- /dev/null
+++ b/src/Items/ItemSapling.h
@@ -0,0 +1,42 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemSaplingHandler : public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemSaplingHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ bool res = super::GetPlacementBlockTypeMeta(
+ a_World, a_Player,
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
+ a_CursorX, a_CursorY, a_CursorZ,
+ a_BlockType, a_BlockMeta
+ );
+ // Only the lowest 3 bits are important
+ a_BlockMeta = a_BlockMeta & 0x7;
+ return res;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemSeeds.h b/src/Items/ItemSeeds.h
new file mode 100644
index 000000000..8ca86663f
--- /dev/null
+++ b/src/Items/ItemSeeds.h
@@ -0,0 +1,65 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cItemSeedsHandler :
+ public cItemHandler
+{
+public:
+ cItemSeedsHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ if (a_BlockFace != BLOCK_FACE_TOP)
+ {
+ // Only allow planting seeds from the top side of the block
+ return false;
+ }
+
+ // Only allow placement on farmland
+ int X = a_BlockX;
+ int Y = a_BlockY;
+ int Z = a_BlockZ;
+ AddFaceDirection(X, Y, Z, a_BlockFace, true);
+ if (a_World->GetBlock(X, Y, Z) != E_BLOCK_FARMLAND)
+ {
+ return false;
+ }
+
+ a_BlockMeta = 0;
+ switch (m_ItemType)
+ {
+ case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true;
+ case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true;
+ case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true;
+ case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true;
+ case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true;
+ default: a_BlockType = E_BLOCK_AIR; return true;
+ }
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemShears.h b/src/Items/ItemShears.h
new file mode 100644
index 000000000..6a17607ee
--- /dev/null
+++ b/src/Items/ItemShears.h
@@ -0,0 +1,62 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cItemShearsHandler :
+ public cItemHandler
+{
+public:
+ cItemShearsHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsTool(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ if (Block == E_BLOCK_LEAVES)
+ {
+ cItems Drops;
+ Drops.push_back(cItem(E_BLOCK_LEAVES, 1, a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x03));
+ a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ);
+
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
+ a_Player->UseEquippedItem();
+ return true;
+ }
+ return false;
+ }
+
+
+ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
+ {
+ switch (a_BlockType)
+ {
+ case E_BLOCK_COBWEB:
+ case E_BLOCK_VINES:
+ case E_BLOCK_LEAVES:
+ {
+ return true;
+ }
+ } // switch (a_BlockType)
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemShovel.h b/src/Items/ItemShovel.h
new file mode 100644
index 000000000..d0625ef1c
--- /dev/null
+++ b/src/Items/ItemShovel.h
@@ -0,0 +1,41 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+#include "../Blocks/BlockHandler.h"
+
+
+
+
+
+class cItemShovelHandler : public cItemHandler
+{
+public:
+ cItemShovelHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ if (Block == E_BLOCK_SNOW)
+ {
+ BlockHandler(Block)->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
+ a_Player->UseEquippedItem();
+ return true;
+ }
+ return false;
+ }
+
+ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
+ {
+ return (a_BlockType == E_BLOCK_SNOW);
+ }
+}; \ No newline at end of file
diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h
new file mode 100644
index 000000000..5ccd79e29
--- /dev/null
+++ b/src/Items/ItemSign.h
@@ -0,0 +1,51 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Blocks/BlockSign.h"
+
+
+
+
+
+class cItemSignHandler :
+ public cItemHandler
+{
+public:
+ cItemSignHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ if (a_BlockFace == BLOCK_FACE_TOP)
+ {
+ a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetRotation());
+ a_BlockType = E_BLOCK_SIGN_POST;
+ }
+ else
+ {
+ a_BlockMeta = cBlockSignHandler::DirectionToMetaData(a_BlockFace);
+ a_BlockType = E_BLOCK_WALLSIGN;
+ }
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h
new file mode 100644
index 000000000..26dd15b7d
--- /dev/null
+++ b/src/Items/ItemSpawnEgg.h
@@ -0,0 +1,52 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cItemSpawnEggHandler : public cItemHandler
+{
+public:
+ cItemSpawnEggHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+
+ }
+
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
+ {
+ if (a_BlockFace < 0)
+ {
+ return false;
+ }
+
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ if (a_BlockFace == BLOCK_FACE_BOTTOM)
+ {
+ a_BlockY--;
+ }
+
+ if (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, (cMonster::eType)(a_Item.m_ItemDamage)) >= 0)
+ {
+ if (a_Player->GetGameMode() != 1)
+ {
+ // The mob was spawned, "use" the item:
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ }
+ return true;
+ }
+
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemSugarcane.h b/src/Items/ItemSugarcane.h
new file mode 100644
index 000000000..ce93aa3e5
--- /dev/null
+++ b/src/Items/ItemSugarcane.h
@@ -0,0 +1,39 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemSugarcaneHandler :
+ public cItemHandler
+{
+public:
+ cItemSugarcaneHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ virtual bool GetPlacementBlockTypeMeta(
+ cWorld * a_World, 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
+ {
+ a_BlockType = E_BLOCK_SUGARCANE;
+ a_BlockMeta = 0;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemSword.h b/src/Items/ItemSword.h
new file mode 100644
index 000000000..a7c1d2432
--- /dev/null
+++ b/src/Items/ItemSword.h
@@ -0,0 +1,30 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cItemSwordHandler :
+ public cItemHandler
+{
+public:
+ cItemSwordHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
+ {
+ return (a_BlockType == E_BLOCK_COBWEB);
+ }
+} ;
+
+
+
+
diff --git a/src/Items/ItemThrowable.h b/src/Items/ItemThrowable.h
new file mode 100644
index 000000000..fc24e775a
--- /dev/null
+++ b/src/Items/ItemThrowable.h
@@ -0,0 +1,140 @@
+
+// ItemThrowable.h
+
+// Declares the itemhandlers for throwable items: eggs, snowballs and ender pearls
+
+
+
+
+
+#pragma once
+
+
+
+
+
+class cItemThrowableHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+public:
+ cItemThrowableHandler(int a_ItemType, cProjectileEntity::eKind a_ProjectileKind, double a_SpeedCoeff) :
+ super(a_ItemType),
+ m_ProjectileKind(a_ProjectileKind),
+ m_SpeedCoeff(a_SpeedCoeff)
+ {
+ }
+
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ if (!a_Player->IsGameModeCreative())
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ }
+
+ Vector3d Pos = a_Player->GetThrowStartPos();
+ Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff;
+ a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &Speed);
+
+ return true;
+ }
+
+protected:
+ cProjectileEntity::eKind m_ProjectileKind;
+ double m_SpeedCoeff;
+} ;
+
+
+
+
+
+class cItemEggHandler :
+ public cItemThrowableHandler
+{
+ typedef cItemThrowableHandler super;
+public:
+ cItemEggHandler(void) :
+ super(E_ITEM_EGG, cProjectileEntity::pkEgg, 30)
+ {
+ }
+} ;
+
+
+
+
+class cItemSnowballHandler :
+ public cItemThrowableHandler
+{
+ typedef cItemThrowableHandler super;
+
+public:
+ cItemSnowballHandler(void) :
+ super(E_ITEM_SNOWBALL, cProjectileEntity::pkSnowball, 30)
+ {
+ }
+} ;
+
+
+
+
+
+class cItemEnderPearlHandler :
+ public cItemThrowableHandler
+{
+ typedef cItemThrowableHandler super;
+
+public:
+ cItemEnderPearlHandler(void) :
+ super(E_ITEM_ENDER_PEARL, cProjectileEntity::pkEnderPearl, 30)
+ {
+ }
+} ;
+
+
+
+
+
+class cItemBottleOEnchantingHandler :
+ public cItemThrowableHandler
+{
+ typedef cItemThrowableHandler super;
+public:
+ cItemBottleOEnchantingHandler(void) :
+ super(E_ITEM_BOTTLE_O_ENCHANTING, cProjectileEntity::pkExpBottle, 10)
+ {
+ }
+};
+
+
+
+
+
+class cItemFireworkHandler :
+ public cItemThrowableHandler
+{
+ typedef cItemThrowableHandler super;
+public:
+ cItemFireworkHandler(void) :
+ super(E_ITEM_FIREWORK_ROCKET, cProjectileEntity::pkFirework, 0)
+ {
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR)
+ {
+ return false;
+ }
+
+ if (!a_Player->IsGameModeCreative())
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ }
+
+ a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, 0);
+
+ return true;
+ }
+
+}; \ No newline at end of file
diff --git a/src/LeakFinder.cpp b/src/LeakFinder.cpp
new file mode 100644
index 000000000..0f84adb2b
--- /dev/null
+++ b/src/LeakFinder.cpp
@@ -0,0 +1,1047 @@
+
+// LeakFinder.cpp
+
+// Finds memory leaks rather effectively
+
+// _X: downloaded from http://www.codeproject.com/Articles/3134/Memory-Leak-and-Exception-Trace-CRT-and-COM-Leaks - the real link is in the comments, RC11 version
+
+
+
+
+
+/**********************************************************************
+ *
+ * LEAKFINDER.CPP
+ *
+ *
+ *
+ * History:
+ * 2010-04-15 RC10 - Updated to VC10 RTM
+ * Fixed Bug: Application Verifier, thanks to handsinmypocket!
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3439751#xx3439751xx
+ * 2008-08-04 RC6 - Updated to VC9 RTM
+ * Fixed Bug: Missing "ole32.lib" LIB
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2253980#xx2253980xx
+ * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN"
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx
+ * Fixed Bug: Compiling with "/Wall"
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx
+ * Removed "#pragma init_seg (compiler)" from h-file
+ *
+ * 2005-12-30 RC5 - Now again VC8 RTM compatible
+ * - Added Xml-Output (like in the old Leakfinder)
+ * YOu need to define XML_LEAK_FINDER to activate it
+ * So you can use the LeakAnalyseTool from
+ * http://www.codeproject.com/tools/leakfinder.asp
+ *
+ * 2005-12-13 RC4 - Merged with the new "StackWalker"-project on
+ * http://www.codeproject.com/threads/StackWalker.asp
+ *
+ * 2005-08-01 RC3 - Merged with the new "StackWalker"-project on
+ * http://www.codeproject.com/threads/StackWalker.asp
+ *
+ * 2005-07-05 RC2 - First version with x86, IA64 and x64 support
+ *
+ * 2005-07-04 RC1 - Added "OutputOptions"
+ * - New define "INIT_LEAK_FINDER_VERBOSE" to
+ * display more info (for error reporting)
+ *
+ * 2005-07-01 Beta3 - Workaround for a bug in the new dbghelp.dll
+ * (version 6.5.3.7 from 2005-05-30; StakWalk64 no
+ * refused to produce an callstack on x86 systems
+ * if the context is NULL or has some registers set
+ * to 0 (for example Esp). This is against the
+ * documented behaviour of StackWalk64...)
+ * - First version with x64-support
+ *
+ * 2005-06-16 Beta1 First public release with the following features:
+ * - Completely rewritten in C++ (object oriented)
+ * - CRT-Leak-Report
+ * - COM-Leak-Report
+ * - Report is done via "OutputDebugString" so
+ * the line can directly selected in the debugger
+ * and is opening the corresponding file/line of
+ * the allocation
+ * - Tried to support x64 systems, bud had some
+ * trouble wih StackWalk64
+ * See: http://blog.kalmbachnet.de/?postid=43
+ *
+ * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Copyright (c) 2005-2010, Jochen Kalmbach
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of Jochen Kalmbach nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include <windows.h>
+#include <objidl.h> // Needed if compiled with "WIN32_LEAN_AND_MEAN"
+#include <tchar.h>
+#include <crtdbg.h>
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+
+#include "LeakFinder.h"
+
+// Currently only tested with MS VC++ 5 to 10
+#if (_MSC_VER < 1100) || (_MSC_VER > 1800)
+#error Only MS VC++ 5/6/7/7.1/8/9/10/11/12 supported. Check if the '_CrtMemBlockHeader' has not changed with this compiler!
+#endif
+
+
+/* _X: MSVC 2012 (MSC 1700) seems to use a different allocation scheme for STL containers,
+* allocating lots of small objects and running out of memory very soon
+* Thus for MSVC 2012 we cut the callstack buffer length in half
+*
+* _X 2013_08_25: The callstack tracking gets worse even for MSVC 2008, a single lua_state eats 50 MiB of RAM
+* Therefore I decided to further reduce the buffers from 0x2000 to 0x1000
+*/
+// Controlling the callstack depth
+#if (_MSC_VER < 1700)
+ #define MAX_CALLSTACK_LEN_BUF 0x1000
+#else
+ #define MAX_CALLSTACK_LEN_BUF 0x0800
+#endif
+
+
+
+
+
+#define IGNORE_CRT_ALLOC
+
+// disable 64-bit compatibility-checks (because we explicite have here either x86 or x64!)
+#pragma warning(disable:4312) // warning C4312: 'type cast' : conversion from 'DWORD' to 'LPCVOID' of greater size
+#pragma warning(disable:4826)
+
+
+// secure-CRT_functions are only available starting with VC8
+#if _MSC_VER < 1400
+#define _snprintf_s _snprintf
+#define _tcscat_s _tcscat
+#endif
+
+
+
+
+
+static std::string SimpleXMLEncode(LPCSTR szText)
+{
+ std::string szRet;
+ for (size_t i=0; i<strlen(szText); i++)
+ {
+ switch(szText[i])
+ {
+ case '&':
+ szRet.append("&amp;");
+ break;
+ case '<':
+ szRet.append("&lt;");
+ break;
+ case '>':
+ szRet.append("&gt;");
+ break;
+ case '"':
+ szRet.append("&quot;");
+ break;
+ case '\'':
+ szRet.append("&apos;");
+ break;
+ default:
+ szRet += szText[i];
+ }
+ }
+ return szRet;
+}
+
+
+
+
+
+LeakFinderOutput::LeakFinderOutput(int options, LPCSTR szSymPath)
+ : StackWalker(options, szSymPath)
+{
+}
+
+
+
+
+
+void LeakFinderOutput::OnLeakSearchStart(LPCSTR szLeakFinderName)
+{
+ CHAR buffer[1024];
+ _snprintf_s(buffer, 1024, "######## %s ########\n", szLeakFinderName);
+ this->OnOutput(buffer);
+}
+
+
+
+
+
+void LeakFinderOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize)
+{
+ CHAR buffer[1024];
+ _snprintf_s(buffer, 1024, "--------------- Key: %s, %d bytes ---------\n", szKeyName, nDataSize);
+ this->OnOutput(buffer);
+}
+
+
+
+
+
+void LeakFinderOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
+{
+ if ( (eType != lastEntry) && (entry.offset != 0) )
+ {
+ if ( ((this->m_options & LeakFinderShowCompleteCallstack) == 0) && (
+ (strstr(entry.lineFileName, "afxmem.cpp") != NULL) ||
+ (strstr(entry.lineFileName, "dbgheap.c") != NULL) ||
+ (strstr(entry.lineFileName, "new.cpp") != NULL) ||
+ (strstr(entry.lineFileName, "newop.cpp") != NULL) ||
+ (strstr(entry.lineFileName, "leakfinder.cpp") != NULL) ||
+ (strstr(entry.lineFileName, "stackwalker.cpp") != NULL)
+ ) )
+ {
+ return;
+ }
+ }
+ StackWalker::OnCallstackEntry(eType, entry);
+}
+
+
+
+
+
+// ####################################################################
+// XML-Output
+LeakFinderXmlOutput::LeakFinderXmlOutput()
+{
+ TCHAR szXMLFileName[1024];
+
+ GetModuleFileName(NULL, szXMLFileName, sizeof(szXMLFileName) / sizeof(TCHAR));
+ _tcscat_s(szXMLFileName, _T(".mem.xml-leaks"));
+#if _MSC_VER < 1400
+ m_fXmlFile = _tfopen(szXMLFileName, _T("w"));
+#else
+ m_fXmlFile = NULL;
+ _tfopen_s(&m_fXmlFile, szXMLFileName, _T("w"));
+#endif
+ if (m_fXmlFile != NULL)
+ {
+ SYSTEMTIME st;
+ GetLocalTime(&st);
+ fprintf(m_fXmlFile, "<MEMREPORT date=\"%.2d/%.2d/%.4d\" time=\"%.2d:%.2d:%.2d\">\n",
+ st.wMonth, st.wDay, st.wYear,
+ st.wHour, st.wMinute, st.wSecond);
+ }
+ else
+ {
+ MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND);
+ }
+}
+
+
+
+
+
+LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName) :
+ m_Progress(10)
+{
+#if _MSC_VER < 1400
+ m_fXmlFile = _tfopen(szFileName, _T("w"));
+#else
+ m_fXmlFile = NULL;
+ _tfopen_s(&m_fXmlFile, szFileName, _T("w"));
+#endif
+ if (m_fXmlFile == NULL)
+ {
+ MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND);
+ }
+ else
+ {
+ fprintf(m_fXmlFile, "<MEMREPORT>\n");
+ }
+}
+
+
+
+
+
+LeakFinderXmlOutput::~LeakFinderXmlOutput()
+{
+ if (m_fXmlFile != NULL)
+ {
+ // Write the ending-tags and close the file
+ fprintf(m_fXmlFile, "</MEMREPORT>\n");
+ fclose(m_fXmlFile);
+ }
+ m_fXmlFile = NULL;
+}
+
+
+
+
+
+void LeakFinderXmlOutput::OnLeakSearchStart(LPCSTR sszLeakFinderName)
+{
+}
+
+
+
+
+
+void LeakFinderXmlOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize)
+{
+ if (m_fXmlFile != NULL)
+ {
+ fprintf(m_fXmlFile, "\t<LEAK requestID=\"%s\" size=\"%d\">\n", SimpleXMLEncode(szKeyName).c_str(), nDataSize);
+ }
+ if (--m_Progress == 0)
+ {
+ m_Progress = 100;
+ putc('.', stdout);
+ }
+}
+
+
+
+
+
+void LeakFinderXmlOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
+{
+ if (m_fXmlFile != NULL)
+ {
+ if (eType != lastEntry)
+ {
+ fprintf(m_fXmlFile, "\t\t<STACKENTRY decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(entry.undName).c_str(), entry.offsetFromSmybol);
+ fprintf(m_fXmlFile, "srcfile=\"%s\" line=\"%d\" line_offset=\"%+ld\" ", SimpleXMLEncode(entry.lineFileName).c_str(), entry.lineNumber, entry.offsetFromLine);
+ fprintf(m_fXmlFile, "module=\"%s\" base=\"%08lx\" ", SimpleXMLEncode(entry.moduleName).c_str(), entry.baseOfImage);
+ fprintf(m_fXmlFile, "/>\n");
+ }
+ else
+ {
+ fprintf(m_fXmlFile, "\t</LEAK>\n");
+ }
+ }
+}
+
+
+
+
+
+// ##########################################################################
+// ##########################################################################
+// ##########################################################################
+// Base class for storing contexts in a hashtable
+template <typename HASHTABLE_KEY> class ContextHashtableBase
+{
+public:
+ ContextHashtableBase(SIZE_T sizeOfHastable, LPCSTR finderName)
+ {
+ SIZE_T s = sizeOfHastable*sizeof(AllocHashEntryType);
+ m_hHeap = HeapCreate(0, 10*1024 + s, 0);
+ if (m_hHeap == NULL)
+ throw;
+ pAllocHashTable = (AllocHashEntryType*) own_malloc(s);
+ sAllocEntries = sizeOfHastable;
+ m_finderName = own_strdup(finderName);
+ }
+
+protected:
+ virtual ~ContextHashtableBase()
+ {
+ if (pAllocHashTable != NULL)
+ own_free(pAllocHashTable);
+ pAllocHashTable = NULL;
+
+ own_free(m_finderName);
+ m_finderName = NULL;
+
+ if (m_hHeap != NULL)
+ HeapDestroy(m_hHeap);
+ }
+
+ __inline LPVOID own_malloc(SIZE_T size)
+ {
+ return HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, size);
+ }
+ __inline VOID own_free(LPVOID memblock)
+ {
+ HeapFree(m_hHeap, 0, memblock);
+ }
+ __inline CHAR *own_strdup(const char *str)
+ {
+ size_t len = strlen(str)+1;
+ CHAR *c = (CHAR*)own_malloc(len);
+#if _MSC_VER >= 1400
+ strcpy_s(c, len, str);
+#else
+ strcpy(c, str);
+#endif
+ return c;
+ }
+
+ // Disables this leak-finder
+ virtual LONG Disable() = 0;
+ // enables the leak-finder again...
+ virtual LONG Enable() = 0;
+
+protected:
+ // Entry for each allocation
+ typedef struct AllocHashEntryType {
+ HASHTABLE_KEY key;
+ SIZE_T nDataSize; // Size of the allocated memory
+ struct AllocHashEntryType *Next;
+ CONTEXT c;
+ PVOID pStackBaseAddr;
+ SIZE_T nMaxStackSize;
+
+ PVOID pCallstackOffset;
+ SIZE_T nCallstackLen;
+ char pcCallstackAddr[MAX_CALLSTACK_LEN_BUF]; // min of both values...
+ } AllocHashEntryType;
+
+protected:
+ virtual SIZE_T HashFunction(HASHTABLE_KEY &key) = 0;
+ virtual BOOL IsKeyEmpty(HASHTABLE_KEY &key) = 0;
+ virtual VOID SetEmptyKey(HASHTABLE_KEY &key) = 0;
+ virtual VOID GetKeyAsString(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) = 0;
+ //virtual SIZE_T GetNativeBytes(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) { return 0; }
+
+public:
+ VOID Insert(HASHTABLE_KEY &key, CONTEXT &context, SIZE_T nDataSize)
+ {
+ SIZE_T HashIdx;
+ AllocHashEntryType *pHashEntry;
+
+ // generate hash-value
+ HashIdx = HashFunction(key);
+
+ pHashEntry = &pAllocHashTable[HashIdx];
+ if (IsKeyEmpty(pHashEntry->key) != FALSE) {
+ // Entry is empty...
+ }
+ else {
+ // Entry is not empy! make a list of entries for this hash value...
+ while(pHashEntry->Next != NULL) {
+ pHashEntry = pHashEntry->Next;
+ }
+
+ pHashEntry->Next = (AllocHashEntryType*) own_malloc(sizeof(AllocHashEntryType));
+ g_CurrentMemUsage += CRTTable::AllocHashEntryTypeSize;
+ pHashEntry = pHashEntry->Next;
+ if (pHashEntry == NULL)
+ {
+ // Exhausted the available memory?
+ return;
+ }
+ }
+ pHashEntry->key = key;
+ pHashEntry->nDataSize = nDataSize;
+ pHashEntry->Next = NULL;
+#ifdef _M_IX86
+ pHashEntry->pCallstackOffset = (LPVOID) min(context.Ebp, context.Esp);
+#elif _M_X64
+ pHashEntry->pCallstackOffset = (LPVOID) min(context.Rdi, context.Rsp);
+#elif _M_IA64
+ pHashEntry->pCallstackOffset = (LPVOID) min(context.IntSp, context.RsBSP);
+#else
+#error "Platform not supported!"
+#endif
+ pHashEntry->c = context;
+
+ // Query the max. stack-area:
+ MEMORY_BASIC_INFORMATION MemBuffer;
+ if(VirtualQuery((LPCVOID) pHashEntry->pCallstackOffset, &MemBuffer, sizeof(MemBuffer)) > 0)
+ {
+ pHashEntry->pStackBaseAddr = MemBuffer.BaseAddress;
+ pHashEntry->nMaxStackSize = MemBuffer.RegionSize;
+ }
+ else
+ {
+ pHashEntry->pStackBaseAddr = 0;
+ pHashEntry->nMaxStackSize = 0;
+ }
+
+ SIZE_T bytesToRead = MAX_CALLSTACK_LEN_BUF;
+ if (pHashEntry->nMaxStackSize > 0)
+ {
+ SIZE_T len = ((SIZE_T) pHashEntry->pStackBaseAddr + pHashEntry->nMaxStackSize) - (SIZE_T)pHashEntry->pCallstackOffset;
+ bytesToRead = min(len, MAX_CALLSTACK_LEN_BUF);
+ }
+ // Now read the callstack:
+ if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) pHashEntry->pCallstackOffset, &(pHashEntry->pcCallstackAddr), bytesToRead, &(pHashEntry->nCallstackLen)) == 0)
+ {
+ // Could not read memory...
+ pHashEntry->nCallstackLen = 0;
+ pHashEntry->pCallstackOffset = 0;
+ } // read callstack
+ } // Insert
+
+ BOOL Remove(HASHTABLE_KEY &key)
+ {
+ SIZE_T HashIdx;
+ AllocHashEntryType *pHashEntry, *pHashEntryLast;
+
+ // get the Hash-Value
+ HashIdx = HashFunction(key);
+
+ pHashEntryLast = NULL;
+ pHashEntry = &pAllocHashTable[HashIdx];
+ while(pHashEntry != NULL) {
+ if (pHashEntry->key == key) {
+ // release my memory
+ if (pHashEntryLast == NULL) {
+ // It is an entry in the table, so do not release this memory
+ if (pHashEntry->Next == NULL) {
+ // It was the last entry, so empty the table entry
+ SetEmptyKey(pAllocHashTable[HashIdx].key);
+ //memset(&pAllocHashTable[HashIdx], 0, sizeof(pAllocHashTable[HashIdx]));
+ }
+ else {
+ // There are some more entries, so shorten the list
+ AllocHashEntryType *pTmp = pHashEntry->Next;
+ *pHashEntry = *(pHashEntry->Next);
+ own_free(pTmp);
+ g_CurrentMemUsage -= CRTTable::AllocHashEntryTypeSize;
+ }
+ return TRUE;
+ }
+ else {
+ // now, I am in an dynamic allocated entry (it was a collision)
+ pHashEntryLast->Next = pHashEntry->Next;
+ own_free(pHashEntry);
+ g_CurrentMemUsage -= CRTTable::AllocHashEntryTypeSize;
+ return TRUE;
+ }
+ }
+ pHashEntryLast = pHashEntry;
+ pHashEntry = pHashEntry->Next;
+ }
+
+ // if we are here, we could not find the RequestID
+ return FALSE;
+ }
+
+ AllocHashEntryType *Find(HASHTABLE_KEY &key)
+ {
+ SIZE_T HashIdx;
+ AllocHashEntryType *pHashEntry;
+
+ // get the Hash-Value
+ HashIdx = HashFunction(key);
+
+ pHashEntry = &pAllocHashTable[HashIdx];
+ while(pHashEntry != NULL) {
+ if (pHashEntry->key == key) {
+ return pHashEntry;
+ }
+ pHashEntry = pHashEntry->Next;
+ }
+
+ // entry was not found!
+ return NULL;
+ }
+
+ // For the followong static-var See comment in "ShowCallstack"...
+ static BOOL CALLBACK ReadProcessMemoryFromHashEntry64(
+ HANDLE hProcess, // hProcess must be a pointer to an hash-entry!
+ DWORD64 lpBaseAddress,
+ PVOID lpBuffer,
+ DWORD nSize,
+ LPDWORD lpNumberOfBytesRead,
+ LPVOID pUserData // optional data, which was passed in "ShowCallstack"
+ )
+ {
+ *lpNumberOfBytesRead = 0;
+ AllocHashEntryType *pHashEntry = (AllocHashEntryType*) pUserData;
+ if (pHashEntry == NULL)
+ {
+ return FALSE;
+ }
+
+ if ( ( (DWORD64)lpBaseAddress >= (DWORD64)pHashEntry->pCallstackOffset) && ((DWORD64)lpBaseAddress <= ((DWORD64)pHashEntry->pCallstackOffset+pHashEntry->nCallstackLen)) ) {
+ // Memory is located in saved Callstack:
+ // Calculate the offset
+ DWORD dwOffset = (DWORD) ((DWORD64)lpBaseAddress - (DWORD64)pHashEntry->pCallstackOffset);
+ DWORD dwSize = __min(nSize, MAX_CALLSTACK_LEN_BUF-dwOffset);
+ memcpy(lpBuffer, &(pHashEntry->pcCallstackAddr[dwOffset]), dwSize);
+ *lpNumberOfBytesRead = dwSize;
+ if (dwSize != nSize)
+ {
+ return FALSE;
+ }
+ *lpNumberOfBytesRead = nSize;
+ return TRUE;
+ }
+
+ if (*lpNumberOfBytesRead == 0) // Memory could not be found
+ {
+ if ( ( (DWORD64)lpBaseAddress < (DWORD64)pHashEntry->pStackBaseAddr) || ((DWORD64)lpBaseAddress > ((DWORD64)pHashEntry->pStackBaseAddr+pHashEntry->nMaxStackSize)) )
+ {
+ // Stackwalking is done by reading the "real memory" (normally this happens when the StackWalk64 tries to read some code)
+ SIZE_T st = 0;
+ BOOL bRet = ReadProcessMemory(hProcess, (LPCVOID) lpBaseAddress, lpBuffer, nSize, &st);
+ *lpNumberOfBytesRead = (DWORD) st;
+ return bRet;
+ }
+ }
+
+ return TRUE;
+ }
+
+ VOID ShowLeaks(LeakFinderOutput &leakFinderOutput)
+ {
+ SIZE_T ulTemp;
+ AllocHashEntryType *pHashEntry;
+ ULONG ulCount = 0;
+ SIZE_T ulLeaksByte = 0;
+
+ leakFinderOutput.OnLeakSearchStart(this->m_finderName);
+
+ // Move throu every entry
+ CHAR keyName[1024];
+ for(ulTemp = 0; ulTemp < this->sAllocEntries; ulTemp++) {
+ pHashEntry = &pAllocHashTable[ulTemp];
+ if (IsKeyEmpty(pHashEntry->key) == FALSE) {
+ while(pHashEntry != NULL) {
+ ulCount++;
+ CONTEXT c;
+ memcpy(&c, &(pHashEntry->c), sizeof(CONTEXT));
+
+ this->GetKeyAsString(pHashEntry->key, keyName, 1024);
+
+ leakFinderOutput.OnLeakStartEntry(keyName, pHashEntry->nDataSize);
+ leakFinderOutput.ShowCallstack(GetCurrentThread(), &c, ReadProcessMemoryFromHashEntry64, pHashEntry);
+
+ // Count the number of leaky bytes
+ ulLeaksByte += pHashEntry->nDataSize;
+
+ pHashEntry = pHashEntry->Next;
+ } // while
+ }
+ }
+ }
+
+ AllocHashEntryType *pAllocHashTable;
+ SIZE_T sAllocEntries;
+ HANDLE m_hHeap;
+ LPSTR m_finderName;
+ bool m_bSupressUselessLines;
+}; // template <typename HASHTABLE_KEY> class ContextHashtableBase
+
+
+
+
+
+// ##########################################################################
+// ##########################################################################
+// ##########################################################################
+// Specialization for CRT-Leaks:
+// VC5 has excluded all types in release-builds
+#ifdef _DEBUG
+
+// The follwoing is copied from dbgint.h:
+// <CRT_INTERNALS>
+/*
+* For diagnostic purpose, blocks are allocated with extra information and
+* stored in a doubly-linked list. This makes all blocks registered with
+* how big they are, when they were allocated, and what they are used for.
+*/
+
+// forward declaration:
+#ifndef _M_CEE_PURE
+#define MyAllocHookCallingConvention __cdecl
+#endif
+#if _MSC_VER >= 1400
+#ifdef _M_CEE
+#define MyAllocHookCallingConvention __clrcall
+#endif
+#endif
+
+static int MyAllocHookCallingConvention MyAllocHook(int nAllocType, void *pvData,
+ size_t nSize, int nBlockUse, long lRequest,
+#if _MSC_VER <= 1100 // Special case for VC 5 and before
+ const char * szFileName,
+#else
+ const unsigned char * szFileName,
+#endif
+ int nLine);
+
+static _CRT_ALLOC_HOOK s_pfnOldCrtAllocHook = NULL;
+static LONG s_CrtDisableCount = 0;
+static LONG s_lMallocCalled = 0;
+
+
+
+
+
+class CRTTable : public ContextHashtableBase<LONG>
+{
+public:
+ CRTTable() : ContextHashtableBase<LONG>(1021, "CRT-Leaks")
+ {
+ // save the previous alloc hook
+ s_pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook);
+ }
+
+ virtual ~CRTTable()
+ {
+ _CrtSetAllocHook(s_pfnOldCrtAllocHook);
+ }
+
+ virtual LONG Disable()
+ {
+ return InterlockedIncrement(&s_CrtDisableCount);
+ }
+ virtual LONG Enable()
+ {
+ return InterlockedDecrement(&s_CrtDisableCount);
+ }
+
+ virtual SIZE_T HashFunction(LONG &key)
+ {
+ // I couldn´t find any better and faster
+ return key % sAllocEntries;
+ }
+ virtual BOOL IsKeyEmpty(LONG &key)
+ {
+ if (key == 0)
+ return TRUE;
+ return FALSE;
+ }
+ virtual VOID SetEmptyKey(LONG &key)
+ {
+ key = 0;
+ }
+ virtual VOID GetKeyAsString(LONG &key, CHAR *szName, SIZE_T nBufferLen)
+ {
+#if _MSC_VER < 1400
+ _snprintf_s(szName, nBufferLen, "%d", key);
+#else
+ _snprintf_s(szName, nBufferLen, nBufferLen, "%d", key);
+#endif
+ }
+
+ static const int AllocHashEntryTypeSize = sizeof(AllocHashEntryType);
+
+protected:
+ CHAR *m_pBuffer;
+ SIZE_T m_maxBufferLen;
+ SIZE_T m_bufferLen;
+}; // class CRTTable
+
+
+#define nNoMansLandSize 4
+
+typedef struct _CrtMemBlockHeader
+{
+ struct _CrtMemBlockHeader * pBlockHeaderNext;
+ struct _CrtMemBlockHeader * pBlockHeaderPrev;
+ char * szFileName;
+ int nLine;
+#ifdef _WIN64
+ /* These items are reversed on Win64 to eliminate gaps in the struct
+ * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
+ * maintained in the debug heap.
+ */
+ int nBlockUse;
+ size_t nDataSize;
+#else /* _WIN64 */
+ size_t nDataSize;
+ int nBlockUse;
+#endif /* _WIN64 */
+ long lRequest;
+ unsigned char gap[nNoMansLandSize];
+ /* followed by:
+ * unsigned char data[nDataSize];
+ * unsigned char anotherGap[nNoMansLandSize];
+ */
+} _CrtMemBlockHeader;
+#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
+#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
+// </CRT_INTERNALS>
+
+static CRTTable *g_pCRTTable = NULL;
+
+size_t g_CurrentMemUsage = 0;
+
+
+
+
+
+// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!
+static int MyAllocHook(int nAllocType, void *pvData,
+ size_t nSize, int nBlockUse, long lRequest,
+#if _MSC_VER <= 1100 // Special case for VC 5
+ const char * szFileName,
+#else
+ const unsigned char * szFileName,
+#endif
+ int nLine)
+{
+ //static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };
+ //static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };
+
+#ifdef IGNORE_CRT_ALLOC
+ if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations
+ return TRUE;
+#endif
+ extern int _crtDbgFlag;
+ if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) )
+ {
+ // Someone has disabled that the runtime should log this allocation
+ // so we do not log this allocation
+ if (s_pfnOldCrtAllocHook != NULL)
+ s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ return TRUE;
+ }
+
+ // Handle the Disable/Enable setting
+ if (InterlockedExchangeAdd(&s_CrtDisableCount, 0) != 0)
+ {
+ return TRUE;
+ }
+
+ // Prevent from reentrat calls
+ if (InterlockedIncrement(&s_lMallocCalled) > 1)
+ {
+ // I was already called
+ InterlockedDecrement(&s_lMallocCalled);
+ // call the previous alloc hook
+ if (s_pfnOldCrtAllocHook != NULL)
+ s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ return TRUE;
+ }
+
+ _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) );
+ _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );
+
+ if (nAllocType == _HOOK_FREE)
+ {
+ // freeing
+ // Try to get the header information
+ if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
+ // get the ID
+ _CrtMemBlockHeader *pHead;
+ // get a pointer to memory block header
+ pHead = pHdr(pvData);
+ nSize = pHead->nDataSize;
+ lRequest = pHead->lRequest; // This is the ID!
+
+ if (pHead->nBlockUse == _IGNORE_BLOCK)
+ {
+ InterlockedDecrement(&s_lMallocCalled);
+ if (s_pfnOldCrtAllocHook != NULL)
+ {
+ s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ }
+ return TRUE;
+ }
+ }
+ if (lRequest != 0)
+ {
+ // RequestID was found
+ size_t temp = g_CurrentMemUsage;
+ g_CurrentMemUsage -= nSize ;
+ g_pCRTTable->Remove(lRequest);
+ if (g_CurrentMemUsage > temp)
+ {
+ printf("********************************************\n");
+ printf("** Server detected underflow in memory **\n");
+ printf("** usage counter. Something is not right. **\n");
+ printf("** Writing memory dump into memdump.xml **\n");
+ printf("********************************************\n");
+ printf("Please wait\n");
+
+ LeakFinderXmlOutput Output("memdump.xml");
+ DumpUsedMemory(&Output);
+
+ printf("\nMemory dump complete. Server will now abort.\n");
+ abort();
+ }
+ }
+ } // freeing
+
+ if (nAllocType == _HOOK_REALLOC)
+ {
+ // re-allocating
+ // Try to get the header information
+ if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
+ BOOL bRet;
+ LONG lReallocRequest;
+ // get the ID
+ _CrtMemBlockHeader *pHead;
+ // get a pointer to memory block header
+ pHead = pHdr(pvData);
+ // Try to find the RequestID in the Hash-Table, mark it that it was freed
+ lReallocRequest = pHead->lRequest;
+ size_t temp = g_CurrentMemUsage;
+ g_CurrentMemUsage -= pHead->nDataSize;
+ bRet = g_pCRTTable->Remove(lReallocRequest);
+ if (g_CurrentMemUsage > temp)
+ {
+ printf("********************************************\n");
+ printf("** Server detected underflow in memory **\n");
+ printf("** usage counter. Something is not right. **\n");
+ printf("** Writing memory dump into memdump.xml **\n");
+ printf("********************************************\n");
+ printf("Please wait\n");
+
+ LeakFinderXmlOutput Output("memdump.xml");
+ DumpUsedMemory(&Output);
+
+ printf("\nMemory dump complete. Server will now abort.\n");
+ abort();
+ }
+ } // ValidHeapPointer
+ } // re-allocating
+
+ //if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {
+ if (nAllocType == _HOOK_FREE)
+ {
+ InterlockedDecrement(&s_lMallocCalled);
+ // call the previous alloc hook
+ if (s_pfnOldCrtAllocHook != NULL)
+ {
+ s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ }
+ return TRUE;
+ }
+
+ CONTEXT c;
+ GET_CURRENT_CONTEXT(c, CONTEXT_FULL);
+
+ // Only insert in the Hash-Table if it is not a "freeing"
+ if (nAllocType != _HOOK_FREE)
+ {
+ if (lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)
+ {
+ //No need to check for overflow since we are checking if we are getting higher than 1gb.
+ //If we change this, then we probably would want an overflow check.
+ g_CurrentMemUsage += nSize ;
+ g_pCRTTable->Insert(lRequest, c, nSize);
+
+ if (g_CurrentMemUsage > 1536 * 1024* 1024)
+ {
+ printf("******************************************\n");
+ printf("** Server reached 1.5 GiB memory usage, **\n");
+ printf("** something is probably wrong. **\n");
+ printf("** Writing memory dump into memdump.xml **\n");
+ printf("******************************************\n");
+ printf("Please wait\n");
+
+ LeakFinderXmlOutput Output("memdump.xml");
+ DumpUsedMemory(&Output);
+
+ printf("\nMemory dump complete. Server will now abort.\n");
+ abort();
+ }
+ }
+ }
+
+ InterlockedDecrement(&s_lMallocCalled);
+ // call the previous alloc hook
+ if (s_pfnOldCrtAllocHook != NULL)
+ s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ return TRUE; // allow the memory operation to proceed
+} // MyAllocHook
+
+#endif // _DEBUG
+
+
+
+
+
+// ##########################################################################
+// ##########################################################################
+// ##########################################################################
+// Init/Deinit functions
+
+HRESULT InitLeakFinder()
+{
+ #ifdef _DEBUG
+ g_pCRTTable = new CRTTable();
+ #endif
+ return S_OK;
+}
+
+
+
+
+
+void DumpUsedMemory(LeakFinderOutput * output)
+{
+ LeakFinderOutput *pLeakFinderOutput = output;
+
+ #ifdef _DEBUG
+ g_pCRTTable->Disable();
+ #endif
+
+ if (pLeakFinderOutput == NULL)
+ {
+ pLeakFinderOutput = new LeakFinderOutput();
+ }
+
+ // explicitly load the modules:
+ pLeakFinderOutput->LoadModules();
+
+ #ifdef _DEBUG
+ g_pCRTTable->ShowLeaks(*pLeakFinderOutput);
+ #endif
+
+ if (output == NULL)
+ {
+ delete pLeakFinderOutput;
+ }
+}
+
+
+
+
+
+void DeinitLeakFinder(LeakFinderOutput *output)
+{
+ DumpUsedMemory(output);
+
+ #ifdef _DEBUG
+ delete g_pCRTTable;
+ g_pCRTTable = NULL;
+ #endif
+}
+
+
+
+
+
+void DeinitLeakFinder()
+{
+ DeinitLeakFinder(NULL);
+}
+
+
+
+
diff --git a/src/LeakFinder.h b/src/LeakFinder.h
new file mode 100644
index 000000000..e63b9ec5d
--- /dev/null
+++ b/src/LeakFinder.h
@@ -0,0 +1,156 @@
+/**********************************************************************
+ *
+ * LEAKFINDER.H
+ *
+ *
+ *
+ * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Copyright (c) 2005-2010, Jochen Kalmbach
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of Jochen Kalmbach nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+// #pragma once is supported starting with _MCS_VER 1000,
+// so we need not to check the version (because we only support _MSC_VER >= 1100)!
+#pragma once
+
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+HRESULT InitLeakFinder();
+void DeinitLeakFinder();
+
+#ifdef __cplusplus
+}
+#endif
+
+
+// The following is only available if the file is CPP
+#ifdef __cplusplus
+
+#include "StackWalker.h"
+
+// Interface for output...
+class LeakFinderOutput : public StackWalker
+{
+public:
+ typedef enum LeakFinderOptions
+ {
+ // No addition info will be retrived
+ // (only the address is available)
+ LeakFinderNone = 0,
+ LeakFinderShowCompleteCallstack = 0x1000
+ } LeakFinderOptions;
+
+ LeakFinderOutput(int options = OptionsAll, LPCSTR szSymPath = NULL);
+ virtual void OnLeakSearchStart(LPCSTR sszLeakFinderName);
+ virtual void OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize);
+protected:
+ virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
+ virtual void OnOutput(LPCSTR szText)
+ {
+ printf(szText);
+ StackWalker::OnOutput(szText);
+ }
+ virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
+ {
+ if (strcmp(szFuncName, "SymGetLineFromAddr64") == 0) return;
+ StackWalker::OnDbgHelpErr(szFuncName, gle, addr);
+ }
+};
+
+class LeakFinderXmlOutput : public LeakFinderOutput
+{
+public:
+ LeakFinderXmlOutput();
+ virtual ~LeakFinderXmlOutput();
+ LeakFinderXmlOutput(LPCTSTR szFileName);
+ virtual void OnLeakSearchStart(LPCSTR sszLeakFinderName);
+ virtual void OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize);
+protected:
+ virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
+ virtual void OnOutput(LPCSTR szText) { }
+ virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { }
+
+ FILE * m_fXmlFile;
+ int m_Progress;
+};
+
+// C++ interface:
+void DeinitLeakFinder(LeakFinderOutput *output);
+
+class ZZZ_LeakFinder
+{
+public:
+ ZZZ_LeakFinder()
+ {
+ m_pXml = NULL;
+#ifdef XML_LEAK_FINDER
+ m_pXml = new LeakFinderXmlOutput();
+#endif
+ InitLeakFinder();
+ }
+ ~ZZZ_LeakFinder()
+ {
+ DeinitLeakFinder(m_pXml);
+ if (m_pXml != NULL) delete m_pXml;
+ }
+protected:
+ LeakFinderXmlOutput *m_pXml;
+};
+
+#if defined(INIT_LEAK_FINDER)
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+#pragma warning (disable:4074)
+
+// WARNING: If you enable this option, the code might run without the CRT being initialized or after the CRT was deinitialized!!!
+// Currently the code is not designed to bypass the CRT...
+//#pragma init_seg (compiler)
+ZZZ_LeakFinder zzz_LeakFinder;
+
+#if _MSC_VER >= 1200
+#pragma warning(pop)
+#else
+#pragma warning(default:4074)
+#endif
+#endif
+
+#endif // __cplusplus
+
+
+
+
+extern void DumpUsedMemory(LeakFinderOutput * output = NULL);
+
+
+
+
+
diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp
new file mode 100644
index 000000000..d7e60e458
--- /dev/null
+++ b/src/LightingThread.cpp
@@ -0,0 +1,562 @@
+
+// LightingThread.cpp
+
+// Implements the cLightingThread class representing the thread that processes requests for lighting
+
+#include "Globals.h"
+#include "LightingThread.h"
+#include "ChunkMap.h"
+#include "World.h"
+
+
+
+
+
+/// If more than this many chunks are in the queue, a warning is printed to the log
+#define WARN_ON_QUEUE_SIZE 800
+
+
+
+
+
+/// Chunk data callback that takes the chunk data and puts them into cLightingThread's m_BlockTypes[] / m_HeightMap[]:
+class cReader :
+ public cChunkDataCallback
+{
+ virtual void BlockTypes(const BLOCKTYPE * a_Type) override
+ {
+ // ROW is a block of 16 Blocks, one whole row is copied at a time (hopefully the compiler will optimize that)
+ // C++ doesn't permit copying arrays, but arrays as a part of a struct is ok :)
+ typedef struct {BLOCKTYPE m_Row[16]; } ROW;
+ ROW * InputRows = (ROW *)a_Type;
+ ROW * OutputRows = (ROW *)m_BlockTypes;
+ int InputIdx = 0;
+ int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3;
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ OutputRows[OutputIdx] = InputRows[InputIdx++];
+ OutputIdx += 3;
+ } // for z
+ // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows
+ // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip
+ OutputIdx += cChunkDef::Width * 6;
+ } // for y
+ } // BlockTypes()
+
+
+ virtual void HeightMap(const cChunkDef::HeightMap * a_Heightmap) override
+ {
+ typedef struct {HEIGHTTYPE m_Row[16]; } ROW;
+ ROW * InputRows = (ROW *)a_Heightmap;
+ ROW * OutputRows = (ROW *)m_HeightMap;
+ int InputIdx = 0;
+ int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ OutputRows[OutputIdx] = InputRows[InputIdx++];
+ OutputIdx += 3;
+ } // for z
+ }
+
+public:
+ int m_ReadingChunkX; // 0, 1 or 2; x-offset of the chunk we're reading from the BlockTypes start
+ int m_ReadingChunkZ; // 0, 1 or 2; z-offset of the chunk we're reading from the BlockTypes start
+ BLOCKTYPE * m_BlockTypes; // 3x3 chunks of block types, organized as a single XZY blob of data (instead of 3x3 XZY blobs)
+ HEIGHTTYPE * m_HeightMap; // 3x3 chunks of height map, organized as a single XZY blob of data (instead of 3x3 XZY blobs)
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cLightingThread:
+
+cLightingThread::cLightingThread(void) :
+ super("cLightingThread"),
+ m_World(NULL)
+{
+}
+
+
+
+
+
+cLightingThread::~cLightingThread()
+{
+ Stop();
+}
+
+
+
+
+
+bool cLightingThread::Start(cWorld * a_World)
+{
+ ASSERT(m_World == NULL); // Not started yet
+ m_World = a_World;
+
+ return super::Start();
+}
+
+
+
+
+
+void cLightingThread::Stop(void)
+{
+ {
+ cCSLock Lock(m_CS);
+ for (sItems::iterator itr = m_Queue.begin(), end = m_Queue.end(); itr != end; ++itr)
+ {
+ delete itr->m_ChunkStay;
+ }
+ m_Queue.clear();
+ }
+ m_ShouldTerminate = true;
+ m_evtItemAdded.Set();
+
+ Wait();
+}
+
+
+
+
+
+void cLightingThread::QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter)
+{
+ ASSERT(m_World != NULL); // Did you call Start() properly?
+
+ cChunkStay * ChunkStay = new cChunkStay(m_World);
+ ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ + 1);
+ ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ);
+ ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ - 1);
+ ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ + 1);
+ ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ - 1);
+ ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ + 1);
+ ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ);
+ ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ - 1);
+ ChunkStay->Enable();
+ ChunkStay->Load();
+ cCSLock Lock(m_CS);
+ m_Queue.push_back(sItem(a_ChunkX, a_ChunkZ, ChunkStay, a_CallbackAfter));
+ if (m_Queue.size() > WARN_ON_QUEUE_SIZE)
+ {
+ LOGINFO("Lighting thread overloaded, %d items in queue", m_Queue.size());
+ }
+ m_evtItemAdded.Set();
+}
+
+
+
+
+
+void cLightingThread::WaitForQueueEmpty(void)
+{
+ cCSLock Lock(m_CS);
+ while (!m_ShouldTerminate && (!m_Queue.empty() || !m_PostponedQueue.empty()))
+ {
+ cCSUnlock Unlock(Lock);
+ m_evtQueueEmpty.Wait();
+ }
+}
+
+
+
+
+
+size_t cLightingThread::GetQueueLength(void)
+{
+ cCSLock Lock(m_CS);
+ return m_Queue.size() + m_PostponedQueue.size();
+}
+
+
+
+
+
+void cLightingThread::ChunkReady(int a_ChunkX, int a_ChunkZ)
+{
+ // Check all the items in the m_PostponedQueue, if the chunk is their neighbor, move the item to m_Queue
+
+ bool NewlyAdded = false;
+ {
+ cCSLock Lock(m_CS);
+ for (sItems::iterator itr = m_PostponedQueue.begin(); itr != m_PostponedQueue.end(); )
+ {
+ if (
+ (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) &&
+ (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1)
+ )
+ {
+ // It is a neighbor
+ m_Queue.push_back(*itr);
+ itr = m_PostponedQueue.erase(itr);
+ NewlyAdded = true;
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - m_PostponedQueue[]
+ } // Lock(m_CS)
+
+ if (NewlyAdded)
+ {
+ m_evtItemAdded.Set(); // Notify the thread it has some work to do
+ }
+}
+
+
+
+
+
+void cLightingThread::Execute(void)
+{
+ while (true)
+ {
+ {
+ cCSLock Lock(m_CS);
+ if (m_Queue.size() == 0)
+ {
+ cCSUnlock Unlock(Lock);
+ m_evtItemAdded.Wait();
+ }
+ }
+
+ if (m_ShouldTerminate)
+ {
+ return;
+ }
+
+ // Process one items from the queue:
+ sItem Item;
+ {
+ cCSLock Lock(m_CS);
+ if (m_Queue.empty())
+ {
+ continue;
+ }
+ Item = m_Queue.front();
+ m_Queue.pop_front();
+ if (m_Queue.empty())
+ {
+ m_evtQueueEmpty.Set();
+ }
+ } // CSLock(m_CS)
+
+ LightChunk(Item);
+ }
+}
+
+
+
+
+
+
+void cLightingThread::LightChunk(cLightingThread::sItem & a_Item)
+{
+ cChunkDef::BlockNibbles BlockLight, SkyLight;
+
+ if (!ReadChunks(a_Item.x, a_Item.z))
+ {
+ // Neighbors not available. Re-queue in the postponed queue
+ cCSLock Lock(m_CS);
+ m_PostponedQueue.push_back(a_Item);
+ return;
+ }
+
+ /*
+ // DEBUG: torch somewhere:
+ m_BlockTypes[19 + 24 * cChunkDef::Width * 3 + (m_HeightMap[24 + 24 * cChunkDef::Width * 3] / 2) * BlocksPerYLayer] = E_BLOCK_TORCH;
+ // m_HeightMap[24 + 24 * cChunkDef::Width * 3]++;
+ */
+
+ PrepareBlockLight();
+ CalcLight(m_BlockLight);
+
+ PrepareSkyLight();
+
+ /*
+ // DEBUG: Save chunk data with highlighted seeds for visual inspection:
+ cFile f4;
+ if (
+ f4.Open(Printf("Chunk_%d_%d_seeds.grab", a_Item.x, a_Item.z), cFile::fmWrite)
+ )
+ {
+ for (int z = 0; z < cChunkDef::Width * 3; z++)
+ {
+ for (int y = cChunkDef::Height / 2; y >= 0; y--)
+ {
+ unsigned char Seeds [cChunkDef::Width * 3];
+ memcpy(Seeds, m_BlockTypes + y * BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3);
+ for (int x = 0; x < cChunkDef::Width * 3; x++)
+ {
+ if (m_IsSeed1[y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x])
+ {
+ Seeds[x] = E_BLOCK_DIAMOND_BLOCK;
+ }
+ }
+ f4.Write(Seeds, cChunkDef::Width * 3);
+ }
+ }
+ }
+ //*/
+
+ CalcLight(m_SkyLight);
+
+ /*
+ // DEBUG: Save XY slices of the chunk data and lighting for visual inspection:
+ cFile f1, f2, f3;
+ if (
+ f1.Open(Printf("Chunk_%d_%d_data.grab", a_Item.x, a_Item.z), cFile::fmWrite) &&
+ f2.Open(Printf("Chunk_%d_%d_sky.grab", a_Item.x, a_Item.z), cFile::fmWrite) &&
+ f3.Open(Printf("Chunk_%d_%d_glow.grab", a_Item.x, a_Item.z), cFile::fmWrite)
+ )
+ {
+ for (int z = 0; z < cChunkDef::Width * 3; z++)
+ {
+ for (int y = cChunkDef::Height / 2; y >= 0; y--)
+ {
+ f1.Write(m_BlockTypes + y * BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3);
+ unsigned char SkyLight [cChunkDef::Width * 3];
+ unsigned char BlockLight[cChunkDef::Width * 3];
+ for (int x = 0; x < cChunkDef::Width * 3; x++)
+ {
+ SkyLight[x] = m_SkyLight [y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4;
+ BlockLight[x] = m_BlockLight[y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4;
+ }
+ f2.Write(SkyLight, cChunkDef::Width * 3);
+ f3.Write(BlockLight, cChunkDef::Width * 3);
+ }
+ }
+ }
+ //*/
+
+ CompressLight(m_BlockLight, BlockLight);
+ CompressLight(m_SkyLight, SkyLight);
+
+ m_World->ChunkLighted(a_Item.x, a_Item.z, BlockLight, SkyLight);
+
+ if (a_Item.m_Callback != NULL)
+ {
+ a_Item.m_Callback->Call(a_Item.x, a_Item.z);
+ }
+ delete a_Item.m_ChunkStay;
+}
+
+
+
+
+
+bool cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ)
+{
+ cReader Reader;
+ Reader.m_BlockTypes = m_BlockTypes;
+ Reader.m_HeightMap = m_HeightMap;
+
+ for (int z = 0; z < 3; z++)
+ {
+ Reader.m_ReadingChunkZ = z;
+ for (int x = 0; x < 3; x++)
+ {
+ Reader.m_ReadingChunkX = x;
+ if (!m_World->GetChunkData(a_ChunkX + x - 1, a_ChunkZ + z - 1, Reader))
+ {
+ return false;
+ }
+ } // for z
+ } // for x
+
+ memset(m_BlockLight, 0, sizeof(m_BlockLight));
+ memset(m_SkyLight, 0, sizeof(m_SkyLight));
+ return true;
+}
+
+
+
+
+
+void cLightingThread::PrepareSkyLight(void)
+{
+ // Clear seeds:
+ memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
+ m_NumSeeds = 0;
+
+ // Walk every column that has all XZ neighbors
+ for (int z = 1; z < cChunkDef::Width * 3 - 1; z++)
+ {
+ int BaseZ = z * cChunkDef::Width * 3;
+ for (int x = 1; x < cChunkDef::Width * 3 - 1; x++)
+ {
+ int idx = BaseZ + x;
+ int Current = m_HeightMap[idx] + 1;
+ int Neighbor1 = m_HeightMap[idx + 1] + 1; // X + 1
+ int Neighbor2 = m_HeightMap[idx - 1] + 1; // X - 1
+ int Neighbor3 = m_HeightMap[idx + cChunkDef::Width * 3] + 1; // Z + 1
+ int Neighbor4 = m_HeightMap[idx - cChunkDef::Width * 3] + 1; // Z - 1
+ int MaxNeighbor = std::max(std::max(Neighbor1, Neighbor2), std::max(Neighbor3, Neighbor4)); // Maximum of the four neighbors
+
+ // Fill the column from the top down to Current with all-light:
+ for (int y = cChunkDef::Height - 1, Index = idx + y * BlocksPerYLayer; y >= Current; y--, Index -= BlocksPerYLayer)
+ {
+ m_SkyLight[Index] = 15;
+ }
+
+ // Add Current as a seed:
+ if (Current < cChunkDef::Height)
+ {
+ int CurrentIdx = idx + Current * BlocksPerYLayer;
+ m_IsSeed1[CurrentIdx] = true;
+ m_SeedIdx1[m_NumSeeds++] = CurrentIdx;
+ }
+
+ // Add seed from Current up to the highest neighbor:
+ for (int y = Current + 1, Index = idx + y * BlocksPerYLayer; y < MaxNeighbor; y++, Index += BlocksPerYLayer)
+ {
+ m_IsSeed1[Index] = true;
+ m_SeedIdx1[m_NumSeeds++] = Index;
+ }
+ }
+ }
+}
+
+
+
+
+
+void cLightingThread::PrepareBlockLight(void)
+{
+ // Clear seeds:
+ memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
+ memset(m_IsSeed2, 0, sizeof(m_IsSeed2));
+ m_NumSeeds = 0;
+
+ // Walk every column that has all XZ neighbors, make a seed for each light-emitting block:
+ for (int z = 1; z < cChunkDef::Width * 3 - 1; z++)
+ {
+ int BaseZ = z * cChunkDef::Width * 3;
+ for (int x = 1; x < cChunkDef::Width * 3 - 1; x++)
+ {
+ int idx = BaseZ + x;
+ for (int y = m_HeightMap[idx], Index = idx + y * BlocksPerYLayer; y >= 0; y--, Index -= BlocksPerYLayer)
+ {
+ if (g_BlockLightValue[m_BlockTypes[Index]] == 0)
+ {
+ continue;
+ }
+
+ // Add current block as a seed:
+ m_IsSeed1[Index] = true;
+ m_SeedIdx1[m_NumSeeds++] = Index;
+
+ // Light it up:
+ m_BlockLight[Index] = g_BlockLightValue[m_BlockTypes[Index]];
+ }
+ }
+ }
+}
+
+
+
+
+
+void cLightingThread::CalcLight(NIBBLETYPE * a_Light)
+{
+ int NumSeeds2 = 0;
+ while (m_NumSeeds > 0)
+ {
+ // Buffer 1 -> buffer 2
+ memset(m_IsSeed2, 0, sizeof(m_IsSeed2));
+ NumSeeds2 = 0;
+ CalcLightStep(a_Light, m_NumSeeds, m_IsSeed1, m_SeedIdx1, NumSeeds2, m_IsSeed2, m_SeedIdx2);
+ if (NumSeeds2 == 0)
+ {
+ return;
+ }
+
+ // Buffer 2 -> buffer 1
+ memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
+ m_NumSeeds = 0;
+ CalcLightStep(a_Light, NumSeeds2, m_IsSeed2, m_SeedIdx2, m_NumSeeds, m_IsSeed1, m_SeedIdx1);
+ }
+}
+
+
+
+
+
+void cLightingThread::CalcLightStep(
+ NIBBLETYPE * a_Light,
+ int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn,
+ int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut
+)
+{
+ int NumSeedsOut = 0;
+ for (int i = 0; i < a_NumSeedsIn; i++)
+ {
+ int SeedIdx = a_SeedIdxIn[i];
+ int SeedX = SeedIdx % (cChunkDef::Width * 3);
+ int SeedZ = (SeedIdx / (cChunkDef::Width * 3)) % (cChunkDef::Width * 3);
+ int SeedY = SeedIdx / BlocksPerYLayer;
+
+ // Propagate seed:
+ if (SeedX < cChunkDef::Width * 3 - 1)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx + 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ if (SeedX > 0)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx - 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ if (SeedZ < cChunkDef::Width * 3 - 1)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ if (SeedZ > 0)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ if (SeedY < cChunkDef::Height - 1)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ if (SeedY > 0)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ } // for i - a_SeedIdxIn[]
+ a_NumSeedsOut = NumSeedsOut;
+}
+
+
+
+
+
+void cLightingThread::CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight)
+{
+ int InIdx = cChunkDef::Width * 49; // Index to the first nibble of the middle chunk in the a_LightArray
+ int OutIdx = 0;
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x += 2)
+ {
+ a_ChunkLight[OutIdx++] = (a_LightArray[InIdx + 1] << 4) | a_LightArray[InIdx];
+ InIdx += 2;
+ }
+ InIdx += cChunkDef::Width * 2;
+ }
+ // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows
+ // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip
+ InIdx += cChunkDef::Width * cChunkDef::Width * 6;
+ }
+}
+
+
+
+
diff --git a/src/LightingThread.h b/src/LightingThread.h
new file mode 100644
index 000000000..498755025
--- /dev/null
+++ b/src/LightingThread.h
@@ -0,0 +1,181 @@
+
+// LightingThread.h
+
+// Interfaces to the cLightingThread class representing the thread that processes requests for lighting
+
+/*
+Lighting is done on whole chunks. For each chunk to be lighted, the whole 3x3 chunk area around it is read,
+then it is processed, so that the middle chunk area has valid lighting, and the lighting is copied into the ChunkMap.
+Lighting is calculated in full char arrays instead of nibbles, so that accessing the arrays is fast.
+Lighting is calculated in a flood-fill fashion:
+1. Generate seeds from where the light spreads (full skylight / light-emitting blocks)
+2. For each seed:
+ - Spread the light 1 block in each of the 6 cardinal directions, if the blocktype allows
+ - If the recipient block has had lower lighting value than that being spread, make it a new seed
+3. Repeat step 2, until there are no more seeds
+The seeds need two fast operations:
+ - Check if a block at [x, y, z] is already a seed
+ - Get the next seed in the row
+For that reason it is stored in two arrays, one stores a bool saying a seed is in that position,
+the other is an array of seed coords, encoded as a single int.
+Step 2 needs two separate storages for old seeds and new seeds, so there are two actual storages for that purpose,
+their content is swapped after each full step-2-cycle.
+
+The thread has two queues of chunks that are to be lighted.
+The first queue, m_Queue, is the only one that is publicly visible, chunks get queued there by external requests.
+The second one, m_PostponedQueue, is for chunks that have been taken out of m_Queue and didn't have neighbors ready.
+Chunks from m_PostponedQueue are moved back into m_Queue when their neighbors get valid, using the ChunkReady callback.
+*/
+
+
+
+#pragma once
+
+#include "OSSupport/IsThread.h"
+#include "ChunkDef.h"
+
+
+
+
+
+// fwd: "cWorld.h"
+class cWorld;
+
+// fwd: "cChunkMap.h"
+class cChunkStay;
+
+
+
+
+
+class cLightingThread :
+ public cIsThread
+{
+ typedef cIsThread super;
+
+public:
+
+ cLightingThread(void);
+ ~cLightingThread();
+
+ bool Start(cWorld * a_World);
+
+ void Stop(void);
+
+ /// Queues the entire chunk for lighting
+ void QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter = NULL);
+
+ /// Blocks until the queue is empty or the thread is terminated
+ void WaitForQueueEmpty(void);
+
+ size_t GetQueueLength(void);
+
+ /// Called from cWorld when a chunk gets valid. Chunks in m_PostponedQueue may need moving into m_Queue
+ void ChunkReady(int a_ChunkX, int a_ChunkZ);
+
+protected:
+
+ struct sItem
+ {
+ int x, z;
+ cChunkStay * m_ChunkStay;
+ cChunkCoordCallback * m_Callback;
+
+ sItem(void) {} // empty default constructor needed
+ sItem(int a_X, int a_Z, cChunkStay * a_ChunkStay, cChunkCoordCallback * a_Callback) :
+ x(a_X),
+ z(a_Z),
+ m_ChunkStay(a_ChunkStay),
+ m_Callback(a_Callback)
+ {
+ }
+ } ;
+
+ typedef std::list<sItem> sItems;
+
+ cWorld * m_World;
+ cCriticalSection m_CS;
+ sItems m_Queue;
+ sItems m_PostponedQueue; // Chunks that have been postponed due to missing neighbors
+ cEvent m_evtItemAdded; // Set when queue is appended, or to stop the thread
+ cEvent m_evtQueueEmpty; // Set when the queue gets empty
+
+ // Buffers for the 3x3 chunk data
+ // These buffers alone are 1.7 MiB in size, therefore they cannot be located on the stack safely - some architectures may have only 1 MiB for stack, or even less
+ // Placing the buffers into the object means that this object can light chunks only in one thread!
+ // The blobs are XZY organized as a whole, instead of 3x3 XZY-organized subarrays ->
+ // -> This means data has to be scatterred when reading and gathered when writing!
+ static const int BlocksPerYLayer = cChunkDef::Width * cChunkDef::Width * 3 * 3;
+ BLOCKTYPE m_BlockTypes[BlocksPerYLayer * cChunkDef::Height];
+ NIBBLETYPE m_BlockLight[BlocksPerYLayer * cChunkDef::Height];
+ NIBBLETYPE m_SkyLight [BlocksPerYLayer * cChunkDef::Height];
+ HEIGHTTYPE m_HeightMap [BlocksPerYLayer];
+
+ // Seed management (5.7 MiB)
+ // Two buffers, in each calc step one is set as input and the other as output, then in the next step they're swapped
+ // Each seed is represented twice in this structure - both as a "list" and as a "position".
+ // "list" allows fast traversal from seed to seed
+ // "position" allows fast checking if a coord is already a seed
+ unsigned char m_IsSeed1 [BlocksPerYLayer * cChunkDef::Height];
+ unsigned int m_SeedIdx1[BlocksPerYLayer * cChunkDef::Height];
+ unsigned char m_IsSeed2 [BlocksPerYLayer * cChunkDef::Height];
+ unsigned int m_SeedIdx2[BlocksPerYLayer * cChunkDef::Height];
+ int m_NumSeeds;
+
+ virtual void Execute(void) override;
+
+ /// Lights the entire chunk. If neighbor chunks don't exist, touches them and re-queues the chunk
+ void LightChunk(sItem & a_Item);
+
+ /// Prepares m_BlockTypes and m_HeightMap data; returns false if any of the chunks fail. Zeroes out the light arrays
+ bool ReadChunks(int a_ChunkX, int a_ChunkZ);
+
+ /// Uses m_HeightMap to initialize the m_SkyLight[] data; fills in seeds for the skylight
+ void PrepareSkyLight(void);
+
+ /// Uses m_BlockTypes to initialize the m_BlockLight[] data; fills in seeds for the blocklight
+ void PrepareBlockLight(void);
+
+ /// Calculates light in the light array specified, using stored seeds
+ void CalcLight(NIBBLETYPE * a_Light);
+
+ /// Does one step in the light calculation - one seed propagation and seed recalculation
+ void CalcLightStep(
+ NIBBLETYPE * a_Light,
+ int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn,
+ int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut
+ );
+
+ /// Compresses from 1-block-per-byte (faster calc) into 2-blocks-per-byte (MC storage):
+ void CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight);
+
+ inline void PropagateLight(
+ NIBBLETYPE * a_Light,
+ int a_SrcIdx, int a_DstIdx,
+ int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut
+ )
+ {
+ ASSERT(a_SrcIdx >= 0);
+ ASSERT(a_SrcIdx < ARRAYCOUNT(m_SkyLight));
+ ASSERT(a_DstIdx >= 0);
+ ASSERT(a_DstIdx < ARRAYCOUNT(m_BlockTypes));
+
+ if (a_Light[a_SrcIdx] <= a_Light[a_DstIdx] + g_BlockSpreadLightFalloff[m_BlockTypes[a_DstIdx]])
+ {
+ // We're not offering more light than the dest block already has
+ return;
+ }
+
+ a_Light[a_DstIdx] = a_Light[a_SrcIdx] - g_BlockSpreadLightFalloff[m_BlockTypes[a_DstIdx]];
+ if (!a_IsSeedOut[a_DstIdx])
+ {
+ a_IsSeedOut[a_DstIdx] = true;
+ a_SeedIdxOut[a_NumSeedsOut++] = a_DstIdx;
+ }
+ }
+
+} ;
+
+
+
+
diff --git a/src/LineBlockTracer.cpp b/src/LineBlockTracer.cpp
new file mode 100644
index 000000000..9fcbca915
--- /dev/null
+++ b/src/LineBlockTracer.cpp
@@ -0,0 +1,262 @@
+
+// LineBlockTracer.cpp
+
+// Implements the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points
+
+#include "Globals.h"
+#include "LineBlockTracer.h"
+#include "Vector3d.h"
+#include "World.h"
+#include "Chunk.h"
+
+
+
+
+
+
+cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) :
+ super(a_World, a_Callbacks)
+{
+}
+
+
+
+
+
+bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End)
+{
+ cLineBlockTracer Tracer(a_World, a_Callbacks);
+ return Tracer.Trace(a_Start.x, a_Start.y, a_Start.z, a_End.x, a_End.y, a_End.z);
+}
+
+
+
+
+
+bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks &a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ)
+{
+ cLineBlockTracer Tracer(a_World, a_Callbacks);
+ return Tracer.Trace(a_StartX, a_StartY, a_StartZ, a_EndX, a_EndY, a_EndZ);
+}
+
+
+
+
+
+bool cLineBlockTracer::Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ)
+{
+ // Initialize the member veriables:
+ m_StartX = a_StartX;
+ m_StartY = a_StartY;
+ m_StartZ = a_StartZ;
+ m_EndX = a_EndX;
+ m_EndY = a_EndY;
+ m_EndZ = a_EndZ;
+ m_DirX = (m_StartX < m_EndX) ? 1 : -1;
+ m_DirY = (m_StartY < m_EndY) ? 1 : -1;
+ m_DirZ = (m_StartZ < m_EndZ) ? 1 : -1;
+ m_CurrentFace = BLOCK_FACE_NONE;
+
+ // Check the start coords, adjust into the world:
+ if (m_StartY < 0)
+ {
+ if (m_EndY < 0)
+ {
+ // Nothing to trace
+ m_Callbacks->OnNoMoreHits();
+ return true;
+ }
+ FixStartBelowWorld();
+ m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ);
+ }
+ else if (m_StartY >= cChunkDef::Height)
+ {
+ if (m_EndY >= cChunkDef::Height)
+ {
+ m_Callbacks->OnNoMoreHits();
+ return true;
+ }
+ FixStartAboveWorld();
+ m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ);
+ }
+
+ m_CurrentX = (int)floor(m_StartX);
+ m_CurrentY = (int)floor(m_StartY);
+ m_CurrentZ = (int)floor(m_StartZ);
+
+ m_DiffX = m_EndX - m_StartX;
+ m_DiffY = m_EndY - m_StartY;
+ m_DiffZ = m_EndZ - m_StartZ;
+
+ // The actual trace is handled with ChunkMapCS locked by calling our Item() for the specified chunk
+ int BlockX = (int)floor(m_StartX);
+ int BlockZ = (int)floor(m_StartZ);
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ);
+ return m_World->DoWithChunk(ChunkX, ChunkZ, *this);
+}
+
+
+
+
+
+void cLineBlockTracer::FixStartAboveWorld(void)
+{
+ // We must set the start Y to less than cChunkDef::Height so that it is considered inside the world later on
+ // Therefore we use an EPS-offset from the height, as small as reasonably possible.
+ const double Height = (double)cChunkDef::Height - 0.00001;
+ CalcXZIntersection(Height, m_StartX, m_StartZ);
+ m_StartY = Height;
+}
+
+
+
+
+
+void cLineBlockTracer::FixStartBelowWorld(void)
+{
+ CalcXZIntersection(0, m_StartX, m_StartZ);
+ m_StartY = 0;
+}
+
+
+
+
+
+void cLineBlockTracer::CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ)
+{
+ double Ratio = (m_StartY - a_Y) / (m_StartY - m_EndY);
+ a_IntersectX = m_StartX + (m_EndX - m_StartX) * Ratio;
+ a_IntersectZ = m_StartZ + (m_EndZ - m_StartZ) * Ratio;
+}
+
+
+
+
+
+bool cLineBlockTracer::MoveToNextBlock(void)
+{
+ // Find out which of the current block's walls gets hit by the path:
+ static const double EPS = 0.00001;
+ double Coeff = 1;
+ enum eDirection
+ {
+ dirNONE,
+ dirX,
+ dirY,
+ dirZ,
+ } Direction = dirNONE;
+ if (abs(m_DiffX) > EPS)
+ {
+ double DestX = (m_DirX > 0) ? (m_CurrentX + 1) : m_CurrentX;
+ Coeff = (DestX - m_StartX) / m_DiffX;
+ if (Coeff <= 1)
+ {
+ Direction = dirX;
+ }
+ }
+ if (abs(m_DiffY) > EPS)
+ {
+ double DestY = (m_DirY > 0) ? (m_CurrentY + 1) : m_CurrentY;
+ double CoeffY = (DestY - m_StartY) / m_DiffY;
+ if (CoeffY < Coeff)
+ {
+ Coeff = CoeffY;
+ Direction = dirY;
+ }
+ }
+ if (abs(m_DiffZ) > EPS)
+ {
+ double DestZ = (m_DirZ > 0) ? (m_CurrentZ + 1) : m_CurrentZ;
+ double CoeffZ = (DestZ - m_StartZ) / m_DiffZ;
+ if (CoeffZ < Coeff)
+ {
+ Coeff = CoeffZ;
+ Direction = dirZ;
+ }
+ }
+
+ // Based on the wall hit, adjust the current coords
+ switch (Direction)
+ {
+ case dirX: m_CurrentX += m_DirX; m_CurrentFace = (m_DirX > 0) ? BLOCK_FACE_XM : BLOCK_FACE_XP; break;
+ case dirY: m_CurrentY += m_DirY; m_CurrentFace = (m_DirY > 0) ? BLOCK_FACE_YM : BLOCK_FACE_YP; break;
+ case dirZ: m_CurrentZ += m_DirZ; m_CurrentFace = (m_DirZ > 0) ? BLOCK_FACE_ZM : BLOCK_FACE_ZP; break;
+ case dirNONE: return false;
+ }
+ return true;
+}
+
+
+
+
+
+bool cLineBlockTracer::Item(cChunk * a_Chunk)
+{
+ ASSERT((m_CurrentY >= 0) && (m_CurrentY < cChunkDef::Height)); // This should be provided by FixStartAboveWorld() / FixStartBelowWorld()
+
+ // This is the actual line tracing loop.
+ bool Finished = false;
+ while (true)
+ {
+ // Report the current block through the callbacks:
+ if (a_Chunk == NULL)
+ {
+ m_Callbacks->OnNoChunk();
+ return false;
+ }
+ if (a_Chunk->IsValid())
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ int RelX = m_CurrentX - a_Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = m_CurrentZ - a_Chunk->GetPosZ() * cChunkDef::Width;
+ a_Chunk->GetBlockTypeMeta(RelX, m_CurrentY, RelZ, BlockType, BlockMeta);
+ if (m_Callbacks->OnNextBlock(m_CurrentX, m_CurrentY, m_CurrentZ, BlockType, BlockMeta, m_CurrentFace))
+ {
+ // The callback terminated the trace
+ return false;
+ }
+ }
+ else
+ {
+ if (m_Callbacks->OnNextBlockNoData(m_CurrentX, m_CurrentY, m_CurrentZ, m_CurrentFace))
+ {
+ // The callback terminated the trace
+ return false;
+ }
+ }
+
+ // Move to next block
+ if (!MoveToNextBlock())
+ {
+ // We've reached the end
+ m_Callbacks->OnNoMoreHits();
+ return true;
+ }
+
+ // Update the current chunk
+ if (a_Chunk != NULL)
+ {
+ a_Chunk = a_Chunk->GetNeighborChunk(m_CurrentX, m_CurrentZ);
+ }
+
+ if ((m_CurrentY < 0) || (m_CurrentY >= cChunkDef::Height))
+ {
+ // We've gone out of the world, that's the end of this trace
+ double IntersectX, IntersectZ;
+ CalcXZIntersection(m_CurrentY, IntersectX, IntersectZ);
+ if (m_Callbacks->OnOutOfWorld(IntersectX, m_CurrentY, IntersectZ))
+ {
+ // The callback terminated the trace
+ return false;
+ }
+ m_Callbacks->OnNoMoreHits();
+ return true;
+ }
+ }
+}
+
+
+
+
diff --git a/src/LineBlockTracer.h b/src/LineBlockTracer.h
new file mode 100644
index 000000000..ccbb70ea6
--- /dev/null
+++ b/src/LineBlockTracer.h
@@ -0,0 +1,87 @@
+
+// LineBlockTracer.h
+
+// Declares the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points
+
+
+
+
+
+#pragma once
+
+#include "BlockTracer.h"
+
+
+
+
+
+// fwd: Chunk.h
+class cChunk;
+
+// fwd: cChunkMap.h
+typedef cItemCallback<cChunk> cChunkCallback;
+
+
+
+
+
+
+class cLineBlockTracer :
+ public cBlockTracer,
+ public cChunkCallback
+{
+ typedef cBlockTracer super;
+
+public:
+ cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks);
+
+ /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits())
+ bool Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ);
+
+ // Utility functions for simple one-line usage:
+ /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits())
+ static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ);
+
+ /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits())
+ static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End);
+
+protected:
+ // The start point of the trace
+ double m_StartX, m_StartY, m_StartZ;
+
+ // The end point of the trace
+ double m_EndX, m_EndY, m_EndZ;
+
+ // The difference in coords, End - Start
+ double m_DiffX, m_DiffY, m_DiffZ;
+
+ // The increment at which the block coords are going from Start to End; either +1 or -1
+ int m_DirX, m_DirY, m_DirZ;
+
+ // The current block
+ int m_CurrentX, m_CurrentY, m_CurrentZ;
+
+ // The face through which the current block has been entered
+ char m_CurrentFace;
+
+
+ /// Adjusts the start point above the world to just at the world's top
+ void FixStartAboveWorld(void);
+
+ /// Adjusts the start point below the world to just at the world's bottom
+ void FixStartBelowWorld(void);
+
+ /// Calculates the XZ coords of an intersection with the specified Yconst plane; assumes that such an intersection exists
+ void CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ);
+
+ /// Moves m_Current to the next block on the line; returns false if no move is possible (reached the end)
+ bool MoveToNextBlock(void);
+
+ // cChunkCallback overrides:
+ virtual bool Item(cChunk * a_Chunk) override;
+} ;
+
+
+
+
+
diff --git a/src/LinearInterpolation.cpp b/src/LinearInterpolation.cpp
new file mode 100644
index 000000000..d4975418b
--- /dev/null
+++ b/src/LinearInterpolation.cpp
@@ -0,0 +1,251 @@
+
+// LinearInterpolation.cpp
+
+// Implements methods for linear interpolation over 1D, 2D and 3D arrays
+
+#include "Globals.h"
+#include "LinearInterpolation.h"
+
+
+
+
+
+/*
+// Perform an automatic test upon program start (use breakpoints to debug):
+
+extern void Debug3DNoise(float * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase);
+
+class Test
+{
+public:
+ Test(void)
+ {
+ // DoTest1();
+ DoTest2();
+ }
+
+
+ void DoTest1(void)
+ {
+ float In[8] = {0, 1, 2, 3, 1, 2, 2, 2};
+ float Out[3 * 3 * 3];
+ LinearInterpolate1DArray(In, 4, Out, 9);
+ LinearInterpolate2DArray(In, 2, 2, Out, 3, 3);
+ LinearInterpolate3DArray(In, 2, 2, 2, Out, 3, 3, 3);
+ LOGD("Out[0]: %f", Out[0]);
+ }
+
+
+ void DoTest2(void)
+ {
+ float In[3 * 3 * 3];
+ for (int i = 0; i < ARRAYCOUNT(In); i++)
+ {
+ In[i] = (float)(i % 5);
+ }
+ float Out[15 * 16 * 17];
+ LinearInterpolate3DArray(In, 3, 3, 3, Out, 15, 16, 17);
+ Debug3DNoise(Out, 15, 16, 17, "LERP test");
+ }
+} gTest;
+//*/
+
+
+
+
+
+// Puts linearly interpolated values from one array into another array. 1D version
+void LinearInterpolate1DArray(
+ float * a_Src,
+ int a_SrcSizeX,
+ float * a_Dst,
+ int a_DstSizeX
+)
+{
+ a_Dst[0] = a_Src[0];
+ int DstSizeXm1 = a_DstSizeX - 1;
+ int SrcSizeXm1 = a_SrcSizeX - 1;
+ float fDstSizeXm1 = (float)DstSizeXm1;
+ float fSrcSizeXm1 = (float)SrcSizeXm1;
+ for (int x = 1; x < DstSizeXm1; x++)
+ {
+ int SrcIdx = x * SrcSizeXm1 / DstSizeXm1;
+ float ValLo = a_Src[SrcIdx];
+ float ValHi = a_Src[SrcIdx + 1];
+ float Ratio = (float)x * fSrcSizeXm1 / fDstSizeXm1 - SrcIdx;
+ a_Dst[x] = ValLo + (ValHi - ValLo) * Ratio;
+ }
+ a_Dst[a_DstSizeX - 1] = a_Src[a_SrcSizeX - 1];
+}
+
+
+
+
+
+// Puts linearly interpolated values from one array into another array. 2D version
+void LinearInterpolate2DArray(
+ float * a_Src,
+ int a_SrcSizeX, int a_SrcSizeY,
+ float * a_Dst,
+ int a_DstSizeX, int a_DstSizeY
+)
+{
+ ASSERT(a_DstSizeX > 0);
+ ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX);
+ ASSERT(a_DstSizeY > 0);
+ ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY);
+
+ // Calculate interpolation ratios and src indices along each axis:
+ float RatioX[MAX_INTERPOL_SIZEX];
+ float RatioY[MAX_INTERPOL_SIZEY];
+ int SrcIdxX[MAX_INTERPOL_SIZEX];
+ int SrcIdxY[MAX_INTERPOL_SIZEY];
+ for (int x = 1; x < a_DstSizeX; x++)
+ {
+ SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1);
+ RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x];
+ }
+ for (int y = 1; y < a_DstSizeY; y++)
+ {
+ SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1);
+ RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y];
+ }
+
+ // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow:
+ SrcIdxX[0] = 0;
+ RatioX[0] = 0;
+ SrcIdxY[0] = 0;
+ RatioY[0] = 0;
+ SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2;
+ RatioX[a_DstSizeX - 1] = 1;
+ SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2;
+ RatioY[a_DstSizeY - 1] = 1;
+
+ // Output all the dst array values using the indices and ratios:
+ int idx = 0;
+ for (int y = 0; y < a_DstSizeY; y++)
+ {
+ int idxLoY = a_SrcSizeX * SrcIdxY[y];
+ int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1);
+ float ry = RatioY[y];
+ for (int x = 0; x < a_DstSizeX; x++)
+ {
+ // The four src corners of the current "cell":
+ float LoXLoY = a_Src[SrcIdxX[x] + idxLoY];
+ float HiXLoY = a_Src[SrcIdxX[x] + 1 + idxLoY];
+ float LoXHiY = a_Src[SrcIdxX[x] + idxHiY];
+ float HiXHiY = a_Src[SrcIdxX[x] + 1 + idxHiY];
+
+ // Linear interpolation along the X axis:
+ float InterpXLoY = LoXLoY + (HiXLoY - LoXLoY) * RatioX[x];
+ float InterpXHiY = LoXHiY + (HiXHiY - LoXHiY) * RatioX[x];
+
+ // Linear interpolation along the Y axis:
+ a_Dst[idx] = InterpXLoY + (InterpXHiY - InterpXLoY) * ry;
+ idx += 1;
+ }
+ }
+}
+
+
+
+
+
+/// Puts linearly interpolated values from one array into another array. 3D version
+void LinearInterpolate3DArray(
+ float * a_Src,
+ int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
+ float * a_Dst,
+ int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ
+)
+{
+ ASSERT(a_DstSizeX > 0);
+ ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX);
+ ASSERT(a_DstSizeY > 0);
+ ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY);
+ ASSERT(a_DstSizeZ > 0);
+ ASSERT(a_DstSizeZ < MAX_INTERPOL_SIZEZ);
+
+ // Calculate interpolation ratios and src indices along each axis:
+ float RatioX[MAX_INTERPOL_SIZEX];
+ float RatioY[MAX_INTERPOL_SIZEY];
+ float RatioZ[MAX_INTERPOL_SIZEZ];
+ int SrcIdxX[MAX_INTERPOL_SIZEX];
+ int SrcIdxY[MAX_INTERPOL_SIZEY];
+ int SrcIdxZ[MAX_INTERPOL_SIZEZ];
+ for (int x = 1; x < a_DstSizeX; x++)
+ {
+ SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1);
+ RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x];
+ }
+ for (int y = 1; y < a_DstSizeY; y++)
+ {
+ SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1);
+ RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y];
+ }
+ for (int z = 1; z < a_DstSizeZ; z++)
+ {
+ SrcIdxZ[z] = z * (a_SrcSizeZ - 1) / (a_DstSizeZ - 1);
+ RatioZ[z] = ((float)(z * (a_SrcSizeZ - 1)) / (a_DstSizeZ - 1)) - SrcIdxZ[z];
+ }
+
+ // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow:
+ SrcIdxX[0] = 0;
+ RatioX[0] = 0;
+ SrcIdxY[0] = 0;
+ RatioY[0] = 0;
+ SrcIdxZ[0] = 0;
+ RatioZ[0] = 0;
+ SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2;
+ RatioX[a_DstSizeX - 1] = 1;
+ SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2;
+ RatioY[a_DstSizeY - 1] = 1;
+ SrcIdxZ[a_DstSizeZ - 1] = a_SrcSizeZ - 2;
+ RatioZ[a_DstSizeZ - 1] = 1;
+
+ // Output all the dst array values using the indices and ratios:
+ int idx = 0;
+ for (int z = 0; z < a_DstSizeZ; z++)
+ {
+ int idxLoZ = a_SrcSizeX * a_SrcSizeY * SrcIdxZ[z];
+ int idxHiZ = a_SrcSizeX * a_SrcSizeY * (SrcIdxZ[z] + 1);
+ float rz = RatioZ[z];
+ for (int y = 0; y < a_DstSizeY; y++)
+ {
+ int idxLoY = a_SrcSizeX * SrcIdxY[y];
+ int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1);
+ float ry = RatioY[y];
+ for (int x = 0; x < a_DstSizeX; x++)
+ {
+ // The eight src corners of the current "cell":
+ float LoXLoYLoZ = a_Src[SrcIdxX[x] + idxLoY + idxLoZ];
+ float HiXLoYLoZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxLoZ];
+ float LoXHiYLoZ = a_Src[SrcIdxX[x] + idxHiY + idxLoZ];
+ float HiXHiYLoZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxLoZ];
+ float LoXLoYHiZ = a_Src[SrcIdxX[x] + idxLoY + idxHiZ];
+ float HiXLoYHiZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxHiZ];
+ float LoXHiYHiZ = a_Src[SrcIdxX[x] + idxHiY + idxHiZ];
+ float HiXHiYHiZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxHiZ];
+
+ // Linear interpolation along the Z axis:
+ float LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * rz;
+ float HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * rz;
+ float LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * rz;
+ float HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * rz;
+
+ // Linear interpolation along the Y axis:
+ float LoXInYInZ = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * ry;
+ float HiXInYInZ = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * ry;
+
+ // Linear interpolation along the X axis:
+ a_Dst[idx] = LoXInYInZ + (HiXInYInZ - LoXInYInZ) * RatioX[x];
+ idx += 1;
+ } // for x
+ } // for y
+ } // for z
+}
+
+
+
+
+
diff --git a/src/LinearInterpolation.h b/src/LinearInterpolation.h
new file mode 100644
index 000000000..4b798d9bc
--- /dev/null
+++ b/src/LinearInterpolation.h
@@ -0,0 +1,60 @@
+
+// LinearInterpolation.h
+
+// Declares methods for linear interpolation over 1D, 2D and 3D arrays
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// 2D and 3D Interpolation is optimized by precalculating the ratios into static-sized arrays
+// These arrays enforce a max size of the dest array, but the limits are settable here:
+const int MAX_INTERPOL_SIZEX = 256; ///< Maximum X-size of the interpolated array
+const int MAX_INTERPOL_SIZEY = 512; ///< Maximum Y-size of the interpolated array
+const int MAX_INTERPOL_SIZEZ = 256; ///< Maximum Z-size of the interpolated array
+
+
+
+
+
+/// Puts linearly interpolated values from one array into another array. 1D version
+void LinearInterpolate1DArray(
+ float * a_Src, ///< Src array
+ int a_SrcSizeX, ///< Count of the src array
+ float * a_Dst, ///< Src array
+ int a_DstSizeX ///< Count of the dst array
+);
+
+
+
+
+
+/// Puts linearly interpolated values from one array into another array. 2D version
+void LinearInterpolate2DArray(
+ float * a_Src, ///< Src array, [x + a_SrcSizeX * y]
+ int a_SrcSizeX, int a_SrcSizeY, ///< Count of the src array, in each direction
+ float * a_Dst, ///< Dst array, [x + a_DstSizeX * y]
+ int a_DstSizeX, int a_DstSizeY ///< Count of the dst array, in each direction
+);
+
+
+
+
+
+/// Puts linearly interpolated values from one array into another array. 3D version
+void LinearInterpolate3DArray(
+ float * a_Src, ///< Src array, [x + a_SrcSizeX * y + a_SrcSizeX * a_SrcSizeY * z]
+ int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Count of the src array, in each direction
+ float * a_Dst, ///< Dst array, [x + a_DstSizeX * y + a_DstSizeX * a_DstSizeY * z]
+ int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ ///< Count of the dst array, in each direction
+);
+
+
+
+
diff --git a/src/LinearUpscale.h b/src/LinearUpscale.h
new file mode 100644
index 000000000..b337b3219
--- /dev/null
+++ b/src/LinearUpscale.h
@@ -0,0 +1,244 @@
+
+// LinearUpscale.h
+
+// Declares the functions for linearly upscaling arrays
+
+/*
+Upscaling means that the array is divided into same-size "cells", and each cell is
+linearly interpolated between its corners. The array's dimensions are therefore
+1 + CellSize * NumCells, for each direction.
+
+Upscaling is more efficient than linear interpolation, because the cell sizes are integral
+and therefore the cells' boundaries are on the array points.
+
+However, upscaling usually requires generating the "1 +" in each direction.
+
+Upscaling is implemented in templates, so that it's compatible with multiple datatypes.
+Therefore, there is no cpp file.
+
+InPlace upscaling works on a single array and assumes that the values to work on have already
+been interspersed into the array to the cell boundaries.
+Specifically, a_Array[x * a_AnchorStepX + y * a_AnchorStepY] contains the anchor value.
+
+Regular upscaling takes two arrays and "moves" the input from src to dst; src is expected packed.
+*/
+
+
+
+
+/**
+Linearly interpolates values in the array between the equidistant anchor points (upscales).
+Works in-place (input is already present at the correct output coords)
+*/
+template<typename TYPE> void LinearUpscale2DArrayInPlace(
+ TYPE * a_Array,
+ int a_SizeX, int a_SizeY, // Dimensions of the array
+ int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction
+)
+{
+ // First interpolate columns where the anchor points are:
+ int LastYCell = a_SizeY - a_AnchorStepY;
+ for (int y = 0; y < LastYCell; y += a_AnchorStepY)
+ {
+ int Idx = a_SizeX * y;
+ for (int x = 0; x < a_SizeX; x += a_AnchorStepX)
+ {
+ TYPE StartValue = a_Array[Idx];
+ TYPE EndValue = a_Array[Idx + a_SizeX * a_AnchorStepY];
+ TYPE Diff = EndValue - StartValue;
+ for (int CellY = 1; CellY < a_AnchorStepY; CellY++)
+ {
+ a_Array[Idx + a_SizeX * CellY] = StartValue + Diff * CellY / a_AnchorStepY;
+ } // for CellY
+ Idx += a_AnchorStepX;
+ } // for x
+ } // for y
+
+ // Now interpolate in rows, each row has values in the anchor columns
+ int LastXCell = a_SizeX - a_AnchorStepX;
+ for (int y = 0; y < a_SizeY; y++)
+ {
+ int Idx = a_SizeX * y;
+ for (int x = 0; x < LastXCell; x += a_AnchorStepX)
+ {
+ TYPE StartValue = a_Array[Idx];
+ TYPE EndValue = a_Array[Idx + a_AnchorStepX];
+ TYPE Diff = EndValue - StartValue;
+ for (int CellX = 1; CellX < a_AnchorStepX; CellX++)
+ {
+ a_Array[Idx + CellX] = StartValue + CellX * Diff / a_AnchorStepX;
+ } // for CellY
+ Idx += a_AnchorStepX;
+ }
+ }
+}
+
+
+
+
+
+/**
+Linearly interpolates values in the array between the equidistant anchor points (upscales).
+Works on two arrays, input is packed and output is to be completely constructed.
+*/
+template<typename TYPE> void LinearUpscale2DArray(
+ TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY
+ int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array
+ TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1)
+ int a_UpscaleX, int a_UpscaleY ///< Upscale factor for each direction
+)
+{
+ // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes
+ // Feel free to enlarge them if needed, but keep in mind that they're on the stack
+ const int MAX_UPSCALE_X = 128;
+ const int MAX_UPSCALE_Y = 128;
+
+ ASSERT(a_Src != NULL);
+ ASSERT(a_Dst != NULL);
+ ASSERT(a_SrcSizeX > 0);
+ ASSERT(a_SrcSizeY > 0);
+ ASSERT(a_UpscaleX > 0);
+ ASSERT(a_UpscaleY > 0);
+ ASSERT(a_UpscaleX <= MAX_UPSCALE_X);
+ ASSERT(a_UpscaleY <= MAX_UPSCALE_Y);
+
+ // Pre-calculate the upscaling ratios:
+ TYPE RatioX[MAX_UPSCALE_X];
+ TYPE RatioY[MAX_UPSCALE_Y];
+ for (int x = 0; x <= a_UpscaleX; x++)
+ {
+ RatioX[x] = (TYPE)x / a_UpscaleX;
+ }
+ for (int y = 0; y <= a_UpscaleY; y++)
+ {
+ RatioY[y] = (TYPE)y / a_UpscaleY;
+ }
+
+ // Interpolate each XY cell:
+ int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1;
+ int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1;
+ for (int y = 0; y < (a_SrcSizeY - 1); y++)
+ {
+ int DstY = y * a_UpscaleY;
+ int idx = y * a_SrcSizeX;
+ for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++)
+ {
+ int DstX = x * a_UpscaleX;
+ TYPE LoXLoY = a_Src[idx];
+ TYPE LoXHiY = a_Src[idx + a_SrcSizeX];
+ TYPE HiXLoY = a_Src[idx + 1];
+ TYPE HiXHiY = a_Src[idx + 1 + a_SrcSizeX];
+ for (int CellY = 0; CellY <= a_UpscaleY; CellY++)
+ {
+ int DestIdx = (DstY + CellY) * DstSizeX + DstX;
+ ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY);
+ TYPE LoXInY = LoXLoY + (LoXHiY - LoXLoY) * RatioY[CellY];
+ TYPE HiXInY = HiXLoY + (HiXHiY - HiXLoY) * RatioY[CellY];
+ for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++)
+ {
+ a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX];
+ }
+ } // for CellY
+ } // for x
+ } // for y
+}
+
+
+
+
+
+/**
+Linearly interpolates values in the array between the equidistant anchor points (upscales).
+Works on two arrays, input is packed and output is to be completely constructed.
+*/
+template<typename TYPE> void LinearUpscale3DArray(
+ TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ
+ int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array
+ TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1)
+ int a_UpscaleX, int a_UpscaleY, int a_UpscaleZ ///< Upscale factor for each direction
+)
+{
+ // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes
+ // Feel free to enlarge them if needed, but keep in mind that they're on the stack
+ const int MAX_UPSCALE_X = 128;
+ const int MAX_UPSCALE_Y = 128;
+ const int MAX_UPSCALE_Z = 128;
+
+ ASSERT(a_Src != NULL);
+ ASSERT(a_Dst != NULL);
+ ASSERT(a_SrcSizeX > 0);
+ ASSERT(a_SrcSizeY > 0);
+ ASSERT(a_SrcSizeZ > 0);
+ ASSERT(a_UpscaleX > 0);
+ ASSERT(a_UpscaleY > 0);
+ ASSERT(a_UpscaleZ > 0);
+ ASSERT(a_UpscaleX <= MAX_UPSCALE_X);
+ ASSERT(a_UpscaleY <= MAX_UPSCALE_Y);
+ ASSERT(a_UpscaleZ <= MAX_UPSCALE_Z);
+
+ // Pre-calculate the upscaling ratios:
+ TYPE RatioX[MAX_UPSCALE_X];
+ TYPE RatioY[MAX_UPSCALE_Y];
+ TYPE RatioZ[MAX_UPSCALE_Z];
+ for (int x = 0; x <= a_UpscaleX; x++)
+ {
+ RatioX[x] = (TYPE)x / a_UpscaleX;
+ }
+ for (int y = 0; y <= a_UpscaleY; y++)
+ {
+ RatioY[y] = (TYPE)y / a_UpscaleY;
+ }
+ for (int z = 0; z <= a_UpscaleZ; z++)
+ {
+ RatioZ[z] = (TYPE)z / a_UpscaleZ;
+ }
+
+ // Interpolate each XYZ cell:
+ int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1;
+ int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1;
+ int DstSizeZ = (a_SrcSizeZ - 1) * a_UpscaleZ + 1;
+ for (int z = 0; z < (a_SrcSizeZ - 1); z++)
+ {
+ int DstZ = z * a_UpscaleZ;
+ for (int y = 0; y < (a_SrcSizeY - 1); y++)
+ {
+ int DstY = y * a_UpscaleY;
+ int idx = y * a_SrcSizeX + z * a_SrcSizeX * a_SrcSizeY;
+ for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++)
+ {
+ int DstX = x * a_UpscaleX;
+ TYPE LoXLoYLoZ = a_Src[idx];
+ TYPE LoXLoYHiZ = a_Src[idx + a_SrcSizeX * a_SrcSizeY];
+ TYPE LoXHiYLoZ = a_Src[idx + a_SrcSizeX];
+ TYPE LoXHiYHiZ = a_Src[idx + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY];
+ TYPE HiXLoYLoZ = a_Src[idx + 1];
+ TYPE HiXLoYHiZ = a_Src[idx + 1 + a_SrcSizeX * a_SrcSizeY];
+ TYPE HiXHiYLoZ = a_Src[idx + 1 + a_SrcSizeX];
+ TYPE HiXHiYHiZ = a_Src[idx + 1 + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY];
+ for (int CellZ = 0; CellZ <= a_UpscaleZ; CellZ++)
+ {
+ TYPE LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * RatioZ[CellZ];
+ TYPE LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * RatioZ[CellZ];
+ TYPE HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * RatioZ[CellZ];
+ TYPE HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * RatioZ[CellZ];
+ for (int CellY = 0; CellY <= a_UpscaleY; CellY++)
+ {
+ int DestIdx = (DstZ + CellZ) * DstSizeX * DstSizeY + (DstY + CellY) * DstSizeX + DstX;
+ ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY * DstSizeZ);
+ TYPE LoXInY = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * RatioY[CellY];
+ TYPE HiXInY = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * RatioY[CellY];
+ for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++)
+ {
+ a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX];
+ }
+ } // for CellY
+ } // for CellZ
+ } // for x
+ } // for y
+ } // for z
+}
+
+
+
+
+
diff --git a/src/Log.cpp b/src/Log.cpp
new file mode 100644
index 000000000..a0de4531b
--- /dev/null
+++ b/src/Log.cpp
@@ -0,0 +1,169 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Log.h"
+
+#include <fstream>
+#include <ctime>
+#include "OSSupport/IsThread.h"
+
+#if defined(ANDROID_NDK)
+ #include <android/log.h>
+ #include "ToJava.h"
+#endif
+
+
+
+
+cLog* cLog::s_Log = NULL;
+
+cLog::cLog(const AString & a_FileName )
+ : m_File(NULL)
+{
+ s_Log = this;
+
+ // create logs directory
+ cFile::CreateFolder(FILE_IO_PREFIX + AString("logs"));
+
+ OpenLog((FILE_IO_PREFIX + AString("logs/") + a_FileName).c_str() );
+}
+
+
+
+
+
+cLog::~cLog()
+{
+ CloseLog();
+ s_Log = NULL;
+}
+
+
+
+
+
+cLog* cLog::GetInstance()
+{
+ if (s_Log != NULL)
+ {
+ return s_Log;
+ }
+
+ new cLog("log.txt");
+ return s_Log;
+}
+
+
+
+
+
+void cLog::CloseLog()
+{
+ if( m_File )
+ fclose (m_File);
+ m_File = 0;
+}
+
+
+
+
+
+void cLog::OpenLog( const char* a_FileName )
+{
+ if(m_File) fclose (m_File);
+ #ifdef _MSC_VER
+ fopen_s( &m_File, a_FileName, "a+" );
+ #else
+ m_File = fopen(a_FileName, "a+" );
+ #endif
+}
+
+
+
+
+
+void cLog::ClearLog()
+{
+ #ifdef _MSC_VER
+ if( fopen_s( &m_File, "log.txt", "w" ) == 0)
+ fclose (m_File);
+ #else
+ m_File = fopen("log.txt", "w" );
+ if( m_File )
+ fclose (m_File);
+ #endif
+ m_File = 0;
+}
+
+
+
+
+
+void cLog::Log(const char * a_Format, va_list argList)
+{
+ AString Message;
+ AppendVPrintf(Message, a_Format, argList);
+
+ time_t rawtime;
+ time ( &rawtime );
+
+ struct tm* timeinfo;
+#ifdef _MSC_VER
+ struct tm timeinforeal;
+ timeinfo = &timeinforeal;
+ localtime_s(timeinfo, &rawtime );
+#else
+ timeinfo = localtime( &rawtime );
+#endif
+
+ AString Line;
+ #ifdef _DEBUG
+ Printf(Line, "[%04x|%02d:%02d:%02d] %s", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str());
+ #else
+ Printf(Line, "[%02d:%02d:%02d] %s", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str());
+ #endif
+ if (m_File)
+ {
+ fprintf(m_File, "%s\n", Line.c_str());
+ fflush(m_File);
+ }
+
+ // Print to console:
+#if defined(ANDROID_NDK)
+ //__android_log_vprint(ANDROID_LOG_ERROR,"MCServer", a_Format, argList);
+ __android_log_print(ANDROID_LOG_ERROR, "MCServer", "%s", Line.c_str() );
+ //CallJavaFunction_Void_String(g_JavaThread, "AddToLog", Line );
+#else
+ printf("%s", Line.c_str());
+#endif
+
+ #if defined (_WIN32) && defined(_DEBUG)
+ // In a Windows Debug build, output the log to debug console as well:
+ OutputDebugStringA((Line + "\n").c_str());
+ #endif // _WIN32
+}
+
+
+
+
+
+void cLog::Log(const char* a_Format, ...)
+{
+ va_list argList;
+ va_start(argList, a_Format);
+ Log( a_Format, argList );
+ va_end(argList);
+}
+
+
+
+
+
+void cLog::SimpleLog(const char* a_String)
+{
+ Log("%s", a_String );
+}
+
+
+
+
diff --git a/src/Log.h b/src/Log.h
new file mode 100644
index 000000000..d00022c6f
--- /dev/null
+++ b/src/Log.h
@@ -0,0 +1,30 @@
+
+#pragma once
+
+
+
+
+
+class cLog
+{ // tolua_export
+private:
+ FILE * m_File;
+ static cLog * s_Log;
+
+public:
+ cLog(const AString & a_FileName);
+ ~cLog();
+ void Log(const char* a_Format, va_list argList );
+ void Log(const char* a_Format, ...);
+ // tolua_begin
+ void SimpleLog(const char* a_String);
+ void OpenLog( const char* a_FileName );
+ void CloseLog();
+ void ClearLog();
+ static cLog* GetInstance();
+};
+// tolua_end
+
+
+
+
diff --git a/src/LuaFunctions.h b/src/LuaFunctions.h
new file mode 100644
index 000000000..0ad420881
--- /dev/null
+++ b/src/LuaFunctions.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "MCLogger.h"
+#include <time.h>
+// tolua_begin
+
+unsigned int GetTime()
+{
+ return (unsigned int)time(0);
+}
+
+std::string GetChar( std::string & a_Str, unsigned int a_Idx )
+{
+ return std::string(1, a_Str[ a_Idx ]);
+}
+
+// tolua_end
diff --git a/src/LuaState.cpp b/src/LuaState.cpp
new file mode 100644
index 000000000..6be1ee58c
--- /dev/null
+++ b/src/LuaState.cpp
@@ -0,0 +1,1024 @@
+
+// LuaState.cpp
+
+// Implements the cLuaState class representing the wrapper over lua_State *, provides associated helper functions
+
+#include "Globals.h"
+#include "LuaState.h"
+
+extern "C"
+{
+ #include "lua/src/lualib.h"
+}
+
+#include "tolua++.h"
+#include "Bindings.h"
+#include "ManualBindings.h"
+
+// fwd: SQLite/lsqlite3.c
+extern "C"
+{
+ LUALIB_API int luaopen_lsqlite3(lua_State * L);
+}
+
+// fwd: LuaExpat/lxplib.c:
+extern "C"
+{
+ int luaopen_lxp(lua_State * L);
+}
+
+
+
+
+
+
+const cLuaState::cRet cLuaState::Return = {};
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cLuaState:
+
+cLuaState::cLuaState(const AString & a_SubsystemName) :
+ m_LuaState(NULL),
+ m_IsOwned(false),
+ m_SubsystemName(a_SubsystemName),
+ m_NumCurrentFunctionArgs(-1)
+{
+}
+
+
+
+
+
+cLuaState::cLuaState(lua_State * a_AttachState) :
+ m_LuaState(a_AttachState),
+ m_IsOwned(false),
+ m_SubsystemName("<attached>"),
+ m_NumCurrentFunctionArgs(-1)
+{
+}
+
+
+
+
+
+cLuaState::~cLuaState()
+{
+ if (IsValid())
+ {
+ if (m_IsOwned)
+ {
+ Close();
+ }
+ else
+ {
+ Detach();
+ }
+ }
+}
+
+
+
+
+
+void cLuaState::Create(void)
+{
+ if (m_LuaState != NULL)
+ {
+ LOGWARNING("%s: Trying to create an already-existing LuaState, ignoring.", __FUNCTION__);
+ return;
+ }
+ m_LuaState = lua_open();
+ luaL_openlibs(m_LuaState);
+ tolua_AllToLua_open(m_LuaState);
+ ManualBindings::Bind(m_LuaState);
+ luaopen_lsqlite3(m_LuaState);
+ luaopen_lxp(m_LuaState);
+ m_IsOwned = true;
+}
+
+
+
+
+
+void cLuaState::Close(void)
+{
+ if (m_LuaState == NULL)
+ {
+ LOGWARNING("%s: Trying to close an invalid LuaState, ignoring.", __FUNCTION__);
+ return;
+ }
+ if (!m_IsOwned)
+ {
+ LOGWARNING(
+ "%s: Detected mis-use, calling Close() on an attached state (0x%p). Detaching instead.",
+ __FUNCTION__, m_LuaState
+ );
+ Detach();
+ return;
+ }
+ lua_close(m_LuaState);
+ m_LuaState = NULL;
+ m_IsOwned = false;
+}
+
+
+
+
+
+void cLuaState::Attach(lua_State * a_State)
+{
+ if (m_LuaState != NULL)
+ {
+ LOGINFO("%s: Already contains a LuaState (0x%p), will be closed / detached.", __FUNCTION__, m_LuaState);
+ if (m_IsOwned)
+ {
+ Close();
+ }
+ else
+ {
+ Detach();
+ }
+ }
+ m_LuaState = a_State;
+ m_IsOwned = false;
+}
+
+
+
+
+
+void cLuaState::Detach(void)
+{
+ if (m_LuaState == NULL)
+ {
+ return;
+ }
+ if (m_IsOwned)
+ {
+ LOGWARNING(
+ "%s: Detected a mis-use, calling Detach() when the state is owned. Closing the owned state (0x%p).",
+ __FUNCTION__, m_LuaState
+ );
+ Close();
+ return;
+ }
+ m_LuaState = NULL;
+}
+
+
+
+
+
+bool cLuaState::LoadFile(const AString & a_FileName)
+{
+ ASSERT(IsValid());
+
+ // Load the file:
+ int s = luaL_loadfile(m_LuaState, a_FileName.c_str());
+ if (ReportErrors(s))
+ {
+ LOGWARNING("Can't load %s because of an error in file %s", m_SubsystemName.c_str(), a_FileName.c_str());
+ return false;
+ }
+
+ // Execute the globals:
+ s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0);
+ if (ReportErrors(s))
+ {
+ LOGWARNING("Error in %s in file %s", m_SubsystemName.c_str(), a_FileName.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cLuaState::HasFunction(const char * a_FunctionName)
+{
+ if (!IsValid())
+ {
+ // This happens if cPlugin::Initialize() fails with an error
+ return false;
+ }
+
+ lua_getglobal(m_LuaState, a_FunctionName);
+ bool res = (!lua_isnil(m_LuaState, -1) && lua_isfunction(m_LuaState, -1));
+ lua_pop(m_LuaState, 1);
+ return res;
+}
+
+
+
+
+
+bool cLuaState::PushFunction(const char * a_FunctionName)
+{
+ ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
+
+ if (!IsValid())
+ {
+ // This happens if cPlugin::Initialize() fails with an error
+ return false;
+ }
+
+ lua_getglobal(m_LuaState, a_FunctionName);
+ if (!lua_isfunction(m_LuaState, -1))
+ {
+ LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName);
+ lua_pop(m_LuaState, 1);
+ return false;
+ }
+ m_CurrentFunctionName.assign(a_FunctionName);
+ m_NumCurrentFunctionArgs = 0;
+ return true;
+}
+
+
+
+
+
+bool cLuaState::PushFunction(int a_FnRef)
+{
+ ASSERT(IsValid());
+ ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
+
+ lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref()
+ if (!lua_isfunction(m_LuaState, -1))
+ {
+ lua_pop(m_LuaState, 1);
+ return false;
+ }
+ m_CurrentFunctionName = "<callback>";
+ m_NumCurrentFunctionArgs = 0;
+ return true;
+}
+
+
+
+
+
+bool cLuaState::PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName)
+{
+ ASSERT(IsValid());
+ ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
+
+ lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef); // Get the table ref
+ if (!lua_istable(m_LuaState, -1))
+ {
+ // Not a table, bail out
+ lua_pop(m_LuaState, 1);
+ return false;
+ }
+ lua_getfield(m_LuaState, -1, a_FnName);
+ if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1))
+ {
+ // Not a valid function, bail out
+ lua_pop(m_LuaState, 2);
+ return false;
+ }
+ lua_remove(m_LuaState, -2); // Remove the table ref from the stack
+ m_CurrentFunctionName = "<table_callback>";
+ m_NumCurrentFunctionArgs = 0;
+ return true;
+}
+
+
+
+
+
+void cLuaState::Push(const AString & a_String)
+{
+ ASSERT(IsValid());
+
+ tolua_pushcppstring(m_LuaState, a_String);
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(const AStringVector & a_Vector)
+{
+ ASSERT(IsValid());
+
+ lua_createtable(m_LuaState, a_Vector.size(), 0);
+ int newTable = lua_gettop(m_LuaState);
+ int index = 1;
+ for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index)
+ {
+ tolua_pushstring(m_LuaState, itr->c_str());
+ lua_rawseti(m_LuaState, newTable, index);
+ }
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::PushUserType(void * a_Object, const char * a_Type)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Object, a_Type);
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(int a_Value)
+{
+ ASSERT(IsValid());
+
+ tolua_pushnumber(m_LuaState, a_Value);
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(double a_Value)
+{
+ ASSERT(IsValid());
+
+ tolua_pushnumber(m_LuaState, a_Value);
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(const char * a_Value)
+{
+ ASSERT(IsValid());
+
+ tolua_pushstring(m_LuaState, a_Value);
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(bool a_Value)
+{
+ ASSERT(IsValid());
+
+ tolua_pushboolean(m_LuaState, a_Value ? 1 : 0);
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cWorld * a_World)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_World, "cWorld");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cPlayer * a_Player)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Player, "cPlayer");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(const cPlayer * a_Player)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cEntity * a_Entity)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Entity, "cEntity");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cMonster * a_Monster)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Monster, "cMonster");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cItem * a_Item)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Item, "cItem");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cItems * a_Items)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Items, "cItems");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cClientHandle * a_Client)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Client, "cClientHandle");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cPickup * a_Pickup)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Pickup, "cPickup");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cChunkDesc * a_ChunkDesc)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_ChunkDesc, "cChunkDesc");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(const cCraftingGrid * a_Grid)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(const cCraftingRecipe * a_Recipe)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(TakeDamageInfo * a_TDI)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cWindow * a_Window)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Window, "cWindow");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cPluginLua * a_Plugin)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Plugin, "cPluginLua");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(const HTTPRequest * a_Request)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPRequest");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cWebAdmin * a_WebAdmin)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_WebAdmin, "cWebAdmin");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(const HTTPTemplateRequest * a_Request)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPTemplateRequest");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cTNTEntity * a_TNTEntity)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_TNTEntity, "cTNTEntity");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cCreeper * a_Creeper)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Creeper, "cCreeper");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(Vector3i * a_Vector)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Vector, "Vector3i");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(void * a_Ptr)
+{
+ ASSERT(IsValid());
+
+ lua_pushnil(m_LuaState);
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cHopperEntity * a_Hopper)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cBlockEntity * a_BlockEntity)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_BlockEntity, "cBlockEntity");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::GetReturn(int a_StackPos, bool & a_ReturnedVal)
+{
+ a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0);
+}
+
+
+
+
+
+void cLuaState::GetReturn(int a_StackPos, AString & a_ReturnedVal)
+{
+ if (lua_isstring(m_LuaState, a_StackPos))
+ {
+ a_ReturnedVal = tolua_tocppstring(m_LuaState, a_StackPos, a_ReturnedVal.c_str());
+ }
+}
+
+
+
+
+
+void cLuaState::GetReturn(int a_StackPos, int & a_ReturnedVal)
+{
+ if (lua_isnumber(m_LuaState, a_StackPos))
+ {
+ a_ReturnedVal = (int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal);
+ }
+}
+
+
+
+
+
+void cLuaState::GetReturn(int a_StackPos, double & a_ReturnedVal)
+{
+ if (lua_isnumber(m_LuaState, a_StackPos))
+ {
+ a_ReturnedVal = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal);
+ }
+}
+
+
+
+
+
+bool cLuaState::CallFunction(int a_NumResults)
+{
+ ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
+ ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1));
+
+ int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, 0);
+ if (ReportErrors(s))
+ {
+ LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), m_CurrentFunctionName.c_str());
+ m_NumCurrentFunctionArgs = -1;
+ m_CurrentFunctionName.clear();
+ return false;
+ }
+ m_NumCurrentFunctionArgs = -1;
+ m_CurrentFunctionName.clear();
+ return true;
+}
+
+
+
+
+
+bool cLuaState::CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam)
+{
+ ASSERT(IsValid());
+
+ if (a_EndParam < 0)
+ {
+ a_EndParam = a_StartParam;
+ }
+
+ tolua_Error tolua_err;
+ for (int i = a_StartParam; i <= a_EndParam; i++)
+ {
+ if (tolua_isusertable(m_LuaState, i, a_UserTable, 0, &tolua_err))
+ {
+ continue;
+ }
+ // Not the correct parameter
+ lua_Debug entry;
+ VERIFY(lua_getstack(m_LuaState, 0, &entry));
+ VERIFY(lua_getinfo (m_LuaState, "n", &entry));
+ AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?");
+ tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
+ return false;
+ } // for i - Param
+
+ // All params checked ok
+ return true;
+}
+
+
+
+
+
+bool cLuaState::CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam)
+{
+ ASSERT(IsValid());
+
+ if (a_EndParam < 0)
+ {
+ a_EndParam = a_StartParam;
+ }
+
+ tolua_Error tolua_err;
+ for (int i = a_StartParam; i <= a_EndParam; i++)
+ {
+ if (tolua_isusertype(m_LuaState, i, a_UserType, 0, &tolua_err))
+ {
+ continue;
+ }
+ // Not the correct parameter
+ lua_Debug entry;
+ VERIFY(lua_getstack(m_LuaState, 0, &entry));
+ VERIFY(lua_getinfo (m_LuaState, "n", &entry));
+ AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?");
+ tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
+ return false;
+ } // for i - Param
+
+ // All params checked ok
+ return true;
+}
+
+
+
+
+
+bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam)
+{
+ ASSERT(IsValid());
+
+ if (a_EndParam < 0)
+ {
+ a_EndParam = a_StartParam;
+ }
+
+ tolua_Error tolua_err;
+ for (int i = a_StartParam; i <= a_EndParam; i++)
+ {
+ if (tolua_istable(m_LuaState, i, 0, &tolua_err))
+ {
+ continue;
+ }
+ // Not the correct parameter
+ lua_Debug entry;
+ VERIFY(lua_getstack(m_LuaState, 0, &entry));
+ VERIFY(lua_getinfo (m_LuaState, "n", &entry));
+ AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?");
+ tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
+ return false;
+ } // for i - Param
+
+ // All params checked ok
+ return true;
+}
+
+
+
+
+
+bool cLuaState::CheckParamNumber(int a_StartParam, int a_EndParam)
+{
+ ASSERT(IsValid());
+
+ if (a_EndParam < 0)
+ {
+ a_EndParam = a_StartParam;
+ }
+
+ tolua_Error tolua_err;
+ for (int i = a_StartParam; i <= a_EndParam; i++)
+ {
+ if (tolua_isnumber(m_LuaState, i, 0, &tolua_err))
+ {
+ continue;
+ }
+ // Not the correct parameter
+ lua_Debug entry;
+ VERIFY(lua_getstack(m_LuaState, 0, &entry));
+ VERIFY(lua_getinfo (m_LuaState, "n", &entry));
+ AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?");
+ tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
+ return false;
+ } // for i - Param
+
+ // All params checked ok
+ return true;
+}
+
+
+
+
+
+bool cLuaState::CheckParamString(int a_StartParam, int a_EndParam)
+{
+ ASSERT(IsValid());
+
+ if (a_EndParam < 0)
+ {
+ a_EndParam = a_StartParam;
+ }
+
+ tolua_Error tolua_err;
+ for (int i = a_StartParam; i <= a_EndParam; i++)
+ {
+ if (tolua_isstring(m_LuaState, i, 0, &tolua_err))
+ {
+ continue;
+ }
+ // Not the correct parameter
+ lua_Debug entry;
+ VERIFY(lua_getstack(m_LuaState, 0, &entry));
+ VERIFY(lua_getinfo (m_LuaState, "n", &entry));
+ AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?");
+ tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
+ return false;
+ } // for i - Param
+
+ // All params checked ok
+ return true;
+}
+
+
+
+
+
+bool cLuaState::CheckParamEnd(int a_Param)
+{
+ tolua_Error tolua_err;
+ if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err))
+ {
+ return true;
+ }
+ // Not the correct parameter
+ lua_Debug entry;
+ VERIFY(lua_getstack(m_LuaState, 0, &entry));
+ VERIFY(lua_getinfo (m_LuaState, "n", &entry));
+ AString ErrMsg = Printf("#ferror in function '%s': Too many arguments.", (entry.name != NULL) ? entry.name : "?");
+ tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
+ return false;
+}
+
+
+
+
+
+bool cLuaState::ReportErrors(int a_Status)
+{
+ return ReportErrors(m_LuaState, a_Status);
+}
+
+
+
+
+
+bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status)
+{
+ if (a_Status == 0)
+ {
+ // No error to report
+ return false;
+ }
+
+ LOGWARNING("LUA: %d - %s", a_Status, lua_tostring(a_LuaState, -1));
+ lua_pop(a_LuaState, 1);
+ return true;
+}
+
+
+
+
+
+void cLuaState::LogStackTrace(void)
+{
+ LOGWARNING("Stack trace:");
+ lua_Debug entry;
+ int depth = 0;
+ while (lua_getstack(m_LuaState, depth, &entry))
+ {
+ int status = lua_getinfo(m_LuaState, "Sln", &entry);
+ assert(status);
+
+ LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?");
+ depth++;
+ }
+ LOGWARNING("Stack trace end");
+}
+
+
+
+
+
+AString cLuaState::GetTypeText(int a_StackPos)
+{
+ int Type = lua_type(m_LuaState, a_StackPos);
+ switch (Type)
+ {
+ case LUA_TNONE: return "TNONE";
+ case LUA_TNIL: return "TNIL";
+ case LUA_TBOOLEAN: return "TBOOLEAN";
+ case LUA_TLIGHTUSERDATA: return "TLIGHTUSERDATA";
+ case LUA_TNUMBER: return "TNUMBER";
+ case LUA_TSTRING: return "TSTRING";
+ case LUA_TTABLE: return "TTABLE";
+ case LUA_TFUNCTION: return "TFUNCTION";
+ case LUA_TUSERDATA: return "TUSERDATA";
+ case LUA_TTHREAD: return "TTHREAD";
+ }
+ return Printf("Unknown (%d)", Type);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cLuaState::cRef:
+
+cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
+ m_LuaState(a_LuaState)
+{
+ ASSERT(m_LuaState.IsValid());
+
+ lua_pushvalue(m_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack
+ m_Ref = luaL_ref(m_LuaState, LUA_REGISTRYINDEX);
+}
+
+
+
+
+
+cLuaState::cRef::~cRef()
+{
+ ASSERT(m_LuaState.IsValid());
+
+ if (IsValid())
+ {
+ luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref);
+ }
+}
+
+
+
+
diff --git a/src/LuaState.h b/src/LuaState.h
new file mode 100644
index 000000000..5e7f3d180
--- /dev/null
+++ b/src/LuaState.h
@@ -0,0 +1,817 @@
+
+// LuaState.h
+
+// Declares the cLuaState class representing the wrapper over lua_State *, provides associated helper functions
+
+/*
+The contained lua_State can be either owned or attached.
+Owned lua_State is created by calling Create() and the cLuaState automatically closes the state
+Or, lua_State can be attached by calling Attach(), the cLuaState doesn't close such a state
+Attaching a state will automatically close an owned state.
+
+Calling a Lua function is done by pushing the function, either by PushFunction() or PushFunctionFromRegistry(),
+then pushing the arguments (PushString(), PushNumber(), PushUserData() etc.) and finally
+executing CallFunction(). cLuaState automatically keeps track of the number of arguments and the name of the
+function (for logging purposes), which makes the call less error-prone.
+
+Reference management is provided by the cLuaState::cRef class. This is used when you need to hold a reference to
+any Lua object across several function calls; usually this is used for callbacks. The class is RAII-like, with
+automatic resource management.
+*/
+
+
+
+
+#pragma once
+
+extern "C"
+{
+ #include "lua/src/lauxlib.h"
+}
+
+
+
+
+
+class cWorld;
+class cPlayer;
+class cEntity;
+class cMonster;
+class cItem;
+class cItems;
+class cClientHandle;
+class cPickup;
+class cChunkDesc;
+class cCraftingGrid;
+class cCraftingRecipe;
+struct TakeDamageInfo;
+class cWindow;
+class cPluginLua;
+struct HTTPRequest;
+class cWebAdmin;
+struct HTTPTemplateRequest;
+class cTNTEntity;
+class cCreeper;
+class Vector3i;
+class cHopperEntity;
+class cBlockEntity;
+
+
+
+
+
+/// Encapsulates a Lua state and provides some syntactic sugar for common operations
+class cLuaState
+{
+public:
+
+ /// Used for storing references to object in the global registry
+ class cRef
+ {
+ public:
+ /// Creates a reference in the specified LuaState for object at the specified StackPos
+ cRef(cLuaState & a_LuaState, int a_StackPos);
+ ~cRef();
+
+ /// Returns true if the reference is valid
+ bool IsValid(void) const {return (m_Ref != LUA_REFNIL); }
+
+ /// Allows to use this class wherever an int (i. e. ref) is to be used
+ operator int(void) const { return m_Ref; }
+
+ protected:
+ cLuaState & m_LuaState;
+ int m_Ref;
+ } ;
+
+
+ /// A dummy class that's used only to delimit function args from return values for cLuaState::Call()
+ class cRet
+ {
+ } ;
+
+ static const cRet Return; // Use this constant to delimit function args from return values for cLuaState::Call()
+
+
+ /** Creates a new instance. The LuaState is not initialized.
+ a_SubsystemName is used for reporting problems in the console, it is "plugin %s" for plugins,
+ or "LuaScript" for the cLuaScript template
+ */
+ cLuaState(const AString & a_SubsystemName);
+
+ /** Creates a new instance. The a_AttachState is attached.
+ Subsystem name is set to "<attached>".
+ */
+ explicit cLuaState(lua_State * a_AttachState);
+
+ ~cLuaState();
+
+ /// Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions
+ operator lua_State * (void) { return m_LuaState; }
+
+ /// Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor
+ void Create(void);
+
+ /// Closes the m_LuaState, if not closed already
+ void Close(void);
+
+ /// Attaches the specified state. Operations will be carried out on this state, but it will not be closed in the destructor
+ void Attach(lua_State * a_State);
+
+ /// Detaches a previously attached state.
+ void Detach(void);
+
+ /// Returns true if the m_LuaState is valid
+ bool IsValid(void) const { return (m_LuaState != NULL); }
+
+ /** Loads the specified file
+ Returns false and logs a warning to the console if not successful (but the LuaState is kept open).
+ m_SubsystemName is displayed in the warning log message.
+ */
+ bool LoadFile(const AString & a_FileName);
+
+ /// Returns true if a_FunctionName is a valid Lua function that can be called
+ bool HasFunction(const char * a_FunctionName);
+
+ /** Pushes the function of the specified name onto the stack.
+ Returns true if successful. Logs a warning on failure (incl. m_SubsystemName)
+ */
+ bool PushFunction(const char * a_FunctionName);
+
+ /** Pushes a function that has been saved into the global registry, identified by a_FnRef.
+ Returns true if successful. Logs a warning on failure
+ */
+ bool PushFunction(int a_FnRef);
+
+ /** Pushes a function that is stored in a table ref.
+ Returns true if successful, false on failure. Doesn't log failure.
+ */
+ bool PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName);
+
+ /// Pushes a usertype of the specified class type onto the stack
+ void PushUserType(void * a_Object, const char * a_Type);
+
+ // Push a value onto the stack
+ void Push(const AString & a_String);
+ void Push(const AStringVector & a_Vector);
+ void Push(int a_Value);
+ void Push(double a_Value);
+ void Push(const char * a_Value);
+ void Push(bool a_Value);
+ void Push(cWorld * a_World);
+ void Push(cPlayer * a_Player);
+ void Push(const cPlayer * a_Player);
+ void Push(cEntity * a_Entity);
+ void Push(cMonster * a_Monster);
+ void Push(cItem * a_Item);
+ void Push(cItems * a_Items);
+ void Push(cClientHandle * a_ClientHandle);
+ void Push(cPickup * a_Pickup);
+ void Push(cChunkDesc * a_ChunkDesc);
+ void Push(const cCraftingGrid * a_Grid);
+ void Push(const cCraftingRecipe * a_Recipe);
+ void Push(TakeDamageInfo * a_TDI);
+ void Push(cWindow * a_Window);
+ void Push(cPluginLua * a_Plugin);
+ void Push(const HTTPRequest * a_Request);
+ void Push(cWebAdmin * a_WebAdmin);
+ void Push(const HTTPTemplateRequest * a_Request);
+ void Push(cTNTEntity * a_TNTEntity);
+ void Push(cCreeper * a_Creeper);
+ void Push(Vector3i * a_Vector);
+ void Push(void * a_Ptr);
+ void Push(cHopperEntity * a_Hopper);
+ void Push(cBlockEntity * a_BlockEntity);
+
+ /// Call any 0-param 0-return Lua function in a single line:
+ template <typename FnT>
+ bool Call(FnT a_FnName)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ return CallFunction(0);
+ }
+
+ /// Call any 1-param 0-return Lua function in a single line:
+ template<
+ typename FnT,
+ typename ArgT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ return CallFunction(0);
+ }
+
+ /// Call any 2-param 0-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ return CallFunction(0);
+ }
+
+ /// Call any 1-param 1-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename RetT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /// Call any 2-param 1-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename RetT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /// Call any 3-param 1-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename RetT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /// Call any 4-param 1-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename RetT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /// Call any 5-param 1-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename RetT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /// Call any 6-param 1-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
+ typename RetT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ Push(a_Arg6);
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /// Call any 7-param 1-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
+ typename ArgT7, typename RetT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ Push(a_Arg6);
+ Push(a_Arg7);
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /// Call any 8-param 1-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
+ typename ArgT7, typename ArgT8, typename RetT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ Push(a_Arg6);
+ Push(a_Arg7);
+ Push(a_Arg8);
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /// Call any 9-param 1-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
+ typename ArgT7, typename ArgT8, typename ArgT9, typename RetT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ Push(a_Arg6);
+ Push(a_Arg7);
+ Push(a_Arg8);
+ Push(a_Arg9);
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /// Call any 10-param 1-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
+ typename ArgT7, typename ArgT8, typename ArgT9, typename ArgT10, typename RetT1
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, ArgT10 a_Arg10, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ Push(a_Arg6);
+ Push(a_Arg7);
+ Push(a_Arg8);
+ Push(a_Arg9);
+ Push(a_Arg10);
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /// Call any 1-param 2-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename RetT1, typename RetT2
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ if (!CallFunction(2))
+ {
+ return false;
+ }
+ GetReturn(-2, a_Ret1);
+ GetReturn(-1, a_Ret2);
+ lua_pop(m_LuaState, 2);
+ return true;
+ }
+
+ /// Call any 2-param 2-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename RetT1, typename RetT2
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ if (!CallFunction(2))
+ {
+ return false;
+ }
+ GetReturn(-2, a_Ret1);
+ GetReturn(-1, a_Ret2);
+ lua_pop(m_LuaState, 2);
+ return true;
+ }
+
+ /// Call any 3-param 2-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3,
+ typename RetT1, typename RetT2
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ if (!CallFunction(2))
+ {
+ return false;
+ }
+ GetReturn(-2, a_Ret1);
+ GetReturn(-1, a_Ret2);
+ lua_pop(m_LuaState, 2);
+ return true;
+ }
+
+ /// Call any 4-param 2-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4,
+ typename RetT1, typename RetT2
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ if (!CallFunction(2))
+ {
+ return false;
+ }
+ GetReturn(-2, a_Ret1);
+ GetReturn(-1, a_Ret2);
+ lua_pop(m_LuaState, 2);
+ return true;
+ }
+
+ /// Call any 5-param 2-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
+ typename RetT1, typename RetT2
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ if (!CallFunction(2))
+ {
+ return false;
+ }
+ GetReturn(-2, a_Ret1);
+ GetReturn(-1, a_Ret2);
+ lua_pop(m_LuaState, 2);
+ return true;
+ }
+
+ /// Call any 6-param 2-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
+ typename ArgT6,
+ typename RetT1, typename RetT2
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ Push(a_Arg6);
+ if (!CallFunction(2))
+ {
+ return false;
+ }
+ GetReturn(-2, a_Ret1);
+ GetReturn(-1, a_Ret2);
+ lua_pop(m_LuaState, 2);
+ return true;
+ }
+
+ /// Call any 7-param 2-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
+ typename ArgT6, typename ArgT7,
+ typename RetT1, typename RetT2
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ Push(a_Arg6);
+ Push(a_Arg7);
+ if (!CallFunction(2))
+ {
+ return false;
+ }
+ GetReturn(-2, a_Ret1);
+ GetReturn(-1, a_Ret2);
+ lua_pop(m_LuaState, 2);
+ return true;
+ }
+
+ /// Call any 7-param 3-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
+ typename ArgT6, typename ArgT7,
+ typename RetT1, typename RetT2, typename RetT3
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ Push(a_Arg6);
+ Push(a_Arg7);
+ if (!CallFunction(3))
+ {
+ return false;
+ }
+ GetReturn(-3, a_Ret1);
+ GetReturn(-2, a_Ret2);
+ GetReturn(-1, a_Ret3);
+ lua_pop(m_LuaState, 3);
+ return true;
+ }
+
+ /// Call any 8-param 3-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
+ typename ArgT6, typename ArgT7, typename ArgT8,
+ typename RetT1, typename RetT2, typename RetT3
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ Push(a_Arg6);
+ Push(a_Arg7);
+ Push(a_Arg8);
+ if (!CallFunction(3))
+ {
+ return false;
+ }
+ GetReturn(-3, a_Ret1);
+ GetReturn(-2, a_Ret2);
+ GetReturn(-1, a_Ret3);
+ lua_pop(m_LuaState, 3);
+ return true;
+ }
+
+ /// Call any 9-param 5-return Lua function in a single line:
+ template<
+ typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
+ typename ArgT6, typename ArgT7, typename ArgT8, typename ArgT9,
+ typename RetT1, typename RetT2, typename RetT3, typename RetT4, typename RetT5
+ >
+ bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3, RetT4 & a_Ret4, RetT5 & a_Ret5)
+ {
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ Push(a_Arg1);
+ Push(a_Arg2);
+ Push(a_Arg3);
+ Push(a_Arg4);
+ Push(a_Arg5);
+ Push(a_Arg6);
+ Push(a_Arg7);
+ Push(a_Arg8);
+ Push(a_Arg9);
+ if (!CallFunction(5))
+ {
+ return false;
+ }
+ GetReturn(-5, a_Ret1);
+ GetReturn(-4, a_Ret2);
+ GetReturn(-3, a_Ret3);
+ GetReturn(-2, a_Ret4);
+ GetReturn(-1, a_Ret5);
+ lua_pop(m_LuaState, 5);
+ return true;
+ }
+
+
+ /// Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged
+ void GetReturn(int a_StackPos, bool & a_ReturnedVal);
+
+ /// Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged
+ void GetReturn(int a_StackPos, AString & a_ReturnedVal);
+
+ /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged
+ void GetReturn(int a_StackPos, int & a_ReturnedVal);
+
+ /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged
+ void GetReturn(int a_StackPos, double & a_ReturnedVal);
+
+ /**
+ Calls the function that has been pushed onto the stack by PushFunction(),
+ with arguments pushed by PushXXX().
+ Returns true if successful, logs a warning on failure.
+ */
+ bool CallFunction(int a_NumReturnValues);
+
+ /// Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions
+ bool CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam = -1);
+
+ /// Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not. Used for regular functions
+ bool CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam = -1);
+
+ /// Returns true if the specified parameters on the stack are tables; also logs warning if not
+ bool CheckParamTable(int a_StartParam, int a_EndParam = -1);
+
+ /// Returns true if the specified parameters on the stack are numbers; also logs warning if not
+ bool CheckParamNumber(int a_StartParam, int a_EndParam = -1);
+
+ /// Returns true if the specified parameters on the stack are strings; also logs warning if not
+ bool CheckParamString(int a_StartParam, int a_EndParam = -1);
+
+ /// Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters)
+ bool CheckParamEnd(int a_Param);
+
+ /// If the status is nonzero, prints the text on the top of Lua stack and returns true
+ bool ReportErrors(int status);
+
+ /// If the status is nonzero, prints the text on the top of Lua stack and returns true
+ static bool ReportErrors(lua_State * a_LuaState, int status);
+
+ /// Logs all items in the current stack trace to the server console
+ void LogStackTrace(void);
+
+ /// Returns the type of the item on the specified position in the stack
+ AString GetTypeText(int a_StackPos);
+
+protected:
+ lua_State * m_LuaState;
+
+ /// If true, the state is owned by this object and will be auto-Closed. False => attached state
+ bool m_IsOwned;
+
+ /** The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScript"
+ whatever is given to the constructor
+ */
+ AString m_SubsystemName;
+
+ /// Name of the currently pushed function (for the Push / Call chain)
+ AString m_CurrentFunctionName;
+
+ /// Number of arguments currently pushed (for the Push / Call chain)
+ int m_NumCurrentFunctionArgs;
+} ;
+
+
+
+
diff --git a/src/LuaWindow.cpp b/src/LuaWindow.cpp
new file mode 100644
index 000000000..f49ba2109
--- /dev/null
+++ b/src/LuaWindow.cpp
@@ -0,0 +1,185 @@
+
+// LuaWindow.cpp
+
+// Implements the cLuaWindow class representing a virtual window that plugins may create and open for the player
+
+#include "Globals.h"
+#include "LuaWindow.h"
+#include "UI/SlotArea.h"
+#include "PluginLua.h"
+#include "Entities/Player.h"
+#include "lib/lua/src/lauxlib.h" // Needed for LUA_REFNIL
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cLuaWindow:
+
+cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) :
+ super(a_WindowType, a_Title),
+ m_Contents(a_SlotsX, a_SlotsY),
+ m_Plugin(NULL),
+ m_LuaRef(LUA_REFNIL),
+ m_OnClosingFnRef(LUA_REFNIL),
+ m_OnSlotChangedFnRef(LUA_REFNIL)
+{
+ m_Contents.AddListener(*this);
+ m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this));
+
+ // If appropriate, add an Armor slot area:
+ switch (a_WindowType)
+ {
+ case cWindow::wtInventory:
+ case cWindow::wtWorkbench:
+ {
+ m_SlotAreas.push_back(new cSlotAreaArmor(*this));
+ break;
+ }
+ }
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+cLuaWindow::~cLuaWindow()
+{
+ m_Contents.RemoveListener(*this);
+
+ // Must delete slot areas now, because they are referencing this->m_Contents and would try to access it in cWindow's
+ // destructor, when the member is already gone.
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ m_SlotAreas.clear();
+
+ ASSERT(m_OpenedBy.empty());
+}
+
+
+
+
+
+void cLuaWindow::SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef)
+{
+ // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object
+ ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin));
+ ASSERT(m_LuaRef == LUA_REFNIL);
+ m_Plugin = a_Plugin;
+ m_LuaRef = a_LuaRef;
+}
+
+
+
+
+
+bool cLuaWindow::IsLuaReferenced(void) const
+{
+ return ((m_Plugin != NULL) && (m_LuaRef != LUA_REFNIL));
+}
+
+
+
+
+
+void cLuaWindow::SetOnClosing(cPluginLua * a_Plugin, int a_FnRef)
+{
+ // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object
+ ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin));
+
+ // If there already was a function, unreference it first
+ if (m_OnClosingFnRef != LUA_REFNIL)
+ {
+ m_Plugin->Unreference(m_OnClosingFnRef);
+ }
+
+ // Store the new reference
+ m_Plugin = a_Plugin;
+ m_OnClosingFnRef = a_FnRef;
+}
+
+
+
+
+
+void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef)
+{
+ // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object
+ ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin));
+
+ // If there already was a function, unreference it first
+ if (m_OnSlotChangedFnRef != LUA_REFNIL)
+ {
+ m_Plugin->Unreference(m_OnSlotChangedFnRef);
+ }
+
+ // Store the new reference
+ m_Plugin = a_Plugin;
+ m_OnSlotChangedFnRef = a_FnRef;
+}
+
+
+
+
+
+bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
+{
+ // First notify the plugin through the registered callback:
+ if (m_OnClosingFnRef != LUA_REFNIL)
+ {
+ ASSERT(m_Plugin != NULL);
+ if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse))
+ {
+ // The callback disagrees (the higher levels check the CanRefuse flag compliance)
+ return false;
+ }
+ }
+
+ return super::ClosedByPlayer(a_Player, a_CanRefuse);
+}
+
+
+
+
+
+void cLuaWindow::Destroy(void)
+{
+ super::Destroy();
+
+ if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != NULL))
+ {
+ // The object is referenced by Lua, un-reference it
+ m_Plugin->Unreference(m_LuaRef);
+ }
+
+ // Lua will take care of this object, it will garbage-collect it, so we *must not* delete it!
+ m_IsDestroyed = false;
+}
+
+
+
+
+
+void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ if (a_ItemGrid != &m_Contents)
+ {
+ ASSERT(!"Invalid ItemGrid in callback");
+ return;
+ }
+
+ // If an OnSlotChanged callback has been registered, call it:
+ if (m_OnSlotChangedFnRef != LUA_REFNIL)
+ {
+ m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum);
+ }
+}
+
+
+
+
diff --git a/src/LuaWindow.h b/src/LuaWindow.h
new file mode 100644
index 000000000..4c32c263e
--- /dev/null
+++ b/src/LuaWindow.h
@@ -0,0 +1,95 @@
+
+// LuaWindow.h
+
+// Declares the cLuaWindow class representing a virtual window that plugins may create and open for the player
+
+
+
+
+
+#pragma once
+
+#include "UI/Window.h"
+#include "ItemGrid.h"
+
+
+
+
+
+// fwd: PluginLua.h
+class cPluginLua;
+
+
+
+
+
+/** A window that has been created by a Lua plugin and is handled entirely by that plugin
+This object needs extra care with its lifetime management:
+- It is created by Lua, so Lua expects to garbage-collect it later
+- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them
+To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer()
+delete the window, but rather leaves it dangling, with only Lua having the reference to it.
+Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for
+cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object.
+This reference needs to be unreferenced in the Destroy() function.
+*/
+class cLuaWindow : // tolua_export
+ public cItemGrid::cListener,
+ // tolua_begin
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ /// Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size
+ cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title);
+
+ virtual ~cLuaWindow();
+
+ /// Returns the internal representation of the contents that are manipulated by Lua
+ cItemGrid & GetContents(void) { return m_Contents; }
+
+ // tolua_end
+
+ /** Sets the plugin reference and the internal Lua object reference index
+ used for preventing Lua's GC to collect this class while the window is open
+ */
+ void SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef);
+
+ /// Returns true if SetLuaRef() has been called
+ bool IsLuaReferenced(void) const;
+
+ /// Sets the callback function (Lua reference) to call when the window is about to close
+ void SetOnClosing(cPluginLua * a_Plugin, int a_FnRef);
+
+ /// Sets the callback function (Lua reference) to call when a slot is changed
+ void SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef);
+
+protected:
+ /// Contents of the non-inventory part
+ cItemGrid m_Contents;
+
+ /// The plugin that has opened the window and owns the m_LuaRef
+ cPluginLua * m_Plugin;
+
+ /// The Lua object reference, used for keeping the object alive as long as any player has the window open
+ int m_LuaRef;
+
+ /// The Lua reference for the callback to call when the window is closing for any player
+ int m_OnClosingFnRef;
+
+ /// The Lua reference for the callback to call when a slot has changed
+ int m_OnSlotChangedFnRef;
+
+ // cWindow overrides:
+ virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override;
+ virtual void Destroy(void) override;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+} ; // tolua_export
+
+
+
+
+
diff --git a/src/MCLogger.cpp b/src/MCLogger.cpp
new file mode 100644
index 000000000..4f3e5dc0f
--- /dev/null
+++ b/src/MCLogger.cpp
@@ -0,0 +1,261 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include <time.h>
+#include "Log.h"
+
+
+
+
+
+cMCLogger * cMCLogger::s_MCLogger = NULL;
+bool g_ShouldColorOutput = false;
+
+#ifdef _WIN32
+ #include <io.h> // Needed for _isatty(), not available on Linux
+
+ HANDLE g_Console = GetStdHandle(STD_OUTPUT_HANDLE);
+ WORD g_DefaultConsoleAttrib = 0x07;
+#elif defined (__linux) && !defined(ANDROID_NDK)
+ #include <unistd.h> // Needed for isatty() on Linux
+#endif
+
+
+
+
+
+cMCLogger * cMCLogger::GetInstance(void)
+{
+ return s_MCLogger;
+}
+
+
+
+
+
+cMCLogger::cMCLogger(void)
+{
+ AString FileName;
+ Printf(FileName, "LOG_%d.txt", (int)time(NULL));
+ InitLog(FileName);
+}
+
+
+
+
+
+cMCLogger::cMCLogger(const AString & a_FileName)
+{
+ InitLog(a_FileName);
+}
+
+
+
+
+
+cMCLogger::~cMCLogger()
+{
+ m_Log->Log("--- Stopped Log ---\n");
+ delete m_Log;
+ if (this == s_MCLogger)
+ {
+ s_MCLogger = NULL;
+ }
+}
+
+
+
+
+
+void cMCLogger::InitLog(const AString & a_FileName)
+{
+ m_Log = new cLog(a_FileName);
+ m_Log->Log("--- Started Log ---\n");
+
+ s_MCLogger = this;
+
+ #ifdef _WIN32
+ // See whether we are writing to a console the default console attrib:
+ g_ShouldColorOutput = (_isatty(_fileno(stdin)) != 0);
+ if (g_ShouldColorOutput)
+ {
+ CONSOLE_SCREEN_BUFFER_INFO sbi;
+ GetConsoleScreenBufferInfo(g_Console, &sbi);
+ g_DefaultConsoleAttrib = sbi.wAttributes;
+ }
+ #elif defined (__linux) && !defined(ANDROID_NDK)
+ g_ShouldColorOutput = isatty(fileno(stdout));
+ // TODO: Check if the terminal supports colors, somehow?
+ #endif
+}
+
+
+
+
+
+void cMCLogger::LogSimple(const char* a_Text, int a_LogType /* = 0 */ )
+{
+ switch( a_LogType )
+ {
+ case 0:
+ LOG("%s", a_Text);
+ break;
+ case 1:
+ LOGINFO("%s", a_Text);
+ break;
+ case 2:
+ LOGWARN("%s", a_Text);
+ break;
+ case 3:
+ LOGERROR("%s", a_Text);
+ break;
+ default:
+ LOG("(#%d#: %s", a_LogType, a_Text);
+ break;
+ }
+}
+
+
+
+
+
+void cMCLogger::Log(const char * a_Format, va_list a_ArgList)
+{
+ cCSLock Lock(m_CriticalSection);
+ SetColor(csRegular);
+ m_Log->Log(a_Format, a_ArgList);
+ ResetColor();
+ puts("");
+}
+
+
+
+
+
+void cMCLogger::Info(const char * a_Format, va_list a_ArgList)
+{
+ cCSLock Lock(m_CriticalSection);
+ SetColor(csInfo);
+ m_Log->Log(a_Format, a_ArgList);
+ ResetColor();
+ puts("");
+}
+
+
+
+
+
+void cMCLogger::Warn(const char * a_Format, va_list a_ArgList)
+{
+ cCSLock Lock(m_CriticalSection);
+ SetColor(csWarning);
+ m_Log->Log(a_Format, a_ArgList);
+ ResetColor();
+ puts("");
+}
+
+
+
+
+
+void cMCLogger::Error(const char * a_Format, va_list a_ArgList)
+{
+ cCSLock Lock(m_CriticalSection);
+ SetColor(csError);
+ m_Log->Log(a_Format, a_ArgList);
+ ResetColor();
+ puts("");
+}
+
+
+
+
+
+void cMCLogger::SetColor(eColorScheme a_Scheme)
+{
+ if (!g_ShouldColorOutput)
+ {
+ return;
+ }
+ #ifdef _WIN32
+ WORD Attrib = 0x07; // by default, gray on black
+ switch (a_Scheme)
+ {
+ case csRegular: Attrib = 0x07; break; // Gray on black
+ case csInfo: Attrib = 0x0e; break; // Yellow on black
+ case csWarning: Attrib = 0x0c; break; // Read on black
+ case csError: Attrib = 0xc0; break; // Black on red
+ default: ASSERT(!"Unhandled color scheme");
+ }
+ SetConsoleTextAttribute(g_Console, Attrib);
+ #elif defined(__linux) && !defined(ANDROID_NDK)
+ switch (a_Scheme)
+ {
+ case csRegular: printf("\x1b[0m"); break; // Whatever the console default is
+ case csInfo: printf("\x1b[33;1m"); break; // Yellow on black
+ case csWarning: printf("\x1b[31;1m"); break; // Red on black
+ case csError: printf("\x1b[1;33;41;1m"); break; // Yellow on red
+ default: ASSERT(!"Unhandled color scheme");
+ }
+ #endif
+}
+
+
+
+
+
+void cMCLogger::ResetColor(void)
+{
+ if (!g_ShouldColorOutput)
+ {
+ return;
+ }
+ #ifdef _WIN32
+ SetConsoleTextAttribute(g_Console, g_DefaultConsoleAttrib);
+ #elif defined(__linux) && !defined(ANDROID_NDK)
+ printf("\x1b[0m");
+ #endif
+}
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// Global functions
+
+void LOG(const char* a_Format, ...)
+{
+ va_list argList;
+ va_start(argList, a_Format);
+ cMCLogger::GetInstance()->Log( a_Format, argList );
+ va_end(argList);
+}
+
+void LOGINFO(const char* a_Format, ...)
+{
+ va_list argList;
+ va_start(argList, a_Format);
+ cMCLogger::GetInstance()->Info( a_Format, argList );
+ va_end(argList);
+}
+
+void LOGWARN(const char* a_Format, ...)
+{
+ va_list argList;
+ va_start(argList, a_Format);
+ cMCLogger::GetInstance()->Warn( a_Format, argList );
+ va_end(argList);
+}
+
+void LOGERROR(const char* a_Format, ...)
+{
+ va_list argList;
+ va_start(argList, a_Format);
+ cMCLogger::GetInstance()->Error( a_Format, argList );
+ va_end(argList);
+}
+
+
+
+
diff --git a/src/MCLogger.h b/src/MCLogger.h
new file mode 100644
index 000000000..c949a4cdf
--- /dev/null
+++ b/src/MCLogger.h
@@ -0,0 +1,84 @@
+
+#pragma once
+
+
+
+
+class cLog;
+
+
+
+
+
+class cMCLogger // tolua_export
+{ // tolua_export
+public: // tolua_export
+ /// Creates a logger with the default filename, "logs/LOG_<timestamp>.log"
+ cMCLogger(void);
+
+ /// Creates a logger with the specified filename inside "logs" folder
+ cMCLogger(const AString & a_FileName); // tolua_export
+
+ ~cMCLogger(); // tolua_export
+
+ void Log(const char* a_Format, va_list a_ArgList);
+ void Info(const char* a_Format, va_list a_ArgList);
+ void Warn(const char* a_Format, va_list a_ArgList);
+ void Error(const char* a_Format, va_list a_ArgList);
+
+ void LogSimple(const char* a_Text, int a_LogType = 0 ); // tolua_export
+
+ static cMCLogger* GetInstance();
+private:
+ enum eColorScheme
+ {
+ csRegular,
+ csInfo,
+ csWarning,
+ csError,
+ } ;
+
+ cCriticalSection m_CriticalSection;
+ cLog * m_Log;
+ static cMCLogger * s_MCLogger;
+
+
+ /// Sets the specified color scheme in the terminal (TODO: if coloring available)
+ void SetColor(eColorScheme a_Scheme);
+
+ /// Resets the color back to whatever is the default in the terminal
+ void ResetColor(void);
+
+ /// Common initialization for all constructors, creates a logfile with the specified name and assigns s_MCLogger to this
+ void InitLog(const AString & a_FileName);
+}; // tolua_export
+
+
+
+
+
+extern void LOG(const char* a_Format, ...);
+extern void LOGINFO(const char* a_Format, ...);
+extern void LOGWARN(const char* a_Format, ...);
+extern void LOGERROR(const char* a_Format, ...);
+
+
+
+
+
+// In debug builds, translate LOGD to LOG, otherwise leave it out altogether:
+#ifdef _DEBUG
+ #define LOGD LOG
+#else
+ #define LOGD(...)
+#endif // _DEBUG
+
+
+
+
+
+#define LOGWARNING LOGWARN
+
+
+
+
diff --git a/src/ManualBindings.cpp b/src/ManualBindings.cpp
new file mode 100644
index 000000000..7ea382b76
--- /dev/null
+++ b/src/ManualBindings.cpp
@@ -0,0 +1,2285 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "ManualBindings.h"
+#include "tolua++.h"
+
+#include "Root.h"
+#include "World.h"
+#include "Plugin.h"
+#include "PluginLua.h"
+#include "PluginManager.h"
+#include "Entities/Player.h"
+#include "WebAdmin.h"
+#include "ClientHandle.h"
+#include "BlockEntities/ChestEntity.h"
+#include "BlockEntities/DispenserEntity.h"
+#include "BlockEntities/DropperEntity.h"
+#include "BlockEntities/FurnaceEntity.h"
+#include "BlockEntities/HopperEntity.h"
+#include "lib/md5/md5.h"
+#include "LuaWindow.h"
+#include "LineBlockTracer.h"
+
+
+
+
+
+/****************************
+ * Better error reporting for Lua
+ **/
+int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError)
+{
+ // Retrieve current function name
+ lua_Debug entry;
+ VERIFY(lua_getstack(L, 0, &entry));
+ VERIFY(lua_getinfo(L, "n", &entry));
+
+ // Insert function name into error msg
+ AString msg(a_pMsg);
+ ReplaceString(msg, "#funcname#", entry.name?entry.name:"?");
+
+ // Send the error to Lua
+ tolua_error(L, msg.c_str(), a_pToLuaError);
+ return 0;
+}
+
+
+
+
+
+int lua_do_error(lua_State* L, const char * a_pFormat, ...)
+{
+ // Retrieve current function name
+ lua_Debug entry;
+ VERIFY(lua_getstack(L, 0, &entry));
+ VERIFY(lua_getinfo(L, "n", &entry));
+
+ // Insert function name into error msg
+ AString msg(a_pFormat);
+ ReplaceString(msg, "#funcname#", entry.name?entry.name:"?");
+
+ // Copied from luaL_error and modified
+ va_list argp;
+ va_start(argp, a_pFormat);
+ luaL_where(L, 1);
+ lua_pushvfstring(L, msg.c_str(), argp);
+ va_end(argp);
+ lua_concat(L, 2);
+ return lua_error(L);
+}
+
+
+
+
+
+/****************************
+ * Lua bound functions with special return types
+ **/
+
+static int tolua_StringSplit(lua_State * tolua_S)
+{
+ cLuaState LuaState(tolua_S);
+ std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0);
+ std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0);
+
+ AStringVector Split = StringSplit(str, delim);
+ LuaState.Push(Split);
+ return 1;
+}
+
+
+
+
+
+static int tolua_StringSplitAndTrim(lua_State * tolua_S)
+{
+ cLuaState LuaState(tolua_S);
+ std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0);
+ std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0);
+
+ AStringVector Split = StringSplitAndTrim(str, delim);
+ LuaState.Push(Split);
+ return 1;
+}
+
+
+
+
+
+static int tolua_LOG(lua_State* tolua_S)
+{
+ const char* str = tolua_tocppstring(tolua_S,1,0);
+ cMCLogger::GetInstance()->LogSimple( str, 0 );
+ return 0;
+}
+
+
+
+
+
+static int tolua_LOGINFO(lua_State* tolua_S)
+{
+ const char* str = tolua_tocppstring(tolua_S,1,0);
+ cMCLogger::GetInstance()->LogSimple( str, 1 );
+ return 0;
+}
+
+
+
+
+
+static int tolua_LOGWARN(lua_State* tolua_S)
+{
+ const char* str = tolua_tocppstring(tolua_S,1,0);
+ cMCLogger::GetInstance()->LogSimple( str, 2 );
+ return 0;
+}
+
+
+
+
+
+static int tolua_LOGERROR(lua_State* tolua_S)
+{
+ const char* str = tolua_tocppstring(tolua_S,1,0);
+ cMCLogger::GetInstance()->LogSimple( str, 3 );
+ return 0;
+}
+
+
+
+
+
+cPluginLua * GetLuaPlugin(lua_State * L)
+{
+ // Get the plugin identification out of LuaState:
+ lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME);
+ if (!lua_islightuserdata(L, -1))
+ {
+ LOGWARNING("%s: cannot get plugin instance, what have you done to my Lua state?", __FUNCTION__);
+ lua_pop(L, 1);
+ return NULL;
+ }
+ cPluginLua * Plugin = (cPluginLua *)lua_topointer(L, -1);
+ lua_pop(L, 1);
+
+ return Plugin;
+}
+
+
+
+
+
+static int tolua_cFile_GetFolderContents(lua_State * tolua_S)
+{
+ cLuaState LuaState(tolua_S);
+ if (
+ !LuaState.CheckParamUserTable(1, "cFile") ||
+ !LuaState.CheckParamString (2) ||
+ !LuaState.CheckParamEnd (3)
+ )
+ {
+ return 0;
+ }
+
+ AString Folder = (AString)tolua_tocppstring(LuaState, 2, 0);
+
+ AStringVector Contents = cFile::GetFolderContents(Folder);
+ LuaState.Push(Contents);
+ return 1;
+}
+
+
+
+
+
+template<
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*Func1)(const AString &, cItemCallback<Ty2> &)
+ >
+static int tolua_DoWith(lua_State* tolua_S)
+{
+ int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
+ if ((NumArgs != 2) && (NumArgs != 3))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs);
+ }
+
+ Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0);
+
+ const char * ItemName = tolua_tocppstring(tolua_S, 2, "");
+ if ((ItemName == NULL) || (ItemName[0] == 0))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1", NumArgs);
+ }
+ if (!lua_isfunction( tolua_S, 3))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs);
+ }
+
+ /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */
+ int TableRef = LUA_REFNIL;
+ if (NumArgs == 3)
+ {
+ TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (TableRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs);
+ }
+ }
+
+ /* table value is popped, and now function is on top of the stack */
+ int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FuncRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs);
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef)
+ : LuaState( a_LuaState )
+ , FuncRef( a_FuncRef )
+ , TableRef( a_TableRef )
+ {}
+
+ private:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
+ tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic());
+ if (TableRef != LUA_REFNIL)
+ {
+ lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */
+ }
+
+ int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
+ if (cLuaState::ReportErrors(LuaState, s))
+ {
+ return true; // Abort enumeration
+ }
+ if (lua_isboolean(LuaState, -1))
+ {
+ return (tolua_toboolean(LuaState, -1, 0) > 0);
+ }
+ return false; /* Continue enumeration */
+ }
+ lua_State * LuaState;
+ int FuncRef;
+ int TableRef;
+ } Callback(tolua_S, FuncRef, TableRef);
+
+
+ bool bRetVal = (self->*Func1)(ItemName, Callback);
+
+ /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef);
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
+
+ /* Push return value on stack */
+ tolua_pushboolean(tolua_S, bRetVal );
+ return 1;
+}
+
+
+
+
+
+template<
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*Func1)(int, cItemCallback<Ty2> &)
+>
+static int tolua_DoWithID(lua_State* tolua_S)
+{
+ int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
+ if ((NumArgs != 2) && (NumArgs != 3))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs);
+ }
+
+ Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, 0);
+
+ int ItemID = (int)tolua_tonumber(tolua_S, 2, 0);
+ if (!lua_isfunction(tolua_S, 3))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs);
+ }
+
+ /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */
+ int TableRef = LUA_REFNIL;
+ if (NumArgs == 3)
+ {
+ TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (TableRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs);
+ }
+ }
+
+ /* table value is popped, and now function is on top of the stack */
+ int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FuncRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs);
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(lua_State * a_LuaState, int a_FuncRef, int a_TableRef) :
+ LuaState(a_LuaState),
+ FuncRef(a_FuncRef),
+ TableRef(a_TableRef)
+ {}
+
+ private:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call
+ tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); // Push the item
+ if (TableRef != LUA_REFNIL)
+ {
+ lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param
+ }
+
+ int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
+ if (cLuaState::ReportErrors(LuaState, s))
+ {
+ return true; // Abort enumeration
+ }
+ if (lua_isboolean(LuaState, -1))
+ {
+ return (tolua_toboolean(LuaState, -1, 0) > 0);
+ }
+ return false; /* Continue enumeration */
+ }
+ lua_State * LuaState;
+ int FuncRef;
+ int TableRef;
+ } Callback(tolua_S, FuncRef, TableRef);
+
+
+ bool bRetVal = (self->*Func1)(ItemID, Callback);
+
+ /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef);
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
+
+ /* Push return value on stack */
+ tolua_pushboolean(tolua_S, bRetVal );
+ return 1;
+}
+
+
+
+
+
+template<
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*Func1)(int, int, int, cItemCallback<Ty2> &)
+>
+static int tolua_DoWithXYZ(lua_State* tolua_S)
+{
+ int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
+ if ((NumArgs != 4) && (NumArgs != 5))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 4 or 5 arguments, got %i", NumArgs);
+ }
+
+ Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0);
+ if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3) || !lua_isnumber(tolua_S, 4))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3");
+ }
+
+ int ItemX = ((int)tolua_tonumber(tolua_S, 2, 0));
+ int ItemY = ((int)tolua_tonumber(tolua_S, 3, 0));
+ int ItemZ = ((int)tolua_tonumber(tolua_S, 4, 0));
+ LOG("x %i y %i z %i", ItemX, ItemY, ItemZ );
+ if (!lua_isfunction( tolua_S, 5))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #4");
+ }
+
+ /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */
+ int TableRef = LUA_REFNIL;
+ if (NumArgs == 5)
+ {
+ TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (TableRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #5");
+ }
+ }
+
+ /* table value is popped, and now function is on top of the stack */
+ int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FuncRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #4");
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef)
+ : LuaState( a_LuaState )
+ , FuncRef( a_FuncRef )
+ , TableRef( a_TableRef )
+ {}
+
+ private:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
+ tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic());
+ if (TableRef != LUA_REFNIL)
+ {
+ lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */
+ }
+
+ int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
+ if (cLuaState::ReportErrors(LuaState, s))
+ {
+ return true; // Abort enumeration
+ }
+ if (lua_isboolean(LuaState, -1))
+ {
+ return (tolua_toboolean(LuaState, -1, 0) > 0);
+ }
+ return false; /* Continue enumeration */
+ }
+ lua_State * LuaState;
+ int FuncRef;
+ int TableRef;
+ } Callback(tolua_S, FuncRef, TableRef);
+
+ bool bRetVal = (self->*Func1)(ItemX, ItemY, ItemZ, Callback);
+
+ /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef);
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
+
+ /* Push return value on stack */
+ tolua_pushboolean(tolua_S, bRetVal );
+ return 1;
+}
+
+
+
+
+
+template< class Ty1,
+ class Ty2,
+ bool (Ty1::*Func1)(int, int, cItemCallback<Ty2> &) >
+static int tolua_ForEachInChunk(lua_State* tolua_S)
+{
+ int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
+ if ((NumArgs != 3) && (NumArgs != 4))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 3 or 4 arguments, got %i", NumArgs);
+ }
+
+ Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0);
+ if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1 and #2");
+ }
+
+ int ChunkX = ((int)tolua_tonumber(tolua_S, 2, 0));
+ int ChunkZ = ((int)tolua_tonumber(tolua_S, 3, 0));
+
+ if (!lua_isfunction( tolua_S, 4))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #3");
+ }
+
+ /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */
+ int TableRef = LUA_REFNIL;
+ if (NumArgs == 4)
+ {
+ TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (TableRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #4");
+ }
+ }
+
+ /* table value is popped, and now function is on top of the stack */
+ int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FuncRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #3");
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef)
+ : LuaState( a_LuaState )
+ , FuncRef( a_FuncRef )
+ , TableRef( a_TableRef )
+ {}
+
+ private:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
+ tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic());
+ if (TableRef != LUA_REFNIL)
+ {
+ lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */
+ }
+
+ int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
+ if (cLuaState::ReportErrors(LuaState, s))
+ {
+ return true; /* Abort enumeration */
+ }
+
+ if (lua_isboolean(LuaState, -1))
+ {
+ return (tolua_toboolean(LuaState, -1, 0) > 0);
+ }
+ return false; /* Continue enumeration */
+ }
+ lua_State * LuaState;
+ int FuncRef;
+ int TableRef;
+ } Callback(tolua_S, FuncRef, TableRef);
+
+ bool bRetVal = (self->*Func1)(ChunkX, ChunkZ, Callback);
+
+ /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef);
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
+
+ /* Push return value on stack */
+ tolua_pushboolean(tolua_S, bRetVal );
+ return 1;
+}
+
+
+
+
+
+template< class Ty1,
+ class Ty2,
+ bool (Ty1::*Func1)(cItemCallback<Ty2> &) >
+static int tolua_ForEach(lua_State * tolua_S)
+{
+ int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
+ if( NumArgs != 1 && NumArgs != 2)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 1 or 2 arguments, got %i", NumArgs);
+ }
+
+ Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0);
+ if (self == NULL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
+ }
+
+ if (!lua_isfunction( tolua_S, 2))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1");
+ }
+
+ /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */
+ int TableRef = LUA_REFNIL;
+ if (NumArgs == 2)
+ {
+ TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (TableRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #2");
+ }
+ }
+
+ /* table value is popped, and now function is on top of the stack */
+ int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FuncRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
+ }
+
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef)
+ : LuaState( a_LuaState )
+ , FuncRef( a_FuncRef )
+ , TableRef( a_TableRef )
+ {}
+
+ private:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
+ tolua_pushusertype( LuaState, a_Item, Ty2::GetClassStatic() );
+ if (TableRef != LUA_REFNIL)
+ {
+ lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */
+ }
+
+ int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
+ if (cLuaState::ReportErrors(LuaState, s))
+ {
+ return true; /* Abort enumeration */
+ }
+
+ if (lua_isboolean(LuaState, -1))
+ {
+ return (tolua_toboolean( LuaState, -1, 0) > 0);
+ }
+ return false; /* Continue enumeration */
+ }
+ lua_State * LuaState;
+ int FuncRef;
+ int TableRef;
+ } Callback(tolua_S, FuncRef, TableRef);
+
+ bool bRetVal = (self->*Func1)(Callback);
+
+ /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef);
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
+
+ /* Push return value on stack */
+ tolua_pushboolean(tolua_S, bRetVal );
+ return 1;
+}
+
+
+
+
+
+static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S)
+{
+ // Exported manually, because tolua would generate useless additional parameters (a_BlockType .. a_BlockSkyLight)
+ // Function signature: GetBlockInfo(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta, BlockSkyLight, BlockBlockLight]
+ #ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 2, 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 3, 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 4, 0, &tolua_err) ||
+ !tolua_isnoobj (tolua_S, 5, &tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ #endif
+ {
+ cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0);
+ int BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
+ int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
+ int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
+ #ifndef TOLUA_RELEASE
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'GetBlockInfo'", NULL);
+ }
+ #endif
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta, BlockSkyLight, BlockBlockLight;
+ bool res = self->GetBlockInfo(BlockX, BlockY, BlockZ, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight);
+ tolua_pushboolean(tolua_S, res ? 1 : 0);
+ if (res)
+ {
+ tolua_pushnumber(tolua_S, BlockType);
+ tolua_pushnumber(tolua_S, BlockMeta);
+ tolua_pushnumber(tolua_S, BlockSkyLight);
+ tolua_pushnumber(tolua_S, BlockBlockLight);
+ return 5;
+ }
+ }
+ }
+ return 1;
+
+ #ifndef TOLUA_RELEASE
+tolua_lerror:
+ tolua_error(tolua_S, "#ferror in function 'GetBlockInfo'.", &tolua_err);
+ return 0;
+ #endif
+}
+
+
+
+
+
+static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S)
+{
+ // Exported manually, because tolua would generate useless additional parameters (a_BlockType, a_BlockMeta)
+ // Function signature: GetBlockTypeMeta(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta]
+ #ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 2, 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 3, 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 4, 0, &tolua_err) ||
+ !tolua_isnoobj (tolua_S, 5, &tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ #endif
+ {
+ cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0);
+ int BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
+ int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
+ int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
+ #ifndef TOLUA_RELEASE
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'GetBlockTypeMeta'", NULL);
+ }
+ #endif
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ bool res = self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta);
+ tolua_pushboolean(tolua_S, res ? 1 : 0);
+ if (res)
+ {
+ tolua_pushnumber(tolua_S, BlockType);
+ tolua_pushnumber(tolua_S, BlockMeta);
+ return 3;
+ }
+ }
+ }
+ return 1;
+
+ #ifndef TOLUA_RELEASE
+tolua_lerror:
+ tolua_error(tolua_S, "#ferror in function 'GetBlockTypeMeta'.", &tolua_err);
+ return 0;
+ #endif
+}
+
+
+
+
+
+static int tolua_cWorld_GetSignLines(lua_State * tolua_S)
+{
+ // Exported manually, because tolua would generate useless additional parameters (a_Line1 .. a_Line4)
+ #ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 2, 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 3, 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 4, 0, &tolua_err) ||
+ !tolua_isnoobj (tolua_S, 10, &tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ #endif
+ {
+ cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0);
+ int BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
+ int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
+ int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
+ #ifndef TOLUA_RELEASE
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'GetSignLines'", NULL);
+ }
+ #endif
+ {
+ AString Line1, Line2, Line3, Line4;
+ bool res = self->GetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
+ tolua_pushboolean(tolua_S, res ? 1 : 0);
+ if (res)
+ {
+ tolua_pushstring(tolua_S, Line1.c_str());
+ tolua_pushstring(tolua_S, Line2.c_str());
+ tolua_pushstring(tolua_S, Line3.c_str());
+ tolua_pushstring(tolua_S, Line4.c_str());
+ return 5;
+ }
+ }
+ }
+ return 1;
+
+ #ifndef TOLUA_RELEASE
+tolua_lerror:
+ tolua_error(tolua_S, "#ferror in function 'GetSignLines'.", &tolua_err);
+ return 0;
+ #endif
+}
+
+
+
+
+
+static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
+{
+ // Exported manually, because tolua would generate useless additional return values (a_Line1 .. a_Line4)
+ #ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 2, 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 3, 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 4, 0, &tolua_err) ||
+ !tolua_iscppstring(tolua_S, 5, 0, &tolua_err) ||
+ !tolua_iscppstring(tolua_S, 6, 0, &tolua_err) ||
+ !tolua_iscppstring(tolua_S, 7, 0, &tolua_err) ||
+ !tolua_iscppstring(tolua_S, 8, 0, &tolua_err) ||
+ !tolua_isusertype (tolua_S, 9, "cPlayer", 1, &tolua_err) ||
+ !tolua_isnoobj (tolua_S, 10, &tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ #endif
+ {
+ cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0);
+ int BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
+ int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
+ int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
+ const AString Line1 = tolua_tocppstring(tolua_S, 5, 0);
+ const AString Line2 = tolua_tocppstring(tolua_S, 6, 0);
+ const AString Line3 = tolua_tocppstring(tolua_S, 7, 0);
+ const AString Line4 = tolua_tocppstring(tolua_S, 8, 0);
+ cPlayer * Player = (cPlayer *)tolua_tousertype (tolua_S, 9, NULL);
+ #ifndef TOLUA_RELEASE
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'SetSignLines' / 'UpdateSign'", NULL);
+ }
+ #endif
+ {
+ bool res = self->UpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player);
+ tolua_pushboolean(tolua_S, res ? 1 : 0);
+ }
+ }
+ return 1;
+
+ #ifndef TOLUA_RELEASE
+tolua_lerror:
+ tolua_error(tolua_S, "#ferror in function 'SetSignLines' / 'UpdateSign'.", &tolua_err);
+ return 0;
+ #endif
+}
+
+
+
+
+static int tolua_cWorld_TryGetHeight(lua_State * tolua_S)
+{
+ // Exported manually, because tolua would require the out-only param a_Height to be used when calling
+ // Takes (a_World,) a_BlockX, a_BlockZ
+ // Returns Height, IsValid
+ #ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 2, 0, &tolua_err) ||
+ !tolua_isnumber (tolua_S, 3, 0, &tolua_err) ||
+ !tolua_isnoobj (tolua_S, 4, &tolua_err)
+ )
+ goto tolua_lerror;
+ else
+ #endif
+ {
+ cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0);
+ int BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
+ int BlockZ = (int) tolua_tonumber (tolua_S, 3, 0);
+ #ifndef TOLUA_RELEASE
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "Invalid 'self' in function 'TryGetHeight'", NULL);
+ }
+ #endif
+ {
+ int Height = 0;
+ bool res = self->TryGetHeight(BlockX, BlockZ, Height);
+ tolua_pushnumber(tolua_S, Height);
+ tolua_pushboolean(tolua_S, res ? 1 : 0);
+ }
+ }
+ return 1;
+
+ #ifndef TOLUA_RELEASE
+tolua_lerror:
+ tolua_error(tolua_S, "#ferror in function 'TryGetHeight'.", &tolua_err);
+ return 0;
+ #endif
+}
+
+
+
+
+
+class cLuaWorldTask :
+ public cWorld::cTask
+{
+public:
+ cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) :
+ m_Plugin(a_Plugin),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+protected:
+ cPluginLua & m_Plugin;
+ int m_FnRef;
+
+ // cWorld::cTask overrides:
+ virtual void Run(cWorld & a_World) override
+ {
+ m_Plugin.Call(m_FnRef, &a_World);
+ }
+} ;
+
+
+
+
+
+static int tolua_cWorld_QueueTask(lua_State * tolua_S)
+{
+ // Binding for cWorld::QueueTask
+ // Params: function
+
+ // Retrieve the cPlugin from the LuaState:
+ cPluginLua * Plugin = GetLuaPlugin(tolua_S);
+ if (Plugin == NULL)
+ {
+ // An error message has been already printed in GetLuaPlugin()
+ return 0;
+ }
+
+ // Retrieve the args:
+ cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, 0);
+ if (self == NULL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
+ }
+ if (!lua_isfunction(tolua_S, 2))
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1");
+ }
+
+ // Create a reference to the function:
+ int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FnRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
+ }
+
+ self->QueueTask(new cLuaWorldTask(*Plugin, FnRef));
+ return 0;
+}
+
+
+
+
+
+static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S)
+{
+ cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+
+ const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins();
+
+ lua_newtable(tolua_S);
+ //lua_createtable(tolua_S, AllPlugins.size(), 0);
+ int newTable = lua_gettop(tolua_S);
+ int index = 1;
+ cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin();
+ while(iter != AllPlugins.end())
+ {
+ const cPlugin* Plugin = iter->second;
+ tolua_pushstring( tolua_S, iter->first.c_str() );
+ if( Plugin != NULL )
+ {
+ tolua_pushusertype( tolua_S, (void*)Plugin, "const cPlugin" );
+ }
+ else
+ {
+ tolua_pushboolean(tolua_S, 0);
+ }
+ //lua_rawseti(tolua_S, newTable, index);
+ lua_rawset(tolua_S, -3);
+ ++iter;
+ ++index;
+ }
+ return 1;
+}
+
+
+
+
+
+static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx)
+{
+ // Helper function for cPluginmanager:AddHook() binding
+ // Takes care of the new case (#121): args are HOOK_TYPE and CallbackFunction
+ // The arg types have already been checked
+
+ // Retrieve the cPlugin from the LuaState:
+ cPluginLua * Plugin = GetLuaPlugin(S);
+ if (Plugin == NULL)
+ {
+ // An error message has been already printed in GetLuaPlugin()
+ return 0;
+ }
+
+ // Retrieve and check the hook type
+ int HookType = (int)tolua_tonumber(S, a_ParamIdx, -1);
+ if (!a_PluginManager->IsValidHookType(HookType))
+ {
+ LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType);
+ S.LogStackTrace();
+ return 0;
+ }
+
+ // Add the hook to the plugin
+ if (!Plugin->AddHookRef(HookType, a_ParamIdx + 1))
+ {
+ LOGWARNING("cPluginManager.AddHook(): Cannot add hook %d, unknown error.", HookType);
+ S.LogStackTrace();
+ return 0;
+ }
+ a_PluginManager->AddHook(Plugin, HookType);
+
+ // Success
+ return 0;
+}
+
+
+
+
+
+static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx)
+{
+ // Helper function for cPluginmanager:AddHook() binding
+ // Takes care of the old case (#121): args are cPluginLua and HOOK_TYPE
+ // The arg types have already been checked
+
+ // Retrieve and check the cPlugin parameter
+ cPluginLua * Plugin = (cPluginLua *)tolua_tousertype(S, a_ParamIdx, NULL);
+ if (Plugin == NULL)
+ {
+ LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, expected a valid cPlugin object. Hook not added");
+ S.LogStackTrace();
+ return 0;
+ }
+ if (Plugin != GetLuaPlugin(S))
+ {
+ // The plugin parameter passed to us is not our stored plugin. Disallow this!
+ LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, cannot add hook to foreign plugins. Hook not added.");
+ S.LogStackTrace();
+ return 0;
+ }
+
+ // Retrieve and check the hook type
+ int HookType = (int)tolua_tonumber(S, a_ParamIdx + 1, -1);
+ if (!a_PluginManager->IsValidHookType(HookType))
+ {
+ LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType);
+ S.LogStackTrace();
+ return 0;
+ }
+
+ // Get the standard name for the callback function:
+ const char * FnName = cPluginLua::GetHookFnName(HookType);
+ if (FnName == NULL)
+ {
+ LOGWARNING("cPluginManager.AddHook(): Unknown hook type (%d). Hook not added.", HookType);
+ S.LogStackTrace();
+ return 0;
+ }
+
+ // Retrieve the function to call and add it to the plugin:
+ lua_pushstring(S, FnName);
+ bool res = Plugin->AddHookRef(HookType, 1);
+ lua_pop(S, 1); // Pop the function off the stack
+ if (!res)
+ {
+ LOGWARNING("cPluginManager.AddHook(): Function %s not found. Hook not added.", FnName);
+ S.LogStackTrace();
+ return 0;
+ }
+ a_PluginManager->AddHook(Plugin, HookType);
+
+ // Success
+ return 0;
+}
+
+
+
+
+
+static int tolua_cPluginManager_AddHook(lua_State * tolua_S)
+{
+ /*
+ Function signatures:
+ cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) -- (1) recommended
+ cPluginManager:Get():AddHook(HOOK_TYPE, CallbackFunction) -- (2) accepted silently
+ cPluginManager:Get():AddHook(Plugin, HOOK_TYPE) -- (3) old style (#121), accepted but complained about
+ cPluginManager.AddHook(Plugin, HOOK_TYPE) -- (4) old style (#121) mangled, accepted but complained about
+ */
+
+ cLuaState S(tolua_S);
+ cPluginManager * PlgMgr = cPluginManager::Get();
+
+ // If the first param is a cPluginManager, use it instead of the global one:
+ int ParamIdx = 1;
+ tolua_Error err;
+ if (tolua_isusertype(S, 1, "cPluginManager", 0, &err))
+ {
+ // Style 2 or 3, retrieve the PlgMgr instance
+ PlgMgr = (cPluginManager *)tolua_tousertype(S, 1, NULL);
+ if (PlgMgr == NULL)
+ {
+ LOGWARNING("Malformed plugin, use cPluginManager.AddHook(HOOK_TYPE, CallbackFunction). Fixing the call for you.");
+ S.LogStackTrace();
+ PlgMgr = cPluginManager::Get();
+ }
+ ParamIdx += 1;
+ }
+
+ if (lua_isnumber(S, ParamIdx) && lua_isfunction(S, ParamIdx + 1))
+ {
+ // The next params are a number and a function, assume style 1 or 2
+ return tolua_cPluginManager_AddHook_FnRef(PlgMgr, S, ParamIdx);
+ }
+ else if (tolua_isusertype(S, ParamIdx, "cPlugin", 0, &err) && lua_isnumber(S, ParamIdx + 1))
+ {
+ // The next params are a cPlugin and a number, assume style 3 or 4
+ LOGINFO("cPluginManager.AddHook(): Deprecated format used, use cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) instead. Fixing the call for you.");
+ S.LogStackTrace();
+ return tolua_cPluginManager_AddHook_DefFn(PlgMgr, S, ParamIdx);
+ }
+
+ AString ParamDesc;
+ Printf(ParamDesc, "%s, %s, %s", S.GetTypeText(1).c_str(), S.GetTypeText(2).c_str(), S.GetTypeText(3).c_str());
+ LOGWARNING("cPluginManager.AddHook(): bad parameters. Expected HOOK_TYPE and CallbackFunction, got %s. Hook not added.", ParamDesc.c_str());
+ S.LogStackTrace();
+ return 0;
+}
+
+
+
+
+
+static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S)
+{
+ int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
+ if( NumArgs != 1)
+ {
+ LOGWARN("Error in function call 'ForEachCommand': Requires 1 argument, got %i", NumArgs);
+ return 0;
+ }
+
+ cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0);
+ if (self == NULL)
+ {
+ LOGWARN("Error in function call 'ForEachCommand': Not called on an object instance");
+ return 0;
+ }
+
+ if (!lua_isfunction(tolua_S, 2))
+ {
+ LOGWARN("Error in function call 'ForEachCommand': Expected a function for parameter #1");
+ return 0;
+ }
+
+ int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FuncRef == LUA_REFNIL)
+ {
+ LOGWARN("Error in function call 'ForEachCommand': Could not get function reference of parameter #1");
+ return 0;
+ }
+
+ class cLuaCallback : public cPluginManager::cCommandEnumCallback
+ {
+ public:
+ cLuaCallback(lua_State * a_LuaState, int a_FuncRef)
+ : LuaState( a_LuaState )
+ , FuncRef( a_FuncRef )
+ {}
+
+ private:
+ virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
+ {
+ lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
+ tolua_pushcppstring(LuaState, a_Command);
+ tolua_pushcppstring(LuaState, a_Permission);
+ tolua_pushcppstring(LuaState, a_HelpString);
+
+ int s = lua_pcall(LuaState, 3, 1, 0);
+ if (cLuaState::ReportErrors(LuaState, s))
+ {
+ return true; /* Abort enumeration */
+ }
+
+ if (lua_isboolean(LuaState, -1))
+ {
+ return (tolua_toboolean( LuaState, -1, 0) > 0);
+ }
+ return false; /* Continue enumeration */
+ }
+ lua_State * LuaState;
+ int FuncRef;
+ } Callback(tolua_S, FuncRef);
+
+ bool bRetVal = self->ForEachCommand(Callback);
+
+ /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
+
+ /* Push return value on stack */
+ tolua_pushboolean(tolua_S, bRetVal);
+ return 1;
+}
+
+
+
+
+
+static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S)
+{
+ int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
+ if( NumArgs != 1)
+ {
+ LOGWARN("Error in function call 'ForEachConsoleCommand': Requires 1 argument, got %i", NumArgs);
+ return 0;
+ }
+
+ cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0);
+ if (self == NULL)
+ {
+ LOGWARN("Error in function call 'ForEachConsoleCommand': Not called on an object instance");
+ return 0;
+ }
+
+ if (!lua_isfunction(tolua_S, 2))
+ {
+ LOGWARN("Error in function call 'ForEachConsoleCommand': Expected a function for parameter #1");
+ return 0;
+ }
+
+ int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FuncRef == LUA_REFNIL)
+ {
+ LOGWARN("Error in function call 'ForEachConsoleCommand': Could not get function reference of parameter #1");
+ return 0;
+ }
+
+ class cLuaCallback : public cPluginManager::cCommandEnumCallback
+ {
+ public:
+ cLuaCallback(lua_State * a_LuaState, int a_FuncRef)
+ : LuaState( a_LuaState )
+ , FuncRef( a_FuncRef )
+ {}
+
+ private:
+ virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
+ {
+ lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
+ tolua_pushcppstring(LuaState, a_Command);
+ tolua_pushcppstring(LuaState, a_HelpString);
+
+ int s = lua_pcall(LuaState, 2, 1, 0);
+ if (cLuaState::ReportErrors(LuaState, s))
+ {
+ return true; /* Abort enumeration */
+ }
+
+ if (lua_isboolean(LuaState, -1))
+ {
+ return (tolua_toboolean( LuaState, -1, 0) > 0);
+ }
+ return false; /* Continue enumeration */
+ }
+ lua_State * LuaState;
+ int FuncRef;
+ } Callback(tolua_S, FuncRef);
+
+ bool bRetVal = self->ForEachConsoleCommand(Callback);
+
+ /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
+ luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
+
+ /* Push return value on stack */
+ tolua_pushboolean(tolua_S, bRetVal);
+ return 1;
+}
+
+
+
+
+
+static int tolua_cPluginManager_BindCommand(lua_State * L)
+{
+ /* Function signatures:
+ cPluginManager:BindCommand(Command, Permission, Function, HelpString)
+ cPluginManager.BindCommand(Command, Permission, Function, HelpString) -- without the "self" param
+ */
+ cPluginLua * Plugin = GetLuaPlugin(L);
+ if (Plugin == NULL)
+ {
+ return 0;
+ }
+
+ // Read the arguments to this API call:
+ tolua_Error tolua_err;
+ int idx = 1;
+ if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err))
+ {
+ idx++;
+ }
+ if (
+ !tolua_iscppstring(L, idx, 0, &tolua_err) ||
+ !tolua_iscppstring(L, idx + 1, 0, &tolua_err) ||
+ !tolua_iscppstring(L, idx + 3, 0, &tolua_err) ||
+ !tolua_isnoobj (L, idx + 4, &tolua_err)
+ )
+ {
+ tolua_error(L, "#ferror in function 'BindCommand'.", &tolua_err);
+ return 0;
+ }
+ if (!lua_isfunction(L, idx + 2))
+ {
+ luaL_error(L, "\"BindCommand\" function expects a function as its 3rd parameter. Command-binding aborted.");
+ return 0;
+ }
+ cPluginManager * self = cPluginManager::Get();
+ AString Command (tolua_tocppstring(L, idx, ""));
+ AString Permission(tolua_tocppstring(L, idx + 1, ""));
+ AString HelpString(tolua_tocppstring(L, idx + 3, ""));
+
+ // Store the function reference:
+ lua_pop(L, 1); // Pop the help string off the stack
+ int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference
+ if (FnRef == LUA_REFNIL)
+ {
+ LOGERROR("\"BindCommand\": Cannot create a function reference. Command \"%s\" not bound.", Command.c_str());
+ return 0;
+ }
+
+ if (!self->BindCommand(Command, Plugin, Permission, HelpString))
+ {
+ // Refused. Possibly already bound. Error message has been given, display the callstack:
+ cLuaState LS(L);
+ LS.LogStackTrace();
+ return 0;
+ }
+
+ Plugin->BindCommand(Command, FnRef);
+ return 0;
+}
+
+
+
+
+
+static int tolua_cPluginManager_BindConsoleCommand(lua_State * L)
+{
+ /* Function signatures:
+ cPluginManager:BindConsoleCommand(Command, Function, HelpString)
+ cPluginManager.BindConsoleCommand(Command, Function, HelpString) -- without the "self" param
+ */
+
+ // Get the plugin identification out of LuaState:
+ cPluginLua * Plugin = GetLuaPlugin(L);
+ if (Plugin == NULL)
+ {
+ return 0;
+ }
+
+ // Read the arguments to this API call:
+ tolua_Error tolua_err;
+ int idx = 1;
+ if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err))
+ {
+ idx++;
+ }
+ if (
+ !tolua_iscppstring(L, idx, 0, &tolua_err) || // Command
+ !tolua_iscppstring(L, idx + 2, 0, &tolua_err) || // HelpString
+ !tolua_isnoobj (L, idx + 3, &tolua_err)
+ )
+ {
+ tolua_error(L, "#ferror in function 'BindConsoleCommand'.", &tolua_err);
+ return 0;
+ }
+ if (!lua_isfunction(L, idx + 1))
+ {
+ luaL_error(L, "\"BindConsoleCommand\" function expects a function as its 2nd parameter. Command-binding aborted.");
+ return 0;
+ }
+ cPluginManager * self = cPluginManager::Get();
+ AString Command (tolua_tocppstring(L, idx, ""));
+ AString HelpString(tolua_tocppstring(L, idx + 2, ""));
+
+ // Store the function reference:
+ lua_pop(L, 1); // Pop the help string off the stack
+ int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference
+ if (FnRef == LUA_REFNIL)
+ {
+ LOGERROR("\"BindConsoleCommand\": Cannot create a function reference. Console Command \"%s\" not bound.", Command.c_str());
+ return 0;
+ }
+
+ if (!self->BindConsoleCommand(Command, Plugin, HelpString))
+ {
+ // Refused. Possibly already bound. Error message has been given, display the callstack:
+ cLuaState LS(L);
+ LS.LogStackTrace();
+ return 0;
+ }
+
+ Plugin->BindConsoleCommand(Command, FnRef);
+ return 0;
+}
+
+
+
+
+
+static int tolua_cPlayer_GetGroups(lua_State* tolua_S)
+{
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+
+ const cPlayer::GroupList & AllGroups = self->GetGroups();
+
+ lua_createtable(tolua_S, AllGroups.size(), 0);
+ int newTable = lua_gettop(tolua_S);
+ int index = 1;
+ cPlayer::GroupList::const_iterator iter = AllGroups.begin();
+ while(iter != AllGroups.end())
+ {
+ const cGroup* Group = *iter;
+ tolua_pushusertype( tolua_S, (void*)Group, "const cGroup" );
+ lua_rawseti(tolua_S, newTable, index);
+ ++iter;
+ ++index;
+ }
+ return 1;
+}
+
+
+
+
+
+static int tolua_cPlayer_GetResolvedPermissions(lua_State* tolua_S)
+{
+ cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+
+ cPlayer::StringList AllPermissions = self->GetResolvedPermissions();
+
+ lua_createtable(tolua_S, AllPermissions.size(), 0);
+ int newTable = lua_gettop(tolua_S);
+ int index = 1;
+ cPlayer::StringList::iterator iter = AllPermissions.begin();
+ while(iter != AllPermissions.end())
+ {
+ std::string& Permission = *iter;
+ tolua_pushstring( tolua_S, Permission.c_str() );
+ lua_rawseti(tolua_S, newTable, index);
+ ++iter;
+ ++index;
+ }
+ return 1;
+}
+
+
+
+
+
+static int tolua_cPlayer_OpenWindow(lua_State * tolua_S)
+{
+ // Function signature: cPlayer:OpenWindow(Window)
+
+ // Retrieve the plugin instance from the Lua state
+ cPluginLua * Plugin = GetLuaPlugin(tolua_S);
+ if (Plugin == NULL)
+ {
+ return 0;
+ }
+
+ // Get the parameters:
+ cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL);
+ cWindow * wnd = (cWindow *)tolua_tousertype(tolua_S, 2, NULL);
+ if ((self == NULL) || (wnd == NULL))
+ {
+ LOGWARNING("%s: invalid self (%p) or wnd (%p)", __FUNCTION__, self, wnd);
+ return 0;
+ }
+
+ // If cLuaWindow, add a reference, so that Lua won't delete the cLuaWindow object mid-processing
+ tolua_Error err;
+ if (tolua_isusertype(tolua_S, 2, "cLuaWindow", 0, &err))
+ {
+ cLuaWindow * LuaWnd = (cLuaWindow *)wnd;
+ // Only if not already referenced
+ if (!LuaWnd->IsLuaReferenced())
+ {
+ int LuaRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (LuaRef == LUA_REFNIL)
+ {
+ LOGWARNING("%s: Cannot create a window reference. Cannot open window \"%s\".",
+ __FUNCTION__, wnd->GetWindowTitle().c_str()
+ );
+ return 0;
+ }
+ LuaWnd->SetLuaRef(Plugin, LuaRef);
+ }
+ }
+
+ // Open the window
+ self->OpenWindow(wnd);
+ return 0;
+}
+
+
+
+
+
+template <
+ class OBJTYPE,
+ void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef)
+>
+static int tolua_SetObjectCallback(lua_State * tolua_S)
+{
+ // Function signature: OBJTYPE:SetWhateverCallback(CallbackFunction)
+
+ // Retrieve the plugin instance from the Lua state
+ cPluginLua * Plugin = GetLuaPlugin(tolua_S);
+ if (Plugin == NULL)
+ {
+ // Warning message has already been printed by GetLuaPlugin(), bail out silently
+ return 0;
+ }
+
+ // Get the parameters - self and the function reference:
+ OBJTYPE * self = (OBJTYPE *)tolua_tousertype(tolua_S, 1, NULL);
+ if (self == NULL)
+ {
+ LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self);
+ return 0;
+ }
+ int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); // Store function reference for later retrieval
+ if (FnRef == LUA_REFNIL)
+ {
+ LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__);
+ return 0;
+ }
+
+ // Set the callback
+ (self->*SetCallback)(Plugin, FnRef);
+ return 0;
+}
+
+
+
+
+
+static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S)
+{
+ cPluginLua * self = (cPluginLua *)tolua_tousertype(tolua_S,1,0);
+
+ tolua_Error tolua_err;
+ tolua_err.array = 0;
+ tolua_err.index = 3;
+ tolua_err.type = "function";
+
+ std::string Title = "";
+ int Reference = LUA_REFNIL;
+
+ if (
+ tolua_isstring(tolua_S, 2, 0, &tolua_err ) &&
+ lua_isfunction(tolua_S, 3 )
+ )
+ {
+ Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ Title = ((std::string) tolua_tocppstring(tolua_S,2,0));
+ }
+ else
+ {
+ return tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err);
+ }
+
+ if( Reference != LUA_REFNIL )
+ {
+ if( !self->AddWebTab( Title.c_str(), tolua_S, Reference ) )
+ {
+ luaL_unref( tolua_S, LUA_REGISTRYINDEX, Reference );
+ }
+ }
+ else
+ {
+ LOGERROR("ERROR: cPluginLua:AddWebTab invalid function reference in 2nd argument (Title: \"%s\")", Title.c_str() );
+ }
+
+ return 0;
+}
+
+
+
+
+
+static int tolua_cPluginLua_AddTab(lua_State* tolua_S)
+{
+ cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0);
+ LOGWARN("WARNING: Using deprecated function AddTab()! Use AddWebTab() instead. (plugin \"%s\" in folder \"%s\")",
+ self->GetName().c_str(), self->GetDirectory().c_str()
+ );
+ return tolua_cPluginLua_AddWebTab( tolua_S );
+}
+
+
+
+
+// Perhaps use this as well for copying tables https://github.com/keplerproject/rings/pull/1
+static int copy_lua_values(lua_State * a_Source, lua_State * a_Destination, int i, int top)
+{
+ for(; i <= top; ++i )
+ {
+ int t = lua_type(a_Source, i);
+ switch (t) {
+ case LUA_TSTRING: /* strings */
+ {
+ const char * s = lua_tostring(a_Source, i);
+ LOGD("%i push string: %s", i, s);
+ tolua_pushstring(a_Destination, s);
+ }
+ break;
+ case LUA_TBOOLEAN: /* booleans */
+ {
+ int b = tolua_toboolean(a_Source, i, false);
+ LOGD("%i push bool: %i", i, b);
+ tolua_pushboolean(a_Destination, b );
+ }
+ break;
+ case LUA_TNUMBER: /* numbers */
+ {
+ lua_Number d = tolua_tonumber(a_Source, i, 0);
+ LOGD("%i push number: %0.2f", i, d);
+ tolua_pushnumber(a_Destination, d );
+ }
+ break;
+ case LUA_TUSERDATA:
+ {
+ const char * type = 0;
+ if (lua_getmetatable(a_Source,i))
+ {
+ lua_rawget(a_Source, LUA_REGISTRYINDEX);
+ type = lua_tostring(a_Source, -1);
+ lua_pop(a_Source, 1); // Pop.. something?! I don't knooow~~ T_T
+ }
+
+ // don't need tolua_tousertype we already have the type
+ void * ud = tolua_touserdata(a_Source, i, 0);
+ LOGD("%i push usertype: %p of type '%s'", i, ud, type);
+ if( type == 0 )
+ {
+ LOGERROR("Call(): Something went wrong when trying to get usertype name!");
+ return 0;
+ }
+ tolua_pushusertype(a_Destination, ud, type);
+ }
+ break;
+ default: /* other values */
+ LOGERROR("Call(): Unsupported value: '%s'. Can only use numbers and strings!", lua_typename(a_Source, t));
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+
+
+
+static int tolua_cPlugin_Call(lua_State* tolua_S)
+{
+ cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0);
+ lua_State* targetState = self->GetLuaState();
+ int targetTop = lua_gettop(targetState);
+
+ int top = lua_gettop(tolua_S);
+ LOGD("total in stack: %i", top );
+
+ std::string funcName = tolua_tostring(tolua_S, 2, "");
+ LOGD("Func name: %s", funcName.c_str() );
+
+ lua_getglobal(targetState, funcName.c_str());
+ if(!lua_isfunction(targetState,-1))
+ {
+ LOGWARN("Error could not find function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() );
+ lua_pop(targetState,1);
+ return 0;
+ }
+
+ if( copy_lua_values(tolua_S, targetState, 3, top) == 0 ) // Start at 3 because 1 and 2 are the plugin and function name respectively
+ {
+ // something went wrong, exit
+ return 0;
+ }
+
+ int s = lua_pcall(targetState, top - 2, LUA_MULTRET, 0);
+ if (cLuaState::ReportErrors(targetState, s))
+ {
+ LOGWARN("Error while calling function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() );
+ return 0;
+ }
+
+ int nresults = lua_gettop(targetState) - targetTop;
+ LOGD("num results: %i", nresults);
+ int ttop = lua_gettop(targetState);
+ if( copy_lua_values(targetState, tolua_S, targetTop+1, ttop) == 0 ) // Start at targetTop+1 and I have no idea why xD
+ {
+ // something went wrong, exit
+ return 0;
+ }
+
+ lua_pop(targetState, nresults); // I have no idea what I'm doing, but it works
+
+ return nresults;
+}
+
+
+
+
+
+static int tolua_md5(lua_State* tolua_S)
+{
+ std::string SourceString = tolua_tostring(tolua_S, 1, 0);
+ std::string CryptedString = md5( SourceString );
+ tolua_pushstring( tolua_S, CryptedString.c_str() );
+ return 1;
+}
+
+
+
+
+
+static int tolua_push_StringStringMap(lua_State* tolua_S, std::map< std::string, std::string >& a_StringStringMap )
+{
+ lua_newtable(tolua_S);
+ int top = lua_gettop(tolua_S);
+
+ for( std::map< std::string, std::string >::iterator it = a_StringStringMap.begin(); it != a_StringStringMap.end(); ++it )
+ {
+ const char* key = it->first.c_str();
+ const char* value = it->second.c_str();
+ lua_pushstring(tolua_S, key);
+ lua_pushstring(tolua_S, value);
+ lua_settable(tolua_S, top);
+ }
+
+ return 1;
+}
+
+
+
+
+
+static int tolua_get_HTTPRequest_Params(lua_State* tolua_S)
+{
+ HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0);
+ return tolua_push_StringStringMap(tolua_S, self->Params);
+}
+
+
+
+
+
+static int tolua_get_HTTPRequest_PostParams(lua_State* tolua_S)
+{
+ HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0);
+ return tolua_push_StringStringMap(tolua_S, self->PostParams);
+}
+
+
+
+
+
+static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S)
+{
+ HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0);
+ std::map< std::string, HTTPFormData >& FormData = self->FormData;
+
+ lua_newtable(tolua_S);
+ int top = lua_gettop(tolua_S);
+
+ for( std::map< std::string, HTTPFormData >::iterator it = FormData.begin(); it != FormData.end(); ++it )
+ {
+ lua_pushstring(tolua_S, it->first.c_str() );
+ tolua_pushusertype(tolua_S, &(it->second), "HTTPFormData" );
+ //lua_pushlstring(tolua_S, it->second.Value.c_str(), it->second.Value.size() ); // Might contain binary data
+ lua_settable(tolua_S, top);
+ }
+
+ return 1;
+}
+
+
+
+
+
+static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S)
+{
+ cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
+
+ const cWebAdmin::PluginList & AllPlugins = self->GetPlugins();
+
+ lua_createtable(tolua_S, AllPlugins.size(), 0);
+ int newTable = lua_gettop(tolua_S);
+ int index = 1;
+ cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin();
+ while(iter != AllPlugins.end())
+ {
+ const cWebPlugin* Plugin = *iter;
+ tolua_pushusertype( tolua_S, (void*)Plugin, "const cWebPlugin" );
+ lua_rawseti(tolua_S, newTable, index);
+ ++iter;
+ ++index;
+ }
+ return 1;
+}
+
+
+
+
+
+static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S)
+{
+ cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S,1,0);
+
+ const cWebPlugin::TabNameList & TabNames = self->GetTabNames();
+
+ lua_newtable(tolua_S);
+ int newTable = lua_gettop(tolua_S);
+ int index = 1;
+ cWebPlugin::TabNameList::const_iterator iter = TabNames.begin();
+ while(iter != TabNames.end())
+ {
+ const AString & FancyName = iter->first;
+ const AString & WebName = iter->second;
+ tolua_pushstring( tolua_S, WebName.c_str() ); // Because the WebName is supposed to be unique, use it as key
+ tolua_pushstring( tolua_S, FancyName.c_str() );
+ //
+ lua_rawset(tolua_S, -3);
+ ++iter;
+ ++index;
+ }
+ return 1;
+}
+
+
+
+
+
+static int Lua_ItemGrid_GetSlotCoords(lua_State * L)
+{
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(L, 1, "const cItemGrid", 0, &tolua_err) ||
+ !tolua_isnumber (L, 2, 0, &tolua_err) ||
+ !tolua_isnoobj (L, 3, &tolua_err)
+ )
+ {
+ goto tolua_lerror;
+ }
+
+ {
+ const cItemGrid * self = (const cItemGrid *)tolua_tousertype(L, 1, 0);
+ int SlotNum = (int)tolua_tonumber(L, 2, 0);
+ if (self == NULL)
+ {
+ tolua_error(L, "invalid 'self' in function 'cItemGrid:GetSlotCoords'", NULL);
+ return 0;
+ }
+ int X, Y;
+ self->GetSlotCoords(SlotNum, X, Y);
+ tolua_pushnumber(L, (lua_Number)X);
+ tolua_pushnumber(L, (lua_Number)Y);
+ return 2;
+ }
+
+tolua_lerror:
+ tolua_error(L, "#ferror in function 'cItemGrid:GetSlotCoords'.", &tolua_err);
+ return 0;
+}
+
+
+
+
+
+/// Provides interface between a Lua table of callbacks and the cBlockTracer::cCallbacks
+class cLuaBlockTracerCallbacks :
+ public cBlockTracer::cCallbacks
+{
+public:
+ cLuaBlockTracerCallbacks(cLuaState & a_LuaState, int a_ParamNum) :
+ m_LuaState(a_LuaState),
+ m_TableRef(a_LuaState, a_ParamNum)
+ {
+ }
+
+ virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override
+ {
+ if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlock"))
+ {
+ // No such function in the table, skip the callback
+ return false;
+ }
+ m_LuaState.Push(a_BlockX);
+ m_LuaState.Push(a_BlockY);
+ m_LuaState.Push(a_BlockZ);
+ m_LuaState.Push(a_BlockType);
+ m_LuaState.Push(a_BlockMeta);
+ m_LuaState.Push(a_EntryFace);
+ if (!m_LuaState.CallFunction(1))
+ {
+ return false;
+ }
+ bool res = false;
+ if (lua_isboolean(m_LuaState, -1))
+ {
+ res = (lua_toboolean(m_LuaState, -1) != 0);
+ }
+ lua_pop(m_LuaState, 1);
+ return res;
+ }
+
+ virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) override
+ {
+ if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlockNoData"))
+ {
+ // No such function in the table, skip the callback
+ return false;
+ }
+ m_LuaState.Push(a_BlockX);
+ m_LuaState.Push(a_BlockY);
+ m_LuaState.Push(a_BlockZ);
+ m_LuaState.Push(a_EntryFace);
+ if (!m_LuaState.CallFunction(1))
+ {
+ return false;
+ }
+ bool res = false;
+ if (lua_isboolean(m_LuaState, -1))
+ {
+ res = (lua_toboolean(m_LuaState, -1) != 0);
+ }
+ lua_pop(m_LuaState, 1);
+ return res;
+ }
+
+ virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override
+ {
+ if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnOutOfWorld"))
+ {
+ // No such function in the table, skip the callback
+ return false;
+ }
+ m_LuaState.Push(a_BlockX);
+ m_LuaState.Push(a_BlockY);
+ m_LuaState.Push(a_BlockZ);
+ if (!m_LuaState.CallFunction(1))
+ {
+ return false;
+ }
+ bool res = false;
+ if (lua_isboolean(m_LuaState, -1))
+ {
+ res = (lua_toboolean(m_LuaState, -1) != 0);
+ }
+ lua_pop(m_LuaState, 1);
+ return res;
+ }
+
+ virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override
+ {
+ if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnIntoWorld"))
+ {
+ // No such function in the table, skip the callback
+ return false;
+ }
+ m_LuaState.Push(a_BlockX);
+ m_LuaState.Push(a_BlockY);
+ m_LuaState.Push(a_BlockZ);
+ if (!m_LuaState.CallFunction(1))
+ {
+ return false;
+ }
+ bool res = false;
+ if (lua_isboolean(m_LuaState, -1))
+ {
+ res = (lua_toboolean(m_LuaState, -1) != 0);
+ }
+ lua_pop(m_LuaState, 1);
+ return res;
+ }
+
+ virtual void OnNoMoreHits(void) override
+ {
+ if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoMoreHits"))
+ {
+ // No such function in the table, skip the callback
+ return;
+ }
+ m_LuaState.CallFunction(0);
+ }
+
+ virtual void OnNoChunk(void) override
+ {
+ if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoChunk"))
+ {
+ // No such function in the table, skip the callback
+ return;
+ }
+ m_LuaState.CallFunction(0);
+ }
+
+protected:
+ cLuaState & m_LuaState;
+ cLuaState::cRef m_TableRef;
+} ;
+
+
+
+
+
+static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S)
+{
+ // cLineBlockTracer.Trace(World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ)
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamTable (2) ||
+ !L.CheckParamNumber (3, 8) ||
+ !L.CheckParamEnd (9)
+ )
+ {
+ return 0;
+ }
+
+ cWorld * World = (cWorld *)tolua_tousertype(L, 1, NULL);
+ cLuaBlockTracerCallbacks Callbacks(L, 2);
+ double StartX = tolua_tonumber(L, 3, 0);
+ double StartY = tolua_tonumber(L, 4, 0);
+ double StartZ = tolua_tonumber(L, 5, 0);
+ double EndX = tolua_tonumber(L, 6, 0);
+ double EndY = tolua_tonumber(L, 7, 0);
+ double EndZ = tolua_tonumber(L, 8, 0);
+ bool res = cLineBlockTracer::Trace(*World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ);
+ tolua_pushboolean(L, res ? 1 : 0);
+ return 1;
+}
+
+
+
+
+
+static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S)
+{
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cRoot") ||
+ !L.CheckParamUserType (2, "const cItem") ||
+ !L.CheckParamEnd (3)
+ )
+ {
+ return 0;
+ }
+
+ // Check the input param:
+ cItem * Input = (cItem *)tolua_tousertype(L, 2, NULL);
+ if (Input == NULL)
+ {
+ LOGWARNING("cRoot:GetFurnaceRecipe: the Input parameter is nil or missing.");
+ return 0;
+ }
+
+ // Get the recipe for the input
+ cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe();
+ const cFurnaceRecipe::Recipe * Recipe = FR->GetRecipeFrom(*Input);
+ if (Recipe == NULL)
+ {
+ // There is no such furnace recipe for this input, return no value
+ return 0;
+ }
+
+ // Push the output, number of ticks and input as the three return values:
+ tolua_pushusertype(L, Recipe->Out, "const cItem");
+ tolua_pushnumber (L, (lua_Number)(Recipe->CookTime));
+ tolua_pushusertype(L, Recipe->In, "const cItem");
+ return 3;
+}
+
+
+
+
+
+static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S)
+{
+ // function cHopperEntity::GetOutputBlockPos()
+ // Exported manually because tolua would require meaningless params
+
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cHopperEntity") ||
+ !L.CheckParamNumber (2) ||
+ !L.CheckParamEnd (3)
+ )
+ {
+ return 0;
+ }
+ cHopperEntity * self = (cHopperEntity *)tolua_tousertype(tolua_S, 1, 0);
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cHopperEntity::GetOutputBlockPos()'", NULL);
+ return 0;
+ }
+
+ NIBBLETYPE a_BlockMeta = ((NIBBLETYPE)tolua_tonumber(tolua_S, 2, 0));
+ int a_OutputX, a_OutputY, a_OutputZ;
+ bool res = self->GetOutputBlockPos(a_BlockMeta, a_OutputX, a_OutputY, a_OutputZ);
+ tolua_pushboolean(tolua_S, res);
+ if (res)
+ {
+ tolua_pushnumber(tolua_S, (lua_Number)a_OutputX);
+ tolua_pushnumber(tolua_S, (lua_Number)a_OutputY);
+ tolua_pushnumber(tolua_S, (lua_Number)a_OutputZ);
+ return 4;
+ }
+ return 1;
+}
+
+
+
+
+
+void ManualBindings::Bind(lua_State * tolua_S)
+{
+ tolua_beginmodule(tolua_S, NULL);
+ tolua_function(tolua_S, "StringSplit", tolua_StringSplit);
+ tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim);
+ tolua_function(tolua_S, "LOG", tolua_LOG);
+ tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO);
+ tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN);
+ tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN);
+ tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR);
+
+ tolua_beginmodule(tolua_S, "cFile");
+ tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cHopperEntity");
+ tolua_function(tolua_S, "GetOutputBlockPos", tolua_cHopperEntity_GetOutputBlockPos);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cLineBlockTracer");
+ tolua_function(tolua_S, "Trace", tolua_cLineBlockTracer_Trace);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cRoot");
+ tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith <cRoot, cPlayer, &cRoot::FindAndDoWithPlayer>);
+ tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>);
+ tolua_function(tolua_S, "ForEachWorld", tolua_ForEach<cRoot, cWorld, &cRoot::ForEachWorld>);
+ tolua_function(tolua_S, "GetFurnaceRecipe", tolua_cRoot_GetFurnaceRecipe);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cWorld");
+ tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
+ tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
+ tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
+ tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
+ tolua_function(tolua_S, "DoWithDropperAt", tolua_DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>);
+ tolua_function(tolua_S, "DoWithEntityByID", tolua_DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>);
+ tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
+ tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
+ tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
+ tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
+ tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
+ tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
+ tolua_function(tolua_S, "ForEachEntityInChunk", tolua_ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>);
+ tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>);
+ tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>);
+ tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo);
+ tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta);
+ tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines);
+ tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask);
+ tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines);
+ tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight);
+ tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cPlugin");
+ tolua_function(tolua_S, "Call", tolua_cPlugin_Call);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cPluginManager");
+ tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook);
+ tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand);
+ tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand);
+ tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand);
+ tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand);
+ tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cPlayer");
+ tolua_function(tolua_S, "GetGroups", tolua_cPlayer_GetGroups);
+ tolua_function(tolua_S, "GetResolvedPermissions", tolua_cPlayer_GetResolvedPermissions);
+ tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cLuaWindow");
+ tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClosing>);
+ tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnSlotChanged>);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cPluginLua");
+ tolua_function(tolua_S, "AddTab", tolua_cPluginLua_AddTab);
+ tolua_function(tolua_S, "AddWebTab", tolua_cPluginLua_AddWebTab);
+ tolua_endmodule(tolua_S);
+
+ tolua_cclass(tolua_S,"HTTPRequest","HTTPRequest","",NULL);
+ tolua_beginmodule(tolua_S,"HTTPRequest");
+ // tolua_variable(tolua_S,"Method",tolua_get_HTTPRequest_Method,tolua_set_HTTPRequest_Method);
+ // tolua_variable(tolua_S,"Path",tolua_get_HTTPRequest_Path,tolua_set_HTTPRequest_Path);
+ tolua_variable(tolua_S,"FormData",tolua_get_HTTPRequest_FormData,0);
+ tolua_variable(tolua_S,"Params",tolua_get_HTTPRequest_Params,0);
+ tolua_variable(tolua_S,"PostParams",tolua_get_HTTPRequest_PostParams,0);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cWebAdmin");
+ tolua_function(tolua_S, "GetPlugins", tolua_cWebAdmin_GetPlugins);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cWebPlugin");
+ tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cClientHandle");
+ tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE);
+ tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cItemGrid");
+ tolua_function(tolua_S, "GetSlotCoords", Lua_ItemGrid_GetSlotCoords);
+ tolua_endmodule(tolua_S);
+
+ tolua_function(tolua_S, "md5", tolua_md5);
+
+ tolua_endmodule(tolua_S);
+}
+
+
+
+
diff --git a/src/ManualBindings.h b/src/ManualBindings.h
new file mode 100644
index 000000000..e6594947e
--- /dev/null
+++ b/src/ManualBindings.h
@@ -0,0 +1,8 @@
+#pragma once
+
+struct lua_State;
+class ManualBindings
+{
+public:
+ static void Bind( lua_State* tolua_S );
+}; \ No newline at end of file
diff --git a/src/Matrix4f.cpp b/src/Matrix4f.cpp
new file mode 100644
index 000000000..d0a407a99
--- /dev/null
+++ b/src/Matrix4f.cpp
@@ -0,0 +1,4 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+// _X: empty file??
diff --git a/src/Matrix4f.h b/src/Matrix4f.h
new file mode 100644
index 000000000..249c92f5f
--- /dev/null
+++ b/src/Matrix4f.h
@@ -0,0 +1,225 @@
+#pragma once
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include "Vector3f.h"
+
+class Matrix4f
+{
+public:
+ enum
+ {
+ TX=3,
+ TY=7,
+ TZ=11,
+ D0=0, D1=5, D2=10, D3=15,
+ SX=D0, SY=D1, SZ=D2,
+ W=D3
+ };
+ Matrix4f() { Identity(); }
+ float& operator [] ( int a_N ) { return cell[a_N]; }
+ void Identity()
+ {
+ cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] =
+ cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0;
+ cell[D0] = cell[D1] = cell[D2] = cell[W] = 1;
+ }
+ void Init( Vector3f a_Pos, float a_RX, float a_RY, float a_RZ )
+ {
+ Matrix4f t;
+ t.RotateX( a_RZ );
+ RotateY( a_RY );
+ Concatenate( t );
+ t.RotateZ( a_RX );
+ Concatenate( t );
+ Translate( a_Pos );
+ }
+ void RotateX( float a_RX )
+ {
+ float sx = (float)sin( a_RX * M_PI / 180 );
+ float cx = (float)cos( a_RX * M_PI / 180 );
+ Identity();
+ cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx;
+ }
+ void RotateY( float a_RY )
+ {
+ float sy = (float)sin( a_RY * M_PI / 180 );
+ float cy = (float)cos( a_RY * M_PI / 180 );
+ Identity ();
+ cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy;
+ }
+ void RotateZ( float a_RZ )
+ {
+ float sz = (float)sin( a_RZ * M_PI / 180 );
+ float cz = (float)cos( a_RZ * M_PI / 180 );
+ Identity ();
+ cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz;
+ }
+ void Translate( Vector3f a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; }
+ void SetTranslation( Vector3f a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; }
+ void Concatenate( const Matrix4f& m2 )
+ {
+ Matrix4f res;
+ int c;
+ for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ )
+ res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] +
+ cell[r * 4 + 1] * m2.cell[c + 4] +
+ cell[r * 4 + 2] * m2.cell[c + 8] +
+ cell[r * 4 + 3] * m2.cell[c + 12];
+ for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c];
+ }
+ Vector3f Transform( const Vector3f& v ) const
+ {
+ float x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3];
+ float y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7];
+ float z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11];
+ return Vector3f( x, y, z );
+ }
+ void Invert()
+ {
+ Matrix4f t;
+ int h, i;
+ float tx = -cell[3], ty = -cell[7], tz = -cell[11];
+ for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4];
+ for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i];
+ cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2];
+ cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6];
+ cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10];
+ }
+ Vector3f GetXColumn() { return Vector3f( cell[0], cell[1], cell[2] ); }
+ Vector3f GetYColumn() { return Vector3f( cell[4], cell[5], cell[6] ); }
+ Vector3f GetZColumn() { return Vector3f( cell[8], cell[9], cell[10] ); }
+ void SetXColumn( const Vector3f & a_X )
+ {
+ cell[0] = a_X.x;
+ cell[1] = a_X.y;
+ cell[2] = a_X.z;
+ }
+ void SetYColumn( const Vector3f & a_Y )
+ {
+ cell[4] = a_Y.x;
+ cell[5] = a_Y.y;
+ cell[6] = a_Y.z;
+ }
+ void SetZColumn( const Vector3f & a_Z )
+ {
+ cell[8] = a_Z.x;
+ cell[9] = a_Z.y;
+ cell[10] = a_Z.z;
+ }
+ float cell[16];
+};
+
+
+
+
+
+class Matrix4d
+{
+public:
+ enum
+ {
+ TX=3,
+ TY=7,
+ TZ=11,
+ D0=0, D1=5, D2=10, D3=15,
+ SX=D0, SY=D1, SZ=D2,
+ W=D3
+ };
+ Matrix4d() { Identity(); }
+ double& operator [] ( int a_N ) { return cell[a_N]; }
+ void Identity()
+ {
+ cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] =
+ cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0;
+ cell[D0] = cell[D1] = cell[D2] = cell[W] = 1;
+ }
+ void Init( Vector3f a_Pos, double a_RX, double a_RY, double a_RZ )
+ {
+ Matrix4d t;
+ t.RotateX( a_RZ );
+ RotateY( a_RY );
+ Concatenate( t );
+ t.RotateZ( a_RX );
+ Concatenate( t );
+ Translate( a_Pos );
+ }
+ void RotateX( double a_RX )
+ {
+ double sx = (double)sin( a_RX * M_PI / 180 );
+ double cx = (double)cos( a_RX * M_PI / 180 );
+ Identity();
+ cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx;
+ }
+ void RotateY( double a_RY )
+ {
+ double sy = (double)sin( a_RY * M_PI / 180 );
+ double cy = (double)cos( a_RY * M_PI / 180 );
+ Identity ();
+ cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy;
+ }
+ void RotateZ( double a_RZ )
+ {
+ double sz = (double)sin( a_RZ * M_PI / 180 );
+ double cz = (double)cos( a_RZ * M_PI / 180 );
+ Identity ();
+ cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz;
+ }
+ void Translate( Vector3d a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; }
+ void SetTranslation( Vector3d a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; }
+ void Concatenate( const Matrix4d & m2 )
+ {
+ Matrix4d res;
+ int c;
+ for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ )
+ res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] +
+ cell[r * 4 + 1] * m2.cell[c + 4] +
+ cell[r * 4 + 2] * m2.cell[c + 8] +
+ cell[r * 4 + 3] * m2.cell[c + 12];
+ for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c];
+ }
+ Vector3d Transform( const Vector3d & v ) const
+ {
+ double x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3];
+ double y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7];
+ double z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11];
+ return Vector3d( x, y, z );
+ }
+ void Invert()
+ {
+ Matrix4d t;
+ int h, i;
+ double tx = -cell[3], ty = -cell[7], tz = -cell[11];
+ for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4];
+ for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i];
+ cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2];
+ cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6];
+ cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10];
+ }
+ Vector3d GetXColumn() { return Vector3d( cell[0], cell[1], cell[2] ); }
+ Vector3d GetYColumn() { return Vector3d( cell[4], cell[5], cell[6] ); }
+ Vector3d GetZColumn() { return Vector3d( cell[8], cell[9], cell[10] ); }
+ void SetXColumn( const Vector3d & a_X )
+ {
+ cell[0] = a_X.x;
+ cell[1] = a_X.y;
+ cell[2] = a_X.z;
+ }
+ void SetYColumn( const Vector3d & a_Y )
+ {
+ cell[4] = a_Y.x;
+ cell[5] = a_Y.y;
+ cell[6] = a_Y.z;
+ }
+ void SetZColumn( const Vector3d & a_Z )
+ {
+ cell[8] = a_Z.x;
+ cell[9] = a_Z.y;
+ cell[10] = a_Z.z;
+ }
+ double cell[16];
+} ;
+
+
+
+
diff --git a/src/MemoryLeak.h b/src/MemoryLeak.h
new file mode 100644
index 000000000..e9c0c34e3
--- /dev/null
+++ b/src/MemoryLeak.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#ifdef _WIN32
+ #ifdef _DEBUG
+ // Enable the CRT debugging features:
+ #define _CRTDBG_MAP_ALLOC
+ #include <stdlib.h>
+ #include <crtdbg.h>
+
+ // This works only in MSVC 2010+:
+ #if _MSC_VER >= 1600
+ // Map the new operator
+ #ifndef DEBUG_NEW
+ #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
+ #define new DEBUG_NEW
+ #endif // _CRTDBG_MAP_ALLOC
+ #endif // _MSC_VER
+ #endif // _DEBUG
+#endif // _WIN32
diff --git a/src/MersenneTwister.h b/src/MersenneTwister.h
new file mode 100644
index 000000000..dc7134a93
--- /dev/null
+++ b/src/MersenneTwister.h
@@ -0,0 +1,456 @@
+// MersenneTwister.h
+// Mersenne Twister random number generator -- a C++ class MTRand
+// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
+// Richard J. Wagner v1.1 28 September 2009 wagnerr@umich.edu
+
+// The Mersenne Twister is an algorithm for generating random numbers. It
+// was designed with consideration of the flaws in various other generators.
+// The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
+// are far greater. The generator is also fast; it avoids multiplication and
+// division, and it benefits from caches and pipelines. For more information
+// see the inventors' web page at
+// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+
+// Reference
+// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
+// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
+// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
+
+// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+// Copyright (C) 2000 - 2009, Richard J. Wagner
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. The names of its contributors may not be used to endorse or promote
+// products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef MERSENNETWISTER_H
+#define MERSENNETWISTER_H
+
+// Not thread safe (unless auto-initialization is avoided and each thread has
+// its own MTRand object)
+
+#include <iostream>
+#include <climits>
+#include <cstdio>
+#include <ctime>
+#include <cmath>
+
+class MTRand {
+// Data
+public:
+ typedef long uint32; // unsigned integer type, at least 32 bits
+
+ enum { N = 624 }; // length of state vector
+ enum { SAVE = N + 1 }; // length of array for save()
+
+protected:
+ enum { M = 397 }; // period parameter
+
+ uint32 state[N]; // internal state
+ uint32 *pNext; // next value to get from state
+ int left; // number of values left before reload needed
+
+// Methods
+public:
+ MTRand( const uint32 oneSeed ); // initialize with a simple uint32
+ MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or array
+ MTRand(); // auto-initialize with /dev/urandom or time() and clock()
+ MTRand( const MTRand& o ); // copy
+
+ // Do NOT use for CRYPTOGRAPHY without securely hashing several returned
+ // values together, otherwise the generator state can be learned after
+ // reading 624 consecutive values.
+
+ // Access to 32-bit random numbers
+ uint32 randInt(); // integer in [0,2^32-1]
+ uint32 randInt( const uint32 n ); // integer in [0,n] for n < 2^32
+ double rand(); // real number in [0,1]
+ double rand( const double n ); // real number in [0,n]
+ double randExc(); // real number in [0,1)
+ double randExc( const double n ); // real number in [0,n)
+ double randDblExc(); // real number in (0,1)
+ double randDblExc( const double n ); // real number in (0,n)
+ double operator()(); // same as rand()
+
+ // Access to 53-bit random numbers (capacity of IEEE double precision)
+ double rand53(); // real number in [0,1)
+
+ // Access to nonuniform random number distributions
+ double randNorm( const double mean = 0.0, const double stddev = 1.0 );
+
+ // Re-seeding functions with same behavior as initializers
+ void seed( const uint32 oneSeed );
+ void seed( uint32 *const bigSeed, const uint32 seedLength = N );
+ void seed();
+
+ // Saving and loading generator state
+ void save( uint32* saveArray ) const; // to array of size SAVE
+ void load( uint32 *const loadArray ); // from such array
+ friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand );
+ friend std::istream& operator>>( std::istream& is, MTRand& mtrand );
+ MTRand& operator=( const MTRand& o );
+
+protected:
+ void initialize( const uint32 oneSeed );
+ void reload();
+ uint32 hiBit( const uint32 u ) const { return u & 0x80000000UL; }
+ uint32 loBit( const uint32 u ) const { return u & 0x00000001UL; }
+ uint32 loBits( const uint32 u ) const { return u & 0x7fffffffUL; }
+ uint32 mixBits( const uint32 u, const uint32 v ) const
+ { return hiBit(u) | loBits(v); }
+ uint32 magic( const uint32 u ) const
+ { return loBit(u) ? 0x9908b0dfUL : 0x0UL; }
+ uint32 twist( const uint32 m, const uint32 s0, const uint32 s1 ) const
+ { return m ^ (mixBits(s0,s1)>>1) ^ magic(s1); }
+ static uint32 hash( time_t t, clock_t c );
+};
+
+// Functions are defined in order of usage to assist inlining
+
+inline MTRand::uint32 MTRand::hash( time_t t, clock_t c )
+{
+ // Get a uint32 from t and c
+ // Better than uint32(x) in case x is floating point in [0,1]
+ // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk)
+
+ static uint32 differ = 0; // guarantee time-based seeds will change
+
+ uint32 h1 = 0;
+ unsigned char *p = (unsigned char *) &t;
+ for( size_t i = 0; i < sizeof(t); ++i )
+ {
+ h1 *= UCHAR_MAX + 2U;
+ h1 += p[i];
+ }
+ uint32 h2 = 0;
+ p = (unsigned char *) &c;
+ for( size_t j = 0; j < sizeof(c); ++j )
+ {
+ h2 *= UCHAR_MAX + 2U;
+ h2 += p[j];
+ }
+ return ( h1 + differ++ ) ^ h2;
+}
+
+inline void MTRand::initialize( const uint32 seed )
+{
+ // Initialize generator state with seed
+ // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
+ // In previous versions, most significant bits (MSBs) of the seed affect
+ // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto.
+ register uint32 *s = state;
+ register uint32 *r = state;
+ register int i = 1;
+ *s++ = seed & 0xffffffffUL;
+ for( ; i < N; ++i )
+ {
+ *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL;
+ r++;
+ }
+}
+
+inline void MTRand::reload()
+{
+ // Generate N new values in state
+ // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com)
+ static const int MmN = int(M) - int(N); // in case enums are unsigned
+ register uint32 *p = state;
+ register int i;
+ for( i = N - M; i--; ++p )
+ *p = twist( p[M], p[0], p[1] );
+ for( i = M; --i; ++p )
+ *p = twist( p[MmN], p[0], p[1] );
+ *p = twist( p[MmN], p[0], state[0] );
+
+ left = N, pNext = state;
+}
+
+inline void MTRand::seed( const uint32 oneSeed )
+{
+ // Seed the generator with a simple uint32
+ initialize(oneSeed);
+ reload();
+}
+
+inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength )
+{
+ // Seed the generator with an array of uint32's
+ // There are 2^19937-1 possible initial states. This function allows
+ // all of those to be accessed by providing at least 19937 bits (with a
+ // default seed length of N = 624 uint32's). Any bits above the lower 32
+ // in each element are discarded.
+ // Just call seed() if you want to get array from /dev/urandom
+ initialize(19650218UL);
+ register int i = 1;
+ register uint32 j = 0;
+ register int k = ( N > seedLength ? N : seedLength );
+ for( ; k; --k )
+ {
+ state[i] =
+ state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL );
+ state[i] += ( bigSeed[j] & 0xffffffffUL ) + j;
+ state[i] &= 0xffffffffUL;
+ ++i; ++j;
+ if( i >= N ) { state[0] = state[N-1]; i = 1; }
+ if( j >= seedLength ) j = 0;
+ }
+ for( k = N - 1; k; --k )
+ {
+ state[i] =
+ state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL );
+ state[i] -= i;
+ state[i] &= 0xffffffffUL;
+ ++i;
+ if( i >= N ) { state[0] = state[N-1]; i = 1; }
+ }
+ state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array
+ reload();
+}
+
+inline void MTRand::seed()
+{
+ // Seed the generator with an array from /dev/urandom if available
+ // Otherwise use a hash of time() and clock() values
+
+ // First try getting an array from /dev/urandom
+
+ /* // Commented out by FakeTruth because doing this 200 times a tick is SUUUUPEERRR SLOW!!~~!ÕNe
+ FILE* urandom = fopen( "/dev/urandom", "rb" );
+ if( urandom )
+ {
+ uint32 bigSeed[N];
+ register uint32 *s = bigSeed;
+ register int i = N;
+ register bool success = true;
+ while( success && i-- )
+ success = fread( s++, sizeof(uint32), 1, urandom );
+ fclose(urandom);
+ if( success ) { seed( bigSeed, N ); return; }
+ }
+ */
+
+ // Was not successful, so use time() and clock() instead
+ seed( hash( time(NULL), clock() ) );
+}
+
+inline MTRand::MTRand( const uint32 oneSeed )
+ { seed(oneSeed); }
+
+inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength )
+ { seed(bigSeed,seedLength); }
+
+inline MTRand::MTRand()
+ { seed(); }
+
+inline MTRand::MTRand( const MTRand& o )
+{
+ register const uint32 *t = o.state;
+ register uint32 *s = state;
+ register int i = N;
+ for( ; i--; *s++ = *t++ ) {}
+ left = o.left;
+ pNext = &state[N-left];
+}
+
+inline MTRand::uint32 MTRand::randInt()
+{
+ // Pull a 32-bit integer from the generator state
+ // Every other access function simply transforms the numbers extracted here
+
+ if( left == 0 ) reload();
+ --left;
+
+ register uint32 s1;
+ s1 = *pNext++;
+ s1 ^= (s1 >> 11);
+ s1 ^= (s1 << 7) & 0x9d2c5680UL;
+ s1 ^= (s1 << 15) & 0xefc60000UL;
+ return ( s1 ^ (s1 >> 18) );
+}
+
+inline MTRand::uint32 MTRand::randInt( const uint32 n )
+{
+ // Find which bits are used in n
+ // Optimized by Magnus Jonsson (magnus@smartelectronix.com)
+ uint32 used = n;
+ used |= used >> 1;
+ used |= used >> 2;
+ used |= used >> 4;
+ used |= used >> 8;
+ used |= used >> 16;
+
+ // Draw numbers until one is found in [0,n]
+ uint32 i;
+ do
+ i = randInt() & used; // toss unused bits to shorten search
+ while( i > n );
+ return i;
+}
+
+inline double MTRand::rand()
+ { return double(randInt()) * (1.0/4294967295.0); }
+
+inline double MTRand::rand( const double n )
+ { return rand() * n; }
+
+inline double MTRand::randExc()
+ { return double(randInt()) * (1.0/4294967296.0); }
+
+inline double MTRand::randExc( const double n )
+ { return randExc() * n; }
+
+inline double MTRand::randDblExc()
+ { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); }
+
+inline double MTRand::randDblExc( const double n )
+ { return randDblExc() * n; }
+
+inline double MTRand::rand53()
+{
+ uint32 a = randInt() >> 5, b = randInt() >> 6;
+ return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada
+}
+
+inline double MTRand::randNorm( const double mean, const double stddev )
+{
+ // Return a real number from a normal (Gaussian) distribution with given
+ // mean and standard deviation by polar form of Box-Muller transformation
+ double x, y, r;
+ do
+ {
+ x = 2.0 * rand() - 1.0;
+ y = 2.0 * rand() - 1.0;
+ r = x * x + y * y;
+ }
+ while ( r >= 1.0 || r == 0.0 );
+ double s = sqrt( -2.0 * log(r) / r );
+ return mean + x * s * stddev;
+}
+
+inline double MTRand::operator()()
+{
+ return rand();
+}
+
+inline void MTRand::save( uint32* saveArray ) const
+{
+ register const uint32 *s = state;
+ register uint32 *sa = saveArray;
+ register int i = N;
+ for( ; i--; *sa++ = *s++ ) {}
+ *sa = left;
+}
+
+inline void MTRand::load( uint32 *const loadArray )
+{
+ register uint32 *s = state;
+ register uint32 *la = loadArray;
+ register int i = N;
+ for( ; i--; *s++ = *la++ ) {}
+ left = *la;
+ pNext = &state[N-left];
+}
+
+inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand )
+{
+ register const MTRand::uint32 *s = mtrand.state;
+ register int i = mtrand.N;
+ for( ; i--; os << *s++ << "\t" ) {}
+ return os << mtrand.left;
+}
+
+inline std::istream& operator>>( std::istream& is, MTRand& mtrand )
+{
+ register MTRand::uint32 *s = mtrand.state;
+ register int i = mtrand.N;
+ for( ; i--; is >> *s++ ) {}
+ is >> mtrand.left;
+ mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left];
+ return is;
+}
+
+inline MTRand& MTRand::operator=( const MTRand& o )
+{
+ if( this == &o ) return (*this);
+ register const uint32 *t = o.state;
+ register uint32 *s = state;
+ register int i = N;
+ for( ; i--; *s++ = *t++ ) {}
+ left = o.left;
+ pNext = &state[N-left];
+ return (*this);
+}
+
+#endif // MERSENNETWISTER_H
+
+// Change log:
+//
+// v0.1 - First release on 15 May 2000
+// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
+// - Translated from C to C++
+// - Made completely ANSI compliant
+// - Designed convenient interface for initialization, seeding, and
+// obtaining numbers in default or user-defined ranges
+// - Added automatic seeding from /dev/urandom or time() and clock()
+// - Provided functions for saving and loading generator state
+//
+// v0.2 - Fixed bug which reloaded generator one step too late
+//
+// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew
+//
+// v0.4 - Removed trailing newline in saved generator format to be consistent
+// with output format of built-in types
+//
+// v0.5 - Improved portability by replacing static const int's with enum's and
+// clarifying return values in seed(); suggested by Eric Heimburg
+// - Removed MAXINT constant; use 0xffffffffUL instead
+//
+// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits
+// - Changed integer [0,n] generator to give better uniformity
+//
+// v0.7 - Fixed operator precedence ambiguity in reload()
+// - Added access for real numbers in (0,1) and (0,n)
+//
+// v0.8 - Included time.h header to properly support time_t and clock_t
+//
+// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto
+// - Allowed for seeding with arrays of any length
+// - Added access for real numbers in [0,1) with 53-bit resolution
+// - Added access for real numbers from normal (Gaussian) distributions
+// - Increased overall speed by optimizing twist()
+// - Doubled speed of integer [0,n] generation
+// - Fixed out-of-range number generation on 64-bit machines
+// - Improved portability by substituting literal constants for long enum's
+// - Changed license from GNU LGPL to BSD
+//
+// v1.1 - Corrected parameter label in randNorm from "variance" to "stddev"
+// - Changed randNorm algorithm from basic to polar form for efficiency
+// - Updated includes from deprecated <xxxx.h> to standard <cxxxx> forms
+// - Cleaned declarations and definitions to please Intel compiler
+// - Revised twist() operator to work on ones'-complement machines
+// - Fixed reload() function to work when N and M are unsigned
+// - Added copy constructor and copy operator from Salvador Espana
diff --git a/src/MobCensus.cpp b/src/MobCensus.cpp
new file mode 100644
index 000000000..66b5932bc
--- /dev/null
+++ b/src/MobCensus.cpp
@@ -0,0 +1,92 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "MobCensus.h"
+
+
+
+
+
+void cMobCensus::CollectMob(cMonster & a_Monster, cChunk & a_Chunk, double a_Distance)
+{
+ m_ProximityCounter.CollectMob(a_Monster, a_Chunk, a_Distance);
+ m_MobFamilyCollecter.CollectMob(a_Monster);
+}
+
+
+
+
+
+bool cMobCensus::IsCapped(cMonster::eFamily a_MobFamily)
+{
+ bool toReturn = true;
+ const int ratio = 319; // this should be 256 as we are only supposed to take account from chunks that are in 17x17 from a player
+ // but for now, we use all chunks loaded by players. that means 19 x 19 chunks. That's why we use 256 * (19*19) / (17*17) = 319
+ // MG TODO : code the correct count
+ if ((GetCapMultiplier(a_MobFamily) * GetNumChunks()) / ratio >= m_MobFamilyCollecter.GetNumberOfCollectedMobs(a_MobFamily))
+ {
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+int cMobCensus::GetCapMultiplier(cMonster::eFamily a_MobFamily)
+{
+ switch (a_MobFamily)
+ {
+ case cMonster::mfHostile: return 79;
+ case cMonster::mfPassive: return 11;
+ case cMonster::mfAmbient: return 16;
+ case cMonster::mfWater: return 5;
+ }
+ ASSERT(!"Unhandled mob family");
+ return -1;
+}
+
+
+
+
+
+void cMobCensus::CollectSpawnableChunk(cChunk & a_Chunk)
+{
+ m_EligibleForSpawnChunks.insert(&a_Chunk);
+}
+
+
+
+
+
+int cMobCensus::GetNumChunks(void)
+{
+ return m_EligibleForSpawnChunks.size();
+}
+
+
+
+
+
+cMobProximityCounter & cMobCensus::GetProximityCounter(void)
+{
+ return m_ProximityCounter;
+}
+
+
+
+
+
+void cMobCensus::Logd()
+{
+ LOGD("Hostile mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfHostile), IsCapped(cMonster::mfHostile) ? "(capped)" : "");
+ LOGD("Ambient mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfAmbient), IsCapped(cMonster::mfAmbient) ? "(capped)" : "");
+ LOGD("Water mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfWater), IsCapped(cMonster::mfWater) ? "(capped)" : "");
+ LOGD("Passive mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfPassive), IsCapped(cMonster::mfPassive) ? "(capped)" : "");
+}
+
+
+
+
+
diff --git a/src/MobCensus.h b/src/MobCensus.h
new file mode 100644
index 000000000..e3892bec6
--- /dev/null
+++ b/src/MobCensus.h
@@ -0,0 +1,59 @@
+
+#pragma once
+
+#include "MobProximityCounter.h"
+#include "MobFamilyCollecter.h"
+
+
+
+
+// fwd:
+class cChunk;
+class cMonster;
+
+
+
+
+
+/** This class is used to collect information, for each Mob, what is the distance of the closest player
+it was first being designed in order to make mobs spawn / despawn / act
+as the behaviour and even life of mobs depends on the distance to closest player
+
+as side effect : it also collect the chunks that are elligible for spawning
+as side effect 2 : it also know the caps for mobs number and can compare census to this numbers
+*/
+class cMobCensus
+{
+public:
+ /// Returns the nested proximity counter
+ cMobProximityCounter & GetProximityCounter(void);
+
+ // collect an elligible Chunk for Mob Spawning
+ // MG TODO : code the correct rule (not loaded chunk but short distant from players)
+ void CollectSpawnableChunk(cChunk & a_Chunk);
+
+ /// Collect a mob - it's distance to player, it's family ...
+ void CollectMob(cMonster& a_Monster, cChunk& a_Chunk, double a_Distance);
+
+ /// Returns true if the family is capped (i.e. there are more mobs of this family than max)
+ bool IsCapped(cMonster::eFamily a_MobFamily);
+
+ /// log the results of census to server console
+ void Logd(void);
+
+protected :
+ cMobProximityCounter m_ProximityCounter;
+ cMobFamilyCollecter m_MobFamilyCollecter;
+
+ std::set<cChunk *> m_EligibleForSpawnChunks;
+
+ /// Returns the number of chunks that are elligible for spawning (for now, the loaded, valid chunks)
+ int GetNumChunks();
+
+ /// Returns the cap multiplier value of the given monster family
+ static int GetCapMultiplier(cMonster::eFamily a_MobFamily);
+} ;
+
+
+
+
diff --git a/src/MobFamilyCollecter.cpp b/src/MobFamilyCollecter.cpp
new file mode 100644
index 000000000..e9c69e078
--- /dev/null
+++ b/src/MobFamilyCollecter.cpp
@@ -0,0 +1,26 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "MobFamilyCollecter.h"
+#include "Mobs/Monster.h"
+
+
+
+void cMobFamilyCollecter::CollectMob(cMonster & a_Monster)
+{
+ cMonster::eFamily MobFamily = a_Monster.GetMobFamily();
+ m_Mobs[MobFamily].insert(&a_Monster);
+}
+
+
+
+
+
+int cMobFamilyCollecter::GetNumberOfCollectedMobs(cMonster::eFamily a_Family)
+{
+ return m_Mobs[a_Family].size();
+}
+
+
+
+
diff --git a/src/MobFamilyCollecter.h b/src/MobFamilyCollecter.h
new file mode 100644
index 000000000..6cef133b5
--- /dev/null
+++ b/src/MobFamilyCollecter.h
@@ -0,0 +1,39 @@
+
+#pragma once
+
+#include <map>
+#include <set>
+#include "BlockID.h"
+#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it
+
+
+
+
+// fwd:
+class cChunk;
+
+
+
+
+
+/** This class is used to collect the list of mobs for each family
+*/
+class cMobFamilyCollecter
+{
+public :
+ typedef const std::set<cMonster::eFamily> tMobFamilyList;
+
+ // collect a mob
+ void CollectMob(cMonster & a_Monster);
+
+ // return the number of mobs for this family
+ int GetNumberOfCollectedMobs(cMonster::eFamily a_Family);
+
+protected :
+ std::map<cMonster::eFamily, std::set<cMonster *> > m_Mobs;
+
+} ;
+
+
+
+
diff --git a/src/MobProximityCounter.cpp b/src/MobProximityCounter.cpp
new file mode 100644
index 000000000..583a71579
--- /dev/null
+++ b/src/MobProximityCounter.cpp
@@ -0,0 +1,83 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "MobProximityCounter.h"
+
+#include "Entities/Entity.h"
+#include "Chunk.h"
+
+void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance)
+{
+// LOGD("Collecting monster %s, with distance %f",a_Monster->GetClass(),a_Distance);
+ tMonsterToDistance::iterator it = m_MonsterToDistance.find(&a_Monster);
+ if (it == m_MonsterToDistance.end())
+ {
+ sDistanceAndChunk newDistanceAndChunk(a_Distance,a_Chunk);
+ std::pair<tMonsterToDistance::iterator,bool> result = m_MonsterToDistance.insert(tMonsterToDistance::value_type(&a_Monster,newDistanceAndChunk));
+ if (!result.second)
+ {
+ ASSERT(!"A collected Monster was not found inside distance map using find(), but insert() said there already is a key for it");
+ }
+ }
+ else
+ {
+ if (a_Distance < it->second.m_Distance)
+ {
+ it->second.m_Distance = a_Distance;
+ it->second.m_Chunk = a_Chunk;
+ }
+ }
+
+ m_EligibleForSpawnChunks.insert(&a_Chunk);
+
+}
+
+void cMobProximityCounter::convertMaps()
+{
+ for(tMonsterToDistance::const_iterator itr = m_MonsterToDistance.begin(); itr != m_MonsterToDistance.end(); itr++)
+ {
+ m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,itr->second.m_Chunk)));
+ }
+}
+
+cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax)
+{
+ sIterablePair toReturn;
+ toReturn.m_Count = 0;
+ toReturn.m_Begin = m_DistanceToMonster.end();
+ toReturn.m_End = m_DistanceToMonster.end();
+
+ a_DistanceMin *= a_DistanceMin;// this is because is use square distance
+ a_DistanceMax *= a_DistanceMax;
+
+ if (m_DistanceToMonster.size() <= 0)
+ {
+ convertMaps();
+ }
+
+ for(tDistanceToMonster::const_iterator itr = m_DistanceToMonster.begin(); itr != m_DistanceToMonster.end(); itr++)
+ {
+ if (toReturn.m_Begin == m_DistanceToMonster.end())
+ {
+ if (a_DistanceMin == -1 || itr->first > a_DistanceMin)
+ {
+ toReturn.m_Begin = itr; // this is the first one with distance > a_DistanceMin;
+ }
+ }
+
+ if (toReturn.m_Begin != m_DistanceToMonster.end())
+ {
+ if (a_DistanceMax != -1 && itr->first > a_DistanceMax)
+ {
+ toReturn.m_End = itr; // this is just after the last one with distance < a_DistanceMax
+ // Note : if we are not going through this, it's ok, toReturn.m_End will be end();
+ break;
+ }
+ else
+ {
+ toReturn.m_Count ++;
+ }
+ }
+ }
+ return toReturn;
+}
diff --git a/src/MobProximityCounter.h b/src/MobProximityCounter.h
new file mode 100644
index 000000000..8a67139aa
--- /dev/null
+++ b/src/MobProximityCounter.h
@@ -0,0 +1,65 @@
+
+#pragma once
+
+#include <set>
+
+class cChunk;
+class cEntity;
+
+
+// This class is used to collect, for each Mob, what is the distance of the closest player
+// it was first being designed in order to make mobs spawn / despawn / act
+// as the behaviour and even life of mobs depends on the distance to closest player
+class cMobProximityCounter
+{
+protected :
+ // structs used for later maps (see m_MonsterToDistance and m_DistanceToMonster)
+ struct sDistanceAndChunk
+ {
+ sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(a_Chunk) {}
+ double m_Distance;
+ cChunk& m_Chunk;
+ };
+ struct sMonsterAndChunk
+ {
+ sMonsterAndChunk(cEntity& a_Monster, cChunk& a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {}
+ cEntity& m_Monster;
+ cChunk& m_Chunk;
+ };
+
+public :
+ typedef std::map<cEntity*,sDistanceAndChunk> tMonsterToDistance;
+ typedef std::multimap<double,sMonsterAndChunk> tDistanceToMonster;
+
+protected :
+ // this map is filled during collection phase, it will be later transformed into DistanceToMonster
+ tMonsterToDistance m_MonsterToDistance;
+
+ // this map is generated after collection phase, in order to access monster by distance to player
+ tDistanceToMonster m_DistanceToMonster;
+
+ // this are the collected chunks. Used to determinate the number of elligible chunk for spawning.
+ std::set<cChunk*> m_EligibleForSpawnChunks;
+
+protected :
+ // transform monsterToDistance map (that was usefull for collecting) into distanceToMonster
+ // that will be usefull for picking up.
+ void convertMaps();
+
+public :
+ // count a mob on a specified chunk with specified distance to an unkown player
+ // if the distance is shortest than the one collected, this become the new closest
+ // distance and the chunk become the "hosting" chunk (that is the one that will perform the action)
+ void CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance);
+
+ // return the mobs that are within the range of distance of the closest player they are
+ // that means that if a mob is 30 m from a player and 150 m from another one. It will be
+ // in the range [0..50] but not in [100..200]
+ struct sIterablePair{
+ tDistanceToMonster::const_iterator m_Begin;
+ tDistanceToMonster::const_iterator m_End;
+ int m_Count;
+ };
+ sIterablePair getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax);
+
+};
diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp
new file mode 100644
index 000000000..4d0b2777b
--- /dev/null
+++ b/src/MobSpawner.cpp
@@ -0,0 +1,361 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "MobSpawner.h"
+#include "Mobs/IncludeAllMonsters.h"
+
+
+
+
+
+cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily,const std::set<cMonster::eType>& a_AllowedTypes) :
+ m_MonsterFamily(a_MonsterFamily),
+ m_NewPack(true),
+ m_MobType(cMonster::mtInvalidType)
+{
+ for (std::set<cMonster::eType>::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); itr++)
+ {
+ if (cMonster::FamilyFromType(*itr) == a_MonsterFamily)
+ {
+ m_AllowedTypes.insert(*itr);
+ }
+ }
+}
+
+
+
+
+
+bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType)
+{
+ // Packs of non-water mobs can only be centered on an air block
+ // Packs of water mobs can only be centered on a water block
+ if (m_MonsterFamily == cMonster::mfWater)
+ {
+ return IsBlockWater(a_BlockType);
+ }
+ else
+ {
+ return a_BlockType == E_BLOCK_AIR;
+ }
+}
+
+
+
+
+
+void cMobSpawner::addIfAllowed(cMonster::eType toAdd, std::set<cMonster::eType>& toAddIn)
+{
+ std::set<cMonster::eType>::iterator itr = m_AllowedTypes.find(toAdd);
+ if (itr != m_AllowedTypes.end())
+ {
+ toAddIn.insert(toAdd);
+ }
+}
+
+
+
+
+
+cMonster::eType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
+{
+ std::set<cMonster::eType> allowedMobs;
+
+ if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore)
+ {
+ addIfAllowed(cMonster::mtMooshroom, allowedMobs);
+ }
+ else if (a_Biome == biNether)
+ {
+ addIfAllowed(cMonster::mtGhast, allowedMobs);
+ addIfAllowed(cMonster::mtZombiePigman, allowedMobs);
+ addIfAllowed(cMonster::mtMagmaCube, allowedMobs);
+ }
+ else if (a_Biome == biEnd)
+ {
+ addIfAllowed(cMonster::mtEnderman, allowedMobs);
+ }
+ else
+ {
+ addIfAllowed(cMonster::mtBat, allowedMobs);
+ addIfAllowed(cMonster::mtSpider, allowedMobs);
+ addIfAllowed(cMonster::mtZombie, allowedMobs);
+ addIfAllowed(cMonster::mtSkeleton, allowedMobs);
+ addIfAllowed(cMonster::mtCreeper, allowedMobs);
+ addIfAllowed(cMonster::mtSquid, allowedMobs);
+
+ if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean)
+ {
+ addIfAllowed(cMonster::mtSheep, allowedMobs);
+ addIfAllowed(cMonster::mtPig, allowedMobs);
+ addIfAllowed(cMonster::mtCow, allowedMobs);
+ addIfAllowed(cMonster::mtChicken, allowedMobs);
+ addIfAllowed(cMonster::mtEnderman, allowedMobs);
+ addIfAllowed(cMonster::mtSlime, allowedMobs); // MG TODO : much more complicated rule
+
+ if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga || a_Biome == biTaigaHills)
+ {
+ addIfAllowed(cMonster::mtWolf, allowedMobs);
+ }
+ else if (a_Biome == biJungle || a_Biome == biJungleHills)
+ {
+ addIfAllowed(cMonster::mtOcelot, allowedMobs);
+ }
+ }
+ }
+
+ int allowedMobsSize = allowedMobs.size();
+ if (allowedMobsSize > 0)
+ {
+ std::set<cMonster::eType>::iterator itr = allowedMobs.begin();
+ int iRandom = m_Random.NextInt(allowedMobsSize,a_Biome);
+
+ for(int i = 0; i < iRandom; i++)
+ {
+ itr++;
+ }
+
+ return *itr;
+ }
+ return cMonster::mtInvalidType;
+}
+
+
+
+
+
+bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, cMonster::eType a_MobType, EMCSBiome a_Biome)
+{
+ BLOCKTYPE TargetBlock;
+ if (m_AllowedTypes.find(a_MobType) != m_AllowedTypes.end() && a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock))
+ {
+ NIBBLETYPE BlockLight = a_Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
+ NIBBLETYPE SkyLight = a_Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
+ BLOCKTYPE BlockAbove = a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ);
+ BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
+
+ SkyLight = a_Chunk->GetTimeAlteredLight(SkyLight);
+
+ switch(a_MobType)
+ {
+ case cMonster::mtSquid:
+ {
+ return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62);
+ }
+
+ case cMonster::mtBat:
+ {
+ return (a_RelY <= 63) && (BlockLight <= 4) && (SkyLight <= 4) && (TargetBlock == E_BLOCK_AIR) && (!g_BlockTransparent[BlockAbove]);
+ }
+
+ case cMonster::mtChicken:
+ case cMonster::mtCow:
+ case cMonster::mtPig:
+ case cMonster::mtHorse:
+ case cMonster::mtSheep:
+ {
+ return (
+ (TargetBlock == E_BLOCK_AIR) &&
+ (BlockAbove == E_BLOCK_AIR) &&
+ (!g_BlockTransparent[BlockBelow]) &&
+ (BlockBelow == E_BLOCK_GRASS) &&
+ (SkyLight >= 9)
+ );
+ }
+
+ case cMonster::mtOcelot:
+ {
+ return (
+ (TargetBlock == E_BLOCK_AIR) &&
+ (BlockAbove == E_BLOCK_AIR) &&
+ (
+ (BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES)
+ ) &&
+ (a_RelY >= 62) &&
+ (m_Random.NextInt(3, a_Biome) != 0)
+ );
+ }
+
+ case cMonster::mtEnderman:
+ {
+ if (a_RelY < 250)
+ {
+ BLOCKTYPE BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 2, a_RelZ);
+ if (BlockTop == E_BLOCK_AIR)
+ {
+ BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 3, a_RelZ);
+ return (
+ (TargetBlock == E_BLOCK_AIR) &&
+ (BlockAbove == E_BLOCK_AIR) &&
+ (BlockTop == E_BLOCK_AIR) &&
+ (!g_BlockTransparent[BlockBelow]) &&
+ (SkyLight <= 7) &&
+ (BlockLight <= 7)
+ );
+ }
+ }
+ break;
+ }
+
+ case cMonster::mtSpider:
+ {
+ bool CanSpawn = true;
+ bool HaveFloor = false;
+ for (int x = 0; x < 2; ++x)
+ {
+ for(int z = 0; z < 2; ++z)
+ {
+ CanSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY, a_RelZ + z, TargetBlock);
+ CanSpawn = CanSpawn && (TargetBlock == E_BLOCK_AIR);
+ if (!CanSpawn)
+ {
+ return false;
+ }
+ HaveFloor = (
+ HaveFloor ||
+ (
+ a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY - 1, a_RelZ + z, TargetBlock) &&
+ !g_BlockTransparent[TargetBlock]
+ )
+ );
+ }
+ }
+ return CanSpawn && HaveFloor && (SkyLight <= 7) && (BlockLight <= 7);
+ }
+
+ case cMonster::mtCreeper:
+ case cMonster::mtSkeleton:
+ case cMonster::mtZombie:
+ {
+ return (
+ (TargetBlock == E_BLOCK_AIR) &&
+ (BlockAbove == E_BLOCK_AIR) &&
+ (!g_BlockTransparent[BlockBelow]) &&
+ (SkyLight <= 7) &&
+ (BlockLight <= 7) &&
+ (m_Random.NextInt(2, a_Biome) == 0)
+ );
+ }
+
+ case cMonster::mtSlime:
+ {
+ return (
+ (TargetBlock == E_BLOCK_AIR) &&
+ (BlockAbove == E_BLOCK_AIR) &&
+ (!g_BlockTransparent[BlockBelow]) &&
+ (
+ (a_RelY <= 40) || (a_Biome == biSwampland)
+ )
+ );
+ }
+
+ case cMonster::mtGhast:
+ case cMonster::mtZombiePigman:
+ {
+ return (
+ (TargetBlock == E_BLOCK_AIR) &&
+ (BlockAbove == E_BLOCK_AIR) &&
+ (!g_BlockTransparent[BlockBelow]) &&
+ (m_Random.NextInt(20, a_Biome) == 0)
+ );
+ }
+
+ case cMonster::mtWolf:
+ {
+ return (
+ (TargetBlock == E_BLOCK_GRASS) &&
+ (BlockAbove == E_BLOCK_AIR) &&
+ (
+ (a_Biome == biTaiga) ||
+ (a_Biome == biTaigaHills) ||
+ (a_Biome == biForest) ||
+ (a_Biome == biForestHills) ||
+ (a_Biome == biColdTaiga) ||
+ (a_Biome == biColdTaigaHills) ||
+ (a_Biome == biTaigaM) ||
+ (a_Biome == biMegaTaiga) ||
+ (a_Biome == biMegaTaigaHills)
+ )
+ );
+ }
+
+ default:
+ {
+ LOGD("MG TODO: Write spawning rule for mob type %d", a_MobType);
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+
+
+
+
+cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize)
+{
+ cMonster* toReturn = NULL;
+ if (m_NewPack)
+ {
+ m_MobType = ChooseMobType(a_Biome);
+ if (m_MobType == cMonster::mtInvalidType)
+ {
+ return toReturn;
+ }
+ if (m_MobType == cMonster::mtWolf)
+ {
+ a_MaxPackSize = 8;
+ }
+ else if (m_MobType == cMonster::mtGhast)
+ {
+ a_MaxPackSize = 1;
+ }
+ m_NewPack = false;
+ }
+
+ // Make sure we are looking at the right chunk to spawn in
+ a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);
+
+ if (CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome))
+ {
+ cMonster * newMob = cMonster::NewMonsterFromType(m_MobType);
+ if (newMob)
+ {
+ m_Spawned.insert(newMob);
+ }
+ toReturn = newMob;
+ }
+ return toReturn;
+}
+
+
+
+
+
+void cMobSpawner::NewPack()
+{
+ m_NewPack = true;
+}
+
+
+
+
+
+cMobSpawner::tSpawnedContainer & cMobSpawner::getSpawned(void)
+{
+ return m_Spawned;
+}
+
+
+
+
+
+bool cMobSpawner::CanSpawnAnything(void)
+{
+ return !m_AllowedTypes.empty();
+}
+
+
+
+
diff --git a/src/MobSpawner.h b/src/MobSpawner.h
new file mode 100644
index 000000000..ea6636310
--- /dev/null
+++ b/src/MobSpawner.h
@@ -0,0 +1,76 @@
+
+#pragma once
+
+#include <set>
+#include "BlockID.h"
+#include "ChunkDef.h"
+#include "Chunk.h"
+#include "FastRandom.h"
+#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it
+
+
+
+
+// fwd:
+class cChunk;
+
+
+
+
+
+/** This class is used to determine which monster can be spawned in which place
+it is essentially static (eg. Squids spawn in water, Zombies spawn in dark places)
+but it also has dynamic part depending on the world.ini settings.
+*/
+class cMobSpawner
+{
+public :
+ // constructor
+ // a_MobFamily is the Family of mobs that this spawner will spawn
+ // a_AllowedTypes is the set of types allowed for mobs it will spawn. Empty set
+ // would result in no spawn at all
+ // Allowed mobs thah are not of the right Family will not be include (no warning)
+ cMobSpawner(cMonster::eFamily MobFamily, const std::set<cMonster::eType> & a_AllowedTypes);
+
+ /// Check if specified block can be a Pack center for this spawner
+ bool CheckPackCenter(BLOCKTYPE a_BlockType);
+
+ // Try to create a monster here
+ // if this is the first of a Pack : determine the type of monster
+ // BlockType & BlockMeta are used to decide what kind of Mob can Spawn here
+ // MaxPackSize is set to the maximal size for a pack this type of mob
+ cMonster * TryToSpawnHere(cChunk * a_Chunk, int A_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize);
+
+ // mark the beginning of a new Pack
+ // all mobs of the same Pack are the same type
+ void NewPack(void);
+
+ // return true if there is at least one allowed type
+ bool CanSpawnAnything(void);
+
+ typedef const std::set<cMonster *> tSpawnedContainer;
+ tSpawnedContainer & getSpawned(void);
+
+protected :
+ // return true if specified type of mob can spawn on specified block
+ bool CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, cMonster::eType a_MobType, EMCSBiome a_Biome);
+
+ // return a random type that can spawn on specified biome.
+ // returns E_ENTITY_TYPE_DONOTUSE if none is possible
+ cMonster::eType ChooseMobType(EMCSBiome a_Biome);
+
+ // add toAdd inside toAddIn, if toAdd is in m_AllowedTypes
+ void addIfAllowed(cMonster::eType toAdd, std::set<cMonster::eType> & toAddIn);
+
+protected :
+ cMonster::eFamily m_MonsterFamily;
+ std::set<cMonster::eType> m_AllowedTypes;
+ bool m_NewPack;
+ cMonster::eType m_MobType;
+ std::set<cMonster*> m_Spawned;
+ cFastRandom m_Random;
+} ;
+
+
+
+
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
new file mode 100644
index 000000000..cc7e7da2b
--- /dev/null
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -0,0 +1,97 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "AggressiveMonster.h"
+
+#include "../World.h"
+#include "../Vector3f.h"
+#include "../Entities/Player.h"
+#include "../MersenneTwister.h"
+
+
+
+
+
+cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height),
+ m_ChaseTime(999999)
+{
+ m_EMPersonality = AGGRESSIVE;
+}
+
+
+
+
+
+// What to do if in Chasing State
+void cAggressiveMonster::InStateChasing(float a_Dt)
+{
+ super::InStateChasing(a_Dt);
+ m_ChaseTime += a_Dt;
+ if (m_Target != NULL)
+ {
+ if (m_Target->IsPlayer())
+ {
+ cPlayer * Player = (cPlayer *) m_Target;
+ if (Player->IsGameModeCreative())
+ {
+ m_EMState = IDLE;
+ return;
+ }
+ }
+
+ Vector3f Pos = Vector3f( GetPosition() );
+ Vector3f Their = Vector3f( m_Target->GetPosition() );
+ if ((Their - Pos).Length() <= m_AttackRange)
+ {
+ Attack(a_Dt);
+ }
+ MoveToPosition(Their + Vector3f(0, 0.65f, 0));
+ }
+ else if (m_ChaseTime > 5.f)
+ {
+ m_ChaseTime = 0;
+ m_EMState = IDLE;
+ }
+}
+
+
+
+
+
+void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity)
+{
+ super::EventSeePlayer(a_Entity);
+ m_EMState = CHASING;
+}
+
+
+
+
+
+void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ m_SeePlayerInterval += a_Dt;
+
+ if (m_SeePlayerInterval > 1)
+ {
+ int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally
+
+ m_SeePlayerInterval = 0.0;
+ if (rem >= 2)
+ {
+ if (m_EMState == CHASING)
+ {
+ CheckEventLostPlayer();
+ }
+ else
+ {
+ CheckEventSeePlayer();
+ }
+ }
+ }
+}
+
+
diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h
new file mode 100644
index 000000000..5a0d93f3d
--- /dev/null
+++ b/src/Mobs/AggressiveMonster.h
@@ -0,0 +1,30 @@
+
+#pragma once
+
+#include "Monster.h"
+
+
+
+
+
+class cAggressiveMonster :
+ public cMonster
+{
+ typedef cMonster super;
+
+public:
+ cAggressiveMonster(const AString & a_ConfigName, eType 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 InStateChasing(float a_Dt) override;
+
+ virtual void EventSeePlayer(cEntity *) override;
+
+
+protected:
+ float m_ChaseTime;
+} ;
+
+
+
+
diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp
new file mode 100644
index 000000000..b9c82996b
--- /dev/null
+++ b/src/Mobs/Bat.cpp
@@ -0,0 +1,15 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Bat.h"
+#include "../Vector3d.h"
+#include "../Chunk.h"
+
+
+cBat::cBat(void) :
+ // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
+ super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7)
+{
+}
+
+
diff --git a/src/Mobs/Bat.h b/src/Mobs/Bat.h
new file mode 100644
index 000000000..e878d0ee8
--- /dev/null
+++ b/src/Mobs/Bat.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cBat :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cBat(void);
+
+ CLASS_PROTODEF(cBat);
+
+ bool IsHanging(void) const {return false; }
+} ;
+
+
+
+
diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp
new file mode 100644
index 000000000..f9c05b17a
--- /dev/null
+++ b/src/Mobs/Blaze.cpp
@@ -0,0 +1,52 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Blaze.h"
+#include "../World.h"
+
+
+
+
+cBlaze::cBlaze(void) :
+ // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
+ super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8)
+{
+}
+
+
+
+
+
+void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD);
+}
+
+
+
+
+
+void cBlaze::Attack(float a_Dt)
+{
+ m_AttackInterval += a_Dt * m_AttackRate;
+
+ if (m_Target != NULL && m_AttackInterval > 3.0)
+ {
+ // Setting this higher gives us more wiggle room for attackrate
+ Vector3d Speed = GetLookVector() * 20;
+ Speed.y = Speed.y + 1;
+ cFireChargeEntity * FireCharge = new cFireChargeEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
+ if (FireCharge == NULL)
+ {
+ return;
+ }
+ if (!FireCharge->Initialize(m_World))
+ {
+ delete FireCharge;
+ return;
+ }
+ m_World->BroadcastSpawnEntity(*FireCharge);
+ m_AttackInterval = 0.0;
+ // ToDo: Shoot 3 fireballs instead of 1.
+ }
+} \ No newline at end of file
diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h
new file mode 100644
index 000000000..cdb3a1306
--- /dev/null
+++ b/src/Mobs/Blaze.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cBlaze :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cBlaze(void);
+
+ CLASS_PROTODEF(cBlaze);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void Attack(float a_Dt) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Cavespider.cpp b/src/Mobs/Cavespider.cpp
new file mode 100644
index 000000000..aba1ff9f5
--- /dev/null
+++ b/src/Mobs/Cavespider.cpp
@@ -0,0 +1,40 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Cavespider.h"
+#include "../World.h"
+
+
+
+
+
+cCavespider::cCavespider(void) :
+ super("Cavespider", mtCaveSpider, "mob.spider.say", "mob.spider.death", 0.7, 0.5)
+{
+}
+
+
+
+
+
+void cCavespider::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ // TODO: Check vanilla if cavespiders really get passive during the day / in daylight
+ m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE;
+}
+
+
+
+
+
+void cCavespider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING);
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE);
+}
+
+
+
+
diff --git a/src/Mobs/Cavespider.h b/src/Mobs/Cavespider.h
new file mode 100644
index 000000000..10ea03f7b
--- /dev/null
+++ b/src/Mobs/Cavespider.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cCavespider :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cCavespider(void);
+
+ CLASS_PROTODEF(cCaveSpider);
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp
new file mode 100644
index 000000000..087fd088a
--- /dev/null
+++ b/src/Mobs/Chicken.cpp
@@ -0,0 +1,62 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Chicken.h"
+#include "../World.h"
+
+
+
+
+
+
+
+
+cChicken::cChicken(void) :
+ super("Chicken", mtChicken, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4),
+ m_EggDropTimer(0)
+{
+}
+
+
+
+
+void cChicken::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if ((m_EggDropTimer == 6000) && (m_World->GetTickRandomNumber(1) == 0))
+ {
+ cItems Drops;
+ m_EggDropTimer = 0;
+ Drops.push_back(cItem(E_ITEM_EGG, 1));
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
+ }
+ else if (m_EggDropTimer == 12000)
+ {
+ cItems Drops;
+ m_EggDropTimer = 0;
+ Drops.push_back(cItem(E_ITEM_EGG, 1));
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
+ }
+ else
+ {
+ m_EggDropTimer++;
+ }
+}
+
+
+
+
+
+void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_FEATHER);
+ a_Drops.push_back(cItem(IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN, 1));
+}
+
+
+
+
+
+
+
+
diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h
new file mode 100644
index 000000000..979c4d8a0
--- /dev/null
+++ b/src/Mobs/Chicken.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cChicken :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cChicken(void);
+
+ CLASS_PROTODEF(cChicken);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+private:
+
+
+ int m_EggDropTimer;
+} ;
+
+
+
diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp
new file mode 100644
index 000000000..9eb74dac2
--- /dev/null
+++ b/src/Mobs/Cow.cpp
@@ -0,0 +1,45 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Cow.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+
+
+cCow::cCow(void) :
+ super("Cow", mtCow, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3)
+{
+}
+
+
+
+
+
+void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
+ AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
+}
+
+
+
+
+
+void cCow::OnRightClicked(cPlayer & a_Player)
+{
+ if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_BUCKET))
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ a_Player.GetInventory().AddItem(E_ITEM_MILK);
+ }
+ }
+}
+
+
+
diff --git a/src/Mobs/Cow.h b/src/Mobs/Cow.h
new file mode 100644
index 000000000..0391d4a31
--- /dev/null
+++ b/src/Mobs/Cow.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cCow :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cCow();
+
+ CLASS_PROTODEF(cCow);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp
new file mode 100644
index 000000000..4e11ae13e
--- /dev/null
+++ b/src/Mobs/Creeper.cpp
@@ -0,0 +1,47 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Creeper.h"
+#include "../World.h"
+
+
+
+
+
+cCreeper::cCreeper(void) :
+ super("Creeper", mtCreeper, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8),
+ m_bIsBlowing(false),
+ m_bIsCharged(false)
+{
+}
+
+
+
+
+
+void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER);
+
+ // TODO Check if killed by a skeleton, then drop random music disk
+}
+
+
+
+
+
+void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+
+ if (a_TDI.DamageType == dtLightning)
+ {
+ m_bIsCharged = true;
+ }
+
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h
new file mode 100644
index 000000000..c3d4edeae
--- /dev/null
+++ b/src/Mobs/Creeper.h
@@ -0,0 +1,34 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cCreeper :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cCreeper(void);
+
+ CLASS_PROTODEF(cCreeper);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+ bool IsBlowing(void) const {return m_bIsBlowing; }
+ bool IsCharged(void) const {return m_bIsCharged; }
+
+private:
+
+ bool m_bIsBlowing, m_bIsCharged;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/EnderDragon.cpp b/src/Mobs/EnderDragon.cpp
new file mode 100644
index 000000000..acd81cde1
--- /dev/null
+++ b/src/Mobs/EnderDragon.cpp
@@ -0,0 +1,27 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "EnderDragon.h"
+
+
+
+
+
+cEnderDragon::cEnderDragon(void) :
+ // TODO: Vanilla source says this, but is it right? Dragons fly, they don't stand
+ super("EnderDragon", mtEnderDragon, "mob.enderdragon.hit", "mob.enderdragon.end", 16.0, 8.0)
+{
+}
+
+
+
+
+
+void cEnderDragon::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ return;
+}
+
+
+
+
diff --git a/src/Mobs/EnderDragon.h b/src/Mobs/EnderDragon.h
new file mode 100644
index 000000000..77177edfe
--- /dev/null
+++ b/src/Mobs/EnderDragon.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cEnderDragon :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cEnderDragon(void);
+
+ CLASS_PROTODEF(cEnderDragon);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp
new file mode 100644
index 000000000..a784131e4
--- /dev/null
+++ b/src/Mobs/Enderman.cpp
@@ -0,0 +1,29 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Enderman.h"
+
+
+
+
+
+cEnderman::cEnderman(void) :
+ super("Enderman", mtEnderman, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.9),
+ m_bIsScreaming(false),
+ CarriedBlock(E_BLOCK_AIR),
+ CarriedMeta(0)
+{
+}
+
+
+
+
+
+void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ENDER_PEARL);
+}
+
+
+
+
diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h
new file mode 100644
index 000000000..32e40e70b
--- /dev/null
+++ b/src/Mobs/Enderman.h
@@ -0,0 +1,36 @@
+
+#pragma once
+
+#include "PassiveAggressiveMonster.h"
+
+
+
+
+
+class cEnderman :
+ public cPassiveAggressiveMonster
+{
+ typedef cPassiveAggressiveMonster super;
+
+public:
+ cEnderman(void);
+
+ CLASS_PROTODEF(cEnderman);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
+ bool IsScreaming(void) const {return m_bIsScreaming; }
+ BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; }
+ NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; }
+
+private:
+
+ bool m_bIsScreaming;
+ BLOCKTYPE CarriedBlock;
+ NIBBLETYPE CarriedMeta;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp
new file mode 100644
index 000000000..96a29b2d8
--- /dev/null
+++ b/src/Mobs/Ghast.cpp
@@ -0,0 +1,54 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Ghast.h"
+#include "../World.h"
+
+
+
+
+cGhast::cGhast(void) :
+ super("Ghast", mtGhast, "mob.ghast.scream", "mob.ghast.death", 4, 4)
+{
+}
+
+
+
+
+
+void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER);
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GHAST_TEAR);
+}
+
+
+
+
+
+void cGhast::Attack(float a_Dt)
+{
+ m_AttackInterval += a_Dt * m_AttackRate;
+
+ if (m_Target != NULL && m_AttackInterval > 3.0)
+ {
+ // Setting this higher gives us more wiggle room for attackrate
+ Vector3d Speed = GetLookVector() * 20;
+ Speed.y = Speed.y + 1;
+ cGhastFireballEntity * GhastBall = new cGhastFireballEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
+ if (GhastBall == NULL)
+ {
+ return;
+ }
+ if (!GhastBall->Initialize(m_World))
+ {
+ delete GhastBall;
+ return;
+ }
+ m_World->BroadcastSpawnEntity(*GhastBall);
+ m_AttackInterval = 0.0;
+ }
+}
+
+
+
diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h
new file mode 100644
index 000000000..43e8bedb6
--- /dev/null
+++ b/src/Mobs/Ghast.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cGhast :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cGhast(void);
+
+ CLASS_PROTODEF(cGhast);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void Attack(float a_Dt) override;
+
+ bool IsCharging(void) const {return false; }
+} ;
+
+
+
+
diff --git a/src/Mobs/Giant.cpp b/src/Mobs/Giant.cpp
new file mode 100644
index 000000000..bbcad46f0
--- /dev/null
+++ b/src/Mobs/Giant.cpp
@@ -0,0 +1,27 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Giant.h"
+
+
+
+
+
+cGiant::cGiant(void) :
+ super("Giant", mtGiant, "mob.zombie.hurt", "mob.zombie.death", 3.6, 10.8)
+{
+
+}
+
+
+
+
+
+void cGiant::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 10, 50, E_ITEM_ROTTEN_FLESH);
+}
+
+
+
+
diff --git a/src/Mobs/Giant.h b/src/Mobs/Giant.h
new file mode 100644
index 000000000..356dd4352
--- /dev/null
+++ b/src/Mobs/Giant.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cGiant :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cGiant(void);
+
+ CLASS_PROTODEF(cGiant);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp
new file mode 100644
index 000000000..bb9a4e3f6
--- /dev/null
+++ b/src/Mobs/Horse.cpp
@@ -0,0 +1,152 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Horse.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
+ super("Horse", mtHorse, "mob.horse.hit", "mob.horse.death", 1.4, 1.6),
+ m_bHasChest(false),
+ m_bIsEating(false),
+ m_bIsRearing(false),
+ m_bIsMouthOpen(false),
+ m_bIsTame(false),
+ m_bIsSaddled(false),
+ m_Type(Type),
+ m_Color(Color),
+ m_Style(Style),
+ m_Armour(0),
+ m_TimesToTame(TameTimes),
+ m_TameAttemptTimes(0),
+ m_RearTickCount(0)
+{
+}
+
+
+
+
+
+void cHorse::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if (!m_bIsMouthOpen)
+ {
+ if (m_World->GetTickRandomNumber(50) == 25)
+ {
+ m_bIsMouthOpen = true;
+ }
+ }
+ else
+ {
+ if (m_World->GetTickRandomNumber(10) == 5)
+ {
+ m_bIsMouthOpen = false;
+ }
+ }
+
+ if ((m_Attachee != NULL) && (!m_bIsTame))
+ {
+ if (m_TameAttemptTimes < m_TimesToTame)
+ {
+ if (m_World->GetTickRandomNumber(50) == 25)
+ {
+ m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 0);
+ m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 2);
+ m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 6);
+ m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 8);
+
+ m_Attachee->Detach();
+ m_bIsRearing = true;
+ }
+ }
+ else
+ {
+ m_bIsTame = true;
+ }
+ }
+
+ if (m_bIsRearing)
+ {
+ if (m_RearTickCount == 20)
+ {
+ m_bIsRearing = false;
+ m_RearTickCount = 0;
+ }
+ else
+ {
+ m_RearTickCount++;
+ }
+ }
+
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cHorse::OnRightClicked(cPlayer & a_Player)
+{
+ if (!m_bIsSaddled && m_bIsTame)
+ {
+ if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE)
+ {
+ // Saddle the horse:
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ m_bIsSaddled = true;
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ else if (!a_Player.GetEquippedItem().IsEmpty())
+ {
+ // The horse doesn't like being hit, make it rear:
+ m_bIsRearing = true;
+ m_RearTickCount = 0;
+ }
+ }
+ else
+ {
+ if (m_Attachee != NULL)
+ {
+ if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ {
+ a_Player.Detach();
+ return;
+ }
+
+ if (m_Attachee->IsPlayer())
+ {
+ return;
+ }
+
+ m_Attachee->Detach();
+ }
+
+ m_TameAttemptTimes++;
+ a_Player.AttachTo(this);
+ }
+}
+
+
+
+
+
+void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
+ if (m_bIsSaddled)
+ {
+ a_Drops.push_back(cItem(E_ITEM_SADDLE, 1));
+ }
+}
+
+
+
+
diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h
new file mode 100644
index 000000000..be0c23f9b
--- /dev/null
+++ b/src/Mobs/Horse.h
@@ -0,0 +1,44 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cHorse :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cHorse(int Type, int Color, int Style, int TameTimes);
+
+ CLASS_PROTODEF(cHorse);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+
+ bool IsSaddled (void) const {return m_bIsSaddled; }
+ bool IsChested (void) const {return m_bHasChest; }
+ bool IsEating (void) const {return m_bIsEating; }
+ bool IsRearing (void) const {return m_bIsRearing; }
+ bool IsMthOpen (void) const {return m_bIsMouthOpen; }
+ bool IsTame (void) const {return m_bIsTame; }
+ int GetHorseType (void) const {return m_Type; }
+ int GetHorseColor (void) const {return m_Color; }
+ int GetHorseStyle (void) const {return m_Style; }
+ int GetHorseArmour (void) const {return m_Armour;}
+
+private:
+
+ bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled;
+ int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h
new file mode 100644
index 000000000..1b436a11f
--- /dev/null
+++ b/src/Mobs/IncludeAllMonsters.h
@@ -0,0 +1,29 @@
+#include "Bat.h"
+#include "Blaze.h"
+#include "Cavespider.h"
+#include "Chicken.h"
+#include "Cow.h"
+#include "Creeper.h"
+#include "Enderman.h"
+#include "EnderDragon.h"
+#include "Ghast.h"
+#include "Giant.h"
+#include "Horse.h"
+#include "IronGolem.h"
+#include "Magmacube.h"
+#include "Mooshroom.h"
+#include "Ocelot.h"
+#include "Pig.h"
+#include "Sheep.h"
+#include "Silverfish.h"
+#include "Skeleton.h"
+#include "Slime.h"
+#include "SnowGolem.h"
+#include "Spider.h"
+#include "Squid.h"
+#include "Villager.h"
+#include "Witch.h"
+#include "Wither.h"
+#include "Wolf.h"
+#include "Zombie.h"
+#include "Zombiepigman.h"
diff --git a/src/Mobs/IronGolem.cpp b/src/Mobs/IronGolem.cpp
new file mode 100644
index 000000000..47c961098
--- /dev/null
+++ b/src/Mobs/IronGolem.cpp
@@ -0,0 +1,26 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "IronGolem.h"
+
+
+
+
+
+cIronGolem::cIronGolem(void) :
+ super("IronGolem", mtIronGolem, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9)
+{
+}
+
+
+
+
+
+void cIronGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 5, E_ITEM_IRON);
+}
+
+
+
+
diff --git a/src/Mobs/IronGolem.h b/src/Mobs/IronGolem.h
new file mode 100644
index 000000000..d49ff4cab
--- /dev/null
+++ b/src/Mobs/IronGolem.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "PassiveAggressiveMonster.h"
+
+
+
+
+
+class cIronGolem :
+ public cPassiveAggressiveMonster
+{
+ typedef cPassiveAggressiveMonster super;
+
+public:
+ cIronGolem(void);
+
+ CLASS_PROTODEF(cIronGolem);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Magmacube.cpp b/src/Mobs/Magmacube.cpp
new file mode 100644
index 000000000..86447ff6b
--- /dev/null
+++ b/src/Mobs/Magmacube.cpp
@@ -0,0 +1,27 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Magmacube.h"
+
+
+
+
+
+cMagmaCube::cMagmaCube(int a_Size) :
+ super("MagmaCube", mtMagmaCube, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size),
+ m_Size(a_Size)
+{
+}
+
+
+
+
+
+void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM);
+}
+
+
+
+
diff --git a/src/Mobs/Magmacube.h b/src/Mobs/Magmacube.h
new file mode 100644
index 000000000..130952970
--- /dev/null
+++ b/src/Mobs/Magmacube.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cMagmaCube :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ /// Creates a MagmaCube of the specified size; size is 1 .. 3, with 1 being the smallest
+ cMagmaCube(int a_Size);
+
+ CLASS_PROTODEF(cMagmaCube);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ int GetSize(void) const { return m_Size; }
+
+protected:
+
+ /// Size of the MagmaCube, 1 .. 3, with 1 being the smallest
+ int m_Size;
+} ;
+
+
+
+
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
new file mode 100644
index 000000000..8a5717e27
--- /dev/null
+++ b/src/Mobs/Monster.cpp
@@ -0,0 +1,758 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "IncludeAllMonsters.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../ClientHandle.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+#include "../Defines.h"
+#include "../MonsterConfig.h"
+#include "../MersenneTwister.h"
+
+#include "../Vector3f.h"
+#include "../Vector3i.h"
+#include "../Vector3d.h"
+#include "../Tracer.h"
+#include "../Chunk.h"
+#include "../FastRandom.h"
+
+
+
+
+
+/** Map for eType <-> string
+Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType()
+The strings need to be lowercase (for more efficient comparisons in StringToMobType())
+*/
+static const struct
+{
+ cMonster::eType m_Type;
+ const char * m_lcName;
+} g_MobTypeNames[] =
+{
+ {cMonster::mtBat, "bat"},
+ {cMonster::mtBlaze, "blaze"},
+ {cMonster::mtCaveSpider, "cavespider"},
+ {cMonster::mtChicken, "chicken"},
+ {cMonster::mtCow, "cow"},
+ {cMonster::mtCreeper, "creeper"},
+ {cMonster::mtEnderman, "enderman"},
+ {cMonster::mtGhast, "ghast"},
+ {cMonster::mtHorse, "horse"},
+ {cMonster::mtMagmaCube, "magmacube"},
+ {cMonster::mtMooshroom, "mooshroom"},
+ {cMonster::mtOcelot, "ocelot"},
+ {cMonster::mtPig, "pig"},
+ {cMonster::mtSheep, "sheep"},
+ {cMonster::mtSilverfish, "silverfish"},
+ {cMonster::mtSkeleton, "skeleton"},
+ {cMonster::mtSlime, "slime"},
+ {cMonster::mtSpider, "spider"},
+ {cMonster::mtSquid, "squid"},
+ {cMonster::mtVillager, "villager"},
+ {cMonster::mtWitch, "witch"},
+ {cMonster::mtWolf, "wolf"},
+ {cMonster::mtZombie, "zombie"},
+ {cMonster::mtZombiePigman, "zombiepigman"},
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMonster:
+
+cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height)
+ : super(etMonster, a_Width, a_Height)
+ , m_Target(NULL)
+ , m_AttackRate(3)
+ , idle_interval(0)
+ , m_bMovingToDestination(false)
+ , m_DestinationTime( 0 )
+ , m_DestroyTimer( 0 )
+ , m_Jump(0)
+ , m_MobType(a_MobType)
+ , m_SoundHurt(a_SoundHurt)
+ , m_SoundDeath(a_SoundDeath)
+ , m_EMState(IDLE)
+ , m_SightDistance(25)
+ , m_SeePlayerInterval (0)
+ , m_EMPersonality(AGGRESSIVE)
+ , m_AttackDamage(1.0f)
+ , m_AttackRange(2.0f)
+ , m_AttackInterval(0)
+ , m_BurnsInDaylight(false)
+{
+ if (!a_ConfigName.empty())
+ {
+ GetMonsterConfig(a_ConfigName);
+ }
+}
+
+
+
+
+
+void cMonster::SpawnOn(cClientHandle & a_Client)
+{
+ a_Client.SendSpawnMob(*this);
+}
+
+
+
+
+
+void cMonster::MoveToPosition( const Vector3f & a_Position )
+{
+ m_bMovingToDestination = true;
+
+ m_Destination = a_Position;
+}
+
+
+
+
+
+bool cMonster::ReachedDestination()
+{
+ Vector3f Distance = (m_Destination) - GetPosition();
+ if( Distance.SqrLength() < 2.f )
+ return true;
+
+ return false;
+}
+
+
+
+
+
+void cMonster::Tick(float 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;
+ if (m_DestroyTimer > 1)
+ {
+ Destroy(true);
+ }
+ return;
+ }
+
+ // Burning in daylight
+ HandleDaylightBurning(a_Chunk);
+
+ HandlePhysics(a_Dt,a_Chunk);
+ BroadcastMovementUpdate();
+
+ a_Dt /= 1000;
+
+ if (m_bMovingToDestination)
+ {
+ Vector3f Pos( GetPosition() );
+ Vector3f Distance = m_Destination - Pos;
+ if( !ReachedDestination() )
+ {
+ Distance.y = 0;
+ Distance.Normalize();
+ Distance *= 3;
+ SetSpeedX( Distance.x );
+ SetSpeedZ( Distance.z );
+
+ if (m_EMState == ESCAPING)
+ { //Runs Faster when escaping :D otherwise they just walk away
+ SetSpeedX (GetSpeedX() * 2.f);
+ SetSpeedZ (GetSpeedZ() * 2.f);
+ }
+ }
+ else
+ {
+ m_bMovingToDestination = false;
+ }
+
+ if( GetSpeed().SqrLength() > 0.f )
+ {
+ if( m_bOnGround )
+ {
+ Vector3f NormSpeed = Vector3f(GetSpeed()).NormalizeCopy();
+ Vector3f NextBlock = Vector3f( GetPosition() ) + NormSpeed;
+ int NextHeight;
+ if (!m_World->TryGetHeight((int)NextBlock.x, (int)NextBlock.z, NextHeight))
+ {
+ // The chunk at NextBlock is not loaded
+ return;
+ }
+ if( NextHeight > (GetPosY() - 1.0) && (NextHeight - GetPosY()) < 2.5 )
+ {
+ m_bOnGround = false;
+ SetSpeedY(5.f); // Jump!!
+ }
+ }
+ }
+ }
+
+ Vector3d Distance = m_Destination - GetPosition();
+ if (Distance.SqrLength() > 0.1f)
+ {
+ double Rotation, Pitch;
+ Distance.Normalize();
+ VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch );
+ SetHeadYaw (Rotation);
+ SetRotation( Rotation );
+ SetPitch( -Pitch );
+ }
+
+ switch (m_EMState)
+ {
+ case IDLE:
+ {
+ // If enemy passive we ignore checks for player visibility
+ InStateIdle(a_Dt);
+ break;
+ }
+
+ case CHASING:
+ {
+ // If we do not see a player anymore skip chasing action
+ InStateChasing(a_Dt);
+ break;
+ }
+
+ case ESCAPING:
+ {
+ InStateEscaping(a_Dt);
+ break;
+ }
+ } // switch (m_EMState)
+}
+
+
+
+
+
+
+void cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+ if((m_SoundHurt != "") && (m_Health > 0)) m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f);
+ if (a_TDI.Attacker != NULL)
+ {
+ m_Target = a_TDI.Attacker;
+ AddReference(m_Target);
+ }
+}
+
+
+
+
+
+void cMonster::KilledBy(cEntity * a_Killer)
+{
+ super::KilledBy(a_Killer);
+ if (m_SoundHurt != "")
+ {
+ m_World->BroadcastSoundEffect(m_SoundDeath, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f);
+ }
+ m_DestroyTimer = 0;
+}
+
+
+
+
+
+//----State Logic
+
+const char *cMonster::GetState()
+{
+ switch(m_EMState)
+ {
+ case IDLE: return "Idle";
+ case ATTACKING: return "Attacking";
+ case CHASING: return "Chasing";
+ default: return "Unknown";
+ }
+}
+
+
+
+
+
+// for debugging
+void cMonster::SetState(const AString & a_State)
+{
+ if (a_State.compare("Idle") == 0)
+ {
+ m_EMState = IDLE;
+ }
+ else if (a_State.compare("Attacking") == 0)
+ {
+ m_EMState = ATTACKING;
+ }
+ else if (a_State.compare("Chasing") == 0)
+ {
+ m_EMState = CHASING;
+ }
+ else
+ {
+ LOGD("cMonster::SetState(): Invalid state");
+ ASSERT(!"Invalid state");
+ }
+}
+
+
+
+
+
+//Checks to see if EventSeePlayer should be fired
+//monster sez: Do I see the player
+void cMonster::CheckEventSeePlayer(void)
+{
+ // TODO: Rewrite this to use cWorld's DoWithPlayers()
+ cPlayer * Closest = FindClosestPlayer();
+
+ if (Closest != NULL)
+ {
+ EventSeePlayer(Closest);
+ }
+}
+
+
+
+
+
+void cMonster::CheckEventLostPlayer(void)
+{
+ Vector3f pos;
+ cTracer LineOfSight(GetWorld());
+
+ if (m_Target != NULL)
+ {
+ pos = m_Target->GetPosition();
+ if ((pos - GetPosition()).Length() > m_SightDistance || LineOfSight.Trace(GetPosition(),(pos - GetPosition()), (int)(pos - GetPosition()).Length()))
+ {
+ EventLosePlayer();
+ }
+ }
+ else
+ {
+ EventLosePlayer();
+ }
+}
+
+
+
+
+
+// What to do if player is seen
+// default to change state to chasing
+void cMonster::EventSeePlayer(cEntity * a_SeenPlayer)
+{
+ m_Target = a_SeenPlayer;
+ AddReference(m_Target);
+}
+
+
+
+
+
+void cMonster::EventLosePlayer(void)
+{
+ Dereference(m_Target);
+ m_Target = NULL;
+ m_EMState = IDLE;
+}
+
+
+
+
+
+// What to do if in Idle State
+void cMonster::InStateIdle(float a_Dt)
+{
+ idle_interval += a_Dt;
+ if (idle_interval > 1)
+ {
+ // at this interval the results are predictable
+ int rem = m_World->GetTickRandomNumber(6) + 1;
+ // LOGD("Moving: int: %3.3f rem: %i",idle_interval,rem);
+ idle_interval -= 1; // So nothing gets dropped when the server hangs for a few seconds
+ Vector3f Dist;
+ Dist.x = (float)(m_World->GetTickRandomNumber(10) - 5);
+ Dist.z = (float)(m_World->GetTickRandomNumber(10) - 5);
+ if ((Dist.SqrLength() > 2) && (rem >= 3))
+ {
+ m_Destination.x = (float)(GetPosX() + Dist.x);
+ m_Destination.z = (float)(GetPosZ() + Dist.z);
+ int PosY;
+ if (m_World->TryGetHeight((int)m_Destination.x, (int)m_Destination.z, PosY))
+ {
+ m_Destination.y = (float)PosY + 1.2f;
+ MoveToPosition(m_Destination);
+ }
+ }
+ }
+}
+
+
+
+
+
+// What to do if in Chasing State
+// This state should always be defined in each child class
+void cMonster::InStateChasing(float a_Dt)
+{
+ UNUSED(a_Dt);
+}
+
+
+
+
+
+// What to do if in Escaping State
+void cMonster::InStateEscaping(float a_Dt)
+{
+ UNUSED(a_Dt);
+
+ if (m_Target != NULL)
+ {
+ Vector3d newloc = GetPosition();
+ newloc.x = (m_Target->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance);
+ newloc.z = (m_Target->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance);
+ MoveToPosition(newloc);
+ }
+ else
+ {
+ m_EMState = IDLE; // This shouldnt be required but just to be safe
+ }
+}
+
+
+
+
+
+// Do attack here
+// a_Dt is passed so we can set attack rate
+void cMonster::Attack(float a_Dt)
+{
+ m_AttackInterval += a_Dt * m_AttackRate;
+ if ((m_Target != NULL) && (m_AttackInterval > 3.0))
+ {
+ // Setting this higher gives us more wiggle room for attackrate
+ m_AttackInterval = 0.0;
+ ((cPawn *)m_Target)->TakeDamage(*this);
+ }
+}
+
+
+
+
+
+// Checks for Players close by and if they are visible return the closest
+cPlayer * cMonster::FindClosestPlayer(void)
+{
+ return m_World->FindClosestPlayer(GetPosition(), m_SightDistance);
+}
+
+
+
+
+
+void cMonster::GetMonsterConfig(const AString & a_Name)
+{
+ cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name);
+}
+
+
+
+
+
+void cMonster::SetAttackRate(int ar)
+{
+ m_AttackRate = (float)ar;
+}
+
+
+
+
+
+void cMonster::SetAttackRange(float ar)
+{
+ m_AttackRange = ar;
+}
+
+
+
+
+
+void cMonster::SetAttackDamage(float ad)
+{
+ m_AttackDamage = ad;
+}
+
+
+
+
+
+void cMonster::SetSightDistance(float sd)
+{
+ m_SightDistance = sd;
+}
+
+
+
+
+
+AString cMonster::MobTypeToString(cMonster::eType a_MobType)
+{
+ // Mob types aren't sorted, so we need to search linearly:
+ for (int i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++)
+ {
+ if (g_MobTypeNames[i].m_Type == a_MobType)
+ {
+ return g_MobTypeNames[i].m_lcName;
+ }
+ }
+
+ // Not found:
+ return "";
+}
+
+
+
+
+
+cMonster::eType cMonster::StringToMobType(const AString & a_Name)
+{
+ AString lcName(a_Name);
+ StrToLower(lcName);
+
+ // Binary-search for the lowercase name:
+ int lo = 0, hi = ARRAYCOUNT(g_MobTypeNames) - 1;
+ while (hi - lo > 1)
+ {
+ int mid = (lo + hi) / 2;
+ int res = strcmp(g_MobTypeNames[mid].m_lcName, lcName.c_str());
+ if (res == 0)
+ {
+ return g_MobTypeNames[mid].m_Type;
+ }
+ if (res < 0)
+ {
+ lo = mid;
+ }
+ else
+ {
+ hi = mid;
+ }
+ }
+ // Range has collapsed to at most two elements, compare each:
+ if (strcmp(g_MobTypeNames[lo].m_lcName, lcName.c_str()) == 0)
+ {
+ return g_MobTypeNames[lo].m_Type;
+ }
+ if ((lo != hi) && (strcmp(g_MobTypeNames[hi].m_lcName, lcName.c_str()) == 0))
+ {
+ return g_MobTypeNames[hi].m_Type;
+ }
+
+ // Not found:
+ return mtInvalidType;
+}
+
+
+
+
+
+cMonster::eFamily cMonster::FamilyFromType(eType a_Type)
+{
+ switch (a_Type)
+ {
+ case mtBat: return mfAmbient;
+ case mtBlaze: return mfHostile;
+ case mtCaveSpider: return mfHostile;
+ case mtChicken: return mfPassive;
+ case mtCow: return mfPassive;
+ case mtCreeper: return mfHostile;
+ case mtEnderman: return mfHostile;
+ case mtGhast: return mfHostile;
+ case mtHorse: return mfPassive;
+ case mtMagmaCube: return mfHostile;
+ case mtMooshroom: return mfHostile;
+ case mtOcelot: return mfHostile;
+ case mtPig: return mfPassive;
+ case mtSheep: return mfPassive;
+ case mtSilverfish: return mfHostile;
+ case mtSkeleton: return mfHostile;
+ case mtSlime: return mfHostile;
+ case mtSpider: return mfHostile;
+ case mtSquid: return mfWater;
+ case mtVillager: return mfPassive;
+ case mtWitch: return mfHostile;
+ case mtWolf: return mfHostile;
+ case mtZombie: return mfHostile;
+ case mtZombiePigman: return mfHostile;
+ } ;
+ ASSERT(!"Unhandled mob type");
+ return mfMaxplusone;
+}
+
+
+
+
+
+int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily)
+{
+ switch (a_MobFamily)
+ {
+ case mfHostile: return 40;
+ case mfPassive: return 40;
+ case mfAmbient: return 40;
+ case mfWater: return 400;
+ }
+ ASSERT(!"Unhandled mob family");
+ return -1;
+}
+
+
+
+
+
+cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType)
+{
+ cFastRandom Random;
+ cMonster * toReturn = NULL;
+
+ // Create the mob entity
+ switch (a_MobType)
+ {
+ case mtMagmaCube:
+ case mtSlime:
+ {
+ toReturn = new cSlime (Random.NextInt(2) + 1);
+ break;
+ }
+ case mtSkeleton:
+ {
+ // TODO: Actual detection of spawning in Nether
+ toReturn = new cSkeleton(Random.NextInt(1) == 0 ? false : true);
+ break;
+ }
+ case mtVillager:
+ {
+ int VillagerType = Random.NextInt(6);
+ if (VillagerType == 6)
+ {
+ // Give farmers a better chance of spawning
+ VillagerType = 0;
+ }
+
+ toReturn = new cVillager((cVillager::eVillagerType)VillagerType);
+ break;
+ }
+ case mtHorse:
+ {
+ // Horses take a type (species), a colour, and a style (dots, stripes, etc.)
+ int HorseType = Random.NextInt(7);
+ int HorseColor = Random.NextInt(6);
+ int HorseStyle = Random.NextInt(6);
+ int HorseTameTimes = Random.NextInt(6) + 1;
+
+ if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7))
+ {
+ // Increase chances of normal horse (zero)
+ HorseType = 0;
+ }
+
+ toReturn = new cHorse(HorseType, HorseColor, HorseStyle, HorseTameTimes);
+ break;
+ }
+
+ case mtBat: toReturn = new cBat(); break;
+ case mtBlaze: toReturn = new cBlaze(); break;
+ case mtCaveSpider: toReturn = new cCavespider(); break;
+ case mtChicken: toReturn = new cChicken(); break;
+ case mtCow: toReturn = new cCow(); break;
+ case mtCreeper: toReturn = new cCreeper(); break;
+ case mtEnderman: toReturn = new cEnderman(); break;
+ case mtGhast: toReturn = new cGhast(); break;
+ case mtMooshroom: toReturn = new cMooshroom(); break;
+ case mtOcelot: toReturn = new cOcelot(); break;
+ case mtPig: toReturn = new cPig(); break;
+ case mtSheep: toReturn = new cSheep (Random.NextInt(15)); break; // Colour parameter
+ case mtSilverfish: toReturn = new cSilverfish(); break;
+ case mtSpider: toReturn = new cSpider(); break;
+ case mtSquid: toReturn = new cSquid(); break;
+ case mtWitch: toReturn = new cWitch(); break;
+ case mtWolf: toReturn = new cWolf(); break;
+ case mtZombie: toReturn = new cZombie(false); break; // TODO: Infected zombie parameter
+ case mtZombiePigman: toReturn = new cZombiePigman(); break;
+ default:
+ {
+ ASSERT(!"Unhandled mob type whilst trying to spawn mob!");
+ }
+ }
+ return toReturn;
+}
+
+
+
+
+
+void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth)
+{
+ MTRand r1;
+ int Count = r1.randInt() % (a_Max + 1 - a_Min) + a_Min;
+ if (Count > 0)
+ {
+ a_Drops.push_back(cItem(a_Item, Count, a_ItemHealth));
+ }
+}
+
+
+
+
+
+void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
+{
+ if (!m_BurnsInDaylight)
+ {
+ return;
+ }
+
+ int RelY = (int)floor(GetPosY());
+ if ((RelY < 0) || (RelY >= cChunkDef::Height))
+ {
+ // Outside the world
+ return;
+ }
+
+ int RelX = (int)floor(GetPosX()) - GetChunkX() * cChunkDef::Width;
+ int RelZ = (int)floor(GetPosZ()) - GetChunkZ() * cChunkDef::Width;
+ if (
+ (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight
+ (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
+ (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime
+ !IsOnFire() // Not already burning
+ )
+ {
+ // Burn for 100 ticks, then decide again
+ StartBurning(100);
+ }
+}
+
+
+
+
+cMonster::eFamily cMonster::GetMobFamily(void) const
+{
+ return FamilyFromType(m_MobType);
+}
+
+
+
+
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
new file mode 100644
index 000000000..29a705d11
--- /dev/null
+++ b/src/Mobs/Monster.h
@@ -0,0 +1,195 @@
+
+#pragma once
+
+#include "../Entities/Pawn.h"
+#include "../Defines.h"
+#include "../BlockID.h"
+#include "../Item.h"
+
+
+
+
+
+class Vector3f;
+class cClientHandle;
+class cWorld;
+
+
+
+
+// tolua_begin
+class cMonster :
+ public cPawn
+{
+ typedef cPawn super;
+public:
+ /// This identifies individual monster type, as well as their network type-ID
+ enum eType
+ {
+ mtInvalidType = -1,
+
+ mtBat = E_META_SPAWN_EGG_BAT,
+ mtBlaze = E_META_SPAWN_EGG_BLAZE,
+ mtCaveSpider = E_META_SPAWN_EGG_CAVE_SPIDER,
+ mtChicken = E_META_SPAWN_EGG_CHICKEN,
+ mtCow = E_META_SPAWN_EGG_COW,
+ mtCreeper = E_META_SPAWN_EGG_CREEPER,
+ mtEnderDragon = E_META_SPAWN_EGG_ENDER_DRAGON,
+ mtEnderman = E_META_SPAWN_EGG_ENDERMAN,
+ mtGhast = E_META_SPAWN_EGG_GHAST,
+ mtGiant = E_META_SPAWN_EGG_GIANT,
+ mtHorse = E_META_SPAWN_EGG_HORSE,
+ mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM,
+ mtMagmaCube = E_META_SPAWN_EGG_MAGMA_CUBE,
+ mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM,
+ mtOcelot = E_META_SPAWN_EGG_OCELOT,
+ mtPig = E_META_SPAWN_EGG_PIG,
+ mtSheep = E_META_SPAWN_EGG_SHEEP,
+ mtSilverfish = E_META_SPAWN_EGG_SILVERFISH,
+ mtSkeleton = E_META_SPAWN_EGG_SKELETON,
+ mtSlime = E_META_SPAWN_EGG_SLIME,
+ mtSnowGolem = E_META_SPAWN_EGG_SNOW_GOLEM,
+ mtSpider = E_META_SPAWN_EGG_SPIDER,
+ mtSquid = E_META_SPAWN_EGG_SQUID,
+ mtVillager = E_META_SPAWN_EGG_VILLAGER,
+ mtWitch = E_META_SPAWN_EGG_WITCH,
+ mtWither = E_META_SPAWN_EGG_WITHER,
+ mtWolf = E_META_SPAWN_EGG_WOLF,
+ mtZombie = E_META_SPAWN_EGG_ZOMBIE,
+ mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN,
+ } ;
+
+ enum eFamily
+ {
+ mfHostile = 0, // Spider, Zombies ...
+ mfPassive = 1, // Cows, Pigs
+ mfAmbient = 2, // Bats
+ mfWater = 3, // Squid
+
+ mfMaxplusone, // Nothing. Be sure this is the last and the others are in order
+ } ;
+
+ // tolua_end
+
+ enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState;
+ enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality;
+
+ float m_SightDistance;
+
+ /** Creates the mob object.
+ * If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig()
+ * a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs , 2012_12_22))
+ * a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively
+ */
+ cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+
+ CLASS_PROTODEF(cMonster);
+
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+ virtual void KilledBy(cEntity * a_Killer) override;
+
+ virtual void MoveToPosition(const Vector3f & a_Position);
+ virtual bool ReachedDestination(void);
+
+ // tolua_begin
+ eType GetMobType(void) const {return m_MobType; }
+ eFamily GetMobFamily(void) const;
+ // tolua_end
+
+
+ const char * GetState();
+ void SetState(const AString & str);
+
+ virtual void CheckEventSeePlayer(void);
+ virtual void EventSeePlayer(cEntity * a_Player);
+ virtual cPlayer * FindClosestPlayer(); // non static is easier. also virtual so other mobs can implement their own searching algo
+
+ /// Reads the monster configuration for the specified monster name and assigns it to this object.
+ void GetMonsterConfig(const AString & a_Name);
+
+ 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 Attack(float a_Dt);
+
+ int GetAttackRate(){return (int)m_AttackRate;}
+ void SetAttackRate(int ar);
+ void SetAttackRange(float ar);
+ void SetAttackDamage(float ad);
+ void SetSightDistance(float sd);
+
+ /// Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick
+ void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; }
+
+ // Overridables to handle ageable mobs
+ virtual bool IsBaby (void) const { return false; }
+ virtual bool IsTame (void) const { return false; }
+ virtual bool IsSitting (void) const { return false; }
+
+ // tolua_begin
+
+ /// Translates MobType enum to a string, empty string if unknown
+ static AString MobTypeToString(eType a_MobType);
+
+ /// Translates MobType string to the enum, mtInvalidType if not recognized
+ static eType StringToMobType(const AString & a_MobTypeName);
+
+ /// Returns the mob family based on the type
+ static eFamily FamilyFromType(eType a_MobType);
+
+ /// Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family
+ static int GetSpawnDelay(cMonster::eFamily a_MobFamily);
+
+ // tolua_end
+
+ /** Creates a new object of the specified mob.
+ a_MobType is the type of the mob to be created
+ Asserts and returns null if mob type is not specified
+ */
+ static cMonster * NewMonsterFromType(eType a_MobType);
+
+protected:
+
+ cEntity * m_Target;
+ float m_AttackRate;
+ float idle_interval;
+
+ Vector3f m_Destination;
+ bool m_bMovingToDestination;
+ bool m_bPassiveAggressive;
+
+ float m_DestinationTime;
+
+ float m_DestroyTimer;
+ float m_Jump;
+
+ eType m_MobType;
+
+ AString m_SoundHurt;
+ AString m_SoundDeath;
+
+ float m_SeePlayerInterval;
+ float m_AttackDamage;
+ float m_AttackRange;
+ float m_AttackInterval;
+
+ bool m_BurnsInDaylight;
+
+ void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0);
+
+ void HandleDaylightBurning(cChunk & a_Chunk);
+
+} ; // tolua_export
+
+
+
+
diff --git a/src/Mobs/Mooshroom.cpp b/src/Mobs/Mooshroom.cpp
new file mode 100644
index 000000000..940e2db44
--- /dev/null
+++ b/src/Mobs/Mooshroom.cpp
@@ -0,0 +1,33 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Mooshroom.h"
+
+
+
+
+
+// TODO: Milk Cow
+
+
+
+
+
+cMooshroom::cMooshroom(void) :
+ super("Mooshroom", mtMooshroom, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3)
+{
+}
+
+
+
+
+
+void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
+ AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
+}
+
+
+
+
diff --git a/src/Mobs/Mooshroom.h b/src/Mobs/Mooshroom.h
new file mode 100644
index 000000000..73f6348b6
--- /dev/null
+++ b/src/Mobs/Mooshroom.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cMooshroom :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cMooshroom(void);
+
+ CLASS_PROTODEF(cMooshroom);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Ocelot.h b/src/Mobs/Ocelot.h
new file mode 100644
index 000000000..adb7a1f75
--- /dev/null
+++ b/src/Mobs/Ocelot.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cOcelot :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cOcelot(void) :
+ super("Ocelot", mtOcelot, "mob.cat.hitt", "mob.cat.hitt", 0.6, 0.8)
+ {
+ }
+
+ CLASS_PROTODEF(cOcelot);
+} ;
+
+
+
+
diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp
new file mode 100644
index 000000000..28de65905
--- /dev/null
+++ b/src/Mobs/PassiveAggressiveMonster.cpp
@@ -0,0 +1,38 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "PassiveAggressiveMonster.h"
+
+#include "../Entities/Player.h"
+
+
+
+
+
+cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
+{
+ m_EMPersonality = PASSIVE;
+}
+
+
+
+
+
+void cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+
+ if ((m_Target != NULL) && (m_Target->IsPlayer()))
+ {
+ cPlayer * Player = (cPlayer *) m_Target;
+ if (Player->GetGameMode() != 1)
+ {
+ m_EMState = CHASING;
+ }
+ }
+}
+
+
+
+
diff --git a/src/Mobs/PassiveAggressiveMonster.h b/src/Mobs/PassiveAggressiveMonster.h
new file mode 100644
index 000000000..2c5ef30b1
--- /dev/null
+++ b/src/Mobs/PassiveAggressiveMonster.h
@@ -0,0 +1,23 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cPassiveAggressiveMonster :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp
new file mode 100644
index 000000000..91ceb5a53
--- /dev/null
+++ b/src/Mobs/PassiveMonster.cpp
@@ -0,0 +1,59 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "PassiveMonster.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
+{
+ m_EMPersonality = PASSIVE;
+}
+
+
+
+
+
+void cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+ if ((a_TDI.Attacker != this) && (a_TDI.Attacker != NULL))
+ {
+ m_EMState = ESCAPING;
+ }
+}
+
+
+
+
+
+void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ m_SeePlayerInterval += a_Dt;
+
+ if (m_SeePlayerInterval > 1) // Check every second
+ {
+ int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally
+
+ m_SeePlayerInterval = 0.0;
+ if (rem >= 2)
+ {
+ if (m_EMState == ESCAPING)
+ {
+ CheckEventLostPlayer();
+ }
+ }
+ }
+}
+
+
+
+
+
diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h
new file mode 100644
index 000000000..14a6be6b1
--- /dev/null
+++ b/src/Mobs/PassiveMonster.h
@@ -0,0 +1,27 @@
+
+#pragma once
+
+#include "Monster.h"
+
+
+
+
+
+class cPassiveMonster :
+ public cMonster
+{
+ typedef cMonster super;
+
+public:
+ cPassiveMonster(const AString & a_ConfigName, eType 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;
+
+ /// When hit by someone, run away
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
new file mode 100644
index 000000000..0871a38a9
--- /dev/null
+++ b/src/Mobs/Pig.cpp
@@ -0,0 +1,77 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Pig.h"
+#include "../Entities/Player.h"
+#include "../World.h"
+
+
+
+
+
+cPig::cPig(void) :
+ super("Pig", mtPig, "mob.pig.say", "mob.pig.death", 0.9, 0.9),
+ m_bIsSaddled(false)
+{
+}
+
+
+
+
+
+void cPig::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_COOKED_PORKCHOP : E_ITEM_RAW_PORKCHOP);
+ if (m_bIsSaddled)
+ {
+ a_Drops.push_back(cItem(E_ITEM_SADDLE, 1));
+ }
+}
+
+
+
+
+
+void cPig::OnRightClicked(cPlayer & a_Player)
+{
+ if (m_bIsSaddled)
+ {
+ if (m_Attachee != NULL)
+ {
+ if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ {
+ // This player is already sitting in, they want out.
+ a_Player.Detach();
+ return;
+ }
+
+ if (m_Attachee->IsPlayer())
+ {
+ // Another player is already sitting in here, cannot attach
+ return;
+ }
+
+ // Detach whatever is sitting in this pig now:
+ m_Attachee->Detach();
+ }
+
+ // Attach the player to this pig
+ a_Player.AttachTo(this);
+ }
+ else if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE)
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+
+ // Set saddle state & broadcast metadata
+ m_bIsSaddled = true;
+ m_World->BroadcastEntityMetadata(*this);
+ }
+}
+
+
+
+
+
diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h
new file mode 100644
index 000000000..4fd0d8db8
--- /dev/null
+++ b/src/Mobs/Pig.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cPig :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cPig(void);
+
+ CLASS_PROTODEF(cPig);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ bool IsSaddled(void) const { return m_bIsSaddled; }
+
+private:
+
+ bool m_bIsSaddled;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp
new file mode 100644
index 000000000..bda4ccff8
--- /dev/null
+++ b/src/Mobs/Sheep.cpp
@@ -0,0 +1,62 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Sheep.h"
+#include "../BlockID.h"
+#include "../Entities/Player.h"
+#include "../World.h"
+
+
+
+
+
+cSheep::cSheep(int a_Color) :
+ super("Sheep", mtSheep, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3),
+ m_IsSheared(false),
+ m_WoolColor(a_Color)
+{
+}
+
+
+
+
+
+void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ if (!m_IsSheared)
+ {
+ a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, m_WoolColor));
+ }
+}
+
+
+
+
+
+void cSheep::OnRightClicked(cPlayer & a_Player)
+{
+ if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_SHEARS) && (!m_IsSheared))
+ {
+ m_IsSheared = true;
+ m_World->BroadcastEntityMetadata(*this);
+
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.UseEquippedItem();
+ }
+
+ cItems Drops;
+ int NumDrops = m_World->GetTickRandomNumber(2) + 1;
+ Drops.push_back(cItem(E_BLOCK_WOOL, NumDrops, m_WoolColor));
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
+ }
+ if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - a_Player.GetEquippedItem().m_ItemDamage))
+ {
+ m_WoolColor = 15 - a_Player.GetEquippedItem().m_ItemDamage;
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ m_World->BroadcastEntityMetadata(*this);
+ }
+}
diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h
new file mode 100644
index 000000000..8293a2c05
--- /dev/null
+++ b/src/Mobs/Sheep.h
@@ -0,0 +1,34 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cSheep :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cSheep(int a_Color);
+
+ CLASS_PROTODEF(cSheep);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ bool IsSheared(void) const { return m_IsSheared; }
+ int GetFurColor(void) const { return m_WoolColor; }
+
+private:
+
+ bool m_IsSheared;
+ int m_WoolColor;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Silverfish.h b/src/Mobs/Silverfish.h
new file mode 100644
index 000000000..a6e11c49d
--- /dev/null
+++ b/src/Mobs/Silverfish.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cSilverfish :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cSilverfish(void) :
+ super("Silverfish", mtSilverfish, "mob.silverfish.hit", "mob.silverfish.kill", 0.3, 0.7)
+ {
+ }
+
+ CLASS_PROTODEF(cSilverfish);
+} ;
+
+
+
+
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
new file mode 100644
index 000000000..509c2191e
--- /dev/null
+++ b/src/Mobs/Skeleton.cpp
@@ -0,0 +1,70 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Skeleton.h"
+#include "../World.h"
+
+
+
+
+cSkeleton::cSkeleton(bool IsWither) :
+ super("Skeleton", mtSkeleton, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8),
+ m_bIsWither(IsWither)
+{
+ SetBurnsInDaylight(true);
+}
+
+
+
+
+
+void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ARROW);
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_BONE);
+}
+
+
+
+
+
+void cSkeleton::MoveToPosition(const Vector3f & a_Position)
+{
+ m_Destination = a_Position;
+
+ // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement.
+ if (!IsOnFire() && m_World->GetTimeOfDay() < 13187 && m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15)
+ {
+ m_bMovingToDestination = false;
+ return;
+ }
+ m_bMovingToDestination = true;
+}
+
+
+
+
+
+void cSkeleton::Attack(float a_Dt)
+{
+ m_AttackInterval += a_Dt * m_AttackRate;
+
+ if (m_Target != NULL && m_AttackInterval > 3.0)
+ {
+ // Setting this higher gives us more wiggle room for attackrate
+ Vector3d Speed = GetLookVector() * 20;
+ Speed.y = Speed.y + 1;
+ cArrowEntity * Arrow = new cArrowEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
+ if (Arrow == NULL)
+ {
+ return;
+ }
+ if (!Arrow->Initialize(m_World))
+ {
+ delete Arrow;
+ return;
+ }
+ m_World->BroadcastSpawnEntity(*Arrow);
+ m_AttackInterval = 0.0;
+ }
+} \ No newline at end of file
diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h
new file mode 100644
index 000000000..8f31b42e1
--- /dev/null
+++ b/src/Mobs/Skeleton.h
@@ -0,0 +1,33 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cSkeleton :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cSkeleton(bool IsWither);
+
+ CLASS_PROTODEF(cSkeleton);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void MoveToPosition(const Vector3f & a_Position) override;
+ virtual void Attack(float a_Dt) override;
+ bool IsWither(void) const { return m_bIsWither; };
+
+private:
+
+ bool m_bIsWither;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp
new file mode 100644
index 000000000..19f376c21
--- /dev/null
+++ b/src/Mobs/Slime.cpp
@@ -0,0 +1,29 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Slime.h"
+
+
+
+
+
+/// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest
+cSlime::cSlime(int a_Size) :
+ super("Slime", mtSlime, "mob.slime.attack", "mob.slime.attack", 0.6 * a_Size, 0.6 * a_Size),
+ m_Size(a_Size)
+{
+}
+
+
+
+
+
+void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ // TODO: only when tiny
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_SLIMEBALL);
+}
+
+
+
+
diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h
new file mode 100644
index 000000000..782c3113f
--- /dev/null
+++ b/src/Mobs/Slime.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cSlime :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ /// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest
+ cSlime(int a_Size);
+
+ CLASS_PROTODEF(cSlime);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ int GetSize(void) const { return m_Size; }
+
+protected:
+
+ /// Size of the slime, 1 .. 3, with 1 being the smallest
+ int m_Size;
+} ;
+
+
+
+
diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp
new file mode 100644
index 000000000..9e199f87e
--- /dev/null
+++ b/src/Mobs/SnowGolem.cpp
@@ -0,0 +1,26 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "SnowGolem.h"
+
+
+
+
+
+cSnowGolem::cSnowGolem(void) :
+ super("SnowGolem", mtSnowGolem, "", "", 0.4, 1.8)
+{
+}
+
+
+
+
+
+void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 5, E_ITEM_SNOWBALL);
+}
+
+
+
+
diff --git a/src/Mobs/SnowGolem.h b/src/Mobs/SnowGolem.h
new file mode 100644
index 000000000..d1344adfd
--- /dev/null
+++ b/src/Mobs/SnowGolem.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cSnowGolem :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cSnowGolem(void);
+
+ CLASS_PROTODEF(cSnowGolem);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp
new file mode 100644
index 000000000..b19a5dcef
--- /dev/null
+++ b/src/Mobs/Spider.cpp
@@ -0,0 +1,27 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Spider.h"
+
+
+
+
+
+cSpider::cSpider(void) :
+ super("Spider", mtSpider, "mob.spider.say", "mob.spider.death", 1.4, 0.9)
+{
+}
+
+
+
+
+
+void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING);
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE);
+}
+
+
+
+
diff --git a/src/Mobs/Spider.h b/src/Mobs/Spider.h
new file mode 100644
index 000000000..51e65d028
--- /dev/null
+++ b/src/Mobs/Spider.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cSpider :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cSpider(void);
+
+ CLASS_PROTODEF(cSpider);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp
new file mode 100644
index 000000000..a311108ae
--- /dev/null
+++ b/src/Mobs/Squid.cpp
@@ -0,0 +1,56 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Squid.h"
+#include "../Vector3d.h"
+#include "../Chunk.h"
+
+
+
+
+
+cSquid::cSquid(void) :
+ super("Squid", mtSquid, "", "", 0.95, 0.95)
+{
+}
+
+
+
+
+
+void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ // Drops 0-3 Ink Sacs
+ AddRandomDropItem(a_Drops, 0, 3, E_ITEM_DYE, E_META_DYE_BLACK);
+}
+
+
+
+
+
+void cSquid::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;
+ if (!IsBlockWater(a_Chunk.GetBlock(RelX, RelY, RelZ)) && !IsOnFire())
+ {
+ // Burn for 10 ticks, then decide again
+ StartBurning(10);
+ }
+
+ super::Tick(a_Dt, a_Chunk);
+}
+
+
+
diff --git a/src/Mobs/Squid.h b/src/Mobs/Squid.h
new file mode 100644
index 000000000..ad299b95c
--- /dev/null
+++ b/src/Mobs/Squid.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cSquid :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cSquid();
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+ CLASS_PROTODEF(cSquid);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp
new file mode 100644
index 000000000..7f89fb6cc
--- /dev/null
+++ b/src/Mobs/Villager.cpp
@@ -0,0 +1,35 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Villager.h"
+#include "../World.h"
+
+
+
+
+
+cVillager::cVillager(eVillagerType VillagerType) :
+ super("Villager", mtVillager, "", "", 0.6, 1.8),
+ m_Type(VillagerType)
+{
+}
+
+
+
+
+
+void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+ if (a_TDI.Attacker->IsPlayer())
+ {
+ if (m_World->GetTickRandomNumber(5) == 3)
+ {
+ m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_VILLAGER_ANGRY);
+ }
+ }
+}
+
+
+
+
diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h
new file mode 100644
index 000000000..4cd9aaa8e
--- /dev/null
+++ b/src/Mobs/Villager.h
@@ -0,0 +1,43 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cVillager :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+
+ enum eVillagerType
+ {
+ vtFarmer = 0,
+ vtLibrarian = 1,
+ vtPriest = 2,
+ vtBlacksmith = 3,
+ vtButcher = 4,
+ vtGeneric = 5,
+ vtMax
+ } ;
+
+ cVillager(eVillagerType VillagerType);
+
+ CLASS_PROTODEF(cVillager);
+
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+ int GetVilType(void) const { return m_Type; }
+
+private:
+
+ int m_Type;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Witch.cpp b/src/Mobs/Witch.cpp
new file mode 100644
index 000000000..25d27041f
--- /dev/null
+++ b/src/Mobs/Witch.cpp
@@ -0,0 +1,32 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Witch.h"
+
+
+
+
+
+cWitch::cWitch(void) :
+ super("Witch", mtWitch, "", "", 0.6, 1.8)
+{
+}
+
+
+
+
+
+void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR);
+}
+
+
+
+
diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h
new file mode 100644
index 000000000..4e637beea
--- /dev/null
+++ b/src/Mobs/Witch.h
@@ -0,0 +1,27 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cWitch :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cWitch();
+
+ CLASS_PROTODEF(cWitch);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
+ bool IsAngry(void) const {return ((m_EMState == ATTACKING) || (m_EMState == CHASING)); }
+} ;
+
+
+
+
diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp
new file mode 100644
index 000000000..c46e0beab
--- /dev/null
+++ b/src/Mobs/Wither.cpp
@@ -0,0 +1,26 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Wither.h"
+
+
+
+
+
+cWither::cWither(void) :
+ super("Wither", mtWither, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0)
+{
+}
+
+
+
+
+
+void cWither::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 1, 1, E_ITEM_NETHER_STAR);
+}
+
+
+
+
diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h
new file mode 100644
index 000000000..56effc6bb
--- /dev/null
+++ b/src/Mobs/Wither.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cWither :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cWither(void);
+
+ CLASS_PROTODEF(cWither);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
new file mode 100644
index 000000000..c86250142
--- /dev/null
+++ b/src/Mobs/Wolf.cpp
@@ -0,0 +1,189 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Wolf.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+cWolf::cWolf(void) :
+ super("Wolf", mtWolf, "mob.wolf.hurt", "mob.wolf.death", 0.6, 0.8),
+ m_IsAngry(false),
+ m_IsTame(false),
+ m_IsSitting(false),
+ m_IsBegging(false),
+ m_OwnerName(""),
+ m_CollarColor(14)
+{
+}
+
+
+
+
+
+void cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+ if (!m_IsTame)
+ {
+ m_IsAngry = true;
+ }
+ m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face
+}
+
+
+
+
+
+void cWolf::OnRightClicked(cPlayer & a_Player)
+{
+ if (!IsTame() && !IsAngry())
+ {
+ if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_BONE)
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+
+ if (m_World->GetTickRandomNumber(7) == 0)
+ {
+ SetMaxHealth(20);
+ SetIsTame(true);
+ SetOwner(a_Player.GetName());
+ m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMED);
+ }
+ else
+ {
+ m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMING);
+ }
+ }
+ }
+ else if (IsTame())
+ {
+ if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog?
+ {
+ if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE)
+ {
+ SetCollarColor(15 - a_Player.GetEquippedItem().m_ItemDamage);
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ }
+ else if (IsSitting())
+ {
+ SetIsSitting(false);
+ }
+ else
+ {
+ SetIsSitting(true);
+ }
+ }
+ }
+
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ if (!IsAngry())
+ {
+ cMonster::Tick(a_Dt, a_Chunk);
+ }
+ else
+ {
+ super::Tick(a_Dt, a_Chunk);
+ }
+
+ if (IsSitting())
+ {
+ m_bMovingToDestination = false;
+ }
+
+ cPlayer * a_Closest_Player = FindClosestPlayer();
+ if (a_Closest_Player != NULL)
+ {
+ switch (a_Closest_Player->GetEquippedItem().m_ItemType)
+ {
+ case E_ITEM_BONE:
+ case E_ITEM_RAW_BEEF:
+ case E_ITEM_STEAK:
+ case E_ITEM_RAW_CHICKEN:
+ case E_ITEM_COOKED_CHICKEN:
+ case E_ITEM_ROTTEN_FLESH:
+ {
+ if (!IsBegging())
+ {
+ SetIsBegging(true);
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ Vector3f a_NewDestination = a_Closest_Player->GetPosition();
+ a_NewDestination.y = a_NewDestination.y + 1; // Look at the head of the player, not his feet.
+ m_Destination = Vector3f(a_NewDestination);
+ m_bMovingToDestination = false;
+ break;
+ }
+ default:
+ {
+ if (IsBegging())
+ {
+ SetIsBegging(false);
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ }
+ }
+ }
+
+ if (IsTame())
+ {
+ TickFollowPlayer();
+ }
+}
+
+
+
+
+
+void cWolf::TickFollowPlayer()
+{
+ class cCallback :
+ public cPlayerListCallback
+ {
+ virtual bool Item(cPlayer * a_Player) override
+ {
+ OwnerPos = a_Player->GetPosition();
+ return false;
+ }
+ public:
+ Vector3f OwnerPos;
+ } Callback;
+ if (m_World->DoWithPlayer(m_OwnerName, Callback))
+ {
+ // The player is present in the world, follow them:
+ double Distance = (Callback.OwnerPos - GetPosition()).Length();
+ if (Distance < 3)
+ {
+ m_bMovingToDestination = false;
+ }
+ else if ((Distance > 30) && (!IsSitting()))
+ {
+ TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z);
+ }
+ else
+ {
+ m_Destination = Callback.OwnerPos;
+ }
+ }
+}
+
+
+
+
diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h
new file mode 100644
index 000000000..040e2cf7a
--- /dev/null
+++ b/src/Mobs/Wolf.h
@@ -0,0 +1,54 @@
+
+#pragma once
+
+#include "PassiveAggressiveMonster.h"
+#include "../Entities/Entity.h"
+
+
+
+
+
+class cWolf :
+ public cPassiveAggressiveMonster
+{
+ typedef cPassiveAggressiveMonster super;
+
+public:
+ cWolf(void);
+
+ CLASS_PROTODEF(cWolf);
+
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void TickFollowPlayer();
+
+ // Get functions
+ bool IsSitting (void) const { return m_IsSitting; }
+ bool IsTame (void) const { return m_IsTame; }
+ bool IsBegging (void) const { return m_IsBegging; }
+ bool IsAngry (void) const { return m_IsAngry; }
+ AString GetOwner (void) const { return m_OwnerName; }
+ int GetCollarColor(void) const { return m_CollarColor; }
+
+ // Set functions
+ void SetIsSitting (bool a_IsSitting) { m_IsSitting = a_IsSitting; }
+ void SetIsTame (bool a_IsTame) { m_IsTame = a_IsTame; }
+ void SetIsBegging (bool a_IsBegging) { m_IsBegging = a_IsBegging; }
+ void SetIsAngry (bool a_IsAngry) { m_IsAngry = a_IsAngry; }
+ void SetOwner (AString a_NewOwner) { m_OwnerName = a_NewOwner; }
+ void SetCollarColor(int a_CollarColor) { m_CollarColor = a_CollarColor; }
+
+protected:
+
+ bool m_IsSitting;
+ bool m_IsTame;
+ bool m_IsBegging;
+ bool m_IsAngry;
+ AString m_OwnerName;
+ int m_CollarColor;
+} ;
+
+
+
+
diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp
new file mode 100644
index 000000000..a485d2b55
--- /dev/null
+++ b/src/Mobs/Zombie.cpp
@@ -0,0 +1,47 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Zombie.h"
+#include "../World.h"
+#include "../LineBlockTracer.h"
+
+
+
+
+cZombie::cZombie(bool IsVillagerZombie) :
+ super("Zombie", mtZombie, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8),
+ m_bIsConverting(false),
+ m_bIsVillagerZombie(IsVillagerZombie)
+{
+ SetBurnsInDaylight(true);
+}
+
+
+
+
+
+void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ROTTEN_FLESH);
+
+ // TODO: Rare drops
+}
+
+
+
+
+
+void cZombie::MoveToPosition(const Vector3f & a_Position)
+{
+ m_Destination = a_Position;
+
+ // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement.
+ if ((m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15) && (m_World->GetTimeOfDay() < 13187) && !IsOnFire())
+ {
+ m_bMovingToDestination = false;
+ return;
+ }
+ m_bMovingToDestination = true;
+}
+
+
diff --git a/src/Mobs/Zombie.h b/src/Mobs/Zombie.h
new file mode 100644
index 000000000..7e14fe42f
--- /dev/null
+++ b/src/Mobs/Zombie.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cZombie :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cZombie(bool IsVillagerZombie);
+
+ CLASS_PROTODEF(cZombie);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void MoveToPosition(const Vector3f & a_Position) override;
+
+ bool IsVillagerZombie(void) const {return m_bIsVillagerZombie; }
+ bool IsConverting(void) const {return m_bIsConverting; }
+
+private:
+
+ bool m_bIsVillagerZombie, m_bIsConverting;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Zombiepigman.cpp b/src/Mobs/Zombiepigman.cpp
new file mode 100644
index 000000000..6ac89ed4c
--- /dev/null
+++ b/src/Mobs/Zombiepigman.cpp
@@ -0,0 +1,45 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Zombiepigman.h"
+#include "../World.h"
+
+
+
+
+
+cZombiePigman::cZombiePigman(void) :
+ super("ZombiePigman", mtZombiePigman, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8)
+{
+}
+
+
+
+
+
+void cZombiePigman::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ROTTEN_FLESH);
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GOLD_NUGGET);
+
+ // TODO: Rare drops
+}
+
+
+
+
+
+void cZombiePigman::KilledBy(cEntity * a_Killer)
+{
+ super::KilledBy(a_Killer);
+
+ if ((a_Killer != NULL) && (a_Killer->IsPlayer()))
+ {
+ // TODO: Anger all nearby zombie pigmen
+ // TODO: In vanilla, if one player angers ZPs, do they attack any nearby player, or only that one attacker?
+ }
+}
+
+
+
+
diff --git a/src/Mobs/Zombiepigman.h b/src/Mobs/Zombiepigman.h
new file mode 100644
index 000000000..67991d56a
--- /dev/null
+++ b/src/Mobs/Zombiepigman.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "PassiveAggressiveMonster.h"
+
+
+
+
+
+class cZombiePigman :
+ public cPassiveAggressiveMonster
+{
+ typedef cPassiveAggressiveMonster super;
+
+public:
+ cZombiePigman(void);
+
+ CLASS_PROTODEF(cZombiePigman);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void KilledBy(cEntity * a_Killer) override;
+} ;
+
+
+
+
diff --git a/src/MonsterConfig.cpp b/src/MonsterConfig.cpp
new file mode 100644
index 000000000..6165606f0
--- /dev/null
+++ b/src/MonsterConfig.cpp
@@ -0,0 +1,103 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "MonsterConfig.h"
+#include "Mobs/Monster.h"
+#include "inifile/iniFile.h"
+
+
+
+
+
+struct cMonsterConfig::sAttributesStruct
+{
+ AString m_Name;
+ double m_SightDistance;
+ double m_AttackDamage;
+ double m_AttackRange;
+ double m_AttackRate;
+ int m_MaxHealth;
+};
+
+
+
+
+
+struct cMonsterConfig::sMonsterConfigState
+{
+ AString MonsterTypes;
+ std::list< sAttributesStruct > AttributesList;
+};
+
+
+
+
+
+cMonsterConfig::cMonsterConfig(void)
+ : m_pState( new sMonsterConfigState )
+{
+ Initialize();
+}
+
+
+
+
+
+cMonsterConfig::~cMonsterConfig()
+{
+ delete m_pState;
+}
+
+
+
+
+
+void cMonsterConfig::Initialize()
+{
+ cIniFile MonstersIniFile;
+
+ if (!MonstersIniFile.ReadFile("monsters.ini"))
+ {
+ LOGWARNING("%s: Cannot read monsters.ini file, monster attributes not available", __FUNCTION__);
+ return;
+ }
+
+ for (int i = (int)MonstersIniFile.GetNumKeys(); i >= 0; i--)
+ {
+ sAttributesStruct Attributes;
+ AString Name = MonstersIniFile.GetKeyName(i);
+ Attributes.m_Name = Name;
+ Attributes.m_AttackDamage = MonstersIniFile.GetValueF(Name, "AttackDamage", 0);
+ Attributes.m_AttackRange = MonstersIniFile.GetValueF(Name, "AttackRange", 0);
+ Attributes.m_SightDistance = MonstersIniFile.GetValueF(Name, "SightDistance", 0);
+ Attributes.m_AttackRate = MonstersIniFile.GetValueF(Name, "AttackRate", 0);
+ Attributes.m_MaxHealth = MonstersIniFile.GetValueI(Name, "MaxHealth", 1);
+ m_pState->AttributesList.push_front(Attributes);
+ } // for i - SplitList[]
+}
+
+
+
+
+
+void cMonsterConfig::AssignAttributes(cMonster * a_Monster, const AString & a_Name)
+{
+ std::list<sAttributesStruct>::const_iterator itr;
+ for (itr = m_pState->AttributesList.begin(); itr != m_pState->AttributesList.end(); ++itr)
+ {
+ if (itr->m_Name.compare(a_Name) == 0)
+ {
+ a_Monster->SetAttackDamage ((float)itr->m_AttackDamage);
+ a_Monster->SetAttackRange ((float)itr->m_AttackRange);
+ a_Monster->SetSightDistance((float)itr->m_SightDistance);
+ a_Monster->SetAttackRate ((int)itr->m_AttackRate);
+ a_Monster->SetMaxHealth (itr->m_MaxHealth);
+ return;
+ }
+ } // for itr - m_pState->AttributesList[]
+}
+
+
+
+
+
diff --git a/src/MonsterConfig.h b/src/MonsterConfig.h
new file mode 100644
index 000000000..371d324c2
--- /dev/null
+++ b/src/MonsterConfig.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+
+
+
+
+// fwd:
+class cMonster;
+
+
+
+
+
+class cMonsterConfig
+{
+public:
+ cMonsterConfig(void);
+ ~cMonsterConfig();
+
+ void AssignAttributes(cMonster * a_Monster, const AString & a_Name);
+
+private:
+ struct sAttributesStruct;
+ struct sMonsterConfigState;
+ sMonsterConfigState* m_pState;
+ void Initialize();
+} ;
+
+
+
+
diff --git a/src/Noise.cpp b/src/Noise.cpp
new file mode 100644
index 000000000..729641961
--- /dev/null
+++ b/src/Noise.cpp
@@ -0,0 +1,951 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Noise.h"
+
+
+
+
+
+#if NOISE_USE_SSE
+ #include <smmintrin.h> //_mm_mul_epi32
+#endif
+
+#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x))
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Globals:
+
+void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase)
+{
+ const int BUF_SIZE = 512;
+ ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed
+
+ // Save in XY cuts:
+ cFile f1;
+ if (f1.Open(Printf("%s_XY (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite))
+ {
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int y = 0; y < a_SizeY; y++)
+ {
+ int idx = y * a_SizeX + z * a_SizeX * a_SizeY;
+ unsigned char buf[BUF_SIZE];
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
+ }
+ f1.Write(buf, a_SizeX);
+ } // for y
+ unsigned char buf[BUF_SIZE];
+ memset(buf, 0, a_SizeX);
+ f1.Write(buf, a_SizeX);
+ } // for z
+ } // if (XY file open)
+
+ cFile f2;
+ if (f2.Open(Printf("%s_XZ (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite))
+ {
+ for (int y = 0; y < a_SizeY; y++)
+ {
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int idx = y * a_SizeX + z * a_SizeX * a_SizeY;
+ unsigned char buf[BUF_SIZE];
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
+ }
+ f2.Write(buf, a_SizeX);
+ } // for z
+ unsigned char buf[BUF_SIZE];
+ memset(buf, 0, a_SizeX);
+ f2.Write(buf, a_SizeX);
+ } // for y
+ } // if (XZ file open)
+}
+
+
+
+
+
+void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase)
+{
+ const int BUF_SIZE = 512;
+ ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed
+
+ cFile f1;
+ if (f1.Open(Printf("%s (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite))
+ {
+ for (int y = 0; y < a_SizeY; y++)
+ {
+ int idx = y * a_SizeX;
+ unsigned char buf[BUF_SIZE];
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
+ }
+ f1.Write(buf, a_SizeX);
+ } // for y
+ } // if (file open)
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCubicCell2D:
+
+class cCubicCell2D
+{
+public:
+ cCubicCell2D(
+ const cNoise & a_Noise, ///< Noise to use for generating the random values
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values
+ const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values
+ );
+
+ /// Uses current m_WorkRnds[] to generate part of the array
+ void Generate(
+ int a_FromX, int a_ToX,
+ int a_FromY, int a_ToY
+ );
+
+ /// Initializes m_WorkRnds[] with the specified Floor values
+ void InitWorkRnds(int a_FloorX, int a_FloorY);
+
+ /// Updates m_WorkRnds[] for the new Floor values.
+ void Move(int a_NewFloorX, int a_NewFloorY);
+
+protected:
+ typedef NOISE_DATATYPE Workspace[4][4];
+
+ const cNoise & m_Noise;
+
+ Workspace * m_WorkRnds; ///< The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering)
+ Workspace m_Workspace1; ///< Buffer 1 for workspace doublebuffering, used in Move()
+ Workspace m_Workspace2; ///< Buffer 2 for workspace doublebuffering, used in Move()
+ int m_CurFloorX;
+ int m_CurFloorY;
+
+ NOISE_DATATYPE * m_Array;
+ int m_SizeX, m_SizeY;
+ const NOISE_DATATYPE * m_FracX;
+ const NOISE_DATATYPE * m_FracY;
+} ;
+
+
+
+
+
+cCubicCell2D::cCubicCell2D(
+ const cNoise & a_Noise, ///< Noise to use for generating the random values
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values
+ const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values
+) :
+ m_Noise(a_Noise),
+ m_WorkRnds(&m_Workspace1),
+ m_Array(a_Array),
+ m_SizeX(a_SizeX),
+ m_SizeY(a_SizeY),
+ m_FracX(a_FracX),
+ m_FracY(a_FracY)
+{
+}
+
+
+
+
+
+void cCubicCell2D::Generate(
+ int a_FromX, int a_ToX,
+ int a_FromY, int a_ToY
+)
+{
+ for (int y = a_FromY; y < a_ToY; y++)
+ {
+ NOISE_DATATYPE Interp[4];
+ NOISE_DATATYPE FracY = m_FracY[y];
+ Interp[0] = cNoise::CubicInterpolate((*m_WorkRnds)[0][0], (*m_WorkRnds)[0][1], (*m_WorkRnds)[0][2], (*m_WorkRnds)[0][3], FracY);
+ Interp[1] = cNoise::CubicInterpolate((*m_WorkRnds)[1][0], (*m_WorkRnds)[1][1], (*m_WorkRnds)[1][2], (*m_WorkRnds)[1][3], FracY);
+ Interp[2] = cNoise::CubicInterpolate((*m_WorkRnds)[2][0], (*m_WorkRnds)[2][1], (*m_WorkRnds)[2][2], (*m_WorkRnds)[2][3], FracY);
+ Interp[3] = cNoise::CubicInterpolate((*m_WorkRnds)[3][0], (*m_WorkRnds)[3][1], (*m_WorkRnds)[3][2], (*m_WorkRnds)[3][3], FracY);
+ int idx = y * m_SizeX + a_FromX;
+ for (int x = a_FromX; x < a_ToX; x++)
+ {
+ m_Array[idx++] = cNoise::CubicInterpolate(Interp[0], Interp[1], Interp[2], Interp[3], m_FracX[x]);
+ } // for x
+ } // for y
+}
+
+
+
+
+
+void cCubicCell2D::InitWorkRnds(int a_FloorX, int a_FloorY)
+{
+ m_CurFloorX = a_FloorX;
+ m_CurFloorY = a_FloorY;
+ for (int x = 0; x < 4; x++)
+ {
+ int cx = a_FloorX + x - 1;
+ for (int y = 0; y < 4; y++)
+ {
+ int cy = a_FloorY + y - 1;
+ (*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy);
+ }
+ }
+}
+
+
+
+
+
+void cCubicCell2D::Move(int a_NewFloorX, int a_NewFloorY)
+{
+ // Swap the doublebuffer:
+ int OldFloorX = m_CurFloorX;
+ int OldFloorY = m_CurFloorY;
+ Workspace * OldWorkRnds = m_WorkRnds;
+ m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1;
+
+ // Reuse as much of the old workspace as possible:
+ int DiffX = OldFloorX - a_NewFloorX;
+ int DiffY = OldFloorY - a_NewFloorY;
+ for (int x = 0; x < 4; x++)
+ {
+ int cx = a_NewFloorX + x - 1;
+ int OldX = x - DiffX; // Where would this X be in the old grid?
+ for (int y = 0; y < 4; y++)
+ {
+ int cy = a_NewFloorY + y - 1;
+ int OldY = y - DiffY; // Where would this Y be in the old grid?
+ if ((OldX >= 0) && (OldX < 4) && (OldY >= 0) && (OldY < 4))
+ {
+ (*m_WorkRnds)[x][y] = (*OldWorkRnds)[OldX][OldY];
+ }
+ else
+ {
+ (*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy);
+ }
+ }
+ }
+ m_CurFloorX = a_NewFloorX;
+ m_CurFloorY = a_NewFloorY;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCubicCell3D:
+
+class cCubicCell3D
+{
+public:
+ cCubicCell3D(
+ const cNoise & a_Noise, ///< Noise to use for generating the random values
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values
+ const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values
+ const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values
+ );
+
+ /// Uses current m_WorkRnds[] to generate part of the array
+ void Generate(
+ int a_FromX, int a_ToX,
+ int a_FromY, int a_ToY,
+ int a_FromZ, int a_ToZ
+ );
+
+ /// Initializes m_WorkRnds[] with the specified Floor values
+ void InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ);
+
+ /// Updates m_WorkRnds[] for the new Floor values.
+ void Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ);
+
+protected:
+ typedef NOISE_DATATYPE Workspace[4][4][4];
+
+ const cNoise & m_Noise;
+
+ Workspace * m_WorkRnds; ///< The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering)
+ Workspace m_Workspace1; ///< Buffer 1 for workspace doublebuffering, used in Move()
+ Workspace m_Workspace2; ///< Buffer 2 for workspace doublebuffering, used in Move()
+ int m_CurFloorX;
+ int m_CurFloorY;
+ int m_CurFloorZ;
+
+ NOISE_DATATYPE * m_Array;
+ int m_SizeX, m_SizeY, m_SizeZ;
+ const NOISE_DATATYPE * m_FracX;
+ const NOISE_DATATYPE * m_FracY;
+ const NOISE_DATATYPE * m_FracZ;
+} ;
+
+
+
+
+
+cCubicCell3D::cCubicCell3D(
+ const cNoise & a_Noise, ///< Noise to use for generating the random values
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values
+ const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values
+ const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values
+) :
+ m_Noise(a_Noise),
+ m_WorkRnds(&m_Workspace1),
+ m_Array(a_Array),
+ m_SizeX(a_SizeX),
+ m_SizeY(a_SizeY),
+ m_SizeZ(a_SizeZ),
+ m_FracX(a_FracX),
+ m_FracY(a_FracY),
+ m_FracZ(a_FracZ)
+{
+}
+
+
+
+
+
+void cCubicCell3D::Generate(
+ int a_FromX, int a_ToX,
+ int a_FromY, int a_ToY,
+ int a_FromZ, int a_ToZ
+)
+{
+ for (int z = a_FromZ; z < a_ToZ; z++)
+ {
+ int idxZ = z * m_SizeX * m_SizeY;
+ NOISE_DATATYPE Interp2[4][4];
+ NOISE_DATATYPE FracZ = m_FracZ[z];
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ Interp2[x][y] = cNoise::CubicInterpolate((*m_WorkRnds)[x][y][0], (*m_WorkRnds)[x][y][1], (*m_WorkRnds)[x][y][2], (*m_WorkRnds)[x][y][3], FracZ);
+ }
+ }
+ for (int y = a_FromY; y < a_ToY; y++)
+ {
+ NOISE_DATATYPE Interp[4];
+ NOISE_DATATYPE FracY = m_FracY[y];
+ Interp[0] = cNoise::CubicInterpolate(Interp2[0][0], Interp2[0][1], Interp2[0][2], Interp2[0][3], FracY);
+ Interp[1] = cNoise::CubicInterpolate(Interp2[1][0], Interp2[1][1], Interp2[1][2], Interp2[1][3], FracY);
+ Interp[2] = cNoise::CubicInterpolate(Interp2[2][0], Interp2[2][1], Interp2[2][2], Interp2[2][3], FracY);
+ Interp[3] = cNoise::CubicInterpolate(Interp2[3][0], Interp2[3][1], Interp2[3][2], Interp2[3][3], FracY);
+ int idx = idxZ + y * m_SizeX + a_FromX;
+ for (int x = a_FromX; x < a_ToX; x++)
+ {
+ m_Array[idx++] = cNoise::CubicInterpolate(Interp[0], Interp[1], Interp[2], Interp[3], m_FracX[x]);
+ } // for x
+ } // for y
+ } // for z
+}
+
+
+
+
+
+void cCubicCell3D::InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ)
+{
+ m_CurFloorX = a_FloorX;
+ m_CurFloorY = a_FloorY;
+ m_CurFloorZ = a_FloorZ;
+ for (int x = 0; x < 4; x++)
+ {
+ int cx = a_FloorX + x - 1;
+ for (int y = 0; y < 4; y++)
+ {
+ int cy = a_FloorY + y - 1;
+ for (int z = 0; z < 4; z++)
+ {
+ int cz = a_FloorZ + z - 1;
+ (*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz);
+ }
+ }
+ }
+}
+
+
+
+
+
+void cCubicCell3D::Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ)
+{
+ // Swap the doublebuffer:
+ int OldFloorX = m_CurFloorX;
+ int OldFloorY = m_CurFloorY;
+ int OldFloorZ = m_CurFloorZ;
+ Workspace * OldWorkRnds = m_WorkRnds;
+ m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1;
+
+ // Reuse as much of the old workspace as possible:
+ int DiffX = OldFloorX - a_NewFloorX;
+ int DiffY = OldFloorY - a_NewFloorY;
+ int DiffZ = OldFloorZ - a_NewFloorZ;
+ for (int x = 0; x < 4; x++)
+ {
+ int cx = a_NewFloorX + x - 1;
+ int OldX = x - DiffX; // Where would this X be in the old grid?
+ for (int y = 0; y < 4; y++)
+ {
+ int cy = a_NewFloorY + y - 1;
+ int OldY = y - DiffY; // Where would this Y be in the old grid?
+ for (int z = 0; z < 4; z++)
+ {
+ int cz = a_NewFloorZ + z - 1;
+ int OldZ = z - DiffZ;
+ if ((OldX >= 0) && (OldX < 4) && (OldY >= 0) && (OldY < 4) && (OldZ >= 0) && (OldZ < 4))
+ {
+ (*m_WorkRnds)[x][y][z] = (*OldWorkRnds)[OldX][OldY][OldZ];
+ }
+ else
+ {
+ (*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz);
+ }
+ } // for z
+ } // for y
+ } // for x
+ m_CurFloorX = a_NewFloorX;
+ m_CurFloorY = a_NewFloorY;
+ m_CurFloorZ = a_NewFloorZ;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cNoise:
+
+cNoise::cNoise(unsigned int a_Seed) :
+ m_Seed(a_Seed)
+{
+}
+
+
+
+
+
+cNoise::cNoise(const cNoise & a_Noise) :
+ m_Seed(a_Noise.m_Seed)
+{
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::LinearNoise1D(NOISE_DATATYPE a_X) const
+{
+ int BaseX = FAST_FLOOR(a_X);
+ NOISE_DATATYPE FracX = a_X - BaseX;
+ return LinearInterpolate(IntNoise1D(BaseX), IntNoise1D(BaseX + 1), FracX);
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::CosineNoise1D(NOISE_DATATYPE a_X) const
+{
+ int BaseX = FAST_FLOOR(a_X);
+ NOISE_DATATYPE FracX = a_X - BaseX;
+ return CosineInterpolate(IntNoise1D(BaseX), IntNoise1D(BaseX + 1), FracX);
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::CubicNoise1D(NOISE_DATATYPE a_X) const
+{
+ int BaseX = FAST_FLOOR(a_X);
+ NOISE_DATATYPE FracX = a_X - BaseX;
+ return CubicInterpolate(IntNoise1D(BaseX - 1), IntNoise1D(BaseX), IntNoise1D(BaseX + 1), IntNoise1D(BaseX + 2), FracX);
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::SmoothNoise1D(int a_X) const
+{
+ return IntNoise1D(a_X) / 2 + IntNoise1D(a_X - 1) / 4 + IntNoise1D(a_X + 1) / 4;
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::CubicNoise2D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const
+{
+ const int BaseX = FAST_FLOOR(a_X);
+ const int BaseY = FAST_FLOOR(a_Y);
+
+ const NOISE_DATATYPE points[4][4] =
+ {
+ IntNoise2D(BaseX - 1, BaseY - 1), IntNoise2D(BaseX, BaseY - 1), IntNoise2D(BaseX + 1, BaseY - 1), IntNoise2D(BaseX + 2, BaseY - 1),
+ IntNoise2D(BaseX - 1, BaseY), IntNoise2D(BaseX, BaseY), IntNoise2D(BaseX + 1, BaseY), IntNoise2D(BaseX + 2, BaseY),
+ IntNoise2D(BaseX - 1, BaseY + 1), IntNoise2D(BaseX, BaseY + 1), IntNoise2D(BaseX + 1, BaseY + 1), IntNoise2D(BaseX + 2, BaseY + 1),
+ IntNoise2D(BaseX - 1, BaseY + 2), IntNoise2D(BaseX, BaseY + 2), IntNoise2D(BaseX + 1, BaseY + 2), IntNoise2D(BaseX + 2, BaseY + 2),
+ };
+
+ const NOISE_DATATYPE FracX = a_X - BaseX;
+ const NOISE_DATATYPE interp1 = CubicInterpolate(points[0][0], points[0][1], points[0][2], points[0][3], FracX);
+ const NOISE_DATATYPE interp2 = CubicInterpolate(points[1][0], points[1][1], points[1][2], points[1][3], FracX);
+ const NOISE_DATATYPE interp3 = CubicInterpolate(points[2][0], points[2][1], points[2][2], points[2][3], FracX);
+ const NOISE_DATATYPE interp4 = CubicInterpolate(points[3][0], points[3][1], points[3][2], points[3][3], FracX);
+
+
+ const NOISE_DATATYPE FracY = a_Y - BaseY;
+ return CubicInterpolate(interp1, interp2, interp3, interp4, FracY);
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const
+{
+ const int BaseX = FAST_FLOOR(a_X);
+ const int BaseY = FAST_FLOOR(a_Y);
+ const int BaseZ = FAST_FLOOR(a_Z);
+
+ const NOISE_DATATYPE points1[4][4] = {
+ IntNoise3D(BaseX - 1, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY - 1, BaseZ - 1),
+ IntNoise3D(BaseX - 1, BaseY, BaseZ - 1), IntNoise3D(BaseX, BaseY, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY, BaseZ - 1),
+ IntNoise3D(BaseX - 1, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY + 1, BaseZ - 1),
+ IntNoise3D(BaseX - 1, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY + 2, BaseZ - 1),
+ };
+
+ const NOISE_DATATYPE FracX = (a_X) - BaseX;
+ const NOISE_DATATYPE x1interp1 = CubicInterpolate( points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX );
+ const NOISE_DATATYPE x1interp2 = CubicInterpolate( points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX );
+ const NOISE_DATATYPE x1interp3 = CubicInterpolate( points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX );
+ const NOISE_DATATYPE x1interp4 = CubicInterpolate( points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX );
+
+ const NOISE_DATATYPE points2[4][4] = {
+ IntNoise3D( BaseX-1, BaseY-1, BaseZ ), IntNoise3D( BaseX, BaseY-1, BaseZ ), IntNoise3D( BaseX+1, BaseY-1, BaseZ ), IntNoise3D( BaseX+2, BaseY-1, BaseZ ),
+ IntNoise3D( BaseX-1, BaseY, BaseZ ), IntNoise3D( BaseX, BaseY, BaseZ ), IntNoise3D( BaseX+1, BaseY, BaseZ ), IntNoise3D( BaseX+2, BaseY, BaseZ ),
+ IntNoise3D( BaseX-1, BaseY+1, BaseZ ), IntNoise3D( BaseX, BaseY+1, BaseZ ), IntNoise3D( BaseX+1, BaseY+1, BaseZ ), IntNoise3D( BaseX+2, BaseY+1, BaseZ ),
+ IntNoise3D( BaseX-1, BaseY+2, BaseZ ), IntNoise3D( BaseX, BaseY+2, BaseZ ), IntNoise3D( BaseX+1, BaseY+2, BaseZ ), IntNoise3D( BaseX+2, BaseY+2, BaseZ ),
+ };
+
+ const NOISE_DATATYPE x2interp1 = CubicInterpolate( points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX );
+ const NOISE_DATATYPE x2interp2 = CubicInterpolate( points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX );
+ const NOISE_DATATYPE x2interp3 = CubicInterpolate( points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX );
+ const NOISE_DATATYPE x2interp4 = CubicInterpolate( points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX );
+
+ const NOISE_DATATYPE points3[4][4] = {
+ IntNoise3D( BaseX-1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+1 ),
+ IntNoise3D( BaseX-1, BaseY, BaseZ+1 ), IntNoise3D( BaseX, BaseY, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY, BaseZ+1 ),
+ IntNoise3D( BaseX-1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+1 ),
+ IntNoise3D( BaseX-1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+1 ),
+ };
+
+ const NOISE_DATATYPE x3interp1 = CubicInterpolate( points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX );
+ const NOISE_DATATYPE x3interp2 = CubicInterpolate( points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX );
+ const NOISE_DATATYPE x3interp3 = CubicInterpolate( points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX );
+ const NOISE_DATATYPE x3interp4 = CubicInterpolate( points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX );
+
+ const NOISE_DATATYPE points4[4][4] = {
+ IntNoise3D( BaseX-1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+2 ),
+ IntNoise3D( BaseX-1, BaseY, BaseZ+2 ), IntNoise3D( BaseX, BaseY, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY, BaseZ+2 ),
+ IntNoise3D( BaseX-1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+2 ),
+ IntNoise3D( BaseX-1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+2 ),
+ };
+
+ const NOISE_DATATYPE x4interp1 = CubicInterpolate( points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX );
+ const NOISE_DATATYPE x4interp2 = CubicInterpolate( points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX );
+ const NOISE_DATATYPE x4interp3 = CubicInterpolate( points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX );
+ const NOISE_DATATYPE x4interp4 = CubicInterpolate( points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX );
+
+ const NOISE_DATATYPE FracY = (a_Y) - BaseY;
+ const NOISE_DATATYPE yinterp1 = CubicInterpolate( x1interp1, x1interp2, x1interp3, x1interp4, FracY );
+ const NOISE_DATATYPE yinterp2 = CubicInterpolate( x2interp1, x2interp2, x2interp3, x2interp4, FracY );
+ const NOISE_DATATYPE yinterp3 = CubicInterpolate( x3interp1, x3interp2, x3interp3, x3interp4, FracY );
+ const NOISE_DATATYPE yinterp4 = CubicInterpolate( x4interp1, x4interp2, x4interp3, x4interp4, FracY );
+
+ const NOISE_DATATYPE FracZ = (a_Z) - BaseZ;
+ return CubicInterpolate( yinterp1, yinterp2, yinterp3, yinterp4, FracZ );
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCubicNoise:
+
+#ifdef _DEBUG
+ int cCubicNoise::m_NumSingleX = 0;
+ int cCubicNoise::m_NumSingleXY = 0;
+ int cCubicNoise::m_NumSingleY = 0;
+ int cCubicNoise::m_NumCalls = 0;
+#endif // _DEBUG
+
+cCubicNoise::cCubicNoise(int a_Seed) :
+ m_Noise(a_Seed)
+{
+}
+
+
+
+
+
+void cCubicNoise::Generate2D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Size of the array (num doubles), in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
+) const
+{
+ ASSERT(a_SizeX < MAX_SIZE);
+ ASSERT(a_SizeY < MAX_SIZE);
+ ASSERT(a_StartX < a_EndX);
+ ASSERT(a_StartY < a_EndY);
+
+ // Calculate the integral and fractional parts of each coord:
+ int FloorX[MAX_SIZE];
+ int FloorY[MAX_SIZE];
+ NOISE_DATATYPE FracX[MAX_SIZE];
+ NOISE_DATATYPE FracY[MAX_SIZE];
+ int SameX[MAX_SIZE];
+ int SameY[MAX_SIZE];
+ int NumSameX, NumSameY;
+ CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX);
+ CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY);
+
+ cCubicCell2D Cell(m_Noise, a_Array, a_SizeX, a_SizeY, FracX, FracY);
+
+ Cell.InitWorkRnds(FloorX[0], FloorY[0]);
+
+ #ifdef _DEBUG
+ // Statistics on the noise-space coords:
+ if (NumSameX == 1)
+ {
+ m_NumSingleX++;
+ if (NumSameY == 1)
+ {
+ m_NumSingleXY++;
+ }
+ }
+ if (NumSameY == 1)
+ {
+ m_NumSingleY++;
+ }
+ m_NumCalls++;
+ #endif // _DEBUG
+
+ // Calculate query values using Cell:
+ int FromY = 0;
+ for (int y = 0; y < NumSameY; y++)
+ {
+ int ToY = FromY + SameY[y];
+ int FromX = 0;
+ int CurFloorY = FloorY[FromY];
+ for (int x = 0; x < NumSameX; x++)
+ {
+ int ToX = FromX + SameX[x];
+ Cell.Generate(FromX, ToX, FromY, ToY);
+ Cell.Move(FloorX[ToX], CurFloorY);
+ FromX = ToX;
+ }
+ Cell.Move(FloorX[0], FloorY[ToY]);
+ FromY = ToY;
+ }
+}
+
+
+
+
+
+void cCubicNoise::Generate3D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Size of the array (num doubles), in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Y direction
+) const
+{
+ ASSERT(a_SizeX < MAX_SIZE);
+ ASSERT(a_SizeY < MAX_SIZE);
+ ASSERT(a_SizeZ < MAX_SIZE);
+ ASSERT(a_StartX < a_EndX);
+ ASSERT(a_StartY < a_EndY);
+ ASSERT(a_StartZ < a_EndZ);
+
+ // Calculate the integral and fractional parts of each coord:
+ int FloorX[MAX_SIZE];
+ int FloorY[MAX_SIZE];
+ int FloorZ[MAX_SIZE];
+ NOISE_DATATYPE FracX[MAX_SIZE];
+ NOISE_DATATYPE FracY[MAX_SIZE];
+ NOISE_DATATYPE FracZ[MAX_SIZE];
+ int SameX[MAX_SIZE];
+ int SameY[MAX_SIZE];
+ int SameZ[MAX_SIZE];
+ int NumSameX, NumSameY, NumSameZ;
+ CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX);
+ CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY);
+ CalcFloorFrac(a_SizeZ, a_StartZ, a_EndZ, FloorZ, FracZ, SameZ, NumSameZ);
+
+ cCubicCell3D Cell(
+ m_Noise, a_Array,
+ a_SizeX, a_SizeY, a_SizeZ,
+ FracX, FracY, FracZ
+ );
+
+ Cell.InitWorkRnds(FloorX[0], FloorY[0], FloorZ[0]);
+
+ // Calculate query values using Cell:
+ int FromZ = 0;
+ for (int z = 0; z < NumSameZ; z++)
+ {
+ int ToZ = FromZ + SameZ[z];
+ int CurFloorZ = FloorZ[FromZ];
+ int FromY = 0;
+ for (int y = 0; y < NumSameY; y++)
+ {
+ int ToY = FromY + SameY[y];
+ int CurFloorY = FloorY[FromY];
+ int FromX = 0;
+ for (int x = 0; x < NumSameX; x++)
+ {
+ int ToX = FromX + SameX[x];
+ Cell.Generate(FromX, ToX, FromY, ToY, FromZ, ToZ);
+ Cell.Move(FloorX[ToX], CurFloorY, CurFloorZ);
+ FromX = ToX;
+ }
+ Cell.Move(FloorX[0], FloorY[ToY], CurFloorZ);
+ FromY = ToY;
+ } // for y
+ Cell.Move(FloorX[0], FloorY[0], FloorZ[ToZ]);
+ FromZ = ToZ;
+ } // for z
+}
+
+
+
+
+
+void cCubicNoise::CalcFloorFrac(
+ int a_Size,
+ NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End,
+ int * a_Floor, NOISE_DATATYPE * a_Frac,
+ int * a_Same, int & a_NumSame
+) const
+{
+ NOISE_DATATYPE val = a_Start;
+ NOISE_DATATYPE dif = (a_End - a_Start) / (a_Size - 1);
+ for (int i = 0; i < a_Size; i++)
+ {
+ a_Floor[i] = FAST_FLOOR(val);
+ a_Frac[i] = val - a_Floor[i];
+ val += dif;
+ }
+
+ // Mark up the same floor values into a_Same / a_NumSame:
+ int CurFloor = a_Floor[0];
+ int LastSame = 0;
+ a_NumSame = 0;
+ for (int i = 1; i < a_Size; i++)
+ {
+ if (a_Floor[i] != CurFloor)
+ {
+ a_Same[a_NumSame] = i - LastSame;
+ LastSame = i;
+ a_NumSame += 1;
+ CurFloor = a_Floor[i];
+ }
+ } // for i - a_Floor[]
+ if (LastSame < a_Size)
+ {
+ a_Same[a_NumSame] = a_Size - LastSame;
+ a_NumSame += 1;
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cPerlinNoise:
+
+cPerlinNoise::cPerlinNoise(void) :
+ m_Seed(0)
+{
+}
+
+
+
+
+
+cPerlinNoise::cPerlinNoise(int a_Seed) :
+ m_Seed(a_Seed)
+{
+}
+
+
+
+
+
+void cPerlinNoise::SetSeed(int a_Seed)
+{
+ m_Seed = a_Seed;
+}
+
+
+
+
+
+void cPerlinNoise::AddOctave(float a_Frequency, float a_Amplitude)
+{
+ m_Octaves.push_back(cOctave(m_Seed * (m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude));
+}
+
+
+
+
+
+void cPerlinNoise::Generate2D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
+) const
+{
+ if (m_Octaves.empty())
+ {
+ // No work to be done
+ ASSERT(!"Perlin: No octaves to generate!");
+ return;
+ }
+
+ bool ShouldFreeWorkspace = (a_Workspace == NULL);
+ int ArrayCount = a_SizeX * a_SizeY;
+ if (ShouldFreeWorkspace)
+ {
+ a_Workspace = new NOISE_DATATYPE[ArrayCount];
+ }
+
+ // Generate the first octave directly into array:
+ m_Octaves.front().m_Noise.Generate2D(
+ a_Workspace, a_SizeX, a_SizeY,
+ a_StartX * m_Octaves.front().m_Frequency, a_EndX * m_Octaves.front().m_Frequency,
+ a_StartY * m_Octaves.front().m_Frequency, a_EndY * m_Octaves.front().m_Frequency
+ );
+ NOISE_DATATYPE Amplitude = m_Octaves.front().m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] *= Amplitude;
+ }
+
+ // Add each octave:
+ for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
+ {
+ // Generate cubic noise for the octave:
+ itr->m_Noise.Generate2D(
+ a_Workspace, a_SizeX, a_SizeY,
+ a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
+ a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
+ );
+ // Add the cubic noise into the output:
+ NOISE_DATATYPE Amplitude = itr->m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] += a_Workspace[i] * Amplitude;
+ }
+ }
+
+ if (ShouldFreeWorkspace)
+ {
+ delete[] a_Workspace;
+ }
+}
+
+
+
+
+
+void cPerlinNoise::Generate3D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
+ NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
+) const
+{
+ if (m_Octaves.empty())
+ {
+ // No work to be done
+ ASSERT(!"Perlin: No octaves to generate!");
+ return;
+ }
+
+ bool ShouldFreeWorkspace = (a_Workspace == NULL);
+ int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
+ if (ShouldFreeWorkspace)
+ {
+ a_Workspace = new NOISE_DATATYPE[ArrayCount];
+ }
+
+ // Generate the first octave directly into array:
+ m_Octaves.front().m_Noise.Generate3D(
+ a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
+ a_StartX * m_Octaves.front().m_Frequency, a_EndX * m_Octaves.front().m_Frequency,
+ a_StartY * m_Octaves.front().m_Frequency, a_EndY * m_Octaves.front().m_Frequency,
+ a_StartZ * m_Octaves.front().m_Frequency, a_EndZ * m_Octaves.front().m_Frequency
+ );
+ NOISE_DATATYPE Amplitude = m_Octaves.front().m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] = a_Workspace[i] * Amplitude;
+ }
+
+ // Add each octave:
+ for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
+ {
+ // Generate cubic noise for the octave:
+ itr->m_Noise.Generate3D(
+ a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
+ a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
+ a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
+ a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
+ );
+ // Add the cubic noise into the output:
+ NOISE_DATATYPE Amplitude = itr->m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] += a_Workspace[i] * Amplitude;
+ }
+ }
+
+ if (ShouldFreeWorkspace)
+ {
+ delete[] a_Workspace;
+ }
+}
+
+
+
+
diff --git a/src/Noise.h b/src/Noise.h
new file mode 100644
index 000000000..ea72c64e9
--- /dev/null
+++ b/src/Noise.h
@@ -0,0 +1,308 @@
+
+// Noise.h
+
+// Declares the cNoise, cCubicNoise and cPerlinNoise classes for generating noise
+
+#pragma once
+
+// Some settings
+#define NOISE_DATATYPE float
+
+
+
+
+
+#ifdef _MSC_VER
+ #define INLINE __forceinline
+#else
+ #define INLINE inline
+#endif
+
+
+
+
+
+class cNoise
+{
+public:
+ cNoise(unsigned int a_Seed);
+ cNoise(const cNoise & a_Noise);
+
+ // The following functions, if not marked INLINE, are about 20 % slower
+ INLINE NOISE_DATATYPE IntNoise1D(int a_X) const;
+ INLINE NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const;
+ INLINE NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const;
+
+ // Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify.
+ INLINE int IntNoise1DInt(int a_X) const;
+ INLINE int IntNoise2DInt(int a_X, int a_Y) const;
+ INLINE int IntNoise3DInt(int a_X, int a_Y, int a_Z) const;
+
+ NOISE_DATATYPE LinearNoise1D(NOISE_DATATYPE a_X) const;
+ NOISE_DATATYPE CosineNoise1D(NOISE_DATATYPE a_X) const;
+ NOISE_DATATYPE CubicNoise1D (NOISE_DATATYPE a_X) const;
+ NOISE_DATATYPE SmoothNoise1D(int a_X) const;
+
+ NOISE_DATATYPE CubicNoise2D (NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const;
+
+ NOISE_DATATYPE CubicNoise3D (NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const;
+
+ void SetSeed(unsigned int a_Seed) { m_Seed = a_Seed; }
+
+ INLINE static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct);
+ INLINE static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
+ INLINE static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
+
+private:
+ unsigned int m_Seed;
+} ;
+
+
+
+
+
+class cCubicNoise
+{
+public:
+ static const int MAX_SIZE = 512; ///< Maximum size of each dimension of the query arrays.
+
+
+ cCubicNoise(int a_Seed);
+
+
+ void Generate1D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into
+ int a_SizeX, ///< Count of the array
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX ///< Noise-space coords of the array
+ ) const;
+
+
+ void Generate2D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
+ ) const;
+
+
+ void Generate3D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
+ ) const;
+
+protected:
+ typedef NOISE_DATATYPE Workspace1D[4];
+ typedef NOISE_DATATYPE Workspace2D[4][4];
+
+ cNoise m_Noise; // Used for integral rnd values
+
+ #ifdef _DEBUG
+ // Statistics on the noise-space coords:
+ static int m_NumSingleX;
+ static int m_NumSingleXY;
+ static int m_NumSingleY;
+ static int m_NumCalls;
+ #endif // _DEBUG
+
+ /// Calculates the integral and fractional parts along one axis.
+ void CalcFloorFrac(
+ int a_Size,
+ NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End,
+ int * a_Floor, NOISE_DATATYPE * a_Frac,
+ int * a_Same, int & a_NumSame
+ ) const;
+
+ void UpdateWorkRnds2DX(
+ Workspace2D & a_WorkRnds,
+ Workspace1D & a_Interps,
+ int a_LastFloorX, int a_NewFloorX,
+ int a_FloorY,
+ NOISE_DATATYPE a_FractionY
+ ) const;
+} ;
+
+
+
+
+
+class cPerlinNoise
+{
+public:
+ cPerlinNoise(void);
+ cPerlinNoise(int a_Seed);
+
+
+ void SetSeed(int a_Seed);
+
+ void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
+
+ void Generate1D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into
+ int a_SizeX, ///< Count of the array
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
+ NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash
+ ) const;
+
+
+ void Generate2D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash
+ ) const;
+
+
+ void Generate3D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
+ NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash
+ ) const;
+
+protected:
+ class cOctave
+ {
+ public:
+ cCubicNoise m_Noise;
+
+ NOISE_DATATYPE m_Frequency; // Coord multiplier
+ NOISE_DATATYPE m_Amplitude; // Value multiplier
+
+ cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
+ m_Noise(a_Seed),
+ m_Frequency(a_Frequency),
+ m_Amplitude(a_Amplitude)
+ {
+ }
+ } ;
+
+ typedef std::vector<cOctave> cOctaves;
+
+ int m_Seed;
+ cOctaves m_Octaves;
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Inline function definitions:
+// These need to be in the header, otherwise linker error occur in MSVC
+
+NOISE_DATATYPE cNoise::IntNoise1D(int a_X) const
+{
+ int x = ((a_X * m_Seed) << 13) ^ a_X;
+ return (1 - (NOISE_DATATYPE)((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824);
+ // returns a float number in the range of [-1, 1]
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::IntNoise2D(int a_X, int a_Y) const
+{
+ int n = a_X + a_Y * 57 + m_Seed * 57 * 57;
+ n = (n << 13) ^ n;
+ return (1 - (NOISE_DATATYPE)((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824);
+ // returns a float number in the range of [-1, 1]
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::IntNoise3D(int a_X, int a_Y, int a_Z) const
+{
+ int n = a_X + a_Y * 57 + a_Z * 57 * 57 + m_Seed * 57 * 57 * 57;
+ n = (n << 13) ^ n;
+ return ((NOISE_DATATYPE)1 - (NOISE_DATATYPE)((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
+ // returns a float number in the range of [-1, 1]
+}
+
+
+
+
+
+int cNoise::IntNoise1DInt(int a_X) const
+{
+ int x = ((a_X * m_Seed) << 13) ^ a_X;
+ return ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff);
+}
+
+
+
+
+
+int cNoise::IntNoise2DInt(int a_X, int a_Y) const
+{
+ int n = a_X + a_Y * 57 + m_Seed * 57 * 57;
+ n = (n << 13) ^ n;
+ return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+}
+
+
+
+
+
+int cNoise::IntNoise3DInt(int a_X, int a_Y, int a_Z) const
+{
+ int n = a_X + a_Y * 57 + a_Z * 57 * 57 + m_Seed * 57 * 57 * 57;
+ n = (n << 13) ^ n;
+ return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::CubicInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct)
+{
+ NOISE_DATATYPE P = (a_D - a_C) - (a_A - a_B);
+ NOISE_DATATYPE Q = (a_A - a_B) - P;
+ NOISE_DATATYPE R = a_C - a_A;
+ NOISE_DATATYPE S = a_B;
+
+ return ((P * a_Pct + Q) * a_Pct + R) * a_Pct + S;
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct)
+{
+ const NOISE_DATATYPE ft = a_Pct * (NOISE_DATATYPE)3.1415927;
+ const NOISE_DATATYPE f = (1 - cos(ft)) * (NOISE_DATATYPE)0.5;
+ return a_A * (1 - f) + a_B * f;
+}
+
+
+
+
+
+NOISE_DATATYPE cNoise::LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct)
+{
+ return a_A * (1 - a_Pct) + a_B * a_Pct;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Global functions:
+
+extern void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase);
+extern void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase);
+
+
+
+
diff --git a/src/OSSupport/BlockingTCPLink.cpp b/src/OSSupport/BlockingTCPLink.cpp
new file mode 100644
index 000000000..55454a4b5
--- /dev/null
+++ b/src/OSSupport/BlockingTCPLink.cpp
@@ -0,0 +1,149 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BlockingTCPLink.h"
+
+
+
+
+
+#ifdef _WIN32
+ #define MSG_NOSIGNAL (0)
+#endif
+#ifdef __MACH__
+ #define MSG_NOSIGNAL (0)
+#endif
+
+
+
+
+
+cBlockingTCPLink::cBlockingTCPLink(void)
+{
+}
+
+
+
+
+
+cBlockingTCPLink::~cBlockingTCPLink()
+{
+ CloseSocket();
+}
+
+
+
+
+
+void cBlockingTCPLink::CloseSocket()
+{
+ if (!m_Socket.IsValid())
+ {
+ m_Socket.CloseSocket();
+ }
+}
+
+
+
+
+
+bool cBlockingTCPLink::Connect(const char * iAddress, unsigned int iPort)
+{
+ ASSERT(!m_Socket.IsValid());
+ if (m_Socket.IsValid())
+ {
+ LOGWARN("WARNING: cTCPLink Connect() called while still connected.");
+ m_Socket.CloseSocket();
+ }
+
+ struct hostent *hp;
+ unsigned int addr;
+ struct sockaddr_in server;
+
+ m_Socket = socket(AF_INET, SOCK_STREAM, 0);
+ if (!m_Socket.IsValid())
+ {
+ LOGERROR("cTCPLink: Cannot create a socket");
+ return false;
+ }
+
+ addr = inet_addr(iAddress);
+ hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+ if (hp == NULL)
+ {
+ //LOGWARN("cTCPLink: gethostbyaddr returned NULL");
+ hp = gethostbyname(iAddress);
+ if (hp == NULL)
+ {
+ LOGWARN("cTCPLink: Could not resolve %s", iAddress);
+ CloseSocket();
+ return false;
+ }
+ }
+
+ server.sin_addr.s_addr = *((unsigned long *)hp->h_addr);
+ server.sin_family = AF_INET;
+ server.sin_port = htons( (unsigned short)iPort);
+ if (connect(m_Socket, (struct sockaddr *)&server, sizeof(server)))
+ {
+ LOGWARN("cTCPLink: Connection to \"%s:%d\" failed (%s)", iAddress, iPort, cSocket::GetErrorString( cSocket::GetLastError() ).c_str() );
+ CloseSocket();
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+int cBlockingTCPLink::Send(char * a_Data, unsigned int a_Size, int a_Flags /* = 0 */ )
+{
+ ASSERT(m_Socket.IsValid());
+ if (!m_Socket.IsValid())
+ {
+ LOGERROR("cBlockingTCPLink: Trying to send data without a valid connection!");
+ return -1;
+ }
+ return m_Socket.Send(a_Data, a_Size);
+}
+
+
+
+
+
+int cBlockingTCPLink::SendMessage( const char* a_Message, int a_Flags /* = 0 */ )
+{
+ ASSERT(m_Socket.IsValid());
+ if (!m_Socket.IsValid())
+ {
+ LOGWARN("cBlockingTCPLink: Trying to send message without a valid connection!");
+ return -1;
+ }
+ return m_Socket.Send(a_Message, strlen(a_Message));
+}
+
+
+
+
+
+void cBlockingTCPLink::ReceiveData(AString & oData)
+{
+ ASSERT(m_Socket.IsValid());
+ if (!m_Socket.IsValid())
+ {
+ return;
+ }
+
+ int Received = 0;
+ char Buffer[256];
+ while ((Received = recv(m_Socket, Buffer, sizeof(Buffer), 0)) > 0)
+ {
+ oData.append(Buffer, Received);
+ }
+}
+
+
+
+
diff --git a/src/OSSupport/BlockingTCPLink.h b/src/OSSupport/BlockingTCPLink.h
new file mode 100644
index 000000000..cb5f9e3f4
--- /dev/null
+++ b/src/OSSupport/BlockingTCPLink.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "Socket.h"
+
+
+
+
+
+class cBlockingTCPLink // tolua_export
+{ // tolua_export
+public: // tolua_export
+ cBlockingTCPLink(void); // tolua_export
+ ~cBlockingTCPLink(); // tolua_export
+
+ bool Connect( const char* a_Address, unsigned int a_Port ); // tolua_export
+ int Send( char* a_Data, unsigned int a_Size, int a_Flags = 0 ); // tolua_export
+ int SendMessage( const char* a_Message, int a_Flags = 0 ); // tolua_export
+ void CloseSocket(); // tolua_export
+ void ReceiveData(AString & oData); // tolua_export
+protected:
+
+ cSocket m_Socket;
+}; // tolua_export
+
+
+
+
diff --git a/src/OSSupport/CriticalSection.cpp b/src/OSSupport/CriticalSection.cpp
new file mode 100644
index 000000000..bda97e3a1
--- /dev/null
+++ b/src/OSSupport/CriticalSection.cpp
@@ -0,0 +1,188 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "IsThread.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCriticalSection:
+
+cCriticalSection::cCriticalSection()
+{
+ #ifdef _WIN32
+ InitializeCriticalSection(&m_CriticalSection);
+ #else
+ pthread_mutexattr_init(&m_Attributes);
+ pthread_mutexattr_settype(&m_Attributes, PTHREAD_MUTEX_RECURSIVE);
+
+ if (pthread_mutex_init(&m_CriticalSection, &m_Attributes) != 0)
+ {
+ LOGERROR("Could not initialize Critical Section!");
+ }
+ #endif
+
+ #ifdef _DEBUG
+ m_IsLocked = 0;
+ #endif // _DEBUG
+}
+
+
+
+
+
+cCriticalSection::~cCriticalSection()
+{
+ #ifdef _WIN32
+ DeleteCriticalSection(&m_CriticalSection);
+ #else
+ if (pthread_mutex_destroy(&m_CriticalSection) != 0)
+ {
+ LOGWARNING("Could not destroy Critical Section!");
+ }
+ pthread_mutexattr_destroy(&m_Attributes);
+ #endif
+}
+
+
+
+
+
+void cCriticalSection::Lock()
+{
+ #ifdef _WIN32
+ EnterCriticalSection(&m_CriticalSection);
+ #else
+ pthread_mutex_lock(&m_CriticalSection);
+ #endif
+
+ #ifdef _DEBUG
+ m_IsLocked += 1;
+ m_OwningThreadID = cIsThread::GetCurrentID();
+ #endif // _DEBUG
+}
+
+
+
+
+
+void cCriticalSection::Unlock()
+{
+ #ifdef _DEBUG
+ ASSERT(m_IsLocked > 0);
+ m_IsLocked -= 1;
+ #endif // _DEBUG
+
+ #ifdef _WIN32
+ LeaveCriticalSection(&m_CriticalSection);
+ #else
+ pthread_mutex_unlock(&m_CriticalSection);
+ #endif
+}
+
+
+
+
+
+#ifdef _DEBUG
+bool cCriticalSection::IsLocked(void)
+{
+ return (m_IsLocked > 0);
+}
+
+
+
+
+
+bool cCriticalSection::IsLockedByCurrentThread(void)
+{
+ return ((m_IsLocked > 0) && (m_OwningThreadID == cIsThread::GetCurrentID()));
+}
+#endif // _DEBUG
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCSLock
+
+cCSLock::cCSLock(cCriticalSection * a_CS)
+ : m_CS(a_CS)
+ , m_IsLocked(false)
+{
+ Lock();
+}
+
+
+
+
+
+cCSLock::cCSLock(cCriticalSection & a_CS)
+ : m_CS(&a_CS)
+ , m_IsLocked(false)
+{
+ Lock();
+}
+
+
+
+
+
+cCSLock::~cCSLock()
+{
+ if (!m_IsLocked)
+ {
+ return;
+ }
+ Unlock();
+}
+
+
+
+
+
+void cCSLock::Lock(void)
+{
+ ASSERT(!m_IsLocked);
+ m_IsLocked = true;
+ m_CS->Lock();
+}
+
+
+
+
+
+void cCSLock::Unlock(void)
+{
+ ASSERT(m_IsLocked);
+ m_IsLocked = false;
+ m_CS->Unlock();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCSUnlock:
+
+cCSUnlock::cCSUnlock(cCSLock & a_Lock) :
+ m_Lock(a_Lock)
+{
+ m_Lock.Unlock();
+}
+
+
+
+
+
+cCSUnlock::~cCSUnlock()
+{
+ m_Lock.Lock();
+}
+
+
+
+
diff --git a/src/OSSupport/CriticalSection.h b/src/OSSupport/CriticalSection.h
new file mode 100644
index 000000000..1bfe81439
--- /dev/null
+++ b/src/OSSupport/CriticalSection.h
@@ -0,0 +1,80 @@
+
+#pragma once
+
+
+
+
+
+class cCriticalSection
+{
+public:
+ cCriticalSection(void);
+ ~cCriticalSection();
+
+ void Lock(void);
+ void Unlock(void);
+
+ #ifdef _DEBUG
+ bool IsLocked(void);
+ bool IsLockedByCurrentThread(void);
+ #endif // _DEBUG
+
+private:
+ #ifdef _DEBUG
+ int m_IsLocked; // Number of times this CS is locked
+ unsigned long m_OwningThreadID;
+ #endif // _DEBUG
+
+ #ifdef _WIN32
+ CRITICAL_SECTION m_CriticalSection;
+ #else // _WIN32
+ pthread_mutex_t m_CriticalSection;
+ pthread_mutexattr_t m_Attributes;
+ #endif // else _WIN32
+} ALIGN_8;
+
+
+
+
+/// RAII for cCriticalSection - locks the CS on creation, unlocks on destruction
+class cCSLock
+{
+ cCriticalSection * m_CS;
+
+ // Unlike a cCriticalSection, this object should be used from a single thread, therefore access to m_IsLocked is not threadsafe
+ // In Windows, it is an error to call cCriticalSection::Unlock() multiple times if the lock is not held,
+ // therefore we need to check this value whether we are locked or not.
+ bool m_IsLocked;
+
+public:
+ cCSLock(cCriticalSection * a_CS);
+ cCSLock(cCriticalSection & a_CS);
+ ~cCSLock();
+
+ // Temporarily unlock or re-lock:
+ void Lock(void);
+ void Unlock(void);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(cCSLock);
+} ;
+
+
+
+
+
+/// Temporary RAII unlock for a cCSLock. Useful for unlock-wait-relock scenarios
+class cCSUnlock
+{
+ cCSLock & m_Lock;
+public:
+ cCSUnlock(cCSLock & a_Lock);
+ ~cCSUnlock();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(cCSUnlock);
+} ;
+
+
+
+
diff --git a/src/OSSupport/Event.cpp b/src/OSSupport/Event.cpp
new file mode 100644
index 000000000..cbacbba17
--- /dev/null
+++ b/src/OSSupport/Event.cpp
@@ -0,0 +1,118 @@
+
+// Event.cpp
+
+// Implements the cEvent object representing an OS-specific synchronization primitive that can be waited-for
+// Implemented as an Event on Win and as a 1-semaphore on *nix
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Event.h"
+
+
+
+
+
+cEvent::cEvent(void)
+{
+#ifdef _WIN32
+ m_Event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (m_Event == NULL)
+ {
+ LOGERROR("cEvent: cannot create event, GLE = %d. Aborting server.", GetLastError());
+ abort();
+ }
+#else // *nix
+ m_bIsNamed = false;
+ m_Event = new sem_t;
+ if (sem_init(m_Event, 0, 0))
+ {
+ // This path is used by MacOS, because it doesn't support unnamed semaphores.
+ delete m_Event;
+ m_bIsNamed = true;
+
+ AString EventName;
+ Printf(EventName, "cEvent%p", this);
+ m_Event = sem_open(EventName.c_str(), O_CREAT, 777, 0 );
+ if (m_Event == SEM_FAILED)
+ {
+ LOGERROR("cEvent: Cannot create event, errno = %i. Aborting server.", errno);
+ abort();
+ }
+ // Unlink the semaphore immediately - it will continue to function but will not pollute the namespace
+ // We don't store the name, so can't call this in the destructor
+ if (sem_unlink(EventName.c_str()) != 0)
+ {
+ LOGWARN("ERROR: Could not unlink cEvent. (%i)", errno);
+ }
+ }
+#endif // *nix
+}
+
+
+
+
+
+cEvent::~cEvent()
+{
+#ifdef _WIN32
+ CloseHandle(m_Event);
+#else
+ if (m_bIsNamed)
+ {
+ if (sem_close(m_Event) != 0)
+ {
+ LOGERROR("ERROR: Could not close cEvent. (%i)", errno);
+ }
+ }
+ else
+ {
+ sem_destroy(m_Event);
+ delete m_Event;
+ }
+#endif
+}
+
+
+
+
+
+void cEvent::Wait(void)
+{
+ #ifdef _WIN32
+ DWORD res = WaitForSingleObject(m_Event, INFINITE);
+ if (res != WAIT_OBJECT_0)
+ {
+ LOGWARN("cEvent: waiting for the event failed: %d, GLE = %d. Continuing, but server may be unstable.", res, GetLastError());
+ }
+ #else
+ int res = sem_wait(m_Event);
+ if (res != 0 )
+ {
+ LOGWARN("cEvent: waiting for the event failed: %i, errno = %i. Continuing, but server may be unstable.", res, errno);
+ }
+ #endif
+}
+
+
+
+
+
+void cEvent::Set(void)
+{
+ #ifdef _WIN32
+ if (!SetEvent(m_Event))
+ {
+ LOGWARN("cEvent: Could not set cEvent: GLE = %d", GetLastError());
+ }
+ #else
+ int res = sem_post(m_Event);
+ if (res != 0)
+ {
+ LOGWARN("cEvent: Could not set cEvent: %i, errno = %d", res, errno);
+ }
+ #endif
+}
+
+
+
+
diff --git a/src/OSSupport/Event.h b/src/OSSupport/Event.h
new file mode 100644
index 000000000..71f418c0c
--- /dev/null
+++ b/src/OSSupport/Event.h
@@ -0,0 +1,47 @@
+
+// Event.h
+
+// Interfaces to the cEvent object representing an OS-specific synchronization primitive that can be waited-for
+// Implemented as an Event on Win and as a 1-semaphore on *nix
+
+
+
+
+
+#pragma once
+#ifndef CEVENT_H_INCLUDED
+#define CEVENT_H_INCLUDED
+
+
+
+
+
+class cEvent
+{
+public:
+ cEvent(void);
+ ~cEvent();
+
+ void Wait(void);
+ void Set (void);
+
+private:
+
+ #ifdef _WIN32
+ HANDLE m_Event;
+ #else
+ sem_t * m_Event;
+ bool m_bIsNamed;
+ #endif
+} ;
+
+
+
+
+
+
+#endif // CEVENT_H_INCLUDED
+
+
+
+
diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp
new file mode 100644
index 000000000..274aa52da
--- /dev/null
+++ b/src/OSSupport/File.cpp
@@ -0,0 +1,451 @@
+
+// cFile.cpp
+
+// Implements the cFile class providing an OS-independent abstraction of a file.
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "File.h"
+#include <fstream>
+
+
+
+
+
+cFile::cFile(void) :
+ #ifdef USE_STDIO_FILE
+ m_File(NULL)
+ #else
+ m_File(INVALID_HANDLE_VALUE)
+ #endif // USE_STDIO_FILE
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+cFile::cFile(const AString & iFileName, eMode iMode) :
+ #ifdef USE_STDIO_FILE
+ m_File(NULL)
+ #else
+ m_File(INVALID_HANDLE_VALUE)
+ #endif // USE_STDIO_FILE
+{
+ Open(iFileName, iMode);
+}
+
+
+
+
+
+cFile::~cFile()
+{
+ if (IsOpen())
+ {
+ Close();
+ }
+}
+
+
+
+
+
+bool cFile::Open(const AString & iFileName, eMode iMode)
+{
+ ASSERT(!IsOpen()); // You should close the file before opening another one
+
+ if (IsOpen())
+ {
+ Close();
+ }
+
+ const char * Mode = NULL;
+ switch (iMode)
+ {
+ case fmRead: Mode = "rb"; break;
+ case fmWrite: Mode = "wb"; break;
+ case fmReadWrite: Mode = "rb+"; break;
+ default:
+ {
+ ASSERT(!"Unhandled file mode");
+ return false;
+ }
+ }
+ m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), Mode);
+ if ((m_File == NULL) && (iMode == fmReadWrite))
+ {
+ // Fix for MS not following C spec, opening "a" mode files for writing at the end only
+ // The file open operation has been tried with "read update", fails if file not found
+ // So now we know either the file doesn't exist or we don't have rights, no need to worry about file contents.
+ // Simply re-open for read-writing, erasing existing contents:
+ m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), "wb+");
+ }
+ return (m_File != NULL);
+}
+
+
+
+
+
+void cFile::Close(void)
+{
+ if (!IsOpen())
+ {
+ // Closing an unopened file is a legal nop
+ return;
+ }
+
+ fclose(m_File);
+ m_File = NULL;
+}
+
+
+
+
+
+bool cFile::IsOpen(void) const
+{
+ return (m_File != NULL);
+}
+
+
+
+
+
+bool cFile::IsEOF(void) const
+{
+ ASSERT(IsOpen());
+
+ if (!IsOpen())
+ {
+ // Unopened files behave as at EOF
+ return true;
+ }
+
+ return (feof(m_File) != 0);
+}
+
+
+
+
+
+int cFile::Read (void * iBuffer, int iNumBytes)
+{
+ ASSERT(IsOpen());
+
+ if (!IsOpen())
+ {
+ return -1;
+ }
+
+ return fread(iBuffer, 1, iNumBytes, m_File); // fread() returns the portion of Count parameter actually read, so we need to send iNumBytes as Count
+}
+
+
+
+
+
+int cFile::Write(const void * iBuffer, int iNumBytes)
+{
+ ASSERT(IsOpen());
+
+ if (!IsOpen())
+ {
+ return -1;
+ }
+
+ int res = fwrite(iBuffer, 1, iNumBytes, m_File); // fwrite() returns the portion of Count parameter actually written, so we need to send iNumBytes as Count
+ return res;
+}
+
+
+
+
+
+int cFile::Seek (int iPosition)
+{
+ ASSERT(IsOpen());
+
+ if (!IsOpen())
+ {
+ return -1;
+ }
+
+ if (fseek(m_File, iPosition, SEEK_SET) != 0)
+ {
+ return -1;
+ }
+ return ftell(m_File);
+}
+
+
+
+
+
+
+int cFile::Tell (void) const
+{
+ ASSERT(IsOpen());
+
+ if (!IsOpen())
+ {
+ return -1;
+ }
+
+ return ftell(m_File);
+}
+
+
+
+
+
+int cFile::GetSize(void) const
+{
+ ASSERT(IsOpen());
+
+ if (!IsOpen())
+ {
+ return -1;
+ }
+
+ int CurPos = ftell(m_File);
+ if (CurPos < 0)
+ {
+ return -1;
+ }
+ if (fseek(m_File, 0, SEEK_END) != 0)
+ {
+ return -1;
+ }
+ int res = ftell(m_File);
+ if (fseek(m_File, CurPos, SEEK_SET) != 0)
+ {
+ return -1;
+ }
+ return res;
+}
+
+
+
+
+
+int cFile::ReadRestOfFile(AString & a_Contents)
+{
+ ASSERT(IsOpen());
+
+ if (!IsOpen())
+ {
+ return -1;
+ }
+
+ int DataSize = GetSize() - Tell();
+
+ // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly
+ a_Contents.assign(DataSize, '\0');
+ return Read((void *)a_Contents.data(), DataSize);
+}
+
+
+
+
+
+bool cFile::Exists(const AString & a_FileName)
+{
+ cFile test(a_FileName, fmRead);
+ return test.IsOpen();
+}
+
+
+
+
+
+bool cFile::Delete(const AString & a_FileName)
+{
+ return (remove(a_FileName.c_str()) == 0);
+}
+
+
+
+
+
+bool cFile::Rename(const AString & a_OrigFileName, const AString & a_NewFileName)
+{
+ return (rename(a_OrigFileName.c_str(), a_NewFileName.c_str()) == 0);
+}
+
+
+
+
+
+bool cFile::Copy(const AString & a_SrcFileName, const AString & a_DstFileName)
+{
+ #ifdef _WIN32
+ return (CopyFile(a_SrcFileName.c_str(), a_DstFileName.c_str(), true) != 0);
+ #else
+ // Other OSs don't have a direct CopyFile equivalent, do it the harder way:
+ std::ifstream src(a_SrcFileName.c_str(), std::ios::binary);
+ std::ofstream dst(a_DstFileName.c_str(), std::ios::binary);
+ if (dst.good())
+ {
+ dst << src.rdbuf();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ #endif
+}
+
+
+
+
+
+bool cFile::IsFolder(const AString & a_Path)
+{
+ #ifdef _WIN32
+ DWORD FileAttrib = GetFileAttributes(a_Path.c_str());
+ return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0));
+ #else
+ struct stat st;
+ return ((stat(a_Path.c_str(), &st) == 0) && S_ISDIR(st.st_mode));
+ #endif
+}
+
+
+
+
+
+bool cFile::IsFile(const AString & a_Path)
+{
+ #ifdef _WIN32
+ DWORD FileAttrib = GetFileAttributes(a_Path.c_str());
+ return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0));
+ #else
+ struct stat st;
+ return ((stat(a_Path.c_str(), &st) == 0) && S_ISREG(st.st_mode));
+ #endif
+}
+
+
+
+
+
+int cFile::GetSize(const AString & a_FileName)
+{
+ struct stat st;
+ if (stat(a_FileName.c_str(), &st) == 0)
+ {
+ return st.st_size;
+ }
+ return -1;
+}
+
+
+
+
+
+bool cFile::CreateFolder(const AString & a_FolderPath)
+{
+ #ifdef _WIN32
+ return (CreateDirectory(a_FolderPath.c_str(), NULL) != 0);
+ #else
+ return (mkdir(a_FolderPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0);
+ #endif
+}
+
+
+
+
+
+AStringVector cFile::GetFolderContents(const AString & a_Folder)
+{
+ AStringVector AllFiles;
+
+ #ifdef _WIN32
+
+ // If the folder name doesn't contain the terminating slash / backslash, add it:
+ AString FileFilter = a_Folder;
+ if (
+ !FileFilter.empty() &&
+ (FileFilter[FileFilter.length() - 1] != '\\') &&
+ (FileFilter[FileFilter.length() - 1] != '/')
+ )
+ {
+ FileFilter.push_back('\\');
+ }
+
+ // Find all files / folders:
+ FileFilter.append("*.*");
+ HANDLE hFind;
+ WIN32_FIND_DATA FindFileData;
+ if ((hFind = FindFirstFile(FileFilter.c_str(), &FindFileData)) != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ AllFiles.push_back(FindFileData.cFileName);
+ } while (FindNextFile(hFind, &FindFileData));
+ FindClose(hFind);
+ }
+
+ #else // _WIN32
+
+ DIR * dp;
+ struct dirent *dirp;
+ if (*a_Directory == 0)
+ {
+ a_Directory = ".";
+ }
+ if ((dp = opendir(a_Directory)) == NULL)
+ {
+ LOGERROR("Error (%i) opening directory \"%s\"\n", errno, a_Directory );
+ }
+ else
+ {
+ while ((dirp = readdir(dp)) != NULL)
+ {
+ AllFiles.push_back(dirp->d_name);
+ }
+ closedir(dp);
+ }
+
+ #endif // else _WIN32
+
+ return AllFiles;
+}
+
+
+
+
+
+AString cFile::ReadWholeFile(const AString & a_FileName)
+{
+ cFile f;
+ if (!f.Open(a_FileName, fmRead))
+ {
+ return "";
+ }
+ AString Contents;
+ f.ReadRestOfFile(Contents);
+ return Contents;
+}
+
+
+
+
+
+int cFile::Printf(const char * a_Fmt, ...)
+{
+ AString buf;
+ va_list args;
+ va_start(args, a_Fmt);
+ AppendVPrintf(buf, a_Fmt, args);
+ va_end(args);
+ return Write(buf.c_str(), buf.length());
+}
+
+
+
+
diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h
new file mode 100644
index 000000000..01663a229
--- /dev/null
+++ b/src/OSSupport/File.h
@@ -0,0 +1,144 @@
+
+// cFile.h
+
+// Interfaces to the cFile class providing an OS-independent abstraction of a file.
+
+/*
+The object is optimized towards binary reads.
+The object has no multithreading locks, don't use from multiple threads!
+Usage:
+1, Construct a cFile instance (no-param constructor)
+2, Open a file using Open(), check return value for success
+3, Read / write
+4, Destroy the instance
+
+-- OR --
+
+1, Construct a cFile instance opening the file (filename-param constructor)
+2, Check if the file was opened using IsOpen()
+3, Read / write
+4, Destroy the instance
+*/
+
+
+
+
+
+#pragma once
+
+
+
+
+
+#ifndef _WIN32
+ #define USE_STDIO_FILE
+#endif // _WIN32
+
+// DEBUG:
+#define USE_STDIO_FILE
+
+
+
+
+
+// tolua_begin
+
+class cFile
+{
+public:
+
+ // tolua_end
+
+ #ifdef _WIN32
+ static const char PathSeparator = '\\';
+ #else
+ static const char PathSeparator = '/';
+ #endif
+
+ /// The mode in which to open the file
+ enum eMode
+ {
+ fmRead, // Read-only. If the file doesn't exist, object will not be valid
+ fmWrite, // Write-only. If the file already exists, it will be overwritten
+ fmReadWrite // Read/write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning
+ } ;
+
+ /// Simple constructor - creates an unopened file object, use Open() to open / create a real file
+ cFile(void);
+
+ /// Constructs and opens / creates the file specified, use IsOpen() to check for success
+ cFile(const AString & iFileName, eMode iMode);
+
+ /// Auto-closes the file, if open
+ ~cFile();
+
+ bool Open(const AString & iFileName, eMode iMode);
+ void Close(void);
+ bool IsOpen(void) const;
+ bool IsEOF(void) const;
+
+ /// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open
+ int Read (void * iBuffer, int iNumBytes);
+
+ /// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open
+ int Write(const void * iBuffer, int iNumBytes);
+
+ /// Seeks to iPosition bytes from file start, returns old position or -1 for failure; asserts if not open
+ int Seek (int iPosition);
+
+ /// Returns the current position (bytes from file start) or -1 for failure; asserts if not open
+ int Tell (void) const;
+
+ /// Returns the size of file, in bytes, or -1 for failure; asserts if not open
+ int GetSize(void) const;
+
+ /// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error
+ int ReadRestOfFile(AString & a_Contents);
+
+ // tolua_begin
+
+ /// Returns true if the file specified exists
+ static bool Exists(const AString & a_FileName);
+
+ /// Deletes a file, returns true if successful
+ static bool Delete(const AString & a_FileName);
+
+ /// Renames a file or folder, returns true if successful. May fail if dest already exists (libc-dependant)!
+ static bool Rename(const AString & a_OrigPath, const AString & a_NewPath);
+
+ /// Copies a file, returns true if successful.
+ static bool Copy(const AString & a_SrcFileName, const AString & a_DstFileName);
+
+ /// Returns true if the specified path is a folder
+ static bool IsFolder(const AString & a_Path);
+
+ /// Returns true if the specified path is a regular file
+ static bool IsFile(const AString & a_Path);
+
+ /// Returns the size of the file, or a negative number on error
+ static int GetSize(const AString & a_FileName);
+
+ /// Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute
+ static bool CreateFolder(const AString & a_FolderPath);
+
+ /// Returns the entire contents of the specified file as a string. Returns empty string on error.
+ static AString ReadWholeFile(const AString & a_FileName);
+
+ // tolua_end
+
+ /// Returns the list of all items in the specified folder (files, folders, nix pipes, whatever's there).
+ static AStringVector GetFolderContents(const AString & a_Folder); // Exported in ManualBindings.cpp
+
+ int Printf(const char * a_Fmt, ...);
+
+private:
+ #ifdef USE_STDIO_FILE
+ FILE * m_File;
+ #else
+ HANDLE m_File;
+ #endif
+} ; // tolua_export
+
+
+
+
diff --git a/src/OSSupport/GZipFile.cpp b/src/OSSupport/GZipFile.cpp
new file mode 100644
index 000000000..cbf6be6c4
--- /dev/null
+++ b/src/OSSupport/GZipFile.cpp
@@ -0,0 +1,107 @@
+
+// GZipFile.cpp
+
+// Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines
+
+#include "Globals.h"
+#include "GZipFile.h"
+
+
+
+
+
+cGZipFile::cGZipFile(void) :
+ m_File(NULL)
+{
+}
+
+
+
+
+
+cGZipFile::~cGZipFile()
+{
+ Close();
+}
+
+
+
+
+
+bool cGZipFile::Open(const AString & a_FileName, eMode a_Mode)
+{
+ if (m_File != NULL)
+ {
+ ASSERT(!"A file is already open in this object");
+ return false;
+ }
+ m_File = gzopen(a_FileName.c_str(), (a_Mode == fmRead) ? "r" : "w");
+ m_Mode = a_Mode;
+ return (m_File != NULL);
+}
+
+
+
+
+
+void cGZipFile::Close(void)
+{
+ if (m_File != NULL)
+ {
+ gzclose(m_File);
+ m_File = NULL;
+ }
+}
+
+
+
+
+
+int cGZipFile::ReadRestOfFile(AString & a_Contents)
+{
+ if (m_File == NULL)
+ {
+ ASSERT(!"No file has been opened");
+ return -1;
+ }
+
+ if (m_Mode != fmRead)
+ {
+ ASSERT(!"Bad file mode, cannot read");
+ return -1;
+ }
+
+ // Since the gzip format doesn't really support getting the uncompressed length, we need to read incrementally. Yuck!
+ int NumBytesRead = 0;
+ char Buffer[64 KiB];
+ while ((NumBytesRead = gzread(m_File, Buffer, sizeof(Buffer))) > 0)
+ {
+ a_Contents.append(Buffer, NumBytesRead);
+ }
+ return NumBytesRead;
+}
+
+
+
+
+
+bool cGZipFile::Write(const char * a_Contents, int a_Size)
+{
+ if (m_File == NULL)
+ {
+ ASSERT(!"No file has been opened");
+ return false;
+ }
+
+ if (m_Mode != fmWrite)
+ {
+ ASSERT(!"Bad file mode, cannot write");
+ return false;
+ }
+
+ return (gzwrite(m_File, a_Contents, a_Size) != 0);
+}
+
+
+
+
diff --git a/src/OSSupport/GZipFile.h b/src/OSSupport/GZipFile.h
new file mode 100644
index 000000000..dcc171a2a
--- /dev/null
+++ b/src/OSSupport/GZipFile.h
@@ -0,0 +1,52 @@
+
+// GZipFile.h
+
+// Declares the cGZipFile class representing a RAII wrapper over zlib's GZip file routines
+
+
+
+
+
+#pragma once
+
+#include "lib/zlib/zlib.h"
+
+
+
+
+
+class cGZipFile
+{
+public:
+ enum eMode
+ {
+ fmRead, // Read-only. If the file doesn't exist, object will not be valid
+ fmWrite, // Write-only. If the file already exists, it will be overwritten
+ } ;
+
+ cGZipFile(void);
+ ~cGZipFile();
+
+ /// Opens the file. Returns true if successful. Fails if a file has already been opened through this object.
+ bool Open(const AString & a_FileName, eMode a_Mode);
+
+ /// Closes the file, flushing all buffers. This object may be then reused for a different file and / or mode
+ void Close(void);
+
+ /// Reads the rest of the file and decompresses it into a_Contents. Returns the number of decompressed bytes, <0 for error
+ int ReadRestOfFile(AString & a_Contents);
+
+ /// Writes a_Contents into file, compressing it along the way. Returns true if successful. Multiple writes are supported.
+ bool Write(const AString & a_Contents) { return Write(a_Contents.data(), (int)(a_Contents.size())); }
+
+ bool Write(const char * a_Data, int a_Size);
+
+protected:
+ gzFile m_File;
+ eMode m_Mode;
+} ;
+
+
+
+
+
diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp
new file mode 100644
index 000000000..4da9f9949
--- /dev/null
+++ b/src/OSSupport/IsThread.cpp
@@ -0,0 +1,172 @@
+
+// IsThread.cpp
+
+// Implements the cIsThread class representing an OS-independent wrapper for a class that implements a thread.
+// This class will eventually suupersede the old cThread class
+
+#include "Globals.h"
+
+#include "IsThread.h"
+
+
+
+
+
+// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here:
+#if defined(_MSC_VER) && defined(_DEBUG)
+//
+// Usage: SetThreadName (-1, "MainThread");
+//
+
+static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName)
+{
+ struct
+ {
+ DWORD dwType; // must be 0x1000
+ LPCSTR szName; // pointer to name (in user addr space)
+ DWORD dwThreadID; // thread ID (-1=caller thread)
+ DWORD dwFlags; // reserved for future use, must be zero
+ } info;
+
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = dwThreadID;
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (DWORD *)&info);
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+}
+#endif // _MSC_VER && _DEBUG
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cIsThread:
+
+cIsThread::cIsThread(const AString & iThreadName) :
+ m_ThreadName(iThreadName),
+ m_ShouldTerminate(false),
+ m_Handle(NULL_HANDLE)
+{
+}
+
+
+
+
+
+cIsThread::~cIsThread()
+{
+ m_ShouldTerminate = true;
+ Wait();
+}
+
+
+
+
+
+bool cIsThread::Start(void)
+{
+ ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread?
+ #ifdef _WIN32
+ // Create the thread suspended, so that the mHandle variable is valid in the thread procedure
+ DWORD ThreadID = 0;
+ m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID);
+ if (m_Handle == NULL)
+ {
+ LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", m_ThreadName.c_str(), GetLastError());
+ return false;
+ }
+ ResumeThread(m_Handle);
+
+ #if defined(_DEBUG) && defined(_MSC_VER)
+ // Thread naming is available only in MSVC
+ if (!m_ThreadName.empty())
+ {
+ SetThreadName(ThreadID, m_ThreadName.c_str());
+ }
+ #endif // _DEBUG and _MSC_VER
+
+ #else // _WIN32
+ if (pthread_create(&m_Handle, NULL, thrExecute, this))
+ {
+ LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str());
+ return false;
+ }
+ #endif // else _WIN32
+
+ return true;
+}
+
+
+
+
+
+void cIsThread::Stop(void)
+{
+ if (m_Handle == NULL_HANDLE)
+ {
+ return;
+ }
+ m_ShouldTerminate = true;
+ Wait();
+}
+
+
+
+
+
+bool cIsThread::Wait(void)
+{
+ if (m_Handle == NULL)
+ {
+ return true;
+ }
+
+ #ifdef LOGD // ProtoProxy doesn't have LOGD
+ LOGD("Waiting for thread %s to finish", m_ThreadName.c_str());
+ #endif // LOGD
+
+ #ifdef _WIN32
+ int res = WaitForSingleObject(m_Handle, INFINITE);
+ m_Handle = NULL;
+
+ #ifdef LOGD // ProtoProxy doesn't have LOGD
+ LOGD("Thread %s finished", m_ThreadName.c_str());
+ #endif // LOGD
+
+ return (res == WAIT_OBJECT_0);
+ #else // _WIN32
+ int res = pthread_join(m_Handle, NULL);
+ m_Handle = NULL;
+
+ #ifdef LOGD // ProtoProxy doesn't have LOGD
+ LOGD("Thread %s finished", m_ThreadName.c_str());
+ #endif // LOGD
+
+ return (res == 0);
+ #endif // else _WIN32
+}
+
+
+
+
+
+unsigned long cIsThread::GetCurrentID(void)
+{
+ #ifdef _WIN32
+ return (unsigned long) GetCurrentThreadId();
+ #else
+ return (unsigned long) pthread_self();
+ #endif
+}
+
+
+
+
diff --git a/src/OSSupport/IsThread.h b/src/OSSupport/IsThread.h
new file mode 100644
index 000000000..b8784ea33
--- /dev/null
+++ b/src/OSSupport/IsThread.h
@@ -0,0 +1,100 @@
+
+// IsThread.h
+
+// Interfaces to the cIsThread class representing an OS-independent wrapper for a class that implements a thread.
+// This class will eventually suupersede the old cThread class
+
+/*
+Usage:
+To have a new thread, declare a class descending from cIsClass.
+Then override its Execute() method to provide your thread processing.
+In the descending class' constructor call the Start() method to start the thread once you're finished with initialization.
+*/
+
+
+
+
+
+#pragma once
+#ifndef CISTHREAD_H_INCLUDED
+#define CISTHREAD_H_INCLUDED
+
+
+
+
+
+class cIsThread
+{
+protected:
+ /// This is the main thread entrypoint
+ virtual void Execute(void) = 0;
+
+ /// The overriden Execute() method should check this value periodically and terminate if this is true
+ volatile bool m_ShouldTerminate;
+
+public:
+ cIsThread(const AString & iThreadName);
+ ~cIsThread();
+
+ /// Starts the thread; returns without waiting for the actual start
+ bool Start(void);
+
+ /// 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
+ bool Wait(void);
+
+ /// Returns the OS-dependent thread ID for the caller's thread
+ static unsigned long GetCurrentID(void);
+
+protected:
+ AString m_ThreadName;
+
+ // Value used for "no handle":
+ #ifdef _WIN32
+ #define NULL_HANDLE NULL
+ #else
+ #define NULL_HANDLE 0
+ #endif
+
+ #ifdef _WIN32
+
+ HANDLE m_Handle;
+
+ static DWORD_PTR __stdcall thrExecute(LPVOID a_Param)
+ {
+ // Create a window so that the thread can be identified by 3rd party tools:
+ HWND IdentificationWnd = CreateWindow("STATIC", ((cIsThread *)a_Param)->m_ThreadName.c_str(), 0, 0, 0, 0, WS_OVERLAPPED, NULL, NULL, NULL, NULL);
+
+ // Run the thread:
+ ((cIsThread *)a_Param)->Execute();
+
+ // Destroy the identification window:
+ DestroyWindow(IdentificationWnd);
+
+ return 0;
+ }
+
+ #else // _WIN32
+
+ pthread_t m_Handle;
+
+ static void * thrExecute(void * a_Param)
+ {
+ ((cIsThread *)a_Param)->Execute();
+ return NULL;
+ }
+
+ #endif // else _WIN32
+} ;
+
+
+
+
+
+#endif // CISTHREAD_H_INCLUDED
+
+
+
+
diff --git a/src/OSSupport/ListenThread.cpp b/src/OSSupport/ListenThread.cpp
new file mode 100644
index 000000000..ba3198764
--- /dev/null
+++ b/src/OSSupport/ListenThread.cpp
@@ -0,0 +1,238 @@
+
+// ListenThread.cpp
+
+// Implements the cListenThread class representing the thread that listens for client connections
+
+#include "Globals.h"
+#include "ListenThread.h"
+
+
+
+
+
+cListenThread::cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName) :
+ super(Printf("ListenThread %s", a_ServiceName.c_str())),
+ m_Callback(a_Callback),
+ m_Family(a_Family),
+ m_ShouldReuseAddr(false),
+ m_ServiceName(a_ServiceName)
+{
+}
+
+
+
+
+
+cListenThread::~cListenThread()
+{
+ Stop();
+}
+
+
+
+
+
+bool cListenThread::Initialize(const AString & a_PortsString)
+{
+ ASSERT(m_Sockets.empty()); // Not yet started
+
+ if (!CreateSockets(a_PortsString))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cListenThread::Start(void)
+{
+ if (m_Sockets.empty())
+ {
+ // There are no sockets listening, either forgotten to initialize or the user specified no listening ports
+ // Report as successful, though
+ return true;
+ }
+ return super::Start();
+}
+
+
+
+
+
+void cListenThread::Stop(void)
+{
+ if (m_Sockets.empty())
+ {
+ // No sockets means no thread was running in the first place
+ return;
+ }
+
+ m_ShouldTerminate = true;
+
+ // Close one socket to wake the thread up from the select() call
+ m_Sockets[0].CloseSocket();
+
+ // Wait for the thread to finish
+ super::Wait();
+
+ // Close all the listening sockets:
+ for (cSockets::iterator itr = m_Sockets.begin() + 1, end = m_Sockets.end(); itr != end; ++itr)
+ {
+ itr->CloseSocket();
+ } // for itr - m_Sockets[]
+ m_Sockets.clear();
+}
+
+
+
+
+
+void cListenThread::SetReuseAddr(bool a_Reuse)
+{
+ ASSERT(m_Sockets.empty()); // Must not have been Initialize()d yet
+
+ m_ShouldReuseAddr = a_Reuse;
+}
+
+
+
+
+
+bool cListenThread::CreateSockets(const AString & a_PortsString)
+{
+ AStringVector Ports = StringSplitAndTrim(a_PortsString, ",");
+
+ if (Ports.empty())
+ {
+ return false;
+ }
+
+ AString FamilyStr = m_ServiceName;
+ switch (m_Family)
+ {
+ case cSocket::IPv4: FamilyStr.append(" IPv4"); break;
+ case cSocket::IPv6: FamilyStr.append(" IPv6"); break;
+ default:
+ {
+ ASSERT(!"Unknown address family");
+ break;
+ }
+ }
+
+ for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr)
+ {
+ int Port = atoi(itr->c_str());
+ if ((Port <= 0) || (Port > 65535))
+ {
+ LOGWARNING("%s: Invalid port specified: \"%s\".", FamilyStr.c_str(), itr->c_str());
+ continue;
+ }
+ m_Sockets.push_back(cSocket::CreateSocket(m_Family));
+ if (!m_Sockets.back().IsValid())
+ {
+ LOGWARNING("%s: Cannot create listening socket for port %d: \"%s\"", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
+ m_Sockets.pop_back();
+ continue;
+ }
+
+ if (m_ShouldReuseAddr)
+ {
+ if (!m_Sockets.back().SetReuseAddress())
+ {
+ LOG("%s: Port %d cannot reuse addr, syscall failed: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
+ }
+ }
+
+ // Bind to port:
+ bool res = false;
+ switch (m_Family)
+ {
+ case cSocket::IPv4: res = m_Sockets.back().BindToAnyIPv4(Port); break;
+ case cSocket::IPv6: res = m_Sockets.back().BindToAnyIPv6(Port); break;
+ default:
+ {
+ ASSERT(!"Unknown address family");
+ res = false;
+ }
+ }
+ if (!res)
+ {
+ LOGWARNING("%s: Cannot bind port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
+ m_Sockets.pop_back();
+ continue;
+ }
+
+ if (!m_Sockets.back().Listen())
+ {
+ LOGWARNING("%s: Cannot listen on port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
+ m_Sockets.pop_back();
+ continue;
+ }
+
+ LOGINFO("%s: Port %d is open for connections", FamilyStr.c_str(), Port);
+ } // for itr - Ports[]
+
+ return !(m_Sockets.empty());
+}
+
+
+
+
+
+void cListenThread::Execute(void)
+{
+ if (m_Sockets.empty())
+ {
+ LOGD("Empty cListenThread, ending thread now.");
+ return;
+ }
+
+ // Find the highest socket number:
+ cSocket::xSocket Highest = m_Sockets[0].GetSocket();
+ for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr)
+ {
+ if (itr->GetSocket() > Highest)
+ {
+ Highest = itr->GetSocket();
+ }
+ } // for itr - m_Sockets[]
+
+ while (!m_ShouldTerminate)
+ {
+ // Put all sockets into a FD set:
+ fd_set fdRead;
+ FD_ZERO(&fdRead);
+ for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr)
+ {
+ FD_SET(itr->GetSocket(), &fdRead);
+ } // for itr - m_Sockets[]
+
+ timeval tv; // On Linux select() doesn't seem to wake up when socket is closed, so let's kinda busy-wait:
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (select(Highest + 1, &fdRead, NULL, NULL, &tv) == -1)
+ {
+ LOG("select(R) call failed in cListenThread: \"%s\"", cSocket::GetLastErrorString().c_str());
+ continue;
+ }
+ for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr)
+ {
+ if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead))
+ {
+ cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6();
+ if (Client.IsValid())
+ {
+ m_Callback.OnConnectionAccepted(Client);
+ }
+ }
+ } // for itr - m_Sockets[]
+ } // while (!m_ShouldTerminate)
+}
+
+
+
+
diff --git a/src/OSSupport/ListenThread.h b/src/OSSupport/ListenThread.h
new file mode 100644
index 000000000..4e337d814
--- /dev/null
+++ b/src/OSSupport/ListenThread.h
@@ -0,0 +1,83 @@
+
+// ListenThread.h
+
+// Declares the cListenThread class representing the thread that listens for client connections
+
+
+
+
+
+#pragma once
+
+#include "IsThread.h"
+#include "Socket.h"
+
+
+
+
+
+// fwd:
+class cServer;
+
+
+
+
+
+class cListenThread :
+ public cIsThread
+{
+ typedef cIsThread super;
+
+public:
+ /// Used as the callback for connection events
+ class cCallback
+ {
+ public:
+ /// This callback is called whenever a socket connection is accepted
+ virtual void OnConnectionAccepted(cSocket & a_Socket) = 0;
+ } ;
+
+ cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName = "");
+ ~cListenThread();
+
+ /// Creates all the sockets, returns trus if successful, false if not.
+ bool Initialize(const AString & a_PortsString);
+
+ bool Start(void);
+
+ void Stop(void);
+
+ /// Call before Initialize() to set the "reuse" flag on the sockets
+ void SetReuseAddr(bool a_Reuse = true);
+
+protected:
+ typedef std::vector<cSocket> cSockets;
+
+ /// The callback which to notify of incoming connections
+ cCallback & m_Callback;
+
+ /// Socket address family to use
+ cSocket::eFamily m_Family;
+
+ /// Sockets that are being monitored
+ cSockets m_Sockets;
+
+ /// If set to true, the SO_REUSEADDR socket option is set to true
+ bool m_ShouldReuseAddr;
+
+ /// Name of the service that's listening on the ports; for logging purposes only
+ AString m_ServiceName;
+
+
+ /** Fills in m_Sockets with individual sockets, each for one port specified in a_PortsString.
+ Returns true if successful and at least one socket has been created
+ */
+ bool CreateSockets(const AString & a_PortsString);
+
+ // cIsThread override:
+ virtual void Execute(void) override;
+} ;
+
+
+
+
diff --git a/src/OSSupport/Semaphore.cpp b/src/OSSupport/Semaphore.cpp
new file mode 100644
index 000000000..468de6858
--- /dev/null
+++ b/src/OSSupport/Semaphore.cpp
@@ -0,0 +1,91 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+
+
+
+
+cSemaphore::cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount /* = 0 */ )
+#ifndef _WIN32
+ : m_bNamed( false )
+#endif
+{
+#ifndef _WIN32
+ (void)a_MaxCount;
+ m_Handle = new sem_t;
+ if (sem_init( (sem_t*)m_Handle, 0, 0))
+ {
+ LOG("WARNING cSemaphore: Could not create unnamed semaphore, fallback to named.");
+ delete (sem_t*)m_Handle; // named semaphores return their own address
+ m_bNamed = true;
+
+ AString Name;
+ Printf(Name, "cSemaphore%p", this );
+ m_Handle = sem_open(Name.c_str(), O_CREAT, 777, a_InitialCount);
+ if( m_Handle == SEM_FAILED )
+ {
+ LOG("ERROR: Could not create Semaphore. (%i)", errno );
+ }
+ else
+ {
+ if( sem_unlink(Name.c_str()) != 0 )
+ {
+ LOG("ERROR: Could not unlink cSemaphore. (%i)", errno);
+ }
+ }
+ }
+#else
+ m_Handle = CreateSemaphore(
+ NULL, // security attribute
+ a_InitialCount, // initial count
+ a_MaxCount, // maximum count
+ 0 // name (optional)
+ );
+#endif
+}
+
+cSemaphore::~cSemaphore()
+{
+#ifdef _WIN32
+ CloseHandle( m_Handle );
+#else
+ if( m_bNamed )
+ {
+ if( sem_close( (sem_t*)m_Handle ) != 0 )
+ {
+ LOG("ERROR: Could not close cSemaphore. (%i)", errno);
+ }
+ }
+ else
+ {
+ sem_destroy( (sem_t*)m_Handle );
+ delete (sem_t*)m_Handle;
+ }
+ m_Handle = 0;
+
+#endif
+}
+
+void cSemaphore::Wait()
+{
+#ifndef _WIN32
+ if( sem_wait( (sem_t*)m_Handle ) != 0)
+ {
+ LOG("ERROR: Could not wait for cSemaphore. (%i)", errno);
+ }
+#else
+ WaitForSingleObject( m_Handle, INFINITE);
+#endif
+}
+
+void cSemaphore::Signal()
+{
+#ifndef _WIN32
+ if( sem_post( (sem_t*)m_Handle ) != 0 )
+ {
+ LOG("ERROR: Could not signal cSemaphore. (%i)", errno);
+ }
+#else
+ ReleaseSemaphore( m_Handle, 1, NULL );
+#endif
+}
diff --git a/src/OSSupport/Semaphore.h b/src/OSSupport/Semaphore.h
new file mode 100644
index 000000000..fbe8907f1
--- /dev/null
+++ b/src/OSSupport/Semaphore.h
@@ -0,0 +1,17 @@
+#pragma once
+
+class cSemaphore
+{
+public:
+ cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount = 0 );
+ ~cSemaphore();
+
+ void Wait();
+ void Signal();
+private:
+ void* m_Handle; // HANDLE pointer
+
+#ifndef _WIN32
+ bool m_bNamed;
+#endif
+};
diff --git a/src/OSSupport/Sleep.cpp b/src/OSSupport/Sleep.cpp
new file mode 100644
index 000000000..70fb06b40
--- /dev/null
+++ b/src/OSSupport/Sleep.cpp
@@ -0,0 +1,19 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#ifndef _WIN32
+ #include <unistd.h>
+#endif
+
+
+
+
+
+void cSleep::MilliSleep( unsigned int a_MilliSeconds )
+{
+#ifdef _WIN32
+ Sleep(a_MilliSeconds); // Don't tick too much
+#else
+ usleep(a_MilliSeconds*1000);
+#endif
+}
diff --git a/src/OSSupport/Sleep.h b/src/OSSupport/Sleep.h
new file mode 100644
index 000000000..5298c15da
--- /dev/null
+++ b/src/OSSupport/Sleep.h
@@ -0,0 +1,7 @@
+#pragma once
+
+class cSleep
+{
+public:
+ static void MilliSleep( unsigned int a_MilliSeconds );
+}; \ No newline at end of file
diff --git a/src/OSSupport/Socket.cpp b/src/OSSupport/Socket.cpp
new file mode 100644
index 000000000..f25f800c2
--- /dev/null
+++ b/src/OSSupport/Socket.cpp
@@ -0,0 +1,393 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Socket.h"
+
+#ifndef _WIN32
+ #include <netdb.h>
+ #include <unistd.h>
+ #include <arpa/inet.h> //inet_ntoa()
+#else
+ #define socklen_t int
+#endif
+
+
+
+
+
+cSocket::cSocket(xSocket a_Socket)
+ : m_Socket(a_Socket)
+{
+}
+
+
+
+
+
+cSocket::~cSocket()
+{
+ // Do NOT close the socket; this class is an API wrapper, not a RAII!
+}
+
+
+
+
+
+cSocket::operator cSocket::xSocket() const
+{
+ return m_Socket;
+}
+
+
+
+
+
+cSocket::xSocket cSocket::GetSocket() const
+{
+ return m_Socket;
+}
+
+
+
+
+
+bool cSocket::IsValidSocket(cSocket::xSocket a_Socket)
+{
+ #ifdef _WIN32
+ return (a_Socket != INVALID_SOCKET);
+ #else // _WIN32
+ return (a_Socket >= 0);
+ #endif // else _WIN32
+}
+
+
+
+
+
+void cSocket::CloseSocket()
+{
+ #ifdef _WIN32
+
+ closesocket(m_Socket);
+
+ #else // _WIN32
+
+ if (shutdown(m_Socket, SHUT_RDWR) != 0)//SD_BOTH);
+ {
+ LOGWARN("Error on shutting down socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str());
+ }
+ if (close(m_Socket) != 0)
+ {
+ LOGWARN("Error closing socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str());
+ }
+
+ #endif // else _WIN32
+
+ // Invalidate the socket so that this object can be re-used for another connection
+ m_Socket = INVALID_SOCKET;
+}
+
+
+
+
+
+AString cSocket::GetErrorString( int a_ErrNo )
+{
+ char buffer[ 1024 ];
+ AString Out;
+
+ #ifdef _WIN32
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, a_ErrNo, 0, buffer, ARRAYCOUNT(buffer), NULL);
+ Printf(Out, "%d: %s", a_ErrNo, buffer);
+ if (!Out.empty() && (Out[Out.length() - 1] == '\n'))
+ {
+ Out.erase(Out.length() - 2);
+ }
+ return Out;
+
+ #else // _WIN32
+
+ // According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r():
+
+ #if ( _GNU_SOURCE ) && !defined(ANDROID_NDK) // GNU version of strerror_r()
+
+ char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) );
+ if( res != NULL )
+ {
+ Printf(Out, "%d: %s", a_ErrNo, res);
+ return Out;
+ }
+
+ #else // XSI version of strerror_r():
+
+ int res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) );
+ if( res == 0 )
+ {
+ Printf(Out, "%d: %s", a_ErrNo, buffer);
+ return Out;
+ }
+
+ #endif // strerror_r() version
+
+ else
+ {
+ Printf(Out, "Error %d while getting error string for error #%d!", errno, a_ErrNo);
+ return Out;
+ }
+
+ #endif // else _WIN32
+}
+
+
+
+
+int cSocket::GetLastError()
+{
+#ifdef _WIN32
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+
+
+
+
+bool cSocket::SetReuseAddress(void)
+{
+ #if defined(_WIN32) || defined(ANDROID_NDK)
+ char yes = 1;
+ #else
+ int yes = 1;
+ #endif
+ return (setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == 0);
+}
+
+
+
+
+
+int cSocket::WSAStartup(void)
+{
+#ifdef _WIN32
+ WSADATA wsaData;
+ memset(&wsaData, 0, sizeof(wsaData));
+ return ::WSAStartup(MAKEWORD(2, 2),&wsaData);
+#else
+ return 0;
+#endif
+}
+
+
+
+
+
+cSocket cSocket::CreateSocket(eFamily a_Family)
+{
+ return socket((int)a_Family, SOCK_STREAM, 0);
+}
+
+
+
+
+
+bool cSocket::BindToAnyIPv4(unsigned short a_Port)
+{
+ sockaddr_in local;
+ memset(&local, 0, sizeof(local));
+
+ local.sin_family = AF_INET;
+ local.sin_port = htons((u_short)a_Port);
+
+ return (bind(m_Socket, (sockaddr *)&local, sizeof(local)) == 0);
+}
+
+
+
+
+
+bool cSocket::BindToAnyIPv6(unsigned short a_Port)
+{
+ sockaddr_in6 local;
+ memset(&local, 0, sizeof(local));
+
+ local.sin6_family = AF_INET6;
+ local.sin6_port = htons((u_short)a_Port);
+
+ return (bind(m_Socket, (sockaddr *)&local, sizeof(local)) == 0);
+}
+
+
+
+
+
+bool cSocket::BindToLocalhostIPv4(unsigned short a_Port)
+{
+ sockaddr_in local;
+ memset(&local, 0, sizeof(local));
+
+ local.sin_family = AF_INET;;
+ local.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ local.sin_port = htons((u_short)a_Port);
+
+ return (bind(m_Socket, (sockaddr*)&local, sizeof(local)) == 0);
+}
+
+
+
+
+
+bool cSocket::Listen(int a_Backlog)
+{
+ return (listen(m_Socket, a_Backlog) == 0);
+}
+
+
+
+
+
+cSocket cSocket::AcceptIPv4(void)
+{
+ sockaddr_in from;
+ socklen_t fromlen = sizeof(from);
+
+ cSocket SClient = accept(m_Socket, (sockaddr *)&from, &fromlen);
+
+ if (SClient.IsValid() && (from.sin_addr.s_addr != 0)) // Get IP in string form
+ {
+ SClient.m_IPString = inet_ntoa(from.sin_addr);
+ }
+ return SClient;
+}
+
+
+
+
+
+cSocket cSocket::AcceptIPv6(void)
+{
+ sockaddr_in6 from;
+ socklen_t fromlen = sizeof(from);
+
+ cSocket SClient = accept(m_Socket, (sockaddr *)&from, &fromlen);
+
+ // Get IP in string form:
+ if (SClient.IsValid())
+ {
+ #if defined(_WIN32)
+ // Windows XP doesn't have inet_ntop, so we need to improvise. And MSVC has different headers than GCC
+ #ifdef _MSC_VER
+ // MSVC version
+ Printf(SClient.m_IPString, "%x:%x:%x:%x:%x:%x:%x:%x",
+ from.sin6_addr.u.Word[0],
+ from.sin6_addr.u.Word[1],
+ from.sin6_addr.u.Word[2],
+ from.sin6_addr.u.Word[3],
+ from.sin6_addr.u.Word[4],
+ from.sin6_addr.u.Word[5],
+ from.sin6_addr.u.Word[6],
+ from.sin6_addr.u.Word[7]
+ );
+ #else // _MSC_VER
+ // MinGW
+ Printf(SClient.m_IPString, "%x:%x:%x:%x:%x:%x:%x:%x",
+ from.sin6_addr.s6_addr16[0],
+ from.sin6_addr.s6_addr16[1],
+ from.sin6_addr.s6_addr16[2],
+ from.sin6_addr.s6_addr16[3],
+ from.sin6_addr.s6_addr16[4],
+ from.sin6_addr.s6_addr16[5],
+ from.sin6_addr.s6_addr16[6],
+ from.sin6_addr.s6_addr16[7]
+ );
+ #endif // else _MSC_VER
+ #else
+ char buffer[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &(from.sin6_addr), buffer, sizeof(buffer));
+ SClient.m_IPString.assign(buffer);
+ #endif // _WIN32
+ }
+ return SClient;
+}
+
+
+
+
+
+bool cSocket::ConnectToLocalhostIPv4(unsigned short a_Port)
+{
+ sockaddr_in server;
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ server.sin_port = htons(a_Port);
+ return (connect(m_Socket, (sockaddr *)&server, sizeof(server)) == 0);
+}
+
+
+
+
+
+bool cSocket::ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port)
+{
+ // First try IP Address string to hostent conversion, because it's faster
+ unsigned long addr = inet_addr(a_HostNameOrAddr.c_str());
+ if (addr == INADDR_NONE)
+ {
+ // It is not an IP Address string, but rather a regular hostname, resolve:
+ hostent * hp = gethostbyname(a_HostNameOrAddr.c_str());
+ if (hp == NULL)
+ {
+ LOGWARNING("%s: Could not resolve hostname \"%s\"", __FUNCTION__, a_HostNameOrAddr.c_str());
+ CloseSocket();
+ return false;
+ }
+ addr = *((unsigned long*)hp->h_addr);
+ }
+
+ sockaddr_in server;
+ server.sin_addr.s_addr = addr;
+ server.sin_family = AF_INET;
+ server.sin_port = htons((unsigned short)a_Port);
+ return (connect(m_Socket, (sockaddr *)&server, sizeof(server)) == 0);
+}
+
+
+
+
+
+int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags)
+{
+ return recv(m_Socket, a_Buffer, a_Length, a_Flags);
+}
+
+
+
+
+
+int cSocket::Send(const char * a_Buffer, unsigned int a_Length)
+{
+ return send(m_Socket, a_Buffer, a_Length, 0);
+}
+
+
+
+
+
+unsigned short cSocket::GetPort(void) const
+{
+ ASSERT(IsValid());
+
+ sockaddr_in Addr;
+ socklen_t AddrSize = sizeof(Addr);
+ if (getsockname(m_Socket, (sockaddr *)&Addr, &AddrSize) != 0)
+ {
+ return 0;
+ }
+ return ntohs(Addr.sin_port);
+}
+
+
+
+
diff --git a/src/OSSupport/Socket.h b/src/OSSupport/Socket.h
new file mode 100644
index 000000000..81bfd28fc
--- /dev/null
+++ b/src/OSSupport/Socket.h
@@ -0,0 +1,102 @@
+
+#pragma once
+
+
+
+
+
+class cSocket
+{
+public:
+ enum eFamily
+ {
+ IPv4 = AF_INET,
+ IPv6 = AF_INET6,
+ } ;
+
+#ifdef _WIN32
+ typedef SOCKET xSocket;
+#else
+ typedef int xSocket;
+ static const int INVALID_SOCKET = -1;
+#endif
+
+ cSocket(void) : m_Socket(INVALID_SOCKET) {}
+ cSocket(xSocket a_Socket);
+ ~cSocket();
+
+ bool IsValid(void) const { return IsValidSocket(m_Socket); }
+ void CloseSocket(void);
+
+ operator xSocket(void) const;
+ xSocket GetSocket(void) const;
+
+ bool operator == (const cSocket & a_Other) {return m_Socket == a_Other.m_Socket; }
+
+ void SetSocket(xSocket a_Socket);
+
+ /// Sets the address-reuse socket flag; returns true on success
+ bool SetReuseAddress(void);
+
+ /// Initializes the network stack. Returns 0 on success, or another number as an error code.
+ static int WSAStartup(void);
+
+ static AString GetErrorString(int a_ErrNo);
+ static int GetLastError();
+ static AString GetLastErrorString(void)
+ {
+ return GetErrorString(GetLastError());
+ }
+
+ /// Creates a new socket of the specified address family
+ static cSocket CreateSocket(eFamily a_Family);
+
+ inline static bool IsSocketError(int a_ReturnedValue)
+ {
+ #ifdef _WIN32
+ return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0);
+ #else
+ return (a_ReturnedValue <= 0);
+ #endif
+ }
+
+ static bool IsValidSocket(xSocket a_Socket);
+
+ static const unsigned short ANY_PORT = 0; // When given to Bind() functions, they will find a free port
+ static const int DEFAULT_BACKLOG = 10;
+
+ /// Binds to the specified port on "any" interface (0.0.0.0). Returns true if successful.
+ bool BindToAnyIPv4(unsigned short a_Port);
+
+ /// Binds to the specified port on "any" interface (::/128). Returns true if successful.
+ bool BindToAnyIPv6(unsigned short a_Port);
+
+ /// Binds to the specified port on localhost interface (127.0.0.1) through IPv4. Returns true if successful.
+ bool BindToLocalhostIPv4(unsigned short a_Port);
+
+ /// Sets the socket to listen for incoming connections. Returns true if successful.
+ bool Listen(int a_Backlog = DEFAULT_BACKLOG);
+
+ /// Accepts an IPv4 incoming connection. Blocks if none available.
+ cSocket AcceptIPv4(void);
+
+ /// Accepts an IPv6 incoming connection. Blocks if none available.
+ cSocket AcceptIPv6(void);
+
+ /// Connects to a localhost socket on the specified port using IPv4; returns true if successful.
+ bool ConnectToLocalhostIPv4(unsigned short a_Port);
+
+ /// Connects to the specified host or string IP address and port, using IPv4. Returns true if successful.
+ bool ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port);
+
+ int Receive(char * a_Buffer, unsigned int a_Length, unsigned int a_Flags);
+ int Send (const char * a_Buffer, unsigned int a_Length);
+
+ unsigned short GetPort(void) const; // Returns 0 on failure
+
+ const AString & GetIPString(void) const { return m_IPString; }
+
+private:
+ xSocket m_Socket;
+ AString m_IPString;
+}; \ No newline at end of file
diff --git a/src/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp
new file mode 100644
index 000000000..3e505616c
--- /dev/null
+++ b/src/OSSupport/SocketThreads.cpp
@@ -0,0 +1,675 @@
+
+// cSocketThreads.cpp
+
+// Implements the cSocketThreads class representing the heart of MCS's client networking.
+// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support
+// For more detail, see http://forum.mc-server.org/showthread.php?tid=327
+
+#include "Globals.h"
+#include "SocketThreads.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSocketThreads:
+
+cSocketThreads::cSocketThreads(void)
+{
+}
+
+
+
+
+
+cSocketThreads::~cSocketThreads()
+{
+ for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
+ {
+ delete *itr;
+ } // for itr - m_Threads[]
+ m_Threads.clear();
+}
+
+
+
+
+
+
+bool cSocketThreads::AddClient(const cSocket & a_Socket, cCallback * a_Client)
+{
+ // Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client
+
+ // Try to add to existing threads:
+ cCSLock Lock(m_CS);
+ for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
+ {
+ if ((*itr)->IsValid() && (*itr)->HasEmptySlot())
+ {
+ (*itr)->AddClient(a_Socket, a_Client);
+ return true;
+ }
+ }
+
+ // No thread has free space, create a new one:
+ LOGD("Creating a new cSocketThread (currently have %d)", m_Threads.size());
+ cSocketThread * Thread = new cSocketThread(this);
+ if (!Thread->Start())
+ {
+ // There was an error launching the thread (but it was already logged along with the reason)
+ LOGERROR("A new cSocketThread failed to start");
+ delete Thread;
+ return false;
+ }
+ Thread->AddClient(a_Socket, a_Client);
+ m_Threads.push_back(Thread);
+ return true;
+}
+
+
+
+
+
+/*
+void cSocketThreads::RemoveClient(const cSocket * a_Socket)
+{
+ // Remove the socket (and associated client) from processing
+
+ cCSLock Lock(m_CS);
+ for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
+ {
+ if ((*itr)->RemoveSocket(a_Socket))
+ {
+ return;
+ }
+ } // for itr - m_Threads[]
+
+ // Cannot assert here, this may actually happen legally, since cClientHandle has to clean up the socket and it may have already closed in the meantime
+ // ASSERT(!"Removing an unknown socket");
+}
+*/
+
+
+
+
+
+void cSocketThreads::RemoveClient(const cCallback * a_Client)
+{
+ // Remove the associated socket and the client from processing
+
+ cCSLock Lock(m_CS);
+ for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
+ {
+ if ((*itr)->RemoveClient(a_Client))
+ {
+ return;
+ }
+ } // for itr - m_Threads[]
+
+ ASSERT(!"Removing an unknown client");
+}
+
+
+
+
+
+void cSocketThreads::NotifyWrite(const cCallback * a_Client)
+{
+ // Notifies the thread responsible for a_Client that the client has something to write
+
+ cCSLock Lock(m_CS);
+ for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
+ {
+ if ((*itr)->NotifyWrite(a_Client))
+ {
+ return;
+ }
+ } // for itr - m_Threads[]
+
+ // Cannot assert - this normally happens if a client disconnects and has pending packets, the cServer::cNotifyWriteThread will call this on invalid clients too
+ // ASSERT(!"Notifying write to an unknown client");
+}
+
+
+
+
+
+void cSocketThreads::Write(const cCallback * a_Client, const AString & a_Data)
+{
+ // Puts a_Data into outgoing data queue for a_Client
+ cCSLock Lock(m_CS);
+ for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
+ {
+ if ((*itr)->Write(a_Client, a_Data))
+ {
+ return;
+ }
+ } // for itr - m_Threads[]
+
+ // This may be perfectly legal, if the socket has been destroyed and the client is finishing up
+ // ASSERT(!"Writing to an unknown socket");
+}
+
+
+
+
+
+/// Stops reading from the socket - when this call returns, no more calls to the callbacks are made
+void cSocketThreads::StopReading(const cCallback * a_Client)
+{
+ cCSLock Lock(m_CS);
+ for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
+ {
+ if ((*itr)->StopReading(a_Client))
+ {
+ return;
+ }
+ } // for itr - m_Threads[]
+
+ // Cannot assert, this normally happens if the socket is closed before the client deinitializes
+ // ASSERT(!"Stopping reading on an unknown client");
+}
+
+
+
+
+
+/// Queues the socket for closing, as soon as its outgoing data is sent
+void cSocketThreads::QueueClose(const cCallback * a_Client)
+{
+ LOGD("QueueClose(client %p)", a_Client);
+
+ cCSLock Lock(m_CS);
+ for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
+ {
+ if ((*itr)->QueueClose(a_Client))
+ {
+ return;
+ }
+ } // for itr - m_Threads[]
+
+ ASSERT(!"Queueing close of an unknown client");
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cSocketThreads::cSocketThread:
+
+cSocketThreads::cSocketThread::cSocketThread(cSocketThreads * a_Parent) :
+ cIsThread("cSocketThread"),
+ m_Parent(a_Parent),
+ m_NumSlots(0)
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+cSocketThreads::cSocketThread::~cSocketThread()
+{
+ m_ShouldTerminate = true;
+
+ // Notify the thread:
+ ASSERT(m_ControlSocket2.IsValid());
+ m_ControlSocket2.Send("a", 1);
+
+ // Wait for the thread to finish:
+ Wait();
+
+ // Close the control sockets:
+ m_ControlSocket1.CloseSocket();
+ m_ControlSocket2.CloseSocket();
+}
+
+
+
+
+
+void cSocketThreads::cSocketThread::AddClient(const cSocket & a_Socket, cCallback * a_Client)
+{
+ ASSERT(m_NumSlots < MAX_SLOTS); // Use HasEmptySlot() to check before adding
+
+ m_Slots[m_NumSlots].m_Client = a_Client;
+ m_Slots[m_NumSlots].m_Socket = a_Socket;
+ m_Slots[m_NumSlots].m_Outgoing.clear();
+ m_Slots[m_NumSlots].m_ShouldClose = false;
+ m_Slots[m_NumSlots].m_ShouldCallClient = true;
+ m_NumSlots++;
+
+ // Notify the thread of the change:
+ ASSERT(m_ControlSocket2.IsValid());
+ m_ControlSocket2.Send("a", 1);
+}
+
+
+
+
+
+bool cSocketThreads::cSocketThread::RemoveClient(const cCallback * a_Client)
+{
+ // Returns true if removed, false if not found
+
+ if (m_NumSlots == 0)
+ {
+ return false;
+ }
+
+ for (int i = m_NumSlots - 1; i >= 0 ; --i)
+ {
+ if (m_Slots[i].m_Client != a_Client)
+ {
+ continue;
+ }
+
+ // Found, remove it:
+ m_Slots[i] = m_Slots[--m_NumSlots];
+
+ // Notify the thread of the change:
+ ASSERT(m_ControlSocket2.IsValid());
+ m_ControlSocket2.Send("r", 1);
+ return true;
+ } // for i - m_Slots[]
+
+ // Not found
+ return false;
+}
+
+
+
+
+
+bool cSocketThreads::cSocketThread::RemoveSocket(const cSocket * a_Socket)
+{
+ // Returns true if removed, false if not found
+
+ for (int i = m_NumSlots - 1; i >= 0 ; --i)
+ {
+ if (m_Slots[i].m_Socket != *a_Socket)
+ {
+ continue;
+ }
+
+ // Found, remove it:
+ m_Slots[i] = m_Slots[--m_NumSlots];
+
+ // Notify the thread of the change:
+ ASSERT(m_ControlSocket2.IsValid());
+ m_ControlSocket2.Send("r", 1);
+ return true;
+ } // for i - m_Slots[]
+
+ // Not found
+ return false;
+}
+
+
+
+
+
+bool cSocketThreads::cSocketThread::HasClient(const cCallback * a_Client) const
+{
+ for (int i = m_NumSlots - 1; i >= 0; --i)
+ {
+ if (m_Slots[i].m_Client == a_Client)
+ {
+ return true;
+ }
+ } // for i - m_Slots[]
+ return false;
+}
+
+
+
+
+
+bool cSocketThreads::cSocketThread::HasSocket(const cSocket * a_Socket) const
+{
+ for (int i = m_NumSlots - 1; i >= 0; --i)
+ {
+ if (m_Slots[i].m_Socket == *a_Socket)
+ {
+ return true;
+ }
+ } // for i - m_Slots[]
+ return false;
+}
+
+
+
+
+
+bool cSocketThreads::cSocketThread::NotifyWrite(const cCallback * a_Client)
+{
+ if (HasClient(a_Client))
+ {
+ // Notify the thread that there's another packet in the queue:
+ ASSERT(m_ControlSocket2.IsValid());
+ m_ControlSocket2.Send("q", 1);
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+bool cSocketThreads::cSocketThread::Write(const cCallback * a_Client, const AString & a_Data)
+{
+ // Returns true if socket handled by this thread
+ for (int i = m_NumSlots - 1; i >= 0; --i)
+ {
+ if (m_Slots[i].m_Client == a_Client)
+ {
+ m_Slots[i].m_Outgoing.append(a_Data);
+
+ // Notify the thread that there's data in the queue:
+ ASSERT(m_ControlSocket2.IsValid());
+ m_ControlSocket2.Send("q", 1);
+
+ return true;
+ }
+ } // for i - m_Slots[]
+ return false;
+}
+
+
+
+
+
+bool cSocketThreads::cSocketThread::StopReading (const cCallback * a_Client)
+{
+ // Returns true if client handled by this thread
+ for (int i = m_NumSlots - 1; i >= 0; --i)
+ {
+ if (m_Slots[i].m_Client == a_Client)
+ {
+ m_Slots[i].m_ShouldCallClient = false;
+ return true;
+ }
+ } // for i - m_Slots[]
+ return false;
+}
+
+
+
+
+
+bool cSocketThreads::cSocketThread::QueueClose(const cCallback * a_Client)
+{
+ // Returns true if socket handled by this thread
+ for (int i = m_NumSlots - 1; i >= 0; --i)
+ {
+ if (m_Slots[i].m_Client == a_Client)
+ {
+ m_Slots[i].m_ShouldClose = true;
+
+ // Notify the thread that there's a close queued (in case its conditions are already met):
+ ASSERT(m_ControlSocket2.IsValid());
+ m_ControlSocket2.Send("c", 1);
+
+ return true;
+ }
+ } // for i - m_Slots[]
+ return false;
+}
+
+
+
+
+
+bool cSocketThreads::cSocketThread::Start(void)
+{
+ // Create the control socket listener
+ m_ControlSocket2 = cSocket::CreateSocket(cSocket::IPv4);
+ if (!m_ControlSocket2.IsValid())
+ {
+ LOGERROR("Cannot create a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
+ return false;
+ }
+ if (!m_ControlSocket2.BindToLocalhostIPv4(cSocket::ANY_PORT))
+ {
+ LOGERROR("Cannot bind a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
+ m_ControlSocket2.CloseSocket();
+ return false;
+ }
+ if (!m_ControlSocket2.Listen(1))
+ {
+ LOGERROR("Cannot listen on a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
+ m_ControlSocket2.CloseSocket();
+ return false;
+ }
+ if (m_ControlSocket2.GetPort() == 0)
+ {
+ LOGERROR("Cannot determine Control socket port (\"%s\"); conitnuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
+ m_ControlSocket2.CloseSocket();
+ return false;
+ }
+
+ // Start the thread
+ if (!super::Start())
+ {
+ LOGERROR("Cannot start new cSocketThread");
+ m_ControlSocket2.CloseSocket();
+ return false;
+ }
+
+ // Finish connecting the control socket by accepting connection from the thread's socket
+ cSocket tmp = m_ControlSocket2.AcceptIPv4();
+ if (!tmp.IsValid())
+ {
+ LOGERROR("Cannot link Control sockets for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
+ m_ControlSocket2.CloseSocket();
+ return false;
+ }
+ m_ControlSocket2.CloseSocket();
+ m_ControlSocket2 = tmp;
+
+ return true;
+}
+
+
+
+
+
+void cSocketThreads::cSocketThread::Execute(void)
+{
+ // Connect the "client" part of the Control socket:
+ m_ControlSocket1 = cSocket::CreateSocket(cSocket::IPv4);
+ ASSERT(m_ControlSocket2.GetPort() != 0); // We checked in the Start() method, but let's be sure
+ if (!m_ControlSocket1.ConnectToLocalhostIPv4(m_ControlSocket2.GetPort()))
+ {
+ LOGERROR("Cannot connect Control sockets for a cSocketThread (\"%s\"); continuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
+ m_ControlSocket2.CloseSocket();
+ return;
+ }
+
+ // The main thread loop:
+ while (!m_ShouldTerminate)
+ {
+ // Put all sockets into the Read set:
+ fd_set fdRead;
+ cSocket::xSocket Highest = m_ControlSocket1.GetSocket();
+
+ PrepareSet(&fdRead, Highest);
+
+ // Wait for the sockets:
+ if (select(Highest + 1, &fdRead, NULL, NULL, NULL) == -1)
+ {
+ LOG("select(R) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str());
+ continue;
+ }
+
+ ReadFromSockets(&fdRead);
+
+ // Test sockets for writing:
+ fd_set fdWrite;
+ Highest = m_ControlSocket1.GetSocket();
+ PrepareSet(&fdWrite, Highest);
+ timeval Timeout;
+ Timeout.tv_sec = 0;
+ Timeout.tv_usec = 0;
+ if (select(Highest + 1, NULL, &fdWrite, NULL, &Timeout) == -1)
+ {
+ LOG("select(W) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str());
+ continue;
+ }
+
+ WriteToSockets(&fdWrite);
+ } // while (!mShouldTerminate)
+}
+
+
+
+
+
+void cSocketThreads::cSocketThread::PrepareSet(fd_set * a_Set, cSocket::xSocket & a_Highest)
+{
+ FD_ZERO(a_Set);
+ FD_SET(m_ControlSocket1.GetSocket(), a_Set);
+
+ cCSLock Lock(m_Parent->m_CS);
+ for (int i = m_NumSlots - 1; i >= 0; --i)
+ {
+ if (!m_Slots[i].m_Socket.IsValid())
+ {
+ continue;
+ }
+ cSocket::xSocket s = m_Slots[i].m_Socket.GetSocket();
+ FD_SET(s, a_Set);
+ if (s > a_Highest)
+ {
+ a_Highest = s;
+ }
+ } // for i - m_Slots[]
+}
+
+
+
+
+
+void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read)
+{
+ // Read on available sockets:
+
+ // Reset Control socket state:
+ if (FD_ISSET(m_ControlSocket1.GetSocket(), a_Read))
+ {
+ char Dummy[128];
+ m_ControlSocket1.Receive(Dummy, sizeof(Dummy), 0);
+ }
+
+ // Read from clients:
+ cCSLock Lock(m_Parent->m_CS);
+ for (int i = m_NumSlots - 1; i >= 0; --i)
+ {
+ cSocket::xSocket Socket = m_Slots[i].m_Socket.GetSocket();
+ if (!cSocket::IsValidSocket(Socket) || !FD_ISSET(Socket, a_Read))
+ {
+ continue;
+ }
+ char Buffer[1024];
+ int Received = m_Slots[i].m_Socket.Receive(Buffer, ARRAYCOUNT(Buffer), 0);
+ if (Received == 0)
+ {
+ // The socket has been closed by the remote party, close our socket and let it be removed after we process all reading
+ m_Slots[i].m_Socket.CloseSocket();
+ if (m_Slots[i].m_ShouldCallClient)
+ {
+ m_Slots[i].m_Client->SocketClosed();
+ }
+ }
+ else if (Received > 0)
+ {
+ if (m_Slots[i].m_ShouldCallClient)
+ {
+ m_Slots[i].m_Client->DataReceived(Buffer, Received);
+ }
+ }
+ else
+ {
+ // The socket has encountered an error, close it and let it be removed after we process all reading
+ m_Slots[i].m_Socket.CloseSocket();
+ if (m_Slots[i].m_ShouldCallClient)
+ {
+ m_Slots[i].m_Client->SocketClosed();
+ }
+ }
+ } // for i - m_Slots[]
+}
+
+
+
+
+
+void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
+{
+ // Write to available client sockets:
+ cCSLock Lock(m_Parent->m_CS);
+ for (int i = m_NumSlots - 1; i >= 0; --i)
+ {
+ cSocket::xSocket Socket = m_Slots[i].m_Socket.GetSocket();
+ if (!cSocket::IsValidSocket(Socket) || !FD_ISSET(Socket, a_Write))
+ {
+ continue;
+ }
+ if (m_Slots[i].m_Outgoing.empty())
+ {
+ // Request another chunk of outgoing data:
+ if (m_Slots[i].m_ShouldCallClient)
+ {
+ m_Slots[i].m_Client->GetOutgoingData(m_Slots[i].m_Outgoing);
+ }
+ if (m_Slots[i].m_Outgoing.empty())
+ {
+ // Nothing ready
+ if (m_Slots[i].m_ShouldClose)
+ {
+ // Socket was queued for closing and there's no more data to send, close it now:
+
+ // DEBUG
+ LOGD("Socket was queued for closing, closing now. Slot %d, client %p, socket %d", i, m_Slots[i].m_Client, m_Slots[i].m_Socket.GetSocket());
+
+ m_Slots[i].m_Socket.CloseSocket();
+ // The slot must be freed actively by the client, using RemoveClient()
+ }
+ continue;
+ }
+ } // if (outgoing data is empty)
+
+ int Sent = m_Slots[i].m_Socket.Send(m_Slots[i].m_Outgoing.data(), m_Slots[i].m_Outgoing.size());
+ if (Sent < 0)
+ {
+ int Err = cSocket::GetLastError();
+ LOGWARNING("Error %d while writing to client \"%s\", disconnecting. \"%s\"", Err, m_Slots[i].m_Socket.GetIPString().c_str(), cSocket::GetErrorString(Err).c_str());
+ m_Slots[i].m_Socket.CloseSocket();
+ if (m_Slots[i].m_ShouldCallClient)
+ {
+ m_Slots[i].m_Client->SocketClosed();
+ }
+ return;
+ }
+ m_Slots[i].m_Outgoing.erase(0, Sent);
+
+ // _X: If there's data left, it means the client is not reading fast enough, the server would unnecessarily spin in the main loop with zero actions taken; so signalling is disabled
+ // This means that if there's data left, it will be sent only when there's incoming data or someone queues another packet (for any socket handled by this thread)
+ /*
+ // If there's any data left, signalize the Control socket:
+ if (!m_Slots[i].m_Outgoing.empty())
+ {
+ ASSERT(m_ControlSocket2.IsValid());
+ m_ControlSocket2.Send("q", 1);
+ }
+ */
+ } // for i - m_Slots[i]
+}
+
+
+
+
diff --git a/src/OSSupport/SocketThreads.h b/src/OSSupport/SocketThreads.h
new file mode 100644
index 000000000..ecbac3aeb
--- /dev/null
+++ b/src/OSSupport/SocketThreads.h
@@ -0,0 +1,169 @@
+
+// SocketThreads.h
+
+// Interfaces to the cSocketThreads class representing the heart of MCS's client networking.
+// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support
+// For more detail, see http://forum.mc-server.org/showthread.php?tid=327
+
+/*
+Additional details:
+When a client is terminating a connection:
+- they call the StopReading() method to disable callbacks for the incoming data
+- they call the Write() method to queue any outstanding outgoing data
+- they call the QueueClose() method to queue the socket to close after outgoing data has been sent.
+When a socket slot is marked as having no callback, it is kept alive until its outgoing data queue is empty and its m_ShouldClose flag is set.
+This means that the socket can be written to several times before finally closing it via QueueClose()
+*/
+
+
+
+
+
+/// How many clients should one thread handle? (must be less than FD_SETSIZE for your platform)
+#define MAX_SLOTS 63
+
+
+
+
+
+#pragma once
+#ifndef CSOCKETTHREADS_H_INCLUDED
+#define CSOCKETTHREADS_H_INCLUDED
+
+#include "Socket.h"
+#include "IsThread.h"
+
+
+
+
+// Check MAX_SLOTS:
+#if MAX_SLOTS >= FD_SETSIZE
+ #error "MAX_SLOTS must be less than FD_SETSIZE for your platform! (otherwise select() won't work)"
+#endif
+
+
+
+
+
+// fwd:
+class cSocket;
+class cClientHandle;
+
+
+
+
+
+class cSocketThreads
+{
+public:
+
+ // Clients of cSocketThreads must implement this interface to be able to communicate
+ class cCallback
+ {
+ public:
+ /// Called when data is received from the remote party
+ virtual void DataReceived(const char * a_Data, int a_Size) = 0;
+
+ /// Called when data can be sent to remote party; the function is supposed to append outgoing data to a_Data
+ virtual void GetOutgoingData(AString & a_Data) = 0;
+
+ /// Called when the socket has been closed for any reason
+ virtual void SocketClosed(void) = 0;
+ } ;
+
+
+ cSocketThreads(void);
+ ~cSocketThreads();
+
+ /// Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client; returns true if successful
+ bool AddClient(const cSocket & a_Socket, cCallback * a_Client);
+
+ /// Remove the associated socket and the client from processing. The socket is left to send its data and is removed only after all its m_OutgoingData is sent
+ void RemoveClient(const cCallback * a_Client);
+
+ /// Notify the thread responsible for a_Client that the client has something to write
+ void NotifyWrite(const cCallback * a_Client);
+
+ /// Puts a_Data into outgoing data queue for a_Client
+ void Write(const cCallback * a_Client, const AString & a_Data);
+
+ /// Stops reading from the client - when this call returns, no more calls to the callbacks are made
+ void StopReading(const cCallback * a_Client);
+
+ /// Queues the client for closing, as soon as its outgoing data is sent
+ void QueueClose(const cCallback * a_Client);
+
+private:
+
+ class cSocketThread :
+ public cIsThread
+ {
+ typedef cIsThread super;
+
+ public:
+
+ cSocketThread(cSocketThreads * a_Parent);
+ ~cSocketThread();
+
+ // All these methods assume parent's m_CS is locked
+ bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; }
+ bool IsEmpty (void) const {return m_NumSlots == 0; }
+
+ void AddClient (const cSocket & a_Socket, cCallback * a_Client); // Takes ownership of the socket
+ bool RemoveClient(const cCallback * a_Client); // Returns true if removed, false if not found
+ bool RemoveSocket(const cSocket * a_Socket); // Returns true if removed, false if not found
+ bool HasClient (const cCallback * a_Client) const;
+ bool HasSocket (const cSocket * a_Socket) const;
+ bool NotifyWrite (const cCallback * a_Client); // Returns true if client handled by this thread
+ bool Write (const cCallback * a_Client, const AString & a_Data); // Returns true if client handled by this thread
+ bool StopReading (const cCallback * a_Client); // Returns true if client handled by this thread
+ bool QueueClose (const cCallback * a_Client); // Returns true if client handled by this thread
+
+ bool Start(void); // Hide the cIsThread's Start method, we need to provide our own startup to create the control socket
+
+ bool IsValid(void) const {return m_ControlSocket2.IsValid(); } // If the Control socket dies, the thread is not valid anymore
+
+ private:
+
+ cSocketThreads * m_Parent;
+
+ // Two ends of the control socket, the first is select()-ed, the second is written to for notifications
+ cSocket m_ControlSocket1;
+ cSocket m_ControlSocket2;
+
+ // Socket-client-packetqueues triplets.
+ // Manipulation with these assumes that the parent's m_CS is locked
+ struct sSlot
+ {
+ cSocket m_Socket; // The socket is primarily owned by this
+ cCallback * m_Client;
+ AString m_Outgoing; // If sending writes only partial data, the rest is stored here for another send
+ bool m_ShouldClose; // If true, the socket is to be closed after sending all outgoing data
+ bool m_ShouldCallClient; // If true, the client callbacks are called. Set to false in StopReading()
+ } ;
+ sSlot m_Slots[MAX_SLOTS];
+ int m_NumSlots; // Number of slots actually used
+
+ virtual void Execute(void) override;
+
+ void PrepareSet (fd_set * a_Set, cSocket::xSocket & a_Highest); // Puts all sockets into the set, along with m_ControlSocket1
+ void ReadFromSockets(fd_set * a_Read); // Reads from sockets indicated in a_Read
+ void WriteToSockets (fd_set * a_Write); // Writes to sockets indicated in a_Write
+ } ;
+
+ typedef std::list<cSocketThread *> cSocketThreadList;
+
+
+ cCriticalSection m_CS;
+ cSocketThreadList m_Threads;
+} ;
+
+
+
+
+
+#endif // CSOCKETTHREADS_H_INCLUDED
+
+
+
+
diff --git a/src/OSSupport/Thread.cpp b/src/OSSupport/Thread.cpp
new file mode 100644
index 000000000..3df75f0e7
--- /dev/null
+++ b/src/OSSupport/Thread.cpp
@@ -0,0 +1,128 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+
+
+
+
+// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here:
+#ifdef _MSC_VER
+//
+// Usage: SetThreadName (-1, "MainThread");
+//
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType; // must be 0x1000
+ LPCSTR szName; // pointer to name (in user addr space)
+ DWORD dwThreadID; // thread ID (-1=caller thread)
+ DWORD dwFlags; // reserved for future use, must be zero
+} THREADNAME_INFO;
+
+void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName)
+{
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = dwThreadID;
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+}
+#endif // _MSC_VER
+
+
+
+
+
+cThread::cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName /* = 0 */ )
+ : m_ThreadFunction( a_ThreadFunction )
+ , m_Param( a_Param )
+ , m_Event( new cEvent() )
+ , m_StopEvent( 0 )
+{
+ if( a_ThreadName )
+ {
+ m_ThreadName.assign(a_ThreadName);
+ }
+}
+
+
+
+
+
+cThread::~cThread()
+{
+ delete m_Event;
+
+ if( m_StopEvent )
+ {
+ m_StopEvent->Wait();
+ delete m_StopEvent;
+ }
+}
+
+
+
+
+
+void cThread::Start( bool a_bWaitOnDelete /* = true */ )
+{
+ if( a_bWaitOnDelete )
+ m_StopEvent = new cEvent();
+
+#ifndef _WIN32
+ pthread_t SndThread;
+ if( pthread_create( &SndThread, NULL, MyThread, this) )
+ LOGERROR("ERROR: Could not create thread!");
+#else
+ DWORD ThreadID = 0;
+ HANDLE hThread = CreateThread( 0 // security
+ ,0 // stack size
+ , (LPTHREAD_START_ROUTINE) MyThread // function name
+ ,this // parameters
+ ,0 // flags
+ ,&ThreadID ); // thread id
+ CloseHandle( hThread );
+
+ #ifdef _MSC_VER
+ if (!m_ThreadName.empty())
+ {
+ SetThreadName(ThreadID, m_ThreadName.c_str());
+ }
+ #endif // _MSC_VER
+#endif
+
+ // Wait until thread has actually been created
+ m_Event->Wait();
+}
+
+
+
+
+
+#ifdef _WIN32
+unsigned long cThread::MyThread(void* a_Param )
+#else
+void *cThread::MyThread( void *a_Param )
+#endif
+{
+ cThread* self = (cThread*)a_Param;
+ cEvent* StopEvent = self->m_StopEvent;
+
+ ThreadFunc* ThreadFunction = self->m_ThreadFunction;
+ void* ThreadParam = self->m_Param;
+
+ // Set event to let other thread know this thread has been created and it's safe to delete the cThread object
+ self->m_Event->Set();
+
+ ThreadFunction( ThreadParam );
+
+ if( StopEvent ) StopEvent->Set();
+ return 0;
+}
diff --git a/src/OSSupport/Thread.h b/src/OSSupport/Thread.h
new file mode 100644
index 000000000..3c9316424
--- /dev/null
+++ b/src/OSSupport/Thread.h
@@ -0,0 +1,26 @@
+#pragma once
+
+class cThread
+{
+public:
+ typedef void (ThreadFunc)(void*);
+ cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName = 0 );
+ ~cThread();
+
+ void Start( bool a_bWaitOnDelete = true );
+ void WaitForThread();
+private:
+ ThreadFunc* m_ThreadFunction;
+
+#ifdef _WIN32
+ static unsigned long MyThread(void* a_Param );
+#else
+ static void *MyThread( void *lpParam );
+#endif
+
+ void* m_Param;
+ cEvent* m_Event;
+ cEvent* m_StopEvent;
+
+ AString m_ThreadName;
+}; \ No newline at end of file
diff --git a/src/OSSupport/Timer.cpp b/src/OSSupport/Timer.cpp
new file mode 100644
index 000000000..ed16f9e3a
--- /dev/null
+++ b/src/OSSupport/Timer.cpp
@@ -0,0 +1,37 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Timer.h"
+
+
+
+
+
+
+cTimer::cTimer(void)
+{
+ #ifdef _WIN32
+ QueryPerformanceFrequency(&m_TicksPerSecond);
+ #endif
+}
+
+
+
+
+
+long long cTimer::GetNowTime(void)
+{
+ #ifdef _WIN32
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+ return ((now.QuadPart * 1000) / m_TicksPerSecond.QuadPart);
+ #else
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return (long long)(now.tv_sec * 1000 + now.tv_usec / 1000);
+ #endif
+}
+
+
+
+
diff --git a/src/OSSupport/Timer.h b/src/OSSupport/Timer.h
new file mode 100644
index 000000000..a059daa41
--- /dev/null
+++ b/src/OSSupport/Timer.h
@@ -0,0 +1,32 @@
+
+// Timer.h
+
+// Declares the cTimer class representing an OS-independent of retrieving current time with msec accuracy
+
+
+
+
+
+#pragma once
+
+
+
+
+
+class cTimer
+{
+public:
+ cTimer(void);
+
+ // Returns the current time expressed in milliseconds
+ long long GetNowTime(void);
+private:
+
+ #ifdef _WIN32
+ LARGE_INTEGER m_TicksPerSecond;
+ #endif
+} ;
+
+
+
+
diff --git a/src/Piston.cpp b/src/Piston.cpp
new file mode 100644
index 000000000..048cc355d
--- /dev/null
+++ b/src/Piston.cpp
@@ -0,0 +1,291 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Piston.h"
+#include "ChunkDef.h"
+#include "Entities/Pickup.h"
+#include "Item.h"
+#include "Root.h"
+#include "ClientHandle.h"
+#include "World.h"
+#include "Server.h"
+#include "Blocks/BlockHandler.h"
+
+
+
+
+
+/// Number of ticks that the piston extending / retracting waits before setting the block
+const int PISTON_TICK_DELAY = 6;
+
+
+
+
+
+cPiston::cPiston(cWorld * a_World)
+ : m_World(a_World)
+{
+
+}
+
+
+
+
+
+int cPiston::FirstPassthroughBlock(int pistonX, int pistonY, int pistonZ, NIBBLETYPE pistonmeta)
+{
+ // Examine each of the 12 blocks ahead of the piston:
+ for (int ret = 0; ret < 12; ret++)
+ {
+ BLOCKTYPE currBlock;
+ NIBBLETYPE currMeta;
+ AddDir(pistonX, pistonY, pistonZ, pistonmeta, 1);
+ m_World->GetBlockTypeMeta(pistonX, pistonY, pistonZ, currBlock, currMeta);
+ if (CanBreakPush(currBlock, currMeta))
+ {
+ // This block breaks when pushed, extend up to here
+ return ret;
+ }
+ if (!CanPush(currBlock, currMeta))
+ {
+ // This block cannot be pushed at all, the piston can't extend
+ return -1;
+ }
+ }
+ // There is no space for the blocks to move, piston can't extend
+ return -1;
+}
+
+
+
+
+
+void cPiston::ExtendPiston(int pistx, int pisty, int pistz)
+{
+ BLOCKTYPE pistonBlock;
+ NIBBLETYPE pistonMeta;
+ m_World->GetBlockTypeMeta(pistx, pisty, pistz, pistonBlock, pistonMeta);
+
+ if (IsExtended(pistonMeta))
+ {
+ // Already extended, bail out
+ return;
+ }
+
+ int dist = FirstPassthroughBlock(pistx, pisty, pistz, pistonMeta);
+ if (dist < 0)
+ {
+ // FirstPassthroughBlock says piston can't push anything, bail out
+ return;
+ }
+
+ m_World->BroadcastBlockAction(pistx, pisty, pistz, 0, pistonMeta, pistonBlock);
+ m_World->BroadcastSoundEffect("tile.piston.out", pistx * 8, pisty * 8, pistz * 8, 0.5f, 0.7f);
+
+ // Drop the breakable block in the line, if appropriate:
+ AddDir(pistx, pisty, pistz, pistonMeta, dist + 1); // "pist" now at the breakable / empty block
+ BLOCKTYPE currBlock;
+ NIBBLETYPE currMeta;
+ m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currMeta);
+ if (currBlock != E_BLOCK_AIR)
+ {
+ cBlockHandler * Handler = BlockHandler(currBlock);
+ if (Handler->DoesDropOnUnsuitable())
+ {
+ Handler->DropBlock(m_World, NULL, pistx, pisty, pistz);
+ }
+ }
+
+ // Push blocks, from the furthest to the nearest:
+ int oldx = pistx, oldy = pisty, oldz = pistz;
+ NIBBLETYPE currBlockMeta;
+ for (int i = dist + 1; i > 1; i--)
+ {
+ AddDir(pistx, pisty, pistz, pistonMeta, -1);
+ m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currBlockMeta);
+ m_World->QueueSetBlock( oldx, oldy, oldz, currBlock, currBlockMeta, PISTON_TICK_DELAY);
+ oldx = pistx;
+ oldy = pisty;
+ oldz = pistz;
+ }
+
+ int extx = pistx;
+ int exty = pisty;
+ int extz = pistz;
+ AddDir(pistx, pisty, pistz, pistonMeta, -1);
+ // "pist" now at piston body, "ext" at future extension
+
+ m_World->SetBlock( pistx, pisty, pistz, pistonBlock, pistonMeta | 0x8);
+ m_World->QueueSetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), PISTON_TICK_DELAY);
+}
+
+
+
+
+
+void cPiston::RetractPiston(int pistx, int pisty, int pistz)
+{
+ BLOCKTYPE pistonBlock;
+ NIBBLETYPE pistonMeta;
+ m_World->GetBlockTypeMeta(pistx, pisty, pistz, pistonBlock, pistonMeta);
+ if (!IsExtended(pistonMeta))
+ {
+ // Already retracted, bail out
+ return;
+ }
+
+ m_World->BroadcastBlockAction(pistx, pisty, pistz, 1, pistonMeta & ~(8), pistonBlock);
+ m_World->BroadcastSoundEffect("tile.piston.in", pistx * 8, pisty * 8, pistz * 8, 0.5f, 0.7f);
+ m_World->SetBlock(pistx, pisty, pistz, pistonBlock, pistonMeta & ~(8));
+
+ // Check the extension:
+ AddDir(pistx, pisty, pistz, pistonMeta, 1);
+ if (m_World->GetBlock(pistx, pisty, pistz) != E_BLOCK_PISTON_EXTENSION)
+ {
+ LOGD("%s: Piston without an extension?", __FUNCTION__);
+ return;
+ }
+
+ // Retract the extension, pull block if appropriate
+ if (IsSticky(pistonBlock))
+ {
+ int tempx = pistx, tempy = pisty, tempz = pistz;
+ AddDir( tempx, tempy, tempz, pistonMeta, 1);
+ BLOCKTYPE tempBlock;
+ NIBBLETYPE tempMeta;
+ m_World->GetBlockTypeMeta(tempx, tempy, tempz, tempBlock, tempMeta);
+ if (CanPull(tempBlock, tempMeta))
+ {
+ // Pull the block
+ m_World->QueueSetBlock(pistx, pisty, pistz, tempBlock, tempMeta, PISTON_TICK_DELAY);
+ m_World->QueueSetBlock(tempx, tempy, tempz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY);
+ }
+ else
+ {
+ // Retract without pulling
+ m_World->QueueSetBlock(pistx, pisty, pistz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY);
+ }
+ }
+ else
+ {
+ m_World->QueueSetBlock(pistx, pisty, pistz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY);
+ }
+}
+
+
+
+
+
+bool cPiston::IsExtended(NIBBLETYPE a_PistonMeta)
+{
+ return ((a_PistonMeta & 0x8) != 0x0);
+}
+
+
+
+
+
+bool cPiston::IsSticky(BLOCKTYPE a_BlockType)
+{
+ return (a_BlockType == E_BLOCK_STICKY_PISTON);
+}
+
+
+
+
+
+bool cPiston::CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_ANVIL:
+ case E_BLOCK_BED:
+ case E_BLOCK_BEDROCK:
+ case E_BLOCK_BREWING_STAND:
+ case E_BLOCK_CHEST:
+ case E_BLOCK_COMMAND_BLOCK:
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER:
+ case E_BLOCK_ENCHANTMENT_TABLE:
+ case E_BLOCK_END_PORTAL:
+ case E_BLOCK_END_PORTAL_FRAME:
+ case E_BLOCK_FURNACE:
+ case E_BLOCK_LIT_FURNACE:
+ case E_BLOCK_HOPPER:
+ case E_BLOCK_JUKEBOX:
+ case E_BLOCK_MOB_SPAWNER:
+ case E_BLOCK_NETHER_PORTAL:
+ case E_BLOCK_NOTE_BLOCK:
+ case E_BLOCK_OBSIDIAN:
+ case E_BLOCK_PISTON_EXTENSION:
+ {
+ return false;
+ }
+ case E_BLOCK_STICKY_PISTON:
+ case E_BLOCK_PISTON:
+ {
+ // A piston can only be pushed if retracted:
+ return !IsExtended(a_BlockMeta);
+ }
+ }
+ return true;
+}
+
+
+
+
+
+bool cPiston::CanBreakPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ return g_BlockPistonBreakable[a_BlockType];
+}
+
+
+
+
+
+bool cPiston::CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_WATER:
+ {
+ return false;
+ }
+ }
+
+ if (CanBreakPush(a_BlockType, a_BlockMeta))
+ {
+ return false; // CanBreakPush returns true, but we need false to prevent pulling
+ }
+
+ return CanPush(a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cPiston::AddDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_PistonMeta, int a_Amount)
+{
+ switch (a_PistonMeta & 0x07)
+ {
+ case 0: a_BlockY -= a_Amount; break;
+ case 1: a_BlockY += a_Amount; break;
+ case 2: a_BlockZ -= a_Amount; break;
+ case 3: a_BlockZ += a_Amount; break;
+ case 4: a_BlockX -= a_Amount; break;
+ case 5: a_BlockX += a_Amount; break;
+ default:
+ {
+ LOGWARNING("%s: invalid direction %d, ignoring", __FUNCTION__, a_PistonMeta & 0x07);
+ break;
+ }
+ }
+}
+
+
+
+
diff --git a/src/Piston.h b/src/Piston.h
new file mode 100644
index 000000000..92ddf6938
--- /dev/null
+++ b/src/Piston.h
@@ -0,0 +1,91 @@
+
+#pragma once
+
+
+
+
+
+// fwd: World.h
+class cWorld;
+
+
+
+
+
+class cPiston
+{
+public:
+
+ cPiston(cWorld * a_World);
+
+ static NIBBLETYPE RotationPitchToMetaData(double a_Rotation, double a_Pitch)
+ {
+ if (a_Pitch >= 50)
+ {
+ return 0x1;
+ }
+ else if (a_Pitch <= -50)
+ {
+ return 0x0;
+ }
+ else
+ {
+ a_Rotation += 90 + 45; // So its not aligned with axis
+
+ if (a_Rotation > 360)
+ {
+ a_Rotation -= 360;
+ }
+ if ((a_Rotation >= 0) && (a_Rotation < 90))
+ {
+ return 0x4;
+ }
+ else if ((a_Rotation >= 180) && (a_Rotation < 270))
+ {
+ return 0x5;
+ }
+ else if ((a_Rotation >= 90) && (a_Rotation < 180))
+ {
+ return 0x2;
+ }
+ else
+ {
+ return 0x3;
+ }
+ }
+ }
+
+ void ExtendPiston( int, int, int );
+ void RetractPiston( int, int, int );
+
+ /// Returns true if the piston (specified by blocktype) is a sticky piston
+ static bool IsSticky(BLOCKTYPE a_BlockType);
+
+ /// Returns true if the piston (with the specified meta) is extended
+ static bool IsExtended(NIBBLETYPE a_PistonMeta);
+
+ /// Returns true if the specified block can be pushed by a piston (and left intact)
+ static bool CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /// Returns true if the specified block can be pushed by a piston and broken / replaced
+ static bool CanBreakPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /// Returns true if the specified block can be pulled by a sticky piston
+ static bool CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /// Updates the coords by the specified amount in the direction a piston of the specified meta is facing
+ static void AddDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_PistonMeta, int a_Amount);
+
+
+ cWorld * m_World;
+
+private:
+ void ChainMove( int, int, int, char, unsigned short * );
+
+ /// Returns how many blocks the piston has to push (where the first free space is); <0 when unpushable
+ int FirstPassthroughBlock(int a_PistonX, int a_PistonY, int a_PistonZ, NIBBLETYPE a_PistonMeta);
+} ;
+
+
+
+
diff --git a/src/Plugin.cpp b/src/Plugin.cpp
new file mode 100644
index 000000000..98ccfb88c
--- /dev/null
+++ b/src/Plugin.cpp
@@ -0,0 +1,38 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Plugin.h"
+
+
+
+
+
+cPlugin::cPlugin(const AString & a_PluginDirectory) :
+ m_Language(E_CPP),
+ m_Name(a_PluginDirectory),
+ m_Version(0),
+ m_Directory(a_PluginDirectory)
+{
+}
+
+
+
+
+
+cPlugin::~cPlugin()
+{
+ LOGD("Destroying plugin \"%s\".", m_Name.c_str());
+}
+
+
+
+
+
+AString cPlugin::GetLocalFolder(void) const
+{
+ return std::string("Plugins/") + m_Directory;
+}
+
+
+
+
diff --git a/src/Plugin.h b/src/Plugin.h
new file mode 100644
index 000000000..06e5819df
--- /dev/null
+++ b/src/Plugin.h
@@ -0,0 +1,149 @@
+
+#pragma once
+
+#include "Item.h"
+#include "PluginManager.h"
+
+
+
+
+
+class cClientHandle;
+class cPlayer;
+class cPickup;
+class cItem;
+class cEntity;
+class cWorld;
+class cChunkDesc;
+struct TakeDamageInfo;
+
+// fwd: cPlayer.h
+class cPlayer;
+
+// fwd: CraftingRecipes.h
+class cCraftingGrid;
+class cCraftingRecipe;
+
+
+
+
+
+// tolua_begin
+class cPlugin
+{
+public:
+ // tolua_end
+
+ cPlugin( const AString & a_PluginDirectory );
+ virtual ~cPlugin();
+
+ virtual void OnDisable(void) {}
+ virtual bool Initialize(void) = 0;
+
+ // Called each tick
+ virtual void Tick(float a_Dt) = 0;
+
+ /**
+ * On all these functions, return true if you want to override default behavior and not call other plugins on that callback.
+ * You can also return false, so default behavior is used.
+ **/
+ virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) = 0;
+ virtual bool OnChat (cPlayer * a_Player, AString & a_Message) = 0;
+ virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0;
+ virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) = 0;
+ virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) = 0;
+ virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0;
+ virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0;
+ virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) = 0;
+ virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
+ virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) = 0;
+ virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0;
+ virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
+ virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
+ virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) = 0;
+ virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) = 0;
+ virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) = 0;
+ virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) = 0;
+ virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) = 0;
+ virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) = 0;
+ virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
+ virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
+ virtual bool OnPlayerEating (cPlayer & a_Player) = 0;
+ 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 OnPlayerMoved (cPlayer & a_Player) = 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 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;
+ virtual bool OnPlayerSpawned (cPlayer & a_Player) = 0;
+ virtual bool OnPlayerTossingItem (cPlayer & a_Player) = 0;
+ virtual bool OnPlayerUsedBlock (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 OnPlayerUsedItem (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 OnPlayerUsingBlock (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 OnPlayerUsingItem (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 OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
+ virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
+ virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) = 0;
+ virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) = 0;
+ virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) = 0;
+ virtual bool OnSpawningMonster (cWorld & a_World, cMonster & a_Monster) = 0;
+ virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo) = 0;
+ virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) = 0;
+ virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) = 0;
+ virtual bool OnWeatherChanged (cWorld & a_World) = 0;
+ virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) = 0;
+ virtual bool OnWorldTick (cWorld & a_World, float a_Dt) = 0;
+
+ /** Handles the command split into a_Split, issued by player a_Player.
+ Command permissions have already been checked.
+ Returns true if command handled successfully
+ */
+ virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) = 0;
+
+ /** Handles the console command split into a_Split.
+ Returns true if command handled successfully. Output is to be sent to the a_Output callback.
+ */
+ virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) = 0;
+
+ /// All bound commands are to be removed, do any language-dependent cleanup here
+ virtual void ClearCommands(void) {} ;
+
+ /// All bound console commands are to be removed, do any language-dependent cleanup here
+ virtual void ClearConsoleCommands(void) {} ;
+
+ // tolua_begin
+ const AString & GetName(void) const { return m_Name; }
+ void SetName(const AString & a_Name) { m_Name = a_Name; }
+
+ int GetVersion(void) const { return m_Version; }
+ void SetVersion(int a_Version) { m_Version = a_Version; }
+
+ const AString & GetDirectory(void) const {return m_Directory; }
+ AString GetLocalDirectory(void) const {return GetLocalFolder(); } // OBSOLETE, use GetLocalFolder() instead
+ AString GetLocalFolder(void) const;
+ // tolua_end
+
+
+ /* This should not be exposed to scripting languages */
+ enum PluginLanguage
+ {
+ E_CPP,
+ E_LUA,
+ E_SQUIRREL, // OBSOLETE, but kept in place to remind us of the horrors lurking in the history
+ };
+ PluginLanguage GetLanguage() { return m_Language; }
+ void SetLanguage( PluginLanguage a_Language ) { m_Language = a_Language; }
+
+private:
+ PluginLanguage m_Language;
+ AString m_Name;
+ int m_Version;
+
+ AString m_Directory;
+}; // tolua_export
+
+
+
+
diff --git a/src/PluginLua.cpp b/src/PluginLua.cpp
new file mode 100644
index 000000000..0afd3844a
--- /dev/null
+++ b/src/PluginLua.cpp
@@ -0,0 +1,1471 @@
+
+// PluginLua.cpp
+
+// Implements the cPluginLua class representing a plugin written in Lua
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#define LUA_USE_POSIX
+#include "PluginLua.h"
+#include "CommandOutput.h"
+
+extern "C"
+{
+ #include"lib/lua/src/lualib.h"
+}
+
+#include "tolua++.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cPluginLua:
+
+cPluginLua::cPluginLua(const AString & a_PluginDirectory) :
+ cPlugin(a_PluginDirectory),
+ m_LuaState(Printf("plugin %s", a_PluginDirectory.c_str()))
+{
+}
+
+
+
+
+
+cPluginLua::~cPluginLua()
+{
+ cCSLock Lock(m_CriticalSection);
+ Close();
+}
+
+
+
+
+
+void cPluginLua::Close(void)
+{
+ if (m_LuaState.IsValid())
+ {
+ // Release all the references in the hook map:
+ for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH)
+ {
+ for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR)
+ {
+ delete *itrR;
+ } // for itrR - itrH->second[]
+ } // for itrH - m_HookMap[]
+ m_HookMap.clear();
+
+ m_LuaState.Close();
+ }
+ else
+ {
+ ASSERT(m_HookMap.empty());
+ }
+}
+
+
+
+
+
+bool cPluginLua::Initialize(void)
+{
+ cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ m_LuaState.Create();
+
+ // Inject the identification global variables into the state:
+ lua_pushlightuserdata(m_LuaState, this);
+ lua_setglobal(m_LuaState, LUA_PLUGIN_INSTANCE_VAR_NAME);
+ lua_pushstring(m_LuaState, GetName().c_str());
+ lua_setglobal(m_LuaState, LUA_PLUGIN_NAME_VAR_NAME);
+
+ tolua_pushusertype(m_LuaState, this, "cPluginLua");
+ lua_setglobal(m_LuaState, "g_Plugin");
+ }
+
+ std::string PluginPath = FILE_IO_PREFIX + GetLocalFolder() + "/";
+
+ // Load all files for this plugin, and execute them
+ AStringVector Files = cFile::GetFolderContents(PluginPath.c_str());
+ for (AStringVector::const_iterator itr = Files.begin(); itr != Files.end(); ++itr)
+ {
+ if (itr->rfind(".lua") == AString::npos)
+ {
+ continue;
+ }
+ AString Path = PluginPath + *itr;
+ if (!m_LuaState.LoadFile(Path))
+ {
+ Close();
+ return false;
+ }
+ } // for itr - Files[]
+
+ // Call intialize function
+ bool res = false;
+ if (!m_LuaState.Call("Initialize", this, cLuaState::Return, res))
+ {
+ LOGWARNING("Error in plugin %s: Cannot call the Initialize() function. Plugin is temporarily disabled.", GetName().c_str());
+ Close();
+ return false;
+ }
+
+ if (!res)
+ {
+ LOGINFO("Plugin %s: Initialize() call failed, plugin is temporarily disabled.", GetName().c_str());
+ Close();
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+void cPluginLua::OnDisable(void)
+{
+ cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.HasFunction("OnDisable"))
+ {
+ return;
+ }
+ m_LuaState.Call("OnDisable");
+}
+
+
+
+
+
+void cPluginLua::Tick(float a_Dt)
+{
+ cCSLock Lock(m_CriticalSection);
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TICK];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_Dt);
+ }
+}
+
+
+
+
+
+bool cPluginLua::OnBlockToPickups(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_TO_PICKUPS];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &a_Pickups, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnChat(cPlayer * a_Player, AString & a_Message)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHAT];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_Player, a_Message, cLuaState::Return, res, a_Message);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnChunkAvailable(cWorld * a_World, int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_AVAILABLE];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATED];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnChunkUnloaded(cWorld * a_World, int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADED];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnChunkUnloading(cWorld * a_World, int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnCollectingPickup(cPlayer * a_Player, cPickup * a_Pickup)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_COLLECTING_PICKUP];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_Player, a_Pickup, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CRAFTING_NO_RECIPE];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), (cPlayer *)a_Player, a_Grid, a_Recipe, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnDisconnect(cPlayer * a_Player, const AString & a_Reason)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_Player, a_Reason, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_Player, a_Split, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODED];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ switch (a_Source)
+ {
+ case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break;
+ case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res); break;
+ case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res); break;
+ case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res); break;
+ case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res); break;
+ case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break;
+ case esWitherSkullBlack:
+ case esWitherSkullBlue: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break;
+ case esWitherBirth: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break;
+ case esPlugin: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break;
+ default:
+ {
+ ASSERT(!"Unhandled ExplosionSource");
+ return false;
+ }
+ }
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ switch (a_Source)
+ {
+ case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
+ case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
+ case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
+ case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
+ case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
+ case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
+ case esWitherSkullBlack:
+ case esWitherSkullBlue: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
+ case esWitherBirth: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
+ case esPlugin: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
+ default:
+ {
+ ASSERT(!"Unhandled ExplosionSource");
+ return false;
+ }
+ }
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnHandshake(cClientHandle * a_Client, const AString & a_Username)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HANDSHAKE];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_Client, a_Username, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PULLING_ITEM];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, a_SrcSlotNum, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PUSHING_ITEM];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, a_DstSlotNum, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Victim, a_Killer, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_LOGIN];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_Client, a_ProtocolVersion, a_Username, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_ANIMATION];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Player, a_Animation, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BREAKING_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_BlockType, a_BlockMeta, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BROKEN_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_BlockType, a_BlockMeta, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerEating(cPlayer & a_Player)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_EATING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerJoined(cPlayer & a_Player)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_JOINED];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_LEFT_CLICK];
+ 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_Status, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerMoved(cPlayer & a_Player)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+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)
+{
+ 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);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+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)
+{
+ 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);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::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)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICK];
+ 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, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Player, &a_Entity, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerShooting(cPlayer & a_Player)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SHOOTING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SPAWNED];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_TOSSING_ITEM];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerUsedBlock(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)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_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);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_ITEM];
+ 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, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerUsingBlock(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)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_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);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_ITEM];
+ 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, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_POST_CRAFTING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_Player, a_Grid, a_Recipe, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PRE_CRAFTING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_Player, a_Grid, a_Recipe, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_ENTITY];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_World, &a_Entity, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_MONSTER];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_World, &a_Monster, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_ENTITY];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_World, &a_Entity, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_MONSTER];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_World, &a_Monster, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TAKE_DAMAGE];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Receiver, &a_TDI, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnUpdatedSign(
+ cWorld * a_World,
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4,
+ cPlayer * a_Player
+)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATED_SIGN];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnUpdatingSign(
+ cWorld * a_World,
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4,
+ cPlayer * a_Player
+)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res, a_Line1, a_Line2, a_Line3, a_Line4);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnWeatherChanged(cWorld & a_World)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGED];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_World, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ int NewWeather = a_NewWeather;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_World, NewWeather, cLuaState::Return, res, NewWeather);
+ if (res)
+ {
+ a_NewWeather = (eWeather)NewWeather;
+ return true;
+ }
+ }
+ a_NewWeather = (eWeather)NewWeather;
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnWorldTick(cWorld & a_World, float a_Dt)
+{
+ cCSLock Lock(m_CriticalSection);
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_World, a_Dt);
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer * a_Player)
+{
+ ASSERT(!a_Split.empty());
+ CommandMap::iterator cmd = m_Commands.find(a_Split[0]);
+ if (cmd == m_Commands.end())
+ {
+ LOGWARNING("Command handler is registered in cPluginManager but not in cPlugin, wtf? Command \"%s\".", a_Split[0].c_str());
+ return false;
+ }
+
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ m_LuaState.Call(cmd->second, a_Split, a_Player, cLuaState::Return, res);
+ return res;
+}
+
+
+
+
+
+bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
+{
+ ASSERT(!a_Split.empty());
+ CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]);
+ if (cmd == m_ConsoleCommands.end())
+ {
+ LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\", plugin \"%s\".",
+ a_Split[0].c_str(), GetName().c_str()
+ );
+ return false;
+ }
+
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ AString str;
+ m_LuaState.Call(cmd->second, a_Split, cLuaState::Return, res, str);
+ if (res && !str.empty())
+ {
+ a_Output.Out(str);
+ }
+ return res;
+}
+
+
+
+
+
+void cPluginLua::ClearCommands(void)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ // Unreference the bound functions so that Lua can GC them
+ if (m_LuaState != NULL)
+ {
+ for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr)
+ {
+ luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second);
+ }
+ }
+ m_Commands.clear();
+}
+
+
+
+
+
+void cPluginLua::ClearConsoleCommands(void)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ // Unreference the bound functions so that Lua can GC them
+ if (m_LuaState != NULL)
+ {
+ for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr)
+ {
+ luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second);
+ }
+ }
+ m_ConsoleCommands.clear();
+}
+
+
+
+
+
+bool cPluginLua::CanAddOldStyleHook(int a_HookType)
+{
+ const char * FnName = GetHookFnName(a_HookType);
+ if (FnName == NULL)
+ {
+ // Unknown hook ID
+ LOGWARNING("Plugin %s wants to add an unknown hook ID (%d). The plugin need not work properly.",
+ GetName().c_str(), a_HookType
+ );
+ m_LuaState.LogStackTrace();
+ return false;
+ }
+
+ // Check if the function is available
+ if (m_LuaState.HasFunction(FnName))
+ {
+ return true;
+ }
+
+ LOGWARNING("Plugin %s wants to add a hook (%d), but it doesn't provide the callback function \"%s\" for it. The plugin need not work properly.",
+ GetName().c_str(), a_HookType, FnName
+ );
+ m_LuaState.LogStackTrace();
+ return false;
+}
+
+
+
+
+
+const char * cPluginLua::GetHookFnName(int a_HookType)
+{
+ switch (a_HookType)
+ {
+ case cPluginManager::HOOK_BLOCK_TO_PICKUPS: return "OnBlockToPickups";
+ case cPluginManager::HOOK_CHAT: return "OnChat";
+ case cPluginManager::HOOK_CHUNK_AVAILABLE: return "OnChunkAvailable";
+ case cPluginManager::HOOK_CHUNK_GENERATED: return "OnChunkGenerated";
+ case cPluginManager::HOOK_CHUNK_GENERATING: return "OnChunkGenerating";
+ case cPluginManager::HOOK_CHUNK_UNLOADED: return "OnChunkUnloaded";
+ case cPluginManager::HOOK_CHUNK_UNLOADING: return "OnChunkUnloading";
+ case cPluginManager::HOOK_COLLECTING_PICKUP: return "OnCollectingPickup";
+ case cPluginManager::HOOK_CRAFTING_NO_RECIPE: return "OnCraftingNoRecipe";
+ case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect";
+ case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand";
+ case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake";
+ case cPluginManager::HOOK_KILLING: return "OnKilling";
+ case cPluginManager::HOOK_LOGIN: return "OnLogin";
+ case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation";
+ case cPluginManager::HOOK_PLAYER_BREAKING_BLOCK: return "OnPlayerBreakingBlock";
+ case cPluginManager::HOOK_PLAYER_BROKEN_BLOCK: return "OnPlayerBrokenBlock";
+ case cPluginManager::HOOK_PLAYER_EATING: return "OnPlayerEating";
+ case cPluginManager::HOOK_PLAYER_JOINED: return "OnPlayerJoined";
+ case cPluginManager::HOOK_PLAYER_LEFT_CLICK: return "OnPlayerLeftClick";
+ case cPluginManager::HOOK_PLAYER_MOVING: return "OnPlayerMoving";
+ case cPluginManager::HOOK_PLAYER_PLACED_BLOCK: return "OnPlayerPlacedBlock";
+ case cPluginManager::HOOK_PLAYER_PLACING_BLOCK: return "OnPlayerPlacingBlock";
+ case cPluginManager::HOOK_PLAYER_RIGHT_CLICK: return "OnPlayerRightClick";
+ case cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY: return "OnPlayerRightClickingEntity";
+ case cPluginManager::HOOK_PLAYER_SHOOTING: return "OnPlayerShooting";
+ case cPluginManager::HOOK_PLAYER_SPAWNED: return "OnPlayerSpawned";
+ case cPluginManager::HOOK_PLAYER_TOSSING_ITEM: return "OnPlayerTossingItem";
+ case cPluginManager::HOOK_PLAYER_USED_BLOCK: return "OnPlayerUsedBlock";
+ case cPluginManager::HOOK_PLAYER_USED_ITEM: return "OnPlayerUsedItem";
+ case cPluginManager::HOOK_PLAYER_USING_BLOCK: return "OnPlayerUsingBlock";
+ case cPluginManager::HOOK_PLAYER_USING_ITEM: return "OnPlayerUsingItem";
+ case cPluginManager::HOOK_POST_CRAFTING: return "OnPostCrafting";
+ case cPluginManager::HOOK_PRE_CRAFTING: return "OnPreCrafting";
+ case cPluginManager::HOOK_SPAWNED_ENTITY: return "OnSpawnedEntity";
+ case cPluginManager::HOOK_SPAWNED_MONSTER: return "OnSpawnedMonster";
+ case cPluginManager::HOOK_SPAWNING_ENTITY: return "OnSpawningEntity";
+ case cPluginManager::HOOK_SPAWNING_MONSTER: return "OnSpawningMonster";
+ case cPluginManager::HOOK_TAKE_DAMAGE: return "OnTakeDamage";
+ case cPluginManager::HOOK_TICK: return "OnTick";
+ case cPluginManager::HOOK_UPDATED_SIGN: return "OnUpdatedSign";
+ case cPluginManager::HOOK_UPDATING_SIGN: return "OnUpdatingSign";
+ case cPluginManager::HOOK_WEATHER_CHANGED: return "OnWeatherChanged";
+ case cPluginManager::HOOK_WEATHER_CHANGING: return "OnWeatherChanging";
+ case cPluginManager::HOOK_WORLD_TICK: return "OnWorldTick";
+ default: return NULL;
+ } // switch (a_Hook)
+}
+
+
+
+
+
+bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx)
+{
+ ASSERT(m_CriticalSection.IsLockedByCurrentThread()); // It probably has to be, how else would we have a LuaState?
+
+ // Check if the function reference is valid:
+ cLuaState::cRef * Ref = new cLuaState::cRef(m_LuaState, a_FnRefIdx);
+ if ((Ref == NULL) || !Ref->IsValid())
+ {
+ LOGWARNING("Plugin %s tried to add a hook %d with bad handler function.", GetName().c_str(), a_HookType);
+ m_LuaState.LogStackTrace();
+ delete Ref;
+ return false;
+ }
+
+ m_HookMap[a_HookType].push_back(Ref);
+ return true;
+}
+
+
+
+
+
+AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request )
+{
+ cCSLock Lock(m_CriticalSection);
+ std::string RetVal = "";
+
+ std::pair< std::string, std::string > TabName = GetTabNameForRequest(a_Request);
+ std::string SafeTabName = TabName.second;
+ if (SafeTabName.empty())
+ {
+ return "";
+ }
+
+ sWebPluginTab * Tab = 0;
+ for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr)
+ {
+ if ((*itr)->SafeTitle.compare(SafeTabName) == 0) // This is the one! Rawr
+ {
+ Tab = *itr;
+ break;
+ }
+ }
+
+ if (Tab != NULL)
+ {
+ AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->Title.c_str());
+ if (!m_LuaState.Call(Tab->UserData, a_Request, cLuaState::Return, Contents))
+ {
+ return "Lua encountered error while processing the page request";
+ }
+
+ RetVal += Contents;
+ }
+
+ return RetVal;
+}
+
+
+
+
+
+bool cPluginLua::AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference)
+{
+ cCSLock Lock(m_CriticalSection);
+ if (a_LuaState != m_LuaState)
+ {
+ LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!");
+ return false;
+ }
+ sWebPluginTab * Tab = new sWebPluginTab();
+ Tab->Title = a_Title;
+ Tab->SafeTitle = SafeString(a_Title);
+
+ Tab->UserData = a_FunctionReference;
+
+ GetTabs().push_back(Tab);
+ return true;
+}
+
+
+
+
+
+void cPluginLua::BindCommand(const AString & a_Command, int a_FnRef)
+{
+ ASSERT(m_Commands.find(a_Command) == m_Commands.end());
+ m_Commands[a_Command] = a_FnRef;
+}
+
+
+
+
+
+void cPluginLua::BindConsoleCommand(const AString & a_Command, int a_FnRef)
+{
+ ASSERT(m_ConsoleCommands.find(a_Command) == m_ConsoleCommands.end());
+ m_ConsoleCommands[a_Command] = a_FnRef;
+}
+
+
+
+
+
+void cPluginLua::Unreference(int a_LuaRef)
+{
+ cCSLock Lock(m_CriticalSection);
+ luaL_unref(m_LuaState, LUA_REGISTRYINDEX, a_LuaRef);
+}
+
+
+
+
+
+bool cPluginLua::CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse)
+{
+ ASSERT(a_FnRef != LUA_REFNIL);
+
+ cCSLock Lock(m_CriticalSection);
+ bool res;
+ m_LuaState.Call(a_FnRef, &a_Window, &a_Player, a_CanRefuse, cLuaState::Return, res);
+ return res;
+}
+
+
+
+
+
+void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum)
+{
+ ASSERT(a_FnRef != LUA_REFNIL);
+
+ cCSLock Lock(m_CriticalSection);
+ m_LuaState.Call(a_FnRef, &a_Window, a_SlotNum);
+}
+
+
+
+
diff --git a/src/PluginLua.h b/src/PluginLua.h
new file mode 100644
index 000000000..908466966
--- /dev/null
+++ b/src/PluginLua.h
@@ -0,0 +1,202 @@
+
+// PluginLua.h
+
+// Declares the cPluginLua class representing a plugin written in Lua
+
+
+
+
+
+#pragma once
+
+#include "Plugin.h"
+#include "WebPlugin.h"
+#include "LuaState.h"
+
+// Names for the global variables through which the plugin is identified in its LuaState
+#define LUA_PLUGIN_NAME_VAR_NAME "_MCServerInternal_PluginName"
+#define LUA_PLUGIN_INSTANCE_VAR_NAME "_MCServerInternal_PluginInstance"
+
+
+
+
+// fwd: UI/Window.h
+class cWindow;
+
+
+
+
+
+// tolua_begin
+class cPluginLua :
+ public cPlugin,
+ public cWebPlugin
+{
+public:
+ // tolua_end
+
+ cPluginLua( const AString & a_PluginDirectory );
+ ~cPluginLua();
+
+ virtual void OnDisable(void) override;
+ virtual bool Initialize(void) override;
+
+ virtual void Tick(float a_Dt) override;
+
+ virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) override;
+ virtual bool OnChat (cPlayer * a_Player, AString & a_Message) override;
+ virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
+ virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override;
+ virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override;
+ virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
+ virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
+ virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override;
+ virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override;
+ virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override;
+ virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
+ virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
+ virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) override;
+ virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) override;
+ virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) override;
+ virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) override;
+ virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override;
+ virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) override;
+ virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual bool OnPlayerEating (cPlayer & a_Player) override;
+ virtual bool OnPlayerJoined (cPlayer & a_Player) override;
+ virtual bool OnPlayerMoved (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 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 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;
+ virtual bool OnPlayerSpawned (cPlayer & a_Player) override;
+ virtual bool OnPlayerTossingItem (cPlayer & a_Player) override;
+ virtual bool OnPlayerUsedBlock (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 OnPlayerUsedItem (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 OnPlayerUsingBlock (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 OnPlayerUsingItem (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 OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) override;
+ virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) override;
+ virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) override;
+ virtual bool OnSpawningMonster (cWorld & a_World, cMonster & a_Monster) override;
+ virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo) override;
+ virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override;
+ virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override;
+ virtual bool OnWeatherChanged (cWorld & a_World) override;
+ virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) override;
+ virtual bool OnWorldTick (cWorld & a_World, float a_Dt) override;
+
+ virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) override;
+
+ virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) override;
+
+ virtual void ClearCommands(void) override;
+
+ virtual void ClearConsoleCommands(void) override;
+
+ /// Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121)
+ bool CanAddOldStyleHook(int a_HookType);
+
+ // cWebPlugin override
+ virtual const AString GetWebTitle(void) const {return GetName(); }
+
+ // cWebPlugin and WebAdmin stuff
+ virtual AString HandleWebRequest(const HTTPRequest * a_Request ) override;
+ bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS <<
+
+ /// Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap.
+ void BindCommand(const AString & a_Command, int a_FnRef);
+
+ /// Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap.
+ void BindConsoleCommand(const AString & a_Command, int a_FnRef);
+
+ cLuaState & GetLuaState(void) { return m_LuaState; }
+
+ cCriticalSection & GetCriticalSection(void) { return m_CriticalSection; }
+
+ /// Removes a previously referenced object (luaL_unref())
+ void Unreference(int a_LuaRef);
+
+ /// Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true
+ bool CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse);
+
+ /// Calls the plugin-specified "cLuaWindow slot changed" callback.
+ void CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum);
+
+ /// Returns the name of Lua function that should handle the specified hook type in the older (#121) API
+ static const char * GetHookFnName(int a_HookType);
+
+ /** Adds a Lua function to be called for the specified hook.
+ The function has to be on the Lua stack at the specified index a_FnRefIdx
+ Returns true if the hook was added successfully.
+ */
+ bool AddHookRef(int a_HookType, int a_FnRefIdx);
+
+ // The following templates allow calls to arbitrary Lua functions residing in the plugin:
+
+ /// Call a Lua function with 0 args
+ template <typename FnT> bool Call(FnT a_Fn)
+ {
+ cCSLock Lock(m_CriticalSection);
+ return m_LuaState.Call(a_Fn);
+ }
+
+ /// Call a Lua function with 1 arg
+ template <typename FnT, typename ArgT0> bool Call(FnT a_Fn, ArgT0 a_Arg0)
+ {
+ cCSLock Lock(m_CriticalSection);
+ return m_LuaState.Call(a_Fn, a_Arg0);
+ }
+
+ /// Call a Lua function with 2 args
+ template <typename FnT, typename ArgT0, typename ArgT1> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1)
+ {
+ cCSLock Lock(m_CriticalSection);
+ return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1);
+ }
+
+ /// Call a Lua function with 3 args
+ template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2)
+ {
+ cCSLock Lock(m_CriticalSection);
+ return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2);
+ }
+
+ /// Call a Lua function with 4 args
+ template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2, typename ArgT3> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3)
+ {
+ cCSLock Lock(m_CriticalSection);
+ return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2, a_Arg3);
+ }
+
+protected:
+ /// Maps command name into Lua function reference
+ typedef std::map<AString, int> CommandMap;
+
+ /// Provides an array of Lua function references
+ typedef std::vector<cLuaState::cRef *> cLuaRefs;
+
+ /// Maps hook types into arrays of Lua function references to call for each hook type
+ typedef std::map<int, cLuaRefs> cHookMap;
+
+ cCriticalSection m_CriticalSection;
+ cLuaState m_LuaState;
+
+ CommandMap m_Commands;
+ CommandMap m_ConsoleCommands;
+
+ cHookMap m_HookMap;
+
+ /// Releases all Lua references and closes the LuaState
+ void Close(void);
+} ; // tolua_export
+
+
+
+
diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp
new file mode 100644
index 000000000..f9d8d7d02
--- /dev/null
+++ b/src/PluginManager.cpp
@@ -0,0 +1,1668 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "PluginManager.h"
+#include "Plugin.h"
+#include "PluginLua.h"
+#include "WebAdmin.h"
+#include "Item.h"
+#include "Root.h"
+#include "Server.h"
+#include "CommandOutput.h"
+
+#include "lib/iniFile/iniFile.h"
+#include "tolua++.h"
+#include "Entities/Player.h"
+
+
+
+
+
+cPluginManager * cPluginManager::Get(void)
+{
+ return cRoot::Get()->GetPluginManager();
+}
+
+
+
+
+
+cPluginManager::cPluginManager(void) :
+ m_bReloadPlugins(false)
+{
+}
+
+
+
+
+
+cPluginManager::~cPluginManager()
+{
+ UnloadPluginsNow();
+}
+
+
+
+
+
+void cPluginManager::ReloadPlugins(void)
+{
+ m_bReloadPlugins = true;
+}
+
+
+
+
+
+void cPluginManager::FindPlugins(void)
+{
+ AString PluginsPath = FILE_IO_PREFIX + AString( "Plugins/" );
+
+ // First get a clean list of only the currently running plugins, we don't want to mess those up
+ for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();)
+ {
+ if (itr->second == NULL)
+ {
+ PluginMap::iterator thiz = itr;
+ ++thiz;
+ m_Plugins.erase( itr );
+ itr = thiz;
+ continue;
+ }
+ ++itr;
+ }
+
+ AStringVector Files = cFile::GetFolderContents(PluginsPath.c_str());
+ for (AStringVector::const_iterator itr = Files.begin(); itr != Files.end(); ++itr)
+ {
+ if ((*itr == ".") || (*itr == "..") || (!cFile::IsFolder(PluginsPath + *itr)))
+ {
+ // We only want folders, and don't want "." or ".."
+ continue;
+ }
+
+ // Add plugin name/directory to the list
+ if (m_Plugins.find(*itr) == m_Plugins.end())
+ {
+ m_Plugins[*itr] = NULL;
+ }
+ }
+}
+
+
+
+
+
+void cPluginManager::ReloadPluginsNow(void)
+{
+ cIniFile a_SettingsIni;
+ a_SettingsIni.ReadFile("settings.ini");
+ ReloadPluginsNow(a_SettingsIni);
+}
+
+
+
+
+
+void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
+{
+ LOG("-- Loading Plugins --");
+ m_bReloadPlugins = false;
+ UnloadPluginsNow();
+
+ FindPlugins();
+
+ cServer::BindBuiltInConsoleCommands();
+
+ // Check if the Plugins section exists.
+ int KeyNum = a_SettingsIni.FindKey("Plugins");
+
+ // If it does, how many plugins are there?
+ unsigned int NumPlugins = ((KeyNum != -1) ? (a_SettingsIni.GetNumValues(KeyNum)) : 0);
+
+ if (KeyNum == -1)
+ {
+ InsertDefaultPlugins(a_SettingsIni);
+ }
+ else if (NumPlugins > 0)
+ {
+ for(unsigned int i = 0; i < NumPlugins; i++)
+ {
+ AString ValueName = a_SettingsIni.GetValueName(KeyNum, i);
+ if (ValueName.compare("Plugin") == 0)
+ {
+ AString PluginFile = a_SettingsIni.GetValue(KeyNum, i);
+ if (!PluginFile.empty())
+ {
+ if (m_Plugins.find(PluginFile) != m_Plugins.end())
+ {
+ LoadPlugin( PluginFile );
+ }
+ }
+ }
+ }
+ }
+
+ if (GetNumPlugins() == 0)
+ {
+ LOG("-- No Plugins Loaded --");
+ }
+ else if (GetNumPlugins() > 1)
+ {
+ LOG("-- Loaded %i Plugins --", GetNumPlugins());
+ }
+ else
+ {
+ LOG("-- Loaded 1 Plugin --");
+ }
+}
+
+
+
+
+
+void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni)
+{
+ a_SettingsIni.AddKeyName("Plugins");
+ a_SettingsIni.AddKeyComment("Plugins", " Plugin=Debuggers");
+ a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify");
+ a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx");
+ a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump");
+ a_SettingsIni.SetValue("Plugins", "Plugin", "Core");
+ a_SettingsIni.SetValue("Plugins", "Plugin", "TransAPI");
+ a_SettingsIni.SetValue("Plugins", "Plugin", "ChatLog");
+}
+
+
+
+
+
+void cPluginManager::Tick(float a_Dt)
+{
+ while (!m_DisablePluginList.empty())
+ {
+ RemovePlugin(m_DisablePluginList.front());
+ m_DisablePluginList.pop_front();
+ }
+
+ if (m_bReloadPlugins)
+ {
+ ReloadPluginsNow();
+ }
+
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_TICK);
+ if (Plugins != m_Hooks.end())
+ {
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ (*itr)->Tick(a_Dt);
+ }
+ }
+}
+
+
+
+
+
+bool cPluginManager::CallHookBlockToPickups(
+ cWorld * a_World, cEntity * a_Digger,
+ int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ cItems & a_Pickups
+)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_BLOCK_TO_PICKUPS);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_Pickups))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookChat(cPlayer * a_Player, AString & a_Message)
+{
+ if (ExecuteCommand(a_Player, a_Message))
+ {
+ return true;
+ }
+
+ // Check if it was a standard command (starts with a slash)
+ if (!a_Message.empty() && (a_Message[0] == '/'))
+ {
+ AStringVector Split(StringSplit(a_Message, " "));
+ ASSERT(!Split.empty()); // This should not happen - we know there's at least one char in the message so the split needs to be at least one item long
+ a_Player->SendMessage(Printf("Unknown Command: \"%s\"", Split[0].c_str()));
+ LOGINFO("Player \"%s\" issued an unknown command: \"%s\"", a_Player->GetName().c_str(), a_Message.c_str());
+ return true; // Cancel sending
+ }
+
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_CHAT);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnChat(a_Player, a_Message))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookChunkAvailable(cWorld * a_World, int a_ChunkX, int a_ChunkZ)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_AVAILABLE);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnChunkAvailable(a_World, a_ChunkX, a_ChunkZ))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_GENERATED);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_GENERATING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnChunkGenerating(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookChunkUnloaded(cWorld * a_World, int a_ChunkX, int a_ChunkZ)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_UNLOADED);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnChunkUnloaded(a_World, a_ChunkX, a_ChunkZ))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookChunkUnloading(cWorld * a_World, int a_ChunkX, int a_ChunkZ)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_UNLOADING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnChunkUnloading(a_World, a_ChunkX, a_ChunkZ))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookCollectingPickup(cPlayer * a_Player, cPickup & a_Pickup)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_COLLECTING_PICKUP);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnCollectingPickup(a_Player, &a_Pickup))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_CRAFTING_NO_RECIPE);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnCraftingNoRecipe(a_Player, a_Grid, a_Recipe))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Reason)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_DISCONNECT);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnDisconnect(a_Player, a_Reason))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_EXECUTE_COMMAND);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnExecuteCommand(a_Player, a_Split))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_EXPLODED);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnExploded(a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_EXPLODING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnExploding(a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookHandshake(cClientHandle * a_ClientHandle, const AString & a_Username)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_HANDSHAKE);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnHandshake(a_ClientHandle, a_Username))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_HOPPER_PULLING_ITEM);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnHopperPullingItem(a_World, a_Hopper, a_DstSlotNum, a_SrcEntity, a_SrcSlotNum))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_HOPPER_PUSHING_ITEM);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnHopperPushingItem(a_World, a_Hopper, a_SrcSlotNum, a_DstEntity, a_DstSlotNum))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookKilling(cEntity & a_Victim, cEntity * a_Killer)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_KILLING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnKilling(a_Victim, a_Killer))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_LOGIN);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnLogin(a_Client, a_ProtocolVersion, a_Username))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerAnimation(cPlayer & a_Player, int a_Animation)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_ANIMATION);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerAnimation(a_Player, a_Animation))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_BREAKING_BLOCK);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerBreakingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_BROKEN_BLOCK);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerBrokenBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerEating(cPlayer & a_Player)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_EATING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerEating(a_Player))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerJoined(cPlayer & a_Player)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_JOINED);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerJoined(a_Player))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_LEFT_CLICK);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerLeftClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_MOVING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerMoved(a_Player))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+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)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_PLACED_BLOCK);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ 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))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+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)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_PLACING_BLOCK);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ 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))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::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)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_RIGHT_CLICK);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerRightClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_RIGHT_CLICKING_ENTITY);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerRightClickingEntity(a_Player, a_Entity))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerShooting(cPlayer & a_Player)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SHOOTING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerShooting(a_Player))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerSpawned(cPlayer & a_Player)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SPAWNED);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerSpawned(a_Player))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerTossingItem(cPlayer & a_Player)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_TOSSING_ITEM);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerTossingItem(a_Player))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerUsedBlock(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)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USED_BLOCK);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerUsedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USED_ITEM);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerUsedItem(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerUsingBlock(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)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USING_BLOCK);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerUsingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USING_ITEM);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerUsingItem(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_POST_CRAFTING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPostCrafting(a_Player, a_Grid, a_Recipe))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PRE_CRAFTING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPreCrafting(a_Player, a_Grid, a_Recipe))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookSpawnedEntity(cWorld & a_World, cEntity & a_Entity)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNED_ENTITY);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnSpawnedEntity(a_World, a_Entity))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+bool cPluginManager::CallHookSpawnedMonster(cWorld & a_World, cMonster & a_Monster)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNED_MONSTER);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnSpawnedMonster(a_World, a_Monster))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+bool cPluginManager::CallHookSpawningEntity(cWorld & a_World, cEntity & a_Entity)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNING_ENTITY);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnSpawningEntity(a_World, a_Entity))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookSpawningMonster(cWorld & a_World, cMonster & a_Monster)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNING_MONSTER);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnSpawningMonster(a_World, a_Monster))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_TAKE_DAMAGE);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnTakeDamage(a_Receiver, a_TDI))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookUpdatingSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATING_SIGN);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnUpdatingSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookUpdatedSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATED_SIGN);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnUpdatedSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookWeatherChanged(cWorld & a_World)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGED);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnWeatherChanged(a_World))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookWeatherChanging(cWorld & a_World, eWeather & a_NewWeather)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnWeatherChanging(a_World, a_NewWeather))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookWorldTick(cWorld & a_World, float a_Dt)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_WORLD_TICK);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnWorldTick(a_World, a_Dt))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions)
+{
+ ASSERT(a_Player != NULL);
+
+ AStringVector Split(StringSplit(a_Command, " "));
+ if (Split.empty())
+ {
+ return false;
+ }
+
+ CommandMap::iterator cmd = m_Commands.find(Split[0]);
+ if (cmd == m_Commands.end())
+ {
+ // Command not found
+ return false;
+ }
+
+ // Ask plugins first if a command is okay to execute the command:
+ if (CallHookExecuteCommand(a_Player, Split))
+ {
+ LOGINFO("Player \"%s\" tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player->GetName().c_str(), Split[0].c_str());
+ return false;
+ }
+
+ if (
+ a_ShouldCheckPermissions &&
+ !cmd->second.m_Permission.empty() &&
+ !a_Player->HasPermission(cmd->second.m_Permission)
+ )
+ {
+ LOGINFO("Player \"%s\" tried to execute forbidden command \"%s\".", a_Player->GetName().c_str(), Split[0].c_str());
+ return false;
+ }
+
+ ASSERT(cmd->second.m_Plugin != NULL);
+
+ return cmd->second.m_Plugin->HandleCommand(Split, a_Player);
+}
+
+
+
+
+
+cPlugin * cPluginManager::GetPlugin( const AString & a_Plugin ) const
+{
+ for( PluginMap::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr )
+ {
+ if (itr->second == NULL ) continue;
+ if (itr->second->GetName().compare(a_Plugin) == 0)
+ {
+ return itr->second;
+ }
+ }
+ return 0;
+}
+
+
+
+
+
+const cPluginManager::PluginMap & cPluginManager::GetAllPlugins() const
+{
+ return m_Plugins;
+}
+
+
+
+
+
+void cPluginManager::UnloadPluginsNow()
+{
+ m_Hooks.clear();
+
+ while (!m_Plugins.empty())
+ {
+ RemovePlugin(m_Plugins.begin()->second);
+ }
+
+ m_Commands.clear();
+ m_ConsoleCommands.clear();
+}
+
+
+
+
+
+bool cPluginManager::DisablePlugin(const AString & a_PluginName)
+{
+ PluginMap::iterator itr = m_Plugins.find(a_PluginName);
+ if (itr == m_Plugins.end())
+ {
+ return false;
+ }
+
+ if (itr->first.compare(a_PluginName) == 0) // _X 2013_02_01: wtf? Isn't this supposed to be what find() does?
+ {
+ m_DisablePluginList.push_back(itr->second);
+ itr->second = NULL; // Get rid of this thing right away
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::LoadPlugin(const AString & a_PluginName)
+{
+ return AddPlugin(new cPluginLua(a_PluginName.c_str()));
+}
+
+
+
+
+
+void cPluginManager::RemoveHooks(cPlugin * a_Plugin)
+{
+ for (HookMap::iterator itr = m_Hooks.begin(), end = m_Hooks.end(); itr != end; ++itr)
+ {
+ itr->second.remove(a_Plugin);
+ }
+}
+
+
+
+
+
+void cPluginManager::RemovePlugin(cPlugin * a_Plugin)
+{
+ for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
+ {
+ if (itr->second == a_Plugin)
+ {
+ m_Plugins.erase(itr);
+ break;
+ }
+ }
+
+ RemovePluginCommands(a_Plugin);
+ RemovePluginConsoleCommands(a_Plugin);
+ RemoveHooks(a_Plugin);
+ if (a_Plugin != NULL)
+ {
+ a_Plugin->OnDisable();
+ }
+ delete a_Plugin;
+}
+
+
+
+
+
+void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin)
+{
+ if (a_Plugin != NULL)
+ {
+ a_Plugin->ClearCommands();
+ }
+
+ for (CommandMap::iterator itr = m_Commands.begin(); itr != m_Commands.end();)
+ {
+ if (itr->second.m_Plugin == a_Plugin)
+ {
+ CommandMap::iterator EraseMe = itr; // Stupid GCC doesn't have a std::map::erase() that would return the next iterator
+ ++itr;
+ m_Commands.erase(EraseMe);
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - m_Commands[]
+}
+
+
+
+
+
+bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString)
+{
+ CommandMap::iterator cmd = m_Commands.find(a_Command);
+ if (cmd != m_Commands.end())
+ {
+ LOGWARNING("Command \"%s\" is already bound to plugin \"%s\".", a_Command.c_str(), cmd->second.m_Plugin->GetName().c_str());
+ return false;
+ }
+
+ m_Commands[a_Command].m_Plugin = a_Plugin;
+ m_Commands[a_Command].m_Permission = a_Permission;
+ m_Commands[a_Command].m_HelpString = a_HelpString;
+ return true;
+}
+
+
+
+
+
+bool cPluginManager::ForEachCommand(cCommandEnumCallback & a_Callback)
+{
+ for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr)
+ {
+ if (a_Callback.Command(itr->first, itr->second.m_Plugin, itr->second.m_Permission, itr->second.m_HelpString))
+ {
+ return false;
+ }
+ } // for itr - m_Commands[]
+ return true;
+}
+
+
+
+
+
+bool cPluginManager::IsCommandBound(const AString & a_Command)
+{
+ return (m_Commands.find(a_Command) != m_Commands.end());
+}
+
+
+
+
+
+AString cPluginManager::GetCommandPermission(const AString & a_Command)
+{
+ CommandMap::iterator cmd = m_Commands.find(a_Command);
+ return (cmd == m_Commands.end()) ? "" : cmd->second.m_Permission;
+}
+
+
+
+
+
+bool cPluginManager::ExecuteCommand(cPlayer * a_Player, const AString & a_Command)
+{
+ return HandleCommand(a_Player, a_Command, true);
+}
+
+
+
+
+
+bool cPluginManager::ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command)
+{
+ return HandleCommand(a_Player, a_Command, false);
+}
+
+
+
+
+
+void cPluginManager::RemovePluginConsoleCommands(cPlugin * a_Plugin)
+{
+ if (a_Plugin != NULL)
+ {
+ a_Plugin->ClearConsoleCommands();
+ }
+
+ for (CommandMap::iterator itr = m_ConsoleCommands.begin(); itr != m_ConsoleCommands.end();)
+ {
+ if (itr->second.m_Plugin == a_Plugin)
+ {
+ CommandMap::iterator EraseMe = itr; // Stupid GCC doesn't have a std::map::erase() that would return the next iterator
+ ++itr;
+ m_ConsoleCommands.erase(EraseMe);
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - m_Commands[]
+}
+
+
+
+
+
+bool cPluginManager::BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString)
+{
+ CommandMap::iterator cmd = m_ConsoleCommands.find(a_Command);
+ if (cmd != m_ConsoleCommands.end())
+ {
+ if (cmd->second.m_Plugin == NULL)
+ {
+ LOGWARNING("Console command \"%s\" is already bound internally by MCServer, cannot bind in plugin \"%s\".", a_Command.c_str(), a_Plugin->GetName().c_str());
+ }
+ else
+ {
+ LOGWARNING("Console command \"%s\" is already bound to plugin \"%s\", cannot bind in plugin \"%s\".", a_Command.c_str(), cmd->second.m_Plugin->GetName().c_str(), a_Plugin->GetName().c_str());
+ }
+ return false;
+ }
+
+ m_ConsoleCommands[a_Command].m_Plugin = a_Plugin;
+ m_ConsoleCommands[a_Command].m_Permission = "";
+ m_ConsoleCommands[a_Command].m_HelpString = a_HelpString;
+ return true;
+}
+
+
+
+
+
+bool cPluginManager::ForEachConsoleCommand(cCommandEnumCallback & a_Callback)
+{
+ for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr)
+ {
+ if (a_Callback.Command(itr->first, itr->second.m_Plugin, "", itr->second.m_HelpString))
+ {
+ return false;
+ }
+ } // for itr - m_Commands[]
+ return true;
+}
+
+
+
+
+
+bool cPluginManager::IsConsoleCommandBound(const AString & a_Command)
+{
+ return (m_ConsoleCommands.find(a_Command) != m_ConsoleCommands.end());
+}
+
+
+
+
+
+bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
+{
+ if (a_Split.empty())
+ {
+ return false;
+ }
+
+ CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]);
+ if (cmd == m_ConsoleCommands.end())
+ {
+ // Command not found
+ return false;
+ }
+
+ if (cmd->second.m_Plugin == NULL)
+ {
+ // This is a built-in command
+ return false;
+ }
+
+ // Ask plugins first if a command is okay to execute the console command:
+ if (CallHookExecuteCommand(NULL, a_Split))
+ {
+ a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str());
+ return false;
+ }
+
+ return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output);
+}
+
+
+
+
+
+void cPluginManager::TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player)
+{
+ for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr)
+ {
+ if (NoCaseCompare(itr->first.substr(0, a_Text.length()), a_Text) != 0)
+ {
+ // Command name doesn't match
+ continue;
+ }
+ if ((a_Player != NULL) && !a_Player->HasPermission(itr->second.m_Permission))
+ {
+ // Player doesn't have permission for the command
+ continue;
+ }
+ a_Results.push_back(itr->first);
+ }
+}
+
+
+
+
+
+bool cPluginManager::IsValidHookType(int a_HookType)
+{
+ return ((a_HookType >= 0) && (a_HookType <= HOOK_MAX));
+}
+
+
+
+
+
+bool cPluginManager::AddPlugin(cPlugin * a_Plugin)
+{
+ m_Plugins[a_Plugin->GetDirectory()] = a_Plugin;
+ if (a_Plugin->Initialize())
+ {
+ // Initialization OK
+ return true;
+ }
+
+ // Initialization failed
+ RemovePlugin(a_Plugin); // Also undoes any registrations that Initialize() might have made
+ return false;
+}
+
+
+
+
+
+void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook)
+{
+ if (!a_Plugin)
+ {
+ LOGWARN("Called cPluginManager::AddHook() with a_Plugin == NULL");
+ return;
+ }
+ PluginList & Plugins = m_Hooks[a_Hook];
+ Plugins.remove(a_Plugin);
+ Plugins.push_back(a_Plugin);
+}
+
+
+
+
+
+unsigned int cPluginManager::GetNumPlugins() const
+{
+ return m_Plugins.size();
+}
+
+
+
+
diff --git a/src/PluginManager.h b/src/PluginManager.h
new file mode 100644
index 000000000..4140bffb5
--- /dev/null
+++ b/src/PluginManager.h
@@ -0,0 +1,295 @@
+
+#pragma once
+
+#include "Item.h"
+
+
+
+
+
+class cPlugin;
+
+// fwd: World.h
+class cWorld;
+
+// fwd: ChunkDesc.h
+class cChunkDesc;
+
+// fwd: Entities/Entity.h
+class cEntity;
+
+// fwd: Mobs/Monster.h
+class cMonster;
+
+// fwd: Player.h
+class cPlayer;
+
+// fwd: CraftingRecipes.h
+class cCraftingGrid;
+class cCraftingRecipe;
+
+// fwd: Pickup.h
+class cPickup;
+
+// fwd: Pawn.h
+struct TakeDamageInfo;
+class cPawn;
+
+// fwd: CommandOutput.h
+class cCommandOutputCallback;
+
+// fwd: BlockEntities/HopperEntity.h
+class cHopperEntity;
+
+// fwd: BlockEntities/BlockEntityWithItems.h
+class cBlockEntityWithItems;
+
+
+
+
+
+class cPluginManager // tolua_export
+{ // tolua_export
+public: // tolua_export
+
+ // Called each tick
+ virtual void Tick(float a_Dt);
+
+ // tolua_begin
+ enum PluginHook
+ {
+ HOOK_BLOCK_TO_PICKUPS,
+ HOOK_CHAT,
+ HOOK_CHUNK_AVAILABLE,
+ HOOK_CHUNK_GENERATED,
+ HOOK_CHUNK_GENERATING,
+ HOOK_CHUNK_UNLOADED,
+ HOOK_CHUNK_UNLOADING,
+ HOOK_COLLECTING_PICKUP,
+ HOOK_CRAFTING_NO_RECIPE,
+ HOOK_DISCONNECT,
+ HOOK_EXECUTE_COMMAND,
+ HOOK_EXPLODED,
+ HOOK_EXPLODING,
+ HOOK_HANDSHAKE,
+ HOOK_HOPPER_PULLING_ITEM,
+ HOOK_HOPPER_PUSHING_ITEM,
+ HOOK_KILLING,
+ HOOK_LOGIN,
+ HOOK_PLAYER_ANIMATION,
+ HOOK_PLAYER_BREAKING_BLOCK,
+ HOOK_PLAYER_BROKEN_BLOCK,
+ HOOK_PLAYER_EATING,
+ HOOK_PLAYER_JOINED,
+ HOOK_PLAYER_LEFT_CLICK,
+ HOOK_PLAYER_MOVING,
+ HOOK_PLAYER_PLACED_BLOCK,
+ HOOK_PLAYER_PLACING_BLOCK,
+ HOOK_PLAYER_RIGHT_CLICK,
+ HOOK_PLAYER_RIGHT_CLICKING_ENTITY,
+ HOOK_PLAYER_SHOOTING,
+ HOOK_PLAYER_SPAWNED,
+ HOOK_PLAYER_TOSSING_ITEM,
+ HOOK_PLAYER_USED_BLOCK,
+ HOOK_PLAYER_USED_ITEM,
+ HOOK_PLAYER_USING_BLOCK,
+ HOOK_PLAYER_USING_ITEM,
+ HOOK_POST_CRAFTING,
+ HOOK_PRE_CRAFTING,
+ HOOK_SPAWNED_ENTITY,
+ HOOK_SPAWNED_MONSTER,
+ HOOK_SPAWNING_ENTITY,
+ HOOK_SPAWNING_MONSTER,
+ HOOK_TAKE_DAMAGE,
+ HOOK_TICK,
+ HOOK_UPDATED_SIGN,
+ HOOK_UPDATING_SIGN,
+ HOOK_WEATHER_CHANGED,
+ HOOK_WEATHER_CHANGING,
+ HOOK_WORLD_TICK,
+
+ // Note that if a hook type is added, it may need processing in cPlugin::CanAddHook() descendants,
+ // and it definitely needs adding in cPluginLua::GetHookFnName() !
+
+ // Keep these two as the last items, they are used for validity checking and get their values automagically
+ HOOK_NUM_HOOKS,
+ HOOK_MAX = HOOK_NUM_HOOKS - 1,
+ } ;
+ // tolua_end
+
+ /// Used as a callback for enumerating bound commands
+ class cCommandEnumCallback
+ {
+ public:
+ /** Called for each command; return true to abort enumeration
+ For console commands, a_Permission is not used (set to empty string)
+ */
+ virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0;
+ } ;
+
+ /// Returns the instance of the Plugin Manager (there is only ever one)
+ static cPluginManager * Get(void); // tolua_export
+
+ typedef std::map< AString, cPlugin * > PluginMap;
+ typedef std::list< cPlugin * > PluginList;
+ cPlugin * GetPlugin( const AString & a_Plugin ) const; // tolua_export
+ const PluginMap & GetAllPlugins() const; // >> EXPORTED IN MANUALBINDINGS <<
+
+ void FindPlugins(); // tolua_export
+ void ReloadPlugins(); // tolua_export
+
+ /// Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add
+ void AddHook(cPlugin * a_Plugin, int a_HookType);
+
+ unsigned int GetNumPlugins() const; // tolua_export
+
+ // Calls for individual hooks. Each returns false if the action is to continue or true if the plugin wants to abort
+ bool CallHookBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups);
+ bool CallHookChat (cPlayer * a_Player, AString & a_Message);
+ bool CallHookChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
+ bool CallHookChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
+ bool CallHookChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
+ bool CallHookChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
+ bool CallHookChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
+ bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup);
+ bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
+ bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason);
+ bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd
+ bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
+ bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
+ bool CallHookHandshake (cClientHandle * a_ClientHandle, const AString & a_Username);
+ bool CallHookHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum);
+ bool CallHookHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum);
+ bool CallHookKilling (cEntity & a_Victim, cEntity * a_Killer);
+ bool CallHookLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username);
+ bool CallHookPlayerAnimation (cPlayer & a_Player, int a_Animation);
+ bool CallHookPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ bool CallHookPlayerEating (cPlayer & a_Player);
+ bool CallHookPlayerJoined (cPlayer & a_Player);
+ bool CallHookPlayerMoving (cPlayer & a_Player);
+ 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 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);
+ bool CallHookPlayerSpawned (cPlayer & a_Player);
+ bool CallHookPlayerTossingItem (cPlayer & a_Player);
+ bool CallHookPlayerUsedBlock (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 CallHookPlayerUsedItem (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 CallHookPlayerUsingBlock (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 CallHookPlayerUsingItem (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 CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
+ bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
+ bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity);
+ bool CallHookSpawnedMonster (cWorld & a_World, cMonster & a_Monster);
+ bool CallHookSpawningEntity (cWorld & a_World, cEntity & a_Entity);
+ bool CallHookSpawningMonster (cWorld & a_World, cMonster & a_Monster);
+ bool CallHookTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TDI);
+ bool CallHookUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player);
+ bool CallHookUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player);
+ bool CallHookWeatherChanged (cWorld & a_World);
+ bool CallHookWeatherChanging (cWorld & a_World, eWeather & a_NewWeather);
+ bool CallHookWorldTick (cWorld & a_World, float a_Dt);
+
+ bool DisablePlugin(const AString & a_PluginName); // tolua_export
+ bool LoadPlugin (const AString & a_PluginName); // tolua_export
+
+ /// Removes all hooks the specified plugin has registered
+ void RemoveHooks(cPlugin * a_Plugin);
+
+ /// Removes the plugin from the internal structures and deletes its object.
+ void RemovePlugin(cPlugin * a_Plugin);
+
+ /// Removes all command bindings that the specified plugin has made
+ void RemovePluginCommands(cPlugin * a_Plugin);
+
+ /// Binds a command to the specified plugin. Returns true if successful, false if command already bound.
+ bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param
+
+ /// Calls a_Callback for each bound command, returns true if all commands were enumerated
+ bool ForEachCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Returns true if the command is in the command map
+ bool IsCommandBound(const AString & a_Command); // tolua_export
+
+ /// Returns the permission needed for the specified command; empty string if command not found
+ AString GetCommandPermission(const AString & a_Command); // tolua_export
+
+ /// Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed.
+ bool ExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export
+
+ /// Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found)
+ bool ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export
+
+ /// Removes all console command bindings that the specified plugin has made
+ void RemovePluginConsoleCommands(cPlugin * a_Plugin);
+
+ /// Binds a console command to the specified plugin. Returns true if successful, false if command already bound.
+ bool BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param
+
+ /// Calls a_Callback for each bound console command, returns true if all commands were enumerated
+ bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Returns true if the console command is in the command map
+ bool IsConsoleCommandBound(const AString & a_Command); // tolua_export
+
+ /// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback
+ bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
+
+ /** Appends all commands beginning with a_Text (case-insensitive) into a_Results.
+ If a_Player is not NULL, only commands for which the player has permissions are added.
+ */
+ void TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player);
+
+ /// Returns true if the specified hook type is within the allowed range
+ static bool IsValidHookType(int a_HookType);
+
+private:
+ friend class cRoot;
+
+ class cCommandReg
+ {
+ public:
+ cPlugin * m_Plugin;
+ AString m_Permission; // Not used for console commands
+ AString m_HelpString;
+ } ;
+
+ typedef std::map<int, cPluginManager::PluginList> HookMap;
+ typedef std::map<AString, cCommandReg> CommandMap;
+
+ PluginList m_DisablePluginList;
+ PluginMap m_Plugins;
+ HookMap m_Hooks;
+ CommandMap m_Commands;
+ CommandMap m_ConsoleCommands;
+
+ bool m_bReloadPlugins;
+
+ cPluginManager();
+ ~cPluginManager();
+
+ /// Reloads all plugins, defaulting to settings.ini for settings location
+ void ReloadPluginsNow(void);
+
+ /// Reloads all plugins with a cIniFile object expected to be initialised to settings.ini
+ void ReloadPluginsNow(cIniFile & a_SettingsIni);
+
+ /// Unloads all plugins
+ void UnloadPluginsNow(void);
+
+ /// Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini
+ void InsertDefaultPlugins(cIniFile & a_SettingsIni);
+
+ /// Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again.
+ bool AddPlugin(cPlugin * a_Plugin);
+
+ /// Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled.
+ bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions);
+} ; // tolua_export
+
+
+
+
diff --git a/src/ProbabDistrib.cpp b/src/ProbabDistrib.cpp
new file mode 100644
index 000000000..5fa17c276
--- /dev/null
+++ b/src/ProbabDistrib.cpp
@@ -0,0 +1,142 @@
+
+// ProbabDistrib.cpp
+
+// Implements the cProbabDistrib class representing a discrete probability distribution curve and random generator
+
+#include "Globals.h"
+#include "ProbabDistrib.h"
+#include "MersenneTwister.h"
+
+
+
+
+
+
+cProbabDistrib::cProbabDistrib(int a_MaxValue) :
+ m_MaxValue(a_MaxValue),
+ m_Sum(-1)
+{
+}
+
+
+
+
+
+
+void cProbabDistrib::SetPoints(const cProbabDistrib::cPoints & a_Points)
+{
+ ASSERT(!a_Points.empty());
+ m_Sum = 0;
+ m_Cumulative.clear();
+ m_Cumulative.reserve(a_Points.size() + 1);
+ int ProbSum = 0;
+ int LastProb = 0;
+ int LastValue = -1;
+ if (a_Points[0].m_Value != 0)
+ {
+ m_Cumulative.push_back(cPoint(0, 0)); // Always push in the [0, 0] point for easier search algorithm bounds
+ LastValue = 0;
+ }
+ for (cPoints::const_iterator itr = a_Points.begin(), end = a_Points.end(); itr != end; ++itr)
+ {
+ if (itr->m_Value == LastValue)
+ {
+ continue;
+ }
+
+ // Add the current trapezoid to the sum:
+ ProbSum += (LastProb + itr->m_Probability) * (itr->m_Value - LastValue) / 2;
+ LastProb = itr->m_Probability;
+ LastValue = itr->m_Value;
+ m_Cumulative.push_back(cPoint(itr->m_Value, ProbSum));
+ } // for itr - a_Points[]
+ if (LastValue != m_MaxValue)
+ {
+ m_Cumulative.push_back(cPoint(m_MaxValue, 0)); // Always push in the last point for easier search algorithm bounds
+ }
+ m_Sum = ProbSum;
+}
+
+
+
+
+
+bool cProbabDistrib::SetDefString(const AString & a_DefString)
+{
+ AStringVector Points = StringSplitAndTrim(a_DefString, ";");
+ if (Points.empty())
+ {
+ return false;
+ }
+ cPoints Pts;
+ for (AStringVector::const_iterator itr = Points.begin(), end = Points.end(); itr != end; ++itr)
+ {
+ AStringVector Split = StringSplitAndTrim(*itr, ",");
+ if (Split.size() != 2)
+ {
+ // Bad format
+ return false;
+ }
+ int Value = atoi(Split[0].c_str());
+ int Prob = atoi(Split[1].c_str());
+ if (
+ ((Value == 0) && (Split[0] != "0")) ||
+ ((Prob == 0) && (Split[1] != "0"))
+ )
+ {
+ // Number parse error
+ return false;
+ }
+ Pts.push_back(cPoint(Value, Prob));
+ } // for itr - Points[]
+
+ SetPoints(Pts);
+ return true;
+}
+
+
+
+
+
+int cProbabDistrib::Random(MTRand & a_Rand) const
+{
+ int v = a_Rand.randInt(m_Sum);
+ return MapValue(v);
+}
+
+
+
+
+
+int cProbabDistrib::MapValue(int a_OrigValue) const
+{
+ ASSERT(a_OrigValue >= 0);
+ ASSERT(a_OrigValue < m_Sum);
+
+ // Binary search through m_Cumulative for placement:
+ size_t Lo = 0;
+ size_t Hi = m_Cumulative.size() - 1;
+ while (Hi - Lo > 1)
+ {
+ int Mid = (Lo + Hi) / 2;
+ int MidProbab = m_Cumulative[Mid].m_Probability;
+ if (MidProbab < a_OrigValue)
+ {
+ Lo = Mid;
+ }
+ else
+ {
+ Hi = Mid;
+ }
+ }
+ ASSERT(Hi - Lo == 1);
+
+ // Linearly interpolate between Lo and Hi:
+ int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability;
+ int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value;
+ return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif;
+}
+
+
+
+
diff --git a/src/ProbabDistrib.h b/src/ProbabDistrib.h
new file mode 100644
index 000000000..ddaadd9b7
--- /dev/null
+++ b/src/ProbabDistrib.h
@@ -0,0 +1,74 @@
+
+// ProbabDistrib.h
+
+// Declares the cProbabDistrib class representing a discrete probability distribution curve and random generator
+
+/*
+Usage:
+1, Create a cProbabDistrib instance
+2, Initialize the distribution either programmatically, using the SetPoints() function, or using a definition string
+3, Ask for random numbers in that probability distribution using the Random() function
+*/
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class MTRand;
+
+
+
+
+
+class cProbabDistrib
+{
+public:
+ class cPoint
+ {
+ public:
+ int m_Value;
+ int m_Probability;
+
+ cPoint(int a_Value, int a_Probability) :
+ m_Value(a_Value),
+ m_Probability(a_Probability)
+ {
+ }
+ } ;
+
+ typedef std::vector<cPoint> cPoints;
+
+
+ cProbabDistrib(int a_MaxValue);
+
+ /// Sets the distribution curve using an array of [value, probability] points, linearly interpolated. a_Points must not be empty.
+ void SetPoints(const cPoints & a_Points);
+
+ /// Sets the distribution curve using a definition string; returns true on successful parse
+ bool SetDefString(const AString & a_DefString);
+
+ /// Gets a random value from a_Rand, shapes it into the distribution curve and returns the value.
+ int Random(MTRand & a_Rand) const;
+
+ /// Maps value in range [0, m_Sum] into the range [0, m_MaxValue] using the stored probability
+ int MapValue(int a_OrigValue) const;
+
+ int GetSum(void) const { return m_Sum; }
+
+protected:
+
+ int m_MaxValue;
+ cPoints m_Cumulative; ///< Cumulative probability of the values, sorted, for fast bsearch lookup
+ int m_Sum; ///< Sum of all the probabilities across all values in the domain; -1 if not set
+} ;
+
+
+
+
diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp
new file mode 100644
index 000000000..d406b8b51
--- /dev/null
+++ b/src/Protocol/ChunkDataSerializer.cpp
@@ -0,0 +1,176 @@
+
+// ChunkDataSerializer.cpp
+
+// Implements the cChunkDataSerializer class representing the object that can:
+// - serialize chunk data to different protocol versions
+// - cache such serialized data for multiple clients
+
+#include "Globals.h"
+#include "ChunkDataSerializer.h"
+#include "lib/zlib/zlib.h"
+
+
+
+
+cChunkDataSerializer::cChunkDataSerializer(
+ const cChunkDef::BlockTypes & a_BlockTypes,
+ const cChunkDef::BlockNibbles & a_BlockMetas,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_BlockSkyLight,
+ const unsigned char * a_BiomeData
+) :
+ m_BlockTypes(a_BlockTypes),
+ m_BlockMetas(a_BlockMetas),
+ m_BlockLight(a_BlockLight),
+ m_BlockSkyLight(a_BlockSkyLight),
+ m_BiomeData(a_BiomeData)
+{
+}
+
+
+
+
+const AString & cChunkDataSerializer::Serialize(int a_Version)
+{
+ Serializations::const_iterator itr = m_Serializations.find(a_Version);
+ if (itr != m_Serializations.end())
+ {
+ return itr->second;
+ }
+
+ AString data;
+ switch (a_Version)
+ {
+ case RELEASE_1_2_5: Serialize29(data); break;
+ case RELEASE_1_3_2: Serialize39(data); break;
+ // TODO: Other protocol versions may serialize the data differently; implement here
+
+ default:
+ {
+ LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version);
+ ASSERT(!"Unknown chunk data serialization version");
+ break;
+ }
+ }
+ m_Serializations[a_Version] = data;
+ return m_Serializations[a_Version];
+}
+
+
+
+
+
+void cChunkDataSerializer::Serialize29(AString & a_Data)
+{
+ // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream)
+
+ const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
+ const int MetadataOffset = sizeof(m_BlockTypes);
+ const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas);
+ const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight);
+ const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight);
+ const int DataSize = BiomeOffset + BiomeDataSize;
+
+ // Temporary buffer for the composed data:
+ char AllData [DataSize];
+
+ memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes));
+ memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas));
+ memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight));
+ memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight));
+ memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize);
+
+ // Compress the data:
+ // In order not to use allocation, use a fixed-size buffer, with the size
+ // that uses the same calculation as compressBound():
+ const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16;
+ char CompressedBlockData[CompressedMaxSize];
+
+ uLongf CompressedSize = compressBound(DataSize);
+
+ // Run-time check that our compile-time guess about CompressedMaxSize was enough:
+ ASSERT(CompressedSize <= CompressedMaxSize);
+
+ compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION);
+
+ // Now put all those data into a_Data:
+
+ // "Ground-up continuous", or rather, "biome data present" flag:
+ a_Data.push_back('\x01');
+
+ // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively
+ // Also, no endian flipping is needed because of the const values
+ unsigned short BitMap1 = 0xffff;
+ unsigned short BitMap2 = 0;
+ a_Data.append((const char *)&BitMap1, sizeof(short));
+ a_Data.append((const char *)&BitMap2, sizeof(short));
+
+ Int32 CompressedSizeBE = htonl(CompressedSize);
+ a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE));
+
+ Int32 UnusedInt32 = 0;
+ a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32));
+
+ a_Data.append(CompressedBlockData, CompressedSize);
+}
+
+
+
+
+
+void cChunkDataSerializer::Serialize39(AString & a_Data)
+{
+ // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream)
+
+ const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
+ const int MetadataOffset = sizeof(m_BlockTypes);
+ const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas);
+ const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight);
+ const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight);
+ const int DataSize = BiomeOffset + BiomeDataSize;
+
+ // Temporary buffer for the composed data:
+ char AllData [DataSize];
+
+ memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes));
+ memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas));
+ memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight));
+ memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight));
+ memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize);
+
+ // Compress the data:
+ // In order not to use allocation, use a fixed-size buffer, with the size
+ // that uses the same calculation as compressBound():
+ const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16;
+ char CompressedBlockData[CompressedMaxSize];
+
+ uLongf CompressedSize = compressBound(DataSize);
+
+ // Run-time check that our compile-time guess about CompressedMaxSize was enough:
+ ASSERT(CompressedSize <= CompressedMaxSize);
+
+ compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION);
+
+ // Now put all those data into a_Data:
+
+ // "Ground-up continuous", or rather, "biome data present" flag:
+ a_Data.push_back('\x01');
+
+ // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively
+ // Also, no endian flipping is needed because of the const values
+ unsigned short BitMap1 = 0xffff;
+ unsigned short BitMap2 = 0;
+ a_Data.append((const char *)&BitMap1, sizeof(short));
+ a_Data.append((const char *)&BitMap2, sizeof(short));
+
+ Int32 CompressedSizeBE = htonl(CompressedSize);
+ a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE));
+
+ // Unlike 29, 39 doesn't have the "unused" int
+
+ a_Data.append(CompressedBlockData, CompressedSize);
+}
+
+
+
+
diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h
new file mode 100644
index 000000000..a42856356
--- /dev/null
+++ b/src/Protocol/ChunkDataSerializer.h
@@ -0,0 +1,48 @@
+
+// ChunkDataSerializer.h
+
+// Interfaces to the cChunkDataSerializer class representing the object that can:
+// - serialize chunk data to different protocol versions
+// - cache such serialized data for multiple clients
+
+
+
+
+
+class cChunkDataSerializer
+{
+protected:
+ const cChunkDef::BlockTypes & m_BlockTypes;
+ const cChunkDef::BlockNibbles & m_BlockMetas;
+ const cChunkDef::BlockNibbles & m_BlockLight;
+ const cChunkDef::BlockNibbles & m_BlockSkyLight;
+ const unsigned char * m_BiomeData;
+
+ typedef std::map<int, AString> Serializations;
+
+ Serializations m_Serializations;
+
+ void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5
+ void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2
+
+public:
+ enum
+ {
+ RELEASE_1_2_5 = 29,
+ RELEASE_1_3_2 = 39,
+ } ;
+
+ cChunkDataSerializer(
+ const cChunkDef::BlockTypes & a_BlockTypes,
+ const cChunkDef::BlockNibbles & a_BlockMetas,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_BlockSkyLight,
+ const unsigned char * a_BiomeData
+ );
+
+ const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[]
+} ;
+
+
+
+
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
new file mode 100644
index 000000000..542060ece
--- /dev/null
+++ b/src/Protocol/Protocol.h
@@ -0,0 +1,216 @@
+
+// Protocol.h
+
+// Interfaces to the cProtocol class representing the generic interface that a protocol
+// parser and serializer must implement
+
+
+
+
+
+#pragma once
+
+#include "../Defines.h"
+#include "../Endianness.h"
+
+
+
+
+class cPlayer;
+class cEntity;
+class cWindow;
+class cInventory;
+class cPawn;
+class cPickup;
+class cWorld;
+class cMonster;
+class cChunkDataSerializer;
+class cFallingBlock;
+
+
+
+
+
+typedef unsigned char Byte;
+
+
+
+
+
+class cProtocol
+{
+public:
+ cProtocol(cClientHandle * a_Client) :
+ m_Client(a_Client)
+ {
+ }
+ virtual ~cProtocol() {}
+
+ /// Called when client sends some data
+ virtual void DataReceived(const char * a_Data, int a_Size) = 0;
+
+ // Sending stuff to clients (alphabetically sorted):
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
+ virtual void SendChat (const AString & a_Message) = 0;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
+ virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
+ virtual void SendDisconnect (const AString & a_Reason) = 0;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0;
+ virtual void SendEntityLook (const cEntity & a_Entity) = 0;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) = 0;
+ virtual void SendEntityProperties (const cEntity & a_Entity) = 0;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) = 0;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0;
+ virtual void SendGameMode (eGameMode a_GameMode) = 0;
+ virtual void SendHealth (void) = 0;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
+ virtual void SendKeepAlive (int a_PingID) = 0;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0;
+ virtual void SendPlayerAbilities (void) = 0;
+ virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0;
+ virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0;
+ virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+)
+ virtual void SendPlayerMoveLook (void) = 0;
+ virtual void SendPlayerPosition (void) = 0;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
+ virtual void SendRespawn (void) = 0;
+ virtual void SendExperience (void) = 0;
+ virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0;
+ virtual void SendSpawnMob (const cMonster & a_Mob) = 0;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) = 0;
+ virtual void SendTabCompletionResults(const AStringVector & a_Results) = 0;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) = 0;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0;
+ virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0;
+ virtual void SendWeather (eWeather a_Weather) = 0;
+ virtual void SendWholeInventory (const cWindow & a_Window) = 0;
+ virtual void SendWindowClose (const cWindow & a_Window) = 0;
+ virtual void SendWindowOpen (const cWindow & a_Window) = 0;
+ virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) = 0;
+
+ /// Returns the ServerID used for authentication through session.minecraft.net
+ virtual AString GetAuthServerID(void) = 0;
+
+protected:
+ cClientHandle * m_Client;
+ cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once
+
+ /// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it
+ virtual void SendData(const char * a_Data, int a_Size) = 0;
+
+ /// Called after writing each packet, enables descendants to flush their buffers
+ virtual void Flush(void) {};
+
+ // Helpers for writing partial packet data, write using SendData()
+ void WriteByte(Byte a_Value)
+ {
+ SendData((const char *)&a_Value, 1);
+ }
+
+ void WriteShort(short a_Value)
+ {
+ a_Value = htons(a_Value);
+ SendData((const char *)&a_Value, 2);
+ }
+
+ /*
+ void WriteShort(unsigned short a_Value)
+ {
+ a_Value = htons(a_Value);
+ SendData((const char *)&a_Value, 2);
+ }
+ */
+
+ void WriteInt(int a_Value)
+ {
+ a_Value = htonl(a_Value);
+ SendData((const char *)&a_Value, 4);
+ }
+
+ void WriteUInt(unsigned int a_Value)
+ {
+ a_Value = htonl(a_Value);
+ SendData((const char *)&a_Value, 4);
+ }
+
+ void WriteInt64 (Int64 a_Value)
+ {
+ a_Value = HostToNetwork8(&a_Value);
+ SendData((const char *)&a_Value, 8);
+ }
+
+ void WriteFloat (float a_Value)
+ {
+ unsigned int val = HostToNetwork4(&a_Value);
+ SendData((const char *)&val, 4);
+ }
+
+ void WriteDouble(double a_Value)
+ {
+ unsigned long long val = HostToNetwork8(&a_Value);
+ SendData((const char *)&val, 8);
+ }
+
+ void WriteString(const AString & a_Value)
+ {
+ AString UTF16;
+ UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16);
+ WriteShort((unsigned short)(UTF16.size() / 2));
+ SendData(UTF16.data(), UTF16.size());
+ }
+
+ void WriteBool(bool a_Value)
+ {
+ WriteByte(a_Value ? 1 : 0);
+ }
+
+ void WriteVectorI(const Vector3i & a_Vector)
+ {
+ WriteInt(a_Vector.x);
+ WriteInt(a_Vector.y);
+ WriteInt(a_Vector.z);
+ }
+
+ void WriteVarInt(UInt32 a_Value)
+ {
+ // A 32-bit integer can be encoded by at most 5 bytes:
+ unsigned char b[5];
+ int idx = 0;
+ do
+ {
+ b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00);
+ a_Value = a_Value >> 7;
+ idx++;
+ } while (a_Value > 0);
+
+ SendData((const char *)b, idx);
+ }
+
+ void WriteVarUTF8String(const AString & a_String)
+ {
+ WriteVarInt(a_String.size());
+ SendData(a_String.data(), a_String.size());
+ }
+} ;
+
+
+
+
+
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
new file mode 100644
index 000000000..54be65b12
--- /dev/null
+++ b/src/Protocol/Protocol125.cpp
@@ -0,0 +1,1884 @@
+
+// Protocol125.cpp
+
+// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29)
+/*
+Documentation:
+ - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513
+ - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262
+ - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152
+*/
+
+#include "Globals.h"
+
+#include "Protocol125.h"
+
+#include "../ClientHandle.h"
+#include "../World.h"
+#include "ChunkDataSerializer.h"
+#include "../Entities/Entity.h"
+#include "../Mobs/Monster.h"
+#include "../Entities/Pickup.h"
+#include "../Entities/Player.h"
+#include "../ChatColor.h"
+#include "../UI/Window.h"
+#include "../Root.h"
+#include "../Server.h"
+
+#include "../Entities/ProjectileEntity.h"
+#include "../Entities/Minecart.h"
+#include "../Entities/FallingBlock.h"
+
+#include "../Mobs/IncludeAllMonsters.h"
+
+
+
+
+
+enum
+{
+ PACKET_KEEP_ALIVE = 0x00,
+ PACKET_LOGIN = 0x01,
+ PACKET_HANDSHAKE = 0x02,
+ PACKET_CHAT = 0x03,
+ PACKET_UPDATE_TIME = 0x04,
+ PACKET_ENTITY_EQUIPMENT = 0x05,
+ PACKET_USE_ENTITY = 0x07,
+ PACKET_UPDATE_HEALTH = 0x08,
+ PACKET_RESPAWN = 0x09,
+ PACKET_PLAYER_ON_GROUND = 0x0a,
+ PACKET_PLAYER_POS = 0x0b,
+ PACKET_PLAYER_LOOK = 0x0c,
+ PACKET_PLAYER_MOVE_LOOK = 0x0d,
+ PACKET_BLOCK_DIG = 0x0e,
+ PACKET_BLOCK_PLACE = 0x0f,
+ PACKET_SLOT_SELECTED = 0x10,
+ PACKET_USE_BED = 0x11,
+ PACKET_ANIMATION = 0x12,
+ PACKET_PACKET_ENTITY_ACTION = 0x13,
+ PACKET_PLAYER_SPAWN = 0x14,
+ PACKET_PICKUP_SPAWN = 0x15,
+ PACKET_COLLECT_PICKUP = 0x16,
+ PACKET_SPAWN_OBJECT = 0x17,
+ PACKET_SPAWN_MOB = 0x18,
+ PACKET_ENTITY_VELOCITY = 0x1c,
+ PACKET_DESTROY_ENTITY = 0x1d,
+ PACKET_ENTITY = 0x1e,
+ PACKET_ENT_REL_MOVE = 0x1f,
+ PACKET_ENT_LOOK = 0x20,
+ PACKET_ENT_REL_MOVE_LOOK = 0x21,
+ PACKET_ENT_TELEPORT = 0x22,
+ PACKET_ENT_HEAD_LOOK = 0x23,
+ PACKET_ENT_STATUS = 0x26,
+ PACKET_ATTACH_ENTITY = 0x27,
+ PACKET_METADATA = 0x28,
+ PACKET_EXPERIENCE = 0x2b,
+ PACKET_PRE_CHUNK = 0x32,
+ PACKET_MAP_CHUNK = 0x33,
+ PACKET_MULTI_BLOCK = 0x34,
+ PACKET_BLOCK_CHANGE = 0x35,
+ PACKET_BLOCK_ACTION = 0x36,
+ PACKET_EXPLOSION = 0x3C,
+ PACKET_SOUND_EFFECT = 0x3e,
+ PACKET_SOUND_PARTICLE_EFFECT = 0x3d,
+ PACKET_CHANGE_GAME_STATE = 0x46,
+ PACKET_THUNDERBOLT = 0x47,
+ PACKET_WINDOW_OPEN = 0x64,
+ PACKET_WINDOW_CLOSE = 0x65,
+ PACKET_WINDOW_CLICK = 0x66,
+ PACKET_INVENTORY_SLOT = 0x67,
+ PACKET_INVENTORY_WHOLE = 0x68,
+ PACKET_WINDOW_PROPERTY = 0x69,
+ PACKET_CREATIVE_INVENTORY_ACTION = 0x6B,
+ PACKET_UPDATE_SIGN = 0x82,
+ PACKET_PLAYER_LIST_ITEM = 0xC9,
+ PACKET_PLAYER_ABILITIES = 0xca,
+ PACKET_PLUGIN_MESSAGE = 0xfa,
+ PACKET_PING = 0xfe,
+ PACKET_DISCONNECT = 0xff
+} ;
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return PARSE_INCOMPLETE; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+typedef unsigned char Byte;
+
+
+
+
+
+cProtocol125::cProtocol125(cClientHandle * a_Client) :
+ super(a_Client),
+ m_ReceivedData(32 KiB)
+{
+}
+
+
+
+
+
+void cProtocol125::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ATTACH_ENTITY);
+ WriteInt(a_Entity.GetUniqueID());
+ WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
+{
+ UNUSED(a_BlockType);
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_BLOCK_ACTION);
+ WriteInt (a_BlockX);
+ WriteShort((short)a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteByte (a_Byte1);
+ WriteByte (a_Byte2);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
+{
+ // Not supported in this protocol version
+}
+
+
+
+
+
+void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_BLOCK_CHANGE);
+ WriteInt (a_BlockX);
+ WriteByte((unsigned char)a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteByte(a_BlockType);
+ WriteByte(a_BlockMeta);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
+{
+ cCSLock Lock(m_CSPacket);
+ if (a_Changes.size() == 1)
+ {
+ // Special packet for single-block changes
+ const sSetBlock & blk = a_Changes.front();
+ SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta);
+ return;
+ }
+
+ WriteByte (PACKET_MULTI_BLOCK);
+ WriteInt (a_ChunkX);
+ WriteInt (a_ChunkZ);
+ WriteShort((unsigned short)a_Changes.size());
+ WriteUInt (sizeof(int) * a_Changes.size());
+ 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);
+ WriteUInt(Coords << 16 | Blocks);
+ }
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendChat(const AString & a_Message)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_CHAT);
+ WriteString(a_Message);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
+{
+ cCSLock Lock(m_CSPacket);
+
+ // Send the pre-chunk:
+ SendPreChunk(a_ChunkX, a_ChunkZ, true);
+
+ // Send the chunk data:
+ AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5);
+ WriteByte(PACKET_MAP_CHUNK);
+ WriteInt (a_ChunkX);
+ WriteInt (a_ChunkZ);
+ SendData(Serialized.data(), Serialized.size());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_COLLECT_PICKUP);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteInt (a_Player.GetUniqueID());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendDestroyEntity(const cEntity & a_Entity)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_DESTROY_ENTITY);
+ WriteInt (a_Entity.GetUniqueID());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendDisconnect(const AString & a_Reason)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte ((unsigned char)PACKET_DISCONNECT);
+ WriteString(a_Reason);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // This protocol version doesn't support this packet, sign editor is invoked by the client automatically
+ UNUSED(a_BlockX);
+ UNUSED(a_BlockY);
+ UNUSED(a_BlockZ);
+}
+
+
+
+
+
+void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_ENTITY_EQUIPMENT);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteShort(a_SlotNum);
+ WriteShort(a_Item.m_ItemType);
+ WriteShort(a_Item.m_ItemDamage);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityHeadLook(const cEntity & a_Entity)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENT_HEAD_LOOK);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte((char)((a_Entity.GetHeadYaw() / 360.f) * 256));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityLook(const cEntity & a_Entity)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENT_LOOK);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
+ WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityMetadata(const cEntity & a_Entity)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_METADATA);
+ WriteInt (a_Entity.GetUniqueID());
+
+ WriteCommonMetadata(a_Entity);
+ if (a_Entity.IsMob())
+ {
+ WriteMobMetadata(((const cMonster &)a_Entity));
+ }
+ else
+ {
+ WriteEntityMetadata(a_Entity);
+ }
+ WriteByte(0x7f);
+
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityProperties(const cEntity & a_Entity)
+{
+ // Not supported in this protocol version
+}
+
+
+
+
+
+void cProtocol125::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENT_REL_MOVE);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(a_RelX);
+ WriteByte(a_RelY);
+ WriteByte(a_RelZ);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENT_REL_MOVE_LOOK);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(a_RelX);
+ WriteByte(a_RelY);
+ WriteByte(a_RelZ);
+ WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
+ WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENT_STATUS);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(a_Status);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityVelocity(const cEntity & a_Entity)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENTITY_VELOCITY);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteShort((short) (a_Entity.GetSpeedX() * 400)); //400 = 8000 / 20
+ WriteShort((short) (a_Entity.GetSpeedY() * 400));
+ WriteShort((short) (a_Entity.GetSpeedZ() * 400));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_EXPLOSION);
+ WriteDouble (a_BlockX);
+ WriteDouble (a_BlockY);
+ WriteDouble (a_BlockZ);
+ WriteFloat (a_Radius);
+ WriteInt (a_BlocksAffected.size());
+ int BlockX = (int)a_BlockX;
+ int BlockY = (int)a_BlockY;
+ int BlockZ = (int)a_BlockZ;
+ for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr)
+ {
+ WriteByte((Byte)(itr->x - BlockX));
+ WriteByte((Byte)(itr->y - BlockY));
+ WriteByte((Byte)(itr->z - BlockZ));
+ }
+ WriteFloat((float)a_PlayerMotion.x);
+ WriteFloat((float)a_PlayerMotion.y);
+ WriteFloat((float)a_PlayerMotion.z);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendGameMode(eGameMode a_GameMode)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_CHANGE_GAME_STATE);
+ WriteByte(3);
+ WriteByte((char)a_GameMode);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendHandshake(const AString & a_ConnectionHash)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_HANDSHAKE);
+ WriteString(a_ConnectionHash);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendHealth(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_UPDATE_HEALTH);
+ WriteShort((short)m_Client->GetPlayer()->GetHealth());
+ WriteShort(m_Client->GetPlayer()->GetFoodLevel());
+ WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_INVENTORY_SLOT);
+ WriteByte (a_WindowID);
+ WriteShort(a_SlotNum);
+ WriteItem (a_Item);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendKeepAlive(int a_PingID)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_KEEP_ALIVE);
+ WriteInt (a_PingID);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+{
+ UNUSED(a_World);
+ cCSLock Lock(m_CSPacket);
+
+ WriteByte (PACKET_LOGIN);
+ WriteInt (a_Player.GetUniqueID()); // EntityID of the player
+ WriteString(""); // Username, not used
+ WriteString("default"); // Level type
+ WriteInt ((int)a_Player.GetGameMode());
+ WriteInt ((int)(a_World.GetDimension()));
+ WriteByte (2); // TODO: Difficulty
+ WriteByte (0); // Unused
+ WriteByte (60); // Client list width or something
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_PICKUP_SPAWN);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteShort (a_Pickup.GetItem().m_ItemType);
+ WriteByte (a_Pickup.GetItem().m_ItemCount);
+ WriteShort (a_Pickup.GetItem().m_ItemDamage);
+ WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32));
+ WriteByte ((char)(a_Pickup.GetSpeed().x * 8));
+ WriteByte ((char)(a_Pickup.GetSpeed().y * 8));
+ WriteByte ((char)(a_Pickup.GetSpeed().z * 8));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ANIMATION);
+ WriteInt (a_Player.GetUniqueID());
+ WriteByte(a_Animation);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
+{
+ cCSLock Lock(m_CSPacket);
+ AString PlayerName(a_Player.GetColor());
+ PlayerName.append(a_Player.GetName());
+ if (PlayerName.length() > 14)
+ {
+ PlayerName.erase(14);
+ }
+ PlayerName += cChatColor::White;
+
+ WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM);
+ WriteString(PlayerName);
+ WriteBool (a_IsOnline);
+ WriteShort (a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendPlayerMaxSpeed(void)
+{
+ // Not supported by this protocol version
+}
+
+
+
+
+
+void cProtocol125::SendPlayerMoveLook(void)
+{
+ cCSLock Lock(m_CSPacket);
+
+ /*
+ LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d",
+ m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0
+ );
+ */
+
+ WriteByte (PACKET_PLAYER_MOVE_LOOK);
+ cPlayer * Player = m_Client->GetPlayer();
+ WriteDouble(Player->GetPosX());
+ WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block
+ WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block
+ WriteDouble(Player->GetPosZ());
+ WriteFloat ((float)(Player->GetRotation()));
+ WriteFloat ((float)(Player->GetPitch()));
+ WriteBool (Player->IsOnGround());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendPlayerPosition(void)
+{
+ cCSLock Lock(m_CSPacket);
+ LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now
+}
+
+
+
+
+
+void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player)
+{
+ const cItem & HeldItem = a_Player.GetEquippedItem();
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_PLAYER_SPAWN);
+ WriteInt (a_Player.GetUniqueID());
+ WriteString(a_Player.GetName());
+ WriteInt ((int)(a_Player.GetPosX() * 32));
+ WriteInt ((int)(a_Player.GetPosY() * 32));
+ WriteInt ((int)(a_Player.GetPosZ() * 32));
+ WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256));
+ WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256));
+ WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendRespawn(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_RESPAWN);
+ WriteInt ((int)(m_Client->GetPlayer()->GetWorld()->GetDimension()));
+ WriteByte (2); // TODO: Difficulty; 2 = Normal
+ WriteByte ((char)m_Client->GetPlayer()->GetGameMode());
+ WriteShort (256); // Current world height
+ WriteString("default");
+}
+
+
+
+
+
+void cProtocol125::SendExperience(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_EXPERIENCE);
+ WriteFloat (m_Client->GetPlayer()->GetXpPercentage());
+ WriteShort (m_Client->GetPlayer()->GetXpLevel());
+ WriteShort (m_Client->GetPlayer()->GetCurrentXp());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
+{
+ // Not needed in this protocol version
+}
+
+
+
+
+
+void cProtocol125::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ // Not implemented in this protocol version
+}
+
+
+
+
+
+void cProtocol125::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
+{
+ // This protocol version implements falling blocks using the spawn object / vehicle packet:
+ SendSpawnObject(a_FallingBlock, 70, a_FallingBlock.GetBlockType(), 0, 0);
+}
+
+
+
+
+
+void cProtocol125::SendSpawnMob(const cMonster & a_Mob)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_SPAWN_MOB);
+ WriteInt (a_Mob.GetUniqueID());
+ WriteByte (a_Mob.GetMobType());
+ WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
+ WriteByte (0);
+ WriteByte (0);
+ WriteByte (0);
+
+ WriteCommonMetadata(a_Mob);
+ WriteMobMetadata(a_Mob);
+ WriteByte(0x7f);
+
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
+{
+ UNUSED(a_Yaw);
+ UNUSED(a_Pitch);
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SPAWN_OBJECT);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(a_ObjectType);
+ WriteInt ((int)(a_Entity.GetPosX() * 32));
+ WriteInt ((int)(a_Entity.GetPosY() * 32));
+ WriteInt ((int)(a_Entity.GetPosZ() * 32));
+ WriteByte(a_Pitch);
+ WriteByte(a_Yaw);
+ WriteInt (a_ObjectData);
+ if (a_ObjectData != 0)
+ {
+ WriteShort((short)(a_Entity.GetSpeedX() * 400));
+ WriteShort((short)(a_Entity.GetSpeedY() * 400));
+ WriteShort((short)(a_Entity.GetSpeedZ() * 400));
+ }
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_SPAWN_OBJECT);
+ WriteInt (a_Vehicle.GetUniqueID());
+ WriteByte (a_VehicleType);
+ WriteInt ((int)(a_Vehicle.GetPosX() * 32));
+ WriteInt ((int)(a_Vehicle.GetPosY() * 32));
+ WriteInt ((int)(a_Vehicle.GetPosZ() * 32));
+ WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
+ WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256));
+ WriteInt (a_VehicleSubType);
+ if (a_VehicleSubType != 0)
+ {
+ WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
+ WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
+ WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
+ }
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendTabCompletionResults(const AStringVector & a_Results)
+{
+ // This protocol version doesn't support tab completion
+ UNUSED(a_Results);
+}
+
+
+
+
+
+void cProtocol125::SendTeleportEntity(const cEntity & a_Entity)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_ENT_TELEPORT);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteInt ((int)(floor(a_Entity.GetPosX() * 32)));
+ WriteInt ((int)(floor(a_Entity.GetPosY() * 32)));
+ WriteInt ((int)(floor(a_Entity.GetPosZ() * 32)));
+ WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256));
+ WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_THUNDERBOLT);
+ WriteInt (0x7fffffff); // Entity ID of the thunderbolt; we use a constant one
+ WriteBool(true); // Unknown bool
+ WriteInt (a_BlockX * 32);
+ WriteInt (a_BlockY * 32);
+ WriteInt (a_BlockZ * 32);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_UPDATE_TIME);
+ // Use a_WorldAge for daycount, and a_TimeOfDay for the proper time of day:
+ WriteInt64((24000 * (a_WorldAge / 24000)) + (a_TimeOfDay % 24000));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSPacket);
+ SendPreChunk(a_ChunkX, a_ChunkZ, false);
+}
+
+
+
+
+
+void cProtocol125::SendUpdateSign(
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4
+)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte ((unsigned char)PACKET_UPDATE_SIGN);
+ WriteInt (a_BlockX);
+ WriteShort ((short)a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteString(a_Line1);
+ WriteString(a_Line2);
+ WriteString(a_Line3);
+ WriteString(a_Line4);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_USE_BED);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(0); // Unknown byte only 0 has been observed
+ WriteInt (a_BlockX);
+ WriteByte(a_BlockY);
+ WriteInt (a_BlockZ);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendWeather(eWeather a_Weather)
+{
+ cCSLock Lock(m_CSPacket);
+ switch( a_Weather )
+ {
+ case eWeather_Sunny:
+ {
+ WriteByte(PACKET_CHANGE_GAME_STATE);
+ WriteByte(2); // Stop rain
+ WriteByte(0); // Unused
+ Flush();
+ break;
+ }
+
+ case eWeather_Rain:
+ case eWeather_ThunderStorm:
+ {
+ WriteByte(PACKET_CHANGE_GAME_STATE);
+ WriteByte(1); // Begin rain
+ WriteByte(0); // Unused
+ Flush();
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cProtocol125::SendWholeInventory(const cWindow & a_Window)
+{
+ cCSLock Lock(m_CSPacket);
+ cItems Slots;
+ a_Window.GetSlots(*(m_Client->GetPlayer()), Slots);
+ SendWindowSlots(a_Window.GetWindowID(), Slots.size(), &(Slots[0]));
+}
+
+
+
+
+
+void cProtocol125::SendWindowClose(const cWindow & a_Window)
+{
+ if (a_Window.GetWindowType() == cWindow::wtInventory)
+ {
+ // Do not send inventory-window-close
+ return;
+ }
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_WINDOW_CLOSE);
+ WriteByte(a_Window.GetWindowID());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendWindowOpen(const cWindow & a_Window)
+{
+ if (a_Window.GetWindowType() < 0)
+ {
+ // Do not send for inventory windows
+ return;
+ }
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_WINDOW_OPEN);
+ WriteByte (a_Window.GetWindowID());
+ WriteByte (a_Window.GetWindowType());
+ WriteString(a_Window.GetWindowTitle());
+ WriteByte (a_Window.GetNumNonInventorySlots());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_WINDOW_PROPERTY);
+ WriteByte (a_Window.GetWindowID());
+ WriteShort(a_Property);
+ WriteShort(a_Value);
+ Flush();
+}
+
+
+
+
+
+AString cProtocol125::GetAuthServerID(void)
+{
+ // http://wiki.vg/wiki/index.php?title=Session&oldid=2262
+ // The server generates a random hash and that is used for all clients, unmodified
+ return cRoot::Get()->GetServer()->GetServerID();
+}
+
+
+
+
+
+void cProtocol125::SendData(const char * a_Data, int a_Size)
+{
+ m_Client->SendData(a_Data, a_Size);
+}
+
+
+
+
+
+void cProtocol125::DataReceived(const char * a_Data, int a_Size)
+{
+ if (!m_ReceivedData.Write(a_Data, a_Size))
+ {
+ // Too much data in the incoming queue, report to caller:
+ m_Client->PacketBufferFull();
+ return;
+ }
+
+ // Parse and handle all complete packets in m_ReceivedData:
+ while (m_ReceivedData.CanReadBytes(1))
+ {
+ unsigned char PacketType;
+ m_ReceivedData.ReadByte(PacketType);
+ switch (ParsePacket(PacketType))
+ {
+ case PARSE_UNKNOWN:
+ {
+ // An unknown packet has been received, notify the client and abort:
+ m_Client->PacketUnknown(PacketType);
+ return;
+ }
+ case PARSE_ERROR:
+ {
+ // An error occurred while parsing a known packet, notify the client and abort:
+ m_Client->PacketError(PacketType);
+ return;
+ }
+ case PARSE_INCOMPLETE:
+ {
+ // Incomplete packet, bail out and process with the next batch of data
+ m_ReceivedData.ResetRead();
+ return;
+ }
+ default:
+ {
+ // Packet successfully parsed, commit the read data and try again one more packet
+ m_ReceivedData.CommitRead();
+ break;
+ }
+ }
+ }
+}
+
+
+
+
+
+int cProtocol125::ParsePacket(unsigned char a_PacketType)
+{
+ switch (a_PacketType)
+ {
+ default: return PARSE_UNKNOWN;
+ case PACKET_ANIMATION: return ParseArmAnim();
+ case PACKET_BLOCK_DIG: return ParseBlockDig();
+ case PACKET_BLOCK_PLACE: return ParseBlockPlace();
+ case PACKET_CHAT: return ParseChat();
+ case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction();
+ case PACKET_DISCONNECT: return ParseDisconnect();
+ case PACKET_HANDSHAKE: return ParseHandshake();
+ case PACKET_KEEP_ALIVE: return ParseKeepAlive();
+ case PACKET_LOGIN: return ParseLogin();
+ case PACKET_PACKET_ENTITY_ACTION: return ParseEntityAction();
+ case PACKET_PING: return ParsePing();
+ case PACKET_PLAYER_ABILITIES: return ParsePlayerAbilities();
+ case PACKET_PLAYER_LOOK: return ParsePlayerLook();
+ case PACKET_PLAYER_MOVE_LOOK: return ParsePlayerMoveLook();
+ case PACKET_PLAYER_ON_GROUND: return ParsePlayerOnGround();
+ case PACKET_PLAYER_POS: return ParsePlayerPosition();
+ case PACKET_PLUGIN_MESSAGE: return ParsePluginMessage();
+ case PACKET_RESPAWN: return ParseRespawn();
+ case PACKET_SLOT_SELECTED: return ParseSlotSelected();
+ case PACKET_UPDATE_SIGN: return ParseUpdateSign();
+ case PACKET_USE_ENTITY: return ParseUseEntity();
+ case PACKET_WINDOW_CLICK: return ParseWindowClick();
+ case PACKET_WINDOW_CLOSE: return ParseWindowClose();
+ }
+}
+
+
+
+
+
+#define HANDLE_PACKET_PARSE(Packet) \
+ { \
+ int res = Packet.Parse(m_ReceivedData); \
+ if (res < 0) \
+ { \
+ return res; \
+ } \
+ }
+
+
+
+
+
+int cProtocol125::ParseArmAnim(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
+ HANDLE_PACKET_READ(ReadChar, char, Animation);
+ m_Client->HandleAnimation(Animation);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseBlockDig(void)
+{
+ HANDLE_PACKET_READ(ReadChar, char, Status);
+ HANDLE_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_PACKET_READ(ReadByte, Byte, PosY);
+ HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_PACKET_READ(ReadChar, char, BlockFace);
+ m_Client->HandleLeftClick(PosX, PosY, PosZ, BlockFace, Status);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseBlockPlace(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_PACKET_READ(ReadByte, Byte, PosY);
+ HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_PACKET_READ(ReadChar, char, BlockFace);
+
+ cItem HeldItem;
+ int res = ParseItem(HeldItem);
+ if (res < 0)
+ {
+ return res;
+ }
+
+ // 1.2.5 didn't have any cursor position, so use 8, 8, 8, so that halfslabs and stairs work correctly and the special value is recognizable.
+ m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, 8, 8, 8, HeldItem);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseChat(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message);
+ m_Client->HandleChat(Message);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseCreativeInventoryAction(void)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
+ cItem HeldItem;
+ int res = ParseItem(HeldItem);
+ if (res < 0)
+ {
+ return res;
+ }
+ m_Client->HandleCreativeInventory(SlotNum, HeldItem);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseDisconnect(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason);
+ m_Client->HandleDisconnect(Reason);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseEntityAction(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
+ HANDLE_PACKET_READ(ReadChar, char, ActionID);
+ m_Client->HandleEntityAction(EntityID, ActionID);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseHandshake(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
+
+ AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565"
+ if (UserData.empty())
+ {
+ m_Client->Kick("Did not receive username");
+ return PARSE_OK;
+ }
+ m_Username = UserData[0];
+
+ LOGD("HANDSHAKE %s", Username.c_str());
+
+ if (!m_Client->HandleHandshake( m_Username ))
+ {
+ return PARSE_OK; // Player is not allowed into the server
+ }
+
+ SendHandshake(cRoot::Get()->GetServer()->GetServerID());
+ LOGD("User \"%s\" was sent a handshake response", m_Username.c_str());
+
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseKeepAlive(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID);
+ m_Client->HandleKeepAlive(KeepAliveID);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseLogin(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, ProtocolVersion);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
+ HANDLE_PACKET_READ(ReadBEInt, int, ServerMode);
+ HANDLE_PACKET_READ(ReadBEInt, int, Dimension);
+ HANDLE_PACKET_READ(ReadChar, char, Difficulty);
+ HANDLE_PACKET_READ(ReadByte, Byte, WorldHeight);
+ HANDLE_PACKET_READ(ReadByte, Byte, MaxPlayers);
+
+ if (ProtocolVersion < 29)
+ {
+ m_Client->Kick("Your client is outdated!");
+ return PARSE_OK;
+ }
+ else if (ProtocolVersion > 29)
+ {
+ m_Client->Kick("Your client version is higher than the server!");
+ return PARSE_OK;
+ }
+
+ if (m_Username.compare(Username) != 0)
+ {
+ LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking",
+ Username.c_str(),
+ m_Username.c_str(),
+ m_Client->GetIPString().c_str()
+ );
+ m_Client->Kick("Hacked client"); // Don't tell them why we don't want them
+ return PARSE_OK;
+ }
+
+ m_Client->HandleLogin(ProtocolVersion, Username);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePing(void)
+{
+ // Packet has no more data
+ m_Client->HandlePing();
+ return PARSE_OK;
+}
+
+
+
+
+
+
+int cProtocol125::ParsePlayerAbilities(void)
+{
+ HANDLE_PACKET_READ(ReadBool, bool, Invulnerable);
+ HANDLE_PACKET_READ(ReadBool, bool, IsFlying);
+ HANDLE_PACKET_READ(ReadBool, bool, CanFly);
+ HANDLE_PACKET_READ(ReadBool, bool, InstaMine);
+ // TODO: m_Client->HandlePlayerAbilities(...);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePlayerLook(void)
+{
+ HANDLE_PACKET_READ(ReadBEFloat, float, Rotation);
+ HANDLE_PACKET_READ(ReadBEFloat, float, Pitch);
+ HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
+ m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePlayerMoveLook(void)
+{
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosX);
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosY);
+ HANDLE_PACKET_READ(ReadBEDouble, double, Stance);
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosZ);
+ HANDLE_PACKET_READ(ReadBEFloat, float, Rotation);
+ HANDLE_PACKET_READ(ReadBEFloat, float, Pitch);
+ HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
+ // LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0);
+ m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePlayerOnGround(void)
+{
+ HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
+ // TODO: m_Client->HandleFlying(IsOnGround);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePlayerPosition(void)
+{
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosX);
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosY);
+ HANDLE_PACKET_READ(ReadBEDouble, double, Stance);
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosZ);
+ HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
+ m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePluginMessage(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ChannelName);
+ HANDLE_PACKET_READ(ReadBEShort, short, Length);
+ AString Data;
+ if (!m_ReceivedData.ReadString(Data, Length))
+ {
+ m_ReceivedData.CheckValid();
+ return PARSE_INCOMPLETE;
+ }
+ m_ReceivedData.CheckValid();
+
+ // TODO: Process the data
+ LOGD("Received %d bytes of plugin data on channel \"%s\".", Length, ChannelName.c_str());
+
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseRespawn(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, Dimension);
+ HANDLE_PACKET_READ(ReadChar, char, Difficulty);
+ HANDLE_PACKET_READ(ReadChar, char, CreativeMode);
+ HANDLE_PACKET_READ(ReadBEShort, short, WorldHeight);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
+ m_Client->HandleRespawn();
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseSlotSelected(void)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
+ m_Client->HandleSlotSelected(SlotNum);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseUpdateSign(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, BlockX);
+ HANDLE_PACKET_READ(ReadBEShort, short, BlockY);
+ HANDLE_PACKET_READ(ReadBEInt, int, BlockZ);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4);
+ m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseUseEntity(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, SourceEntityID);
+ HANDLE_PACKET_READ(ReadBEInt, int, TargetEntityID);
+ HANDLE_PACKET_READ(ReadBool, bool, IsLeftClick);
+ m_Client->HandleUseEntity(TargetEntityID, IsLeftClick);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseWindowClick(void)
+{
+ HANDLE_PACKET_READ(ReadChar, char, WindowID);
+ HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
+ HANDLE_PACKET_READ(ReadBool, bool, IsRightClick);
+ HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
+ HANDLE_PACKET_READ(ReadBool, bool, IsShiftPressed);
+ cItem HeldItem;
+ int res = ParseItem(HeldItem);
+ if (res < 0)
+ {
+ return res;
+ }
+
+ // Convert IsShiftPressed, IsRightClick, SlotNum and HeldItem into eClickAction used in the newer protocols:
+ eClickAction Action;
+ if (IsRightClick)
+ {
+ if (IsShiftPressed)
+ {
+ Action = caShiftRightClick;
+ }
+ else
+ {
+ if (SlotNum == -999)
+ {
+ Action = (HeldItem.IsEmpty()) ? caRightClickOutsideHoldNothing : caRightClickOutside;
+ }
+ else
+ {
+ Action = caRightClick;
+ }
+ }
+ }
+ else
+ {
+ // IsLeftClick
+ if (IsShiftPressed)
+ {
+ Action = caShiftLeftClick;
+ }
+ else
+ {
+ if (SlotNum == -999)
+ {
+ Action = (HeldItem.IsEmpty()) ? caLeftClickOutsideHoldNothing : caRightClickOutside;
+ }
+ else
+ {
+ Action = caLeftClick;
+ }
+ }
+ }
+ m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseWindowClose(void)
+{
+ HANDLE_PACKET_READ(ReadChar, char, WindowID);
+ m_Client->HandleWindowClose(WindowID);
+ return PARSE_OK;
+}
+
+
+
+
+
+void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad)
+{
+ WriteByte(PACKET_PRE_CHUNK);
+ WriteInt (a_ChunkX);
+ WriteInt (a_ChunkZ);
+ WriteBool(a_ShouldLoad);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items)
+{
+ WriteByte (PACKET_INVENTORY_WHOLE);
+ WriteByte (a_WindowID);
+ WriteShort((short)a_NumItems);
+
+ for (int j = 0; j < a_NumItems; j++)
+ {
+ WriteItem(a_Items[j]);
+ }
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::WriteItem(const cItem & a_Item)
+{
+ short ItemType = a_Item.m_ItemType;
+ ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
+ if (ItemType <= 0)
+ {
+ // Fix, to make sure no invalid values are sent.
+ ItemType = -1;
+ }
+
+ WriteShort(ItemType);
+ if (a_Item.IsEmpty())
+ {
+ return;
+ }
+
+ WriteByte (a_Item.m_ItemCount);
+ WriteShort(a_Item.m_ItemDamage);
+
+ if (cItem::IsEnchantable(a_Item.m_ItemType))
+ {
+ // TODO: Implement enchantments
+ WriteShort(-1);
+ }
+}
+
+
+
+
+
+int cProtocol125::ParseItem(cItem & a_Item)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, ItemType);
+
+ if (ItemType <= -1)
+ {
+ a_Item.Empty();
+ return PARSE_OK;
+ }
+ a_Item.m_ItemType = ItemType;
+
+ HANDLE_PACKET_READ(ReadChar, char, ItemCount);
+ HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
+ a_Item.m_ItemCount = ItemCount;
+ a_Item.m_ItemDamage = ItemDamage;
+ if (ItemCount <= 0)
+ {
+ a_Item.Empty();
+ }
+
+ if (!cItem::IsEnchantable(ItemType))
+ {
+ return PARSE_OK;
+ }
+
+ HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes);
+
+ if (EnchantNumBytes <= 0)
+ {
+ return PARSE_OK;
+ }
+
+ // TODO: Enchantment not implemented yet!
+ if (!m_ReceivedData.SkipRead(EnchantNumBytes))
+ {
+ return PARSE_INCOMPLETE;
+ }
+
+ return PARSE_OK;
+}
+
+
+
+
+
+void cProtocol125::WriteCommonMetadata(const cEntity & a_Entity)
+{
+ Byte CommonMetadata = 0;
+
+ if (a_Entity.IsOnFire())
+ {
+ CommonMetadata |= 0x1;
+ }
+ if (a_Entity.IsCrouched())
+ {
+ CommonMetadata |= 0x2;
+ }
+ if (a_Entity.IsRiding())
+ {
+ CommonMetadata |= 0x4;
+ }
+ if (a_Entity.IsSprinting())
+ {
+ CommonMetadata |= 0x8;
+ }
+ if (a_Entity.IsRclking())
+ {
+ CommonMetadata |= 0x10;
+ }
+ if (a_Entity.IsInvisible())
+ {
+ CommonMetadata |= 0x20;
+ }
+
+ WriteByte(0x0);
+ WriteByte(CommonMetadata);
+}
+
+
+
+
+
+void cProtocol125::WriteEntityMetadata(const cEntity & a_Entity)
+{
+ if (a_Entity.IsMinecart())
+ {
+ WriteByte(0x51);
+ // No idea how Mojang makes their carts shakey shakey, so here is a complicated one-liner expression that does something similar
+ WriteInt( (((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4 );
+ WriteByte(0x52);
+ WriteInt(1); // Shaking direction, doesn't seem to affect anything
+ WriteByte(0x73);
+ WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer
+
+ if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace)
+ {
+ WriteByte(0x10);
+ WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); // Fueled?
+ }
+ }
+ else if ((a_Entity.IsProjectile() && ((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow))
+ {
+ WriteByte(0x10);
+ WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); // Critical hitting arrow?
+ }
+}
+
+
+
+
+
+void cProtocol125::WriteMobMetadata(const cMonster & a_Mob)
+{
+ switch (a_Mob.GetMobType())
+ {
+ case cMonster::mtCreeper:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); // Blowing up?
+ WriteByte(0x11);
+ WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); // Lightning-charged?
+ break;
+ }
+ case cMonster::mtBat:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0); // Upside down?
+ break;
+ }
+ case cMonster::mtPig:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0); // Saddled?
+ break;
+ }
+ case cMonster::mtVillager:
+ {
+ WriteByte(0x50);
+ WriteInt(((const cVillager &)a_Mob).GetVilType()); // What sort of TESTIFICATE?
+ break;
+ }
+ case cMonster::mtZombie:
+ {
+ WriteByte(0xC);
+ WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0); // Babby zombie?
+ WriteByte(0xD);
+ WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0); // Converted zombie?
+ WriteByte(0xE);
+ WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0); // Converted-but-converting-back zombllager?
+ break;
+ }
+ case cMonster::mtGhast:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cGhast &)a_Mob).IsCharging()); // About to eject un flamé-bol? :P
+ break;
+ }
+ case cMonster::mtWolf:
+ {
+ Byte WolfStatus = 0;
+ if (((const cWolf &)a_Mob).IsSitting())
+ {
+ WolfStatus |= 0x1;
+ }
+ if (((const cWolf &)a_Mob).IsAngry())
+ {
+ WolfStatus |= 0x2;
+ }
+ if (((const cWolf &)a_Mob).IsTame())
+ {
+ WolfStatus |= 0x4;
+ }
+ WriteByte(0x10);
+ WriteByte(WolfStatus);
+
+ WriteByte(0x72);
+ WriteFloat((float)(a_Mob.GetHealth())); // Tail health-o-meter (only shown when tamed, by the way)
+ WriteByte(0x13);
+ WriteByte(((const cWolf &)a_Mob).IsBegging() ? 1 : 0); // Ultra cute mode?
+ break;
+ }
+ case cMonster::mtSheep:
+ {
+ // [1](1111)
+ // [] = Is sheared? () = Color, from 0 to 15
+
+ WriteByte(0x10);
+ Byte SheepMetadata = 0;
+ SheepMetadata = ((const cSheep &)a_Mob).GetFurColor(); // Fur colour
+
+ if (((const cSheep &)a_Mob).IsSheared()) // Is sheared?
+ {
+ SheepMetadata |= 0x16;
+ }
+ WriteByte(SheepMetadata);
+ break;
+ }
+ case cMonster::mtEnderman:
+ {
+ WriteByte(0x10);
+ WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock())); // Block that he stole from your house
+ WriteByte(0x11);
+ WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta())); // Meta of block that he stole from your house
+ WriteByte(0x12);
+ WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0); // Screaming at your face?
+ break;
+ }
+ case cMonster::mtSkeleton:
+ {
+ WriteByte(0xD);
+ WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0); // It's a skeleton, but it's not
+ break;
+ }
+ case cMonster::mtWitch:
+ {
+ WriteByte(0x15);
+ WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); // Aggravated? Doesn't seem to do anything
+ break;
+ }
+ case cMonster::mtSlime:
+ case cMonster::mtMagmaCube:
+ {
+ WriteByte(0x10);
+ if (a_Mob.GetMobType() == cMonster::mtSlime)
+ {
+ WriteByte(((const cSlime &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME
+ }
+ else
+ {
+ WriteByte(((const cMagmaCube &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME
+ }
+ break;
+ }
+ case cMonster::mtHorse:
+ {
+ int Flags = 0;
+ if (((const cHorse &)a_Mob).IsTame())
+ {
+ Flags |= 0x2;
+ }
+ if (((const cHorse &)a_Mob).IsSaddled())
+ {
+ Flags |= 0x4;
+ }
+ if (((const cHorse &)a_Mob).IsChested())
+ {
+ Flags |= 0x8;
+ }
+ if (((const cHorse &)a_Mob).IsBaby())
+ {
+ Flags |= 0x10; // IsBred flag, according to wiki.vg - don't think it does anything in multiplayer
+ }
+ if (((const cHorse &)a_Mob).IsEating())
+ {
+ Flags |= 0x20;
+ }
+ if (((const cHorse &)a_Mob).IsRearing())
+ {
+ Flags |= 0x40;
+ }
+ if (((const cHorse &)a_Mob).IsMthOpen())
+ {
+ Flags |= 0x80;
+ }
+ WriteByte(0x50);
+ WriteInt(Flags);
+
+ WriteByte(0x13);
+ WriteByte(((const cHorse &)a_Mob).GetHorseType()); // Type of horse (donkey, chestnut, etc.)
+
+ WriteByte(0x54);
+ int Appearance = 0;
+ Appearance = ((const cHorse &)a_Mob).GetHorseColor(); // Mask FF
+ Appearance |= ((const cHorse &)a_Mob).GetHorseStyle() * 256; // Mask FF00, so multiply by 256
+ WriteInt(Appearance);
+
+ WriteByte(0x56);
+ WriteInt(((const cHorse &)a_Mob).GetHorseArmour()); // Horshey armour
+ break;
+ }
+ }
+}
+
+
+
+
diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
new file mode 100644
index 000000000..c5f44c818
--- /dev/null
+++ b/src/Protocol/Protocol125.h
@@ -0,0 +1,158 @@
+
+// Protocol125.h
+
+// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29)
+
+
+
+
+
+#pragma once
+
+#include "Protocol.h"
+#include "../ByteBuffer.h"
+
+
+
+
+
+class cProtocol125 :
+ public cProtocol
+{
+ typedef cProtocol super;
+public:
+ cProtocol125(cClientHandle * a_Client);
+
+ /// Called when client sends some data:
+ virtual void DataReceived(const char * a_Data, int a_Size) override;
+
+ /// Sending stuff to clients (alphabetically sorted):
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
+ virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) override;
+ virtual void SendDisconnect (const AString & a_Reason) override;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
+ virtual void SendEntityLook (const cEntity & a_Entity) override;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) override;
+ virtual void SendEntityProperties (const cEntity & a_Entity) override;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) override;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
+ virtual void SendGameMode (eGameMode a_GameMode) override;
+ virtual void SendHealth (void) override;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendKeepAlive (int a_PingID) override;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message
+ virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override;
+ virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
+ virtual void SendPlayerMaxSpeed (void) override;
+ virtual void SendPlayerMoveLook (void) override;
+ virtual void SendPlayerPosition (void) override;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendRespawn (void) override;
+ virtual void SendExperience (void) override;
+ virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
+ virtual void SendSpawnMob (const cMonster & a_Mob) override;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
+ virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) override;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
+ virtual void SendWeather (eWeather a_Weather) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+ virtual void SendWindowClose (const cWindow & a_Window) override;
+ virtual void SendWindowOpen (const cWindow & a_Window) override;
+ virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override;
+
+ virtual AString GetAuthServerID(void) override;
+
+protected:
+ /// Results of packet-parsing:
+ enum {
+ PARSE_OK = 1,
+ PARSE_ERROR = -1,
+ PARSE_UNKNOWN = -2,
+ PARSE_INCOMPLETE = -3,
+ } ;
+
+ cByteBuffer m_ReceivedData; ///< Buffer for the received data
+
+ AString m_Username; ///< Stored in ParseHandshake(), compared to Login username
+
+ virtual void SendData(const char * a_Data, int a_Size) override;
+
+ /// Sends the Handshake packet
+ void SendHandshake(const AString & a_ConnectionHash);
+
+ /// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() )
+ virtual int ParsePacket(unsigned char a_PacketType);
+
+ // Specific packet parsers:
+ virtual int ParseArmAnim (void);
+ virtual int ParseBlockDig (void);
+ virtual int ParseBlockPlace (void);
+ virtual int ParseChat (void);
+ virtual int ParseCreativeInventoryAction(void);
+ virtual int ParseDisconnect (void);
+ virtual int ParseEntityAction (void);
+ virtual int ParseHandshake (void);
+ virtual int ParseKeepAlive (void);
+ virtual int ParseLogin (void);
+ virtual int ParsePing (void);
+ virtual int ParsePlayerAbilities (void);
+ virtual int ParsePlayerLook (void);
+ virtual int ParsePlayerMoveLook (void);
+ virtual int ParsePlayerOnGround (void);
+ virtual int ParsePlayerPosition (void);
+ virtual int ParsePluginMessage (void);
+ virtual int ParseRespawn (void);
+ virtual int ParseSlotSelected (void);
+ virtual int ParseUpdateSign (void);
+ virtual int ParseUseEntity (void);
+ virtual int ParseWindowClick (void);
+ virtual int ParseWindowClose (void);
+
+ // Utility functions:
+ /// Writes a "pre-chunk" packet
+ void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad);
+
+ /// Writes a "set window items" packet with the specified params
+ void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items);
+
+ /// Writes one item, "slot" as the protocol wiki calls it
+ virtual void WriteItem(const cItem & a_Item);
+
+ /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes
+ virtual int ParseItem(cItem & a_Item);
+
+ /// Writes the COMMON entity metadata
+ void WriteCommonMetadata(const cEntity & a_Entity);
+
+ /// Writes normal entity metadata
+ void WriteEntityMetadata(const cEntity & a_Entity);
+
+ /// Writes mobile entity metadata
+ void WriteMobMetadata(const cMonster & a_Mob);
+} ;
+
+
+
+
diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp
new file mode 100644
index 000000000..8f7891e4b
--- /dev/null
+++ b/src/Protocol/Protocol132.cpp
@@ -0,0 +1,950 @@
+
+// Protocol132.cpp
+
+// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39)
+
+#include "Globals.h"
+#include "ChunkDataSerializer.h"
+#include "lib/cryptopp/randpool.h"
+#include "Protocol132.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../World.h"
+#include "../ClientHandle.h"
+#include "../Item.h"
+#include "../Entities/Player.h"
+#include "../Mobs/Monster.h"
+#include "../UI/Window.h"
+#include "../Entities/Pickup.h"
+#include "../WorldStorage/FastNBT.h"
+#include "../StringCompression.h"
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return PARSE_INCOMPLETE; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+typedef unsigned char Byte;
+
+
+
+
+
+using namespace CryptoPP;
+
+
+
+
+
+const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
+
+
+
+
+
+enum
+{
+ PACKET_KEEP_ALIVE = 0x00,
+ PACKET_LOGIN = 0x01,
+ PACKET_ENTITY_EQUIPMENT = 0x05,
+ PACKET_COMPASS = 0x06,
+ PACKET_PLAYER_SPAWN = 0x14,
+ PACKET_COLLECT_PICKUP = 0x16,
+ PACKET_SPAWN_MOB = 0x18,
+ PACKET_DESTROY_ENTITIES = 0x1d,
+ PACKET_CHUNK_DATA = 0x33,
+ PACKET_BLOCK_CHANGE = 0x35,
+ PACKET_BLOCK_ACTION = 0x36,
+ PACKET_BLOCK_BREAK_ANIM = 0x37,
+ PACKET_SOUND_EFFECT = 0x3e,
+ PACKET_SOUND_PARTICLE_EFFECT = 0x3d,
+ PACKET_TAB_COMPLETION = 0xcb,
+ PACKET_LOCALE_VIEW_DISTANCE = 0xcc,
+ PACKET_CLIENT_STATUSES = 0xcd,
+ PACKET_ENCRYPTION_KEY_RESP = 0xfc,
+} ;
+
+
+
+
+
+// Converts a raw 160-bit SHA1 digest into a Java Hex representation
+// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802
+static void DigestToJava(byte a_Digest[20], AString & a_Out)
+{
+ bool IsNegative = (a_Digest[0] >= 0x80);
+ if (IsNegative)
+ {
+ // Two's complement:
+ bool carry = true; // Add one to the whole number
+ for (int i = 19; i >= 0; i--)
+ {
+ a_Digest[i] = ~a_Digest[i];
+ if (carry)
+ {
+ carry = (a_Digest[i] == 0xff);
+ a_Digest[i]++;
+ }
+ }
+ }
+ a_Out.clear();
+ a_Out.reserve(40);
+ for (int i = 0; i < 20; i++)
+ {
+ AppendPrintf(a_Out, "%02x", a_Digest[i]);
+ }
+ while ((a_Out.length() > 0) && (a_Out[0] == '0'))
+ {
+ a_Out.erase(0, 1);
+ }
+ if (IsNegative)
+ {
+ a_Out.insert(0, "-");
+ }
+}
+
+
+
+
+
+/*
+// Self-test the hash formatting for known values:
+// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48
+// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1
+// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6
+
+class Test
+{
+public:
+ Test(void)
+ {
+ AString DigestNotch, DigestJeb, DigestSimon;
+ byte Digest[20];
+ CryptoPP::SHA1 Checksum;
+ Checksum.Update((const byte *)"Notch", 5);
+ Checksum.Final(Digest);
+ DigestToJava(Digest, DigestNotch);
+ Checksum.Restart();
+ Checksum.Update((const byte *)"jeb_", 4);
+ Checksum.Final(Digest);
+ DigestToJava(Digest, DigestJeb);
+ Checksum.Restart();
+ Checksum.Update((const byte *)"simon", 5);
+ Checksum.Final(Digest);
+ DigestToJava(Digest, DigestSimon);
+ printf("Notch: \"%s\"", DigestNotch.c_str());
+ printf("jeb_: \"%s\"", DigestJeb.c_str());
+ printf("simon: \"%s\"", DigestSimon.c_str());
+ }
+} test;
+*/
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol132:
+
+cProtocol132::cProtocol132(cClientHandle * a_Client) :
+ super(a_Client),
+ m_IsEncrypted(false)
+{
+}
+
+
+
+
+
+cProtocol132::~cProtocol132()
+{
+ if (!m_DataToSend.empty())
+ {
+ LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size());
+ }
+}
+
+
+
+
+
+void cProtocol132::DataReceived(const char * a_Data, int a_Size)
+{
+ if (m_IsEncrypted)
+ {
+ byte Decrypted[512];
+ while (a_Size > 0)
+ {
+ int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
+ m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes);
+ super::DataReceived((const char *)Decrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ super::DataReceived(a_Data, a_Size);
+ }
+}
+
+
+
+
+
+void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_BLOCK_ACTION);
+ WriteInt (a_BlockX);
+ WriteShort((short)a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteByte (a_Byte1);
+ WriteByte (a_Byte2);
+ WriteShort(a_BlockType);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_BLOCK_BREAK_ANIM);
+ WriteInt (a_entityID);
+ WriteInt (a_BlockX);
+ WriteInt (a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteByte (stage);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_BLOCK_CHANGE);
+ WriteInt (a_BlockX);
+ WriteByte ((unsigned char)a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteShort(a_BlockType);
+ WriteByte (a_BlockMeta);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
+{
+ cCSLock Lock(m_CSPacket);
+
+ // Pre-chunk not used in 1.3.2. Finally.
+
+ // Send the chunk data:
+ AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2);
+ WriteByte(PACKET_CHUNK_DATA);
+ WriteInt (a_ChunkX);
+ WriteInt (a_ChunkZ);
+ SendData(Serialized.data(), Serialized.size());
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_COLLECT_PICKUP);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteInt (a_Player.GetUniqueID());
+ Flush();
+
+ // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
+ SendSoundEffect(
+ "random.pop",
+ (int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8),
+ 0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64)
+ );
+}
+
+
+
+
+
+void cProtocol132::SendDestroyEntity(const cEntity & a_Entity)
+{
+ if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID())
+ {
+ // Do not send "destroy self" to the client, the client would crash (FS #254)
+ return;
+ }
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_DESTROY_ENTITIES);
+ WriteByte(1); // entity count
+ WriteInt (a_Entity.GetUniqueID());
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_ENTITY_EQUIPMENT);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteShort(a_SlotNum);
+ WriteItem (a_Item);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_LOGIN);
+ WriteInt (a_Player.GetUniqueID()); // EntityID of the player
+ WriteString("default"); // Level type
+ WriteByte ((int)a_Player.GetGameMode());
+ WriteByte ((Byte)(a_World.GetDimension()));
+ WriteByte (2); // TODO: Difficulty
+ WriteByte (0); // Unused, used to be world height
+ WriteByte (8); // Client list width or something
+ Flush();
+
+ SendCompass(a_World);
+}
+
+
+
+
+
+void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player)
+{
+ const cItem & HeldItem = a_Player.GetEquippedItem();
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_PLAYER_SPAWN);
+ WriteInt (a_Player.GetUniqueID());
+ WriteString(a_Player.GetName());
+ WriteInt ((int)(a_Player.GetPosX() * 32));
+ WriteInt ((int)(a_Player.GetPosY() * 32));
+ WriteInt ((int)(a_Player.GetPosZ() * 32));
+ WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256));
+ WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256));
+ WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
+ // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata:
+ WriteByte (0); // Index 0, byte (flags)
+ WriteByte (0); // Flags, empty
+ WriteByte (0x7f); // End of metadata
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_SOUND_EFFECT);
+ WriteString (a_SoundName);
+ WriteInt (a_SrcX);
+ WriteInt (a_SrcY);
+ WriteInt (a_SrcZ);
+ WriteFloat (a_Volume);
+ WriteByte ((char)(a_Pitch * 63.0f));
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
+ WriteInt (a_EffectID);
+ WriteInt (a_SrcX);
+ WriteByte(a_SrcY);
+ WriteInt (a_SrcZ);
+ WriteInt (a_Data);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendSpawnMob(const cMonster & a_Mob)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_SPAWN_MOB);
+ WriteInt (a_Mob.GetUniqueID());
+ WriteByte (a_Mob.GetMobType());
+ WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
+ WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256));
+ WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256));
+ WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256));
+ WriteShort ((short)(a_Mob.GetSpeedX() * 400));
+ WriteShort ((short)(a_Mob.GetSpeedY() * 400));
+ WriteShort ((short)(a_Mob.GetSpeedZ() * 400));
+
+ WriteCommonMetadata(a_Mob);
+ WriteMobMetadata(a_Mob);
+ WriteByte(0x7f);
+
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendTabCompletionResults(const AStringVector & a_Results)
+{
+ if (a_Results.empty())
+ {
+ // No results to send
+ return;
+ }
+
+ AString Serialized(a_Results[0]);
+ for (AStringVector::const_iterator itr = a_Results.begin() + 1, end = a_Results.end(); itr != end; ++itr)
+ {
+ Serialized.push_back(0);
+ Serialized.append(*itr);
+ } // for itr - a_Results[]
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_TAB_COMPLETION);
+ WriteString(Serialized);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+{
+ // Unloading the chunk is done by sending a "map chunk" packet
+ // with IncludeInitialize set to true and primary bitmap set to 0:
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_CHUNK_DATA);
+ WriteInt (a_ChunkX);
+ WriteInt (a_ChunkZ);
+ WriteBool(true); // IncludeInitialize
+ WriteShort(0); // Primary bitmap
+ WriteShort(0); // Add bitmap
+ WriteInt(0);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendWholeInventory(const cWindow & a_Window)
+{
+ // 1.3.2 requires player inventory slots to be sent as SetSlot packets,
+ // otherwise it sometimes fails to update the window
+
+ // Send the entire window:
+ super::SendWholeInventory(a_Window);
+
+ // Send the player inventory and hotbar:
+ const cInventory & Inventory = m_Client->GetPlayer()->GetInventory();
+ int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots
+ char WindowID = a_Window.GetWindowID();
+ for (int i = 0; i < cInventory::invInventoryCount; i++)
+ {
+ SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i));
+ } // for i - Inventory[]
+ BaseOffset += cInventory::invInventoryCount;
+ for (int i = 0; i < cInventory::invHotbarCount; i++)
+ {
+ SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i));
+ } // for i - Hotbar[]
+
+ // Send even the item being dragged:
+ SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem());
+}
+
+
+
+
+
+AString cProtocol132::GetAuthServerID(void)
+{
+ // http://wiki.vg/wiki/index.php?title=Session&oldid=2615
+ // Server uses SHA1 to mix ServerID, Client secret and server public key together
+ // The mixing is done in StartEncryption, the result is in m_AuthServerID
+
+ return m_AuthServerID;
+}
+
+
+
+
+
+int cProtocol132::ParsePacket(unsigned char a_PacketType)
+{
+ switch (a_PacketType)
+ {
+ default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125
+ case PACKET_CLIENT_STATUSES: return ParseClientStatuses();
+ case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse();
+ case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance();
+ case PACKET_TAB_COMPLETION: return ParseTabCompletion();
+ }
+}
+
+
+
+
+
+int cProtocol132::ParseBlockPlace(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_PACKET_READ(ReadByte, Byte, PosY);
+ HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_PACKET_READ(ReadChar, char, BlockFace);
+
+ cItem HeldItem;
+ int res = ParseItem(HeldItem);
+ if (res < 0)
+ {
+ return res;
+ }
+
+ HANDLE_PACKET_READ(ReadChar, char, CursorX);
+ HANDLE_PACKET_READ(ReadChar, char, CursorY);
+ HANDLE_PACKET_READ(ReadChar, char, CursorZ);
+
+ m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, HeldItem);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseHandshake(void)
+{
+ HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost);
+ HANDLE_PACKET_READ(ReadBEInt, int, ServerPort);
+ m_Username = Username;
+
+ if (!m_Client->HandleHandshake( m_Username ))
+ {
+ return PARSE_OK; // Player is not allowed into the server
+ }
+
+ // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD
+ CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs
+ cRoot::Get()->GetServer()->GetPublicKey().Save(sink);
+ SendEncryptionKeyRequest();
+
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseClientStatuses(void)
+{
+ HANDLE_PACKET_READ(ReadByte, byte, Status);
+ if ((Status & 1) == 0)
+ {
+ m_Client->HandleLogin(39, m_Username);
+ }
+ else
+ {
+ m_Client->HandleRespawn();
+ }
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseEncryptionKeyResponse(void)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength);
+ AString EncKey;
+ if (!m_ReceivedData.ReadString(EncKey, EncKeyLength))
+ {
+ return PARSE_INCOMPLETE;
+ }
+ HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength);
+ AString EncNonce;
+ if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength))
+ {
+ return PARSE_INCOMPLETE;
+ }
+ if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
+ {
+ LOGD("Too long encryption");
+ m_Client->Kick("Hacked client");
+ return PARSE_OK;
+ }
+
+ HandleEncryptionKeyResponse(EncKey, EncNonce);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseLocaleViewDistance(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
+ HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
+ HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
+ HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
+ // TODO: m_Client->HandleLocale(Locale);
+ // TODO: m_Client->HandleViewDistance(ViewDistance);
+ // TODO: m_Client->HandleChatFlags(ChatFlags);
+ // Ignoring client difficulty
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseLogin(void)
+{
+ // Login packet not used in 1.3.2
+ return PARSE_ERROR;
+}
+
+
+
+
+
+int cProtocol132::ParsePlayerAbilities(void)
+{
+ HANDLE_PACKET_READ(ReadBool, bool, Flags);
+ HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed);
+ HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed);
+ // TODO: m_Client->HandlePlayerAbilities(...);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseTabCompletion(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Text);
+ m_Client->HandleTabCompletion(Text);
+ return PARSE_OK;
+}
+
+
+
+
+
+void cProtocol132::SendData(const char * a_Data, int a_Size)
+{
+ m_DataToSend.append(a_Data, a_Size);
+}
+
+
+
+
+
+void cProtocol132::Flush(void)
+{
+ ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly?
+
+ if (m_DataToSend.empty())
+ {
+ LOGD("Flushing empty");
+ return;
+ }
+ const char * a_Data = m_DataToSend.data();
+ int a_Size = m_DataToSend.size();
+ if (m_IsEncrypted)
+ {
+ byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
+ while (a_Size > 0)
+ {
+ int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size;
+ m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes);
+ super::SendData((const char *)Encrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ super::SendData(a_Data, a_Size);
+ }
+ m_DataToSend.clear();
+}
+
+
+
+
+
+void cProtocol132::WriteItem(const cItem & a_Item)
+{
+ short ItemType = a_Item.m_ItemType;
+ ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
+ if (ItemType <= 0)
+ {
+ // Fix, to make sure no invalid values are sent.
+ ItemType = -1;
+ }
+
+ if (a_Item.IsEmpty())
+ {
+ WriteShort(-1);
+ return;
+ }
+
+ WriteShort(ItemType);
+ WriteByte (a_Item.m_ItemCount);
+ WriteShort(a_Item.m_ItemDamage);
+
+ if (a_Item.m_Enchantments.IsEmpty())
+ {
+ WriteShort(-1);
+ return;
+ }
+
+ // Send the enchantments:
+ cFastNBTWriter Writer;
+ const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
+ a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName);
+ Writer.Finish();
+ AString Compressed;
+ CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
+ WriteShort(Compressed.size());
+ SendData(Compressed.data(), Compressed.size());
+}
+
+
+
+
+
+int cProtocol132::ParseItem(cItem & a_Item)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, ItemType);
+
+ if (ItemType <= -1)
+ {
+ a_Item.Empty();
+ return PARSE_OK;
+ }
+ a_Item.m_ItemType = ItemType;
+
+ HANDLE_PACKET_READ(ReadChar, char, ItemCount);
+ HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
+ a_Item.m_ItemCount = ItemCount;
+ a_Item.m_ItemDamage = ItemDamage;
+ if (ItemCount <= 0)
+ {
+ a_Item.Empty();
+ }
+
+ HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength);
+ if (MetadataLength <= 0)
+ {
+ return PARSE_OK;
+ }
+
+ // Read the metadata
+ AString Metadata;
+ Metadata.resize(MetadataLength);
+ if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength))
+ {
+ return PARSE_INCOMPLETE;
+ }
+
+ return ParseItemMetadata(a_Item, Metadata);
+}
+
+
+
+
+
+int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
+{
+ // Uncompress the GZIPped data:
+ AString Uncompressed;
+ if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK)
+ {
+ AString HexDump;
+ CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16);
+ LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str());
+ return PARSE_ERROR;
+ }
+
+ // Parse into NBT:
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
+ if (!NBT.IsValid())
+ {
+ AString HexDump;
+ CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16);
+ LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str());
+ return PARSE_ERROR;
+ }
+
+ // Load enchantments from the NBT:
+ for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
+ {
+ if (
+ (NBT.GetType(tag) == TAG_List) &&
+ (
+ (NBT.GetName(tag) == "ench") ||
+ (NBT.GetName(tag) == "StoredEnchantments")
+ )
+ )
+ {
+ a_Item.m_Enchantments.ParseFromNBT(NBT, tag);
+ }
+ }
+
+ return PARSE_OK;
+}
+
+
+
+
+
+void cProtocol132::SendCompass(const cWorld & a_World)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_COMPASS);
+ WriteInt((int)(a_World.GetSpawnX()));
+ WriteInt((int)(a_World.GetSpawnY()));
+ WriteInt((int)(a_World.GetSpawnZ()));
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendEncryptionKeyRequest(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte((char)0xfd);
+ WriteString(cRoot::Get()->GetServer()->GetServerID());
+ WriteShort((short)m_ServerPublicKey.size());
+ SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size());
+ WriteShort(4);
+ WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce)
+{
+ // Decrypt EncNonce using privkey
+ RSAES<PKCS1v15>::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey());
+ time_t CurTime = time(NULL);
+ CryptoPP::RandomPool rng;
+ rng.Put((const byte *)&CurTime, sizeof(CurTime));
+ byte DecryptedNonce[MAX_ENC_LEN];
+ DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce);
+ if (!res.isValidCoding || (res.messageLength != 4))
+ {
+ LOGD("Bad nonce length");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+ if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this)
+ {
+ LOGD("Bad nonce value");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt the symmetric encryption key using privkey:
+ byte DecryptedKey[MAX_ENC_LEN];
+ res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey);
+ if (!res.isValidCoding || (res.messageLength != 16))
+ {
+ LOGD("Bad key length");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ {
+ // Send encryption key response:
+ cCSLock Lock(m_CSPacket);
+ WriteByte((char)0xfc);
+ WriteShort(0);
+ WriteShort(0);
+ Flush();
+ }
+
+ StartEncryption(DecryptedKey);
+ return;
+}
+
+
+
+
+
+void cProtocol132::StartEncryption(const byte * a_Key)
+{
+ m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
+ m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
+ m_IsEncrypted = true;
+
+ // Prepare the m_AuthServerID:
+ CryptoPP::SHA1 Checksum;
+ AString ServerID = cRoot::Get()->GetServer()->GetServerID();
+ Checksum.Update((const byte *)ServerID.c_str(), ServerID.length());
+ Checksum.Update(a_Key, 16);
+ Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length());
+ byte Digest[20];
+ Checksum.Final(Digest);
+ DigestToJava(Digest, m_AuthServerID);
+}
+
+
+
+
diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h
new file mode 100644
index 000000000..2023d7960
--- /dev/null
+++ b/src/Protocol/Protocol132.h
@@ -0,0 +1,102 @@
+
+// Protocol132.h
+
+// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39)
+
+
+
+
+
+#pragma once
+
+#include "Protocol125.h"
+#include "lib/cryptopp/modes.h"
+#include "lib/cryptopp/aes.h"
+
+
+
+
+
+class cProtocol132 :
+ public cProtocol125
+{
+ typedef cProtocol125 super;
+public:
+
+ cProtocol132(cClientHandle * a_Client);
+ virtual ~cProtocol132();
+
+ /// Called when client sends some data:
+ virtual void DataReceived(const char * a_Data, int a_Size) override;
+
+ // Sending commands (alphabetically sorted):
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
+ virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) override;
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendSpawnMob (const cMonster & a_Mob) override;
+ virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+
+ virtual AString GetAuthServerID(void) override;
+
+ /// Handling of the additional packets:
+ virtual int ParsePacket(unsigned char a_PacketType) override;
+
+ // Modified packets:
+ virtual int ParseBlockPlace (void) override;
+ virtual int ParseHandshake (void) override;
+ virtual int ParseLogin (void) override;
+ virtual int ParsePlayerAbilities(void) override;
+
+ // New packets:
+ virtual int ParseClientStatuses (void);
+ virtual int ParseEncryptionKeyResponse(void);
+ virtual int ParseLocaleViewDistance (void);
+ virtual int ParseTabCompletion (void);
+
+protected:
+ bool m_IsEncrypted;
+ CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor;
+ CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor;
+ AString m_DataToSend;
+
+ /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID()
+ AString m_AuthServerID;
+
+ /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption()
+ AString m_ServerPublicKey;
+
+ virtual void SendData(const char * a_Data, int a_Size) override;
+
+ // DEBUG:
+ virtual void Flush(void) override;
+
+ // Items in slots are sent differently
+ virtual void WriteItem(const cItem & a_Item) override;
+ virtual int ParseItem(cItem & a_Item) override;
+
+ /// Parses the metadata that may come with the item.
+ int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
+
+ virtual void SendCompass(const cWorld & a_World);
+ virtual void SendEncryptionKeyRequest(void);
+
+ /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption
+ void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce);
+
+ /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID
+ void StartEncryption(const byte * a_Key);
+} ;
+
+
+
+
diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp
new file mode 100644
index 000000000..ea406d7a2
--- /dev/null
+++ b/src/Protocol/Protocol14x.cpp
@@ -0,0 +1,256 @@
+
+// Protocol14x.cpp
+
+/*
+Implements the 1.4.x protocol classes representing these protocols:
+- cProtocol142:
+ - release 1.4.2 protocol (#47)
+ - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA)
+ - release 1.4.5 protocol (same as 1.4.4)
+- cProtocol146:
+ - release 1.4.6 protocol (#51)
+*/
+
+#include "Globals.h"
+#include "Protocol14x.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../ClientHandle.h"
+#include "lib/cryptopp/randpool.h"
+#include "../Item.h"
+#include "ChunkDataSerializer.h"
+#include "../Entities/Player.h"
+#include "../Mobs/Monster.h"
+#include "../UI/Window.h"
+#include "../Entities/Pickup.h"
+#include "../Entities/FallingBlock.h"
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return PARSE_INCOMPLETE; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+
+enum
+{
+ PACKET_UPDATE_TIME = 0x04,
+ PACKET_PICKUP_SPAWN = 0x15,
+ PACKET_SPAWN_OBJECT = 0x17,
+ PACKET_ENTITY_METADATA = 0x28,
+ PACKET_SOUND_PARTICLE_EFFECT = 0x3d
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol142:
+
+cProtocol142::cProtocol142(cClientHandle * a_Client) :
+ super(a_Client)
+{
+}
+
+
+
+
+
+int cProtocol142::ParseLocaleViewDistance(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
+ HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
+ HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
+ HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
+ HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2
+ // TODO: m_Client->HandleLocale(Locale);
+ // TODO: m_Client->HandleViewDistance(ViewDistance);
+ // TODO: m_Client->HandleChatFlags(ChatFlags);
+ // Ignoring client difficulty
+ return PARSE_OK;
+}
+
+
+
+
+
+void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_PICKUP_SPAWN);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteItem (a_Pickup.GetItem());
+ WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32));
+ WriteByte ((char)(a_Pickup.GetSpeed().x * 8));
+ WriteByte ((char)(a_Pickup.GetSpeed().y * 8));
+ WriteByte ((char)(a_Pickup.GetSpeed().z * 8));
+ Flush();
+}
+
+
+
+
+
+void cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
+ WriteInt (a_EffectID);
+ WriteInt (a_SrcX);
+ WriteByte(a_SrcY);
+ WriteInt (a_SrcZ);
+ WriteInt (a_Data);
+ WriteBool(0);
+ Flush();
+}
+
+
+
+
+
+void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_UPDATE_TIME);
+ WriteInt64(a_WorldAge);
+ WriteInt64(a_TimeOfDay);
+ Flush();
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol146:
+
+cProtocol146::cProtocol146(cClientHandle * a_Client) :
+ super(a_Client)
+{
+}
+
+
+
+
+
+void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ ASSERT(!a_Pickup.GetItem().IsEmpty());
+
+ cCSLock Lock(m_CSPacket);
+
+ // Send a SPAWN_OBJECT packet for the base entity:
+ WriteByte(PACKET_SPAWN_OBJECT);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteByte(0x02);
+ WriteInt ((int)(a_Pickup.GetPosX() * 32));
+ WriteInt ((int)(a_Pickup.GetPosY() * 32));
+ WriteInt ((int)(a_Pickup.GetPosZ() * 32));
+ WriteInt (1);
+ WriteShort((short)(a_Pickup.GetSpeed().x * 32));
+ WriteShort((short)(a_Pickup.GetSpeed().y * 32));
+ WriteShort((short)(a_Pickup.GetSpeed().z * 32));
+ WriteByte(0);
+ WriteByte(0);
+
+ // Send a ENTITY_METADATA packet with the slot info:
+ WriteByte(PACKET_ENTITY_METADATA);
+ WriteInt(a_Pickup.GetUniqueID());
+ WriteByte(0xaa); // a slot value at index 10
+ WriteItem(a_Pickup.GetItem());
+ WriteByte(0x7f); // End of metadata
+ Flush();
+}
+
+
+
+
+
+void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
+{
+ // Send a spawn object / vehicle packet
+ cCSLock Lock(m_CSPacket);
+
+ WriteByte(PACKET_SPAWN_OBJECT);
+ WriteInt (a_FallingBlock.GetUniqueID());
+ WriteByte(70);
+ WriteInt ((int)(a_FallingBlock.GetPosX() * 32));
+ WriteInt ((int)(a_FallingBlock.GetPosY() * 32));
+ WriteInt ((int)(a_FallingBlock.GetPosZ() * 32));
+ WriteByte (0); // Pitch
+ WriteByte (0); // Yaw
+ WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype
+ WriteShort((short)(a_FallingBlock.GetSpeedX() * 400));
+ WriteShort((short)(a_FallingBlock.GetSpeedY() * 400));
+ WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400));
+ Flush();
+}
+
+
+
+
+
+void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SPAWN_OBJECT);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(a_ObjectType);
+ WriteInt ((int)(a_Entity.GetPosX() * 32));
+ WriteInt ((int)(a_Entity.GetPosY() * 32));
+ WriteInt ((int)(a_Entity.GetPosZ() * 32));
+ WriteByte(a_Pitch);
+ WriteByte(a_Yaw);
+ WriteInt (a_ObjectData);
+ if (a_ObjectData != 0)
+ {
+ WriteShort((short)(a_Entity.GetSpeedX() * 400));
+ WriteShort((short)(a_Entity.GetSpeedY() * 400));
+ WriteShort((short)(a_Entity.GetSpeedZ() * 400));
+ }
+ Flush();
+}
+
+
+
+
+
+void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_SPAWN_OBJECT);
+ WriteInt (a_Vehicle.GetUniqueID());
+ WriteByte (a_VehicleType);
+ WriteInt ((int)(a_Vehicle.GetPosX() * 32));
+ WriteInt ((int)(a_Vehicle.GetPosY() * 32));
+ WriteInt ((int)(a_Vehicle.GetPosZ() * 32));
+ WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
+ WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256));
+ WriteInt (a_VehicleSubType);
+ if (a_VehicleSubType != 0)
+ {
+ WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
+ WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
+ WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
+ }
+ Flush();
+}
+
+
+
+
+
diff --git a/src/Protocol/Protocol14x.h b/src/Protocol/Protocol14x.h
new file mode 100644
index 000000000..ca497bbc1
--- /dev/null
+++ b/src/Protocol/Protocol14x.h
@@ -0,0 +1,63 @@
+
+// Protocol14x.h
+
+/*
+Interfaces to the 1.4.x protocol classes representing these protocols:
+- cProtocol142:
+ - release 1.4.2 protocol (#47)
+ - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA)
+ - release 1.4.5 protocol (same as 1.4.4)
+- cProtocol146:
+ - release 1.4.6 protocol (#51)
+*/
+
+
+
+
+
+#pragma once
+
+#include "Protocol132.h"
+
+
+
+
+
+class cProtocol142 :
+ public cProtocol132
+{
+ typedef cProtocol132 super;
+
+public:
+ cProtocol142(cClientHandle * a_Client);
+
+ // Sending commands (alphabetically sorted):
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
+
+ // Specific packet parsers:
+ virtual int ParseLocaleViewDistance(void) override;
+} ;
+
+
+
+
+
+class cProtocol146 :
+ public cProtocol142
+{
+ typedef cProtocol142 super;
+
+public:
+ cProtocol146(cClientHandle * a_Client);
+
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) override;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
+} ;
+
+
+
+
diff --git a/src/Protocol/Protocol15x.cpp b/src/Protocol/Protocol15x.cpp
new file mode 100644
index 000000000..c337d26e7
--- /dev/null
+++ b/src/Protocol/Protocol15x.cpp
@@ -0,0 +1,138 @@
+
+// Protocol15x.cpp
+
+/*
+Implements the 1.5.x protocol classes:
+ - cProtocol150
+ - release 1.5 protocol (#60)
+ - release 1.5.2 protocol (#61, no relevant changes found)
+*/
+
+#include "Globals.h"
+#include "Protocol15x.h"
+#include "../ClientHandle.h"
+#include "../Item.h"
+#include "../UI/Window.h"
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return PARSE_INCOMPLETE; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+
+enum
+{
+ PACKET_WINDOW_OPEN = 0x64,
+} ;
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol150:
+
+cProtocol150::cProtocol150(cClientHandle * a_Client) :
+ super(a_Client)
+{
+}
+
+
+
+
+
+void cProtocol150::SendWindowOpen(const cWindow & a_Window)
+{
+ if (a_Window.GetWindowType() < 0)
+ {
+ // Do not send for inventory windows
+ return;
+ }
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_WINDOW_OPEN);
+ WriteByte (a_Window.GetWindowID());
+ WriteByte (a_Window.GetWindowType());
+ WriteString(a_Window.GetWindowTitle());
+ WriteByte (a_Window.GetNumNonInventorySlots());
+ WriteByte (1); // Use title
+ Flush();
+}
+
+
+
+
+
+int cProtocol150::ParseWindowClick(void)
+{
+ HANDLE_PACKET_READ(ReadChar, char, WindowID);
+ HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
+ HANDLE_PACKET_READ(ReadByte, Byte, Button);
+ HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
+ HANDLE_PACKET_READ(ReadByte, Byte, Mode);
+ cItem HeldItem;
+ int res = ParseItem(HeldItem);
+ if (res < 0)
+ {
+ return res;
+ }
+
+ // Convert Button, Mode, SlotNum and HeldItem into eClickAction:
+ eClickAction Action;
+ switch ((Mode << 8) | Button)
+ {
+ case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break;
+ case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break;
+ case 0x0100: Action = caShiftLeftClick; break;
+ case 0x0101: Action = caShiftRightClick; break;
+ case 0x0200: Action = caNumber1; break;
+ case 0x0201: Action = caNumber2; break;
+ case 0x0202: Action = caNumber3; break;
+ case 0x0203: Action = caNumber4; break;
+ case 0x0204: Action = caNumber5; break;
+ case 0x0205: Action = caNumber6; break;
+ case 0x0206: Action = caNumber7; break;
+ case 0x0207: Action = caNumber8; break;
+ case 0x0208: Action = caNumber9; break;
+ case 0x0300: Action = caMiddleClick; break;
+ case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break;
+ case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break;
+ case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break;
+ case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break;
+ case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break;
+ case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break;
+ case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break;
+ case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break;
+ case 0x0600: Action = caDblClick; break;
+ }
+
+ if (Action == caUnknown)
+ {
+ LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.",
+ Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str()
+ );
+ ASSERT(!"Unknown click action");
+ return PARSE_OK;
+ }
+
+ m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
+ return PARSE_OK;
+}
+
+
+
+
+
diff --git a/src/Protocol/Protocol15x.h b/src/Protocol/Protocol15x.h
new file mode 100644
index 000000000..e554fe130
--- /dev/null
+++ b/src/Protocol/Protocol15x.h
@@ -0,0 +1,38 @@
+
+// Protocol15x.h
+
+/*
+Declares the 1.5.x protocol classes:
+ - cProtocol150
+ - release 1.5 and 1.5.1 protocol (#60)
+ - release 1.5.2 protocol (#61; no relevant changes found)
+*/
+
+
+
+
+
+#pragma once
+
+#include "Protocol14x.h"
+
+
+
+
+
+class cProtocol150 :
+ public cProtocol146
+{
+ typedef cProtocol146 super;
+
+public:
+ cProtocol150(cClientHandle * a_Client);
+
+ virtual void SendWindowOpen(const cWindow & a_Window) override;
+
+ virtual int ParseWindowClick(void);
+} ;
+
+
+
+
diff --git a/src/Protocol/Protocol16x.cpp b/src/Protocol/Protocol16x.cpp
new file mode 100644
index 000000000..cfa27b3c4
--- /dev/null
+++ b/src/Protocol/Protocol16x.cpp
@@ -0,0 +1,268 @@
+
+// Protocol16x.cpp
+
+/*
+Implements the 1.6.x protocol classes:
+ - cProtocol161
+ - release 1.6.1 protocol (#73)
+ - cProtocol162
+ - release 1.6.2 protocol (#74)
+ - release 1.6.3 protocol (#77) - no relevant changes
+ - release 1.6.4 protocol (#78) - no relevant changes
+(others may be added later in the future for the 1.6 release series)
+*/
+
+#include "Globals.h"
+#include "Protocol16x.h"
+#include "../ClientHandle.h"
+#include "../Entities/Entity.h"
+#include "../Entities/Player.h"
+#include "../UI/Window.h"
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return PARSE_INCOMPLETE; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+
+enum
+{
+ PACKET_CHAT = 0x03,
+ PACKET_UPDATE_HEALTH = 0x08,
+ PACKET_STEER_VEHICLE = 0x1b,
+ PACKET_ATTACH_ENTITY = 0x27,
+ PACKET_ENTITY_PROPERTIES = 0x2c,
+ PACKET_WINDOW_OPEN = 0x64,
+ PACKET_TILE_EDITOR_OPEN = 0x85,
+ PACKET_PLAYER_ABILITIES = 0xca,
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol161:
+
+cProtocol161::cProtocol161(cClientHandle * a_Client) :
+ super(a_Client)
+{
+}
+
+
+
+
+
+void cProtocol161::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ATTACH_ENTITY);
+ WriteInt(a_Entity.GetUniqueID());
+ WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID());
+ WriteBool(false); // TODO: "Should use leash?" -> no
+ Flush();
+}
+
+
+
+
+
+void cProtocol161::SendChat(const AString & a_Message)
+{
+ super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str()));
+}
+
+
+
+
+
+void cProtocol161::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_TILE_EDITOR_OPEN);
+ WriteByte(0);
+ WriteInt(a_BlockX);
+ WriteInt(a_BlockY);
+ WriteInt(a_BlockZ);
+ Flush();
+}
+
+
+
+
+
+void cProtocol161::SendGameMode(eGameMode a_GameMode)
+{
+ super::SendGameMode(a_GameMode);
+ SendPlayerMaxSpeed();
+}
+
+
+
+
+
+void cProtocol161::SendHealth(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_UPDATE_HEALTH);
+ WriteFloat((float)m_Client->GetPlayer()->GetHealth());
+ WriteShort(m_Client->GetPlayer()->GetFoodLevel());
+ WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel());
+ Flush();
+}
+
+
+
+
+
+void cProtocol161::SendPlayerMaxSpeed(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENTITY_PROPERTIES);
+ WriteInt(m_Client->GetPlayer()->GetUniqueID());
+ WriteInt(1);
+ WriteString("generic.movementSpeed");
+ WriteDouble(m_Client->GetPlayer()->GetMaxSpeed());
+ Flush();
+}
+
+
+
+
+
+void cProtocol161::SendRespawn(void)
+{
+ // Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast
+ super::SendRespawn();
+ SendPlayerMaxSpeed();
+}
+
+
+
+
+
+void cProtocol161::SendWindowOpen(const cWindow & a_Window)
+{
+ if (a_Window.GetWindowType() < 0)
+ {
+ // Do not send for inventory windows
+ return;
+ }
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_WINDOW_OPEN);
+ WriteByte (a_Window.GetWindowID());
+ WriteByte (a_Window.GetWindowType());
+ WriteString(a_Window.GetWindowTitle());
+ WriteByte (a_Window.GetNumNonInventorySlots());
+ WriteByte (1); // Use title
+ if (a_Window.GetWindowType() == cWindow::wtAnimalChest)
+ {
+ WriteInt(0); // TODO: The animal's EntityID
+ }
+ Flush();
+}
+
+
+
+
+
+int cProtocol161::ParseEntityAction(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
+ HANDLE_PACKET_READ(ReadChar, char, ActionID);
+ HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal);
+ m_Client->HandleEntityAction(EntityID, ActionID);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol161::ParsePlayerAbilities(void)
+{
+ HANDLE_PACKET_READ(ReadByte, Byte, Flags);
+ HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed);
+ HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed);
+ // TODO: m_Client->HandlePlayerAbilities(...);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol161::ParseSteerVehicle(void)
+{
+ HANDLE_PACKET_READ(ReadBEFloat, float, Sideways);
+ HANDLE_PACKET_READ(ReadBEFloat, float, Forward);
+ HANDLE_PACKET_READ(ReadBool, bool, Jump);
+ HANDLE_PACKET_READ(ReadBool, bool, Unmount);
+ if (Unmount)
+ {
+ m_Client->HandleUnmount();
+ }
+ else
+ {
+ m_Client->HandleSteerVehicle(Forward, Sideways);
+ }
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol161::ParsePacket(unsigned char a_PacketType)
+{
+ switch (a_PacketType)
+ {
+ case PACKET_STEER_VEHICLE: return ParseSteerVehicle();
+ default: return super::ParsePacket(a_PacketType);
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol162:
+
+cProtocol162::cProtocol162(cClientHandle * a_Client) :
+ super(a_Client)
+{
+}
+
+
+
+
+
+void cProtocol162::SendPlayerMaxSpeed(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENTITY_PROPERTIES);
+ WriteInt(m_Client->GetPlayer()->GetUniqueID());
+ WriteInt(1);
+ WriteString("generic.movementSpeed");
+ WriteDouble(m_Client->GetPlayer()->GetMaxSpeed());
+ WriteShort(0);
+ Flush();
+}
+
+
+
+
diff --git a/src/Protocol/Protocol16x.h b/src/Protocol/Protocol16x.h
new file mode 100644
index 000000000..325e41c5a
--- /dev/null
+++ b/src/Protocol/Protocol16x.h
@@ -0,0 +1,76 @@
+
+// Protocol16x.h
+
+/*
+Declares the 1.6.x protocol classes:
+ - cProtocol161
+ - release 1.6.1 protocol (#73)
+ - cProtocol162
+ - release 1.6.2 protocol (#74)
+ - release 1.6.3 protocol (#77) - no relevant changes
+ - release 1.6.4 protocol (#78) - no relevant changes
+(others may be added later in the future for the 1.6 release series)
+*/
+
+
+
+
+
+#pragma once
+
+#include "Protocol15x.h"
+
+
+
+
+
+class cProtocol161 :
+ public cProtocol150
+{
+ typedef cProtocol150 super;
+
+public:
+ cProtocol161(cClientHandle * a_Client);
+
+protected:
+
+ // cProtocol150 overrides:
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
+ virtual void SendChat (const AString & a_Message) override;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendGameMode (eGameMode a_GameMode) override;
+ virtual void SendHealth (void) override;
+ virtual void SendPlayerMaxSpeed(void) override;
+ virtual void SendRespawn (void) override;
+ virtual void SendWindowOpen (const cWindow & a_Window) override;
+
+ virtual int ParseEntityAction (void) override;
+ virtual int ParsePlayerAbilities(void) override;
+
+ // New packets:
+ virtual int ParseSteerVehicle(void);
+
+ // Enable new packets' handling
+ virtual int ParsePacket(unsigned char a_PacketType) override;
+} ;
+
+
+
+
+
+class cProtocol162 :
+ public cProtocol161
+{
+ typedef cProtocol161 super;
+
+public:
+ cProtocol162(cClientHandle * a_Client);
+
+protected:
+ // cProtocol161 overrides:
+ virtual void SendPlayerMaxSpeed(void) override;
+} ;
+
+
+
+
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
new file mode 100644
index 000000000..ae1df7395
--- /dev/null
+++ b/src/Protocol/Protocol17x.cpp
@@ -0,0 +1,1929 @@
+
+// Protocol17x.cpp
+
+/*
+Implements the 1.7.x protocol classes:
+ - cProtocol172
+ - release 1.7.2 protocol (#4)
+(others may be added later in the future for the 1.7 release series)
+*/
+
+#include "Globals.h"
+#include "Protocol17x.h"
+#include "ChunkDataSerializer.h"
+#include "../ClientHandle.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../World.h"
+#include "../WorldStorage/FastNBT.h"
+#include "../StringCompression.h"
+#include "../Entities/Minecart.h"
+#include "../Entities/FallingBlock.h"
+#include "../Entities/Pickup.h"
+#include "../Entities/Player.h"
+#include "../Mobs/IncludeAllMonsters.h"
+#include "../UI/Window.h"
+
+
+
+
+
+#define HANDLE_READ(Proc, Type, Var) \
+ Type Var; \
+ m_ReceivedData.Proc(Var);
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return false; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+
+cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) :
+ super(a_Client),
+ m_ServerAddress(a_ServerAddress),
+ m_ServerPort(a_ServerPort),
+ m_State(a_State),
+ m_ReceivedData(32 KiB),
+ m_OutPacketBuffer(64 KiB),
+ m_OutPacketLenBuffer(20), // 20 bytes is more than enough for one VarInt
+ m_IsEncrypted(false)
+{
+}
+
+
+
+
+
+void cProtocol172::DataReceived(const char * a_Data, int a_Size)
+{
+ if (m_IsEncrypted)
+ {
+ byte Decrypted[512];
+ while (a_Size > 0)
+ {
+ int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
+ m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes);
+ AddReceivedData((const char *)Decrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ AddReceivedData(a_Data, a_Size);
+ }
+}
+
+
+
+
+
+void cProtocol172::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ cPacketizer Pkt(*this, 0x1b); // Attach Entity packet
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteInt((a_Vehicle != NULL) ? a_Vehicle->GetUniqueID() : 0);
+ Pkt.WriteBool(false);
+}
+
+
+
+
+
+void cProtocol172::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
+{
+ cPacketizer Pkt(*this, 0x24); // Block Action packet
+ Pkt.WriteInt(a_BlockX);
+ Pkt.WriteShort(a_BlockY);
+ Pkt.WriteInt(a_BlockZ);
+ Pkt.WriteByte(a_Byte1);
+ Pkt.WriteByte(a_Byte2);
+ Pkt.WriteVarInt(a_BlockType);
+}
+
+
+
+
+
+void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage)
+{
+ cPacketizer Pkt(*this, 0x24); // Block Break Animation packet
+ Pkt.WriteInt(a_EntityID);
+ Pkt.WriteInt(a_BlockX);
+ Pkt.WriteInt(a_BlockY);
+ Pkt.WriteInt(a_BlockZ);
+ Pkt.WriteChar(a_Stage);
+}
+
+
+
+
+
+void cProtocol172::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ cPacketizer Pkt(*this, 0x23); // Block Change packet
+ Pkt.WriteInt(a_BlockX);
+ Pkt.WriteByte(a_BlockY);
+ Pkt.WriteInt(a_BlockZ);
+ Pkt.WriteVarInt(a_BlockType);
+ Pkt.WriteByte(a_BlockMeta);
+}
+
+
+
+
+
+void cProtocol172::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
+{
+ cPacketizer Pkt(*this, 0x22); // Multi Block Change packet
+ Pkt.WriteInt(a_ChunkX);
+ Pkt.WriteInt(a_ChunkZ);
+ Pkt.WriteShort((short)a_Changes.size());
+ Pkt.WriteInt(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);
+ Pkt.WriteInt((Coords << 16) | Blocks);
+ } // for itr - a_Changes[]
+}
+
+
+
+
+
+void cProtocol172::SendChat(const AString & a_Message)
+{
+ cPacketizer Pkt(*this, 0x02); // Chat Message packet
+ Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str()));
+}
+
+
+
+
+
+void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
+{
+ // Serialize first, before creating the Packetizer (the packetizer locks a CS)
+ // This contains the flags and bitmasks, too
+ const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2);
+
+ cPacketizer Pkt(*this, 0x21); // Chunk Data packet
+ Pkt.WriteInt(a_ChunkX);
+ Pkt.WriteInt(a_ChunkZ);
+ Pkt.WriteBuf(ChunkData.data(), ChunkData.size());
+}
+
+
+
+
+
+void cProtocol172::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
+{
+ cPacketizer Pkt(*this, 0x0d); // Collect Item packet
+ Pkt.WriteInt(a_Pickup.GetUniqueID());
+ Pkt.WriteInt(a_Player.GetUniqueID());
+}
+
+
+
+
+
+void cProtocol172::SendDestroyEntity(const cEntity & a_Entity)
+{
+ cPacketizer Pkt(*this, 0x13); // Destroy Entities packet
+ Pkt.WriteByte(1);
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+}
+
+
+
+
+
+void cProtocol172::SendDisconnect(const AString & a_Reason)
+{
+ cPacketizer Pkt(*this, 0x40);
+ Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str()));
+}
+
+
+
+
+
+void cProtocol172::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ cPacketizer Pkt(*this, 0x36); // Sign Editor Open packet
+ Pkt.WriteInt(a_BlockX);
+ Pkt.WriteInt(a_BlockY);
+ Pkt.WriteInt(a_BlockZ);
+}
+
+
+
+
+
+void cProtocol172::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
+{
+ cPacketizer Pkt(*this, 0x04); // Entity Equipment packet
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteShort(a_SlotNum);
+ Pkt.WriteItem(a_Item);
+}
+
+
+
+
+
+void cProtocol172::SendEntityHeadLook(const cEntity & a_Entity)
+{
+ cPacketizer Pkt(*this, 0x19); // Entity Head Look packet
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteByteAngle(a_Entity.GetHeadYaw());
+}
+
+
+
+
+
+void cProtocol172::SendEntityLook(const cEntity & a_Entity)
+{
+ cPacketizer Pkt(*this, 0x16); // Entity Look packet
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteByteAngle(a_Entity.GetYaw());
+ Pkt.WriteByteAngle(a_Entity.GetPitch());
+}
+
+
+
+
+
+void cProtocol172::SendEntityMetadata(const cEntity & a_Entity)
+{
+ cPacketizer Pkt(*this, 0x1c); // Entity Metadata packet
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteEntityMetadata(a_Entity);
+ Pkt.WriteByte(0x7f); // The termination byte
+}
+
+
+
+
+
+void cProtocol172::SendEntityProperties(const cEntity & a_Entity)
+{
+ cPacketizer Pkt(*this, 0x20); // Entity Properties packet
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteEntityProperties(a_Entity);
+}
+
+
+
+
+
+void cProtocol172::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ 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);
+}
+
+
+
+
+
+void cProtocol172::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ 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.WriteByteAngle(a_Entity.GetYaw());
+ Pkt.WriteByteAngle(a_Entity.GetPitch());
+}
+
+
+
+
+
+void cProtocol172::SendEntityStatus(const cEntity & a_Entity, char a_Status)
+{
+ cPacketizer Pkt(*this, 0x1a); // Entity Status packet
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteChar(a_Status);
+}
+
+
+
+
+
+void cProtocol172::SendEntityVelocity(const cEntity & a_Entity)
+{
+ cPacketizer Pkt(*this, 0x12); // Entity Velocity packet
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ // 400 = 8000 / 20 ... Conversion from our speed in m/s to 8000 m/tick
+ Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400));
+ Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400));
+ Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400));
+}
+
+
+
+
+
+void cProtocol172::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
+{
+ cPacketizer Pkt(*this, 0x27); // Explosion packet
+ Pkt.WriteFloat((float)a_BlockX);
+ Pkt.WriteFloat((float)a_BlockY);
+ Pkt.WriteFloat((float)a_BlockZ);
+ Pkt.WriteFloat((float)a_Radius);
+ Pkt.WriteInt(a_BlocksAffected.size());
+ for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(), end = a_BlocksAffected.end(); itr != end; ++itr)
+ {
+ Pkt.WriteChar((char)itr->x);
+ Pkt.WriteChar((char)itr->y);
+ Pkt.WriteChar((char)itr->z);
+ } // for itr - a_BlockAffected[]
+ Pkt.WriteFloat((float)a_PlayerMotion.x);
+ Pkt.WriteFloat((float)a_PlayerMotion.y);
+ Pkt.WriteFloat((float)a_PlayerMotion.z);
+}
+
+
+
+
+
+void cProtocol172::SendGameMode(eGameMode a_GameMode)
+{
+ cPacketizer Pkt(*this, 0x2b); // Change Game State packet
+ Pkt.WriteByte(3); // Reason: Change game mode
+ Pkt.WriteFloat((float)a_GameMode);
+}
+
+
+
+
+
+void cProtocol172::SendHealth(void)
+{
+ cPacketizer Pkt(*this, 0x06); // Update Health packet
+ Pkt.WriteFloat((float)m_Client->GetPlayer()->GetHealth());
+ Pkt.WriteShort(m_Client->GetPlayer()->GetFoodLevel());
+ Pkt.WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel());
+}
+
+
+
+
+
+void cProtocol172::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
+{
+ cPacketizer Pkt(*this, 0x2f); // Set Slot packet
+ Pkt.WriteChar(a_WindowID);
+ Pkt.WriteShort(a_SlotNum);
+ Pkt.WriteItem(a_Item);
+}
+
+
+
+
+
+void cProtocol172::SendKeepAlive(int a_PingID)
+{
+ cPacketizer Pkt(*this, 0x00); // Keep Alive packet
+ Pkt.WriteInt(a_PingID);
+}
+
+
+
+
+
+void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+{
+ // Send the Join Game packet:
+ {
+ cPacketizer Pkt(*this, 0x01); // Join Game packet
+ Pkt.WriteInt(a_Player.GetUniqueID());
+ Pkt.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (cRoot::Get()->GetServer()->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4
+ Pkt.WriteChar((char)a_World.GetDimension());
+ Pkt.WriteByte(2); // TODO: Difficulty (set to Normal)
+ Pkt.WriteByte(cRoot::Get()->GetServer()->GetMaxPlayers());
+ Pkt.WriteString("default"); // Level type - wtf?
+ }
+
+ // 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());
+ }
+
+ // Send player abilities:
+ SendPlayerAbilities();
+}
+
+
+
+
+
+void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ {
+ cPacketizer Pkt(*this, 0x0e); // Spawn Object packet
+ Pkt.WriteVarInt(a_Pickup.GetUniqueID());
+ Pkt.WriteByte(2); // Type = Pickup
+ Pkt.WriteFPInt(a_Pickup.GetPosX());
+ Pkt.WriteFPInt(a_Pickup.GetPosY());
+ Pkt.WriteFPInt(a_Pickup.GetPosZ());
+ Pkt.WriteByteAngle(a_Pickup.GetYaw());
+ Pkt.WriteByteAngle(a_Pickup.GetPitch());
+ Pkt.WriteInt(0); // No object data
+ }
+ {
+ cPacketizer Pkt(*this, 0x1c); // Entity Metadata packet
+ Pkt.WriteInt(a_Pickup.GetUniqueID());
+ Pkt.WriteByte((0x05 << 5) | 10); // Slot type + index 10
+ Pkt.WriteItem(a_Pickup.GetItem());
+ Pkt.WriteByte(0x7f); // End of metadata
+ }
+}
+
+
+
+
+
+void cProtocol172::SendPlayerAbilities(void)
+{
+ cPacketizer Pkt(*this, 0x39); // Player Abilities packet
+ Byte Flags = 0;
+ if (m_Client->GetPlayer()->IsGameModeCreative())
+ {
+ Flags |= 0x01;
+ }
+ // TODO: Other flags (god mode, flying, can fly
+ Pkt.WriteByte(Flags);
+ // TODO: Pkt.WriteFloat(m_Client->GetPlayer()->GetMaxFlyingSpeed());
+ Pkt.WriteFloat(0.05f);
+ Pkt.WriteFloat((float)m_Client->GetPlayer()->GetMaxSpeed());
+}
+
+
+
+
+
+void cProtocol172::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
+{
+ cPacketizer Pkt(*this, 0x0b); // Animation packet
+ Pkt.WriteVarInt(a_Player.GetUniqueID());
+ Pkt.WriteChar(a_Animation);
+}
+
+
+
+
+
+void cProtocol172::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
+{
+ cPacketizer Pkt(*this, 0x38); // Playerlist Item packet
+ Pkt.WriteString(a_Player.GetName());
+ Pkt.WriteBool(a_IsOnline);
+ Pkt.WriteShort(a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0);
+}
+
+
+
+
+
+void cProtocol172::SendPlayerMaxSpeed(void)
+{
+ cPacketizer Pkt(*this, 0x20); // Entity Properties
+ Pkt.WriteInt(m_Client->GetPlayer()->GetUniqueID());
+ Pkt.WriteInt(1); // Count
+ Pkt.WriteString("generic.movementSpeed");
+ Pkt.WriteDouble(0.1);
+ if (m_Client->GetPlayer()->IsSprinting())
+ {
+ Pkt.WriteShort(1); // Modifier count
+ Pkt.WriteInt64(0x662a6b8dda3e4c1c);
+ Pkt.WriteInt64(0x881396ea6097278d); // UUID of the modifier
+ Pkt.WriteDouble(0.3);
+ Pkt.WriteByte(2);
+ }
+ else
+ {
+ Pkt.WriteShort(0); // Modifier count
+ }
+}
+
+
+
+
+
+void cProtocol172::SendPlayerMoveLook(void)
+{
+ cPacketizer Pkt(*this, 0x08); // Player Position And Look packet
+ Pkt.WriteDouble(m_Client->GetPlayer()->GetPosX());
+ Pkt.WriteDouble(m_Client->GetPlayer()->GetPosY());
+ Pkt.WriteDouble(m_Client->GetPlayer()->GetPosZ());
+ Pkt.WriteFloat((float)m_Client->GetPlayer()->GetYaw());
+ Pkt.WriteFloat((float)m_Client->GetPlayer()->GetPitch());
+ Pkt.WriteBool(m_Client->GetPlayer()->IsOnGround());
+}
+
+
+
+
+
+void cProtocol172::SendPlayerPosition(void)
+{
+ // There is no dedicated packet for this, send the whole thing:
+ SendPlayerMoveLook();
+}
+
+
+
+
+
+void cProtocol172::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.WriteString(Printf("%d", a_Player.GetUniqueID())); // TODO: Proper UUID
+ Pkt.WriteString(a_Player.GetName());
+ Pkt.WriteFPInt(a_Player.GetPosX());
+ Pkt.WriteFPInt(a_Player.GetPosY());
+ Pkt.WriteFPInt(a_Player.GetPosZ());
+ Pkt.WriteByteAngle(a_Player.GetYaw());
+ Pkt.WriteByteAngle(a_Player.GetPitch());
+ short ItemType = a_Player.GetEquippedItem().IsEmpty() ? 0 : a_Player.GetEquippedItem().m_ItemType;
+ Pkt.WriteShort(ItemType);
+ Pkt.WriteByte((3 << 5) | 6); // Metadata: float + index 6
+ Pkt.WriteFloat((float)a_Player.GetHealth());
+ Pkt.WriteByte(0x7f); // Metadata: end
+}
+
+
+
+
+
+void cProtocol172::SendRespawn(void)
+{
+ cPacketizer Pkt(*this, 0x07); // Respawn packet
+ Pkt.WriteInt(m_Client->GetPlayer()->GetWorld()->GetDimension());
+ Pkt.WriteByte(2); // TODO: Difficulty (set to Normal)
+ Pkt.WriteByte((Byte)m_Client->GetPlayer()->GetEffectiveGameMode());
+ Pkt.WriteString("default");
+}
+
+
+
+
+
+void cProtocol172::SendExperience (void)
+{
+ cPacketizer Pkt(*this, 0x1F); //Experience Packet
+ Pkt.WriteFloat(m_Client->GetPlayer()->GetXpPercentage());
+ Pkt.WriteShort(m_Client->GetPlayer()->GetXpLevel());
+ Pkt.WriteShort(m_Client->GetPlayer()->GetCurrentXp());
+}
+
+
+
+
+
+void cProtocol172::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) // a_Src coords are Block * 8
+{
+ cPacketizer Pkt(*this, 0x29); // Sound Effect packet
+ Pkt.WriteString(a_SoundName);
+ Pkt.WriteInt(a_SrcX);
+ Pkt.WriteInt(a_SrcY);
+ Pkt.WriteInt(a_SrcZ);
+ Pkt.WriteFloat(a_Volume);
+ Pkt.WriteByte((Byte)(a_Pitch * 63));
+}
+
+
+
+
+
+void cProtocol172::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ cPacketizer Pkt(*this, 0x28); // Effect packet
+ Pkt.WriteInt(a_EffectID);
+ Pkt.WriteInt(a_SrcX);
+ Pkt.WriteByte(a_SrcY);
+ Pkt.WriteInt(a_SrcZ);
+ Pkt.WriteInt(a_Data);
+ Pkt.WriteBool(false);
+}
+
+
+
+
+
+void cProtocol172::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
+{
+ cPacketizer Pkt(*this, 0x0e); // Spawn Object packet
+ Pkt.WriteVarInt(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()) << 12));
+ Pkt.WriteShort((short)(a_FallingBlock.GetSpeedX() * 400));
+ Pkt.WriteShort((short)(a_FallingBlock.GetSpeedY() * 400));
+ Pkt.WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400));
+}
+
+
+
+
+
+void cProtocol172::SendSpawnMob(const cMonster & a_Mob)
+{
+ cPacketizer Pkt(*this, 0x0f); // Spawn Mob packet
+ Pkt.WriteVarInt(a_Mob.GetUniqueID());
+ Pkt.WriteByte((Byte)a_Mob.GetMobType());
+ Pkt.WriteFPInt(a_Mob.GetPosX());
+ Pkt.WriteFPInt(a_Mob.GetPosY());
+ Pkt.WriteFPInt(a_Mob.GetPosZ());
+ 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.WriteEntityMetadata(a_Mob);
+ Pkt.WriteByte(0x7f); // Metadata terminator
+}
+
+
+
+
+
+void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
+{
+ cPacketizer Pkt(*this, 0xe); // Spawn Object packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteByte(a_ObjectType);
+ Pkt.WriteFPInt(a_Entity.GetPosX());
+ Pkt.WriteFPInt(a_Entity.GetPosY());
+ Pkt.WriteFPInt(a_Entity.GetPosZ());
+ Pkt.WriteByteAngle(a_Entity.GetYaw());
+ Pkt.WriteByteAngle(a_Entity.GetPitch());
+ 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));
+ }
+}
+
+
+
+
+
+void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType)
+{
+ cPacketizer Pkt(*this, 0xe); // Spawn Object packet
+ Pkt.WriteVarInt(a_Vehicle.GetUniqueID());
+ Pkt.WriteByte(a_VehicleType);
+ Pkt.WriteFPInt(a_Vehicle.GetPosX());
+ Pkt.WriteFPInt(a_Vehicle.GetPosY());
+ Pkt.WriteFPInt(a_Vehicle.GetPosZ());
+ Pkt.WriteByteAngle(a_Vehicle.GetYaw());
+ Pkt.WriteByteAngle(a_Vehicle.GetPitch());
+ 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));
+ }
+}
+
+
+
+
+
+void cProtocol172::SendTabCompletionResults(const AStringVector & a_Results)
+{
+ AString Results;
+ Results.reserve(500); // Make a moderate reservation to avoid excessive reallocations
+ for (AStringVector::const_iterator itr = a_Results.begin(), end = a_Results.end(); itr != end; ++itr)
+ {
+ Results.append(*itr);
+ Results.push_back(0);
+ }
+
+ cPacketizer Pkt(*this, 0x3a); // Tab-Complete packet
+ Pkt.WriteVarInt(a_Results.size());
+ Pkt.WriteString(Results);
+}
+
+
+
+
+
+void cProtocol172::SendTeleportEntity(const cEntity & a_Entity)
+{
+ cPacketizer Pkt(*this, 0x18);
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteFPInt(a_Entity.GetPosX());
+ Pkt.WriteFPInt(a_Entity.GetPosY());
+ Pkt.WriteFPInt(a_Entity.GetPosZ());
+ Pkt.WriteByteAngle(a_Entity.GetYaw());
+ Pkt.WriteByteAngle(a_Entity.GetPitch());
+}
+
+
+
+
+
+void cProtocol172::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ cPacketizer Pkt(*this, 0x2c); // Spawn Global Entity packet
+ Pkt.WriteVarInt(0); // EntityID = 0, always
+ Pkt.WriteByte(1); // Type = Thunderbolt
+ Pkt.WriteFPInt(a_BlockX);
+ Pkt.WriteFPInt(a_BlockY);
+ Pkt.WriteFPInt(a_BlockZ);
+}
+
+
+
+
+
+void cProtocol172::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
+{
+ cPacketizer Pkt(*this, 0x03);
+ Pkt.WriteInt64(a_WorldAge);
+ Pkt.WriteInt64(a_TimeOfDay);
+}
+
+
+
+
+
+void cProtocol172::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+{
+ cPacketizer Pkt(*this, 0x21); // Chunk Data packet
+ Pkt.WriteInt(a_ChunkX);
+ Pkt.WriteInt(a_ChunkZ);
+ Pkt.WriteBool(true);
+ Pkt.WriteShort(0); // Primary bitmap
+ Pkt.WriteShort(0); // Add bitmap
+ Pkt.WriteInt(0); // Compressed data size
+}
+
+
+
+
+
+void cProtocol172::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
+{
+ cPacketizer Pkt(*this, 0x33);
+ Pkt.WriteInt(a_BlockX);
+ Pkt.WriteShort((short)a_BlockY);
+ Pkt.WriteInt(a_BlockZ);
+ Pkt.WriteString(a_Line1);
+ Pkt.WriteString(a_Line2);
+ Pkt.WriteString(a_Line3);
+ Pkt.WriteString(a_Line4);
+}
+
+
+
+
+
+void cProtocol172::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ cPacketizer Pkt(*this, 0x0a);
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteInt(a_BlockX);
+ Pkt.WriteByte((Byte)a_BlockY);
+ Pkt.WriteInt(a_BlockZ);
+}
+
+
+
+
+
+void cProtocol172::SendWeather(eWeather a_Weather)
+{
+ {
+ cPacketizer Pkt(*this, 0x2b); // Change Game State packet
+ Pkt.WriteByte((a_Weather == wSunny) ? 1 : 2); // End rain / begin rain
+ Pkt.WriteFloat(0); // Unused for weather
+ }
+
+ // TODO: Fade effect, somehow
+}
+
+
+
+
+
+void cProtocol172::SendWholeInventory(const cWindow & a_Window)
+{
+ cPacketizer Pkt(*this, 0x30); // Window Items packet
+ Pkt.WriteChar(a_Window.GetWindowID());
+ Pkt.WriteShort(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)
+ {
+ Pkt.WriteItem(*itr);
+ } // for itr - Slots[]
+}
+
+
+
+
+
+void cProtocol172::SendWindowClose(const cWindow & a_Window)
+{
+ cPacketizer Pkt(*this, 0x2e);
+ Pkt.WriteChar(a_Window.GetWindowID());
+}
+
+
+
+
+
+void cProtocol172::SendWindowOpen(const cWindow & a_Window)
+{
+ if (a_Window.GetWindowType() < 0)
+ {
+ // Do not send this packet for player inventory windows
+ return;
+ }
+
+ cPacketizer Pkt(*this, 0x2d);
+ Pkt.WriteChar(a_Window.GetWindowID());
+ Pkt.WriteChar(a_Window.GetWindowType());
+ Pkt.WriteString(a_Window.GetWindowTitle());
+ Pkt.WriteChar(a_Window.GetNumNonInventorySlots());
+ Pkt.WriteBool(true);
+ if (a_Window.GetWindowType() == cWindow::wtAnimalChest)
+ {
+ Pkt.WriteInt(0); // TODO: The animal's EntityID
+ }
+}
+
+
+
+
+
+void cProtocol172::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value)
+{
+ cPacketizer Pkt(*this, 0x31); // Window Property packet
+ Pkt.WriteChar(a_Window.GetWindowID());
+ Pkt.WriteShort(a_Property);
+ Pkt.WriteShort(a_Value);
+}
+
+
+
+
+
+void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
+{
+ if (!m_ReceivedData.Write(a_Data, a_Size))
+ {
+ // Too much data in the incoming queue, report to caller:
+ m_Client->PacketBufferFull();
+ return;
+ }
+
+ // Handle all complete packets:
+ while (true)
+ {
+ UInt32 PacketLen;
+ if (!m_ReceivedData.ReadVarInt(PacketLen))
+ {
+ // Not enough data
+ return;
+ }
+ if (!m_ReceivedData.CanReadBytes(PacketLen))
+ {
+ // The full packet hasn't been received yet
+ return;
+ }
+ UInt32 PacketType;
+ UInt32 Mark1 = m_ReceivedData.GetReadableSpace();
+ if (!m_ReceivedData.ReadVarInt(PacketType))
+ {
+ // Not enough data
+ return;
+ }
+
+ UInt32 NumBytesRead = Mark1 - m_ReceivedData.GetReadableSpace();
+ HandlePacket(PacketType, PacketLen - NumBytesRead);
+
+ if (Mark1 - m_ReceivedData.GetReadableSpace() > PacketLen)
+ {
+ // Read more than packet length, report as error
+ m_Client->PacketError(PacketType);
+ }
+
+ // Go to packet end in any case:
+ m_ReceivedData.ResetRead();
+ m_ReceivedData.ReadVarInt(PacketType);
+ m_ReceivedData.SkipRead(PacketLen);
+ m_ReceivedData.CommitRead();
+ } // while (true)
+}
+
+
+
+
+void cProtocol172::HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes)
+{
+ switch (m_State)
+ {
+ case 1:
+ {
+ // Status
+ switch (a_PacketType)
+ {
+ case 0x00: HandlePacketStatusRequest(a_RemainingBytes); return;
+ case 0x01: HandlePacketStatusPing (a_RemainingBytes); return;
+ }
+ break;
+ }
+
+ case 2:
+ {
+ // Login
+ switch (a_PacketType)
+ {
+ case 0x00: HandlePacketLoginStart(a_RemainingBytes); return;
+ case 0x01: HandlePacketLoginEncryptionResponse(a_RemainingBytes); return;
+ }
+ break;
+ }
+
+ case 3:
+ {
+ // Game
+ switch (a_PacketType)
+ {
+ case 0x00: HandlePacketKeepAlive (a_RemainingBytes); return;
+ case 0x01: HandlePacketChatMessage (a_RemainingBytes); return;
+ case 0x02: HandlePacketUseEntity (a_RemainingBytes); return;
+ case 0x03: HandlePacketPlayer (a_RemainingBytes); return;
+ case 0x04: HandlePacketPlayerPos (a_RemainingBytes); return;
+ case 0x05: HandlePacketPlayerLook (a_RemainingBytes); return;
+ case 0x06: HandlePacketPlayerPosLook (a_RemainingBytes); return;
+ case 0x07: HandlePacketBlockDig (a_RemainingBytes); return;
+ case 0x08: HandlePacketBlockPlace (a_RemainingBytes); return;
+ case 0x09: HandlePacketSlotSelect (a_RemainingBytes); return;
+ case 0x0a: HandlePacketAnimation (a_RemainingBytes); return;
+ case 0x0b: HandlePacketEntityAction (a_RemainingBytes); return;
+ case 0x0c: HandlePacketSteerVehicle (a_RemainingBytes); return;
+ case 0x0d: HandlePacketWindowClose (a_RemainingBytes); return;
+ case 0x0e: HandlePacketWindowClick (a_RemainingBytes); return;
+ case 0x0f: // Confirm transaction - not used in MCS
+ case 0x10: HandlePacketCreativeInventoryAction(a_RemainingBytes); return;
+ case 0x12: HandlePacketUpdateSign (a_RemainingBytes); return;
+ case 0x13: HandlePacketPlayerAbilities (a_RemainingBytes); return;
+ case 0x14: HandlePacketTabComplete (a_RemainingBytes); return;
+ case 0x15: HandlePacketClientSettings (a_RemainingBytes); return;
+ case 0x16: HandlePacketClientStatus (a_RemainingBytes); return;
+ case 0x17: HandlePacketPluginMessage (a_RemainingBytes); return;
+ }
+ break;
+ }
+ } // switch (m_State)
+
+ // Unknown packet type, report to the client:
+ m_Client->PacketUnknown(a_PacketType);
+ m_ReceivedData.SkipRead(a_RemainingBytes);
+ m_ReceivedData.CommitRead();
+}
+
+
+
+
+
+void cProtocol172::HandlePacketStatusPing(UInt32 a_RemainingBytes)
+{
+ ASSERT(a_RemainingBytes == 8);
+ if (a_RemainingBytes != 8)
+ {
+ m_Client->PacketError(0x01);
+ m_ReceivedData.SkipRead(a_RemainingBytes);
+ m_ReceivedData.CommitRead();
+ return;
+ }
+ Int64 Timestamp;
+ m_ReceivedData.ReadBEInt64(Timestamp);
+ m_ReceivedData.CommitRead();
+
+ cPacketizer Pkt(*this, 0x01); // Ping packet
+ Pkt.WriteInt64(Timestamp);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketStatusRequest(UInt32 a_RemainingBytes)
+{
+ // No more bytes in this packet
+ ASSERT(a_RemainingBytes == 0);
+ m_ReceivedData.CommitRead();
+
+ // Send the response:
+ AString Response = "{\"version\":{\"name\":\"1.7.2\",\"protocol\":4},\"players\":{";
+ AppendPrintf(Response, "\"max\":%u,\"online\":%u,\"sample\":[]},",
+ cRoot::Get()->GetServer()->GetMaxPlayers(),
+ cRoot::Get()->GetServer()->GetNumPlayers()
+ );
+ AppendPrintf(Response, "\"description\":{\"text\":\"%s\"}",
+ cRoot::Get()->GetServer()->GetDescription().c_str()
+ );
+ Response.append("}");
+
+ cPacketizer Pkt(*this, 0x00); // Response packet
+ Pkt.WriteString(Response);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes)
+{
+ // TODO: Add protocol encryption
+}
+
+
+
+
+
+void cProtocol172::HandlePacketLoginStart(UInt32 a_RemainingBytes)
+{
+ AString Username;
+ m_ReceivedData.ReadVarUTF8String(Username);
+
+ // TODO: Protocol encryption should be set up here if not localhost / auth
+
+ // Send login success:
+ {
+ cPacketizer Pkt(*this, 0x02); // Login success packet
+ Pkt.WriteString(Printf("%d", m_Client->GetUniqueID())); // TODO: proper UUID
+ Pkt.WriteString(Username);
+ }
+
+ m_State = 3; // State = Game
+ m_Client->HandleLogin(4, Username);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketAnimation(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEInt, int, EntityID);
+ HANDLE_READ(ReadByte, Byte, Animation);
+ m_Client->HandleAnimation(Animation);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketBlockDig(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadByte, Byte, Status);
+ HANDLE_READ(ReadBEInt, int, BlockX);
+ HANDLE_READ(ReadByte, Byte, BlockY);
+ HANDLE_READ(ReadBEInt, int, BlockZ);
+ HANDLE_READ(ReadByte, Byte, Face);
+ m_Client->HandleLeftClick(BlockX, BlockY, BlockZ, Face, Status);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketBlockPlace(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEInt, int, BlockX);
+ HANDLE_READ(ReadByte, Byte, BlockY);
+ HANDLE_READ(ReadBEInt, int, BlockZ);
+ HANDLE_READ(ReadByte, Byte, Face);
+ HANDLE_READ(ReadByte, Byte, CursorX);
+ HANDLE_READ(ReadByte, Byte, CursorY);
+ HANDLE_READ(ReadByte, Byte, CursorZ);
+ m_Client->HandleRightClick(BlockX, BlockY, BlockZ, Face, CursorX, CursorY, CursorZ, m_Client->GetPlayer()->GetEquippedItem());
+}
+
+
+
+
+
+void cProtocol172::HandlePacketChatMessage(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadVarUTF8String, AString, Message);
+ m_Client->HandleChat(Message);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketClientSettings(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadVarUTF8String, AString, Locale);
+ HANDLE_READ(ReadByte, Byte, ViewDistance);
+ HANDLE_READ(ReadByte, Byte, ChatFlags);
+ HANDLE_READ(ReadByte, Byte, Unused);
+ HANDLE_READ(ReadByte, Byte, Difficulty);
+ HANDLE_READ(ReadByte, Byte, ShowCape);
+ // TODO: handle in m_Client
+}
+
+
+
+
+
+void cProtocol172::HandlePacketClientStatus(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadByte, Byte, ActionID);
+ switch (ActionID)
+ {
+ case 0:
+ {
+ // Respawn
+ m_Client->HandleRespawn();
+ break;
+ }
+ case 1:
+ {
+ // Request stats
+ // TODO
+ break;
+ }
+ case 2:
+ {
+ // Open Inventory achievement
+ // TODO
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cProtocol172::HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEShort, short, SlotNum);
+ cItem Item;
+ if (!ReadItem(Item))
+ {
+ return;
+ }
+ m_Client->HandleCreativeInventory(SlotNum, Item);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketEntityAction(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEInt, int, PlayerID);
+ HANDLE_READ(ReadByte, Byte, Action);
+ HANDLE_READ(ReadBEInt, int, JumpBoost);
+ m_Client->HandleEntityAction(PlayerID, Action);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketKeepAlive(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEInt, int, KeepAliveID);
+ m_Client->HandleKeepAlive(KeepAliveID);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPlayer(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBool, bool, IsOnGround);
+ // TODO: m_Client->HandlePlayerOnGround(IsOnGround);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPlayerAbilities(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadByte, Byte, Flags);
+ HANDLE_READ(ReadBEFloat, float, FlyingSpeed);
+ HANDLE_READ(ReadBEFloat, float, WalkingSpeed);
+ // TODO: m_Client->HandlePlayerAbilities();
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPlayerLook(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEFloat, float, Yaw);
+ HANDLE_READ(ReadBEFloat, float, Pitch);
+ HANDLE_READ(ReadBool, bool, IsOnGround);
+ m_Client->HandlePlayerLook(Yaw, Pitch, IsOnGround);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPlayerPos(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEDouble, double, PosX);
+ HANDLE_READ(ReadBEDouble, double, PosY);
+ HANDLE_READ(ReadBEDouble, double, Stance);
+ HANDLE_READ(ReadBEDouble, double, PosZ);
+ HANDLE_READ(ReadBool, bool, IsOnGround);
+ m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPlayerPosLook(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEDouble, double, PosX);
+ HANDLE_READ(ReadBEDouble, double, PosY);
+ HANDLE_READ(ReadBEDouble, double, Stance);
+ HANDLE_READ(ReadBEDouble, double, PosZ);
+ HANDLE_READ(ReadBEFloat, float, Yaw);
+ HANDLE_READ(ReadBEFloat, float, Pitch);
+ HANDLE_READ(ReadBool, bool, IsOnGround);
+ m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Yaw, Pitch, IsOnGround);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketPluginMessage(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadVarUTF8String, AString, Channel);
+ HANDLE_READ(ReadBEShort, short, Length);
+ AString Data;
+ m_ReceivedData.ReadString(Data, Length);
+ // TODO: m_Client->HandlePluginMessage(Channel, Data);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketSlotSelect(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEShort, short, SlotNum);
+ m_Client->HandleSlotSelected(SlotNum);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketSteerVehicle(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEFloat, float, Forward);
+ HANDLE_READ(ReadBEFloat, float, Sideways);
+ HANDLE_READ(ReadBool, bool, ShouldJump);
+ HANDLE_READ(ReadBool, bool, ShouldUnmount);
+ if (ShouldUnmount)
+ {
+ m_Client->HandleUnmount();
+ }
+ else
+ {
+ m_Client->HandleSteerVehicle(Forward, Sideways);
+ }
+}
+
+
+
+
+
+void cProtocol172::HandlePacketTabComplete(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadVarUTF8String, AString, Text);
+ m_Client->HandleTabCompletion(Text);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketUpdateSign(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEInt, int, BlockX);
+ HANDLE_READ(ReadBEShort, short, BlockY);
+ HANDLE_READ(ReadBEInt, int, BlockZ);
+ HANDLE_READ(ReadVarUTF8String, AString, Line1);
+ HANDLE_READ(ReadVarUTF8String, AString, Line2);
+ HANDLE_READ(ReadVarUTF8String, AString, Line3);
+ HANDLE_READ(ReadVarUTF8String, AString, Line4);
+ m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketUseEntity(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadBEInt, int, EntityID);
+ HANDLE_READ(ReadByte, Byte, MouseButton);
+ m_Client->HandleUseEntity(EntityID, (MouseButton == 1));
+}
+
+
+
+
+
+void cProtocol172::HandlePacketWindowClick(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadChar, char, WindowID);
+ HANDLE_READ(ReadBEShort, short, SlotNum);
+ HANDLE_READ(ReadByte, Byte, Button);
+ HANDLE_READ(ReadBEShort, short, TransactionID);
+ HANDLE_READ(ReadByte, Byte, Mode);
+ cItem Item;
+ ReadItem(Item);
+
+ // Convert Button, Mode, SlotNum and HeldItem into eClickAction:
+ eClickAction Action;
+ switch ((Mode << 8) | Button)
+ {
+ case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break;
+ case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break;
+ case 0x0100: Action = caShiftLeftClick; break;
+ case 0x0101: Action = caShiftRightClick; break;
+ case 0x0200: Action = caNumber1; break;
+ case 0x0201: Action = caNumber2; break;
+ case 0x0202: Action = caNumber3; break;
+ case 0x0203: Action = caNumber4; break;
+ case 0x0204: Action = caNumber5; break;
+ case 0x0205: Action = caNumber6; break;
+ case 0x0206: Action = caNumber7; break;
+ case 0x0207: Action = caNumber8; break;
+ case 0x0208: Action = caNumber9; break;
+ case 0x0300: Action = caMiddleClick; break;
+ case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break;
+ case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break;
+ case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break;
+ case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break;
+ case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break;
+ case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break;
+ case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break;
+ case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break;
+ case 0x0600: Action = caDblClick; break;
+ }
+
+ m_Client->HandleWindowClick(WindowID, SlotNum, Action, Item);
+}
+
+
+
+
+
+void cProtocol172::HandlePacketWindowClose(UInt32 a_RemainingBytes)
+{
+ HANDLE_READ(ReadChar, char, WindowID);
+ m_Client->HandleWindowClose(WindowID);
+}
+
+
+
+
+
+void cProtocol172::WritePacket(cByteBuffer & a_Packet)
+{
+ cCSLock Lock(m_CSPacket);
+ AString Pkt;
+ a_Packet.ReadAll(Pkt);
+ WriteVarInt(Pkt.size());
+ SendData(Pkt.data(), Pkt.size());
+ Flush();
+}
+
+
+
+
+
+void cProtocol172::SendData(const char * a_Data, int a_Size)
+{
+ if (m_IsEncrypted)
+ {
+ byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
+ while (a_Size > 0)
+ {
+ int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size;
+ m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes);
+ m_Client->SendData((const char *)Encrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ m_Client->SendData(a_Data, a_Size);
+ }
+}
+
+
+
+
+
+
+bool cProtocol172::ReadItem(cItem & a_Item)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, ItemType);
+ if (ItemType == -1)
+ {
+ // The item is empty, no more data follows
+ a_Item.Empty();
+ return true;
+ }
+ a_Item.m_ItemType = ItemType;
+
+ HANDLE_PACKET_READ(ReadChar, char, ItemCount);
+ HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
+ a_Item.m_ItemCount = ItemCount;
+ a_Item.m_ItemDamage = ItemDamage;
+ if (ItemCount <= 0)
+ {
+ a_Item.Empty();
+ }
+
+ HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength);
+ if (MetadataLength <= 0)
+ {
+ return true;
+ }
+
+ // Read the metadata
+ AString Metadata;
+ if (!m_ReceivedData.ReadString(Metadata, MetadataLength))
+ {
+ return false;
+ }
+
+ ParseItemMetadata(a_Item, Metadata);
+ return true;
+}
+
+
+
+
+
+void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
+{
+ // Uncompress the GZIPped data:
+ AString Uncompressed;
+ if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK)
+ {
+ AString HexDump;
+ CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16);
+ LOGWARNING("Cannot unGZIP item metadata (%u bytes):\n%s", a_Metadata.size(), HexDump.c_str());
+ return;
+ }
+
+ // Parse into NBT:
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
+ if (!NBT.IsValid())
+ {
+ AString HexDump;
+ CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16);
+ LOGWARNING("Cannot parse NBT item metadata: (%u bytes)\n%s", Uncompressed.size(), HexDump.c_str());
+ return;
+ }
+
+ // Load enchantments from the NBT:
+ for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
+ {
+ if (
+ (NBT.GetType(tag) == TAG_List) &&
+ (
+ (NBT.GetName(tag) == "ench") ||
+ (NBT.GetName(tag) == "StoredEnchantments")
+ )
+ )
+ {
+ a_Item.m_Enchantments.ParseFromNBT(NBT, tag);
+ }
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol172::cPacketizer:
+
+cProtocol172::cPacketizer::~cPacketizer()
+{
+ AString DataToSend;
+
+ // Send the packet length
+ UInt32 PacketLen = m_Out.GetUsedSpace();
+ m_Protocol.m_OutPacketLenBuffer.WriteVarInt(PacketLen);
+ m_Protocol.m_OutPacketLenBuffer.ReadAll(DataToSend);
+ m_Protocol.SendData(DataToSend.data(), DataToSend.size());
+ m_Protocol.m_OutPacketLenBuffer.CommitRead();
+
+ // Send the packet data:
+ m_Out.ReadAll(DataToSend);
+ m_Protocol.SendData(DataToSend.data(), DataToSend.size());
+ m_Out.CommitRead();
+}
+
+
+
+
+
+void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item)
+{
+ short ItemType = a_Item.m_ItemType;
+ ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
+ if (ItemType <= 0)
+ {
+ // Fix, to make sure no invalid values are sent.
+ ItemType = -1;
+ }
+
+ if (a_Item.IsEmpty())
+ {
+ WriteShort(-1);
+ return;
+ }
+
+ WriteShort(ItemType);
+ WriteByte (a_Item.m_ItemCount);
+ WriteShort(a_Item.m_ItemDamage);
+
+ if (a_Item.m_Enchantments.IsEmpty())
+ {
+ WriteShort(-1);
+ return;
+ }
+
+ // Send the enchantments:
+ cFastNBTWriter Writer;
+ const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
+ a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName);
+ Writer.Finish();
+ AString Compressed;
+ CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
+ WriteShort(Compressed.size());
+ WriteBuf(Compressed.data(), Compressed.size());
+}
+
+
+
+
+
+void cProtocol172::cPacketizer::WriteByteAngle(double a_Angle)
+{
+ WriteByte((char)(255 * a_Angle / 360));
+}
+
+
+
+
+
+void cProtocol172::cPacketizer::WriteFPInt(double a_Value)
+{
+ int Value = (int)(a_Value * 32);
+ WriteInt(Value);
+}
+
+
+
+
+
+void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity)
+{
+ // Common metadata:
+ Byte Flags = 0;
+ if (a_Entity.IsOnFire())
+ {
+ Flags |= 0x01;
+ }
+ if (a_Entity.IsCrouched())
+ {
+ Flags |= 0x02;
+ }
+ if (a_Entity.IsSprinting())
+ {
+ Flags |= 0x08;
+ }
+ if (a_Entity.IsRclking())
+ {
+ Flags |= 0x10;
+ }
+ if (a_Entity.IsInvisible())
+ {
+ Flags |= 0x20;
+ }
+ WriteByte(0); // Byte(0) + index 0
+ WriteByte(Flags);
+
+ switch (a_Entity.GetEntityType())
+ {
+ case cEntity::etPlayer: break; // TODO?
+ case cEntity::etPickup:
+ {
+ WriteByte((5 << 5) | 10); // Slot(5) + index 10
+ WriteItem(((const cPickup &)a_Entity).GetItem());
+ break;
+ }
+ case cEntity::etMinecart:
+ {
+ WriteByte(0x51);
+
+ // The following expression makes Minecarts shake more with less health or higher damage taken
+ // It gets half the maximum health, and takes it away from the current health minus the half health:
+ /* Health: 5 | 3 - (5 - 3) = 1 (shake power)
+ Health: 3 | 3 - (3 - 3) = 3
+ Health: 1 | 3 - (1 - 3) = 5
+ */
+ WriteInt((((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4);
+ WriteByte(0x52);
+ WriteInt(1); // Shaking direction, doesn't seem to affect anything
+ WriteByte(0x73);
+ WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer
+
+ if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace)
+ {
+ WriteByte(0x10);
+ WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0);
+ }
+ break;
+ }
+ case cEntity::etProjectile:
+ {
+ if (((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow)
+ {
+ WriteByte(0x10);
+ WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0);
+ }
+ break;
+ }
+ case cEntity::etMonster:
+ {
+ WriteMobMetadata((const cMonster &)a_Entity);
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
+{
+ switch (a_Mob.GetMobType())
+ {
+ case cMonster::mtCreeper:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1);
+ WriteByte(0x11);
+ WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0);
+ break;
+ }
+
+ case cMonster::mtBat:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0);
+ break;
+ }
+
+ case cMonster::mtPig:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0);
+ break;
+ }
+
+ case cMonster::mtVillager:
+ {
+ WriteByte(0x50);
+ WriteInt(((const cVillager &)a_Mob).GetVilType());
+ break;
+ }
+
+ case cMonster::mtZombie:
+ {
+ WriteByte(0x0c);
+ WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0);
+ WriteByte(0x0d);
+ WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0);
+ WriteByte(0x0e);
+ WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0);
+ break;
+ }
+
+ case cMonster::mtGhast:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cGhast &)a_Mob).IsCharging());
+ break;
+ }
+
+ case cMonster::mtWolf:
+ {
+ const cWolf & Wolf = (const cWolf &)a_Mob;
+ Byte WolfStatus = 0;
+ if (Wolf.IsSitting())
+ {
+ WolfStatus |= 0x1;
+ }
+ if (Wolf.IsAngry())
+ {
+ WolfStatus |= 0x2;
+ }
+ if (Wolf.IsTame())
+ {
+ WolfStatus |= 0x4;
+ }
+ WriteByte(0x10);
+ WriteByte(WolfStatus);
+
+ WriteByte(0x72);
+ WriteFloat((float)(a_Mob.GetHealth()));
+ WriteByte(0x13);
+ WriteByte(Wolf.IsBegging() ? 1 : 0);
+ WriteByte(0x14);
+ WriteByte(Wolf.GetCollarColor());
+ break;
+ }
+
+ case cMonster::mtSheep:
+ {
+ WriteByte(0x10);
+ Byte SheepMetadata = 0;
+ SheepMetadata = ((const cSheep &)a_Mob).GetFurColor();
+ if (((const cSheep &)a_Mob).IsSheared())
+ {
+ SheepMetadata |= 0x10;
+ }
+ WriteByte(SheepMetadata);
+ break;
+ }
+
+ case cMonster::mtEnderman:
+ {
+ WriteByte(0x10);
+ WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock()));
+ WriteByte(0x11);
+ WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta()));
+ WriteByte(0x12);
+ WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0);
+ break;
+ }
+
+ case cMonster::mtSkeleton:
+ {
+ WriteByte(0x0d);
+ WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0);
+ break;
+ }
+
+ case cMonster::mtWitch:
+ {
+ WriteByte(0x15);
+ WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0);
+ break;
+ }
+
+ case cMonster::mtSlime:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cSlime &)a_Mob).GetSize());
+ break;
+ }
+
+ case cMonster::mtMagmaCube:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cMagmaCube &)a_Mob).GetSize());
+ break;
+ }
+
+ case cMonster::mtHorse:
+ {
+ const cHorse & Horse = (const cHorse &)a_Mob;
+ int Flags = 0;
+ if (Horse.IsTame())
+ {
+ Flags |= 0x02;
+ }
+ if (Horse.IsSaddled())
+ {
+ Flags |= 0x04;
+ }
+ if (Horse.IsChested())
+ {
+ Flags |= 0x08;
+ }
+ if (Horse.IsBaby())
+ {
+ Flags |= 0x10;
+ }
+ if (Horse.IsEating())
+ {
+ Flags |= 0x20;
+ }
+ if (Horse.IsRearing())
+ {
+ Flags |= 0x40;
+ }
+ if (Horse.IsMthOpen())
+ {
+ Flags |= 0x80;
+ }
+ WriteByte(0x50); // Int at index 16
+ WriteInt(Flags);
+ WriteByte(0x13); // Byte at index 19
+ WriteByte(Horse.GetHorseType());
+ WriteByte(0x54); // Int at index 20
+ int Appearance = 0;
+ Appearance = Horse.GetHorseColor();
+ Appearance |= Horse.GetHorseStyle() << 8;
+ WriteInt(Appearance);
+ WriteByte(0x56); // Int at index 22
+ WriteInt(Horse.GetHorseArmour());
+ break;
+ }
+ } // switch (a_Mob.GetType())
+}
+
+
+
+
+
+void cProtocol172::cPacketizer::WriteEntityProperties(const cEntity & a_Entity)
+{
+ if (!a_Entity.IsMob())
+ {
+ // No properties for anything else than mobs
+ WriteInt(0);
+ return;
+ }
+ const cMonster & Mob = (const cMonster &)a_Entity;
+
+ // TODO: Send properties and modifiers based on the mob type
+
+ WriteInt(0); // NumProperties
+}
+
+
+
+
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
new file mode 100644
index 000000000..449727854
--- /dev/null
+++ b/src/Protocol/Protocol17x.h
@@ -0,0 +1,259 @@
+
+// Protocol17x.h
+
+/*
+Declares the 1.7.x protocol classes:
+ - cProtocol172
+ - release 1.7.2 protocol (#4)
+(others may be added later in the future for the 1.7 release series)
+*/
+
+
+
+
+
+#pragma once
+
+#include "Protocol.h"
+#include "../ByteBuffer.h"
+#include "lib/cryptopp/modes.h"
+#include "lib/cryptopp/aes.h"
+
+
+
+
+
+class cProtocol172 :
+ public cProtocol // TODO
+{
+ typedef cProtocol super; // TODO
+
+public:
+
+ cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
+
+ /// Called when client sends some data:
+ virtual void DataReceived(const char * a_Data, int a_Size) override;
+
+ /// Sending stuff to clients (alphabetically sorted):
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
+ virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) override;
+ virtual void SendDisconnect (const AString & a_Reason) override;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
+ virtual void SendEntityLook (const cEntity & a_Entity) override;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) override;
+ virtual void SendEntityProperties (const cEntity & a_Entity) override;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) override;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
+ virtual void SendGameMode (eGameMode a_GameMode) override;
+ virtual void SendHealth (void) override;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendKeepAlive (int a_PingID) override;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendPlayerAbilities (void) override;
+ virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override;
+ virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
+ virtual void SendPlayerMaxSpeed (void) override;
+ virtual void SendPlayerMoveLook (void) override;
+ virtual void SendPlayerPosition (void) override;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendRespawn (void) override;
+ virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
+ virtual void SendExperience (void) override;
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
+ virtual void SendSpawnMob (const cMonster & a_Mob) override;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
+ virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) override;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
+ virtual void SendWeather (eWeather a_Weather) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+ virtual void SendWindowClose (const cWindow & a_Window) override;
+ virtual void SendWindowOpen (const cWindow & a_Window) override;
+ virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override;
+
+ virtual AString GetAuthServerID(void) override { return m_AuthServerID; }
+
+protected:
+
+ /// Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed
+ class cPacketizer
+ {
+ public:
+ cPacketizer(cProtocol172 & a_Protocol, UInt32 a_PacketType) :
+ m_Protocol(a_Protocol),
+ m_Out(a_Protocol.m_OutPacketBuffer),
+ m_Lock(a_Protocol.m_CSPacket)
+ {
+ m_Out.WriteVarInt(a_PacketType);
+ }
+
+ ~cPacketizer();
+
+ void WriteBool(bool a_Value)
+ {
+ m_Out.WriteBool(a_Value);
+ }
+
+ void WriteByte(Byte a_Value)
+ {
+ m_Out.WriteByte(a_Value);
+ }
+
+ void WriteChar(char a_Value)
+ {
+ m_Out.WriteChar(a_Value);
+ }
+
+ void WriteShort(short a_Value)
+ {
+ m_Out.WriteBEShort(a_Value);
+ }
+
+ void WriteInt(int a_Value)
+ {
+ m_Out.WriteBEInt(a_Value);
+ }
+
+ void WriteInt64(Int64 a_Value)
+ {
+ m_Out.WriteBEInt64(a_Value);
+ }
+
+ void WriteFloat(float a_Value)
+ {
+ m_Out.WriteBEFloat(a_Value);
+ }
+
+ void WriteDouble(double a_Value)
+ {
+ m_Out.WriteBEDouble(a_Value);
+ }
+
+ void WriteVarInt(UInt32 a_Value)
+ {
+ m_Out.WriteVarInt(a_Value);
+ }
+
+ void WriteString(const AString & a_Value)
+ {
+ m_Out.WriteVarUTF8String(a_Value);
+ }
+
+ void WriteBuf(const char * a_Data, int a_Size)
+ {
+ m_Out.Write(a_Data, a_Size);
+ }
+
+ void WriteItem(const cItem & a_Item);
+ void WriteByteAngle(double a_Angle); // Writes the specified angle using a single byte
+ void WriteFPInt(double a_Value); // Writes the double value as a 27:5 fixed-point integer
+ void WriteEntityMetadata(const cEntity & a_Entity); // Writes the metadata for the specified entity, not including the terminating 0x7f
+ void WriteMobMetadata(const cMonster & a_Mob); // Writes the mob-specific metadata for the specified mob
+ void WriteEntityProperties(const cEntity & a_Entity); // Writes the entity properties for the specified entity, including the Count field
+
+ protected:
+ cProtocol172 & m_Protocol;
+ cByteBuffer & m_Out;
+ cCSLock m_Lock;
+ } ;
+
+ AString m_ServerAddress;
+
+ UInt16 m_ServerPort;
+
+ AString m_AuthServerID;
+
+ /// State of the protocol. 1 = status, 2 = login, 3 = game
+ UInt32 m_State;
+
+ /// Buffer for the received data
+ cByteBuffer m_ReceivedData;
+
+ /// Buffer for composing the outgoing packets, through cPacketizer
+ cByteBuffer m_OutPacketBuffer;
+
+ /// Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer)
+ cByteBuffer m_OutPacketLenBuffer;
+
+ bool m_IsEncrypted;
+ CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor;
+ CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor;
+
+
+ /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets
+ void AddReceivedData(const char * a_Data, int a_Size);
+
+ /// Reads and handles the packet. The packet length and type have already been read.
+ void HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes);
+
+ // Packet handlers while in the Status state (m_State == 1):
+ void HandlePacketStatusPing (UInt32 a_RemainingBytes);
+ void HandlePacketStatusRequest(UInt32 a_RemainingBytes);
+
+ // Packet handlers while in the Login state (m_State == 2):
+ void HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes);
+ void HandlePacketLoginStart (UInt32 a_RemainingBytes);
+
+ // Packet handlers while in the Game state (m_State == 3):
+ void HandlePacketAnimation (UInt32 a_RemainingBytes);
+ void HandlePacketBlockDig (UInt32 a_RemainingBytes);
+ void HandlePacketBlockPlace (UInt32 a_RemainingBytes);
+ void HandlePacketChatMessage (UInt32 a_RemainingBytes);
+ void HandlePacketClientSettings (UInt32 a_RemainingBytes);
+ void HandlePacketClientStatus (UInt32 a_RemainingBytes);
+ void HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes);
+ void HandlePacketEntityAction (UInt32 a_RemainingBytes);
+ void HandlePacketKeepAlive (UInt32 a_RemainingBytes);
+ void HandlePacketPlayer (UInt32 a_RemainingBytes);
+ void HandlePacketPlayerAbilities (UInt32 a_RemainingBytes);
+ void HandlePacketPlayerLook (UInt32 a_RemainingBytes);
+ void HandlePacketPlayerPos (UInt32 a_RemainingBytes);
+ void HandlePacketPlayerPosLook (UInt32 a_RemainingBytes);
+ void HandlePacketPluginMessage (UInt32 a_RemainingBytes);
+ void HandlePacketSlotSelect (UInt32 a_RemainingBytes);
+ void HandlePacketSteerVehicle (UInt32 a_RemainingBytes);
+ void HandlePacketTabComplete (UInt32 a_RemainingBytes);
+ void HandlePacketUpdateSign (UInt32 a_RemainingBytes);
+ void HandlePacketUseEntity (UInt32 a_RemainingBytes);
+ void HandlePacketWindowClick (UInt32 a_RemainingBytes);
+ void HandlePacketWindowClose (UInt32 a_RemainingBytes);
+
+
+ /// Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here.
+ void WritePacket(cByteBuffer & a_Packet);
+
+ /// Sends the data to the client, encrypting them if needed.
+ virtual void SendData(const char * a_Data, int a_Size) override;
+
+ void SendCompass(const cWorld & a_World);
+
+ /// Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data
+ bool ReadItem(cItem & a_Item);
+
+ /// Parses item metadata as read by ReadItem(), into the item enchantments.
+ void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
+} ;
+
+
+
+
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
new file mode 100644
index 000000000..64bd83075
--- /dev/null
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -0,0 +1,915 @@
+
+// ProtocolRecognizer.cpp
+
+// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple
+// protocol versions and redirects everything to them
+
+#include "Globals.h"
+
+#include "ProtocolRecognizer.h"
+#include "Protocol125.h"
+#include "Protocol132.h"
+#include "Protocol14x.h"
+#include "Protocol15x.h"
+#include "Protocol16x.h"
+#include "Protocol17x.h"
+#include "../ClientHandle.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../World.h"
+#include "../ChatColor.h"
+
+
+
+
+
+cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) :
+ super(a_Client),
+ m_Protocol(NULL),
+ m_Buffer(512)
+{
+}
+
+
+
+
+
+cProtocolRecognizer::~cProtocolRecognizer()
+{
+ delete m_Protocol;
+}
+
+
+
+
+
+AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion)
+{
+ switch (a_ProtocolVersion)
+ {
+ case PROTO_VERSION_1_2_5: return "1.2.5";
+ case PROTO_VERSION_1_3_2: return "1.3.2";
+ case PROTO_VERSION_1_4_2: return "1.4.2";
+ case PROTO_VERSION_1_4_4: return "1.4.4";
+ case PROTO_VERSION_1_4_6: return "1.4.6";
+ case PROTO_VERSION_1_5_0: return "1.5";
+ case PROTO_VERSION_1_5_2: return "1.5.2";
+ case PROTO_VERSION_1_6_1: return "1.6.1";
+ case PROTO_VERSION_1_6_2: return "1.6.2";
+ case PROTO_VERSION_1_6_3: return "1.6.3";
+ case PROTO_VERSION_1_6_4: return "1.6.4";
+ case PROTO_VERSION_1_7_2: return "1.7.2";
+ }
+ ASSERT(!"Unknown protocol version");
+ return Printf("Unknown protocol (%d)", a_ProtocolVersion);
+}
+
+
+
+
+
+void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size)
+{
+ if (m_Protocol == NULL)
+ {
+ if (!m_Buffer.Write(a_Data, a_Size))
+ {
+ m_Client->Kick("Unsupported protocol version");
+ return;
+ }
+
+ if (!TryRecognizeProtocol())
+ {
+ return;
+ }
+
+ // The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing:
+ AString Dump;
+ m_Buffer.ResetRead();
+ m_Buffer.ReadAll(Dump);
+ m_Protocol->DataReceived(Dump.data(), Dump.size());
+ }
+ else
+ {
+ m_Protocol->DataReceived(a_Data, a_Size);
+ }
+}
+
+
+
+
+
+void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendAttachEntity(a_Entity, a_Vehicle);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendBlockBreakAnim(a_entityID, a_BlockX, a_BlockY, a_BlockZ, stage);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendChat(const AString & a_Message)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendChat(a_Message);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendCollectPickup(a_Pickup, a_Player);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendDestroyEntity(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendDisconnect(const AString & a_Reason)
+{
+ if (m_Protocol != NULL)
+ {
+ m_Protocol->SendDisconnect(a_Reason);
+ }
+ else
+ {
+ // This is used when the client sends a server-ping, respond with the default packet:
+ WriteByte ((char)0xff); // PACKET_DISCONNECT
+ WriteString(a_Reason);
+ }
+}
+
+
+
+
+void cProtocolRecognizer::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityHeadLook(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityHeadLook(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityLook(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityMetadata(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityProperties(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityStatus(a_Entity, a_Status);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityVelocity(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendExplosion(a_BlockX,a_BlockY,a_BlockZ,a_Radius, a_BlocksAffected, a_PlayerMotion);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendGameMode(a_GameMode);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendHealth(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendHealth();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendKeepAlive(int a_PingID)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendKeepAlive(a_PingID);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendLogin(a_Player, a_World);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPickupSpawn(a_Pickup);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerAbilities(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerAbilities();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerAnimation(a_Player, a_Animation);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerListItem(a_Player, a_IsOnline);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerMaxSpeed(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerMaxSpeed();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerMoveLook(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerMoveLook();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerPosition(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerPosition();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerSpawn(a_Player);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendRespawn(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendRespawn();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendExperience(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendExperience();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSpawnFallingBlock(a_FallingBlock);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSpawnMob(a_Mob);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendTabCompletionResults(const AStringVector & a_Results)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendTabCompletionResults(a_Results);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendTeleportEntity(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendWeather(eWeather a_Weather)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendWeather(a_Weather);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendWholeInventory(a_Window);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendWindowClose(a_Window);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendWindowOpen(const cWindow & a_Window)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendWindowOpen(a_Window);
+}
+
+
+
+
+
+AString cProtocolRecognizer::GetAuthServerID(void)
+{
+ ASSERT(m_Protocol != NULL);
+ return m_Protocol->GetAuthServerID();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendData(const char * a_Data, int a_Size)
+{
+ // This is used only when handling the server ping
+ m_Client->SendData(a_Data, a_Size);
+}
+
+
+
+
+
+bool cProtocolRecognizer::TryRecognizeProtocol(void)
+{
+ // NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and
+ // MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro
+
+ // The first packet should be a Handshake, 0x02:
+ unsigned char PacketType;
+ if (!m_Buffer.ReadByte(PacketType))
+ {
+ return false;
+ }
+ switch (PacketType)
+ {
+ case 0x02: return TryRecognizeLengthlessProtocol(); // Handshake, continue recognizing
+ case 0xfe:
+ {
+ // This may be either a packet length or the length-less Ping packet
+ Byte NextByte;
+ if (!m_Buffer.ReadByte(NextByte))
+ {
+ // Not enough data for either protocol
+ // This could actually happen with the 1.2 / 1.3 client, but their support is fading out anyway
+ return false;
+ }
+ if (NextByte != 0x01)
+ {
+ // This is definitely NOT a length-less Ping packet, handle as lengthed protocol:
+ break;
+ }
+ if (!m_Buffer.ReadByte(NextByte))
+ {
+ // There is no more data. Although this *could* mean TCP fragmentation, it is highly unlikely
+ // and rather this is a 1.4 client sending a regular Ping packet (without the following Plugin message)
+ SendLengthlessServerPing();
+ return false;
+ }
+ if (NextByte == 0xfa)
+ {
+ // Definitely a length-less Ping followed by a Plugin message
+ SendLengthlessServerPing();
+ return false;
+ }
+ // Definitely a lengthed Initial handshake, handle below:
+ break;
+ }
+ } // switch (PacketType)
+
+ // This must be a lengthed protocol, try if it has the entire initial handshake packet:
+ m_Buffer.ResetRead();
+ UInt32 PacketLen;
+ UInt32 ReadSoFar = m_Buffer.GetReadableSpace();
+ if (!m_Buffer.ReadVarInt(PacketLen))
+ {
+ // Not enough bytes for the packet length, keep waiting
+ return false;
+ }
+ ReadSoFar -= m_Buffer.GetReadableSpace();
+ if (!m_Buffer.CanReadBytes(PacketLen))
+ {
+ // Not enough bytes for the packet, keep waiting
+ return false;
+ }
+ return TryRecognizeLengthedProtocol(PacketLen - ReadSoFar);
+}
+
+
+
+
+
+bool cProtocolRecognizer::TryRecognizeLengthlessProtocol(void)
+{
+ // The comm started with 0x02, which is a Handshake packet in the length-less protocol family
+ // 1.3.2 starts with 0x02 0x39 <name-length-short>
+ // 1.2.5 starts with 0x02 <name-length-short> and name is expected to less than 0x3900 long :)
+ char ch;
+ if (!m_Buffer.ReadChar(ch))
+ {
+ return false;
+ }
+ switch (ch)
+ {
+ case PROTO_VERSION_1_3_2:
+ {
+ m_Protocol = new cProtocol132(m_Client);
+ return true;
+ }
+ case PROTO_VERSION_1_4_2:
+ case PROTO_VERSION_1_4_4:
+ {
+ m_Protocol = new cProtocol142(m_Client);
+ return true;
+ }
+ case PROTO_VERSION_1_4_6:
+ {
+ m_Protocol = new cProtocol146(m_Client);
+ return true;
+ }
+ case PROTO_VERSION_1_5_0:
+ case PROTO_VERSION_1_5_2:
+ {
+ m_Protocol = new cProtocol150(m_Client);
+ return true;
+ }
+ case PROTO_VERSION_1_6_1:
+ {
+ m_Protocol = new cProtocol161(m_Client);
+ return true;
+ }
+ case PROTO_VERSION_1_6_2:
+ case PROTO_VERSION_1_6_3:
+ case PROTO_VERSION_1_6_4:
+ {
+ m_Protocol = new cProtocol162(m_Client);
+ return true;
+ }
+ }
+ m_Protocol = new cProtocol125(m_Client);
+ return true;
+}
+
+
+
+
+
+bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining)
+{
+ UInt32 PacketType;
+ UInt32 NumBytesRead = m_Buffer.GetReadableSpace();
+ if (!m_Buffer.ReadVarInt(PacketType))
+ {
+ return false;
+ }
+ if (PacketType != 0x00)
+ {
+ // Not an initial handshake packet, we don't know how to talk to them
+ LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, initial packet %u)",
+ m_Client->GetIPString().c_str(), PacketType
+ );
+ m_Client->Kick("Unsupported protocol version");
+ return false;
+ }
+ UInt32 ProtocolVersion;
+ if (!m_Buffer.ReadVarInt(ProtocolVersion))
+ {
+ return false;
+ }
+ NumBytesRead -= m_Buffer.GetReadableSpace();
+ switch (ProtocolVersion)
+ {
+ case PROTO_VERSION_1_7_2:
+ {
+ AString ServerAddress;
+ short ServerPort;
+ UInt32 NextState;
+ m_Buffer.ReadVarUTF8String(ServerAddress);
+ m_Buffer.ReadBEShort(ServerPort);
+ m_Buffer.ReadVarInt(NextState);
+ m_Buffer.CommitRead();
+ m_Protocol = new cProtocol172(m_Client, ServerAddress, ServerPort, NextState);
+ return true;
+ }
+ }
+ LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u)",
+ m_Client->GetIPString().c_str(), ProtocolVersion
+ );
+ m_Client->Kick("Unsupported protocol version");
+ return false;
+}
+
+
+
+
+
+void cProtocolRecognizer::SendLengthlessServerPing(void)
+{
+ AString Reply;
+ switch (cRoot::Get()->GetPrimaryServerVersion())
+ {
+ case PROTO_VERSION_1_2_5:
+ case PROTO_VERSION_1_3_2:
+ {
+ // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29
+ Printf(Reply, "%s%s%i%s%i",
+ cRoot::Get()->GetServer()->GetDescription().c_str(),
+ cChatColor::Delimiter.c_str(),
+ cRoot::Get()->GetServer()->GetNumPlayers(),
+ cChatColor::Delimiter.c_str(),
+ cRoot::Get()->GetServer()->GetMaxPlayers()
+ );
+ break;
+ }
+
+ case PROTO_VERSION_1_4_2:
+ case PROTO_VERSION_1_4_4:
+ case PROTO_VERSION_1_4_6:
+ case PROTO_VERSION_1_5_0:
+ case PROTO_VERSION_1_5_2:
+ case PROTO_VERSION_1_6_1:
+ case PROTO_VERSION_1_6_2:
+ case PROTO_VERSION_1_6_3:
+ case PROTO_VERSION_1_6_4:
+ {
+ // The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff.
+ // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29
+ // _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit.
+ // Who cares? We're disconnecting anyway.
+ m_Buffer.ResetRead();
+ if (m_Buffer.CanReadBytes(2))
+ {
+ byte val;
+ m_Buffer.ReadByte(val); // Packet type - Serverlist ping
+ m_Buffer.ReadByte(val); // 0x01 magic value
+ ASSERT(val == 0x01);
+ }
+
+ // http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
+ AString NumPlayers;
+ Printf(NumPlayers, "%d", cRoot::Get()->GetServer()->GetNumPlayers());
+ AString MaxPlayers;
+ Printf(MaxPlayers, "%d", cRoot::Get()->GetServer()->GetMaxPlayers());
+
+ AString ProtocolVersionNum;
+ Printf(ProtocolVersionNum, "%d", cRoot::Get()->GetPrimaryServerVersion());
+ AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->GetPrimaryServerVersion()));
+
+ // Cannot use Printf() because of in-string NUL bytes.
+ Reply = cChatColor::Delimiter;
+ Reply.append("1");
+ Reply.push_back(0);
+ Reply.append(ProtocolVersionNum);
+ Reply.push_back(0);
+ Reply.append(ProtocolVersionTxt);
+ Reply.push_back(0);
+ Reply.append(cRoot::Get()->GetServer()->GetDescription());
+ Reply.push_back(0);
+ Reply.append(NumPlayers);
+ Reply.push_back(0);
+ Reply.append(MaxPlayers);
+ break;
+ }
+ } // switch (m_PrimaryServerVersion)
+ m_Client->Kick(Reply);
+}
+
+
+
+
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
new file mode 100644
index 000000000..03f48fb35
--- /dev/null
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -0,0 +1,153 @@
+
+// ProtocolRecognizer.h
+
+// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple
+// protocol versions and redirects everything to them
+
+
+
+
+
+#pragma once
+
+#include "Protocol.h"
+#include "../ByteBuffer.h"
+
+
+
+
+
+// Adjust these if a new protocol is added or an old one is removed:
+#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2"
+#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78, 4"
+
+
+
+
+
+class cProtocolRecognizer :
+ public cProtocol
+{
+ typedef cProtocol super;
+
+public:
+ enum
+ {
+ PROTO_VERSION_1_2_5 = 29,
+ PROTO_VERSION_1_3_2 = 39,
+ PROTO_VERSION_1_4_2 = 47,
+ PROTO_VERSION_1_4_4 = 49,
+ PROTO_VERSION_1_4_6 = 51,
+ PROTO_VERSION_1_5_0 = 60,
+ PROTO_VERSION_1_5_2 = 61,
+ PROTO_VERSION_1_6_1 = 73,
+ PROTO_VERSION_1_6_2 = 74,
+ PROTO_VERSION_1_6_3 = 77,
+ PROTO_VERSION_1_6_4 = 78,
+
+ PROTO_VERSION_NEXT,
+ PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion
+
+ // These will be kept "under" the next / latest, because the next and latest are only needed for previous protocols
+ PROTO_VERSION_1_7_2 = 4,
+ } ;
+
+ cProtocolRecognizer(cClientHandle * a_Client);
+ virtual ~cProtocolRecognizer();
+
+ /// Translates protocol version number into protocol version text: 49 -> "1.4.4"
+ static AString GetVersionTextFromInt(int a_ProtocolVersion);
+
+ /// Called when client sends some data:
+ virtual void DataReceived(const char * a_Data, int a_Size) override;
+
+ /// Sending stuff to clients (alphabetically sorted):
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
+ virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) override;
+ virtual void SendDisconnect (const AString & a_Reason) override;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
+ virtual void SendEntityLook (const cEntity & a_Entity) override;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) override;
+ virtual void SendEntityProperties (const cEntity & a_Entity) override;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) override;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
+ virtual void SendGameMode (eGameMode a_GameMode) override;
+ virtual void SendHealth (void) override;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendKeepAlive (int a_PingID) override;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendPlayerAbilities (void) override;
+ virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override;
+ virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
+ virtual void SendPlayerMaxSpeed (void) override;
+ virtual void SendPlayerMoveLook (void) override;
+ virtual void SendPlayerPosition (void) override;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendRespawn (void) override;
+ virtual void SendExperience (void) override;
+ virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override;
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
+ virtual void SendSpawnMob (const cMonster & a_Mob) override;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
+ virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) override;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
+ virtual void SendWeather (eWeather a_Weather) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+ virtual void SendWindowClose (const cWindow & a_Window) override;
+ virtual void SendWindowOpen (const cWindow & a_Window) override;
+ virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override;
+
+ virtual AString GetAuthServerID(void) override;
+
+ virtual void SendData(const char * a_Data, int a_Size) override;
+
+protected:
+ cProtocol * m_Protocol; //< The recognized protocol
+ cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol
+
+ /// Tries to recognize protocol based on m_Buffer contents; returns true if recognized
+ bool TryRecognizeProtocol(void);
+
+ /** Tries to recognize a protocol in the length-less family, based on m_Buffer; returns true if recognized.
+ Handles protocols before release 1.7, that didn't include packet lengths, and started with a 0x02 handshake packet
+ Note that length-less server ping is handled directly in TryRecognizeProtocol(), this function is called only
+ when the 0x02 Handshake packet has been received
+ */
+ bool TryRecognizeLengthlessProtocol(void);
+
+ /** Tries to recognize a protocol in the leghted family (1.7+), based on m_Buffer; returns true if recognized.
+ The packet length and type have already been read, type is 0
+ The number of bytes remaining in the packet is passed as a_PacketLengthRemaining
+ **/
+ bool TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining);
+
+ /** Called when the recognizer gets a length-less protocol's server ping packet
+ Responds with server stats and destroys the client.
+ */
+ void SendLengthlessServerPing(void);
+} ;
+
+
+
+
+
diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp
new file mode 100644
index 000000000..72f2d9ba9
--- /dev/null
+++ b/src/RCONServer.cpp
@@ -0,0 +1,333 @@
+
+// RCONServer.cpp
+
+// Implements the cRCONServer class representing the RCON server
+
+#include "Globals.h"
+#include "inifile/iniFile.h"
+#include "RCONServer.h"
+#include "Server.h"
+#include "Root.h"
+#include "CommandOutput.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+
+
+
+
+enum
+{
+ // Client -> Server:
+ RCON_PACKET_COMMAND = 2,
+ RCON_PACKET_LOGIN = 3,
+
+ // Server -> Client:
+ RCON_PACKET_RESPONSE = 2,
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cRCONCommandOutput:
+
+class cRCONCommandOutput :
+ public cCommandOutputCallback
+{
+public:
+ cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) :
+ m_Connection(a_Connection),
+ m_RequestID(a_RequestID)
+ {
+ }
+
+ // cCommandOutputCallback overrides:
+ virtual void Out(const AString & a_Text) override
+ {
+ m_Buffer.append(a_Text);
+ }
+
+ virtual void Finished(void) override
+ {
+ m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, m_Buffer.size(), m_Buffer.c_str());
+ delete this;
+ }
+
+protected:
+ cRCONServer::cConnection & m_Connection;
+ int m_RequestID;
+ AString m_Buffer;
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cRCONServer:
+
+cRCONServer::cRCONServer(cServer & a_Server) :
+ m_Server(a_Server),
+ m_ListenThread4(*this, cSocket::IPv4, "RCON IPv4"),
+ m_ListenThread6(*this, cSocket::IPv6, "RCON IPv6")
+{
+}
+
+
+
+
+
+cRCONServer::~cRCONServer()
+{
+ m_ListenThread4.Stop();
+ m_ListenThread6.Stop();
+}
+
+
+
+
+
+void cRCONServer::Initialize(cIniFile & a_IniFile)
+{
+ if (!a_IniFile.GetValueSetB("RCON", "Enabled", false))
+ {
+ return;
+ }
+
+ // Read the password, don't allow an empty one:
+ m_Password = a_IniFile.GetValueSet("RCON", "Password", "");
+ if (m_Password.empty())
+ {
+ LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled.");
+ return;
+ }
+
+ // Read and initialize both IPv4 and IPv6 ports for RCON
+ bool HasAnyPorts = false;
+ AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575");
+ if (m_ListenThread4.Initialize(Ports4))
+ {
+ HasAnyPorts = true;
+ m_ListenThread4.Start();
+ }
+ AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575");
+ if (m_ListenThread6.Initialize(Ports6))
+ {
+ HasAnyPorts = true;
+ m_ListenThread6.Start();
+ }
+ if (!HasAnyPorts)
+ {
+ LOGWARNING("RCON is requested, but no ports are specified. Specify at least one port in PortsIPv4 or PortsIPv6. RCON is now disabled.");
+ return;
+ }
+}
+
+
+
+
+
+void cRCONServer::OnConnectionAccepted(cSocket & a_Socket)
+{
+ if (!a_Socket.IsValid())
+ {
+ return;
+ }
+
+ LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str());
+
+ // Create a new cConnection object, it will be deleted when the connection is closed
+ m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cRCONServer::cConnection:
+
+cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket) :
+ m_IsAuthenticated(false),
+ m_RCONServer(a_RCONServer),
+ m_Socket(a_Socket),
+ m_IPAddress(a_Socket.GetIPString())
+{
+}
+
+
+
+
+
+void cRCONServer::cConnection::DataReceived(const char * a_Data, int a_Size)
+{
+ // Append data to the buffer:
+ m_Buffer.append(a_Data, a_Size);
+
+ // Process the packets in the buffer:
+ while (m_Buffer.size() >= 14)
+ {
+ int Length = IntFromBuffer(m_Buffer.data());
+ if (Length > 1500)
+ {
+ // Too long, drop the connection
+ LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.",
+ Length, m_IPAddress.c_str()
+ );
+ m_RCONServer.m_SocketThreads.RemoveClient(this);
+ m_Socket.CloseSocket();
+ delete this;
+ return;
+ }
+ if (Length > (int)(m_Buffer.size() + 4))
+ {
+ // Incomplete packet yet, wait for more data to come
+ return;
+ }
+
+ int RequestID = IntFromBuffer(m_Buffer.data() + 4);
+ int PacketType = IntFromBuffer(m_Buffer.data() + 8);
+ if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12))
+ {
+ m_RCONServer.m_SocketThreads.RemoveClient(this);
+ m_Socket.CloseSocket();
+ delete this;
+ return;
+ }
+ m_Buffer.erase(0, Length + 4);
+ } // while (m_Buffer.size() >= 14)
+}
+
+
+
+
+
+void cRCONServer::cConnection::GetOutgoingData(AString & a_Data)
+{
+ a_Data.assign(m_Outgoing);
+ m_Outgoing.clear();
+}
+
+
+
+
+
+void cRCONServer::cConnection::SocketClosed(void)
+{
+ m_RCONServer.m_SocketThreads.RemoveClient(this);
+ delete this;
+}
+
+
+
+
+
+bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload)
+{
+ switch (a_PacketType)
+ {
+ case RCON_PACKET_LOGIN:
+ {
+ if (strncmp(a_Payload, m_RCONServer.m_Password.c_str(), a_PayloadLength) != 0)
+ {
+ LOGINFO("RCON: Invalid password from client %s, dropping connection.", m_IPAddress.c_str());
+ SendResponse(-1, RCON_PACKET_RESPONSE, 0, NULL);
+ return false;
+ }
+ m_IsAuthenticated = true;
+
+ LOGD("RCON: Client at %s has successfully authenticated", m_IPAddress.c_str());
+
+ // Send OK response:
+ SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL);
+ return true;
+ }
+
+ case RCON_PACKET_COMMAND:
+ {
+ if (!m_IsAuthenticated)
+ {
+ char AuthNeeded[] = "You need to authenticate first!";
+ SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded);
+ return false;
+ }
+
+ AString cmd(a_Payload, a_PayloadLength);
+ LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str());
+ cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID)));
+
+ // Send an empty response:
+ SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL);
+ return true;
+ }
+ }
+
+ // Unknown packet type, drop the connection:
+ LOGWARNING("RCON: Client at %s has sent an unknown packet type %d, dropping connection.",
+ m_IPAddress.c_str(), a_PacketType
+ );
+ return false;
+}
+
+
+
+
+
+/// Reads 4 bytes from a_Buffer and returns the int they represent
+int cRCONServer::cConnection::IntFromBuffer(const char * a_Buffer)
+{
+ return ((unsigned char)a_Buffer[3] << 24) | ((unsigned char)a_Buffer[2] << 16) | ((unsigned char)a_Buffer[1] << 8) | (unsigned char)a_Buffer[0];
+}
+
+
+
+
+
+/// Puts 4 bytes representing the int into the buffer
+void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer)
+{
+ a_Buffer[0] = a_Value & 0xff;
+ a_Buffer[1] = (a_Value >> 8) & 0xff;
+ a_Buffer[2] = (a_Value >> 16) & 0xff;
+ a_Buffer[3] = (a_Value >> 24) & 0xff;
+}
+
+
+
+
+
+/// Sends a RCON packet back to the client
+void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload)
+{
+ ASSERT((a_PayloadLength == 0) || (a_Payload != NULL)); // Either zero data to send, or a valid payload ptr
+
+ char Buffer[4];
+ int Length = a_PayloadLength + 10;
+ IntToBuffer(Length, Buffer);
+ m_Outgoing.append(Buffer, 4);
+ IntToBuffer(a_RequestID, Buffer);
+ m_Outgoing.append(Buffer, 4);
+ IntToBuffer(a_PacketType, Buffer);
+ m_Outgoing.append(Buffer, 4);
+ if (a_PayloadLength > 0)
+ {
+ m_Outgoing.append(a_Payload, a_PayloadLength);
+ }
+ m_Outgoing.push_back(0);
+ m_Outgoing.push_back(0);
+ m_RCONServer.m_SocketThreads.NotifyWrite(this);
+}
+
+
+
+
diff --git a/src/RCONServer.h b/src/RCONServer.h
new file mode 100644
index 000000000..0e89800a2
--- /dev/null
+++ b/src/RCONServer.h
@@ -0,0 +1,109 @@
+
+// RCONServer.h
+
+// Declares the cRCONServer class representing the RCON server
+
+
+
+
+
+#pragma once
+
+#include "OSSupport/SocketThreads.h"
+#include "OSSupport/ListenThread.h"
+
+
+
+
+
+// fwd:
+class cServer;
+class cIniFile;
+
+
+
+
+
+class cRCONServer :
+ public cListenThread::cCallback
+{
+public:
+ cRCONServer(cServer & a_Server);
+ ~cRCONServer();
+
+ void Initialize(cIniFile & a_IniFile);
+
+protected:
+ friend class cRCONCommandOutput;
+
+ class cConnection :
+ public cSocketThreads::cCallback
+ {
+ public:
+ cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket);
+
+ protected:
+ friend class cRCONCommandOutput;
+
+ /// Set to true if the client has successfully authenticated
+ bool m_IsAuthenticated;
+
+ /// Buffer for the incoming data
+ AString m_Buffer;
+
+ /// Buffer for the outgoing data
+ AString m_Outgoing;
+
+ /// Server that owns this connection and processes requests
+ cRCONServer & m_RCONServer;
+
+ /// The socket belonging to the client
+ cSocket & m_Socket;
+
+ /// Address of the client
+ AString m_IPAddress;
+
+
+ // cSocketThreads::cCallback overrides:
+ virtual void DataReceived(const char * a_Data, int a_Size) override;
+ virtual void GetOutgoingData(AString & a_Data) override;
+ virtual void SocketClosed(void) override;
+
+ /// Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped
+ bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload);
+
+ /// Reads 4 bytes from a_Buffer and returns the int they represent
+ int IntFromBuffer(const char * a_Buffer);
+
+ /// Puts 4 bytes representing the int into the buffer
+ void IntToBuffer(int a_Value, char * a_Buffer);
+
+ /// Sends a RCON packet back to the client
+ void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload);
+ } ;
+
+
+ /// The server object that will process the commands received
+ cServer & m_Server;
+
+ /// The thread(s) that take care of all the traffic on the RCON ports
+ cSocketThreads m_SocketThreads;
+
+ /// The thread for accepting IPv4 RCON connections
+ cListenThread m_ListenThread4;
+
+ /// The thread for accepting IPv6 RCON connections
+ cListenThread m_ListenThread6;
+
+ /// Password for authentication
+ AString m_Password;
+
+
+ // cListenThread::cCallback overrides:
+ virtual void OnConnectionAccepted(cSocket & a_Socket) override;
+} ;
+
+
+
+
+
diff --git a/src/ReferenceManager.cpp b/src/ReferenceManager.cpp
new file mode 100644
index 000000000..6a9ed0e43
--- /dev/null
+++ b/src/ReferenceManager.cpp
@@ -0,0 +1,43 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "ReferenceManager.h"
+#include "Entities/Entity.h"
+
+
+
+
+
+cReferenceManager::cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type )
+ : m_Type( a_Type )
+{
+}
+
+cReferenceManager::~cReferenceManager()
+{
+ if( m_Type == RFMNGR_REFERENCERS )
+ {
+ for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr )
+ {
+ *(*itr) = 0; // Set referenced pointer to 0
+ }
+ }
+ else
+ {
+ for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr )
+ {
+ cEntity* Ptr = (*(*itr));
+ if( Ptr ) Ptr->Dereference( *(*itr) );
+ }
+ }
+}
+
+void cReferenceManager::AddReference( cEntity*& a_EntityPtr )
+{
+ m_References.push_back( &a_EntityPtr );
+}
+
+void cReferenceManager::Dereference( cEntity*& a_EntityPtr )
+{
+ m_References.remove( &a_EntityPtr );
+} \ No newline at end of file
diff --git a/src/ReferenceManager.h b/src/ReferenceManager.h
new file mode 100644
index 000000000..bcd451f72
--- /dev/null
+++ b/src/ReferenceManager.h
@@ -0,0 +1,34 @@
+
+#pragma once
+
+
+
+
+
+class cEntity;
+
+
+
+
+
+class cReferenceManager
+{
+public:
+ enum ENUM_REFERENCE_MANAGER_TYPE
+ {
+ RFMNGR_REFERENCERS,
+ RFMNGR_REFERENCES,
+ };
+ cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type );
+ ~cReferenceManager();
+
+ void AddReference( cEntity*& a_EntityPtr );
+ void Dereference( cEntity*& a_EntityPtr );
+private:
+ ENUM_REFERENCE_MANAGER_TYPE m_Type;
+ std::list< cEntity** > m_References;
+};
+
+
+
+
diff --git a/src/Root.cpp b/src/Root.cpp
new file mode 100644
index 000000000..282227525
--- /dev/null
+++ b/src/Root.cpp
@@ -0,0 +1,754 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Root.h"
+#include "Server.h"
+#include "World.h"
+#include "WebAdmin.h"
+#include "FurnaceRecipe.h"
+#include "GroupManager.h"
+#include "CraftingRecipes.h"
+#include "PluginManager.h"
+#include "MonsterConfig.h"
+#include "Entities/Player.h"
+#include "Blocks/BlockHandler.h"
+#include "Items/ItemHandler.h"
+#include "Chunk.h"
+#include "Protocol/ProtocolRecognizer.h" // for protocol version constants
+#include "CommandOutput.h"
+#include "DeadlockDetect.h"
+#include "OSSupport/Timer.h"
+
+#include "lib/iniFile/iniFile.h"
+
+#ifdef _WIN32
+ #include <psapi.h>
+#elif defined(__linux__)
+ #include <fstream>
+#elif defined(__APPLE__)
+ #include <mach/mach.h>
+#endif
+
+
+
+
+
+cRoot* cRoot::s_Root = NULL;
+
+
+
+
+
+cRoot::cRoot()
+ : m_Server( NULL )
+ , m_MonsterConfig( NULL )
+ , m_GroupManager( NULL )
+ , m_CraftingRecipes(NULL)
+ , m_FurnaceRecipe( NULL )
+ , m_WebAdmin( NULL )
+ , m_PluginManager( NULL )
+ , m_Log( NULL )
+ , m_bStop( false )
+ , m_bRestart( false )
+ , m_InputThread( NULL )
+ , m_pDefaultWorld( NULL )
+{
+ s_Root = this;
+}
+
+
+
+
+
+cRoot::~cRoot()
+{
+ s_Root = 0;
+}
+
+
+
+
+
+void cRoot::InputThread(void * a_Params)
+{
+ cRoot & self = *(cRoot*)a_Params;
+
+ cLogCommandOutputCallback Output;
+
+ while (!(self.m_bStop || self.m_bRestart) && std::cin.good())
+ {
+ std::string Command;
+ std::getline(std::cin, Command);
+ if (!Command.empty())
+ {
+ self.ExecuteConsoleCommand(Command, Output);
+ }
+ }
+
+ if (!(self.m_bStop || self.m_bRestart))
+ {
+ // We have come here because the std::cin has received an EOF and the server is still running; stop the server:
+ self.m_bStop = true;
+ }
+}
+
+
+
+
+
+void cRoot::Start(void)
+{
+ cDeadlockDetect dd;
+ delete m_Log;
+ m_Log = new cMCLogger();
+
+ m_bStop = false;
+ while (!m_bStop)
+ {
+ cTimer Time;
+ long long mseconds = Time.GetNowTime();
+
+ m_bRestart = false;
+
+ LoadGlobalSettings();
+
+ LOG("Creating new server instance...");
+ m_Server = new cServer();
+
+ LOG("Reading server config...");
+ cIniFile IniFile;
+ if (!IniFile.ReadFile("settings.ini"))
+ {
+ LOGWARN("Regenerating settings.ini, all settings will be reset");
+ IniFile.AddHeaderComment(" This is the main server configuration");
+ IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
+ IniFile.AddHeaderComment(" See: http://www.mc-server.org/wiki/doku.php?id=configure:settings.ini for further configuration help");
+ }
+
+ m_PrimaryServerVersion = IniFile.GetValueI("Server", "PrimaryServerVersion", 0);
+ if (m_PrimaryServerVersion == 0)
+ {
+ m_PrimaryServerVersion = cProtocolRecognizer::PROTO_VERSION_LATEST;
+ }
+ else
+ {
+ // Make a note in the log that the primary server version is explicitly set in the ini file
+ LOGINFO("Primary server version set explicitly to %d.", m_PrimaryServerVersion);
+ }
+
+ LOG("Starting server...");
+ if (!m_Server->InitServer(IniFile))
+ {
+ LOGERROR("Failure starting server, aborting...");
+ return;
+ }
+
+ m_WebAdmin = new cWebAdmin();
+ m_WebAdmin->Init();
+
+ LOGD("Loading settings...");
+ m_GroupManager = new cGroupManager();
+ m_CraftingRecipes = new cCraftingRecipes;
+ m_FurnaceRecipe = new cFurnaceRecipe();
+
+ LOGD("Loading worlds...");
+ LoadWorlds(IniFile);
+
+ LOGD("Loading plugin manager...");
+ m_PluginManager = new cPluginManager();
+ m_PluginManager->ReloadPluginsNow(IniFile);
+
+ LOGD("Loading MonsterConfig...");
+ m_MonsterConfig = new cMonsterConfig;
+
+ // This sets stuff in motion
+ LOGD("Starting Authenticator...");
+ m_Authenticator.Start(IniFile);
+
+ IniFile.WriteFile("settings.ini");
+
+ LOGD("Starting worlds...");
+ StartWorlds();
+
+ LOGD("Starting deadlock detector...");
+ dd.Start();
+
+ LOGD("Finalising startup...");
+ m_Server->Start();
+
+ m_WebAdmin->Start();
+
+ #if !defined(ANDROID_NDK)
+ LOGD("Starting InputThread...");
+ m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" );
+ m_InputThread->Start( false ); // We should NOT wait? Otherwise we can´t stop the server from other threads than the input thread
+ #endif
+
+ long long finishmseconds = Time.GetNowTime();
+ finishmseconds -= mseconds;
+
+ LOG("Startup complete, took %i ms!", finishmseconds);
+
+ while (!m_bStop && !m_bRestart) // These are modified by external threads
+ {
+ cSleep::MilliSleep(1000);
+ }
+
+ #if !defined(ANDROID_NDK)
+ delete m_InputThread; m_InputThread = NULL;
+ #endif
+
+ // Deallocate stuffs
+ LOG("Shutting down server...");
+ m_Server->Shutdown();
+
+ 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 = NULL;
+ delete m_WebAdmin; m_WebAdmin = NULL;
+ LOGD("Unloading recipes...");
+ delete m_FurnaceRecipe; m_FurnaceRecipe = NULL;
+ delete m_CraftingRecipes; m_CraftingRecipes = NULL;
+ LOGD("Forgetting groups...");
+ delete m_GroupManager; m_GroupManager = 0;
+ LOGD("Unloading worlds...");
+ UnloadWorlds();
+
+ LOGD("Stopping plugin manager...");
+ delete m_PluginManager; m_PluginManager = NULL;
+
+ cItemHandler::Deinit();
+ cBlockHandler::Deinit();
+
+ LOG("Cleaning up...");
+ //delete HeartBeat; HeartBeat = 0;
+ delete m_Server; m_Server = 0;
+ LOG("Shutdown successful!");
+ }
+
+ delete m_Log; m_Log = 0;
+}
+
+
+
+
+
+void cRoot::LoadGlobalSettings()
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+void cRoot::LoadWorlds(cIniFile & IniFile)
+{
+ // First get the default world
+ AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world");
+ m_pDefaultWorld = new cWorld( DefaultWorldName.c_str() );
+ m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld;
+
+ // Then load the other worlds
+ unsigned int KeyNum = IniFile.FindKey("Worlds");
+ unsigned int NumWorlds = IniFile.GetNumValues( KeyNum );
+ if (NumWorlds <= 0)
+ {
+ return;
+ }
+
+ bool FoundAdditionalWorlds = false;
+ for (unsigned int i = 0; i < NumWorlds; i++)
+ {
+ AString ValueName = IniFile.GetValueName(KeyNum, i );
+ if (ValueName.compare("World") != 0)
+ {
+ continue;
+ }
+ AString WorldName = IniFile.GetValue(KeyNum, i );
+ if (WorldName.empty())
+ {
+ continue;
+ }
+ FoundAdditionalWorlds = true;
+ cWorld* NewWorld = new cWorld( WorldName.c_str() );
+ m_WorldsByName[ WorldName ] = NewWorld;
+ } // for i - Worlds
+
+ if (!FoundAdditionalWorlds)
+ {
+ if (IniFile.GetKeyComment("Worlds", 0) != " World=secondworld")
+ {
+ IniFile.AddKeyComment("Worlds", " World=secondworld");
+ }
+ }
+}
+
+
+
+
+
+void cRoot::StartWorlds(void)
+{
+ for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
+ {
+ itr->second->Start();
+ itr->second->InitializeSpawn();
+ }
+}
+
+
+
+
+
+void cRoot::StopWorlds(void)
+{
+ for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
+ {
+ itr->second->Stop();
+ }
+}
+
+
+
+
+
+void cRoot::UnloadWorlds(void)
+{
+ m_pDefaultWorld = NULL;
+ for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr )
+ {
+ delete itr->second;
+ }
+ m_WorldsByName.clear();
+}
+
+
+
+
+
+cWorld* cRoot::GetDefaultWorld()
+{
+ return m_pDefaultWorld;
+}
+
+
+
+
+
+cWorld* cRoot::GetWorld( const AString & a_WorldName )
+{
+ WorldMap::iterator itr = m_WorldsByName.find( a_WorldName );
+ if( itr != m_WorldsByName.end() )
+ return itr->second;
+ return 0;
+}
+
+
+
+
+
+bool cRoot::ForEachWorld(cWorldListCallback & a_Callback)
+{
+ for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2)
+ {
+ ++itr2;
+ if (a_Callback.Item(itr->second))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+
+
+void cRoot::TickCommands(void)
+{
+ // Execute any pending commands:
+ cCommandQueue PendingCommands;
+ {
+ cCSLock Lock(m_CSPendingCommands);
+ std::swap(PendingCommands, m_PendingCommands);
+ }
+ for (cCommandQueue::iterator itr = PendingCommands.begin(), end = PendingCommands.end(); itr != end; ++itr)
+ {
+ ExecuteConsoleCommand(itr->m_Command, *(itr->m_Output));
+ }
+}
+
+
+
+
+
+void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
+{
+ // Some commands are built-in:
+ if (a_Cmd == "stop")
+ {
+ m_bStop = true;
+ }
+ else if (a_Cmd == "restart")
+ {
+ m_bRestart = true;
+ }
+
+ // Put the command into a queue (Alleviates FS #363):
+ cCSLock Lock(m_CSPendingCommands);
+ m_PendingCommands.push_back(cCommand(a_Cmd, &a_Output));
+}
+
+
+
+
+
+void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
+{
+ // Some commands are built-in:
+ if (a_Cmd == "stop")
+ {
+ m_bStop = true;
+ }
+ else if (a_Cmd == "restart")
+ {
+ m_bRestart = true;
+ }
+
+ // Put the command into a queue (Alleviates FS #363):
+ cCSLock Lock(m_CSPendingCommands);
+ m_PendingCommands.push_back(cCommand(a_Cmd, new cLogCommandDeleteSelfOutputCallback));
+}
+
+
+
+
+
+void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
+{
+ // Some commands are built-in:
+ if (a_Cmd == "stop")
+ {
+ m_bStop = true;
+ }
+ else if (a_Cmd == "restart")
+ {
+ m_bRestart = true;
+ }
+
+ LOG("Executing console command: \"%s\"", a_Cmd.c_str());
+ m_Server->ExecuteConsoleCommand(a_Cmd, a_Output);
+}
+
+
+
+
+
+void cRoot::KickUser(int a_ClientID, const AString & a_Reason)
+{
+ m_Server->KickUser(a_ClientID, a_Reason);
+}
+
+
+
+
+
+void cRoot::AuthenticateUser(int a_ClientID)
+{
+ m_Server->AuthenticateUser(a_ClientID);
+}
+
+
+
+
+
+int cRoot::GetTotalChunkCount(void)
+{
+ int res = 0;
+ for ( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr )
+ {
+ res += itr->second->GetNumChunks();
+ }
+ return res;
+}
+
+
+
+
+
+void cRoot::SaveAllChunks(void)
+{
+ for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
+ {
+ itr->second->QueueSaveAllChunks();
+ }
+}
+
+
+
+
+
+void cRoot::BroadcastChat(const AString & a_Message)
+{
+ for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr)
+ {
+ itr->second->BroadcastChat(a_Message);
+ } // for itr - m_WorldsByName[]
+}
+
+
+
+
+
+bool cRoot::ForEachPlayer(cPlayerListCallback & a_Callback)
+{
+ for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2)
+ {
+ ++itr2;
+ if (!itr->second->ForEachPlayer(a_Callback))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+
+
+bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback)
+{
+ class cCallback : public cPlayerListCallback
+ {
+ unsigned int BestRating;
+ unsigned int NameLength;
+ const AString PlayerName;
+
+ cPlayerListCallback & m_Callback;
+ virtual bool Item (cPlayer * a_pPlayer)
+ {
+ unsigned int Rating = RateCompareString (PlayerName, a_pPlayer->GetName());
+ if (Rating > 0 && Rating >= BestRating)
+ {
+ BestMatch = a_pPlayer;
+ if( Rating > BestRating ) NumMatches = 0;
+ BestRating = Rating;
+ ++NumMatches;
+ }
+ if (Rating == NameLength) // Perfect match
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public:
+ cCallback (const AString & a_PlayerName, cPlayerListCallback & a_Callback)
+ : m_Callback( a_Callback )
+ , BestMatch( NULL )
+ , BestRating( 0 )
+ , NumMatches( 0 )
+ , NameLength( a_PlayerName.length() )
+ , PlayerName( a_PlayerName )
+ {}
+
+ cPlayer * BestMatch;
+ unsigned int NumMatches;
+ } Callback (a_PlayerName, a_Callback);
+ ForEachPlayer( Callback );
+
+ if (Callback.NumMatches == 1)
+ {
+ return a_Callback.Item (Callback.BestMatch);
+ }
+ return false;
+}
+
+
+
+
+
+AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion)
+{
+ return cProtocolRecognizer::GetVersionTextFromInt(a_ProtocolVersion);
+}
+
+
+
+
+
+int cRoot::GetVirtualRAMUsage(void)
+{
+ #ifdef _WIN32
+ PROCESS_MEMORY_COUNTERS_EX pmc;
+ if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS *)&pmc, sizeof(pmc)))
+ {
+ return (int)(pmc.PrivateUsage / 1024);
+ }
+ return -1;
+ #elif defined(__linux__)
+ // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
+ std::ifstream StatFile("/proc/self/status");
+ if (!StatFile.good())
+ {
+ return -1;
+ }
+ while (StatFile.good())
+ {
+ AString Line;
+ std::getline(StatFile, Line);
+ if (strncmp(Line.c_str(), "VmSize:", 7) == 0)
+ {
+ int res = atoi(Line.c_str() + 8);
+ return (res == 0) ? -1 : res; // If parsing failed, return -1
+ }
+ }
+ return -1;
+ #elif defined (__APPLE__)
+ // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
+ struct task_basic_info t_info;
+ mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
+
+ if (KERN_SUCCESS == task_info(
+ mach_task_self(),
+ TASK_BASIC_INFO,
+ (task_info_t)&t_info,
+ &t_info_count
+ ))
+ {
+ return (int)(t_info.virtual_size / 1024);
+ }
+ return -1;
+ #else
+ LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__);
+ return -1;
+ #endif
+}
+
+
+
+
+
+int cRoot::GetPhysicalRAMUsage(void)
+{
+ #ifdef _WIN32
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
+ {
+ return (int)(pmc.WorkingSetSize / 1024);
+ }
+ return -1;
+ #elif defined(__linux__)
+ // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
+ std::ifstream StatFile("/proc/self/status");
+ if (!StatFile.good())
+ {
+ return -1;
+ }
+ while (StatFile.good())
+ {
+ AString Line;
+ std::getline(StatFile, Line);
+ if (strncmp(Line.c_str(), "VmRSS:", 7) == 0)
+ {
+ int res = atoi(Line.c_str() + 8);
+ return (res == 0) ? -1 : res; // If parsing failed, return -1
+ }
+ }
+ return -1;
+ #elif defined (__APPLE__)
+ // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process
+ struct task_basic_info t_info;
+ mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
+
+ if (KERN_SUCCESS == task_info(
+ mach_task_self(),
+ TASK_BASIC_INFO,
+ (task_info_t)&t_info,
+ &t_info_count
+ ))
+ {
+ return (int)(t_info.resident_size / 1024);
+ }
+ return -1;
+ #else
+ LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__);
+ return -1;
+ #endif
+}
+
+
+
+
+
+void cRoot::LogChunkStats(cCommandOutputCallback & a_Output)
+{
+ int SumNumValid = 0;
+ int SumNumDirty = 0;
+ int SumNumInLighting = 0;
+ int SumNumInGenerator = 0;
+ int SumMem = 0;
+ for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr)
+ {
+ cWorld * World = itr->second;
+ int NumInGenerator = World->GetGeneratorQueueLength();
+ int NumInSaveQueue = World->GetStorageSaveQueueLength();
+ int NumInLoadQueue = World->GetStorageLoadQueueLength();
+ int NumValid = 0;
+ int NumDirty = 0;
+ int NumInLighting = 0;
+ World->GetChunkStats(NumValid, NumDirty, NumInLighting);
+ a_Output.Out("World %s:", World->GetName().c_str());
+ a_Output.Out(" Num loaded chunks: %d", NumValid);
+ a_Output.Out(" Num dirty chunks: %d", NumDirty);
+ a_Output.Out(" Num chunks in lighting queue: %d", NumInLighting);
+ a_Output.Out(" Num chunks in generator queue: %d", NumInGenerator);
+ a_Output.Out(" Num chunks in storage load queue: %d", NumInLoadQueue);
+ a_Output.Out(" Num chunks in storage save queue: %d", NumInSaveQueue);
+ int Mem = NumValid * sizeof(cChunk);
+ a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024));
+ a_Output.Out(" Per-chunk memory size breakdown:");
+ a_Output.Out(" block types: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockTypes), (sizeof(cChunkDef::BlockTypes) + 1023) / 1024);
+ a_Output.Out(" block metadata: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockNibbles), (sizeof(cChunkDef::BlockNibbles) + 1023) / 1024);
+ a_Output.Out(" block lighting: %6d bytes (%3d KiB)", 2 * sizeof(cChunkDef::BlockNibbles), (2 * sizeof(cChunkDef::BlockNibbles) + 1023) / 1024);
+ a_Output.Out(" heightmap: %6d bytes (%3d KiB)", sizeof(cChunkDef::HeightMap), (sizeof(cChunkDef::HeightMap) + 1023) / 1024);
+ a_Output.Out(" biomemap: %6d bytes (%3d KiB)", sizeof(cChunkDef::BiomeMap), (sizeof(cChunkDef::BiomeMap) + 1023) / 1024);
+ int Rest = sizeof(cChunk) - sizeof(cChunkDef::BlockTypes) - 3 * sizeof(cChunkDef::BlockNibbles) - sizeof(cChunkDef::HeightMap) - sizeof(cChunkDef::BiomeMap);
+ a_Output.Out(" other: %6d bytes (%3d KiB)", Rest, (Rest + 1023) / 1024);
+ SumNumValid += NumValid;
+ SumNumDirty += NumDirty;
+ SumNumInLighting += NumInLighting;
+ SumNumInGenerator += NumInGenerator;
+ SumMem += Mem;
+ }
+ a_Output.Out("Totals:");
+ a_Output.Out(" Num loaded chunks: %d", SumNumValid);
+ a_Output.Out(" Num dirty chunks: %d", SumNumDirty);
+ a_Output.Out(" Num chunks in lighting queue: %d", SumNumInLighting);
+ a_Output.Out(" Num chunks in generator queue: %d", SumNumInGenerator);
+ a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024));
+}
+
+
+
+
+
+int cRoot::GetFurnaceFuelBurnTime(const cItem & a_Fuel)
+{
+ cFurnaceRecipe * FR = Get()->GetFurnaceRecipe();
+ return FR->GetBurnTime(a_Fuel);
+}
+
+
+
+
diff --git a/src/Root.h b/src/Root.h
new file mode 100644
index 000000000..4e38dd17f
--- /dev/null
+++ b/src/Root.h
@@ -0,0 +1,190 @@
+
+#pragma once
+
+#include "Authenticator.h"
+#include "HTTPServer/HTTPServer.h"
+
+
+
+
+
+// fwd:
+class cThread;
+class cMonsterConfig;
+class cGroupManager;
+class cCraftingRecipes;
+class cFurnaceRecipe;
+class cWebAdmin;
+class cPluginManager;
+class cServer;
+class cWorld;
+class cPlayer;
+class cCommandOutputCallback ;
+
+typedef cItemCallback<cPlayer> cPlayerListCallback;
+typedef cItemCallback<cWorld> cWorldListCallback;
+
+
+
+
+
+/// The root of the object hierarchy
+class cRoot // tolua_export
+{ // tolua_export
+public:
+ static cRoot * Get() { return s_Root; } // tolua_export
+
+ cRoot(void);
+ ~cRoot();
+
+ void Start(void);
+
+ cServer * GetServer(void) { return m_Server; } // tolua_export
+ cWorld * GetDefaultWorld(void); // tolua_export
+ cWorld * GetWorld(const AString & a_WorldName); // tolua_export
+
+ /// Calls the callback for each world; returns true if the callback didn't abort (return true)
+ bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings <<
+
+ /// Writes chunkstats, for each world and totals, to the output callback
+ void LogChunkStats(cCommandOutputCallback & a_Output);
+
+ int GetPrimaryServerVersion(void) const { return m_PrimaryServerVersion; } // tolua_export
+ void SetPrimaryServerVersion(int a_Version) { m_PrimaryServerVersion = a_Version; } // tolua_export
+
+ cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; }
+
+ cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export
+ cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export
+ cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature
+
+ /// Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel
+ static int GetFurnaceFuelBurnTime(const cItem & a_Fuel); // tolua_export
+
+ cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export
+ cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
+ cAuthenticator & GetAuthenticator (void) { return m_Authenticator; }
+
+ /** Queues a console command for execution through the cServer class.
+ The command will be executed in the tick thread
+ The command's output will be written to the a_Output callback
+ "stop" and "restart" commands have special handling.
+ */
+ void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
+
+ /** Queues a console command for execution through the cServer class.
+ The command will be executed in the tick thread
+ The command's output will be sent to console
+ "stop" and "restart" commands have special handling.
+ */
+ void QueueExecuteConsoleCommand(const AString & a_Cmd); // tolua_export
+
+ /// Executes a console command through the cServer class; does special handling for "stop" and "restart".
+ void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
+
+ /// Kicks the user, no matter in what world they are. Used from cAuthenticator
+ void KickUser(int a_ClientID, const AString & a_Reason);
+
+ /// Called by cAuthenticator to auth the specified user
+ void AuthenticateUser(int a_ClientID);
+
+ /// Executes commands queued in the command queue
+ void TickCommands(void);
+
+ /// Returns the number of chunks loaded
+ int GetTotalChunkCount(void); // tolua_export
+
+ /// Saves all chunks in all worlds
+ void SaveAllChunks(void); // tolua_export
+
+ /// Sends a chat message to all connected clients (in all worlds)
+ void BroadcastChat(const AString & a_Message); // tolua_export
+
+ /// Calls the callback for each player in all worlds
+ bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+
+ /// Finds a player from a partial or complete player name and calls the callback - case-insensitive
+ bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+
+ // tolua_begin
+
+ /// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API
+ static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum);
+
+ /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error
+ static int GetVirtualRAMUsage(void);
+
+ /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error
+ static int GetPhysicalRAMUsage(void);
+
+ // tolua_end
+
+private:
+ class cCommand
+ {
+ public:
+ cCommand(const AString & a_Command, cCommandOutputCallback * a_Output) :
+ m_Command(a_Command),
+ m_Output(a_Output)
+ {
+ }
+
+ AString m_Command;
+ cCommandOutputCallback * m_Output;
+ } ;
+
+ typedef std::map<AString, cWorld *> WorldMap;
+ typedef std::vector<cCommand> cCommandQueue;
+
+ /// The version of the protocol that is primary for the server (reported in the server list). All versions are still supported.
+ int m_PrimaryServerVersion;
+
+ cWorld * m_pDefaultWorld;
+ WorldMap m_WorldsByName;
+
+ cCriticalSection m_CSPendingCommands;
+ cCommandQueue m_PendingCommands;
+
+ cThread * m_InputThread;
+
+ cServer * m_Server;
+ cMonsterConfig * m_MonsterConfig;
+
+ cGroupManager * m_GroupManager;
+ cCraftingRecipes * m_CraftingRecipes;
+ cFurnaceRecipe * m_FurnaceRecipe;
+ cWebAdmin * m_WebAdmin;
+ cPluginManager * m_PluginManager;
+ cAuthenticator m_Authenticator;
+ cHTTPServer m_HTTPServer;
+
+ cMCLogger * m_Log;
+
+ bool m_bStop;
+ bool m_bRestart;
+
+ void LoadGlobalSettings();
+
+ /// Loads the worlds from settings.ini, creates the worldmap
+ void LoadWorlds(cIniFile & IniFile);
+
+ /// Starts each world's life
+ void StartWorlds(void);
+
+ /// Stops each world's threads, so that it's safe to unload them
+ void StopWorlds(void);
+
+ /// Unloads all worlds from memory
+ void UnloadWorlds(void);
+
+ /// Does the actual work of executing a command
+ void DoExecuteConsoleCommand(const AString & a_Cmd);
+
+ static void InputThread(void* a_Params);
+
+ static cRoot* s_Root;
+}; // tolua_export
+
+
+
+
diff --git a/src/Server.cpp b/src/Server.cpp
new file mode 100644
index 000000000..5f0e007d7
--- /dev/null
+++ b/src/Server.cpp
@@ -0,0 +1,707 @@
+// ReDucTor is an awesome guy who helped me a lot
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Server.h"
+#include "ClientHandle.h"
+#include "OSSupport/Timer.h"
+#include "Mobs/Monster.h"
+#include "OSSupport/Socket.h"
+#include "Root.h"
+#include "World.h"
+#include "ChunkDef.h"
+#include "PluginManager.h"
+#include "GroupManager.h"
+#include "ChatColor.h"
+#include "Entities/Player.h"
+#include "Inventory.h"
+#include "Item.h"
+#include "FurnaceRecipe.h"
+#include "WebAdmin.h"
+#include "Protocol/ProtocolRecognizer.h"
+#include "CommandOutput.h"
+
+#include "MersenneTwister.h"
+
+#include "lib/iniFile/iniFile.h"
+#include "Vector3f.h"
+
+#include <fstream>
+#include <sstream>
+#include <iostream>
+
+extern "C" {
+ #include "lib/zlib/zlib.h"
+}
+
+
+
+
+// For the "dumpmem" server command:
+/// Synchronize this with main.cpp - the leak finder needs initialization before it can be used to dump memory
+#define ENABLE_LEAK_FINDER
+
+#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
+ #pragma warning(push)
+ #pragma warning(disable:4100)
+ #include "LeakFinder.h"
+ #pragma warning(pop)
+#endif
+
+
+
+
+
+typedef std::list< cClientHandle* > ClientList;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cServer::cTickThread:
+
+cServer::cTickThread::cTickThread(cServer & a_Server) :
+ super("ServerTickThread"),
+ m_Server(a_Server)
+{
+}
+
+
+
+
+
+void cServer::cTickThread::Execute(void)
+{
+ cTimer Timer;
+
+ long long msPerTick = 50;
+ long long LastTime = Timer.GetNowTime();
+
+ while (!m_ShouldTerminate)
+ {
+ long long NowTime = Timer.GetNowTime();
+ float DeltaTime = (float)(NowTime-LastTime);
+ m_ShouldTerminate = !m_Server.Tick(DeltaTime);
+ long long TickTime = Timer.GetNowTime() - NowTime;
+
+ if (TickTime < msPerTick)
+ {
+ // Stretch tick time until it's at least msPerTick
+ cSleep::MilliSleep((unsigned int)(msPerTick - TickTime));
+ }
+
+ LastTime = NowTime;
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cServer:
+
+cServer::cServer(void) :
+ m_ListenThreadIPv4(*this, cSocket::IPv4, "Client IPv4"),
+ m_ListenThreadIPv6(*this, cSocket::IPv6, "Client IPv6"),
+ m_bIsConnected(false),
+ m_bRestarting(false),
+ m_RCONServer(*this),
+ m_TickThread(*this)
+{
+}
+
+
+
+
+
+void cServer::ClientDestroying(const cClientHandle * a_Client)
+{
+ m_SocketThreads.StopReading(a_Client);
+}
+
+
+
+
+
+void cServer::NotifyClientWrite(const cClientHandle * a_Client)
+{
+ m_NotifyWriteThread.NotifyClientWrite(a_Client);
+}
+
+
+
+
+
+void cServer::WriteToClient(const cClientHandle * a_Client, const AString & a_Data)
+{
+ m_SocketThreads.Write(a_Client, a_Data);
+}
+
+
+
+
+
+void cServer::QueueClientClose(const cClientHandle * a_Client)
+{
+ m_SocketThreads.QueueClose(a_Client);
+}
+
+
+
+
+
+void cServer::RemoveClient(const cClientHandle * a_Client)
+{
+ m_SocketThreads.RemoveClient(a_Client);
+}
+
+
+
+
+
+void cServer::ClientMovedToWorld(const cClientHandle * a_Client)
+{
+ cCSLock Lock(m_CSClients);
+ m_ClientsToRemove.push_back(const_cast<cClientHandle *>(a_Client));
+}
+
+
+
+
+
+void cServer::PlayerCreated(const cPlayer * a_Player)
+{
+ // To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread
+ cCSLock Lock(m_CSPlayerCountDiff);
+ m_PlayerCountDiff += 1;
+}
+
+
+
+
+
+void cServer::PlayerDestroying(const cPlayer * a_Player)
+{
+ // To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread
+ cCSLock Lock(m_CSPlayerCountDiff);
+ m_PlayerCountDiff -= 1;
+}
+
+
+
+
+
+bool cServer::InitServer(cIniFile & a_SettingsIni)
+{
+ m_Description = a_SettingsIni.GetValueSet("Server", "Description", "MCServer - in C++!").c_str();
+ m_MaxPlayers = a_SettingsIni.GetValueSetI("Server", "MaxPlayers", 100);
+ m_bIsHardcore = a_SettingsIni.GetValueSetB("Server", "HardcoreEnabled", false);
+ m_PlayerCount = 0;
+ m_PlayerCountDiff = 0;
+
+ if (m_bIsConnected)
+ {
+ LOGERROR("ERROR: Trying to initialize server while server is already running!");
+ return false;
+ }
+
+ LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS);
+ LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS);
+
+ if (cSocket::WSAStartup() != 0) // Only does anything on Windows, but whatever
+ {
+ LOGERROR("WSAStartup() != 0");
+ return false;
+ }
+
+ bool HasAnyPorts = false;
+ AString Ports = a_SettingsIni.GetValueSet("Server", "Port", "25565");
+ m_ListenThreadIPv4.SetReuseAddr(true);
+ if (m_ListenThreadIPv4.Initialize(Ports))
+ {
+ HasAnyPorts = true;
+ }
+
+ Ports = a_SettingsIni.GetValueSet("Server", "PortsIPv6", "25565");
+ m_ListenThreadIPv6.SetReuseAddr(true);
+ if (m_ListenThreadIPv6.Initialize(Ports))
+ {
+ HasAnyPorts = true;
+ }
+
+ if (!HasAnyPorts)
+ {
+ LOGERROR("Couldn't open any ports. Aborting the server");
+ return false;
+ }
+
+ m_RCONServer.Initialize(a_SettingsIni);
+
+ m_bIsConnected = true;
+
+ m_ServerID = "-";
+ if (a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true))
+ {
+ MTRand mtrand1;
+ unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000;
+ unsigned int r2 = (mtrand1.randInt() % 1147483647) + 1000000000;
+ std::ostringstream sid;
+ sid << std::hex << r1;
+ sid << std::hex << r2;
+ m_ServerID = sid.str();
+ m_ServerID.resize(16, '0');
+ }
+
+ m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
+ if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE)
+ {
+ m_ClientViewDistance = cClientHandle::MIN_VIEW_DISTANCE;
+ LOGINFO("Setting default viewdistance to the minimum of %d", m_ClientViewDistance);
+ }
+ if (m_ClientViewDistance > cClientHandle::MAX_VIEW_DISTANCE)
+ {
+ m_ClientViewDistance = cClientHandle::MAX_VIEW_DISTANCE;
+ LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance);
+ }
+
+ m_NotifyWriteThread.Start(this);
+
+ PrepareKeys();
+
+ return true;
+}
+
+
+
+
+
+int cServer::GetNumPlayers(void)
+{
+ cCSLock Lock(m_CSPlayerCount);
+ return m_PlayerCount;
+}
+
+
+
+
+
+void cServer::PrepareKeys(void)
+{
+ // TODO: Save and load key for persistence across sessions
+ // But generating the key takes only a moment, do we even need that?
+
+ LOGD("Generating protocol encryption keypair...");
+
+ time_t CurTime = time(NULL);
+ CryptoPP::RandomPool rng;
+ rng.Put((const byte *)&CurTime, sizeof(CurTime));
+ m_PrivateKey.GenerateRandomWithKeySize(rng, 1024);
+ CryptoPP::RSA::PublicKey pk(m_PrivateKey);
+ m_PublicKey = pk;
+}
+
+
+
+
+
+void cServer::OnConnectionAccepted(cSocket & a_Socket)
+{
+ if (!a_Socket.IsValid())
+ {
+ return;
+ }
+
+ const AString & ClientIP = a_Socket.GetIPString();
+ if (ClientIP.empty())
+ {
+ LOGWARN("cServer: A client connected, but didn't present its IP, disconnecting.");
+ a_Socket.CloseSocket();
+ return;
+ }
+
+ LOGD("Client \"%s\" connected!", ClientIP.c_str());
+
+ cClientHandle * NewHandle = new cClientHandle(&a_Socket, m_ClientViewDistance);
+ if (!m_SocketThreads.AddClient(a_Socket, NewHandle))
+ {
+ // For some reason SocketThreads have rejected the handle, clean it up
+ LOGERROR("Client \"%s\" cannot be handled, server probably unstable", ClientIP.c_str());
+ a_Socket.CloseSocket();
+ delete NewHandle;
+ return;
+ }
+
+ cCSLock Lock(m_CSClients);
+ m_Clients.push_back(NewHandle);
+}
+
+
+
+
+
+bool cServer::Tick(float a_Dt)
+{
+ // Apply the queued playercount adjustments (postponed to avoid deadlocks)
+ int PlayerCountDiff = 0;
+ {
+ cCSLock Lock(m_CSPlayerCountDiff);
+ std::swap(PlayerCountDiff, m_PlayerCountDiff);
+ }
+ {
+ cCSLock Lock(m_CSPlayerCount);
+ m_PlayerCount += PlayerCountDiff;
+ }
+
+ // Send the tick to the plugins, as well as let the plugin manager reload, if asked to (issue #102):
+ cPluginManager::Get()->Tick(a_Dt);
+
+ // Let the Root process all the queued commands:
+ cRoot::Get()->TickCommands();
+
+ // Tick all clients not yet assigned to a world:
+ TickClients(a_Dt);
+
+ if (!m_bRestarting)
+ {
+ return true;
+ }
+ else
+ {
+ m_bRestarting = false;
+ m_RestartEvent.Set();
+ return false;
+ }
+}
+
+
+
+
+
+void cServer::TickClients(float a_Dt)
+{
+ cClientHandleList RemoveClients;
+ {
+ cCSLock Lock(m_CSClients);
+
+ // Remove clients that have moved to a world (the world will be ticking them from now on)
+ for (cClientHandleList::const_iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr)
+ {
+ m_Clients.remove(*itr);
+ } // for itr - m_ClientsToRemove[]
+ m_ClientsToRemove.clear();
+
+ // Tick the remaining clients, take out those that have been destroyed into RemoveClients
+ for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();)
+ {
+ if ((*itr)->IsDestroyed())
+ {
+ // Remove the client later, when CS is not held, to avoid deadlock ( http://forum.mc-server.org/showthread.php?tid=374 )
+ RemoveClients.push_back(*itr);
+ itr = m_Clients.erase(itr);
+ continue;
+ }
+ (*itr)->Tick(a_Dt);
+ ++itr;
+ } // for itr - m_Clients[]
+ }
+
+ // Delete the clients that have been destroyed
+ for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr)
+ {
+ delete *itr;
+ } // for itr - RemoveClients[]
+}
+
+
+
+
+
+bool cServer::Start(void)
+{
+ if (!m_ListenThreadIPv4.Start())
+ {
+ return false;
+ }
+ if (!m_ListenThreadIPv6.Start())
+ {
+ return false;
+ }
+ if (!m_TickThread.Start())
+ {
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+bool cServer::Command(cClientHandle & a_Client, AString & a_Cmd)
+{
+ return cRoot::Get()->GetPluginManager()->CallHookChat(a_Client.GetPlayer(), a_Cmd);
+}
+
+
+
+
+
+void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
+{
+ AStringVector split = StringSplit(a_Cmd, " ");
+ if (split.empty())
+ {
+ return;
+ }
+
+ // Special handling: "stop" and "restart" are built in
+ if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0))
+ {
+ return;
+ }
+
+ // "help" and "reload" are to be handled by MCS, so that they work no matter what
+ if (split[0] == "help")
+ {
+ PrintHelp(split, a_Output);
+ return;
+ }
+ if (split[0] == "reload")
+ {
+ cPluginManager::Get()->ReloadPlugins();
+ return;
+ }
+
+ // There is currently no way a plugin can do these (and probably won't ever be):
+ if (split[0].compare("chunkstats") == 0)
+ {
+ cRoot::Get()->LogChunkStats(a_Output);
+ a_Output.Finished();
+ return;
+ }
+ #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
+ if (split[0].compare("dumpmem") == 0)
+ {
+ LeakFinderXmlOutput Output("memdump.xml");
+ DumpUsedMemory(&Output);
+ return;
+ }
+
+ if (split[0].compare("killmem") == 0)
+ {
+ while (true)
+ {
+ new char[100 * 1024 * 1024]; // Allocate and leak 100 MiB in a loop -> fill memory and kill MCS
+ }
+ }
+ #endif
+
+ if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output))
+ {
+ a_Output.Finished();
+ return;
+ }
+
+ a_Output.Out("Unknown command, type 'help' for all commands.");
+ a_Output.Finished();
+}
+
+
+
+
+
+void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
+{
+ typedef std::pair<AString, AString> AStringPair;
+ typedef std::vector<AStringPair> AStringPairs;
+
+ class cCallback :
+ public cPluginManager::cCommandEnumCallback
+ {
+ public:
+ cCallback(void) : m_MaxLen(0) {}
+
+ virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
+ {
+ if (!a_HelpString.empty())
+ {
+ m_Commands.push_back(AStringPair(a_Command, a_HelpString));
+ if (m_MaxLen < a_Command.length())
+ {
+ m_MaxLen = a_Command.length();
+ }
+ }
+ return false;
+ }
+
+ AStringPairs m_Commands;
+ size_t m_MaxLen;
+ } Callback;
+ cPluginManager::Get()->ForEachConsoleCommand(Callback);
+ std::sort(Callback.m_Commands.begin(), Callback.m_Commands.end());
+ for (AStringPairs::const_iterator itr = Callback.m_Commands.begin(), end = Callback.m_Commands.end(); itr != end; ++itr)
+ {
+ const AStringPair & cmd = *itr;
+ a_Output.Out(Printf("%-*s%s\n", Callback.m_MaxLen, cmd.first.c_str(), cmd.second.c_str()));
+ } // for itr - Callback.m_Commands[]
+ a_Output.Finished();
+}
+
+
+
+
+
+void cServer::BindBuiltInConsoleCommands(void)
+{
+ cPluginManager * PlgMgr = cPluginManager::Get();
+ PlgMgr->BindConsoleCommand("help", NULL, " - Shows the available commands");
+ PlgMgr->BindConsoleCommand("reload", NULL, " - Reloads all plugins");
+ PlgMgr->BindConsoleCommand("restart", NULL, " - Restarts the server cleanly");
+ PlgMgr->BindConsoleCommand("stop", NULL, " - Stops the server cleanly");
+ PlgMgr->BindConsoleCommand("chunkstats", NULL, " - Displays detailed chunk memory statistics");
+ #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
+ PlgMgr->BindConsoleCommand("dumpmem", NULL, " - Dumps all used memory blocks together with their callstacks into memdump.xml");
+ #endif
+}
+
+
+
+
+
+void cServer::Shutdown(void)
+{
+ m_ListenThreadIPv4.Stop();
+ m_ListenThreadIPv6.Stop();
+
+ m_bRestarting = true;
+ m_RestartEvent.Wait();
+
+ cRoot::Get()->SaveAllChunks();
+
+ cCSLock Lock(m_CSClients);
+ for( ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr )
+ {
+ (*itr)->Destroy();
+ delete *itr;
+ }
+ m_Clients.clear();
+}
+
+
+
+
+
+void cServer::KickUser(int a_ClientID, const AString & a_Reason)
+{
+ cCSLock Lock(m_CSClients);
+ for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
+ {
+ if ((*itr)->GetUniqueID() == a_ClientID)
+ {
+ (*itr)->Kick(a_Reason);
+ }
+ } // for itr - m_Clients[]
+}
+
+
+
+
+
+void cServer::AuthenticateUser(int a_ClientID)
+{
+ cCSLock Lock(m_CSClients);
+ for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
+ {
+ if ((*itr)->GetUniqueID() == a_ClientID)
+ {
+ (*itr)->Authenticate();
+ return;
+ }
+ } // for itr - m_Clients[]
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cServer::cNotifyWriteThread:
+
+cServer::cNotifyWriteThread::cNotifyWriteThread(void) :
+ super("ClientPacketThread"),
+ m_Server(NULL)
+{
+}
+
+
+
+
+
+cServer::cNotifyWriteThread::~cNotifyWriteThread()
+{
+ m_ShouldTerminate = true;
+ m_Event.Set();
+ Wait();
+}
+
+
+
+
+
+bool cServer::cNotifyWriteThread::Start(cServer * a_Server)
+{
+ m_Server = a_Server;
+ return super::Start();
+}
+
+
+
+
+
+void cServer::cNotifyWriteThread::Execute(void)
+{
+ cClientHandleList Clients;
+ while (!m_ShouldTerminate)
+ {
+ cCSLock Lock(m_CS);
+ while (m_Clients.size() == 0)
+ {
+ cCSUnlock Unlock(Lock);
+ m_Event.Wait();
+ if (m_ShouldTerminate)
+ {
+ return;
+ }
+ }
+
+ // Copy the clients to notify and unlock the CS:
+ Clients.splice(Clients.begin(), m_Clients);
+ Lock.Unlock();
+
+ for (cClientHandleList::iterator itr = Clients.begin(); itr != Clients.end(); ++itr)
+ {
+ m_Server->m_SocketThreads.NotifyWrite(*itr);
+ } // for itr - Clients[]
+ Clients.clear();
+ } // while (!mShouldTerminate)
+}
+
+
+
+
+
+void cServer::cNotifyWriteThread::NotifyClientWrite(const cClientHandle * a_Client)
+{
+ {
+ cCSLock Lock(m_CS);
+ m_Clients.remove(const_cast<cClientHandle *>(a_Client)); // Put it there only once
+ m_Clients.push_back(const_cast<cClientHandle *>(a_Client));
+ }
+ m_Event.Set();
+}
+
+
+
+
diff --git a/src/Server.h b/src/Server.h
new file mode 100644
index 000000000..0d93469a5
--- /dev/null
+++ b/src/Server.h
@@ -0,0 +1,195 @@
+
+// cServer.h
+
+// Interfaces to the cServer object representing the network server
+
+
+
+
+
+#pragma once
+
+#include "OSSupport/SocketThreads.h"
+#include "OSSupport/ListenThread.h"
+#include "cryptopp/rsa.h"
+#include "cryptopp/randpool.h"
+#include "RCONServer.h"
+
+
+
+
+
+// fwd:
+class cPlayer;
+class cClientHandle;
+class cIniFile;
+class cCommandOutputCallback ;
+
+typedef std::list<cClientHandle *> cClientHandleList;
+
+
+
+
+
+class cServer // tolua_export
+ : public cListenThread::cCallback
+{ // tolua_export
+public: // tolua_export
+ bool InitServer(cIniFile & a_SettingsIni);
+
+ // tolua_begin
+
+ const AString & GetDescription(void) const {return m_Description; }
+
+ // Player counts:
+ int GetMaxPlayers(void) const {return m_MaxPlayers; }
+ int GetNumPlayers(void);
+ void SetMaxPlayers(int a_MaxPlayers) { m_MaxPlayers = a_MaxPlayers; }
+
+ // Hardcore mode or not:
+ bool IsHardcore(void) const {return m_bIsHardcore; }
+
+ // tolua_end
+
+ bool Start(void);
+
+ bool Command(cClientHandle & a_Client, AString & a_Cmd);
+
+ /// Executes the console command, sends output through the specified callback
+ void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
+
+ /// Lists all available console commands and their helpstrings
+ void PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
+
+ /// Binds the built-in console commands with the plugin manager
+ static void BindBuiltInConsoleCommands(void);
+
+ void Shutdown(void);
+
+ void KickUser(int a_ClientID, const AString & a_Reason);
+ void AuthenticateUser(int a_ClientID); // Called by cAuthenticator to auth the specified user
+
+ const AString & GetServerID(void) const { return m_ServerID; } // tolua_export
+
+ void ClientDestroying(const cClientHandle * a_Client); // Called by cClientHandle::Destroy(); stop m_SocketThreads from calling back into a_Client
+
+ void NotifyClientWrite(const cClientHandle * a_Client); // Notifies m_SocketThreads that client has something to be written
+
+ void WriteToClient(const cClientHandle * a_Client, const AString & a_Data); // Queues outgoing data for the client through m_SocketThreads
+
+ void QueueClientClose(const cClientHandle * a_Client); // Queues the clienthandle to close when all its outgoing data is sent
+
+ void RemoveClient(const cClientHandle * a_Client); // Removes the clienthandle from m_SocketThreads
+
+ /// Don't tick a_Client anymore, it will be ticked from its cPlayer instead
+ void ClientMovedToWorld(const cClientHandle * a_Client);
+
+ /// Notifies the server that a player was created; the server uses this to adjust the number of players
+ void PlayerCreated(const cPlayer * a_Player);
+
+ /// Notifies the server that a player is being destroyed; the server uses this to adjust the number of players
+ void PlayerDestroying(const cPlayer * a_Player);
+
+ CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
+ CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; }
+
+private:
+
+ friend class cRoot; // so cRoot can create and destroy cServer
+
+ /// When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap)
+ class cNotifyWriteThread :
+ public cIsThread
+ {
+ typedef cIsThread super;
+
+ cEvent m_Event; // Set when m_Clients gets appended
+ cServer * m_Server;
+
+ cCriticalSection m_CS;
+ cClientHandleList m_Clients;
+
+ virtual void Execute(void);
+
+ public:
+
+ cNotifyWriteThread(void);
+ ~cNotifyWriteThread();
+
+ bool Start(cServer * a_Server);
+
+ void NotifyClientWrite(const cClientHandle * a_Client);
+ } ;
+
+ /// The server tick thread takes care of the players who aren't yet spawned in a world
+ class cTickThread :
+ public cIsThread
+ {
+ typedef cIsThread super;
+
+ public:
+ cTickThread(cServer & a_Server);
+
+ protected:
+ cServer & m_Server;
+
+ // cIsThread overrides:
+ virtual void Execute(void) override;
+ } ;
+
+
+ cNotifyWriteThread m_NotifyWriteThread;
+
+ cListenThread m_ListenThreadIPv4;
+ cListenThread m_ListenThreadIPv6;
+
+ cCriticalSection m_CSClients; ///< Locks client lists
+ cClientHandleList m_Clients; ///< Clients that are connected to the server and not yet assigned to a cWorld
+ cClientHandleList m_ClientsToRemove; ///< Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick()
+
+ cCriticalSection m_CSPlayerCount; ///< Locks the m_PlayerCount
+ int m_PlayerCount; ///< Number of players currently playing in the server
+ cCriticalSection m_CSPlayerCountDiff; ///< Locks the m_PlayerCountDiff
+ int m_PlayerCountDiff; ///< Adjustment to m_PlayerCount to be applied in the Tick thread
+
+ cSocketThreads m_SocketThreads;
+
+ int m_ClientViewDistance; // The default view distance for clients; settable in Settings.ini
+
+ bool m_bIsConnected; // true - connected false - not connected
+
+ bool m_bRestarting;
+
+ CryptoPP::RSA::PrivateKey m_PrivateKey;
+ CryptoPP::RSA::PublicKey m_PublicKey;
+
+ cRCONServer m_RCONServer;
+
+ AString m_Description;
+ int m_MaxPlayers;
+ bool m_bIsHardcore;
+
+ cTickThread m_TickThread;
+ cEvent m_RestartEvent;
+
+ /// The server ID used for client authentication
+ AString m_ServerID;
+
+
+ cServer(void);
+
+ /// Loads, or generates, if missing, RSA keys for protocol encryption
+ void PrepareKeys(void);
+
+ bool Tick(float a_Dt);
+
+ /// Ticks the clients in m_Clients, manages the list in respect to removing clients
+ void TickClients(float a_Dt);
+
+ // cListenThread::cCallback overrides:
+ virtual void OnConnectionAccepted(cSocket & a_Socket) override;
+}; // tolua_export
+
+
+
+
diff --git a/src/Simulator/DelayedFluidSimulator.cpp b/src/Simulator/DelayedFluidSimulator.cpp
new file mode 100644
index 000000000..a4645ca09
--- /dev/null
+++ b/src/Simulator/DelayedFluidSimulator.cpp
@@ -0,0 +1,158 @@
+
+// DelayedFluidSimulator.cpp
+
+// Interfaces to the cDelayedFluidSimulator class representing a fluid simulator that has a configurable delay
+// before simulating a block. Each tick it takes a consecutive delay "slot" and simulates only blocks in that slot.
+
+#include "Globals.h"
+
+#include "DelayedFluidSimulator.h"
+#include "../World.h"
+#include "../Chunk.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cDelayedFluidSimulatorChunkData::cSlot
+
+bool cDelayedFluidSimulatorChunkData::cSlot::Add(int a_RelX, int a_RelY, int a_RelZ)
+{
+ ASSERT(a_RelZ >= 0);
+ ASSERT(a_RelZ < ARRAYCOUNT(m_Blocks));
+
+ cCoordWithIntVector & Blocks = m_Blocks[a_RelZ];
+ int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
+ for (cCoordWithIntVector::const_iterator itr = Blocks.begin(), end = Blocks.end(); itr != end; ++itr)
+ {
+ if (itr->Data == Index)
+ {
+ // Already present
+ return false;
+ }
+ } // for itr - Blocks[]
+ Blocks.push_back(cCoordWithInt(a_RelX, a_RelY, a_RelZ, Index));
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cDelayedFluidSimulatorChunkData:
+
+cDelayedFluidSimulatorChunkData::cDelayedFluidSimulatorChunkData(int a_TickDelay) :
+ m_Slots(new cSlot[a_TickDelay])
+{
+}
+
+
+
+
+
+cDelayedFluidSimulatorChunkData::~cDelayedFluidSimulatorChunkData()
+{
+ delete[] m_Slots;
+ m_Slots = NULL;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cDelayedFluidSimulator:
+
+cDelayedFluidSimulator::cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay) :
+ super(a_World, a_Fluid, a_StationaryFluid),
+ m_TickDelay(a_TickDelay),
+ m_AddSlotNum(a_TickDelay - 1),
+ m_SimSlotNum(0),
+ m_TotalBlocks(0)
+{
+}
+
+
+
+
+
+void cDelayedFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+{
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ // Not inside the world (may happen when rclk with a full bucket - the client sends Y = -1)
+ return;
+ }
+
+ if ((a_Chunk == NULL) || !a_Chunk->IsValid())
+ {
+ return;
+ }
+
+ int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
+ BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
+ if (BlockType != m_FluidBlock)
+ {
+ return;
+ }
+
+ void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData();
+ cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw;
+ cDelayedFluidSimulatorChunkData::cSlot & Slot = ChunkData->m_Slots[m_AddSlotNum];
+
+ // Add, if not already present:
+ if (!Slot.Add(RelX, a_BlockY, RelZ))
+ {
+ return;
+ }
+
+ ++m_TotalBlocks;
+}
+
+
+
+
+
+void cDelayedFluidSimulator::Simulate(float a_Dt)
+{
+ m_AddSlotNum = m_SimSlotNum;
+ m_SimSlotNum += 1;
+ if (m_SimSlotNum >= m_TickDelay)
+ {
+ m_SimSlotNum = 0;
+ }
+}
+
+
+
+
+
+void cDelayedFluidSimulator::SimulateChunk(float 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;
+ cDelayedFluidSimulatorChunkData::cSlot & Slot = ChunkData->m_Slots[m_SimSlotNum];
+
+ // Simulate all the blocks in the scheduled slot:
+ for (int i = 0; i < ARRAYCOUNT(Slot.m_Blocks); i++)
+ {
+ cCoordWithIntVector & Blocks = Slot.m_Blocks[i];
+ if (Blocks.empty())
+ {
+ continue;
+ }
+ for (cCoordWithIntVector::iterator itr = Blocks.begin(), end = Blocks.end(); itr != end; ++itr)
+ {
+ SimulateBlock(a_Chunk, itr->x, itr->y, itr->z);
+ }
+ m_TotalBlocks -= Blocks.size();
+ Blocks.clear();
+ }
+}
+
+
+
+
diff --git a/src/Simulator/DelayedFluidSimulator.h b/src/Simulator/DelayedFluidSimulator.h
new file mode 100644
index 000000000..c81500741
--- /dev/null
+++ b/src/Simulator/DelayedFluidSimulator.h
@@ -0,0 +1,82 @@
+
+// DelayedFluidSimulator.h
+
+// Interfaces to the cDelayedFluidSimulator class representing a fluid simulator that has a configurable delay
+// before simulating a block. Each tick it takes a consecutive delay "slot" and simulates only blocks in that slot.
+
+
+
+
+#pragma once
+
+#include "FluidSimulator.h"
+
+
+
+
+
+class cDelayedFluidSimulatorChunkData :
+ public cFluidSimulatorData
+{
+public:
+ class cSlot
+ {
+ public:
+ /// Returns true if the specified block is stored
+ bool HasBlock(int a_RelX, int a_RelY, int a_RelZ);
+
+ /// Adds the specified block unless already present; returns true if added, false if the block was already present
+ bool Add(int a_RelX, int a_RelY, int a_RelZ);
+
+ /** Array of block containers, each item stores blocks for one Z coord
+ Int param is the block index (for faster duplicate comparison in Add())
+ */
+ cCoordWithIntVector m_Blocks[16];
+ } ;
+
+ cDelayedFluidSimulatorChunkData(int a_TickDelay);
+ virtual ~cDelayedFluidSimulatorChunkData();
+
+ /// Slots, one for each delay tick, each containing the blocks to simulate
+ cSlot * m_Slots;
+} ;
+
+
+
+
+
+class cDelayedFluidSimulator :
+ public cFluidSimulator
+{
+ typedef cFluidSimulator super;
+
+public:
+ cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay);
+
+ // 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 cFluidSimulatorData * CreateChunkData(void) override { return new cDelayedFluidSimulatorChunkData(m_TickDelay); }
+
+protected:
+
+ int m_TickDelay; // Count of the m_Slots array in each ChunkData
+ int m_AddSlotNum; // Index into m_Slots[] where to add new blocks in each ChunkData
+ int m_SimSlotNum; // Index into m_Slots[] where to simulate blocks in each ChunkData
+
+ int m_TotalBlocks; // Statistics only: the total number of blocks currently queued
+
+ /*
+ Slots:
+ | 0 | 1 | ... | m_AddSlotNum | m_SimSlotNum | ... | m_TickDelay - 1 |
+ adding blocks here ^ | ^ simulating here
+ */
+
+ /// Called from SimulateChunk() to simulate each block in one slot of blocks. Descendants override this method to provide custom simulation.
+ virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) = 0;
+} ;
+
+
+
+
diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp
new file mode 100644
index 000000000..ac3fb9695
--- /dev/null
+++ b/src/Simulator/FireSimulator.cpp
@@ -0,0 +1,374 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "FireSimulator.h"
+#include "../World.h"
+#include "../BlockID.h"
+#include "../Defines.h"
+#include "../Chunk.h"
+
+
+
+
+
+// Easy switch for turning on debugging logging:
+#if 0
+ #define FLOG LOGD
+#else
+ #define FLOG(...)
+#endif
+
+
+
+
+
+#define MAX_CHANCE_REPLACE_FUEL 100000
+#define MAX_CHANCE_FLAMMABILITY 100000
+
+
+
+
+
+static const struct
+{
+ int x, y, z;
+} gCrossCoords[] =
+{
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+} ;
+
+
+
+
+
+static const struct
+{
+ int x, y, z;
+} gNeighborCoords[] =
+{
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 1, 0},
+ { 0, -1, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFireSimulator:
+
+cFireSimulator::cFireSimulator(cWorld & a_World, cIniFile & a_IniFile) :
+ cSimulator(a_World)
+{
+ // Read params from the ini file:
+ m_BurnStepTimeFuel = a_IniFile.GetValueSetI("FireSimulator", "BurnStepTimeFuel", 500);
+ m_BurnStepTimeNonfuel = a_IniFile.GetValueSetI("FireSimulator", "BurnStepTimeNonfuel", 100);
+ m_Flammability = a_IniFile.GetValueSetI("FireSimulator", "Flammability", 50);
+ m_ReplaceFuelChance = a_IniFile.GetValueSetI("FireSimulator", "ReplaceFuelChance", 50000);
+}
+
+
+
+
+
+cFireSimulator::~cFireSimulator()
+{
+}
+
+
+
+
+
+void cFireSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+{
+ cCoordWithIntList & Data = a_Chunk->GetFireSimulatorData();
+
+ int NumMSecs = (int)a_Dt;
+ for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();)
+ {
+ int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z);
+ BLOCKTYPE BlockType = a_Chunk->GetBlock(idx);
+
+ if (!IsAllowedBlock(BlockType))
+ {
+ // The block is no longer eligible (not a fire block anymore; a player probably placed a block over the fire)
+ FLOG("FS: Removing block {%d, %d, %d}",
+ itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
+ );
+ itr = Data.erase(itr);
+ continue;
+ }
+
+ // Try to spread the fire:
+ TrySpreadFire(a_Chunk, itr->x, itr->y, itr->z);
+
+ itr->Data -= NumMSecs;
+ if (itr->Data >= 0)
+ {
+ // Not yet, wait for it longer
+ ++itr;
+ continue;
+ }
+
+ // Burn out the fire one step by increasing the meta:
+ /*
+ FLOG("FS: Fire at {%d, %d, %d} is stepping",
+ itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
+ );
+ */
+ NIBBLETYPE BlockMeta = a_Chunk->GetMeta(idx);
+ if (BlockMeta == 0x0f)
+ {
+ // The fire burnt out completely
+ FLOG("FS: Fire at {%d, %d, %d} burnt out, removing the fire block",
+ itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
+ );
+ a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0);
+ RemoveFuelNeighbors(a_Chunk, itr->x, itr->y, itr->z);
+ itr = Data.erase(itr);
+ continue;
+ }
+ a_Chunk->SetMeta(idx, BlockMeta + 1);
+ itr->Data = GetBurnStepTime(a_Chunk, itr->x, itr->y, itr->z); // TODO: Add some randomness into this
+ } // for itr - Data[]
+}
+
+
+
+
+
+bool cFireSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
+{
+ return (a_BlockType == E_BLOCK_FIRE);
+}
+
+
+
+
+
+bool cFireSimulator::IsFuel(BLOCKTYPE a_BlockType)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_PLANKS:
+ case E_BLOCK_LEAVES:
+ case E_BLOCK_LOG:
+ case E_BLOCK_WOOL:
+ case E_BLOCK_BOOKCASE:
+ case E_BLOCK_FENCE:
+ case E_BLOCK_TNT:
+ case E_BLOCK_VINES:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cFireSimulator::IsForever(BLOCKTYPE a_BlockType)
+{
+ return (a_BlockType == E_BLOCK_NETHERRACK);
+}
+
+
+
+
+
+void cFireSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+{
+ if ((a_Chunk == NULL) || !a_Chunk->IsValid())
+ {
+ return;
+ }
+
+ int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
+ BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
+ if (!IsAllowedBlock(BlockType))
+ {
+ return;
+ }
+
+ // Check for duplicates:
+ cFireSimulatorChunkData & ChunkData = a_Chunk->GetFireSimulatorData();
+ for (cCoordWithIntList::iterator itr = ChunkData.begin(), end = ChunkData.end(); itr != end; ++itr)
+ {
+ if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ))
+ {
+ // Already present, skip adding
+ return;
+ }
+ } // for itr - ChunkData[]
+
+ FLOG("FS: Adding block {%d, %d, %d}", a_BlockX, a_BlockY, a_BlockZ);
+ ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ, 100));
+}
+
+
+
+
+
+int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+{
+ bool IsBlockBelowSolid = false;
+ if (a_RelY > 0)
+ {
+ BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
+ if (IsForever(BlockBelow))
+ {
+ // Is burning atop of netherrack, burn forever (re-check in 10 sec)
+ return 10000;
+ }
+ if (IsFuel(BlockBelow))
+ {
+ return m_BurnStepTimeFuel;
+ }
+ IsBlockBelowSolid = g_BlockIsSolid[BlockBelow];
+ }
+
+ for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (a_Chunk->UnboundedRelGetBlock(a_RelX + gCrossCoords[i].x, a_RelY, a_RelZ + gCrossCoords[i].z, BlockType, BlockMeta))
+ {
+ if (IsFuel(BlockType))
+ {
+ return m_BurnStepTimeFuel;
+ }
+ }
+ } // for i - gCrossCoords[]
+
+ if (!IsBlockBelowSolid && (a_RelY >= 0))
+ {
+ // Checked through everything, nothing was flammable
+ // If block below isn't solid, we can't have fire, it would be a non-fueled fire
+ // SetBlock just to make sure fire doesn't spawn
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
+ return 0;
+ }
+ return m_BurnStepTimeNonfuel;
+}
+
+
+
+
+
+void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+{
+ /*
+ if (m_World.GetTickRandomNumber(10000) > 100)
+ {
+ // Make the chance to spread 100x smaller
+ return;
+ }
+ */
+
+ for (int x = a_RelX - 1; x <= a_RelX + 1; x++)
+ {
+ for (int z = a_RelZ - 1; z <= a_RelZ + 1; z++)
+ {
+ for (int y = a_RelY - 1; y <= a_RelY + 2; y++) // flames spread up one more block than around
+ {
+ // No need to check the coords for equality with the parent block,
+ // it cannot catch fire anyway (because it's not an air block)
+
+ if (m_World.GetTickRandomNumber(MAX_CHANCE_FLAMMABILITY) > m_Flammability)
+ {
+ continue;
+ }
+
+ // Start the fire in the neighbor {x, y, z}
+ /*
+ FLOG("FS: Trying to start fire at {%d, %d, %d}.",
+ x + a_Chunk->GetPosX() * cChunkDef::Width, y, z + a_Chunk->GetPosZ() * cChunkDef::Width
+ );
+ */
+ if (CanStartFireInBlock(a_Chunk, x, y, z))
+ {
+ FLOG("FS: Starting new fire at {%d, %d, %d}.",
+ x + a_Chunk->GetPosX() * cChunkDef::Width, y, z + a_Chunk->GetPosZ() * cChunkDef::Width
+ );
+ a_Chunk->UnboundedRelSetBlock(x, y, z, E_BLOCK_FIRE, 0);
+ }
+ } // for y
+ } // for z
+ } // for x
+}
+
+
+
+
+
+void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+{
+ for (int i = 0; i < ARRAYCOUNT(gNeighborCoords); i++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_Chunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta))
+ {
+ // Neighbor not accessible, ignore it
+ continue;
+ }
+ if (!IsFuel(BlockType))
+ {
+ continue;
+ }
+ bool ShouldReplaceFuel = (m_World.GetTickRandomNumber(MAX_CHANCE_REPLACE_FUEL) < m_ReplaceFuelChance);
+ a_Chunk->UnboundedRelSetBlock(
+ a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z,
+ ShouldReplaceFuel ? E_BLOCK_FIRE : E_BLOCK_AIR, 0
+ );
+ } // for i - Coords[]
+}
+
+
+
+
+
+bool cFireSimulator::CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ)
+{
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
+ {
+ // The chunk is not accessible
+ return false;
+ }
+
+ if (BlockType != E_BLOCK_AIR)
+ {
+ // Only an air block can be replaced by a fire block
+ return false;
+ }
+
+ for (int i = 0; i < ARRAYCOUNT(gNeighborCoords); i++)
+ {
+ if (!a_NearChunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta))
+ {
+ // Neighbor inaccessible, skip it while evaluating
+ continue;
+ }
+ if (IsFuel(BlockType))
+ {
+ return true;
+ }
+ } // for i - Coords[]
+ return false;
+}
+
+
+
+
diff --git a/src/Simulator/FireSimulator.h b/src/Simulator/FireSimulator.h
new file mode 100644
index 000000000..0d8a548ef
--- /dev/null
+++ b/src/Simulator/FireSimulator.h
@@ -0,0 +1,75 @@
+
+#pragma once
+
+#include "Simulator.h"
+#include "../BlockEntities/BlockEntity.h"
+
+
+
+
+
+/** The fire simulator takes care of the fire blocks.
+It periodically increases their meta ("steps") until they "burn out"; it also supports the forever burning netherrack.
+Each individual fire block gets stored in per-chunk data; that list is then used for fast retrieval.
+The data value associated with each coord is used as the number of msec that the fire takes until
+it progresses to the next step (blockmeta++). This value is updated if a neighbor is changed.
+The simulator reads its parameters from the ini file given to the constructor.
+*/
+class cFireSimulator :
+ public cSimulator
+{
+public:
+ cFireSimulator(cWorld & a_World, cIniFile & a_IniFile);
+ ~cFireSimulator();
+
+ virtual void Simulate(float a_Dt) override {} // not used
+ virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
+
+ virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override;
+
+ bool IsFuel (BLOCKTYPE a_BlockType);
+ bool IsForever(BLOCKTYPE a_BlockType);
+
+protected:
+ /// Time (in msec) that a fire block takes to burn with a fuel block into the next step
+ unsigned m_BurnStepTimeFuel;
+
+ /// Time (in msec) that a fire block takes to burn without a fuel block into the next step
+ unsigned m_BurnStepTimeNonfuel;
+
+ /// Chance [0..100000] of an adjacent fuel to catch fire on each tick
+ int m_Flammability;
+
+ /// Chance [0..100000] of a fuel burning out being replaced by a new fire block instead of an air block
+ int m_ReplaceFuelChance;
+
+
+ virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
+
+ /// Returns the time [msec] after which the specified fire block is stepped again; based on surrounding fuels
+ int GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
+
+ /// Tries to spread fire to a neighborhood of the specified block
+ void TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
+
+ /// Removes all burnable blocks neighboring the specified block
+ void RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
+
+ /** Returns true if a fire can be started in the specified block,
+ that is, it is an air block and has fuel next to it.
+ Note that a_NearChunk may be a chunk neighbor to the block specified!
+ The coords are relative to a_NearChunk but not necessarily in it.
+ */
+ bool CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ);
+} ;
+
+
+
+
+
+/// Stores individual fire blocks in the chunk; the int data is used as the time [msec] the fire takes to step to another stage (blockmeta++)
+typedef cCoordWithIntList cFireSimulatorChunkData;
+
+
+
+
diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp
new file mode 100644
index 000000000..d204a1f8b
--- /dev/null
+++ b/src/Simulator/FloodyFluidSimulator.cpp
@@ -0,0 +1,330 @@
+
+// FloodyFluidSimulator.cpp
+
+// Interfaces to the cFloodyFluidSimulator that represents a fluid simulator that tries to flood everything :)
+// http://forum.mc-server.org/showthread.php?tid=565
+
+#include "Globals.h"
+
+#include "FloodyFluidSimulator.h"
+#include "../World.h"
+#include "../Chunk.h"
+#include "../BlockArea.h"
+#include "../Blocks/BlockHandler.h"
+
+
+
+
+
+// Enable or disable detailed logging
+#if 0
+ #define FLOG LOGD
+#else
+ #define FLOG(...)
+#endif
+
+
+
+
+
+cFloodyFluidSimulator::cFloodyFluidSimulator(
+ cWorld & a_World,
+ BLOCKTYPE a_Fluid,
+ BLOCKTYPE a_StationaryFluid,
+ NIBBLETYPE a_Falloff,
+ int a_TickDelay,
+ int a_NumNeighborsForSource
+) :
+ super(a_World, a_Fluid, a_StationaryFluid, a_TickDelay),
+ m_Falloff(a_Falloff),
+ m_NumNeighborsForSource(a_NumNeighborsForSource)
+{
+}
+
+
+
+
+
+void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+{
+ FLOG("Simulating block {%d, %d, %d}: block %d, meta %d",
+ a_Chunk->GetPosX() * cChunkDef::Width + a_RelX, a_RelY, a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ,
+ a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ),
+ a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ)
+ );
+
+ NIBBLETYPE MyMeta = a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ);
+ if (!IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ)))
+ {
+ // Can happen - if a block is scheduled for simulating and gets replaced in the meantime.
+ FLOG(" BadBlockType exit");
+ return;
+ }
+
+ if (MyMeta != 0)
+ {
+ // Source blocks aren't checked for tributaries, others are.
+ if (CheckTributaries(a_Chunk, a_RelX, a_RelY, a_RelZ, MyMeta))
+ {
+ // Has no tributary, has been decreased (in CheckTributaries()),
+ // no more processing needed (neighbors have been scheduled by the decrease)
+ FLOG(" CheckTributaries exit");
+ return;
+ }
+ }
+
+ // New meta for the spreading to neighbors:
+ // If this is a source block or was falling, the new meta is just the falloff
+ // Otherwise it is the current meta plus falloff (may be larger than max height, will be checked later)
+ NIBBLETYPE NewMeta = ((MyMeta == 0) || ((MyMeta & 0x08) != 0)) ? m_Falloff : (MyMeta + m_Falloff);
+ bool SpreadFurther = true;
+ if (a_RelY > 0)
+ {
+ BLOCKTYPE Below = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
+ if (IsPassableForFluid(Below) || IsBlockLava(Below) || IsBlockWater(Below))
+ {
+ // Spread only down, possibly washing away what's there or turning lava to stone / cobble / obsidian:
+ SpreadToNeighbor(a_Chunk, a_RelX, a_RelY - 1, a_RelZ, 8);
+ SpreadFurther = false;
+ }
+ // If source creation is on, check for it here:
+ else if (
+ (m_NumNeighborsForSource > 0) && // Source creation is on
+ (MyMeta == m_Falloff) && // Only exactly one block away from a source (fast bail-out)
+ !IsPassableForFluid(Below) && // Only exactly 1 block deep
+ CheckNeighborsForSource(a_Chunk, a_RelX, a_RelY, a_RelZ) // Did we create a source?
+ )
+ {
+ // We created a source, no more spreading is to be done now
+ // Also has been re-scheduled for ticking in the next wave, so no marking is needed
+ return;
+ }
+ }
+
+ if (SpreadFurther && (NewMeta < 8))
+ {
+ // Spread to the neighbors:
+ SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, NewMeta);
+ SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, NewMeta);
+ SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, NewMeta);
+ SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, NewMeta);
+ }
+
+ // Mark as processed:
+ a_Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, MyMeta);
+}
+
+
+
+
+
+bool cFloodyFluidSimulator::CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta)
+{
+ // If we have a section above, check if there's fluid above this block that would feed it:
+ if (a_RelY < cChunkDef::Height - 1)
+ {
+ if (IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ)))
+ {
+ // This block is fed from above, no more processing needed
+ FLOG(" Fed from above");
+ return false;
+ }
+ }
+
+ // Not fed from above, check if there's a feed from the side (but not if it's a downward-flowing block):
+ if (a_MyMeta != 8)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ static const Vector3i Coords[] =
+ {
+ Vector3i( 1, 0, 0),
+ Vector3i(-1, 0, 0),
+ Vector3i( 0, 0, 1),
+ Vector3i( 0, 0, -1),
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ if (!a_Chunk->UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta))
+ {
+ continue;
+ }
+ if (IsAllowedBlock(BlockType) && IsHigherMeta(BlockMeta, a_MyMeta))
+ {
+ // This block is fed, no more processing needed
+ FLOG(" Fed from {%d, %d, %d}, type %d, meta %d",
+ a_Chunk->GetPosX() * cChunkDef::Width + a_RelX + Coords[i].x,
+ a_RelY,
+ a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ + Coords[i].z,
+ BlockType, BlockMeta
+ );
+ return false;
+ }
+ } // for i - Coords[]
+ } // if not fed from above
+
+ // Block is not fed, decrease by m_Falloff levels:
+ if (a_MyMeta >= 8)
+ {
+ FLOG(" Not fed and downwards, turning into non-downwards meta %d", m_Falloff);
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, m_Falloff);
+ }
+ else
+ {
+ a_MyMeta += m_Falloff;
+ if (a_MyMeta < 8)
+ {
+ FLOG(" Not fed, decreasing from %d to %d", a_MyMeta - m_Falloff, a_MyMeta);
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, a_MyMeta);
+ }
+ else
+ {
+ FLOG(" Not fed, meta %d, erasing altogether", a_MyMeta);
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
+ }
+ }
+ return true;
+}
+
+
+
+
+
+void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta)
+{
+ ASSERT(a_NewMeta <= 8); // Invalid meta values
+ ASSERT(a_NewMeta > 0); // Source blocks aren't spread
+
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
+ {
+ // Chunk not available
+ return;
+ }
+
+ if (IsAllowedBlock(BlockType))
+ {
+ if ((BlockMeta == a_NewMeta) || IsHigherMeta(BlockMeta, a_NewMeta))
+ {
+ // Don't spread there, there's already a higher or same level there
+ return;
+ }
+ }
+
+ // Check water - lava interaction:
+ if (m_FluidBlock == E_BLOCK_LAVA)
+ {
+ if (IsBlockWater(BlockType))
+ {
+ // Lava flowing into water, change to stone / cobblestone based on direction:
+ BLOCKTYPE NewBlock = (a_NewMeta == 8) ? E_BLOCK_STONE : E_BLOCK_COBBLESTONE;
+ FLOG(" Lava flowing into water, turning water at rel {%d, %d, %d} into stone",
+ a_RelX, a_RelY, a_RelZ,
+ ItemTypeToString(NewBlock).c_str()
+ );
+ a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0);
+ m_World.BroadcastSoundEffect("random.fizz", a_RelX * 8, a_RelY * 8, a_RelZ * 8, 0.5f, 1.5f);
+ return;
+ }
+ }
+ else if (m_FluidBlock == E_BLOCK_WATER)
+ {
+ if (IsBlockLava(BlockType))
+ {
+ // Water flowing into lava, change to cobblestone / obsidian based on dest block:
+ BLOCKTYPE NewBlock = (BlockMeta == 0) ? E_BLOCK_OBSIDIAN : E_BLOCK_COBBLESTONE;
+ FLOG(" Water flowing into lava, turning lava at rel {%d, %d, %d} into %s",
+ a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str()
+ );
+ a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0);
+ m_World.BroadcastSoundEffect("random.fizz", a_RelX * 8, a_RelY * 8, a_RelZ * 8, 0.5f, 1.5f);
+ return;
+ }
+ }
+ else
+ {
+ ASSERT(!"Unknown fluid!");
+ }
+
+ if (!IsPassableForFluid(BlockType))
+ {
+ // Can't spread there
+ return;
+ }
+
+ // Wash away the block there, if possible:
+ if (CanWashAway(BlockType))
+ {
+ cBlockHandler * Handler = BlockHandler(BlockType);
+ if (Handler->DoesDropOnUnsuitable())
+ {
+ Handler->DropBlock(
+ &m_World, NULL,
+ a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX,
+ a_RelY,
+ a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ
+ );
+ }
+ } // if (CanWashAway)
+
+ // Spread:
+ FLOG(" Spreading to {%d, %d, %d} with meta %d",
+ a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX,
+ a_RelY,
+ a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ,
+ a_NewMeta
+ );
+ a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta);
+}
+
+
+
+
+
+bool cFloodyFluidSimulator::CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+{
+ FLOG(" Checking neighbors for source creation");
+
+ static const Vector3i NeighborCoords[] =
+ {
+ Vector3i(-1, 0, 0),
+ Vector3i( 1, 0, 0),
+ Vector3i( 0, 0, -1),
+ Vector3i( 0, 0, 1),
+ } ;
+
+ int NumNeeded = m_NumNeighborsForSource;
+ for (int i = 0; i < ARRAYCOUNT(NeighborCoords); i++)
+ {
+ int x = a_RelX + NeighborCoords[i].x;
+ int y = a_RelY + NeighborCoords[i].y;
+ int z = a_RelZ + NeighborCoords[i].z;
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_Chunk->UnboundedRelGetBlock(x, y, z, BlockType, BlockMeta))
+ {
+ // Neighbor not available, skip it
+ continue;
+ }
+ // FLOG(" Neighbor at {%d, %d, %d}: %s", x, y, z, ItemToFullString(cItem(BlockType, 1, BlockMeta)).c_str());
+ if ((BlockMeta == 0) && IsAnyFluidBlock(BlockType))
+ {
+ NumNeeded--;
+ // FLOG(" Found a neighbor source at {%d, %d, %d}, NumNeeded := %d", x, y, z, NumNeeded);
+ if (NumNeeded == 0)
+ {
+ // Found enough, turn into a source and bail out
+ // FLOG(" Found enough neighbor sources, turning into a source");
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, 0);
+ return true;
+ }
+ }
+ }
+ // FLOG(" Not enough neighbors for turning into a source, NumNeeded = %d", NumNeeded);
+ return false;
+}
+
+
+
+
diff --git a/src/Simulator/FloodyFluidSimulator.h b/src/Simulator/FloodyFluidSimulator.h
new file mode 100644
index 000000000..c4af2e246
--- /dev/null
+++ b/src/Simulator/FloodyFluidSimulator.h
@@ -0,0 +1,53 @@
+
+// FloodyFluidSimulator.h
+
+// Interfaces to the cFloodyFluidSimulator that represents a fluid simulator that tries to flood everything :)
+// http://forum.mc-server.org/showthread.php?tid=565
+
+
+
+
+
+#pragma once
+
+#include "DelayedFluidSimulator.h"
+
+
+
+
+
+// fwd:
+class cBlockArea;
+
+
+
+
+
+class cFloodyFluidSimulator :
+ public cDelayedFluidSimulator
+{
+ typedef cDelayedFluidSimulator super;
+
+public:
+ cFloodyFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, NIBBLETYPE a_Falloff, int a_TickDelay, int a_NumNeighborsForSource);
+
+protected:
+ NIBBLETYPE m_Falloff;
+ int m_NumNeighborsForSource;
+
+ // cDelayedFluidSimulator overrides:
+ virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override;
+
+ /// Checks tributaries, if not fed, decreases the block's level and returns true
+ bool CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta);
+
+ /// Spreads into the specified block, if the blocktype there allows. a_Area is for checking.
+ void SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta);
+
+ /// Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so
+ bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
+} ;
+
+
+
+
diff --git a/src/Simulator/FluidSimulator.cpp b/src/Simulator/FluidSimulator.cpp
new file mode 100644
index 000000000..dac666484
--- /dev/null
+++ b/src/Simulator/FluidSimulator.cpp
@@ -0,0 +1,212 @@
+
+#include "Globals.h"
+
+#include "FluidSimulator.h"
+#include "../World.h"
+
+
+
+
+
+cFluidSimulator::cFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) :
+ super(a_World),
+ m_FluidBlock(a_Fluid),
+ m_StationaryFluidBlock(a_StationaryFluid)
+{
+}
+
+
+
+
+
+bool cFluidSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
+{
+ return ((a_BlockType == m_FluidBlock) || (a_BlockType == m_StationaryFluidBlock));
+}
+
+
+
+
+
+bool cFluidSimulator::CanWashAway(BLOCKTYPE a_BlockType)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_BROWN_MUSHROOM:
+ case E_BLOCK_CACTUS:
+ case E_BLOCK_COBWEB:
+ case E_BLOCK_CROPS:
+ case E_BLOCK_DEAD_BUSH:
+ case E_BLOCK_RAIL:
+ case E_BLOCK_REDSTONE_TORCH_OFF:
+ case E_BLOCK_REDSTONE_TORCH_ON:
+ case E_BLOCK_REDSTONE_WIRE:
+ case E_BLOCK_RED_MUSHROOM:
+ case E_BLOCK_RED_ROSE:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_SUGARCANE:
+ case E_BLOCK_TALL_GRASS:
+ case E_BLOCK_TORCH:
+ case E_BLOCK_YELLOW_FLOWER:
+ {
+ return true;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+
+
+
+
+bool cFluidSimulator::IsSolidBlock(BLOCKTYPE a_BlockType)
+{
+ return !IsPassableForFluid(a_BlockType);
+}
+
+
+
+
+
+bool cFluidSimulator::IsPassableForFluid(BLOCKTYPE a_BlockType)
+{
+ return (
+ (a_BlockType == E_BLOCK_AIR) ||
+ (a_BlockType == E_BLOCK_FIRE) ||
+ IsAllowedBlock(a_BlockType) ||
+ CanWashAway(a_BlockType)
+ );
+}
+
+
+
+
+
+bool cFluidSimulator::IsHigherMeta(NIBBLETYPE a_Meta1, NIBBLETYPE a_Meta2)
+{
+ if (a_Meta1 == 0)
+ {
+ // Source block is higher than anything, even itself.
+ return true;
+ }
+ if ((a_Meta1 & 0x08) != 0)
+ {
+ // Falling fluid is higher than anything, including self
+ return true;
+ }
+
+ if (a_Meta2 == 0)
+ {
+ // Second block is a source and first block isn't
+ return false;
+ }
+ if ((a_Meta2 & 0x08) != 0)
+ {
+ // Second block is falling and the first one is neither a source nor falling
+ return false;
+ }
+
+ // All special cases have been handled, now it's just a raw comparison:
+ return (a_Meta1 < a_Meta2);
+}
+
+
+
+
+
+// TODO Not working very well yet :s
+Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over)
+{
+ if ((a_Y < 0) || (a_Y >= cChunkDef::Height))
+ {
+ return NONE;
+ }
+ BLOCKTYPE BlockID = m_World.GetBlock(a_X, a_Y, a_Z);
+ if (!IsAllowedBlock(BlockID)) // No Fluid -> No Flowing direction :D
+ {
+ return NONE;
+ }
+
+ /*
+ Disabled because of causing problems and being useless atm
+ char BlockBelow = m_World.GetBlock(a_X, a_Y - 1, a_Z); //If there is nothing or fluid below it -> dominating flow is down :D
+ if (BlockBelow == E_BLOCK_AIR || IsAllowedBlock(BlockBelow))
+ return Y_MINUS;
+ */
+
+ NIBBLETYPE LowestPoint = m_World.GetBlockMeta(a_X, a_Y, a_Z); //Current Block Meta so only lower points will be counted
+ int X = 0, Y = 0, Z = 0; //Lowest Pos will be stored here
+
+ if (IsAllowedBlock(m_World.GetBlock(a_X, a_Y + 1, a_Z)) && a_Over) //check for upper block to flow because this also affects the flowing direction
+ {
+ return GetFlowingDirection(a_X, a_Y + 1, a_Z, false);
+ }
+
+ std::vector< Vector3i * > Points;
+
+ Points.reserve(4); //Already allocate 4 places :D
+
+ //add blocks around the checking pos
+ Points.push_back(new Vector3i(a_X - 1, a_Y, a_Z));
+ Points.push_back(new Vector3i(a_X + 1, a_Y, a_Z));
+ Points.push_back(new Vector3i(a_X, a_Y, a_Z + 1));
+ Points.push_back(new Vector3i(a_X, a_Y, a_Z - 1));
+
+ for (std::vector<Vector3i *>::iterator it = Points.begin(); it < Points.end(); it++)
+ {
+ Vector3i *Pos = (*it);
+ char BlockID = m_World.GetBlock(Pos->x, Pos->y, Pos->z);
+ if(IsAllowedBlock(BlockID))
+ {
+ char Meta = m_World.GetBlockMeta(Pos->x, Pos->y, Pos->z);
+
+ if(Meta > LowestPoint)
+ {
+ LowestPoint = Meta;
+ X = Pos->x;
+ Y = Pos->y;
+ Z = Pos->z;
+ }
+ }else if(BlockID == E_BLOCK_AIR)
+ {
+ LowestPoint = 9; //This always dominates
+ X = Pos->x;
+ Y = Pos->y;
+ Z = Pos->z;
+
+ }
+ delete Pos;
+ }
+
+ if (LowestPoint == m_World.GetBlockMeta(a_X, a_Y, a_Z))
+ return NONE;
+
+ if (a_X - X > 0)
+ {
+ return X_MINUS;
+ }
+
+ if (a_X - X < 0)
+ {
+ return X_PLUS;
+ }
+
+ if (a_Z - Z > 0)
+ {
+ return Z_MINUS;
+ }
+
+ if (a_Z - Z < 0)
+ {
+ return Z_PLUS;
+ }
+
+ return NONE;
+}
+
+
+
+
diff --git a/src/Simulator/FluidSimulator.h b/src/Simulator/FluidSimulator.h
new file mode 100644
index 000000000..672b740a2
--- /dev/null
+++ b/src/Simulator/FluidSimulator.h
@@ -0,0 +1,75 @@
+
+#pragma once
+
+#include "Simulator.h"
+
+
+
+
+
+enum Direction
+{
+ X_PLUS,
+ X_MINUS,
+ Y_PLUS,
+ Y_MINUS,
+ Z_PLUS,
+ Z_MINUS,
+ NONE
+};
+
+
+
+
+
+/** This is a base class for all fluid simulator data classes.
+Needed so that cChunk can properly delete instances of fluid simulator data, no matter what simulator it's using
+*/
+class cFluidSimulatorData
+{
+public:
+ virtual ~cFluidSimulatorData() {}
+} ;
+
+
+
+
+
+class cFluidSimulator :
+ public cSimulator
+{
+ typedef cSimulator super;
+
+public:
+ cFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid);
+
+ // cSimulator overrides:
+ virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override;
+
+ /// Gets the flowing direction. If a_Over is true also the block over the current block affects the direction (standard)
+ virtual Direction GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over = true);
+
+ /// Creates a ChunkData object for the simulator to use. The simulator returns the correct object type.
+ virtual cFluidSimulatorData * CreateChunkData(void) { return NULL; }
+
+ bool IsFluidBlock (BLOCKTYPE a_BlockType) const { return (a_BlockType == m_FluidBlock); }
+ bool IsStationaryFluidBlock(BLOCKTYPE a_BlockType) const { return (a_BlockType == m_StationaryFluidBlock); }
+ bool IsAnyFluidBlock (BLOCKTYPE a_BlockType) const { return ((a_BlockType == m_FluidBlock) || (a_BlockType == m_StationaryFluidBlock)); }
+
+ static bool CanWashAway(BLOCKTYPE a_BlockType);
+
+ bool IsSolidBlock (BLOCKTYPE a_BlockType);
+ bool IsPassableForFluid(BLOCKTYPE a_BlockType);
+
+ /// Returns true if a_Meta1 is a higher fluid than a_Meta2. Takes source blocks into account.
+ bool IsHigherMeta(NIBBLETYPE a_Meta1, NIBBLETYPE a_Meta2);
+
+protected:
+ BLOCKTYPE m_FluidBlock; // The fluid block type that needs simulating
+ BLOCKTYPE m_StationaryFluidBlock; // The fluid block type that indicates no simulation is needed
+} ;
+
+
+
+
+
diff --git a/src/Simulator/NoopFluidSimulator.h b/src/Simulator/NoopFluidSimulator.h
new file mode 100644
index 000000000..8f894433f
--- /dev/null
+++ b/src/Simulator/NoopFluidSimulator.h
@@ -0,0 +1,36 @@
+
+// NoopFluidSimulator.h
+
+// Declares the cNoopFluidSimulator class representing a fluid simulator that performs nothing, it ignores all blocks
+
+
+
+
+
+#pragma once
+
+#include "FluidSimulator.h"
+
+
+
+
+
+class cNoopFluidSimulator :
+ public cFluidSimulator
+{
+ typedef cFluidSimulator super;
+
+public:
+ cNoopFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) :
+ super(a_World, a_Fluid, a_StationaryFluid)
+ {
+ }
+
+ // 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 {}
+} ;
+
+
+
+
diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp
new file mode 100644
index 000000000..906961490
--- /dev/null
+++ b/src/Simulator/RedstoneSimulator.cpp
@@ -0,0 +1,1073 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "RedstoneSimulator.h"
+#include "../BlockEntities/DropSpenserEntity.h"
+#include "../Entities/TNTEntity.h"
+#include "../Blocks/BlockTorch.h"
+#include "../Blocks/BlockDoor.h"
+#include "../Piston.h"
+
+
+
+
+
+cRedstoneSimulator::cRedstoneSimulator(cWorld & a_World)
+ : super(a_World)
+{
+}
+
+
+
+
+
+cRedstoneSimulator::~cRedstoneSimulator()
+{
+}
+
+
+
+
+
+void cRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+{
+ if ((a_Chunk == NULL) || !a_Chunk->IsValid())
+ {
+ return;
+ }
+ else if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height))
+ {
+ return;
+ }
+
+ int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
+
+ if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_BlockY, RelZ)))
+ {
+ return;
+ }
+
+ // Check for duplicates:
+ cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
+ for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr)
+ {
+ if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ))
+ {
+ return;
+ }
+ }
+
+ ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ));
+}
+
+
+
+
+
+void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+{
+ cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
+ if (ChunkData.empty())
+ {
+ return;
+ }
+
+ int BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
+ int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width;
+
+ for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(), end = ChunkData.end(); dataitr != end;)
+ {
+ BLOCKTYPE BlockType = a_Chunk->GetBlock(dataitr->x, dataitr->y, dataitr->z);
+ if (!IsAllowedBlock(BlockType))
+ {
+ dataitr = ChunkData.erase(dataitr);
+ continue;
+ }
+
+ // Check to see if PoweredBlocks have invalid items (source is air or an unpowered source)
+ for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end();)
+ {
+ sPoweredBlocks & Change = *itr;
+ BLOCKTYPE SourceBlockType = m_World.GetBlock(Change.a_SourcePos);
+
+ if (SourceBlockType != Change.a_SourceBlock)
+ {
+ itr = m_PoweredBlocks.erase(itr);
+ }
+ else if (
+ // Changeable sources
+ ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (m_World.GetBlockMeta(Change.a_SourcePos) == 0)) ||
+ ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(m_World.GetBlockMeta(Change.a_SourcePos))) ||
+ ((SourceBlockType == E_BLOCK_DETECTOR_RAIL) && (m_World.GetBlockMeta(Change.a_SourcePos) & 0x08) == 0x08) ||
+ (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(m_World.GetBlockMeta(Change.a_SourcePos))))
+ )
+ {
+ itr = m_PoweredBlocks.erase(itr);
+ }
+ else
+ {
+ itr++;
+ }
+ }
+
+ // Check to see if LinkedPoweredBlocks have invalid items: source, block powered through, or power destination block has changed
+ for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end();)
+ {
+ sLinkedPoweredBlocks & Change = *itr;
+ BLOCKTYPE SourceBlockType = m_World.GetBlock(Change.a_SourcePos);
+ BLOCKTYPE MiddleBlockType = m_World.GetBlock(Change.a_MiddlePos);
+
+ if (SourceBlockType != Change.a_SourceBlock)
+ {
+ itr = m_LinkedPoweredBlocks.erase(itr);
+ }
+ else if (MiddleBlockType != Change.a_MiddleBlock)
+ {
+ itr = m_LinkedPoweredBlocks.erase(itr);
+ }
+ else if (
+ // Things that can send power through a block but which depends on meta
+ ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (m_World.GetBlockMeta(Change.a_SourcePos) == 0)) ||
+ ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(m_World.GetBlockMeta(Change.a_SourcePos))) ||
+ (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(m_World.GetBlockMeta(Change.a_SourcePos))))
+ )
+ {
+ itr = m_LinkedPoweredBlocks.erase(itr);
+ }
+ else
+ {
+ itr++;
+ }
+ }
+
+ // PoweredBlock list was fine, now to the actual handling
+ int a_X = BaseX + dataitr->x;
+ int a_Z = BaseZ + dataitr->z;
+ switch (BlockType)
+ {
+ case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(a_X, dataitr->y, a_Z); break;
+ case E_BLOCK_LEVER: HandleRedstoneLever(a_X, dataitr->y, a_Z); break;
+ case E_BLOCK_TNT: HandleTNT(a_X, dataitr->y, a_Z); break;
+ case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(a_X, dataitr->y, a_Z); break;
+
+ case E_BLOCK_REDSTONE_TORCH_OFF:
+ case E_BLOCK_REDSTONE_TORCH_ON:
+ {
+ HandleRedstoneTorch(a_X, dataitr->y, a_Z, BlockType);
+ break;
+ }
+ case E_BLOCK_STONE_BUTTON:
+ case E_BLOCK_WOODEN_BUTTON:
+ {
+ HandleRedstoneButton(a_X, dataitr->y, a_Z, BlockType);
+ break;
+ }
+ case E_BLOCK_REDSTONE_REPEATER_OFF:
+ case E_BLOCK_REDSTONE_REPEATER_ON:
+ {
+ HandleRedstoneRepeater(a_X, dataitr->y, a_Z, BlockType);
+ break;
+ }
+ case E_BLOCK_PISTON:
+ case E_BLOCK_STICKY_PISTON:
+ {
+ HandlePiston(a_X, dataitr->y, a_Z);
+ break;
+ }
+ case E_BLOCK_REDSTONE_LAMP_OFF:
+ case E_BLOCK_REDSTONE_LAMP_ON:
+ {
+ HandleRedstoneLamp(a_X, dataitr->y, a_Z, BlockType);
+ break;
+ }
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER:
+ {
+ HandleDropSpenser(a_X, dataitr->y, a_Z);
+ break;
+ }
+ case E_BLOCK_WOODEN_DOOR:
+ case E_BLOCK_IRON_DOOR:
+ {
+ HandleDoor(a_X, dataitr->y, a_Z);
+ break;
+ }
+ case E_BLOCK_ACTIVATOR_RAIL:
+ case E_BLOCK_DETECTOR_RAIL:
+ case E_BLOCK_POWERED_RAIL:
+ {
+ HandleRail(a_X, dataitr->y, a_Z, BlockType);
+ break;
+ }
+ }
+
+ ++dataitr;
+ }
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState)
+{
+ static const struct // Define which directions the torch can power
+ {
+ int x, y, z;
+ } gCrossCoords[] =
+ {
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ { 0, 1, 0},
+ } ;
+
+ if (a_MyState == E_BLOCK_REDSTONE_TORCH_ON)
+ {
+ // Check if the block the torch is on is powered
+ int X = a_BlockX; int Y = a_BlockY; int Z = a_BlockZ;
+ AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)), true); // Inverse true to get the block torch is on
+
+ if (AreCoordsPowered(X, Y, Z))
+ {
+ // There was a match, torch goes off
+ // FastSetBlock so the server doesn't fail an assert -_-
+ m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_OFF, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ));
+ return;
+ }
+
+ // Torch still on, make all 4(X, Z) + 1(Y) sides powered
+ for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ {
+ BLOCKTYPE Type = m_World.GetBlock(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z);
+ if (i < ARRAYCOUNT(gCrossCoords) - 1) // Sides of torch, not top (top is last)
+ {
+ if (
+ ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) && // Is it a mechanism or wire? Not block/other torch etc.
+ (!Vector3i(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on
+ )
+ {
+ SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON);
+ }
+ }
+ else
+ {
+ // Top side, power whatever is there, including blocks
+ SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON);
+ }
+ }
+
+ if (m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) != 0x5) // Is torch standing on ground? If not (i.e. on wall), power block beneath
+ {
+ BLOCKTYPE Type = m_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
+
+ if ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) // Still can't make a normal block powered though!
+ {
+ SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON);
+ }
+ }
+ }
+ else
+ {
+ // Check if the block the torch is on is powered
+ int X = a_BlockX; int Y = a_BlockY; int Z = a_BlockZ;
+ AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)), true); // Inverse true to get the block torch is on
+
+ // See if off state torch can be turned on again
+ if (AreCoordsPowered(X, Y, Z))
+ {
+ return; // Something matches, torch still powered
+ }
+
+ // Block torch on not powered, can be turned on again!
+ // FastSetBlock so the server doesn't fail an assert -_-
+ m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ));
+ }
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ static const struct // Define which directions the redstone block can power
+ {
+ int x, y, z;
+ } gCrossCoords[] =
+ {
+ { 0, 0, 0}, // Oh, anomalous redstone. Only block that powers itself
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ { 0, 1, 0},
+ { 0,-1, 0},
+ } ;
+
+ for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ {
+ // Power everything
+ SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BLOCK_OF_REDSTONE);
+ }
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ if (IsLeverOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)))
+ {
+ static const struct // Define which directions the redstone lever can power (all sides)
+ {
+ int x, y, z;
+ } gCrossCoords[] =
+ {
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ { 0, 1, 0},
+ { 0,-1, 0},
+ } ;
+
+ for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ {
+ // Power everything
+ SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LEVER);
+ }
+
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_LEVER);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_LEVER);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, E_BLOCK_LEVER);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, E_BLOCK_LEVER);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_LEVER);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_LEVER);
+ }
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
+{
+ if (IsButtonOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)))
+ {
+ static const struct // Define which directions the redstone button can power (all sides)
+ {
+ int x, y, z;
+ } gCrossCoords[] =
+ {
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ { 0, 1, 0},
+ { 0,-1, 0},
+ } ;
+
+ for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ {
+ // Power everything
+ SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, a_BlockType);
+ }
+
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, a_BlockType);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, a_BlockType);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, a_BlockType);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, a_BlockType);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, a_BlockType);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, a_BlockType);
+ }
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ static const struct // Define which directions the wire can receive power from
+ {
+ int x, y, z;
+ } gCrossCoords[] =
+ {
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ { 1, 1, 0}, // From here to end, check for wire placed on sides of blocks
+ {-1, 1, 0},
+ { 0, 1, 1},
+ { 0, 1, -1},
+ { 1,-1, 0},
+ {-1,-1, 0},
+ { 0,-1, 1},
+ { 0,-1, -1},
+ } ;
+
+ // Check to see if directly beside a power source
+ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ))
+ {
+ m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 15); // Maximum power
+ }
+ else
+ {
+ NIBBLETYPE MyMeta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ int TimesMetaSmaller = 0, TimesFoundAWire = 0;
+
+ for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through all directions to transfer or receive power
+ {
+ BLOCKTYPE SurroundType;
+ NIBBLETYPE SurroundMeta;
+ m_World.GetBlockTypeMeta(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, SurroundType, SurroundMeta);
+
+ if (SurroundType == E_BLOCK_REDSTONE_WIRE)
+ {
+ TimesFoundAWire++;
+
+ if (SurroundMeta > 1) // Wires of power 1 or 0 cannot transfer power TO ME, don't bother checking
+ {
+ if (SurroundMeta > MyMeta) // Does surrounding wire have a higher power level than self?
+ {
+ m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, SurroundMeta - 1);
+ }
+ }
+
+ if (SurroundMeta < MyMeta) // Go through all surroundings to see if self power is larger than everyone else's
+ {
+ TimesMetaSmaller++;
+ }
+ }
+ }
+
+ if (TimesMetaSmaller == TimesFoundAWire)
+ {
+ // All surrounding metas were smaller - self must have been a wire that was
+ // transferring power to other wires around.
+ // However, self not directly powered anymore, so source must have been removed,
+ // therefore, self must be set to meta zero
+ m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0);
+ }
+ }
+
+ if (m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) != 0) // A powered wire
+ {
+ //SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); // No matter what, block underneath gets powered
+
+ switch (GetWireDirection(a_BlockX, a_BlockY, a_BlockZ))
+ {
+ case REDSTONE_NONE:
+ {
+ static const struct // Define which directions the redstone wire can power
+ {
+ int x, y, z;
+ } gCrossCoords[] =
+ {
+ { 1, 0, 0}, // Power block in front
+ { 2, 0, 0}, // Power block in front of that (strongly power)
+ {-1, 0, 0},
+ {-2, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, 2},
+ { 0, 0, -1},
+ { 0, 0, -2},
+ { 0, 1, 0},
+ { 0, 2, 0},
+ { 0,-1, 0},
+ { 0,-2, 0},
+ } ;
+
+ for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ {
+ // Power if block is solid, CURRENTLY all mechanisms are solid
+ if (g_BlockIsSolid[m_World.GetBlock(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z)])
+ {
+ SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
+ }
+ }
+ break;
+ }
+ case REDSTONE_X_POS:
+ {
+ if (m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) != E_BLOCK_REDSTONE_WIRE)
+ {
+ SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
+ }
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_WIRE);
+ break;
+ }
+ case REDSTONE_X_NEG:
+ {
+ if (m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) != E_BLOCK_REDSTONE_WIRE)
+ {
+ SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
+ }
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_WIRE);
+ break;
+ }
+ case REDSTONE_Z_POS:
+ {
+ if (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) != E_BLOCK_REDSTONE_WIRE)
+ {
+ SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
+ }
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_WIRE);
+ break;
+ }
+ case REDSTONE_Z_NEG:
+ {
+ if (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) != E_BLOCK_REDSTONE_WIRE)
+ {
+ SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
+ }
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_REDSTONE_WIRE);
+ break;
+ }
+ }
+ }
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState)
+{
+ NIBBLETYPE a_Meta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if (a_MyState == E_BLOCK_REDSTONE_REPEATER_OFF)
+ {
+ if (IsRepeaterPowered(a_BlockX, a_BlockY, a_BlockZ, a_Meta & 0x3))
+ {
+ m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON, a_Meta);
+ switch (a_Meta & 0x3) // We only want the direction (bottom) bits
+ {
+ case 0x0:
+ {
+ SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_REDSTONE_REPEATER_ON);
+ break;
+ }
+ case 0x1:
+ {
+ SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_REPEATER_ON);
+ break;
+ }
+ case 0x2:
+ {
+ SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_REPEATER_ON);
+ break;
+ }
+ case 0x3:
+ {
+ SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON);
+ SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_REPEATER_ON);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!IsRepeaterPowered(a_BlockX, a_BlockY, a_BlockZ, a_Meta & 0x3))
+ {
+ m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, a_Meta);
+ }
+ }
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ))
+ {
+ cPiston Piston(&m_World);
+ Piston.ExtendPiston(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ else
+ {
+ cPiston Piston(&m_World);
+ Piston.RetractPiston(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ class cSetPowerToDropSpenser :
+ public cDropSpenserCallback
+ {
+ bool m_IsPowered;
+ public:
+ cSetPowerToDropSpenser(bool a_IsPowered) : m_IsPowered(a_IsPowered) {}
+
+ virtual bool Item(cDropSpenserEntity * a_DropSpenser) override
+ {
+ a_DropSpenser->SetRedstonePower(m_IsPowered);
+ return false;
+ }
+ } DrSpSP (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ));
+
+ m_World.DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, DrSpSP);
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState)
+{
+ if (a_MyState == E_BLOCK_REDSTONE_LAMP_OFF)
+ {
+ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ))
+ {
+ m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_LAMP_ON, 0);
+ }
+ }
+ else
+ {
+ if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ))
+ {
+ m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_LAMP_OFF, 0);
+ }
+ }
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ))
+ {
+ m_World.BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f);
+ m_World.SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom
+ m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
+ }
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x08) == 0x08)
+ {
+ // Block position is located at top half of door
+ // Is Y - 1 both within world boundaries, a door block, and the bottom half of a door?
+ // The bottom half stores the open/closed information
+ if (
+ (a_BlockY - 1 >= 0) &&
+ ((m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) || (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_IRON_DOOR)) &&
+ (m_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ & 0x08) == 0)
+ )
+ {
+ if ((m_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ) & 0x04) == 0) // Closed door?
+ {
+ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Powered? If so, toggle open
+ {
+ cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+ else // Opened door
+ {
+ if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Unpowered? Close if so
+ {
+ cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x04) == 0) // Closed door?
+ {
+ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Powered? If so, toggle open
+ {
+ cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+ else // Opened door
+ {
+ if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Unpowered? Close if so
+ {
+ cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+ }
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType)
+{
+ switch (a_MyType)
+ {
+ case E_BLOCK_DETECTOR_RAIL:
+ {
+ if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x08) == 0x08)
+ {
+ static const struct // Define which directions the rail can power (all sides)
+ {
+ int x, y, z;
+ } gCrossCoords[] =
+ {
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ { 0, 1, 0},
+ { 0,-1, 0},
+ } ;
+
+ for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ {
+ // Power everything
+ SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, a_MyType);
+ }
+ }
+ break;
+ }
+ case E_BLOCK_ACTIVATOR_RAIL:
+ case E_BLOCK_POWERED_RAIL:
+ {
+ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ))
+ {
+ m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x08);
+ }
+ else
+ {
+ m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x07);
+ }
+ break;
+ }
+ }
+}
+
+
+
+
+
+bool cRedstoneSimulator::AreCoordsPowered(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list
+ {
+ sPoweredBlocks & Change = *itr;
+
+ if (Change.a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
+ {
+ return true;
+ }
+ }
+
+ for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) // Check linked powered list
+ {
+ sLinkedPoweredBlocks & Change = *itr;
+
+ if (Change.a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cRedstoneSimulator::IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta)
+{
+ // Check through powered blocks list
+ for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr)
+ {
+ sPoweredBlocks & Change = *itr;
+
+ switch (a_Meta)
+ {
+ case 0x0:
+ {
+ // Flip the coords to check the back of the repeater
+ if (Change.a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ + 1))) { return true; }
+ break;
+ }
+ case 0x1:
+ {
+ if (Change.a_SourcePos.Equals(Vector3i(a_BlockX - 1, a_BlockY, a_BlockZ))) { return true; }
+ break;
+ }
+ case 0x2:
+ {
+ if (Change.a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ - 1))) { return true; }
+ break;
+ }
+ case 0x3:
+ {
+ if (Change.a_SourcePos.Equals(Vector3i(a_BlockX + 1, a_BlockY, a_BlockZ))) { return true; }
+ break;
+ }
+ }
+ }
+
+ // Check linked powered list, 'middle' blocks
+ for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr)
+ {
+ sLinkedPoweredBlocks & Change = *itr;
+
+ switch (a_Meta)
+ {
+ case 0x0:
+ {
+ if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ + 1))) { return true; }
+ break;
+ }
+ case 0x1:
+ {
+ if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX - 1, a_BlockY, a_BlockZ))) { return true; }
+ break;
+ }
+ case 0x2:
+ {
+ if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ - 1))) { return true; }
+ break;
+ }
+ case 0x3:
+ {
+ if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX + 1, a_BlockY, a_BlockZ))) { return true; }
+ break;
+ }
+ }
+ }
+ return false; // Couldn't find power source behind repeater
+}
+
+
+
+
+
+void cRedstoneSimulator::SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceType)
+{
+ switch (a_Direction)
+ {
+ case BLOCK_FACE_XM:
+ {
+ BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ);
+ if (!g_BlockIsSolid[MiddleBlock]) { return; }
+
+ SetBlockLinkedPowered(a_BlockX - 2, a_BlockY, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX - 1, a_BlockY + 1, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX - 1, a_BlockY - 1, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ + 1, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ - 1, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ break;
+ }
+ case BLOCK_FACE_XP:
+ {
+ BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ);
+ if (!g_BlockIsSolid[MiddleBlock]) { return; }
+
+ SetBlockLinkedPowered(a_BlockX + 2, a_BlockY, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX + 1, a_BlockY + 1, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX + 1, a_BlockY - 1, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ + 1, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ - 1, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ break;
+ }
+ case BLOCK_FACE_YM:
+ {
+ BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
+ if (!g_BlockIsSolid[MiddleBlock]) { return; }
+
+ SetBlockLinkedPowered(a_BlockX, a_BlockY - 2, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX + 1, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX - 1, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ + 1, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ - 1, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ break;
+ }
+ case BLOCK_FACE_YP:
+ {
+ BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
+ if (!g_BlockIsSolid[MiddleBlock]) { return; }
+
+ SetBlockLinkedPowered(a_BlockX, a_BlockY + 2, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX + 1, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX - 1, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ + 1, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ - 1, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ break;
+ }
+ case BLOCK_FACE_ZM:
+ {
+ BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1);
+ if (!g_BlockIsSolid[MiddleBlock]) { return; }
+
+ SetBlockLinkedPowered(a_BlockX, a_BlockY, a_BlockZ - 2, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ break;
+ }
+ case BLOCK_FACE_ZP:
+ {
+ BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1);
+ if (!g_BlockIsSolid[MiddleBlock]) { return; }
+
+ SetBlockLinkedPowered(a_BlockX, a_BlockY, a_BlockZ + 2, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock);
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled face direction when attempting to set blocks as linked powered!");
+ break;
+ }
+ }
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock)
+{
+ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { return; } // Check for duplicates
+
+ sPoweredBlocks RC;
+ RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
+ RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ);
+ RC.a_SourceBlock = a_SourceBlock;
+ m_PoweredBlocks.push_back(RC);
+ return;
+}
+
+
+
+
+
+void cRedstoneSimulator::SetBlockLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ,
+ int a_MiddleX, int a_MiddleY, int a_MiddleZ,
+ int a_SourceX, int a_SourceY, int a_SourceZ,
+ BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddleBlock
+ )
+{
+ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { return; } // Check for duplicates
+
+ sLinkedPoweredBlocks RC;
+ RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
+ RC.a_MiddlePos = Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ);
+ RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ);
+ RC.a_SourceBlock = a_SourceBlock;
+ RC.a_MiddleBlock = a_MiddleBlock;
+ m_LinkedPoweredBlocks.push_back(RC);
+ return;
+}
+
+
+
+
+
+cRedstoneSimulator::eRedstoneDirection cRedstoneSimulator::GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ int Dir = REDSTONE_NONE;
+
+ BLOCKTYPE NegX = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ);
+ if (IsPotentialSource(NegX))
+ {
+ Dir |= (REDSTONE_X_POS);
+ }
+
+ BLOCKTYPE PosX = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ);
+ if (IsPotentialSource(PosX))
+ {
+ Dir |= (REDSTONE_X_NEG);
+ }
+
+ BLOCKTYPE NegZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1);
+ if (IsPotentialSource(NegZ))
+ {
+ if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner
+ {
+ Dir ^= REDSTONE_X_POS;
+ Dir |= REDSTONE_X_NEG;
+ }
+ if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner
+ {
+ Dir ^= REDSTONE_X_NEG;
+ Dir |= REDSTONE_X_POS;
+ }
+ Dir |= REDSTONE_Z_POS;
+ }
+
+ BLOCKTYPE PosZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1);
+ if (IsPotentialSource(PosZ))
+ {
+ if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner
+ {
+ Dir ^= REDSTONE_X_POS;
+ Dir |= REDSTONE_X_NEG;
+ }
+ if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner
+ {
+ Dir ^= REDSTONE_X_NEG;
+ Dir |= REDSTONE_X_POS;
+ }
+ Dir |= REDSTONE_Z_NEG;
+ }
+ return (eRedstoneDirection)Dir;
+}
+
+
+
+
+
+bool cRedstoneSimulator::IsLeverOn(NIBBLETYPE a_BlockMeta)
+{
+ // Extract the ON bit from metadata and return if true if it is set:
+ return ((a_BlockMeta & 0x8) == 0x8);
+}
+
+
+
+
+
+bool cRedstoneSimulator::IsButtonOn(NIBBLETYPE a_BlockMeta)
+{
+ return IsLeverOn(a_BlockMeta);
+}
+
+
+
+
diff --git a/src/Simulator/RedstoneSimulator.h b/src/Simulator/RedstoneSimulator.h
new file mode 100644
index 000000000..d68c6daeb
--- /dev/null
+++ b/src/Simulator/RedstoneSimulator.h
@@ -0,0 +1,199 @@
+
+#pragma once
+
+#include "Simulator.h"
+
+/// Per-chunk data for the simulator, specified individual chunks to simulate; 'Data' is not used
+typedef cCoordWithIntList cRedstoneSimulatorChunkData;
+
+
+
+
+
+class cRedstoneSimulator :
+ public cSimulator
+{
+ typedef cSimulator super;
+public:
+
+ cRedstoneSimulator(cWorld & a_World);
+ ~cRedstoneSimulator();
+
+ virtual void Simulate(float a_Dt) override {}; // Not used in this simulator
+ virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
+ virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType ) override { return IsRedstone(a_BlockType); }
+
+ enum eRedstoneDirection
+ {
+ REDSTONE_NONE = 0,
+ REDSTONE_X_POS = 0x1,
+ REDSTONE_X_NEG = 0x2,
+ REDSTONE_Z_POS = 0x4,
+ REDSTONE_Z_NEG = 0x8,
+ };
+ eRedstoneDirection GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ);
+ eRedstoneDirection GetWireDirection(const Vector3i & a_Pos) { return GetWireDirection(a_Pos.x, a_Pos.y, a_Pos.z); }
+
+private:
+
+ struct sPoweredBlocks // Define structure of the directly powered blocks list
+ {
+ Vector3i a_BlockPos; // Position of powered block
+ Vector3i a_SourcePos; // Position of source powering the block at a_BlockPos
+ BLOCKTYPE a_SourceBlock; // The source block type (for pistons pushing away sources and replacing with non source etc.)
+ };
+
+ struct sLinkedPoweredBlocks // Define structure of the indirectly powered blocks list (i.e. repeaters powering through a block to the block at the other side)
+ {
+ Vector3i a_BlockPos;
+ Vector3i a_MiddlePos;
+ Vector3i a_SourcePos;
+ BLOCKTYPE a_SourceBlock;
+ BLOCKTYPE a_MiddleBlock;
+ };
+
+ typedef std::vector <sPoweredBlocks> PoweredBlocksList;
+ typedef std::vector <sLinkedPoweredBlocks> LinkedBlocksList;
+
+ PoweredBlocksList m_PoweredBlocks;
+ LinkedBlocksList m_LinkedPoweredBlocks;
+
+ virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
+
+ // We want a_MyState for devices needing a full FastSetBlock (as opposed to meta) because with our simulation model, we cannot keep setting the block if it is already set correctly
+ // In addition to being non-performant, it would stop the player from actually breaking said device
+
+ /* ====== SOURCES ====== */
+ ///<summary>Handles the redstone torch</summary>
+ void HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState);
+ ///<summary>Handles the redstone block</summary>
+ void HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
+ ///<summary>Handles levers</summary>
+ void HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ);
+ ///<summary>Handles buttons</summary>
+ void HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
+ /* ==================== */
+
+ /* ====== CARRIERS ====== */
+ ///<summary>Handles redstone wire</summary>
+ void HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ);
+ ///<summary>Handles repeaters</summary>
+ void HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState);
+ /* ====================== */
+
+ /* ====== DEVICES ====== */
+ ///<summary>Handles pistons</summary>
+ void HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ);
+ ///<summary>Handles dispensers and droppers</summary>
+ void HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ);
+ ///<summary>Handles TNT (exploding)</summary>
+ void HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ);
+ ///<summary>Handles redstone lamps</summary>
+ void HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState);
+ ///<summary>Handles doords</summary>
+ void HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ);
+ ///<summary>Handles activator, detector, and powered rails</summary>
+ void HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType);
+ /* ===================== */
+
+ /* ====== Helper functions ====== */
+ void SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock);
+ void SetBlockLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_MiddleX, int a_MiddleY, int a_MiddleZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddeBlock);
+ void SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceType);
+
+ bool AreCoordsPowered(int a_BlockX, int a_BlockY, int a_BlockZ);
+ bool IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta);
+
+ bool IsLeverOn(NIBBLETYPE a_BlockMeta);
+ bool IsButtonOn(NIBBLETYPE a_BlockMeta);
+ /* ============================== */
+
+ inline static bool IsMechanism(BLOCKTYPE Block)
+ {
+ switch (Block)
+ {
+ case E_BLOCK_PISTON:
+ case E_BLOCK_STICKY_PISTON:
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER:
+ case E_BLOCK_TNT:
+ case E_BLOCK_REDSTONE_LAMP_OFF:
+ case E_BLOCK_REDSTONE_LAMP_ON:
+ case E_BLOCK_WOODEN_DOOR:
+ case E_BLOCK_IRON_DOOR:
+ case E_BLOCK_REDSTONE_REPEATER_OFF:
+ case E_BLOCK_REDSTONE_REPEATER_ON:
+ case E_BLOCK_POWERED_RAIL:
+ {
+ return true;
+ }
+ default: return false;
+ }
+ }
+
+ inline static bool IsPotentialSource(BLOCKTYPE Block)
+ {
+ switch (Block)
+ {
+ case E_BLOCK_WOODEN_BUTTON:
+ case E_BLOCK_STONE_BUTTON:
+ case E_BLOCK_REDSTONE_WIRE:
+ case E_BLOCK_REDSTONE_TORCH_OFF:
+ case E_BLOCK_REDSTONE_TORCH_ON:
+ case E_BLOCK_LEVER:
+ case E_BLOCK_REDSTONE_REPEATER_ON:
+ case E_BLOCK_REDSTONE_REPEATER_OFF:
+ case E_BLOCK_BLOCK_OF_REDSTONE:
+ case E_BLOCK_ACTIVE_COMPARATOR:
+ case E_BLOCK_INACTIVE_COMPARATOR:
+ {
+ return true;
+ }
+ default: return false;
+ }
+ }
+
+ inline static bool IsRedstone(BLOCKTYPE Block)
+ {
+ switch (Block)
+ {
+ // All redstone devices, please alpha sort
+ case E_BLOCK_ACTIVATOR_RAIL:
+ case E_BLOCK_ACTIVE_COMPARATOR:
+ case E_BLOCK_BLOCK_OF_REDSTONE:
+ case E_BLOCK_DETECTOR_RAIL:
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DAYLIGHT_SENSOR:
+ case E_BLOCK_DROPPER:
+ case E_BLOCK_FENCE_GATE:
+ case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE:
+ case E_BLOCK_HOPPER:
+ case E_BLOCK_INACTIVE_COMPARATOR:
+ case E_BLOCK_IRON_DOOR:
+ case E_BLOCK_LEVER:
+ case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
+ case E_BLOCK_NOTE_BLOCK:
+ case E_BLOCK_REDSTONE_LAMP_OFF:
+ case E_BLOCK_REDSTONE_LAMP_ON:
+ case E_BLOCK_REDSTONE_REPEATER_OFF:
+ case E_BLOCK_REDSTONE_REPEATER_ON:
+ case E_BLOCK_REDSTONE_TORCH_OFF:
+ case E_BLOCK_REDSTONE_TORCH_ON:
+ case E_BLOCK_REDSTONE_WIRE:
+ case E_BLOCK_STICKY_PISTON:
+ case E_BLOCK_STONE_BUTTON:
+ case E_BLOCK_STONE_PRESSURE_PLATE:
+ case E_BLOCK_TNT:
+ case E_BLOCK_TRAPDOOR:
+ case E_BLOCK_TRIPWIRE_HOOK:
+ case E_BLOCK_WOODEN_BUTTON:
+ case E_BLOCK_WOODEN_DOOR:
+ case E_BLOCK_WOODEN_PRESSURE_PLATE:
+ case E_BLOCK_PISTON:
+ {
+ return true;
+ }
+ default: return false;
+ }
+ }
+}; \ No newline at end of file
diff --git a/src/Simulator/SandSimulator.cpp b/src/Simulator/SandSimulator.cpp
new file mode 100644
index 000000000..87fb83357
--- /dev/null
+++ b/src/Simulator/SandSimulator.cpp
@@ -0,0 +1,309 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "SandSimulator.h"
+#include "../World.h"
+#include "../BlockID.h"
+#include "../Defines.h"
+#include "../Entities/FallingBlock.h"
+#include "../Chunk.h"
+
+
+
+
+
+cSandSimulator::cSandSimulator(cWorld & a_World, cIniFile & a_IniFile) :
+ cSimulator(a_World),
+ m_TotalBlocks(0)
+{
+ m_IsInstantFall = a_IniFile.GetValueSetB("Physics", "SandInstantFall", false);
+}
+
+
+
+
+
+void cSandSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+{
+ cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData();
+ if (ChunkData.empty())
+ {
+ return;
+ }
+
+ int BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
+ int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width;
+ for (cSandSimulatorChunkData::const_iterator itr = ChunkData.begin(), end = ChunkData.end(); itr != end; ++itr)
+ {
+ BLOCKTYPE BlockType = a_Chunk->GetBlock(itr->x, itr->y, itr->z);
+ if (!IsAllowedBlock(BlockType) || (itr->y <= 0))
+ {
+ continue;
+ }
+
+ BLOCKTYPE BlockBelow = (itr->y > 0) ? a_Chunk->GetBlock(itr->x, itr->y - 1, itr->z) : E_BLOCK_AIR;
+ if (CanStartFallingThrough(BlockBelow))
+ {
+ if (m_IsInstantFall)
+ {
+ DoInstantFall(a_Chunk, itr->x, itr->y, itr->z);
+ continue;
+ }
+ Vector3i Pos;
+ Pos.x = itr->x + BaseX;
+ Pos.y = itr->y;
+ Pos.z = itr->z + BaseZ;
+ /*
+ LOGD(
+ "Creating a falling block at {%d, %d, %d} of type %s, block below: %s",
+ Pos.x, Pos.y, Pos.z, ItemTypeToString(BlockType).c_str(), ItemTypeToString(BlockBelow).c_str()
+ );
+ */
+ cFallingBlock * FallingBlock = new cFallingBlock(Pos, BlockType, a_Chunk->GetMeta(itr->x, itr->y, itr->z));
+ FallingBlock->Initialize(&m_World);
+ a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0);
+ }
+ }
+ m_TotalBlocks -= ChunkData.size();
+ ChunkData.clear();
+}
+
+
+
+
+
+bool cSandSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_SAND:
+ case E_BLOCK_GRAVEL:
+ case E_BLOCK_ANVIL:
+ case E_BLOCK_DRAGON_EGG:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+void cSandSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+{
+ if ((a_Chunk == NULL) || !a_Chunk->IsValid())
+ {
+ return;
+ }
+ int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
+ if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_BlockY, RelZ)))
+ {
+ return;
+ }
+
+ // Check for duplicates:
+ cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData();
+ for (cSandSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr)
+ {
+ if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ))
+ {
+ return;
+ }
+ }
+
+ m_TotalBlocks += 1;
+ ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ));
+}
+
+
+
+
+
+bool cSandSimulator::CanStartFallingThrough(BLOCKTYPE a_BlockType)
+{
+ // Please keep the list alpha-sorted
+ switch (a_BlockType)
+ {
+ case E_BLOCK_AIR:
+ case E_BLOCK_FIRE:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_STATIONARY_LAVA:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_WATER:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cSandSimulator::CanContinueFallThrough(BLOCKTYPE a_BlockType)
+{
+ // Please keep the list alpha-sorted
+ switch (a_BlockType)
+ {
+ case E_BLOCK_AIR:
+ case E_BLOCK_BROWN_MUSHROOM:
+ case E_BLOCK_COBWEB:
+ case E_BLOCK_CROPS:
+ case E_BLOCK_DEAD_BUSH:
+ case E_BLOCK_DETECTOR_RAIL:
+ case E_BLOCK_FIRE:
+ case E_BLOCK_FLOWER_POT:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_LEVER:
+ case E_BLOCK_MINECART_TRACKS:
+ case E_BLOCK_MELON_STEM:
+ case E_BLOCK_POWERED_RAIL:
+ case E_BLOCK_PUMPKIN_STEM:
+ case E_BLOCK_REDSTONE_REPEATER_OFF:
+ case E_BLOCK_REDSTONE_REPEATER_ON:
+ case E_BLOCK_REDSTONE_TORCH_OFF:
+ case E_BLOCK_REDSTONE_TORCH_ON:
+ case E_BLOCK_REDSTONE_WIRE:
+ case E_BLOCK_RED_MUSHROOM:
+ case E_BLOCK_RED_ROSE:
+ case E_BLOCK_SIGN_POST:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_STATIONARY_LAVA:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_STONE_BUTTON:
+ case E_BLOCK_STONE_PRESSURE_PLATE:
+ case E_BLOCK_TALL_GRASS:
+ case E_BLOCK_TORCH:
+ case E_BLOCK_TRAPDOOR:
+ case E_BLOCK_TRIPWIRE:
+ case E_BLOCK_TRIPWIRE_HOOK:
+ case E_BLOCK_WALLSIGN:
+ case E_BLOCK_WATER:
+ case E_BLOCK_WOODEN_BUTTON:
+ case E_BLOCK_WOODEN_PRESSURE_PLATE:
+ case E_BLOCK_YELLOW_FLOWER:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cSandSimulator::IsReplacedOnRematerialization(BLOCKTYPE a_BlockType)
+{
+ // Please keep the list alpha-sorted
+ switch (a_BlockType)
+ {
+ case E_BLOCK_AIR:
+ case E_BLOCK_DEAD_BUSH:
+ case E_BLOCK_FIRE:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_STATIONARY_LAVA:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_TALL_GRASS:
+ case E_BLOCK_WATER:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cSandSimulator::DoesBreakFallingThrough(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_STONE_SLAB:
+ case E_BLOCK_WOODEN_SLAB:
+ {
+ return ((a_BlockMeta & 0x08) == 0); // Only a bottom-slab breaks the block
+ }
+ }
+ return false;
+}
+
+
+
+
+
+void cSandSimulator::FinishFalling(
+ cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ,
+ BLOCKTYPE a_FallingBlockType, NIBBLETYPE a_FallingBlockMeta
+)
+{
+ ASSERT(a_BlockY < cChunkDef::Height);
+
+ BLOCKTYPE CurrentBlockType = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ if ((a_FallingBlockType == E_BLOCK_ANVIL) || IsReplacedOnRematerialization(CurrentBlockType))
+ {
+ // Rematerialize the material here:
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FallingBlockType, a_FallingBlockMeta);
+ return;
+ }
+
+ // Create a pickup instead:
+ cItems Pickups;
+ Pickups.Add((ENUM_ITEM_ID)a_FallingBlockType, 1, a_FallingBlockMeta);
+ a_World->SpawnItemPickups(Pickups, (double)a_BlockX + 0.5, (double)a_BlockY + 0.5, (double)a_BlockZ + 0.5);
+}
+
+
+
+
+
+void cSandSimulator::DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+{
+ // Remove the original block:
+ BLOCKTYPE FallingBlockType;
+ NIBBLETYPE FallingBlockMeta;
+ a_Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, FallingBlockType, FallingBlockMeta);
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
+
+ // Search for a place to put it:
+ for (int y = a_RelY - 1; y >= 0; y--)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ a_Chunk->GetBlockTypeMeta(a_RelX, y, a_RelZ, BlockType, BlockMeta);
+ int BlockY;
+ if (DoesBreakFallingThrough(BlockType, BlockMeta))
+ {
+ BlockY = y;
+ }
+ else if (!CanContinueFallThrough(BlockType))
+ {
+ BlockY = y + 1;
+ }
+ else
+ {
+ // Can fall further down
+ continue;
+ }
+
+ // Finish the fall at the found bottom:
+ int BlockX = a_RelX + a_Chunk->GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk->GetPosZ() * cChunkDef::Width;
+ FinishFalling(&m_World, BlockX, BlockY, BlockZ, FallingBlockType, FallingBlockMeta);
+ return;
+ }
+
+ // The block just "fell off the world" without leaving a trace
+}
+
+
+
+
diff --git a/src/Simulator/SandSimulator.h b/src/Simulator/SandSimulator.h
new file mode 100644
index 000000000..6e9ea15ac
--- /dev/null
+++ b/src/Simulator/SandSimulator.h
@@ -0,0 +1,63 @@
+
+#pragma once
+
+#include "Simulator.h"
+
+
+
+
+
+/// Despite the class name, this simulator takes care of all blocks that fall when suspended in the air.
+class cSandSimulator :
+ public cSimulator
+{
+public:
+ cSandSimulator(cWorld & a_World, cIniFile & a_IniFile);
+
+ // cSimulator overrides:
+ virtual void Simulate(float a_Dt) override {} // Unused in this simulator
+ virtual void SimulateChunk(float 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
+ static bool CanStartFallingThrough(BLOCKTYPE a_BlockType);
+
+ /// Returns true if an already-falling block can pass through the specified block type (e. g. torch)
+ static bool CanContinueFallThrough(BLOCKTYPE a_BlockType);
+
+ /// Returns true if the falling block rematerializing will replace the specified block type (e. g. tall grass)
+ static bool IsReplacedOnRematerialization(BLOCKTYPE a_BlockType);
+
+ /// Returns true if the specified block breaks falling blocks while they fall through it (e. g. halfslabs)
+ static bool DoesBreakFallingThrough(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /** Called when a block finishes falling at the specified coords, either by insta-fall,
+ or through cFallingBlock entity.
+ It either rematerializes the block (a_FallingBlockType) at the specified coords, or creates a pickup,
+ based on the block currently present in the world at the dest specified coords
+ */
+ static void FinishFalling(
+ cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ,
+ BLOCKTYPE a_FallingBlockType, NIBBLETYPE a_FallingBlockMeta
+ );
+
+protected:
+ bool m_IsInstantFall; // If set to true, blocks don't fall using cFallingBlock entity, but instantly instead
+
+ int m_TotalBlocks; // Total number of blocks currently in the queue for simulating
+
+ virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
+
+ /// Performs the instant fall of the block - removes it from top, Finishes it at the bottom
+ void DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
+};
+
+
+
+
+/// Per-chunk data for the simulator, specified individual chunks to simulate; Data is not used
+typedef cCoordWithIntList cSandSimulatorChunkData;
+
+
+
+
diff --git a/src/Simulator/Simulator.cpp b/src/Simulator/Simulator.cpp
new file mode 100644
index 000000000..06fd0f858
--- /dev/null
+++ b/src/Simulator/Simulator.cpp
@@ -0,0 +1,51 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Simulator.h"
+#include "../World.h"
+#include "../Vector3i.h"
+#include "../BlockID.h"
+#include "../Defines.h"
+#include "../Chunk.h"
+
+
+
+
+
+cSimulator::cSimulator(cWorld & a_World)
+ : m_World(a_World)
+{
+}
+
+
+
+
+
+cSimulator::~cSimulator()
+{
+}
+
+
+
+
+
+void cSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+{
+ AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
+ AddBlock(a_BlockX - 1, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 1, a_BlockZ));
+ AddBlock(a_BlockX + 1, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 1, a_BlockZ));
+ AddBlock(a_BlockX, a_BlockY, a_BlockZ - 1, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 1));
+ AddBlock(a_BlockX, a_BlockY, a_BlockZ + 1, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 1));
+ if (a_BlockY > 0)
+ {
+ AddBlock(a_BlockX, a_BlockY - 1, a_BlockZ, a_Chunk);
+ }
+ if (a_BlockY < cChunkDef::Height - 1)
+ {
+ AddBlock(a_BlockX, a_BlockY + 1, a_BlockZ, a_Chunk);
+ }
+}
+
+
+
+
diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h
new file mode 100644
index 000000000..4f555693d
--- /dev/null
+++ b/src/Simulator/Simulator.h
@@ -0,0 +1,46 @@
+
+#pragma once
+
+#include "../Vector3i.h"
+#include "lib/inifile/iniFile.h"
+
+
+
+
+
+class cWorld;
+class cChunk;
+
+
+
+
+
+class cSimulator
+{
+public:
+ cSimulator(cWorld & a_World);
+ virtual ~cSimulator();
+
+ /// Called in each tick, a_Dt is the time passed since the last tick, in msec
+ 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) {};
+
+ /// Called when a block changes
+ virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk);
+
+ virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) = 0;
+
+protected:
+ friend class cChunk; // Calls AddBlock() in its WakeUpSimulators() function, to speed things up
+
+ /// Called to simulate a new block
+ virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) = 0;
+
+ cWorld & m_World;
+} ;
+
+
+
+
diff --git a/src/Simulator/SimulatorManager.cpp b/src/Simulator/SimulatorManager.cpp
new file mode 100644
index 000000000..2bc483cbd
--- /dev/null
+++ b/src/Simulator/SimulatorManager.cpp
@@ -0,0 +1,80 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "SimulatorManager.h"
+#include "../World.h"
+
+
+
+
+
+cSimulatorManager::cSimulatorManager(cWorld & a_World) :
+ m_World(a_World),
+ m_Ticks(0)
+{
+}
+
+
+
+
+
+cSimulatorManager::~cSimulatorManager()
+{
+}
+
+
+
+
+
+void cSimulatorManager::Simulate(float a_Dt)
+{
+ m_Ticks++;
+ for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr )
+ {
+ if ((m_Ticks % itr->second) == 0)
+ {
+ itr->first->Simulate(a_Dt);
+ }
+ }
+}
+
+
+
+
+
+void cSimulatorManager::SimulateChunk(float 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 )
+ {
+ if ((m_Ticks % itr->second) == 0)
+ {
+ itr->first->SimulateChunk(a_Dt, a_ChunkX, a_ChunkZ, a_Chunk);
+ }
+ }
+}
+
+
+
+
+
+void cSimulatorManager::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+{
+ for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr )
+ {
+ itr->first->WakeUp(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
+ }
+}
+
+
+
+
+
+void cSimulatorManager::RegisterSimulator(cSimulator * a_Simulator, int a_Rate)
+{
+ m_Simulators.push_back(std::make_pair(a_Simulator, a_Rate));
+}
+
+
+
+
diff --git a/src/Simulator/SimulatorManager.h b/src/Simulator/SimulatorManager.h
new file mode 100644
index 000000000..31a709316
--- /dev/null
+++ b/src/Simulator/SimulatorManager.h
@@ -0,0 +1,52 @@
+
+// cSimulatorManager.h
+
+
+
+
+#pragma once
+
+
+
+
+#include "Simulator.h"
+
+
+
+
+
+// fwd: Chunk.h
+class cChunk;
+
+// fwd: World.h
+class cWorld;
+
+
+
+
+
+class cSimulatorManager
+{
+public:
+ cSimulatorManager(cWorld & a_World);
+ ~cSimulatorManager();
+
+ void Simulate(float a_Dt);
+
+ void SimulateChunk(float 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);
+
+ void RegisterSimulator(cSimulator * a_Simulator, int a_Rate); // Takes ownership of the simulator object!
+
+protected:
+ typedef std::vector <std::pair<cSimulator *, int> > cSimulators;
+
+ cWorld & m_World;
+ cSimulators m_Simulators;
+ long long m_Ticks;
+};
+
+
+
+
diff --git a/src/Simulator/VaporizeFluidSimulator.cpp b/src/Simulator/VaporizeFluidSimulator.cpp
new file mode 100644
index 000000000..4206c64d1
--- /dev/null
+++ b/src/Simulator/VaporizeFluidSimulator.cpp
@@ -0,0 +1,53 @@
+
+// VaporizeFluidSimulator.cpp
+
+// Implements the cVaporizeFluidSimulator class representing a fluid simulator that replaces all fluid blocks with air
+
+#include "Globals.h"
+#include "VaporizeFluidSimulator.h"
+#include "../Chunk.h"
+
+
+
+
+
+cVaporizeFluidSimulator::cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) :
+ super(a_World, a_Fluid, a_StationaryFluid)
+{
+}
+
+
+
+
+
+void cVaporizeFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+{
+ if (a_Chunk == NULL)
+ {
+ return;
+ }
+ int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
+ BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
+ if (
+ (BlockType == m_FluidBlock) ||
+ (BlockType == m_StationaryFluidBlock)
+ )
+ {
+ a_Chunk->SetBlock(RelX, a_BlockY, RelZ, E_BLOCK_AIR, 0);
+ a_Chunk->BroadcastSoundEffect("random.fizz", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.6f);
+ }
+}
+
+
+
+
+
+void cVaporizeFluidSimulator::Simulate(float a_Dt)
+{
+ // Nothing needed
+}
+
+
+
+
diff --git a/src/Simulator/VaporizeFluidSimulator.h b/src/Simulator/VaporizeFluidSimulator.h
new file mode 100644
index 000000000..c8eb7802b
--- /dev/null
+++ b/src/Simulator/VaporizeFluidSimulator.h
@@ -0,0 +1,34 @@
+
+// VaporizeFluidSimulator.h
+
+// Declares the cVaporizeFluidSimulator class representing a fluid simulator that replaces all fluid blocks with air
+// Useful for water simulation in the Nether
+
+
+
+
+
+#pragma once
+
+#include "FluidSimulator.h"
+
+
+
+
+
+class cVaporizeFluidSimulator :
+ public cFluidSimulator
+{
+ typedef cFluidSimulator super;
+
+public:
+ cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid);
+
+ // 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;
+} ;
+
+
+
+
diff --git a/src/StackWalker.cpp b/src/StackWalker.cpp
new file mode 100644
index 000000000..bf18b9fc9
--- /dev/null
+++ b/src/StackWalker.cpp
@@ -0,0 +1,1345 @@
+/**********************************************************************
+ *
+ * StackWalker.cpp
+ *
+ *
+ * History:
+ * 2005-07-27 v1 - First public release on http://www.codeproject.com/
+ * http://www.codeproject.com/threads/StackWalker.asp
+ * 2005-07-28 v2 - Changed the params of the constructor and ShowCallstack
+ * (to simplify the usage)
+ * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL
+ * (should also be enough)
+ * - Changed to compile correctly with the PSDK of VC7.0
+ * (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined:
+ * it uses LPSTR instead of LPCSTR as first paremeter)
+ * - Added declarations to support VC5/6 without using 'dbghelp.h'
+ * - Added a 'pUserData' member to the ShowCallstack function and the
+ * PReadProcessMemoryRoutine declaration (to pass some user-defined data,
+ * which can be used in the readMemoryFunction-callback)
+ * 2005-08-02 v4 - OnSymInit now also outputs the OS-Version by default
+ * - Added example for doing an exception-callstack-walking in main.cpp
+ * (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268)
+ * 2005-08-05 v5 - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse!
+ * 2008-08-04 v6 - Fixed Bug: Missing LEAK-end-tag
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2502890#xx2502890xx
+ * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN"
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx
+ * Fixed Bug: Compiling with "/Wall"
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx
+ * Fixed Bug: Now checking SymUseSymSrv
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1388979#xx1388979xx
+ * Fixed Bug: Support for recursive function calls
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1434538#xx1434538xx
+ * Fixed Bug: Missing FreeLibrary call in "GetModuleListTH32"
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1326923#xx1326923xx
+ * Fixed Bug: SymDia is number 7, not 9!
+ * 2008-09-11 v7 For some (undocumented) reason, dbhelp.h is needing a packing of 8!
+ * Thanks to Teajay which reported the bug...
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2718933#xx2718933xx
+ * 2008-11-27 v8 Debugging Tools for Windows are now stored in a different directory
+ * Thanks to Luiz Salamon which reported this "bug"...
+ * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2822736#xx2822736xx
+ * 2009-04-10 v9 License slihtly corrected (<ORGANIZATION> replaced)
+ * 2010-04-15 v10 Added support for VS2010 RTM
+ * 2010-05-2ß v11 Now using secure MyStrcCpy. Thanks to luke.simon:
+ * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3477467#xx3477467xx
+ *
+ * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Copyright (c) 2005-2010, Jochen Kalmbach
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of Jochen Kalmbach nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <stdlib.h>
+#pragma comment(lib, "version.lib") // for "VerQueryValue"
+#pragma warning(disable:4826)
+
+#include "StackWalker.h"
+
+
+// If VC7 and later, then use the shipped 'dbghelp.h'-file
+#pragma pack(push,8)
+#if _MSC_VER >= 1300
+#include <dbghelp.h>
+#else
+// inline the important dbghelp.h-declarations...
+typedef enum {
+ SymNone = 0,
+ SymCoff,
+ SymCv,
+ SymPdb,
+ SymExport,
+ SymDeferred,
+ SymSym,
+ SymDia,
+ SymVirtual,
+ NumSymTypes
+} SYM_TYPE;
+typedef struct _IMAGEHLP_LINE64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
+ PVOID Key; // internal
+ DWORD LineNumber; // line number in file
+ PCHAR FileName; // full filename
+ DWORD64 Address; // first instruction of line
+} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
+typedef struct _IMAGEHLP_MODULE64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ CHAR LoadedImageName[256]; // symbol file name
+} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
+typedef struct _IMAGEHLP_SYMBOL64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)
+ DWORD64 Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ CHAR Name[1]; // symbol name (null terminated string)
+} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
+typedef enum {
+ AddrMode1616,
+ AddrMode1632,
+ AddrModeReal,
+ AddrModeFlat
+} ADDRESS_MODE;
+typedef struct _tagADDRESS64 {
+ DWORD64 Offset;
+ WORD Segment;
+ ADDRESS_MODE Mode;
+} ADDRESS64, *LPADDRESS64;
+typedef struct _KDHELP64 {
+ DWORD64 Thread;
+ DWORD ThCallbackStack;
+ DWORD ThCallbackBStore;
+ DWORD NextCallback;
+ DWORD FramePointer;
+ DWORD64 KiCallUserMode;
+ DWORD64 KeUserCallbackDispatcher;
+ DWORD64 SystemRangeStart;
+ DWORD64 Reserved[8];
+} KDHELP64, *PKDHELP64;
+typedef struct _tagSTACKFRAME64 {
+ ADDRESS64 AddrPC; // program counter
+ ADDRESS64 AddrReturn; // return address
+ ADDRESS64 AddrFrame; // frame pointer
+ ADDRESS64 AddrStack; // stack pointer
+ ADDRESS64 AddrBStore; // backing store pointer
+ PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
+ DWORD64 Params[4]; // possible arguments to the function
+ BOOL Far; // WOW far call
+ BOOL Virtual; // is this a virtual frame?
+ DWORD64 Reserved[3];
+ KDHELP64 KdHelp;
+} STACKFRAME64, *LPSTACKFRAME64;
+typedef
+BOOL
+(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
+ HANDLE hProcess,
+ DWORD64 qwBaseAddress,
+ PVOID lpBuffer,
+ DWORD nSize,
+ LPDWORD lpNumberOfBytesRead
+ );
+typedef
+PVOID
+(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
+ HANDLE hProcess,
+ DWORD64 AddrBase
+ );
+typedef
+DWORD64
+(__stdcall *PGET_MODULE_BASE_ROUTINE64)(
+ HANDLE hProcess,
+ DWORD64 Address
+ );
+typedef
+DWORD64
+(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
+ HANDLE hProcess,
+ HANDLE hThread,
+ LPADDRESS64 lpaddr
+ );
+#define SYMOPT_CASE_INSENSITIVE 0x00000001
+#define SYMOPT_UNDNAME 0x00000002
+#define SYMOPT_DEFERRED_LOADS 0x00000004
+#define SYMOPT_NO_CPP 0x00000008
+#define SYMOPT_LOAD_LINES 0x00000010
+#define SYMOPT_OMAP_FIND_NEAREST 0x00000020
+#define SYMOPT_LOAD_ANYTHING 0x00000040
+#define SYMOPT_IGNORE_CVREC 0x00000080
+#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
+#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
+#define SYMOPT_EXACT_SYMBOLS 0x00000400
+#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
+#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
+#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
+#define SYMOPT_PUBLICS_ONLY 0x00004000
+#define SYMOPT_NO_PUBLICS 0x00008000
+#define SYMOPT_AUTO_PUBLICS 0x00010000
+#define SYMOPT_NO_IMAGE_SEARCH 0x00020000
+#define SYMOPT_SECURE 0x00040000
+#define SYMOPT_DEBUG 0x80000000
+#define UNDNAME_COMPLETE (0x0000) // Enable full undecoration
+#define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration;
+#endif // _MSC_VER < 1300
+#pragma pack(pop)
+
+// Some missing defines (for VC5/6):
+#ifndef INVALID_FILE_ATTRIBUTES
+#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+
+
+// secure-CRT_functions are only available starting with VC8
+#if _MSC_VER < 1400
+#define strcpy_s strcpy
+#define strncpy_s strncpy
+#define strcat_s(dst, len, src) strcat(dst, src)
+#define _snprintf_s _snprintf
+#define _tcscat_s _tcscat
+#endif
+
+static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc)
+{
+ if (nMaxDestSize <= 0) return;
+ if (strlen(szSrc) < nMaxDestSize)
+ {
+ strcpy_s(szDest, nMaxDestSize, szSrc);
+ }
+ else
+ {
+ strncpy_s(szDest, nMaxDestSize, szSrc, nMaxDestSize);
+ szDest[nMaxDestSize-1] = 0;
+ }
+} // MyStrCpy
+
+// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
+#define USED_CONTEXT_FLAGS CONTEXT_FULL
+
+
+class StackWalkerInternal
+{
+public:
+ StackWalkerInternal(StackWalker *parent, HANDLE hProcess)
+ {
+ m_parent = parent;
+ m_hDbhHelp = NULL;
+ pSC = NULL;
+ m_hProcess = hProcess;
+ m_szSymPath = NULL;
+ pSFTA = NULL;
+ pSGLFA = NULL;
+ pSGMB = NULL;
+ pSGMI = NULL;
+ pSGO = NULL;
+ pSGSFA = NULL;
+ pSI = NULL;
+ pSLM = NULL;
+ pSSO = NULL;
+ pSW = NULL;
+ pUDSN = NULL;
+ pSGSP = NULL;
+ }
+ ~StackWalkerInternal()
+ {
+ if (pSC != NULL)
+ pSC(m_hProcess); // SymCleanup
+ if (m_hDbhHelp != NULL)
+ FreeLibrary(m_hDbhHelp);
+ m_hDbhHelp = NULL;
+ m_parent = NULL;
+ if(m_szSymPath != NULL)
+ free(m_szSymPath);
+ m_szSymPath = NULL;
+ }
+ BOOL Init(LPCSTR szSymPath)
+ {
+ if (m_parent == NULL)
+ return FALSE;
+ // Dynamically load the Entry-Points for dbghelp.dll:
+ // First try to load the newsest one from
+ TCHAR szTemp[4096];
+ // But before wqe do this, we first check if the ".local" file exists
+ if (GetModuleFileName(NULL, szTemp, 4096) > 0)
+ {
+ _tcscat_s(szTemp, _T(".local"));
+ if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES)
+ {
+ // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"
+ // Ok, first try the new path according to the archtitecture:
+#ifdef _M_IX86
+ if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
+ {
+ _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll"));
+ // now check if the file exists:
+ if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
+ {
+ m_hDbhHelp = LoadLibrary(szTemp);
+ }
+ }
+#elif _M_X64
+ if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
+ {
+ _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll"));
+ // now check if the file exists:
+ if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
+ {
+ m_hDbhHelp = LoadLibrary(szTemp);
+ }
+ }
+#elif _M_IA64
+ if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
+ {
+ _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll"));
+ // now check if the file exists:
+ if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
+ {
+ m_hDbhHelp = LoadLibrary(szTemp);
+ }
+ }
+#endif
+ // If still not found, try the old directories...
+ if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
+ {
+ _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll"));
+ // now check if the file exists:
+ if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
+ {
+ m_hDbhHelp = LoadLibrary(szTemp);
+ }
+ }
+#if defined _M_X64 || defined _M_IA64
+ // Still not found? Then try to load the (old) 64-Bit version:
+ if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) )
+ {
+ _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));
+ if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
+ {
+ m_hDbhHelp = LoadLibrary(szTemp);
+ }
+ }
+#endif
+ }
+ }
+ if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one
+ m_hDbhHelp = LoadLibrary( _T("dbghelp.dll") );
+ if (m_hDbhHelp == NULL)
+ return FALSE;
+ pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" );
+ pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" );
+
+ pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" );
+ pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" );
+ pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" );
+
+ pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" );
+ pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" );
+ pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" );
+ pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );
+ //pSGMI_V3 = (tSGMI_V3) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );
+ pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" );
+ pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" );
+ pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" );
+ pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" );
+
+ if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
+ pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
+ pSW == NULL || pUDSN == NULL || pSLM == NULL )
+ {
+ FreeLibrary(m_hDbhHelp);
+ m_hDbhHelp = NULL;
+ pSC = NULL;
+ return FALSE;
+ }
+
+ // SymInitialize
+ if (szSymPath != NULL)
+ m_szSymPath = _strdup(szSymPath);
+ if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE)
+ this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
+
+ DWORD symOptions = this->pSGO(); // SymGetOptions
+ symOptions |= SYMOPT_LOAD_LINES;
+ symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
+ //symOptions |= SYMOPT_NO_PROMPTS;
+ // SymSetOptions
+ symOptions = this->pSSO(symOptions);
+
+ char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
+ if (this->pSGSP != NULL)
+ {
+ if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE)
+ this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
+ }
+ char szUserName[1024] = {0};
+ DWORD dwSize = 1024;
+ GetUserNameA(szUserName, &dwSize);
+ this->m_parent->OnSymInit(buf, symOptions, szUserName);
+
+ return TRUE;
+ }
+
+ StackWalker *m_parent;
+
+ HMODULE m_hDbhHelp;
+ HANDLE m_hProcess;
+ LPSTR m_szSymPath;
+
+/*typedef struct IMAGEHLP_MODULE64_V3 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ // new elements: 07-Jun-2002
+ CHAR LoadedImageName[256]; // symbol file name
+ CHAR LoadedPdbName[256]; // pdb file name
+ DWORD CVSig; // Signature of the CV record in the debug directories
+ CHAR CVData[MAX_PATH * 3]; // Contents of the CV record
+ DWORD PdbSig; // Signature of PDB
+ GUID PdbSig70; // Signature of PDB (VC 7 and up)
+ DWORD PdbAge; // DBI age of pdb
+ BOOL PdbUnmatched; // loaded an unmatched pdb
+ BOOL DbgUnmatched; // loaded an unmatched dbg
+ BOOL LineNumbers; // we have line number information
+ BOOL GlobalSymbols; // we have internal symbol information
+ BOOL TypeInfo; // we have type information
+ // new elements: 17-Dec-2003
+ BOOL SourceIndexed; // pdb supports source server
+ BOOL Publics; // contains public symbols
+};
+*/
+
+#pragma pack(push,8)
+typedef struct IMAGEHLP_MODULE64_V2 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
+ DWORD64 BaseOfImage; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ CHAR LoadedImageName[256]; // symbol file name
+};
+#pragma pack(pop)
+
+
+ // SymCleanup()
+ typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
+ tSC pSC;
+
+ // SymFunctionTableAccess64()
+ typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );
+ tSFTA pSFTA;
+
+ // SymGetLineFromAddr64()
+ typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
+ OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
+ tSGLFA pSGLFA;
+
+ // SymGetModuleBase64()
+ typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );
+ tSGMB pSGMB;
+
+ // SymGetModuleInfo64()
+ typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V2 *ModuleInfo );
+ tSGMI pSGMI;
+
+// // SymGetModuleInfo64()
+// typedef BOOL (__stdcall *tSGMI_V3)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo );
+// tSGMI_V3 pSGMI_V3;
+
+ // SymGetOptions()
+ typedef DWORD (__stdcall *tSGO)( VOID );
+ tSGO pSGO;
+
+ // SymGetSymFromAddr64()
+ typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
+ OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
+ tSGSFA pSGSFA;
+
+ // SymInitialize()
+ typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
+ tSI pSI;
+
+ // SymLoadModule64()
+ typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,
+ IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
+ tSLM pSLM;
+
+ // SymSetOptions()
+ typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
+ tSSO pSSO;
+
+ // StackWalk64()
+ typedef BOOL (__stdcall *tSW)(
+ DWORD MachineType,
+ HANDLE hProcess,
+ HANDLE hThread,
+ LPSTACKFRAME64 StackFrame,
+ PVOID ContextRecord,
+ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
+ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
+ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
+ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
+ tSW pSW;
+
+ // UnDecorateSymbolName()
+ typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,
+ DWORD UndecoratedLength, DWORD Flags );
+ tUDSN pUDSN;
+
+ typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);
+ tSGSP pSGSP;
+
+
+private:
+ // **************************************** ToolHelp32 ************************
+ #define MAX_MODULE_NAME32 255
+ #define TH32CS_SNAPMODULE 0x00000008
+ #pragma pack( push, 8 )
+ typedef struct tagMODULEENTRY32
+ {
+ DWORD dwSize;
+ DWORD th32ModuleID; // This module
+ DWORD th32ProcessID; // owning process
+ DWORD GlblcntUsage; // Global usage count on the module
+ DWORD ProccntUsage; // Module usage count in th32ProcessID's context
+ BYTE * modBaseAddr; // Base address of module in th32ProcessID's context
+ DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
+ HMODULE hModule; // The hModule of this module in th32ProcessID's context
+ char szModule[MAX_MODULE_NAME32 + 1];
+ char szExePath[MAX_PATH];
+ } MODULEENTRY32;
+ typedef MODULEENTRY32 * PMODULEENTRY32;
+ typedef MODULEENTRY32 * LPMODULEENTRY32;
+ #pragma pack( pop )
+
+ BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid)
+ {
+ // CreateToolhelp32Snapshot()
+ typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
+ // Module32First()
+ typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
+ // Module32Next()
+ typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
+
+ // try both dlls...
+ const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
+ HINSTANCE hToolhelp = NULL;
+ tCT32S pCT32S = NULL;
+ tM32F pM32F = NULL;
+ tM32N pM32N = NULL;
+
+ HANDLE hSnap;
+ MODULEENTRY32 me;
+ me.dwSize = sizeof(me);
+ BOOL keepGoing;
+ size_t i;
+
+ for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ )
+ {
+ hToolhelp = LoadLibrary( dllname[i] );
+ if (hToolhelp == NULL)
+ continue;
+ pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
+ pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
+ pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
+ if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) )
+ break; // found the functions!
+ FreeLibrary(hToolhelp);
+ hToolhelp = NULL;
+ }
+
+ if (hToolhelp == NULL)
+ return FALSE;
+
+ hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
+ if (hSnap == (HANDLE) -1)
+ {
+ FreeLibrary(hToolhelp);
+ return FALSE;
+ }
+
+ keepGoing = !!pM32F( hSnap, &me );
+ int cnt = 0;
+ while (keepGoing)
+ {
+ this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize);
+ cnt++;
+ keepGoing = !!pM32N( hSnap, &me );
+ }
+ CloseHandle(hSnap);
+ FreeLibrary(hToolhelp);
+ if (cnt <= 0)
+ return FALSE;
+ return TRUE;
+ } // GetModuleListTH32
+
+ // **************************************** PSAPI ************************
+ typedef struct _MODULEINFO {
+ LPVOID lpBaseOfDll;
+ DWORD SizeOfImage;
+ LPVOID EntryPoint;
+ } MODULEINFO, *LPMODULEINFO;
+
+ BOOL GetModuleListPSAPI(HANDLE hProcess)
+ {
+ // EnumProcessModules()
+ typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
+ // GetModuleFileNameEx()
+ typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
+ // GetModuleBaseName()
+ typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
+ // GetModuleInformation()
+ typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );
+
+ HINSTANCE hPsapi;
+ tEPM pEPM;
+ tGMFNE pGMFNE;
+ tGMBN pGMBN;
+ tGMI pGMI;
+
+ DWORD i;
+ //ModuleEntry e;
+ DWORD cbNeeded;
+ MODULEINFO mi;
+ HMODULE *hMods = 0;
+ char *tt = NULL;
+ char *tt2 = NULL;
+ const SIZE_T TTBUFLEN = 8096;
+ int cnt = 0;
+
+ hPsapi = LoadLibrary( _T("psapi.dll") );
+ if (hPsapi == NULL)
+ return FALSE;
+
+ pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
+ pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
+ pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
+ pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
+ if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) )
+ {
+ // we couldn´t find all functions
+ FreeLibrary(hPsapi);
+ return FALSE;
+ }
+
+ hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
+ tt = (char*) malloc(sizeof(char) * TTBUFLEN);
+ tt2 = (char*) malloc(sizeof(char) * TTBUFLEN);
+ if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) )
+ goto cleanup;
+
+ if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
+ {
+ //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
+ goto cleanup;
+ }
+
+ if ( cbNeeded > TTBUFLEN )
+ {
+ //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
+ goto cleanup;
+ }
+
+ for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
+ {
+ // base address, size
+ pGMI(hProcess, hMods[i], &mi, sizeof mi );
+ // image file name
+ tt[0] = 0;
+ pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );
+ // module name
+ tt2[0] = 0;
+ pGMBN(hProcess, hMods[i], tt2, TTBUFLEN );
+
+ DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage);
+ if (dwRes != ERROR_SUCCESS)
+ this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);
+ cnt++;
+ }
+
+ cleanup:
+ if (hPsapi != NULL) FreeLibrary(hPsapi);
+ if (tt2 != NULL) free(tt2);
+ if (tt != NULL) free(tt);
+ if (hMods != NULL) free(hMods);
+
+ return cnt != 0;
+ } // GetModuleListPSAPI
+
+ DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)
+ {
+ CHAR *szImg = _strdup(img);
+ CHAR *szMod = _strdup(mod);
+ DWORD result = ERROR_SUCCESS;
+ if ( (szImg == NULL) || (szMod == NULL) )
+ result = ERROR_NOT_ENOUGH_MEMORY;
+ else
+ {
+ if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
+ result = GetLastError();
+ }
+ ULONGLONG fileVersion = 0;
+ if ( (m_parent != NULL) && (szImg != NULL) )
+ {
+ // try to retrive the file-version:
+ if ( (this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0)
+ {
+ VS_FIXEDFILEINFO *fInfo = NULL;
+ DWORD dwHandle;
+ DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
+ if (dwSize > 0)
+ {
+ LPVOID vData = malloc(dwSize);
+ if (vData != NULL)
+ {
+ if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)
+ {
+ UINT len;
+ TCHAR szSubBlock[] = _T("\\");
+ if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0)
+ fInfo = NULL;
+ else
+ {
+ fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);
+ }
+ }
+ free(vData);
+ }
+ }
+ }
+
+ // Retrive some additional-infos about the module
+ IMAGEHLP_MODULE64_V2 Module;
+ const char *szSymType = "-unknown-";
+ if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)
+ {
+ switch(Module.SymType)
+ {
+ case SymNone:
+ szSymType = "-nosymbols-";
+ break;
+ case SymCoff: // 1
+ szSymType = "COFF";
+ break;
+ case SymCv: // 2
+ szSymType = "CV";
+ break;
+ case SymPdb: // 3
+ szSymType = "PDB";
+ break;
+ case SymExport: // 4
+ szSymType = "-exported-";
+ break;
+ case SymDeferred: // 5
+ szSymType = "-deferred-";
+ break;
+ case SymSym: // 6
+ szSymType = "SYM";
+ break;
+ case 7: // SymDia:
+ szSymType = "DIA";
+ break;
+ case 8: //SymVirtual:
+ szSymType = "Virtual";
+ break;
+ }
+ }
+ this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion);
+ }
+ if (szImg != NULL) free(szImg);
+ if (szMod != NULL) free(szMod);
+ return result;
+ }
+public:
+ BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId)
+ {
+ // first try toolhelp32
+ if (GetModuleListTH32(hProcess, dwProcessId))
+ return true;
+ // then try psapi
+ return GetModuleListPSAPI(hProcess);
+ }
+
+
+ BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V2 *pModuleInfo)
+ {
+ if(this->pSGMI == NULL)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+ // First try to use the larger ModuleInfo-Structure
+// memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3));
+// pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
+// if (this->pSGMI_V3 != NULL)
+// {
+// if (this->pSGMI_V3(hProcess, baseAddr, pModuleInfo) != FALSE)
+// return TRUE;
+// // check if the parameter was wrong (size is bad...)
+// if (GetLastError() != ERROR_INVALID_PARAMETER)
+// return FALSE;
+// }
+ // could not retrive the bigger structure, try with the smaller one (as defined in VC7.1)...
+ pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
+ void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
+ if (pData == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));
+ if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V2*) pData) != FALSE)
+ {
+ // only copy as much memory as is reserved...
+ memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));
+ pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
+ free(pData);
+ return TRUE;
+ }
+ free(pData);
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+};
+
+// #############################################################
+StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess)
+{
+ this->m_options = OptionsAll;
+ this->m_modulesLoaded = FALSE;
+ this->m_hProcess = hProcess;
+ this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
+ this->m_dwProcessId = dwProcessId;
+ this->m_szSymPath = NULL;
+ this->m_MaxRecursionCount = 1000;
+}
+StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)
+{
+ this->m_options = options;
+ this->m_modulesLoaded = FALSE;
+ this->m_hProcess = hProcess;
+ this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
+ this->m_dwProcessId = dwProcessId;
+ if (szSymPath != NULL)
+ {
+ this->m_szSymPath = _strdup(szSymPath);
+ this->m_options |= SymBuildPath;
+ }
+ else
+ this->m_szSymPath = NULL;
+ this->m_MaxRecursionCount = 1000;
+}
+
+StackWalker::~StackWalker()
+{
+ if (m_szSymPath != NULL)
+ free(m_szSymPath);
+ m_szSymPath = NULL;
+ if (this->m_sw != NULL)
+ delete this->m_sw;
+ this->m_sw = NULL;
+}
+
+BOOL StackWalker::LoadModules()
+{
+ if (this->m_sw == NULL)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+ if (m_modulesLoaded != FALSE)
+ return TRUE;
+
+ // Build the sym-path:
+ char *szSymPath = NULL;
+ if ( (this->m_options & SymBuildPath) != 0)
+ {
+ const size_t nSymPathLen = 4096;
+ szSymPath = (char*) malloc(nSymPathLen);
+ if (szSymPath == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ szSymPath[0] = 0;
+ // Now first add the (optional) provided sympath:
+ if (this->m_szSymPath != NULL)
+ {
+ strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+
+ strcat_s(szSymPath, nSymPathLen, ".;");
+
+ const size_t nTempLen = 1024;
+ char szTemp[nTempLen];
+ // Now add the current directory:
+ if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+
+ // Now add the path for the main-module:
+ if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p)
+ {
+ // locate the rightmost path separator
+ if ( (*p == '\\') || (*p == '/') || (*p == ':') )
+ {
+ *p = 0;
+ break;
+ }
+ } // for (search for path separator...)
+ if (strlen(szTemp) > 0)
+ {
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+ }
+ if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+ if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+ if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ // also add the "system32"-directory:
+ strcat_s(szTemp, nTempLen, "\\system32");
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, ";");
+ }
+
+ if ( (this->m_options & SymUseSymSrv) != 0)
+ {
+ if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)
+ {
+ szTemp[nTempLen-1] = 0;
+ strcat_s(szSymPath, nSymPathLen, "SRV*");
+ strcat_s(szSymPath, nSymPathLen, szTemp);
+ strcat_s(szSymPath, nSymPathLen, "\\websymbols");
+ strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");
+ }
+ else
+ strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
+ }
+ } // if SymBuildPath
+
+ // First Init the whole stuff...
+ BOOL bRet = this->m_sw->Init(szSymPath);
+ if (szSymPath != NULL) free(szSymPath); szSymPath = NULL;
+ if (bRet == FALSE)
+ {
+ this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+
+ bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId);
+ if (bRet != FALSE)
+ m_modulesLoaded = TRUE;
+ return bRet;
+}
+
+
+// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
+// This has to be done due to a problem with the "hProcess"-parameter in x64...
+// Because this class is in no case multi-threading-enabled (because of the limitations
+// of dbghelp.dll) it is "safe" to use a static-variable
+static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL;
+static LPVOID s_readMemoryFunction_UserData = NULL;
+
+BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData)
+{
+ CONTEXT c;
+ CallstackEntry csEntry;
+ IMAGEHLP_SYMBOL64 *pSym = NULL;
+ StackWalkerInternal::IMAGEHLP_MODULE64_V2 Module;
+ IMAGEHLP_LINE64 Line;
+ int frameNum;
+ bool bLastEntryCalled = true;
+ int curRecursionCount = 0;
+
+ if (m_modulesLoaded == FALSE)
+ this->LoadModules(); // ignore the result...
+
+ if (this->m_sw->m_hDbhHelp == NULL)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+
+ s_readMemoryFunction = readMemoryFunction;
+ s_readMemoryFunction_UserData = pUserData;
+
+ if (context == NULL)
+ {
+ // If no context is provided, capture the context
+ if (hThread == GetCurrentThread())
+ {
+ GET_CURRENT_CONTEXT(c, USED_CONTEXT_FLAGS);
+ }
+ else
+ {
+ SuspendThread(hThread);
+ memset(&c, 0, sizeof(CONTEXT));
+ c.ContextFlags = USED_CONTEXT_FLAGS;
+ if (GetThreadContext(hThread, &c) == FALSE)
+ {
+ ResumeThread(hThread);
+ return FALSE;
+ }
+ }
+ }
+ else
+ c = *context;
+
+ // init STACKFRAME for first call
+ STACKFRAME64 s; // in/out stackframe
+ memset(&s, 0, sizeof(s));
+ DWORD imageType;
+#ifdef _M_IX86
+ // normally, call ImageNtHeader() and use machine info from PE header
+ imageType = IMAGE_FILE_MACHINE_I386;
+ s.AddrPC.Offset = c.Eip;
+ s.AddrPC.Mode = AddrModeFlat;
+ s.AddrFrame.Offset = c.Ebp;
+ s.AddrFrame.Mode = AddrModeFlat;
+ s.AddrStack.Offset = c.Esp;
+ s.AddrStack.Mode = AddrModeFlat;
+#elif _M_X64
+ imageType = IMAGE_FILE_MACHINE_AMD64;
+ s.AddrPC.Offset = c.Rip;
+ s.AddrPC.Mode = AddrModeFlat;
+ s.AddrFrame.Offset = c.Rsp;
+ s.AddrFrame.Mode = AddrModeFlat;
+ s.AddrStack.Offset = c.Rsp;
+ s.AddrStack.Mode = AddrModeFlat;
+#elif _M_IA64
+ imageType = IMAGE_FILE_MACHINE_IA64;
+ s.AddrPC.Offset = c.StIIP;
+ s.AddrPC.Mode = AddrModeFlat;
+ s.AddrFrame.Offset = c.IntSp;
+ s.AddrFrame.Mode = AddrModeFlat;
+ s.AddrBStore.Offset = c.RsBSP;
+ s.AddrBStore.Mode = AddrModeFlat;
+ s.AddrStack.Offset = c.IntSp;
+ s.AddrStack.Mode = AddrModeFlat;
+#else
+#error "Platform not supported!"
+#endif
+
+ pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
+ if (!pSym) goto cleanup; // not enough memory...
+ memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
+ pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
+
+ memset(&Line, 0, sizeof(Line));
+ Line.SizeOfStruct = sizeof(Line);
+
+ memset(&Module, 0, sizeof(Module));
+ Module.SizeOfStruct = sizeof(Module);
+
+ for (frameNum = 0; ; ++frameNum )
+ {
+ // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
+ // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
+ // assume that either you are done, or that the stack is so hosed that the next
+ // deeper frame could not be found.
+ // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
+ if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) )
+ {
+ // INFO: "StackWalk64" does not set "GetLastError"...
+ this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset);
+ break;
+ }
+
+ csEntry.offset = s.AddrPC.Offset;
+ csEntry.name[0] = 0;
+ csEntry.undName[0] = 0;
+ csEntry.undFullName[0] = 0;
+ csEntry.offsetFromSmybol = 0;
+ csEntry.offsetFromLine = 0;
+ csEntry.lineFileName[0] = 0;
+ csEntry.lineNumber = 0;
+ csEntry.loadedImageName[0] = 0;
+ csEntry.moduleName[0] = 0;
+ if (s.AddrPC.Offset == s.AddrReturn.Offset)
+ {
+ if ( (this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount) )
+ {
+ this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
+ break;
+ }
+ curRecursionCount++;
+ }
+ else
+ curRecursionCount = 0;
+ if (s.AddrPC.Offset != 0)
+ {
+ // we seem to have a valid PC
+ // show procedure info (SymGetSymFromAddr64())
+ if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE)
+ {
+ MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name);
+ // UnDecorateSymbolName()
+ this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY );
+ this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE );
+ }
+ else
+ {
+ this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
+ }
+
+ // show line number info, NT5.0-method (SymGetLineFromAddr64())
+ if (this->m_sw->pSGLFA != NULL )
+ { // yes, we have SymGetLineFromAddr64()
+ if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE)
+ {
+ csEntry.lineNumber = Line.LineNumber;
+ MyStrCpy(csEntry.lineFileName, STACKWALK_MAX_NAMELEN, Line.FileName);
+ }
+ else
+ {
+ this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
+ }
+ } // yes, we have SymGetLineFromAddr64()
+
+ // show module info (SymGetModuleInfo64())
+ if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE)
+ { // got module info OK
+ switch ( Module.SymType )
+ {
+ case SymNone:
+ csEntry.symTypeString = "-nosymbols-";
+ break;
+ case SymCoff:
+ csEntry.symTypeString = "COFF";
+ break;
+ case SymCv:
+ csEntry.symTypeString = "CV";
+ break;
+ case SymPdb:
+ csEntry.symTypeString = "PDB";
+ break;
+ case SymExport:
+ csEntry.symTypeString = "-exported-";
+ break;
+ case SymDeferred:
+ csEntry.symTypeString = "-deferred-";
+ break;
+ case SymSym:
+ csEntry.symTypeString = "SYM";
+ break;
+#if API_VERSION_NUMBER >= 9
+ case SymDia:
+ csEntry.symTypeString = "DIA";
+ break;
+#endif
+ case 8: //SymVirtual:
+ csEntry.symTypeString = "Virtual";
+ break;
+ default:
+ //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
+ csEntry.symTypeString = NULL;
+ break;
+ }
+
+ // TODO: Mache dies sicher...!
+ MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, Module.ModuleName);
+ csEntry.baseOfImage = Module.BaseOfImage;
+ MyStrCpy(csEntry.loadedImageName, STACKWALK_MAX_NAMELEN, Module.LoadedImageName);
+ } // got module info OK
+ else
+ {
+ this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
+ }
+ } // we seem to have a valid PC
+
+ CallstackEntryType et = nextEntry;
+ if (frameNum == 0)
+ et = firstEntry;
+ bLastEntryCalled = false;
+ this->OnCallstackEntry(et, csEntry);
+
+ if (s.AddrReturn.Offset == 0)
+ {
+ bLastEntryCalled = true;
+ this->OnCallstackEntry(lastEntry, csEntry);
+ SetLastError(ERROR_SUCCESS);
+ break;
+ }
+ } // for ( frameNum )
+
+ cleanup:
+ if (pSym) free( pSym );
+
+ if (bLastEntryCalled == false)
+ this->OnCallstackEntry(lastEntry, csEntry);
+
+ if (context == NULL)
+ ResumeThread(hThread);
+
+ return TRUE;
+}
+
+BOOL __stdcall StackWalker::myReadProcMem(
+ HANDLE hProcess,
+ DWORD64 qwBaseAddress,
+ PVOID lpBuffer,
+ DWORD nSize,
+ LPDWORD lpNumberOfBytesRead
+ )
+{
+ if (s_readMemoryFunction == NULL)
+ {
+ SIZE_T st;
+ BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st);
+ *lpNumberOfBytesRead = (DWORD) st;
+ //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet);
+ return bRet;
+ }
+ else
+ {
+ return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData);
+ }
+}
+
+void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
+{
+ CHAR buffer[STACKWALK_MAX_NAMELEN];
+ if (fileVersion == 0)
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName);
+ else
+ {
+ DWORD v4 = (DWORD) fileVersion & 0xFFFF;
+ DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF;
+ DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF;
+ DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF;
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);
+ }
+ OnOutput(buffer);
+}
+
+void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
+{
+ CHAR buffer[STACKWALK_MAX_NAMELEN];
+ if ( (eType != lastEntry) && (entry.offset != 0) )
+ {
+ if (entry.name[0] == 0)
+ MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)");
+ if (entry.undName[0] != 0)
+ MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName);
+ if (entry.undFullName[0] != 0)
+ MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName);
+ if (entry.lineFileName[0] == 0)
+ {
+ MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)");
+ if (entry.moduleName[0] == 0)
+ MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)");
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name);
+ }
+ else
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name);
+ buffer[STACKWALK_MAX_NAMELEN-1] = 0;
+ OnOutput(buffer);
+ }
+}
+
+void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
+{
+ CHAR buffer[STACKWALK_MAX_NAMELEN];
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr);
+ OnOutput(buffer);
+}
+
+void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
+{
+ CHAR buffer[STACKWALK_MAX_NAMELEN];
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName);
+ OnOutput(buffer);
+ // Also display the OS-version
+#if _MSC_VER <= 1200
+ OSVERSIONINFOA ver;
+ ZeroMemory(&ver, sizeof(OSVERSIONINFOA));
+ ver.dwOSVersionInfoSize = sizeof(ver);
+ if (GetVersionExA(&ver) != FALSE)
+ {
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s)\n",
+ ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
+ ver.szCSDVersion);
+ OnOutput(buffer);
+ }
+#else
+ OSVERSIONINFOEXA ver;
+ ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));
+ ver.dwOSVersionInfoSize = sizeof(ver);
+ if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE)
+ {
+ _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n",
+ ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
+ ver.szCSDVersion, ver.wSuiteMask, ver.wProductType);
+ OnOutput(buffer);
+ }
+#endif
+}
+
+void StackWalker::OnOutput(LPCSTR buffer)
+{
+ OutputDebugStringA(buffer);
+}
diff --git a/src/StackWalker.h b/src/StackWalker.h
new file mode 100644
index 000000000..bf47d3726
--- /dev/null
+++ b/src/StackWalker.h
@@ -0,0 +1,214 @@
+/**********************************************************************
+ *
+ * StackWalker.h
+ *
+ *
+ *
+ * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Copyright (c) 2005-2010, Jochen Kalmbach
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of Jochen Kalmbach nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * **********************************************************************/
+// #pragma once is supported starting with _MCS_VER 1000,
+// so we need not to check the version (because we only support _MSC_VER >= 1100)!
+#pragma once
+
+#include <windows.h>
+
+// special defines for VC5/6 (if no actual PSDK is installed):
+#if _MSC_VER < 1300
+typedef unsigned __int64 DWORD64, *PDWORD64;
+#if defined(_WIN64)
+typedef unsigned __int64 SIZE_T, *PSIZE_T;
+#else
+typedef unsigned long SIZE_T, *PSIZE_T;
+#endif
+#endif // _MSC_VER < 1300
+
+class StackWalkerInternal; // forward
+class StackWalker
+{
+public:
+ typedef enum StackWalkOptions
+ {
+ // No addition info will be retrived
+ // (only the address is available)
+ RetrieveNone = 0,
+
+ // Try to get the symbol-name
+ RetrieveSymbol = 1,
+
+ // Try to get the line for this symbol
+ RetrieveLine = 2,
+
+ // Try to retrieve the module-infos
+ RetrieveModuleInfo = 4,
+
+ // Also retrieve the version for the DLL/EXE
+ RetrieveFileVersion = 8,
+
+ // Contains all the abouve
+ RetrieveVerbose = 0xF,
+
+ // Generate a "good" symbol-search-path
+ SymBuildPath = 0x10,
+
+ // Also use the public Microsoft-Symbol-Server
+ SymUseSymSrv = 0x20,
+
+ // Contains all the abouve "Sym"-options
+ SymAll = 0x30,
+
+ // Contains all options (default)
+ OptionsAll = 0x3F
+ } StackWalkOptions;
+
+ StackWalker(
+ int options = OptionsAll, // 'int' is by design, to combine the enum-flags
+ LPCSTR szSymPath = NULL,
+ DWORD dwProcessId = GetCurrentProcessId(),
+ HANDLE hProcess = GetCurrentProcess()
+ );
+ StackWalker(DWORD dwProcessId, HANDLE hProcess);
+ virtual ~StackWalker();
+
+ typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
+ HANDLE hProcess,
+ DWORD64 qwBaseAddress,
+ PVOID lpBuffer,
+ DWORD nSize,
+ LPDWORD lpNumberOfBytesRead,
+ LPVOID pUserData // optional data, which was passed in "ShowCallstack"
+ );
+
+ BOOL LoadModules();
+
+ BOOL ShowCallstack(
+ HANDLE hThread = GetCurrentThread(),
+ const CONTEXT *context = NULL,
+ PReadProcessMemoryRoutine readMemoryFunction = NULL,
+ LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
+ );
+
+#if _MSC_VER >= 1300
+// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
+// in older compilers in order to use it... starting with VC7 we can declare it as "protected"
+protected:
+#endif
+ enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
+
+protected:
+ // Entry for each Callstack-Entry
+ typedef struct CallstackEntry
+ {
+ DWORD64 offset; // if 0, we have no valid entry
+ CHAR name[STACKWALK_MAX_NAMELEN];
+ CHAR undName[STACKWALK_MAX_NAMELEN];
+ CHAR undFullName[STACKWALK_MAX_NAMELEN];
+ DWORD64 offsetFromSmybol;
+ DWORD offsetFromLine;
+ DWORD lineNumber;
+ CHAR lineFileName[STACKWALK_MAX_NAMELEN];
+ DWORD symType;
+ LPCSTR symTypeString;
+ CHAR moduleName[STACKWALK_MAX_NAMELEN];
+ DWORD64 baseOfImage;
+ CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
+ } CallstackEntry;
+
+ typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
+
+ virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
+ virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
+ virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
+ virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
+ virtual void OnOutput(LPCSTR szText);
+
+ StackWalkerInternal *m_sw;
+ HANDLE m_hProcess;
+ DWORD m_dwProcessId;
+ BOOL m_modulesLoaded;
+ LPSTR m_szSymPath;
+
+ int m_options;
+ int m_MaxRecursionCount;
+
+ static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
+
+ friend StackWalkerInternal;
+};
+
+
+// The "ugly" assembler-implementation is needed for systems before XP
+// If you have a new PSDK and you only compile for XP and later, then you can use
+// the "RtlCaptureContext"
+// Currently there is no define which determines the PSDK-Version...
+// So we just use the compiler-version (and assumes that the PSDK is
+// the one which was installed by the VS-IDE)
+
+// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
+// But I currently use it in x64/IA64 environments...
+//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
+
+#if defined(_M_IX86)
+#ifdef CURRENT_THREAD_VIA_EXCEPTION
+// TODO: The following is not a "good" implementation,
+// because the callstack is only valid in the "__except" block...
+#define GET_CURRENT_CONTEXT(c, contextFlags) \
+ do { \
+ memset(&c, 0, sizeof(CONTEXT)); \
+ EXCEPTION_POINTERS *pExp = NULL; \
+ __try { \
+ throw 0; \
+ } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
+ if (pExp != NULL) \
+ memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
+ c.ContextFlags = contextFlags; \
+ } while(0);
+#else
+// The following should be enough for walking the callstack...
+#define GET_CURRENT_CONTEXT(c, contextFlags) \
+ do { \
+ memset(&c, 0, sizeof(CONTEXT)); \
+ c.ContextFlags = contextFlags; \
+ __asm call x \
+ __asm x: pop eax \
+ __asm mov c.Eip, eax \
+ __asm mov c.Ebp, ebp \
+ __asm mov c.Esp, esp \
+ } while(0);
+#endif
+
+#else
+
+// The following is defined for x86 (XP and higher), x64 and IA64:
+#define GET_CURRENT_CONTEXT(c, contextFlags) \
+ do { \
+ memset(&c, 0, sizeof(CONTEXT)); \
+ c.ContextFlags = contextFlags; \
+ RtlCaptureContext(&c); \
+} while(0);
+#endif
diff --git a/src/StringCompression.cpp b/src/StringCompression.cpp
new file mode 100644
index 000000000..36946b282
--- /dev/null
+++ b/src/StringCompression.cpp
@@ -0,0 +1,180 @@
+
+// StringCompression.cpp
+
+// Implements the wrapping functions for compression and decompression using AString as their data
+
+#include "Globals.h"
+#include "StringCompression.h"
+
+
+
+
+
+/// Compresses a_Data into a_Compressed; returns Z_XXX error constants same as zlib's compress2()
+int CompressString(const char * a_Data, int a_Length, AString & a_Compressed)
+{
+ uLongf CompressedSize = compressBound(a_Length);
+
+ // HACK: We're assuming that AString returns its internal buffer in its data() call and we're overwriting that buffer!
+ // It saves us one allocation and one memcpy of the entire compressed data
+ // It may not work on some STL implementations! (Confirmed working on MSVC 2008 & 2010)
+ a_Compressed.resize(CompressedSize);
+ int errorcode = compress2( (Bytef*)a_Compressed.data(), &CompressedSize, (const Bytef*)a_Data, a_Length, Z_DEFAULT_COMPRESSION);
+ if (errorcode != Z_OK)
+ {
+ return errorcode;
+ }
+ a_Compressed.resize(CompressedSize);
+ return Z_OK;
+}
+
+
+
+
+
+/// Uncompresses a_Data into a_Decompressed; returns Z_XXX error constants same as zlib's uncompress()
+int UncompressString(const char * a_Data, int a_Length, AString & a_Uncompressed, int a_UncompressedSize)
+{
+ // HACK: We're assuming that AString returns its internal buffer in its data() call and we're overwriting that buffer!
+ // It saves us one allocation and one memcpy of the entire compressed data
+ // It may not work on some STL implementations! (Confirmed working on MSVC 2008 & 2010)
+ a_Uncompressed.resize(a_UncompressedSize);
+ uLongf UncompressedSize = (uLongf)a_UncompressedSize; // On some architectures the uLongf is different in size to int, that may be the cause of the -5 error
+ int errorcode = uncompress((Bytef*)a_Uncompressed.data(), &UncompressedSize, (const Bytef*)a_Data, a_Length);
+ if (errorcode != Z_OK)
+ {
+ return errorcode;
+ }
+ a_Uncompressed.resize(UncompressedSize);
+ return Z_OK;
+}
+
+
+
+
+
+int CompressStringGZIP(const char * a_Data, int a_Length, AString & a_Compressed)
+{
+ // Compress a_Data into a_Compressed using GZIP; return Z_XXX error constants same as zlib's compress2()
+
+ a_Compressed.reserve(a_Length);
+
+ char Buffer[64 KiB];
+ z_stream strm;
+ memset(&strm, 0, sizeof(strm));
+ strm.next_in = (Bytef *)a_Data;
+ strm.avail_in = a_Length;
+ strm.next_out = (Bytef *)Buffer;
+ strm.avail_out = sizeof(Buffer);
+
+ int res = deflateInit2(&strm, 9, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY);
+ if (res != Z_OK)
+ {
+ LOG("%s: compression initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg);
+ return res;
+ }
+
+ while (true)
+ {
+ res = deflate(&strm, Z_FINISH);
+ switch (res)
+ {
+ case Z_OK:
+ {
+ // Some data has been compressed. Consume the buffer and continue compressing
+ a_Compressed.append(Buffer, sizeof(Buffer) - strm.avail_out);
+ strm.avail_out = sizeof(Buffer);
+ if (strm.avail_in == 0)
+ {
+ // All data has been compressed
+ deflateEnd(&strm);
+ return Z_OK;
+ }
+ break;
+ }
+
+ case Z_STREAM_END:
+ {
+ // Finished compressing. Consume the rest of the buffer and return
+ a_Compressed.append(Buffer, sizeof(Buffer) - strm.avail_out);
+ deflateEnd(&strm);
+ return Z_OK;
+ }
+
+ default:
+ {
+ // An error has occurred, log it and return the error value
+ LOG("%s: compression failed: %d (\"%s\").", __FUNCTION__, res, strm.msg);
+ deflateEnd(&strm);
+ return res;
+ }
+ } // switch (res)
+ } // while (true)
+}
+
+
+
+
+
+extern int UncompressStringGZIP(const char * a_Data, int a_Length, AString & a_Uncompressed)
+{
+ // Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib
+
+ a_Uncompressed.reserve(a_Length);
+
+ char Buffer[64 KiB];
+ z_stream strm;
+ memset(&strm, 0, sizeof(strm));
+ strm.next_in = (Bytef *)a_Data;
+ strm.avail_in = a_Length;
+ strm.next_out = (Bytef *)Buffer;
+ strm.avail_out = sizeof(Buffer);
+
+ int res = inflateInit2(&strm, 31); // Force GZIP decoding
+ if (res != Z_OK)
+ {
+ LOG("%s: uncompression initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg);
+ return res;
+ }
+
+ while (true)
+ {
+ res = inflate(&strm, Z_FINISH);
+ switch (res)
+ {
+ case Z_OK:
+ {
+ // Some data has been uncompressed. Consume the buffer and continue uncompressing
+ a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out);
+ strm.avail_out = sizeof(Buffer);
+ if (strm.avail_in == 0)
+ {
+ // All data has been uncompressed
+ inflateEnd(&strm);
+ return Z_OK;
+ }
+ break;
+ }
+
+ case Z_STREAM_END:
+ {
+ // Finished uncompressing. Consume the rest of the buffer and return
+ a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out);
+ inflateEnd(&strm);
+ return Z_OK;
+ }
+
+ default:
+ {
+ // An error has occurred, log it and return the error value
+ LOG("%s: uncompression failed: %d (\"%s\").", __FUNCTION__, res, strm.msg);
+ inflateEnd(&strm);
+ return res;
+ }
+ } // switch (res)
+ } // while (true)
+}
+
+
+
+
diff --git a/src/StringCompression.h b/src/StringCompression.h
new file mode 100644
index 000000000..459e8f568
--- /dev/null
+++ b/src/StringCompression.h
@@ -0,0 +1,25 @@
+
+// StringCompression.h
+
+// Interfaces to the wrapping functions for compression and decompression using AString as their data
+
+#include "zlib/zlib.h" // Needed for the Z_XXX return values
+
+
+
+
+
+/// Compresses a_Data into a_Compressed using ZLIB; returns Z_XXX error constants same as zlib's compress2()
+extern int CompressString(const char * a_Data, int a_Length, AString & a_Compressed);
+
+/// Uncompresses a_Data into a_Uncompressed; returns Z_XXX error constants same as zlib's decompress()
+extern int UncompressString(const char * a_Data, int a_Length, AString & a_Uncompressed, int a_UncompressedSize);
+
+/// Compresses a_Data into a_Compressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib
+extern int CompressStringGZIP(const char * a_Data, int a_Length, AString & a_Compressed);
+
+/// Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib
+extern int UncompressStringGZIP(const char * a_Data, int a_Length, AString & a_Uncompressed);
+
+
+
diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp
new file mode 100644
index 000000000..f7aeeed26
--- /dev/null
+++ b/src/StringUtils.cpp
@@ -0,0 +1,766 @@
+
+// StringUtils.cpp
+
+// Implements the various string helper functions:
+
+#include "Globals.h"
+
+#if defined(ANDROID_NDK)
+#include <ctype.h>
+#endif
+
+#ifdef _MSC_VER
+ // Under MSVC, link to WinSock2 (needed by RawBEToUTF8's byteswapping)
+ #pragma comment(lib, "ws2_32.lib")
+#endif
+
+
+
+
+
+AString & AppendVPrintf(AString & str, const char *format, va_list args)
+{
+ ASSERT(format != NULL);
+
+ char buffer[2048];
+ size_t len;
+ #ifdef _MSC_VER
+ // MS CRT provides secure printf that doesn't behave like in the C99 standard
+ if ((len = _vsnprintf_s(buffer, ARRAYCOUNT(buffer), _TRUNCATE, format, args)) != -1)
+ #else // _MSC_VER
+ if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, args)) < ARRAYCOUNT(buffer))
+ #endif // else _MSC_VER
+ {
+ // The result did fit into the static buffer
+ str.append(buffer, len);
+ return str;
+ }
+
+ // The result did not fit into the static buffer
+ #ifdef _MSC_VER
+ // for MS CRT, we need to calculate the result length
+ len = _vscprintf(format, args);
+ if (len == -1)
+ {
+ return str;
+ }
+ #endif // _MSC_VER
+
+ // Allocate a buffer and printf into it:
+ str.resize(len + 1);
+ // HACK: we're accessing AString's internal buffer in a way that is NOT guaranteed to always work. But it works on all STL implementations tested.
+ // I can't think of any other way that is safe, doesn't allocate twice as much space as needed and doesn't use C++11 features like the move constructor
+ #ifdef _MSC_VER
+ vsprintf_s((char *)str.data(), len + 1, format, args);
+ #else // _MSC_VER
+ vsnprintf((char *)str.data(), len + 1, format, args);
+ #endif // else _MSC_VER
+ str.resize(len);
+ return str;
+}
+
+
+
+
+
+AString & Printf(AString & str, const char * format, ...)
+{
+ str.clear();
+ va_list args;
+ va_start(args, format);
+ std::string &retval = AppendVPrintf(str, format, args);
+ va_end(args);
+ return retval;
+}
+
+
+
+
+
+AString Printf(const char * format, ...)
+{
+ AString res;
+ va_list args;
+ va_start(args, format);
+ AppendVPrintf(res, format, args);
+ va_end(args);
+ return res;
+}
+
+
+
+
+
+AString & AppendPrintf(AString &str, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ std::string &retval = AppendVPrintf(str, format, args);
+ va_end(args);
+ return retval;
+}
+
+
+
+
+
+AStringVector StringSplit(const AString & str, const AString & delim)
+{
+ AStringVector results;
+ size_t cutAt = 0;
+ size_t Prev = 0;
+ while ((cutAt = str.find_first_of(delim, Prev)) != str.npos)
+ {
+ results.push_back(str.substr(Prev, cutAt - Prev));
+ Prev = cutAt + 1;
+ }
+ if (Prev < str.length())
+ {
+ results.push_back(str.substr(Prev));
+ }
+ return results;
+}
+
+
+
+
+
+AStringVector StringSplitAndTrim(const AString & str, const AString & delim)
+{
+ AStringVector results;
+ size_t cutAt = 0;
+ size_t Prev = 0;
+ while ((cutAt = str.find_first_of(delim, Prev)) != str.npos)
+ {
+ results.push_back(TrimString(str.substr(Prev, cutAt - Prev)));
+ Prev = cutAt + 1;
+ }
+ if (Prev < str.length())
+ {
+ results.push_back(TrimString(str.substr(Prev)));
+ }
+ return results;
+}
+
+
+
+
+AString TrimString(const AString & str)
+{
+ size_t len = str.length();
+ size_t start = 0;
+ while (start < len)
+ {
+ if (str[start] > 32)
+ {
+ break;
+ }
+ ++start;
+ }
+ if (start == len)
+ {
+ return "";
+ }
+
+ size_t end = len;
+ while (end >= start)
+ {
+ if (str[end] > 32)
+ {
+ break;
+ }
+ --end;
+ }
+
+ return str.substr(start, end - start + 1);
+}
+
+
+
+
+
+AString & StrToUpper(AString & s)
+{
+ AString::iterator i = s.begin();
+ AString::iterator end = s.end();
+
+ while (i != end)
+ {
+ *i = (char)toupper(*i);
+ ++i;
+ }
+ return s;
+}
+
+
+
+
+
+AString & StrToLower(AString & s)
+{
+ AString::iterator i = s.begin();
+ AString::iterator end = s.end();
+
+ while (i != end)
+ {
+ *i = (char)tolower(*i);
+ ++i;
+ }
+ return s;
+}
+
+
+
+
+
+int NoCaseCompare(const AString & s1, const AString & s2)
+{
+ #ifdef _MSC_VER
+ // MSVC has stricmp that compares case-insensitive:
+ return _stricmp(s1.c_str(), s2.c_str());
+ #else
+ // Do it the hard way:
+ AString s1Copy(s1);
+ AString s2Copy(s2);
+ return StrToUpper(s1Copy).compare(StrToUpper(s2Copy));
+ #endif // else _MSC_VER
+}
+
+
+
+
+
+unsigned int RateCompareString(const AString & s1, const AString & s2 )
+{
+ unsigned int MatchedLetters = 0;
+ unsigned int s1Length = s1.length();
+
+ if( s1Length > s2.length() ) return 0; // Definitely not a match
+
+ for (unsigned int i = 0; i < s1Length; i++)
+ {
+ char c1 = (char)toupper( s1[i] );
+ char c2 = (char)toupper( s2[i] );
+ if( c1 == c2 )
+ {
+ ++MatchedLetters;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return MatchedLetters;
+}
+
+
+
+
+
+void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith)
+{
+ size_t pos1 = iHayStack.find(iNeedle);
+ while (pos1 != AString::npos)
+ {
+ iHayStack.replace( pos1, iNeedle.size(), iReplaceWith);
+ pos1 = iHayStack.find(iNeedle, pos1);
+ }
+}
+
+
+
+
+// Converts a stream of BE shorts into UTF-8 string; returns a ref to a_UTF8
+AString & RawBEToUTF8(short * a_RawData, int a_NumShorts, AString & a_UTF8)
+{
+ a_UTF8.clear();
+ a_UTF8.reserve(3 * a_NumShorts / 2); // a quick guess of the resulting size
+ for (int i = 0; i < a_NumShorts; i++)
+ {
+ int c = ntohs(*(a_RawData + i));
+ if (c < 0x80)
+ {
+ a_UTF8.push_back((char)c);
+ }
+ else if (c < 0x800)
+ {
+ a_UTF8.push_back((char)(192 + c / 64));
+ a_UTF8.push_back((char)(128 + c % 64));
+ }
+ else if (c - 0xd800u < 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));
+ }
+ 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));
+ }
+ else
+ {
+ // Error, silently drop
+ }
+ }
+ return a_UTF8;
+}
+
+
+
+
+// UTF-8 conversion code adapted from:
+// http://stackoverflow.com/questions/2867123/convert-utf-16-to-utf-8-under-windows-and-linux-in-c
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Begin of Unicode, Inc.'s code / information
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+Notice from the original file:
+* Copyright 2001-2004 Unicode, Inc.
+*
+* Disclaimer
+*
+* This source code is provided as is by Unicode, Inc. No claims are
+* made as to fitness for any particular purpose. No warranties of any
+* kind are expressed or implied. The recipient agrees to determine
+* applicability of information provided. If this file has been
+* purchased on magnetic or optical media from Unicode, Inc., the
+* sole remedy for any claim will be exchange of defective media
+* within 90 days of receipt.
+*
+* Limitations on Rights to Redistribute This Code
+*
+* Unicode, Inc. hereby grants the right to freely use the information
+* supplied in this file in the creation of products supporting the
+* Unicode Standard, and to make copies of this file in any form
+* for internal or external distribution as long as this notice
+* remains attached.
+*/
+
+#define UNI_MAX_BMP 0x0000FFFF
+#define UNI_MAX_UTF16 0x0010FFFF
+#define UNI_MAX_UTF32 0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 0x0010FFFF
+#define UNI_SUR_HIGH_START 0xD800
+#define UNI_SUR_HIGH_END 0xDBFF
+#define UNI_SUR_LOW_START 0xDC00
+#define UNI_SUR_LOW_END 0xDFFF
+
+
+
+
+
+static const char 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,
+ 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,
+ 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
+};
+
+
+
+
+
+static const unsigned int offsetsFromUTF8[6] =
+{
+ 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL
+};
+
+
+
+
+
+static bool isLegalUTF8(const unsigned char * source, int length)
+{
+ unsigned char a;
+ const unsigned char * srcptr = source + length;
+ switch (length)
+ {
+ default: return false;
+ // Everything else falls through when "true"...
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 2:
+ {
+ if ((a = (*--srcptr)) > 0xBF) return false;
+ switch (*source)
+ {
+ // no fall-through in this inner switch
+ case 0xE0: if (a < 0xA0) return false; break;
+ case 0xED: if (a > 0x9F) return false; break;
+ case 0xF0: if (a < 0x90) return false; break;
+ case 0xF4: if (a > 0x8F) return false; break;
+ default: if (a < 0x80) return false;
+ }
+ }
+ case 1: if (*source >= 0x80 && *source < 0xC2) return false;
+ }
+ if (*source > 0xF4) return false;
+ return true;
+}
+
+
+
+
+
+AString & UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length, AString & a_UTF16)
+{
+ a_UTF16.clear();
+ a_UTF16.reserve(a_UTF8Length * 3);
+
+ const unsigned char * source = (const unsigned char*)a_UTF8;
+ const unsigned char * sourceEnd = source + a_UTF8Length;
+ const int halfShift = 10; // used for shifting by 10 bits
+ const unsigned int halfBase = 0x0010000UL;
+ const unsigned int halfMask = 0x3FFUL;
+
+ while (source < sourceEnd)
+ {
+ unsigned int ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd)
+ {
+ return a_UTF16;
+ }
+ // Do this check whether lenient or strict
+ if (!isLegalUTF8(source, extraBytesToRead + 1))
+ {
+ return a_UTF16;
+ break;
+ }
+
+ // The cases all fall through. See "Note A" below.
+ switch (extraBytesToRead)
+ {
+ case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (ch <= UNI_MAX_BMP)
+ {
+ // Target is a character <= 0xFFFF
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
+ {
+ // UTF-16 surrogate values are illegal in UTF-32
+ ch = ' ';
+ }
+ unsigned short v = htons((unsigned short)ch);
+ a_UTF16.append((const char *)&v, 2);
+ }
+ else if (ch > UNI_MAX_UTF16)
+ {
+ // Invalid value, replace with a space
+ unsigned short v = htons(' ');
+ a_UTF16.append((const char *)&v, 2);
+ }
+ else
+ {
+ // target is a character in range 0xFFFF - 0x10FFFF.
+ ch -= halfBase;
+ unsigned short v1 = htons((ch >> halfShift) + UNI_SUR_HIGH_START);
+ unsigned short v2 = htons((ch & halfMask) + UNI_SUR_LOW_START);
+ a_UTF16.append((const char *)&v1, 2);
+ a_UTF16.append((const char *)&v2, 2);
+ }
+ }
+ return a_UTF16;
+}
+
+/* ---------------------------------------------------------------------
+
+ Note A.
+ The fall-through switches in UTF-8 reading code save a
+ temp variable, some decrements & conditionals. The switches
+ are equivalent to the following loop:
+ {
+ int tmpBytesToRead = extraBytesToRead+1;
+ do {
+ ch += *source++;
+ --tmpBytesToRead;
+ if (tmpBytesToRead) ch <<= 6;
+ } while (tmpBytesToRead > 0);
+ }
+
+ ---------------------------------------------------------------------
+*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// End of Unicode, Inc.'s code / information
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+#define HEX(x) ((x) > 9 ? (x) + 'A' - 10 : (x) + '0')
+
+/**
+format binary data this way:
+00001234: 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 1234567890abcdef
+*/
+AString & CreateHexDump(AString & a_Out, const void * a_Data, int a_Size, int a_LineLength)
+{
+ ASSERT(a_LineLength <= 120); // Due to using a fixed size line buffer; increase line[]'s size to lift this max
+ char line[512];
+ char * p;
+ char * q;
+
+ a_Out.reserve(a_Size / a_LineLength * (18 + 6 * a_LineLength));
+ for (int i = 0; i < a_Size; i += a_LineLength)
+ {
+ int k = a_Size - i;
+ if (k > a_LineLength)
+ {
+ k = a_LineLength;
+ }
+ #ifdef _MSC_VER
+ // MSVC provides a "secure" version of sprintf()
+ int Count = sprintf_s(line, sizeof(line), "%08x:", i);
+ #else
+ int Count = sprintf(line, "%08x:", i);
+ #endif
+ // Remove the terminating NULL / leftover garbage in line, after the sprintf-ed value
+ memset(line + Count, 32, sizeof(line) - Count);
+ p = line + 10;
+ q = p + 2 + a_LineLength * 3 + 1;
+ for (int j = 0; j < k; j++)
+ {
+ unsigned char c = ((unsigned char *)a_Data)[i + j];
+ p[0] = HEX(c >> 4);
+ p[1] = HEX(c & 0xf);
+ p[2] = ' ';
+ if (c >= ' ')
+ {
+ q[0] = (char)c;
+ }
+ else
+ {
+ q[0] = '.';
+ }
+ p += 3;
+ q ++;
+ } // for j
+ q[0] = '\n';
+ q[1] = 0;
+ a_Out.append(line);
+ } // for i
+ return a_Out;
+}
+
+
+
+
+
+AString EscapeString(const AString & a_Message)
+{
+ AString EscapedMsg;
+ size_t len = a_Message.size();
+ size_t last = 0;
+ EscapedMsg.reserve(len);
+ for (size_t i = 0; i < len; i++)
+ {
+ char ch = a_Message[i];
+ switch (ch)
+ {
+ case '\'':
+ case '\"':
+ case '\\':
+ {
+ if (i > last)
+ {
+ EscapedMsg.append(a_Message, last, i - last);
+ }
+ EscapedMsg.push_back('\\');
+ EscapedMsg.push_back(ch);
+ last = i + 1;
+ break;
+ }
+ } // switch (ch)
+ } // for i - a_Message[]
+ if (len > last)
+ {
+ EscapedMsg.append(a_Message, last, len - last);
+ }
+ return EscapedMsg;
+}
+
+
+
+
+
+AString StripColorCodes(const AString & a_Message)
+{
+ AString res(a_Message);
+ size_t idx = 0;
+ while (true)
+ {
+ idx = res.find("\xc2\xa7", idx);
+ if (idx == AString::npos)
+ {
+ return res;
+ }
+ res.erase(idx, 3);
+ }
+}
+
+
+
+
+
+AString URLDecode(const AString & a_String)
+{
+ AString res;
+ size_t len = a_String.length();
+ res.reserve(len);
+ for (size_t i = 0; i < len; i++)
+ {
+ char ch = a_String[i];
+ if ((ch != '%') || (i > len - 3))
+ {
+ res.push_back(ch);
+ continue;
+ }
+ // Decode the hex value:
+ char hi = a_String[i + 1], lo = a_String[i + 2];
+ if ((hi >= '0') && (hi <= '9'))
+ {
+ hi = hi - '0';
+ }
+ else if ((hi >= 'a') && (hi <= 'f'))
+ {
+ hi = hi - 'a' + 10;
+ }
+ else if ((hi >= 'A') && (hi <= 'F'))
+ {
+ hi = hi - 'F' + 10;
+ }
+ else
+ {
+ res.push_back(ch);
+ continue;
+ }
+ if ((lo >= '0') && (lo <= '9'))
+ {
+ lo = lo - '0';
+ }
+ else if ((lo >= 'a') && (lo <= 'f'))
+ {
+ lo = lo - 'a' + 10;
+ }
+ else if ((lo >= 'A') && (lo <= 'F'))
+ {
+ lo = lo - 'A' + 10;
+ }
+ else
+ {
+ res.push_back(ch);
+ continue;
+ }
+ res.push_back((hi << 4) | lo);
+ i += 2;
+ } // for i - a_String[]
+ return res;
+}
+
+
+
+
+
+AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_To)
+{
+ AString res(a_String);
+ std::replace(res.begin(), res.end(), a_From, a_To);
+ return res;
+}
+
+
+
+
+
+/// Converts one Hex character in a Base64 encoding into the data value
+static inline int UnBase64(char c)
+{
+ if (c >='A' && c <= 'Z')
+ {
+ return c - 'A';
+ }
+ if (c >='a' && c <= 'z')
+ {
+ return c - 'a' + 26;
+ }
+ if (c >= '0' && c <= '9')
+ {
+ return c - '0' + 52;
+ }
+ if (c == '+')
+ {
+ return 62;
+ }
+ if (c == '/')
+ {
+ return 63;
+ }
+ if (c == '=')
+ {
+ return -1;
+ }
+ return -2;
+}
+
+
+
+
+
+AString Base64Decode(const AString & a_Base64String)
+{
+ AString res;
+ size_t i, len = a_Base64String.size();
+ int o, c;
+ res.resize((len * 4) / 3 + 5, 0); // Approximate the upper bound on the result length
+ for (o = 0, i = 0; i < len; i++)
+ {
+ c = UnBase64(a_Base64String[i]);
+ if (c >= 0)
+ {
+ switch (o & 7)
+ {
+ case 0: res[o >> 3] |= (c << 2); break;
+ case 6: res[o >> 3] |= (c >> 4); res[(o >> 3) + 1] |= (c << 4); break;
+ case 4: res[o >> 3] |= (c >> 2); res[(o >> 3) + 1] |= (c << 6); break;
+ case 2: res[o >> 3] |= c; break;
+ }
+ o += 6;
+ }
+ if (c == -1)
+ {
+ // Error while decoding, invalid input. Return as much as we've decoded:
+ res.resize(o >> 3);
+ return res;
+ }
+ }
+ res.resize(o >> 3);
+ return res;}
+
+
+
+
diff --git a/src/StringUtils.h b/src/StringUtils.h
new file mode 100644
index 000000000..3917cc4ec
--- /dev/null
+++ b/src/StringUtils.h
@@ -0,0 +1,93 @@
+
+// StringUtils.h
+
+// Interfaces to various string helper functions
+
+
+
+
+#ifndef STRINGUTILS_H_INCLUDED
+#define STRINGUTILS_H_INCLUDED
+
+
+
+
+
+typedef std::string AString;
+typedef std::vector<AString> AStringVector;
+typedef std::list<AString> AStringList;
+
+
+
+
+
+/// Add the formated string to the existing data in the string
+extern AString & AppendVPrintf(AString & str, const char * format, va_list args);
+
+/// Output the formatted text into the string
+extern AString & Printf (AString & str, const char * format, ...);
+
+/// Output the formatted text into string, return string by value
+extern AString Printf(const char * format, ...);
+
+/// Add the formatted string to the existing data in the string
+extern AString & AppendPrintf (AString & str, const char * format, ...);
+
+/// Split the string at any of the listed delimiters, return 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
+extern AStringVector StringSplitAndTrim(const AString & str, const AString & delim);
+
+/// Trime whitespace at both ends of the string
+extern AString TrimString(const AString & str); // tolua_export
+
+/// In-place string conversion to uppercase; returns the same string
+extern AString & StrToUpper(AString & s);
+
+/// In-place string conversion to lowercase; returns the same string
+extern AString & StrToLower(AString & s);
+
+/// Case-insensitive string comparison; returns 0 if the strings are the same
+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()]
+extern unsigned int RateCompareString(const AString & s1, const AString & s2 );
+
+/// 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
+extern AString & RawBEToUTF8(short * a_RawData, int a_NumShorts, AString & a_UTF8);
+
+/// Converts a UTF-8 string into a UTF-16 BE string, packing that back into AString; return a ref to a_UTF16
+extern AString & UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length, AString & a_UTF16);
+
+/// 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, int a_Size, int a_BytesPerLine);
+
+/// 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
+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
+extern AString URLDecode(const AString & a_String); // Cannot export to Lua automatically - would generated an extra return value
+
+/// Replaces all occurrences of char a_From inside a_String with char a_To.
+extern AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_To); // Needn't export to Lua, since Lua doesn't have chars anyway
+
+/// Decodes a Base64-encoded string into the raw data
+extern AString Base64Decode(const AString & a_Base64String);
+
+// If you have any other string helper functions, declare them here
+
+
+
+
+#endif // STRINGUTILS_H_INCLUDED
+
+
+
+
diff --git a/src/Tracer.cpp b/src/Tracer.cpp
new file mode 100644
index 000000000..ef136302f
--- /dev/null
+++ b/src/Tracer.cpp
@@ -0,0 +1,398 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Tracer.h"
+#include "World.h"
+
+#include "Vector3f.h"
+#include "Vector3i.h"
+#include "Vector3d.h"
+
+#include "Entities/Entity.h"
+
+#ifndef _WIN32
+ #include <stdlib.h> // abs()
+#endif
+
+
+
+
+
+cTracer::cTracer(cWorld * a_World)
+ : m_World(a_World)
+{
+ m_NormalTable[0].Set(-1, 0, 0);
+ m_NormalTable[1].Set( 0, 0,-1);
+ m_NormalTable[2].Set( 1, 0, 0);
+ m_NormalTable[3].Set( 0, 0, 1);
+ m_NormalTable[4].Set( 0, 1, 0);
+ m_NormalTable[5].Set( 0,-1, 0);
+}
+
+
+
+
+
+cTracer::~cTracer()
+{
+}
+
+
+
+
+
+float cTracer::SigNum( float a_Num )
+{
+ if (a_Num < 0.f) return -1.f;
+ if (a_Num > 0.f) return 1.f;
+ return 0.f;
+}
+
+
+
+
+
+void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction)
+{
+ // calculate the direction of the ray (linear algebra)
+ dir = a_Direction;
+
+ // decide which direction to start walking in
+ step.x = (int) SigNum(dir.x);
+ step.y = (int) SigNum(dir.y);
+ step.z = (int) SigNum(dir.z);
+
+ // normalize the direction vector
+ if( dir.SqrLength() > 0.f ) dir.Normalize();
+
+ // how far we must move in the ray direction before
+ // we encounter a new voxel in x-direction
+ // same but y-direction
+ if (dir.x != 0.f)
+ {
+ tDelta.x = 1 / fabs(dir.x);
+ }
+ else
+ {
+ tDelta.x = 0;
+ }
+ if (dir.y != 0.f)
+ {
+ tDelta.y = 1 / fabs(dir.y);
+ }
+ else
+ {
+ tDelta.y = 0;
+ }
+ if (dir.z != 0.f)
+ {
+ tDelta.z = 1 / fabs(dir.z);
+ }
+ else
+ {
+ tDelta.z = 0;
+ }
+
+ // start voxel coordinates
+ pos.x = (int)floorf(a_Start.x);
+ pos.y = (int)floorf(a_Start.y);
+ pos.z = (int)floorf(a_Start.z);
+
+ // calculate distance to first intersection in the voxel we start from
+ if (dir.x < 0)
+ {
+ tMax.x = ((float)pos.x - a_Start.x) / dir.x;
+ }
+ else
+ {
+ tMax.x = (((float)pos.x + 1) - a_Start.x) / dir.x;
+ }
+
+ if (dir.y < 0)
+ {
+ tMax.y = ((float)pos.y - a_Start.y) / dir.y;
+ }
+ else
+ {
+ tMax.y = (((float)pos.y + 1) - a_Start.y) / dir.y;
+ }
+
+ if (dir.z < 0)
+ {
+ tMax.z = ((float)pos.z - a_Start.z) / dir.z;
+ }
+ else
+ {
+ tMax.z = (((float)pos.z + 1) - a_Start.z) / dir.z;
+ }
+}
+
+
+
+
+
+bool cTracer::Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight)
+{
+ if ((a_Start.y < 0) || (a_Start.y >= cChunkDef::Height))
+ {
+ LOGD("%s: Start Y is outside the world (%.2f), not tracing.", __FUNCTION__, a_Start.y);
+ return false;
+ }
+
+ SetValues(a_Start, a_Direction);
+
+ Vector3f End = a_Start + (dir * (float)a_Distance);
+
+ if (End.y < 0)
+ {
+ float dist = -a_Start.y / dir.y;
+ End = a_Start + (dir * dist);
+ }
+
+ // end voxel coordinates
+ end1.x = (int)floorf(End.x);
+ end1.y = (int)floorf(End.y);
+ end1.z = (int)floorf(End.z);
+
+ // check if first is occupied
+ if (pos.Equals(end1))
+ {
+ return false;
+ }
+
+ bool reachedX = false, reachedY = false, reachedZ = false;
+
+ int Iterations = 0;
+ while (Iterations < a_Distance)
+ {
+ Iterations++;
+ if ((tMax.x < tMax.y) && (tMax.x < tMax.z))
+ {
+ tMax.x += tDelta.x;
+ pos.x += step.x;
+ }
+ else if (tMax.y < tMax.z)
+ {
+ tMax.y += tDelta.y;
+ pos.y += step.y;
+ }
+ else
+ {
+ tMax.z += tDelta.z;
+ pos.z += step.z;
+ }
+
+ if (step.x > 0.0f)
+ {
+ if (pos.x >= end1.x)
+ {
+ reachedX = true;
+ }
+ }
+ else if (pos.x <= end1.x)
+ {
+ reachedX = true;
+ }
+
+ if (step.y > 0.0f)
+ {
+ if(pos.y >= end1.y)
+ {
+ reachedY = true;
+ }
+ }
+ else if (pos.y <= end1.y)
+ {
+ reachedY = true;
+ }
+
+ if (step.z > 0.0f)
+ {
+ if (pos.z >= end1.z)
+ {
+ reachedZ = true;
+ }
+ }
+ else if (pos.z <= end1.z)
+ {
+ reachedZ = true;
+ }
+
+ if (reachedX && reachedY && reachedZ)
+ {
+ return false;
+ }
+
+ BLOCKTYPE BlockID = m_World->GetBlock(pos.x, pos.y, pos.z);
+ // Block is counted as a collision if we are not doing a line of sight and it is solid,
+ // or if the block is not air and not water. That way mobs can still see underwater.
+ if ((!a_LineOfSight && g_BlockIsSolid[BlockID]) || (a_LineOfSight && (BlockID != E_BLOCK_AIR) && !IsBlockWater(BlockID)))
+ {
+ BlockHitPosition = pos;
+ int Normal = GetHitNormal(a_Start, End, pos );
+ if(Normal > 0)
+ {
+ HitNormal = m_NormalTable[Normal-1];
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+// return 1 = hit, other is not hit
+int LinesCross(float x0,float y0,float x1,float y1,float x2,float y2,float x3,float y3)
+{
+ //float linx, liny;
+
+ float d=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2);
+ if (abs(d)<0.001) {return 0;}
+ float AB=((y0-y2)*(x3-x2)-(x0-x2)*(y3-y2))/d;
+ if (AB>=0.0 && AB<=1.0)
+ {
+ float CD=((y0-y2)*(x1-x0)-(x0-x2)*(y1-y0))/d;
+ if (CD>=0.0 && CD<=1.0)
+ {
+ //linx=x0+AB*(x1-x0);
+ //liny=y0+AB*(y1-y0);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// intersect3D_SegmentPlane(): intersect a segment and a plane
+// Input: a_Ray = a segment, and a_Plane = a plane = {Point V0; Vector n;}
+// Output: *I0 = the intersect point (when it exists)
+// Return: 0 = disjoint (no intersection)
+// 1 = intersection in the unique point *I0
+// 2 = the segment lies in the plane
+int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal )
+{
+ Vector3f u = a_End - a_Origin;//a_Ray.P1 - S.P0;
+ Vector3f w = a_Origin - a_PlanePos;//S.P0 - Pn.V0;
+
+ float D = a_PlaneNormal.Dot( u );//dot(Pn.n, u);
+ float N = -(a_PlaneNormal.Dot( w ) );//-dot(a_Plane.n, w);
+
+ const float EPSILON = 0.0001f;
+ if (fabs(D) < EPSILON) { // segment is parallel to plane
+ if (N == 0) // segment lies in plane
+ return 2;
+ return 0; // no intersection
+ }
+ // they are not parallel
+ // compute intersect param
+ float sI = N / D;
+ if (sI < 0 || sI > 1)
+ return 0; // no intersection
+
+ //Vector3f I ( a_Ray->GetOrigin() + sI * u );//S.P0 + sI * u; // compute segment intersect point
+ RealHit = a_Origin + u * sI;
+ return 1;
+}
+
+int cTracer::GetHitNormal(const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos)
+{
+ Vector3i SmallBlockPos = a_BlockPos;
+ char BlockID = m_World->GetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z );
+
+ if( BlockID == E_BLOCK_AIR || IsBlockWater(BlockID))
+ return 0;
+
+ Vector3f BlockPos;
+ BlockPos = Vector3f(SmallBlockPos);
+
+ Vector3f Look = (end - start);
+ Look.Normalize();
+
+ float dot = Look.Dot( Vector3f(-1, 0, 0) ); // first face normal is x -1
+ if(dot < 0)
+ {
+ int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x, BlockPos.y, BlockPos.x, BlockPos.y + 1 );
+ if(Lines == 1)
+ {
+ Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x, BlockPos.z, BlockPos.x, BlockPos.z + 1 );
+ if(Lines == 1)
+ {
+ intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(-1, 0, 0) );
+ return 1;
+ }
+ }
+ }
+ dot = Look.Dot( Vector3f(0, 0, -1) ); // second face normal is z -1
+ if(dot < 0)
+ {
+ int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z, BlockPos.y, BlockPos.z, BlockPos.y + 1 );
+ if(Lines == 1)
+ {
+ Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z, BlockPos.x, BlockPos.z, BlockPos.x + 1 );
+ if(Lines == 1)
+ {
+ intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, 0, -1) );
+ return 2;
+ }
+ }
+ }
+ dot = Look.Dot( Vector3f(1, 0, 0) ); // third face normal is x 1
+ if(dot < 0)
+ {
+ int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x + 1, BlockPos.y, BlockPos.x + 1, BlockPos.y + 1 );
+ if(Lines == 1)
+ {
+ Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x + 1, BlockPos.z, BlockPos.x + 1, BlockPos.z + 1 );
+ if(Lines == 1)
+ {
+ intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(1, 0, 0), Vector3f(1, 0, 0) );
+ return 3;
+ }
+ }
+ }
+ dot = Look.Dot( Vector3f(0, 0, 1) ); // fourth face normal is z 1
+ if(dot < 0)
+ {
+ int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z + 1, BlockPos.y, BlockPos.z + 1, BlockPos.y + 1 );
+ if(Lines == 1)
+ {
+ Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z + 1, BlockPos.x, BlockPos.z + 1, BlockPos.x + 1 );
+ if(Lines == 1)
+ {
+ intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 0, 1), Vector3f(0, 0, 1) );
+ return 4;
+ }
+ }
+ }
+ dot = Look.Dot( Vector3f(0, 1, 0) ); // fifth face normal is y 1
+ if(dot < 0)
+ {
+ int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y + 1, BlockPos.x, BlockPos.y + 1, BlockPos.x + 1 );
+ if(Lines == 1)
+ {
+ Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y + 1, BlockPos.z, BlockPos.y + 1, BlockPos.z + 1 );
+ if(Lines == 1)
+ {
+ intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 1, 0), Vector3f(0, 1, 0) );
+ return 5;
+ }
+ }
+ }
+ dot = Look.Dot( Vector3f(0, -1, 0) ); // sixth face normal is y -1
+ if(dot < 0)
+ {
+ int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y, BlockPos.x, BlockPos.y, BlockPos.x + 1 );
+ if(Lines == 1)
+ {
+ Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y, BlockPos.z, BlockPos.y, BlockPos.z + 1 );
+ if(Lines == 1)
+ {
+ intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, -1, 0) );
+ return 6;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/src/Tracer.h b/src/Tracer.h
new file mode 100644
index 000000000..6c2ab6792
--- /dev/null
+++ b/src/Tracer.h
@@ -0,0 +1,82 @@
+
+#pragma once
+
+#include "Vector3i.h"
+#include "Vector3f.h"
+
+
+
+
+
+// fwd:
+class cWorld;
+
+
+
+
+
+// tolua_begin
+
+class cTracer
+{
+public:
+
+ /// Contains the position of the block that caused the collision
+ Vector3f BlockHitPosition;
+
+ /// Contains which face was hit
+ Vector3f HitNormal;
+
+ /// Contains the exact position where a collision occured. (BlockHitPosition + Offset on block)
+ Vector3f RealHit;
+
+
+ cTracer(cWorld * a_World);
+ ~cTracer();
+
+ /// Determines if a collision occures along a line. Returns true if a collision occurs.
+ bool Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance)
+ {
+ return Trace(a_Start, a_Direction, a_Distance, false);
+ }
+
+ /// Determines if a collision occures along a line. Returns true if a collision occurs.
+ /// When a_LineOfSight is true, we don't use the standard collision detection rules. Instead we use
+ /// the rules for monster vision. E.g. Only water and air do not block vision.
+ /// a_Distance is the number of iterations (blocks hits) that are tested.
+ bool Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight);
+
+private:
+
+ /// Preps Tracer object for call of Trace function. Only used internally.
+ void SetValues( const Vector3f & a_Start, const Vector3f & a_Direction );
+
+ /// Calculates where on the block a collision occured, if it does occur
+ /// Returns 0 if no intersection occured
+ /// Returns 1 if an intersection occured at a single point
+ /// Returns 2 if the line segment lies in the plane being checked
+ int intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal );
+
+ /// Determines which face on the block a collision occured, if it does occur
+ /// Returns 0 if the block is air, water or no collision occured
+ /// Return 1 through 6 for the following block faces, repectively: -x, -z, x, z, y, -y
+ int GetHitNormal( const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos);
+
+ float SigNum( float a_Num );
+ cWorld* m_World;
+
+ Vector3f m_NormalTable[6];
+
+ Vector3f dir;
+ Vector3f tDelta;
+ Vector3i pos;
+ Vector3i end1;
+ Vector3i step;
+ Vector3f tMax;
+};
+
+// tolua_end
+
+
+
+
diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp
new file mode 100644
index 000000000..7fd7cd996
--- /dev/null
+++ b/src/UI/SlotArea.cpp
@@ -0,0 +1,897 @@
+
+// SlotArea.cpp
+
+// Implements the cSlotArea class and its descendants
+
+#include "Globals.h"
+#include "SlotArea.h"
+#include "../Entities/Player.h"
+#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/DropSpenserEntity.h"
+#include "../BlockEntities/FurnaceEntity.h"
+#include "../Items/ItemHandler.h"
+#include "Window.h"
+#include "../CraftingRecipes.h"
+#include "../Root.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotArea:
+
+cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) :
+ m_NumSlots(a_NumSlots),
+ m_ParentWindow(a_ParentWindow)
+{
+}
+
+
+
+
+
+void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ /*
+ LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s",
+ GetNumSlots(), a_SlotNum,
+ ItemToFullString(a_ClickedItem).c_str(),
+ ItemToFullString(*GetSlot(a_SlotNum, a_Player)).c_str()
+ );
+ */
+
+ ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots()));
+
+ bool bAsync = false;
+ if (GetSlot(a_SlotNum, a_Player) == NULL)
+ {
+ LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum);
+ return;
+ }
+
+ switch (a_ClickAction)
+ {
+ case caShiftLeftClick:
+ case caShiftRightClick:
+ {
+ ShiftClicked(a_Player, a_SlotNum, a_ClickedItem);
+ return;
+ }
+
+ case caDblClick:
+ {
+ DblClicked(a_Player, a_SlotNum);
+ return;
+ }
+ }
+
+ cItem Slot(*GetSlot(a_SlotNum, a_Player));
+ if (!Slot.IsSameType(a_ClickedItem))
+ {
+ LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots);
+ LOGWARNING("My item: %s", ItemToFullString(Slot).c_str());
+ LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str());
+ bAsync = true;
+ }
+ cItem & DraggingItem = a_Player.GetDraggingItem();
+ switch (a_ClickAction)
+ {
+ case caRightClick:
+ {
+ if (DraggingItem.m_ItemType <= 0) // Empty-handed?
+ {
+ DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f);
+ Slot.m_ItemCount -= DraggingItem.m_ItemCount;
+ DraggingItem.m_ItemType = Slot.m_ItemType;
+ DraggingItem.m_ItemDamage = Slot.m_ItemDamage;
+
+ if (Slot.m_ItemCount <= 0)
+ {
+ Slot.Empty();
+ }
+ }
+ else if ((Slot.m_ItemType <= 0) || DraggingItem.IsEqual(Slot))
+ {
+ // Drop one item in slot
+ cItemHandler * Handler = ItemHandler(Slot.m_ItemType);
+ if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize()))
+ {
+ Slot.m_ItemType = DraggingItem.m_ItemType;
+ Slot.m_ItemCount++;
+ Slot.m_ItemDamage = DraggingItem.m_ItemDamage;
+ DraggingItem.m_ItemCount--;
+ }
+ if (DraggingItem.m_ItemCount <= 0)
+ {
+ DraggingItem.Empty();
+ }
+ }
+ else if (!DraggingItem.IsEqual(Slot))
+ {
+ // Swap contents
+ cItem tmp(DraggingItem);
+ DraggingItem = Slot;
+ Slot = tmp;
+ }
+ break;
+ }
+
+ case caLeftClick:
+ {
+ // Left-clicked
+ if (!DraggingItem.IsEqual(Slot))
+ {
+ // Switch contents
+ cItem tmp(DraggingItem);
+ DraggingItem = Slot;
+ Slot = tmp;
+ }
+ else
+ {
+ // Same type, add items:
+ cItemHandler * Handler = ItemHandler(DraggingItem.m_ItemType);
+ int FreeSlots = Handler->GetMaxStackSize() - Slot.m_ItemCount;
+ if (FreeSlots < 0)
+ {
+ ASSERT(!"Bad item stack size - where did we get more items in a slot than allowed?");
+ FreeSlots = 0;
+ }
+ int Filling = (FreeSlots > DraggingItem.m_ItemCount) ? DraggingItem.m_ItemCount : FreeSlots;
+ Slot.m_ItemCount += (char)Filling;
+ DraggingItem.m_ItemCount -= (char)Filling;
+ if (DraggingItem.m_ItemCount <= 0)
+ {
+ DraggingItem.Empty();
+ }
+ }
+ break;
+ }
+ default:
+ {
+ LOGWARNING("SlotArea: Unhandled click action: %d (%s)", a_ClickAction, ClickActionToString(a_ClickAction));
+ m_ParentWindow.BroadcastWholeWindow();
+ return;
+ }
+ } // switch (a_ClickAction
+
+ SetSlot(a_SlotNum, a_Player, Slot);
+ if (bAsync)
+ {
+ m_ParentWindow.BroadcastWholeWindow();
+ }
+
+}
+
+
+
+
+
+void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem)
+{
+ // Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover:
+ cItem Slot(*GetSlot(a_SlotNum, a_Player));
+ m_ParentWindow.DistributeStack(Slot, a_Player, this, true);
+ if (Slot.IsEmpty())
+ {
+ // Empty the slot completely, the cilent doesn't like left-over ItemType with zero count
+ Slot.Empty();
+ }
+ SetSlot(a_SlotNum, a_Player, Slot);
+
+ // Some clients try to guess our actions and not always right (armor slots in 1.2.5), so we fix them:
+ m_ParentWindow.BroadcastWholeWindow();
+}
+
+
+
+
+
+void cSlotArea::DblClicked(cPlayer & a_Player, int a_SlotNum)
+{
+ cItem & Dragging = a_Player.GetDraggingItem();
+ if (Dragging.IsEmpty())
+ {
+ // Move the item in the dblclicked slot into hand:
+ Dragging = *GetSlot(a_SlotNum, a_Player);
+ cItem EmptyItem;
+ SetSlot(a_SlotNum, a_Player, EmptyItem);
+ }
+ if (Dragging.IsEmpty())
+ {
+ LOGD("%s DblClicked with an empty hand over empty slot, ignoring", a_Player.GetName().c_str());
+ return;
+ }
+
+ // Add as many items from the surrounding area into hand as possible:
+ // First skip full stacks, then if there's still space, process full stacks as well:
+ if (!m_ParentWindow.CollectItemsToHand(Dragging, *this, a_Player, false))
+ {
+ m_ParentWindow.CollectItemsToHand(Dragging, *this, a_Player, true);
+ }
+
+ m_ParentWindow.BroadcastWholeWindow(); // We need to broadcast, in case the window was a chest opened by multiple players
+}
+
+
+
+
+
+void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots)
+{
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ const cItem * Slot = GetSlot(i, a_Player);
+ if (!Slot->IsStackableWith(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
+ {
+ // Different items
+ continue;
+ }
+ int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
+ if (NumFit <= 0)
+ {
+ // Full stack already
+ continue;
+ }
+ if (NumFit > a_ItemStack.m_ItemCount)
+ {
+ NumFit = a_ItemStack.m_ItemCount;
+ }
+ if (a_Apply)
+ {
+ cItem NewSlot(a_ItemStack);
+ NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit;
+ SetSlot(i, a_Player, NewSlot);
+ }
+ a_ItemStack.m_ItemCount -= NumFit;
+ if (a_ItemStack.IsEmpty())
+ {
+ return;
+ }
+ } // for i - Slots
+}
+
+
+
+
+
+bool cSlotArea::CollectItemsToHand(cItem & a_Dragging, cPlayer & a_Player, bool a_CollectFullStacks)
+{
+ int NumSlots = GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+ const cItem & SlotItem = *GetSlot(i, a_Player);
+ if (!SlotItem.IsStackableWith(a_Dragging))
+ {
+ continue;
+ }
+ int ToMove = a_Dragging.GetMaxStackSize() - a_Dragging.m_ItemCount;
+ if (ToMove > SlotItem.m_ItemCount)
+ {
+ ToMove = SlotItem.m_ItemCount;
+ }
+ a_Dragging.m_ItemCount += ToMove;
+ cItem NewSlot(SlotItem);
+ NewSlot.m_ItemCount -= ToMove;
+ SetSlot(i, a_Player, NewSlot);
+ if (!NewSlot.IsEmpty())
+ {
+ // There are leftovers in the slot, so a_Dragging must be full
+ return true;
+ }
+ } // for i - Slots[]
+ // a_Dragging may be full if there were exactly the number of items needed to fill it
+ return a_Dragging.IsFullStack();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaChest:
+
+cSlotAreaChest::cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow) :
+ cSlotArea(27, a_ParentWindow),
+ m_Chest(a_Chest)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly:
+ return &(m_Chest->GetSlot(a_SlotNum));
+}
+
+
+
+
+
+void cSlotAreaChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ m_Chest->SetSlot(a_SlotNum, a_Item);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaDoubleChest:
+
+cSlotAreaDoubleChest::cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow) :
+ cSlotArea(54, a_ParentWindow),
+ m_TopChest(a_TopChest),
+ m_BottomChest(a_BottomChest)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaDoubleChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 53, use that to index the correct chest's inventory:
+ if (a_SlotNum < 27)
+ {
+ return &(m_TopChest->GetSlot(a_SlotNum));
+ }
+ else
+ {
+ return &(m_BottomChest->GetSlot(a_SlotNum - 27));
+ }
+}
+
+
+
+
+
+void cSlotAreaDoubleChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ if (a_SlotNum < 27)
+ {
+ m_TopChest->SetSlot(a_SlotNum, a_Item);
+ }
+ else
+ {
+ m_BottomChest->SetSlot(a_SlotNum - 27, a_Item);
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaCrafting:
+
+cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) :
+ cSlotAreaTemporary(1 + a_GridSize * a_GridSize, a_ParentWindow),
+ m_GridSize(a_GridSize)
+{
+ ASSERT((a_GridSize == 2) || (a_GridSize == 3));
+}
+
+
+
+
+
+void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ // Override for craft result slot
+ if (a_SlotNum == 0)
+ {
+ if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick))
+ {
+ ShiftClickedResult(a_Player);
+ }
+ else
+ {
+ ClickedResult(a_Player);
+ }
+ return;
+ }
+ super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
+ UpdateRecipe(a_Player);
+}
+
+
+
+
+
+void cSlotAreaCrafting::DblClicked(cPlayer & a_Player, int a_SlotNum)
+{
+ if (a_SlotNum == 0)
+ {
+ // Dbl-clicking the crafting result slot shouldn't collect items to hand
+ return;
+ }
+ super::DblClicked(a_Player, a_SlotNum);
+}
+
+
+
+
+
+void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player)
+{
+ // Toss all items on the crafting grid:
+ TossItems(a_Player, 1, m_NumSlots);
+
+ // Remove the current recipe from the player -> recipe map:
+ for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr)
+ {
+ if (itr->first == a_Player.GetUniqueID())
+ {
+ // Remove the player from the recipe map:
+ m_Recipes.erase(itr);
+ return;
+ }
+ } // for itr - m_Recipes[]
+ // Player not found - that is acceptable
+}
+
+
+
+
+
+void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player)
+{
+ const cItem * ResultSlot = GetSlot(0, a_Player);
+ cItem & DraggingItem = a_Player.GetDraggingItem();
+
+ // Get the current recipe:
+ cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
+
+ cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
+ cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize);
+
+ // If possible, craft:
+ if (DraggingItem.IsEmpty())
+ {
+ DraggingItem = Recipe.GetResult();
+ Recipe.ConsumeIngredients(Grid);
+ Grid.CopyToItems(PlayerSlots);
+ }
+ else if (DraggingItem.IsEqual(Recipe.GetResult()))
+ {
+ cItemHandler * Handler = ItemHandler(Recipe.GetResult().m_ItemType);
+ if (DraggingItem.m_ItemCount + Recipe.GetResult().m_ItemCount <= Handler->GetMaxStackSize())
+ {
+ DraggingItem.m_ItemCount += Recipe.GetResult().m_ItemCount;
+ Recipe.ConsumeIngredients(Grid);
+ Grid.CopyToItems(PlayerSlots);
+ }
+ }
+
+ // Get the new recipe and update the result slot:
+ UpdateRecipe(a_Player);
+
+ // We're done. Send all changes to the client and bail out:
+ m_ParentWindow.BroadcastWholeWindow();
+}
+
+
+
+
+
+void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player)
+{
+ cItem Result(*GetSlot(0, a_Player));
+ if (Result.IsEmpty())
+ {
+ return;
+ }
+ cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
+ do
+ {
+ // Try distributing the result. If it fails, bail out:
+ cItem ResultCopy(Result);
+ m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, false);
+ if (!ResultCopy.IsEmpty())
+ {
+ // Couldn't distribute all of it. Bail out
+ return;
+ }
+
+ // Distribute the result, this time for real:
+ ResultCopy = Result;
+ m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true);
+
+ // Remove the ingredients from the crafting grid and update the recipe:
+ cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
+ cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize);
+ Recipe.ConsumeIngredients(Grid);
+ Grid.CopyToItems(PlayerSlots);
+ UpdateRecipe(a_Player);
+ if (!Recipe.GetResult().IsEqual(Result))
+ {
+ // The recipe has changed, bail out
+ return;
+ }
+ } while (true);
+}
+
+
+
+
+
+void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player)
+{
+ cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize);
+ cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
+ cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
+ SetSlot(0, a_Player, Recipe.GetResult());
+ m_ParentWindow.SendSlot(a_Player, this, 0);
+}
+
+
+
+
+
+cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player)
+{
+ for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr)
+ {
+ if (itr->first == a_Player.GetUniqueID())
+ {
+ return itr->second;
+ }
+ } // for itr - m_Recipes[]
+
+ // Not found. Add a new one:
+ cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize);
+ cCraftingRecipe Recipe(Grid);
+ cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
+ m_Recipes.push_back(std::make_pair(a_Player.GetUniqueID(), Recipe));
+ return m_Recipes.back().second;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaFurnace:
+
+cSlotAreaFurnace::cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow) :
+ cSlotArea(3, a_ParentWindow),
+ m_Furnace(a_Furnace)
+{
+ m_Furnace->GetContents().AddListener(*this);
+}
+
+
+
+
+
+cSlotAreaFurnace::~cSlotAreaFurnace()
+{
+ m_Furnace->GetContents().RemoveListener(*this);
+}
+
+
+
+
+
+void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
+
+ if (m_Furnace == NULL)
+ {
+ LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
+ ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
+ return;
+ }
+}
+
+
+
+
+
+const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 2, query the items from the underlying furnace:
+ return &(m_Furnace->GetSlot(a_SlotNum));
+}
+
+
+
+
+
+void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ m_Furnace->SetSlot(a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ // Something has changed in the window, broadcast the entire window to all clients
+ ASSERT(a_ItemGrid == &(m_Furnace->GetContents()));
+
+ m_ParentWindow.BroadcastWholeWindow();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaInventoryBase:
+
+cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow) :
+ cSlotArea(a_NumSlots, a_ParentWindow),
+ m_SlotOffset(a_SlotOffset)
+{
+}
+
+
+
+
+
+void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ if (a_Player.IsGameModeCreative() && (m_ParentWindow.GetWindowType() == cWindow::wtInventory))
+ {
+ // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it
+ SetSlot(a_SlotNum, a_Player, a_ClickedItem);
+ return;
+ }
+
+ // Survival inventory and all other windows' inventory has the same handling as normal slot areas
+ super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
+ return;
+}
+
+
+
+
+
+const cItem * cSlotAreaInventoryBase::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 35, map that to the player's inventory slots according to the internal offset
+ return &a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset);
+}
+
+
+
+
+
+void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ a_Player.GetInventory().SetSlot(a_SlotNum + m_SlotOffset, a_Item);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaArmor:
+
+void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
+{
+ if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(0, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+ else if (ItemCategory::IsChestPlate(a_ItemStack.m_ItemType) && GetSlot(1, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(1, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+ else if (ItemCategory::IsLeggings(a_ItemStack.m_ItemType) && GetSlot(2, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(2, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+ else if (ItemCategory::IsBoots(a_ItemStack.m_ItemType) && GetSlot(3, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(3, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaItemGrid:
+
+cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow) :
+ super(a_ItemGrid.GetNumSlots(), a_ParentWindow),
+ m_ItemGrid(a_ItemGrid)
+{
+ m_ItemGrid.AddListener(*this);
+}
+
+
+
+
+
+cSlotAreaItemGrid::~cSlotAreaItemGrid()
+{
+ m_ItemGrid.RemoveListener(*this);
+}
+
+
+
+
+
+const cItem * cSlotAreaItemGrid::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ return &m_ItemGrid.GetSlot(a_SlotNum);
+}
+
+
+
+
+
+void cSlotAreaItemGrid::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ m_ItemGrid.SetSlot(a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ ASSERT(a_ItemGrid == &m_ItemGrid);
+ m_ParentWindow.BroadcastSlot(this, a_SlotNum);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaTemporary:
+
+cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) :
+ cSlotArea(a_NumSlots, a_ParentWindow)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaTemporary::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ cItemMap::const_iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ LOGERROR("cSlotAreaTemporary: player \"%s\" not found for slot %d!", a_Player.GetName().c_str(), a_SlotNum);
+ ASSERT(!"cSlotAreaTemporary: player not found!");
+
+ // Player not found, this should not happen, ever! Return NULL, but things may break by this.
+ return NULL;
+ }
+
+ if (a_SlotNum >= (int)(itr->second.size()))
+ {
+ LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!");
+ ASSERT(!"cSlotAreaTemporary: asking for more slots than actually stored!");
+ return NULL;
+ }
+
+ return &(itr->second[a_SlotNum]);
+}
+
+
+
+
+
+void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ // Player not found
+ LOGWARNING("cSlotAreaTemporary: player not found!");
+ return;
+ }
+
+ if (a_SlotNum >= (int)(itr->second.size()))
+ {
+ LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!");
+ return;
+ }
+
+ itr->second[a_SlotNum] = a_Item;
+}
+
+
+
+
+
+void cSlotAreaTemporary::OnPlayerAdded(cPlayer & a_Player)
+{
+ ASSERT(m_Items.find(a_Player.GetUniqueID()) == m_Items.end()); // The player shouldn't be in the itemmap, otherwise we probably have a leak
+ m_Items[a_Player.GetUniqueID()].resize(m_NumSlots); // Make the vector the specified size of empty items
+}
+
+
+
+
+
+void cSlotAreaTemporary::OnPlayerRemoved(cPlayer & a_Player)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ ASSERT(itr != m_Items.end()); // The player should be in the list, otherwise a call to OnPlayerAdded() was mismatched
+ m_Items.erase(itr);
+}
+
+
+
+
+
+void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ LOGWARNING("Player tossing items (%s) not found in the item map", a_Player.GetName().c_str());
+ return;
+ }
+
+ cItems Drops;
+ for (int i = a_Begin; i < a_End; i++)
+ {
+ cItem & Item = itr->second[i];
+ if (!Item.IsEmpty())
+ {
+ Drops.push_back(Item);
+ }
+ Item.Empty();
+ } // for i - itr->second[]
+
+ double vX = 0, vY = 0, vZ = 0;
+ EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY);
+ vY = -vY * 2 + 1.f;
+ a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because player created
+}
+
+
+
+
+
+cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ return NULL;
+ }
+ return &(itr->second[0]);
+}
+
+
+
+
diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h
new file mode 100644
index 000000000..b1944d901
--- /dev/null
+++ b/src/UI/SlotArea.h
@@ -0,0 +1,313 @@
+
+// SlotArea.h
+
+// Interfaces to the cSlotArea class representing a contiguous area of slots in a UI window
+
+
+
+
+#pragma once
+
+#include "../Inventory.h"
+
+
+
+class cWindow;
+class cPlayer;
+class cChestEntity;
+class cDropSpenserEntity;
+class cFurnaceEntity;
+class cCraftingRecipe;
+
+
+
+
+
+class cSlotArea
+{
+public:
+ cSlotArea(int a_NumSlots, cWindow & a_ParentWindow);
+ virtual ~cSlotArea() {} // force a virtual destructor in all subclasses
+
+ int GetNumSlots(void) const { return m_NumSlots; }
+
+ /// Called to retrieve an item in the specified slot for the specified player. Must return a valid cItem.
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const = 0;
+
+ /// Called to set an item in the specified slot for the specified player
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) = 0;
+
+ /// Called when a player clicks in the window. Parameters taken from the click packet.
+ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem);
+
+ /// Called from Clicked when the action is a shiftclick (left or right)
+ virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem);
+
+ /// Called from Clicked when the action is a caDblClick
+ virtual void DblClicked(cPlayer & a_Player, int a_SlotNum);
+
+ /// Called when a new player opens the same parent window. The window already tracks the player. CS-locked.
+ virtual void OnPlayerAdded(cPlayer & a_Player) {} ;
+
+ /// Called when one of the players closes the parent window. The window already doesn't track the player. CS-locked.
+ virtual void OnPlayerRemoved(cPlayer & a_Player) {} ;
+
+ /** Called to store as much of a_ItemStack in the area as possible. a_ItemStack is modified to reflect the change.
+ The default implementation searches each slot for available space and distributes the stack there.
+ if a_ShouldApply is true, the changes are written into the slots;
+ if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes)
+ If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled
+ */
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots);
+
+ /// Called on DblClicking to collect all stackable items into hand.
+ /// The items are accumulated in a_Dragging and removed from the slots immediately.
+ /// If a_CollectFullStacks is false, slots with full stacks are skipped while collecting.
+ /// Returns true if full stack has been collected in a_Dragging, false if there's space remaining to fill.
+ virtual bool CollectItemsToHand(cItem & a_Dragging, cPlayer & a_Player, bool a_CollectFullStacks);
+
+protected:
+ int m_NumSlots;
+ cWindow & m_ParentWindow;
+} ;
+
+
+
+
+
+/// Handles any part of the inventory, using parameters in constructor to distinguish between the parts
+class cSlotAreaInventoryBase :
+ public cSlotArea
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow);
+
+ // Creative inventory's click handling is somewhat different from survival inventory's, handle that here:
+ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ int m_SlotOffset; // Index that this area's slot 0 has in the underlying cInventory
+} ;
+
+
+
+
+
+/// Handles the main inventory of each player, excluding the armor and hotbar
+class cSlotAreaInventory :
+ public cSlotAreaInventoryBase
+{
+ typedef cSlotAreaInventoryBase super;
+
+public:
+ cSlotAreaInventory(cWindow & a_ParentWindow) :
+ cSlotAreaInventoryBase(cInventory::invInventoryCount, cInventory::invInventoryOffset, a_ParentWindow)
+ {
+ }
+} ;
+
+
+
+
+
+/// Handles the hotbar of each player
+class cSlotAreaHotBar :
+ public cSlotAreaInventoryBase
+{
+ typedef cSlotAreaInventoryBase super;
+
+public:
+ cSlotAreaHotBar(cWindow & a_ParentWindow) :
+ cSlotAreaInventoryBase(cInventory::invHotbarCount, cInventory::invHotbarOffset, a_ParentWindow)
+ {
+ }
+} ;
+
+
+
+
+
+/// Handles the armor area of the player's inventory
+class cSlotAreaArmor :
+ public cSlotAreaInventoryBase
+{
+public:
+ cSlotAreaArmor(cWindow & a_ParentWindow) :
+ cSlotAreaInventoryBase(cInventory::invArmorCount, cInventory::invArmorOffset, a_ParentWindow)
+ {
+ }
+
+ // Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.)
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
+} ;
+
+
+
+
+
+/// Handles any slot area that is representing a cItemGrid; same items for all the players
+class cSlotAreaItemGrid :
+ public cSlotArea,
+ public cItemGrid::cListener
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow);
+
+ virtual ~cSlotAreaItemGrid();
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cItemGrid & m_ItemGrid;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+} ;
+
+
+
+
+
+/** A cSlotArea with items layout that is private to each player and is temporary, such as
+a crafting grid or an enchantment table.
+This common ancestor stores the items in a per-player map. It also implements tossing items from the map.
+*/
+class cSlotAreaTemporary :
+ public cSlotArea
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow);
+
+ // cSlotArea overrides:
+ virtual const cItem * GetSlot (int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+ virtual void OnPlayerAdded (cPlayer & a_Player) override;
+ virtual void OnPlayerRemoved(cPlayer & a_Player) override;
+
+ /// Tosses the player's items in slots [a_Begin, a_End) (ie. incl. a_Begin, but excl. a_End)
+ void TossItems(cPlayer & a_Player, int a_Begin, int a_End);
+
+protected:
+ typedef std::map<int, std::vector<cItem> > cItemMap; // Maps EntityID -> items
+
+ cItemMap m_Items;
+
+ /// Returns the pointer to the slot array for the player specified.
+ cItem * GetPlayerSlots(cPlayer & a_Player);
+} ;
+
+
+
+
+
+class cSlotAreaCrafting :
+ public cSlotAreaTemporary
+{
+ typedef cSlotAreaTemporary super;
+
+public:
+ /// a_GridSize is allowed to be only 2 or 3
+ cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow);
+
+ // cSlotAreaTemporary overrides:
+ virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
+ virtual void DblClicked (cPlayer & a_Player, int a_SlotNum);
+ virtual void OnPlayerRemoved(cPlayer & a_Player) override;
+
+ // Distributing items into this area is completely disabled
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override {}
+
+protected:
+ /// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params
+ typedef std::list<std::pair<int, cCraftingRecipe> > cRecipeMap;
+
+ int m_GridSize;
+ cRecipeMap m_Recipes;
+
+ /// Handles a click in the result slot. Crafts using the current recipe, if possible
+ void ClickedResult(cPlayer & a_Player);
+
+ /// Handles a shift-click in the result slot. Crafts using the current recipe until it changes or no more space for result.
+ void ShiftClickedResult(cPlayer & a_Player);
+
+ /// Updates the current recipe and result slot based on the ingredients currently in the crafting grid of the specified player
+ void UpdateRecipe(cPlayer & a_Player);
+
+ /// Retrieves the recipe for the specified player from the map, or creates one if not found
+ cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player);
+} ;
+
+
+
+
+
+class cSlotAreaChest :
+ public cSlotArea
+{
+public:
+ cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow);
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cChestEntity * m_Chest;
+} ;
+
+
+
+
+
+class cSlotAreaDoubleChest :
+ public cSlotArea
+{
+public:
+ cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow);
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cChestEntity * m_TopChest;
+ cChestEntity * m_BottomChest;
+} ;
+
+
+
+
+
+class cSlotAreaFurnace :
+ public cSlotArea,
+ public cItemGrid::cListener
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow);
+
+ virtual ~cSlotAreaFurnace();
+
+ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cFurnaceEntity * m_Furnace;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+} ;
+
+
+
+
diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp
new file mode 100644
index 000000000..f5c62692f
--- /dev/null
+++ b/src/UI/Window.cpp
@@ -0,0 +1,886 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Window.h"
+#include "WindowOwner.h"
+#include "SlotArea.h"
+#include "../Item.h"
+#include "../ClientHandle.h"
+#include "../Entities/Player.h"
+#include "../Entities/Pickup.h"
+#include "../Inventory.h"
+#include "../Items/ItemHandler.h"
+#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/DropSpenserEntity.h"
+#include "../BlockEntities/HopperEntity.h"
+
+
+
+
+
+char cWindow::m_WindowIDCounter = 1;
+
+
+
+
+
+cWindow::cWindow(WindowType a_WindowType, const AString & a_WindowTitle) :
+ m_WindowID((++m_WindowIDCounter) % 127),
+ m_WindowType(a_WindowType),
+ m_WindowTitle(a_WindowTitle),
+ m_Owner(NULL),
+ m_IsDestroyed(false),
+ m_ShouldDistributeToHotbarFirst(true)
+{
+ if (a_WindowType == wtInventory)
+ {
+ m_WindowID = 0;
+ }
+}
+
+
+
+
+
+cWindow::~cWindow()
+{
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ m_SlotAreas.clear();
+}
+
+
+
+
+
+int cWindow::GetNumSlots(void) const
+{
+ int res = 0;
+ for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ res += (*itr)->GetNumSlots();
+ } // for itr - m_SlotAreas[]
+ return res;
+}
+
+
+
+
+
+const cItem * cWindow::GetSlot(cPlayer & a_Player, int a_SlotNum) const
+{
+ // Return the item at the specified slot for the specified player
+ int LocalSlotNum = 0;
+ const cSlotArea * Area = GetSlotArea(a_SlotNum, LocalSlotNum);
+ if (Area == NULL)
+ {
+ LOGWARNING("%s: requesting item from an invalid SlotArea (SlotNum %d), returning NULL.", __FUNCTION__, a_SlotNum);
+ return NULL;
+ }
+ return Area->GetSlot(LocalSlotNum, a_Player);
+}
+
+
+
+
+
+void cWindow::SetSlot(cPlayer & a_Player, int a_SlotNum, const cItem & a_Item)
+{
+ // Set the item to the specified slot for the specified player
+ int LocalSlotNum = 0;
+ cSlotArea * Area = GetSlotArea(a_SlotNum, LocalSlotNum);
+ if (Area == NULL)
+ {
+ LOGWARNING("%s: requesting write to an invalid SlotArea (SlotNum %d), ignoring.", __FUNCTION__, a_SlotNum);
+ return;
+ }
+ Area->SetSlot(LocalSlotNum, a_Player, a_Item);
+}
+
+
+
+
+
+bool cWindow::IsSlotInPlayerMainInventory(int a_SlotNum) const
+{
+ // Returns true if the specified slot is in the Player Main Inventory slotarea
+ // The player main inventory is always 27 slots, 9 slots from the end of the inventory
+ return ((a_SlotNum >= GetNumSlots() - 36) && (a_SlotNum < GetNumSlots() - 9));
+}
+
+
+
+
+
+bool cWindow::IsSlotInPlayerHotbar(int a_SlotNum) const
+{
+ // Returns true if the specified slot is in the Player Hotbar slotarea
+ // The hotbar is always the last 9 slots
+ return ((a_SlotNum >= GetNumSlots() - 9) && (a_SlotNum < GetNumSlots()));
+}
+
+
+
+
+
+bool cWindow::IsSlotInPlayerInventory(int a_SlotNum) const
+{
+ // Returns true if the specified slot is in the Player Main Inventory or Hotbar slotareas. Note that returns false for Armor.
+ // The player combined inventory is always the last 36 slots
+ return ((a_SlotNum >= GetNumSlots() - 36) && (a_SlotNum < GetNumSlots()));
+}
+
+
+
+
+
+void cWindow::GetSlots(cPlayer & a_Player, cItems & a_Slots) const
+{
+ a_Slots.clear();
+ a_Slots.reserve(GetNumSlots());
+ for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ int NumSlots = (*itr)->GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+
+ const cItem * Item = (*itr)->GetSlot(i, a_Player);
+ if (Item == NULL)
+ {
+ a_Slots.push_back(cItem());
+ }
+ else
+ {
+ a_Slots.push_back(*Item);
+ }
+ }
+ } // for itr - m_SlotAreas[]
+}
+
+
+
+
+
+void cWindow::Clicked(
+ cPlayer & a_Player,
+ int a_WindowID, short a_SlotNum, eClickAction a_ClickAction,
+ const cItem & a_ClickedItem
+)
+{
+ if (a_WindowID != m_WindowID)
+ {
+ LOGWARNING("%s: Wrong window ID (exp %d, got %d) received from \"%s\"; ignoring click.", __FUNCTION__, m_WindowID, a_WindowID, a_Player.GetName().c_str());
+ return;
+ }
+
+ switch (a_ClickAction)
+ {
+ case caRightClickOutside:
+ {
+ // Toss one of the dragged items:
+ a_Player.TossItem(true);
+ return;
+ }
+ case caLeftClickOutside:
+ {
+ // Toss all dragged items:
+ a_Player.TossItem(true, a_Player.GetDraggingItem().m_ItemCount);
+ return;
+ }
+ case caLeftClickOutsideHoldNothing:
+ case caRightClickOutsideHoldNothing:
+ {
+ // Nothing needed
+ return;
+ }
+ case caLeftPaintBegin: OnPaintBegin (a_Player); return;
+ case caRightPaintBegin: OnPaintBegin (a_Player); return;
+ case caLeftPaintProgress: OnPaintProgress(a_Player, a_SlotNum); return;
+ case caRightPaintProgress: OnPaintProgress(a_Player, a_SlotNum); return;
+ case caLeftPaintEnd: OnLeftPaintEnd (a_Player); return;
+ case caRightPaintEnd: OnRightPaintEnd(a_Player); return;
+ }
+
+ if (a_SlotNum < 0)
+ {
+ // TODO: Other click actions with irrelevant slot number (FS #371)
+ return;
+ }
+
+ int LocalSlotNum = a_SlotNum;
+ int idx = 0;
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ if (LocalSlotNum < (*itr)->GetNumSlots())
+ {
+ (*itr)->Clicked(a_Player, LocalSlotNum, a_ClickAction, a_ClickedItem);
+ return;
+ }
+ LocalSlotNum -= (*itr)->GetNumSlots();
+ idx++;
+ }
+
+ LOGWARNING("Slot number higher than available window slots: %d, max %d received from \"%s\"; ignoring.",
+ a_SlotNum, GetNumSlots(), a_Player.GetName().c_str()
+ );
+}
+
+
+
+
+
+void cWindow::OpenedByPlayer(cPlayer & a_Player)
+{
+ {
+ cCSLock Lock(m_CS);
+ // If player is already in OpenedBy remove player first
+ m_OpenedBy.remove(&a_Player);
+ // Then add player
+ m_OpenedBy.push_back(&a_Player);
+
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ (*itr)->OnPlayerAdded(a_Player);
+ } // for itr - m_SlotAreas[]
+ }
+
+ a_Player.GetClientHandle()->SendWindowOpen(*this);
+}
+
+
+
+
+
+bool cWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
+{
+ // Checks whether the player is still holding an item
+ if (a_Player.IsDraggingItem())
+ {
+ LOGD("Player holds item! Dropping it...");
+ a_Player.TossItem(true, a_Player.GetDraggingItem().m_ItemCount);
+ }
+
+ cClientHandle * ClientHandle = a_Player.GetClientHandle();
+ if (ClientHandle != NULL)
+ {
+ ClientHandle->SendWindowClose(*this);
+ }
+
+ {
+ cCSLock Lock(m_CS);
+
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ (*itr)->OnPlayerRemoved(a_Player);
+ } // for itr - m_SlotAreas[]
+
+ m_OpenedBy.remove(&a_Player);
+
+ if ((m_WindowType != wtInventory) && m_OpenedBy.empty())
+ {
+ Destroy();
+ }
+ }
+ if (m_IsDestroyed)
+ {
+ delete this;
+ }
+
+ return true;
+}
+
+
+
+
+
+void cWindow::OwnerDestroyed()
+{
+ m_Owner = NULL;
+ // Close window for each player. Note that the last one needs special handling
+ while (m_OpenedBy.size() > 1)
+ {
+ (*m_OpenedBy.begin() )->CloseWindow();
+ }
+ (*m_OpenedBy.begin() )->CloseWindow();
+}
+
+
+
+
+
+bool cWindow::ForEachPlayer(cItemCallback<cPlayer> & a_Callback)
+{
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr)
+ {
+ if (a_Callback.Item(*itr))
+ {
+ return false;
+ }
+ } // for itr - m_OpenedBy[]
+ return true;
+}
+
+
+
+
+
+bool cWindow::ForEachClient(cItemCallback<cClientHandle> & a_Callback)
+{
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr)
+ {
+ if (a_Callback.Item((*itr)->GetClientHandle()))
+ {
+ return false;
+ }
+ } // for itr - m_OpenedBy[]
+ return true;
+}
+
+
+
+
+
+void cWindow::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply)
+{
+ // Ask each slot area to take as much of the stack as it can.
+ // First ask only slots that already have the same kind of item
+ // Then ask any remaining slots
+ for (int Pass = 0; Pass < 2; ++Pass)
+ {
+ if (m_ShouldDistributeToHotbarFirst)
+ {
+ // First distribute into the hotbar:
+ if (a_ExcludeArea != m_SlotAreas.back())
+ {
+ m_SlotAreas.back()->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0));
+ if (a_ItemStack.IsEmpty())
+ {
+ // Distributed it all
+ return;
+ }
+ }
+ }
+
+ // The distribute to all other areas:
+ cSlotAreas::iterator end = m_ShouldDistributeToHotbarFirst ? (m_SlotAreas.end() - 1) : m_SlotAreas.end();
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(); itr != end; ++itr)
+ {
+ if (*itr == a_ExcludeArea)
+ {
+ continue;
+ }
+ (*itr)->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0));
+ if (a_ItemStack.IsEmpty())
+ {
+ // Distributed it all
+ return;
+ }
+ } // for itr - m_SlotAreas[]
+ } // for Pass - repeat twice
+}
+
+
+
+
+
+bool cWindow::CollectItemsToHand(cItem & a_Dragging, cSlotArea & a_Area, cPlayer & a_Player, bool a_CollectFullStacks)
+{
+ // First ask the slot areas from a_Area till the end of list:
+ bool ShouldCollect = false;
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ if (&a_Area == *itr)
+ {
+ ShouldCollect = true;
+ }
+ if (!ShouldCollect)
+ {
+ continue;
+ }
+ if ((*itr)->CollectItemsToHand(a_Dragging, a_Player, a_CollectFullStacks))
+ {
+ // a_Dragging is full
+ return true;
+ }
+ }
+
+ // a_Dragging still not full, ask slot areas before a_Area in the list:
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ if (*itr == &a_Area)
+ {
+ // All areas processed
+ return false;
+ }
+ if ((*itr)->CollectItemsToHand(a_Dragging, a_Player, a_CollectFullStacks))
+ {
+ // a_Dragging is full
+ return true;
+ }
+ }
+ // Shouldn't reach here
+ // a_Area is expected to be part of m_SlotAreas[], so the "return false" in the loop above should have returned already
+ ASSERT(!"This branch should not be reached");
+ return false;
+}
+
+
+
+
+
+void cWindow::SendSlot(cPlayer & a_Player, cSlotArea * a_SlotArea, int a_RelativeSlotNum)
+{
+ int SlotBase = 0;
+ bool Found = false;
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ if (*itr == a_SlotArea)
+ {
+ Found = true;
+ break;
+ }
+ SlotBase += (*itr)->GetNumSlots();
+ } // for itr - m_SlotAreas[]
+ if (!Found)
+ {
+ LOGERROR("cWindow::SendSlot(): unknown a_SlotArea");
+ ASSERT(!"cWindow::SendSlot(): unknown a_SlotArea");
+ return;
+ }
+
+ a_Player.GetClientHandle()->SendInventorySlot(
+ m_WindowID, a_RelativeSlotNum + SlotBase, *(a_SlotArea->GetSlot(a_RelativeSlotNum, a_Player))
+ );
+}
+
+
+
+
+
+void cWindow::Destroy(void)
+{
+ if (m_Owner != NULL)
+ {
+ m_Owner->CloseWindow();
+ m_Owner = NULL;
+ }
+ m_IsDestroyed = true;
+}
+
+
+
+
+
+cSlotArea * cWindow::GetSlotArea(int a_GlobalSlotNum, int & a_LocalSlotNum)
+{
+ if ((a_GlobalSlotNum < 0) || (a_GlobalSlotNum >= GetNumSlots()))
+ {
+ LOGWARNING("%s: requesting an invalid SlotNum: %d out of %d slots", __FUNCTION__, a_GlobalSlotNum, GetNumSlots() - 1);
+ ASSERT(!"Invalid SlotNum");
+ return NULL;
+ }
+
+ // Iterate through all the SlotAreas, find the correct one
+ int LocalSlotNum = a_GlobalSlotNum;
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ if (LocalSlotNum < (*itr)->GetNumSlots())
+ {
+ a_LocalSlotNum = LocalSlotNum;
+ return *itr;
+ }
+ LocalSlotNum -= (*itr)->GetNumSlots();
+ } // for itr - m_SlotAreas[]
+
+ // We shouldn't be here - the check at the beginnning should prevent this. Log and assert
+ LOGWARNING("%s: GetNumSlots() is out of sync: %d; LocalSlotNum = %d", __FUNCTION__, GetNumSlots(), LocalSlotNum);
+ ASSERT(!"Invalid GetNumSlots");
+ return NULL;
+}
+
+
+
+
+
+const cSlotArea * cWindow::GetSlotArea(int a_GlobalSlotNum, int & a_LocalSlotNum) const
+{
+ if ((a_GlobalSlotNum < 0) || (a_GlobalSlotNum >= GetNumSlots()))
+ {
+ LOGWARNING("%s: requesting an invalid SlotNum: %d out of %d slots", __FUNCTION__, a_GlobalSlotNum, GetNumSlots() - 1);
+ ASSERT(!"Invalid SlotNum");
+ return NULL;
+ }
+
+ // Iterate through all the SlotAreas, find the correct one
+ int LocalSlotNum = a_GlobalSlotNum;
+ for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ if (LocalSlotNum < (*itr)->GetNumSlots())
+ {
+ a_LocalSlotNum = LocalSlotNum;
+ return *itr;
+ }
+ LocalSlotNum -= (*itr)->GetNumSlots();
+ } // for itr - m_SlotAreas[]
+
+ // We shouldn't be here - the check at the beginnning should prevent this. Log and assert
+ LOGWARNING("%s: GetNumSlots() is out of sync: %d; LocalSlotNum = %d", __FUNCTION__, GetNumSlots(), LocalSlotNum);
+ ASSERT(!"Invalid GetNumSlots");
+ return NULL;
+}
+
+
+
+
+
+void cWindow::OnPaintBegin(cPlayer & a_Player)
+{
+ // Prepares the internal structures for inventory painting from the specified player
+ a_Player.ClearInventoryPaintSlots();
+}
+
+
+
+
+
+void cWindow::OnPaintProgress(cPlayer & a_Player, int a_SlotNum)
+{
+ // Add the slot to the internal structures for inventory painting by the specified player
+ a_Player.AddInventoryPaintSlot(a_SlotNum);
+}
+
+
+
+
+
+void cWindow::OnLeftPaintEnd(cPlayer & a_Player)
+{
+ // Process the entire action stored in the internal structures for inventory painting
+ // distribute as many items as possible
+
+ const cSlotNums & SlotNums = a_Player.GetInventoryPaintSlots();
+ cItem ToDistribute(a_Player.GetDraggingItem());
+ int ToEachSlot = (int)ToDistribute.m_ItemCount / SlotNums.size();
+
+ int NumDistributed = DistributeItemToSlots(a_Player, ToDistribute, ToEachSlot, SlotNums);
+
+ // Remove the items distributed from the dragging item:
+ a_Player.GetDraggingItem().m_ItemCount -= NumDistributed;
+ if (a_Player.GetDraggingItem().m_ItemCount == 0)
+ {
+ a_Player.GetDraggingItem().Empty();
+ }
+
+ SendWholeWindow(*a_Player.GetClientHandle());
+}
+
+
+
+
+
+void cWindow::OnRightPaintEnd(cPlayer & a_Player)
+{
+ // Process the entire action stored in the internal structures for inventory painting
+ // distribute one item into each slot
+
+ const cSlotNums & SlotNums = a_Player.GetInventoryPaintSlots();
+ cItem ToDistribute(a_Player.GetDraggingItem());
+
+ int NumDistributed = DistributeItemToSlots(a_Player, ToDistribute, 1, SlotNums);
+
+ // Remove the items distributed from the dragging item:
+ a_Player.GetDraggingItem().m_ItemCount -= NumDistributed;
+ if (a_Player.GetDraggingItem().m_ItemCount == 0)
+ {
+ a_Player.GetDraggingItem().Empty();
+ }
+
+ SendWholeWindow(*a_Player.GetClientHandle());
+}
+
+
+
+
+
+int cWindow::DistributeItemToSlots(cPlayer & a_Player, const cItem & a_Item, int a_NumToEachSlot, const cSlotNums & a_SlotNums)
+{
+ if ((size_t)(a_Item.m_ItemCount) < a_SlotNums.size())
+ {
+ LOGWARNING("%s: Distributing less items (%d) than slots (%u)", __FUNCTION__, (int)a_Item.m_ItemCount, a_SlotNums.size());
+ // This doesn't seem to happen with the 1.5.1 client, so we don't worry about it for now
+ return 0;
+ }
+
+ // Distribute to individual slots, keep track of how many items were actually distributed (full stacks etc.)
+ int NumDistributed = 0;
+ for (cSlotNums::const_iterator itr = a_SlotNums.begin(), end = a_SlotNums.end(); itr != end; ++itr)
+ {
+ int LocalSlotNum = 0;
+ cSlotArea * Area = GetSlotArea(*itr, LocalSlotNum);
+ if (Area == NULL)
+ {
+ LOGWARNING("%s: Bad SlotArea for slot %d", __FUNCTION__, *itr);
+ continue;
+ }
+
+ // Modify the item at the slot
+ cItem AtSlot(*Area->GetSlot(LocalSlotNum, a_Player));
+ int MaxStack = AtSlot.GetMaxStackSize();
+ if (AtSlot.IsEmpty())
+ {
+ // Empty, just move all of it there:
+ cItem ToStore(a_Item);
+ ToStore.m_ItemCount = std::min(a_NumToEachSlot, (int)MaxStack);
+ Area->SetSlot(LocalSlotNum, a_Player, ToStore);
+ NumDistributed += ToStore.m_ItemCount;
+ }
+ else if (AtSlot.IsStackableWith(a_Item))
+ {
+ // Occupied, add and cap at MaxStack:
+ int CanStore = std::min(a_NumToEachSlot, (int)MaxStack - AtSlot.m_ItemCount);
+ AtSlot.m_ItemCount += CanStore;
+ Area->SetSlot(LocalSlotNum, a_Player, AtSlot);
+ NumDistributed += CanStore;
+ }
+ } // for itr - SlotNums[]
+ return NumDistributed;
+}
+
+
+
+
+
+void cWindow::BroadcastSlot(cSlotArea * a_Area, int a_LocalSlotNum)
+{
+ // Translate local slot num into global slot num:
+ int SlotNum = 0;
+ bool HasFound = false;
+ for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ if (a_Area == *itr)
+ {
+ SlotNum += a_LocalSlotNum;
+ HasFound = true;
+ break;
+ }
+ SlotNum += (*itr)->GetNumSlots();
+ } // for itr - m_SlotAreas[]
+ if (!HasFound)
+ {
+ LOGWARNING("%s: Invalid slot area parameter", __FUNCTION__);
+ ASSERT(!"Invalid slot area");
+ return;
+ }
+
+ // Broadcast the update packet:
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
+ {
+ (*itr)->GetClientHandle()->SendInventorySlot(m_WindowID, SlotNum, *a_Area->GetSlot(a_LocalSlotNum, **itr));
+ } // for itr - m_OpenedBy[]
+}
+
+
+
+
+
+void cWindow::SendWholeWindow(cClientHandle & a_Client)
+{
+ a_Client.SendWholeInventory(*this);
+}
+
+
+
+
+
+void cWindow::BroadcastWholeWindow(void)
+{
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
+ {
+ SendWholeWindow(*(*itr)->GetClientHandle());
+ } // for itr - m_OpenedBy[]
+}
+
+
+
+
+
+void cWindow::BroadcastProgress(int a_Progressbar, int a_Value)
+{
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
+ {
+ (*itr)->GetClientHandle()->SendWindowProperty(*this, a_Progressbar, a_Value);
+ } // for itr - m_OpenedBy[]
+}
+
+
+
+
+
+void cWindow::SetProperty(int a_Property, int a_Value)
+{
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr)
+ {
+ (*itr)->GetClientHandle()->SendWindowProperty(*this, a_Property, a_Value);
+ } // for itr - m_OpenedBy[]
+}
+
+
+
+
+
+void cWindow::SetProperty(int a_Property, int a_Value, cPlayer & a_Player)
+{
+ a_Player.GetClientHandle()->SendWindowProperty(*this, a_Property, a_Value);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cInventoryWindow:
+
+cInventoryWindow::cInventoryWindow(cPlayer & a_Player) :
+ cWindow(wtInventory, "Inventory"),
+ m_Player(a_Player)
+{
+ m_SlotAreas.push_back(new cSlotAreaCrafting(2, *this)); // The creative inventory doesn't display it, but it's still counted into slot numbers
+ m_SlotAreas.push_back(new cSlotAreaArmor(*this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCraftingWindow:
+
+cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
+ cWindow(wtWorkbench, "Crafting Table")
+{
+ m_SlotAreas.push_back(new cSlotAreaCrafting(3, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cChestWindow:
+
+cChestWindow::cChestWindow(cChestEntity * a_Chest) :
+ cWindow(wtChest, "Chest"),
+ m_World(a_Chest->GetWorld()),
+ m_BlockX(a_Chest->GetPosX()),
+ m_BlockY(a_Chest->GetPosY()),
+ m_BlockZ(a_Chest->GetPosZ())
+{
+ m_SlotAreas.push_back(new cSlotAreaChest(a_Chest, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+
+ // Play the opening sound:
+ m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1);
+
+ // Send out the chest-open packet:
+ m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST);
+}
+
+
+
+
+
+cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest) :
+ cWindow(wtChest, "Double Chest"),
+ m_World(a_PrimaryChest->GetWorld()),
+ m_BlockX(a_PrimaryChest->GetPosX()),
+ m_BlockY(a_PrimaryChest->GetPosY()),
+ m_BlockZ(a_PrimaryChest->GetPosZ())
+{
+ m_SlotAreas.push_back(new cSlotAreaDoubleChest(a_PrimaryChest, a_SecondaryChest, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+
+ m_ShouldDistributeToHotbarFirst = false;
+
+ // Play the opening sound:
+ m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1);
+
+ // Send out the chest-open packet:
+ m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST);
+}
+
+
+
+
+
+cChestWindow::~cChestWindow()
+{
+ // Send out the chest-close packet:
+ m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_CHEST);
+
+ m_World->BroadcastSoundEffect("random.chestclosed", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cDropSpenserWindow:
+
+cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) :
+ cWindow(wtDropSpenser, "Dropspenser")
+{
+ m_ShouldDistributeToHotbarFirst = false;
+ m_SlotAreas.push_back(new cSlotAreaItemGrid(a_DropSpenser->GetContents(), *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHopperWindow:
+
+cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper) :
+ super(wtHopper, "Hopper")
+{
+ m_ShouldDistributeToHotbarFirst = false;
+ m_SlotAreas.push_back(new cSlotAreaItemGrid(a_Hopper->GetContents(), *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFurnaceWindow:
+
+cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) :
+ cWindow(wtFurnace, "Furnace")
+{
+ m_ShouldDistributeToHotbarFirst = false;
+ m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
diff --git a/src/UI/Window.h b/src/UI/Window.h
new file mode 100644
index 000000000..c44b900d7
--- /dev/null
+++ b/src/UI/Window.h
@@ -0,0 +1,300 @@
+
+// Window.h
+
+// Interfaces to the cWindow class representing a UI window for a specific block
+
+
+
+
+
+#pragma once
+
+#include "../ItemGrid.h"
+
+
+
+
+
+class cPlayer;
+class cWindowOwner;
+class cClientHandle;
+class cChestEntity;
+class cDropSpenserEntity;
+class cFurnaceEntity;
+class cHopperEntity;
+class cSlotArea;
+class cWorld;
+
+typedef std::list<cPlayer *> cPlayerList;
+typedef std::vector<cSlotArea *> cSlotAreas;
+
+
+
+
+
+// tolua_begin
+
+/**
+Represents a UI window.
+
+Each window has a list of players that are currently using it
+When there's no player using a window, it is destroyed.
+A window consists of several areas of slots with similar functionality - for example the crafting grid area, or
+the inventory area. Each area knows what its slots are (GetSlot() function) and can handle mouse clicks.
+The window acts only as a top-level container for those areas, redirecting the click events to the correct areas.
+Inventory painting, introduced in 1.5, is handled by the window, too
+*/
+class cWindow
+{
+public:
+ enum WindowType
+ {
+ wtInventory = -1, // This value is never actually sent to a client
+ wtChest = 0,
+ wtWorkbench = 1,
+ wtFurnace = 2,
+ wtDropSpenser = 3, // Dropper or Dispenser
+ wtEnchantment = 4,
+ wtBrewery = 5,
+ wtNPCTrade = 6,
+ wtBeacon = 7,
+ wtAnvil = 8,
+ wtHopper = 9,
+ // Unknown: 10
+ wtAnimalChest = 11,
+ };
+
+ // tolua_end
+
+ static const int c_NumInventorySlots = 36;
+
+ cWindow(WindowType a_WindowType, const AString & a_WindowTitle);
+ virtual ~cWindow();
+
+ char GetWindowID(void) const { return m_WindowID; } // tolua_export
+ int GetWindowType(void) const { return m_WindowType; } // tolua_export
+
+ cWindowOwner * GetOwner(void) { return m_Owner; }
+ void SetOwner( cWindowOwner * a_Owner ) { m_Owner = a_Owner; }
+
+ /// Returns the total number of slots
+ int GetNumSlots(void) const;
+
+ /// Returns the number of slots, excluding the player's inventory (used for network protocols)
+ int GetNumNonInventorySlots(void) const { return GetNumSlots() - c_NumInventorySlots; }
+
+ // tolua_begin
+
+ /// Returns the item at the specified slot for the specified player. Returns NULL if invalid SlotNum requested
+ const cItem * GetSlot(cPlayer & a_Player, int a_SlotNum) const;
+
+ /// Sets the item to the specified slot for the specified player
+ void SetSlot(cPlayer & a_Player, int a_SlotNum, const cItem & a_Item);
+
+ /// Returns true if the specified slot is in the Player Main Inventory slotarea
+ bool IsSlotInPlayerMainInventory(int a_SlotNum) const;
+
+ /// Returns true if the specified slot is in the Player Hotbar slotarea
+ bool IsSlotInPlayerHotbar(int a_SlotNum) const;
+
+ /// Returns true if the specified slot is in the Player Main Inventory or Hotbar slotareas. Note that returns false for Armor.
+ bool IsSlotInPlayerInventory(int a_SlotNum) const;
+
+ // tolua_end
+
+ /// Fills a_Slots with the slots read from m_SlotAreas[], for the specified player
+ void GetSlots(cPlayer & a_Player, cItems & a_Slots) const;
+
+ /// Handles a click event from a player
+ void Clicked(
+ cPlayer & a_Player, int a_WindowID,
+ short a_SlotNum, eClickAction a_ClickAction,
+ const cItem & a_ClickedItem
+ );
+
+ void OpenedByPlayer(cPlayer & a_Player);
+
+ /// Called when a player closes this window; notifies all slot areas. Returns true if close accepted
+ virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse);
+
+ /// Sends the specified slot's contents to all clients of this window; the slot is specified as local in an area
+ void BroadcastSlot(cSlotArea * a_Area, int a_LocalSlotNum);
+
+ /// Sends the contents of the whole window to the specified client
+ void SendWholeWindow(cClientHandle & a_Client);
+
+ /// Sends the contents of the whole window to all clients of this window.
+ void BroadcastWholeWindow(void);
+
+ /// Sends the progressbar to all clients of this window (same as SetProperty)
+ void BroadcastProgress(int a_Progressbar, int a_Value);
+
+ // tolua_begin
+
+ const AString & GetWindowTitle() const { return m_WindowTitle; }
+ void SetWindowTitle(const AString & a_WindowTitle ) { m_WindowTitle = a_WindowTitle; }
+
+ /// Sends the UpdateWindowProperty (0x69) packet to all clients of the window
+ void SetProperty(int a_Property, int a_Value);
+
+ /// Sends the UpdateWindowPropert(0x69) packet to the specified player
+ void SetProperty(int a_Property, int a_Value, cPlayer & a_Player);
+
+ // tolua_end
+
+ void OwnerDestroyed(void);
+
+ /// Calls the callback safely for each player that has this window open; returns true if all players have been enumerated
+ bool ForEachPlayer(cItemCallback<cPlayer> & a_Callback);
+
+ /// Calls the callback safely for each client that has this window open; returns true if all clients have been enumerated
+ bool ForEachClient(cItemCallback<cClientHandle> & a_Callback);
+
+ /** Called on shift-clicking to distribute the stack into other areas; Modifies a_ItemStack as it is distributed!
+ if a_ShouldApply is true, the changes are written into the slots;
+ if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes)
+ */
+ void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply);
+
+ /// Called on DblClicking to collect all stackable items from all areas into hand, starting with the specified area.
+ /// The items are accumulated in a_Dragging and removed from the SlotAreas immediately.
+ /// If a_CollectFullStacks is false, slots with full stacks in the area are skipped while collecting.
+ /// Returns true if full stack has been collected, false if there's space remaining to fill.
+ bool CollectItemsToHand(cItem & a_Dragging, cSlotArea & a_Area, cPlayer & a_Player, bool a_CollectFullStacks);
+
+ /// Used by cSlotAreas to send individual slots to clients, a_RelativeSlotNum is the slot number relative to a_SlotArea
+ void SendSlot(cPlayer & a_Player, cSlotArea * a_SlotArea, int a_RelativeSlotNum);
+
+protected:
+ cSlotAreas m_SlotAreas;
+
+ char m_WindowID;
+ int m_WindowType;
+ AString m_WindowTitle;
+
+ cCriticalSection m_CS;
+ cPlayerList m_OpenedBy;
+
+ bool m_IsDestroyed;
+ bool m_ShouldDistributeToHotbarFirst; ///< If set (default), shift+click tries to distribute to hotbar first, then other areas. False for doublechests
+
+ cWindowOwner * m_Owner;
+
+ static char m_WindowIDCounter;
+
+ /// Sets the internal flag as "destroyed"; notifies the owner that the window is destroying
+ virtual void Destroy(void);
+
+ /** Returns the correct slot area for the specified window-global SlotNum
+ Also returns the area-local SlotNum corresponding to the GlobalSlotNum
+ If the global SlotNum is out of range, returns NULL
+ */
+ cSlotArea * GetSlotArea(int a_GlobalSlotNum, int & a_LocalSlotNum);
+
+ /** Returns the correct slot area for the specified window-global SlotNum
+ Also returns the area-local SlotNum corresponding to the GlobalSlotNum
+ If the global SlotNum is out of range, returns NULL.
+ Const version.
+ */
+ const cSlotArea * GetSlotArea(int a_GlobalSlotNum, int & a_LocalSlotNum) const;
+
+ /// Prepares the internal structures for inventory painting from the specified player
+ void OnPaintBegin(cPlayer & a_Player);
+
+ /// Adds the slot to the internal structures for inventory painting by the specified player
+ void OnPaintProgress(cPlayer & a_Player, int a_SlotNum);
+
+ /// Processes the entire action stored in the internal structures for inventory painting; distributes as many items as possible
+ void OnLeftPaintEnd(cPlayer & a_Player);
+
+ /// Processes the entire action stored in the internal structures for inventory painting; distributes one item into each slot
+ void OnRightPaintEnd(cPlayer & a_Player);
+
+ /// Distributes a_NumToEachSlot items into the slots specified in a_SlotNums; returns the total number of items distributed
+ int DistributeItemToSlots(cPlayer & a_Player, const cItem & a_Item, int a_NumToEachSlot, const cSlotNums & a_SlotNums);
+} ; // tolua_export
+
+
+
+
+
+class cCraftingWindow :
+ public cWindow
+{
+ typedef cWindow super;
+public:
+ cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ);
+} ;
+
+
+
+
+
+class cFurnaceWindow :
+ public cWindow
+{
+ typedef cWindow super;
+public:
+ cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace);
+} ;
+
+
+
+
+
+class cDropSpenserWindow :
+ public cWindow
+{
+ typedef cWindow super;
+public:
+ cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_Dispenser);
+} ;
+
+
+
+
+
+class cHopperWindow :
+ public cWindow
+{
+ typedef cWindow super;
+public:
+ cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper);
+} ;
+
+
+
+
+
+class cChestWindow :
+ public cWindow
+{
+public:
+ cChestWindow(cChestEntity * a_Chest);
+ cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest);
+ ~cChestWindow();
+
+protected:
+ cWorld * m_World;
+ int m_BlockX, m_BlockY, m_BlockZ; // Position of the chest, for the window-close packet
+} ;
+
+
+
+
+
+class cInventoryWindow :
+ public cWindow
+{
+public:
+ cInventoryWindow(cPlayer & a_Player);
+
+protected:
+ cPlayer & m_Player;
+
+} ;
+
+
+
+
+
diff --git a/src/UI/WindowOwner.h b/src/UI/WindowOwner.h
new file mode 100644
index 000000000..d41abf66d
--- /dev/null
+++ b/src/UI/WindowOwner.h
@@ -0,0 +1,125 @@
+
+#pragma once
+
+#include "../BlockEntities/BlockEntity.h"
+#include "../Entities/Entity.h"
+#include "Window.h"
+
+/*
+Being a descendant of cWindowOwner means that the class can own one window. That window can be
+queried, opened by other players, closed by players and finally destroyed.
+Also, a cWindowOwner can be queried for the block coords where the window is displayed. That will be used
+for entities / players in motion to close their windows when they get too far away from the window "source".
+*/
+
+
+
+
+
+// class cWindow;
+
+
+
+
+
+/**
+Base class for the window owning
+*/
+class cWindowOwner
+{
+public:
+ cWindowOwner() :
+ m_Window(NULL)
+ {
+ }
+
+ void CloseWindow(void)
+ {
+ m_Window = NULL;
+ }
+
+ void OpenWindow(cWindow * a_Window)
+ {
+ m_Window = a_Window;
+ m_Window->SetOwner(this);
+ }
+
+ cWindow * GetWindow(void) const
+ {
+ return m_Window;
+ }
+
+ /// Returns the block position at which the element owning the window is
+ virtual void GetBlockPos(int & a_BlockX, int & a_BlockY, int & a_BlockZ) = 0;
+
+private:
+ cWindow * m_Window;
+} ;
+
+
+
+
+
+/**
+Window owner that is associated with a block entity (chest, furnace, ...)
+*/
+class cBlockEntityWindowOwner :
+ public cWindowOwner
+{
+public:
+ cBlockEntityWindowOwner(void) :
+ m_BlockEntity(NULL)
+ {
+ }
+
+ void SetBlockEntity(cBlockEntity * a_BlockEntity)
+ {
+ m_BlockEntity = a_BlockEntity;
+ }
+
+ virtual void GetBlockPos(int & a_BlockX, int & a_BlockY, int & a_BlockZ) override
+ {
+ a_BlockX = m_BlockEntity->GetPosX();
+ a_BlockY = m_BlockEntity->GetPosY();
+ a_BlockZ = m_BlockEntity->GetPosZ();
+ }
+
+private:
+ cBlockEntity * m_BlockEntity;
+} ;
+
+
+
+
+
+/**
+Window owner that is associated with an entity (chest minecart)
+*/
+class cEntityWindowOwner :
+ public cWindowOwner
+{
+public:
+ cEntityWindowOwner(void) :
+ m_Entity(NULL)
+ {
+ }
+
+ void SetEntity(cEntity * a_Entity)
+ {
+ m_Entity = a_Entity;
+ }
+
+ virtual void GetBlockPos(int & a_BlockX, int & a_BlockY, int & a_BlockZ) override
+ {
+ a_BlockX = (int)floor(m_Entity->GetPosX() + 0.5);
+ a_BlockY = (int)floor(m_Entity->GetPosY() + 0.5);
+ a_BlockZ = (int)floor(m_Entity->GetPosZ() + 0.5);
+ }
+
+private:
+ cEntity * m_Entity;
+} ;
+
+
+
+
diff --git a/src/Vector3d.cpp b/src/Vector3d.cpp
new file mode 100644
index 000000000..96ebebab5
--- /dev/null
+++ b/src/Vector3d.cpp
@@ -0,0 +1,77 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Vector3d.h"
+#include "Vector3f.h"
+
+
+
+
+
+const double Vector3d::EPS = 0.000001; ///< The max difference between two coords for which the coords are assumed equal
+const double Vector3d::NO_INTERSECTION = 1e70; ///< Return value of LineCoeffToPlane() if the line is parallel to the plane
+
+
+
+
+
+Vector3d::Vector3d(const Vector3f & v) :
+ x(v.x),
+ y(v.y),
+ z(v.z)
+{
+}
+
+
+
+
+
+Vector3d::Vector3d(const Vector3f * v) :
+ x(v->x),
+ y(v->y),
+ z(v->z)
+{
+}
+
+
+
+
+
+double Vector3d::LineCoeffToXYPlane(const Vector3d & a_OtherEnd, double a_Z) const
+{
+ if (abs(z - a_OtherEnd.z) < EPS)
+ {
+ return NO_INTERSECTION;
+ }
+ return (a_Z - z) / (a_OtherEnd.z - z);
+}
+
+
+
+
+
+double Vector3d::LineCoeffToXZPlane(const Vector3d & a_OtherEnd, double a_Y) const
+{
+ if (abs(y - a_OtherEnd.y) < EPS)
+ {
+ return NO_INTERSECTION;
+ }
+ return (a_Y - y) / (a_OtherEnd.y - y);
+}
+
+
+
+
+
+double Vector3d::LineCoeffToYZPlane(const Vector3d & a_OtherEnd, double a_X) const
+{
+ if (abs(x - a_OtherEnd.x) < EPS)
+ {
+ return NO_INTERSECTION;
+ }
+ return (a_X - x) / (a_OtherEnd.x - x);
+}
+
+
+
+
diff --git a/src/Vector3d.h b/src/Vector3d.h
new file mode 100644
index 000000000..a06a17c09
--- /dev/null
+++ b/src/Vector3d.h
@@ -0,0 +1,81 @@
+#pragma once
+
+#include <math.h>
+
+class Vector3f;
+
+
+
+// tolua_begin
+
+class Vector3d
+{
+public:
+ // convert from float
+ Vector3d(const Vector3f & v);
+ Vector3d(const Vector3f * v);
+
+ Vector3d() : x(0), y(0), z(0) {}
+ Vector3d(double a_x, double a_y, double a_z) : x(a_x), y(a_y), z(a_z) {}
+
+ inline void Set(double a_x, double a_y, double a_z) { x = a_x, y = a_y, z = a_z; }
+ inline void Normalize() { double l = 1.0f / Length(); x *= l; y *= l; z *= l; }
+ inline Vector3d NormalizeCopy() { double l = 1.0f / Length(); return Vector3d( x * l, y * l, z * l ); }
+ inline void NormalizeCopy(Vector3d & a_V) { double l = 1.0f / Length(); a_V.Set(x*l, y*l, z*l ); }
+ inline double Length() const { return (double)sqrt( x * x + y * y + z * z ); }
+ inline double SqrLength() const { return x * x + y * y + z * z; }
+ inline double Dot( const Vector3d & a_V ) const { return x * a_V.x + y * a_V.y + z * a_V.z; }
+ inline Vector3d Cross( const Vector3d & v ) const { return Vector3d( y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x ); }
+
+ /** Returns the coefficient for the (a_OtherEnd - this) line to reach the specified Z coord
+ The result satisfies the following equation:
+ (*this + Result * (a_OtherEnd - *this)).z = a_Z
+ If the line is too close to being parallel, this function returns NO_INTERSECTION
+ */
+ double LineCoeffToXYPlane(const Vector3d & a_OtherEnd, double a_Z) const;
+
+ /** Returns the coefficient for the (a_OtherEnd - this) line to reach the specified Y coord
+ The result satisfies the following equation:
+ (*this + Result * (a_OtherEnd - *this)).y = a_Y
+ If the line is too close to being parallel, this function returns NO_INTERSECTION
+ */
+ double LineCoeffToXZPlane(const Vector3d & a_OtherEnd, double a_Y) const;
+
+ /** Returns the coefficient for the (a_OtherEnd - this) line to reach the specified X coord
+ The result satisfies the following equation:
+ (*this + Result * (a_OtherEnd - *this)).x = a_X
+ If the line is too close to being parallel, this function returns NO_INTERSECTION
+ */
+ double LineCoeffToYZPlane(const Vector3d & a_OtherEnd, double a_X) const;
+
+ inline bool Equals(const Vector3d & v) const { return ((x == v.x) && (y == v.y) && (z == v.z)); }
+
+ // tolua_end
+
+ void operator += ( const Vector3d& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; }
+ void operator += ( Vector3d* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; }
+ void operator -= ( const Vector3d& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; }
+ void operator -= ( Vector3d* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; }
+ void operator *= ( double a_f ) { x *= a_f; y *= a_f; z *= a_f; }
+
+ // tolua_begin
+
+ Vector3d operator + (const Vector3d & v2) const { return Vector3d(x + v2.x, y + v2.y, z + v2.z ); }
+ Vector3d operator + (const Vector3d * v2) const { return Vector3d(x + v2->x, y + v2->y, z + v2->z ); }
+ Vector3d operator - (const Vector3d & v2) const { return Vector3d(x - v2.x, y - v2.y, z - v2.z ); }
+ Vector3d operator - (const Vector3d * v2) const { return Vector3d(x - v2->x, y - v2->y, z - v2->z ); }
+ Vector3d operator * (const double f) const { return Vector3d(x * f, y * f, z * f ); }
+ Vector3d operator * (const Vector3d & v2) const { return Vector3d(x * v2.x, y * v2.y, z * v2.z ); }
+ Vector3d operator / (const double f) const { return Vector3d(x / f, y / f, z / f ); }
+
+ double x, y, z;
+
+ static const double EPS; ///< The max difference between two coords for which the coords are assumed equal
+ static const double NO_INTERSECTION; ///< Return value of LineCoeffToPlane() if the line is parallel to the plane
+} ;
+
+// tolua_end
+
+
+
+
diff --git a/src/Vector3f.cpp b/src/Vector3f.cpp
new file mode 100644
index 000000000..59d71d371
--- /dev/null
+++ b/src/Vector3f.cpp
@@ -0,0 +1,34 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Vector3f.h"
+#include "Vector3d.h"
+#include "Vector3i.h"
+
+Vector3f::Vector3f( const Vector3d & v )
+ : x( (float)v.x )
+ , y( (float)v.y )
+ , z( (float)v.z )
+{
+}
+
+Vector3f::Vector3f( const Vector3d * v )
+ : x( (float)v->x )
+ , y( (float)v->y )
+ , z( (float)v->z )
+{
+}
+
+Vector3f::Vector3f( const Vector3i & v )
+ : x( (float)v.x )
+ , y( (float)v.y )
+ , z( (float)v.z )
+{
+}
+
+Vector3f::Vector3f( const Vector3i * v )
+ : x( (float)v->x )
+ , y( (float)v->y )
+ , z( (float)v->z )
+{
+} \ No newline at end of file
diff --git a/src/Vector3f.h b/src/Vector3f.h
new file mode 100644
index 000000000..adb154ad7
--- /dev/null
+++ b/src/Vector3f.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <math.h>
+
+class Vector3i;
+class Vector3d;
+class Vector3f // tolua_export
+{ // tolua_export
+public: // tolua_export
+ Vector3f( const Vector3d & v ); // tolua_export
+ Vector3f( const Vector3d * v ); // tolua_export
+ Vector3f( const Vector3i & v ); // tolua_export
+ Vector3f( const Vector3i * v ); // tolua_export
+
+
+ Vector3f() : x(0), y(0), z(0) {} // tolua_export
+ Vector3f(float a_x, float a_y, float a_z) : x(a_x), y(a_y), z(a_z) {} // tolua_export
+
+ inline void Set(float a_x, float a_y, float a_z) { x = a_x, y = a_y, z = a_z; } // tolua_export
+ inline void Normalize() { float l = 1.0f / Length(); x *= l; y *= l; z *= l; } // tolua_export
+ inline Vector3f NormalizeCopy() const { float l = 1.0f / Length(); return Vector3f( x * l, y * l, z * l ); }// tolua_export
+ inline void NormalizeCopy(Vector3f & a_V) const { float l = 1.0f / Length(); a_V.Set(x*l, y*l, z*l ); } // tolua_export
+ inline float Length() const { return (float)sqrtf( x * x + y * y + z * z ); } // tolua_export
+ inline float SqrLength() const { return x * x + y * y + z * z; } // tolua_export
+ inline float Dot( const Vector3f & a_V ) const { return x * a_V.x + y * a_V.y + z * a_V.z; } // tolua_export
+ inline Vector3f Cross( const Vector3f & v ) const { return Vector3f( y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x ); } // tolua_export
+
+ inline bool Equals( const Vector3f & v ) const { return (x == v.x && y == v.y && z == v.z ); } // tolua_export
+
+ void operator += ( const Vector3f& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; }
+ void operator += ( Vector3f* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; }
+ void operator -= ( const Vector3f& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; }
+ void operator -= ( Vector3f* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; }
+ void operator *= ( float a_f ) { x *= a_f; y *= a_f; z *= a_f; }
+ void operator *= ( Vector3f* a_V ) { x *= a_V->x; y *= a_V->y; z *= a_V->z; }
+ void operator *= ( const Vector3f& a_V ) { x *= a_V.x; y *= a_V.y; z *= a_V.z; }
+
+ Vector3f operator + ( const Vector3f& v2 ) const { return Vector3f( x + v2.x, y + v2.y, z + v2.z ); } // tolua_export
+ Vector3f operator + ( const Vector3f* v2 ) const { return Vector3f( x + v2->x, y + v2->y, z + v2->z ); } // tolua_export
+ Vector3f operator - ( const Vector3f& v2 ) const { return Vector3f( x - v2.x, y - v2.y, z - v2.z ); } // tolua_export
+ Vector3f operator - ( const Vector3f* v2 ) const { return Vector3f( x - v2->x, y - v2->y, z - v2->z ); } // tolua_export
+ Vector3f operator * ( const float f ) const { return Vector3f( x * f, y * f, z * f ); } // tolua_export
+ Vector3f operator * ( const Vector3f& v2 ) const { return Vector3f( x * v2.x, y * v2.y, z * v2.z ); } // tolua_export
+
+ float x, y, z; // tolua_export
+
+};// tolua_export
diff --git a/src/Vector3i.cpp b/src/Vector3i.cpp
new file mode 100644
index 000000000..4ce1e2cf3
--- /dev/null
+++ b/src/Vector3i.cpp
@@ -0,0 +1,16 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Vector3i.h"
+#include "Vector3d.h"
+
+
+
+
+
+Vector3i::Vector3i( const Vector3d & v )
+ : x( (int)v.x )
+ , y( (int)v.y )
+ , z( (int)v.z )
+{
+} \ No newline at end of file
diff --git a/src/Vector3i.h b/src/Vector3i.h
new file mode 100644
index 000000000..7d726a7b3
--- /dev/null
+++ b/src/Vector3i.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <math.h>
+
+class Vector3d;
+class Vector3i // tolua_export
+{ // tolua_export
+public: // tolua_export
+ Vector3i( const Vector3d & v ); // tolua_export
+
+ Vector3i() : x(0), y(0), z(0) {} // tolua_export
+ Vector3i(int a_x, int a_y, int a_z) : x(a_x), y(a_y), z(a_z) {} // tolua_export
+
+ inline void Set(int a_x, int a_y, int a_z) { x = a_x, y = a_y, z = a_z; } // tolua_export
+ inline float Length() const { return sqrtf( (float)( x * x + y * y + z * z) ); } // tolua_export
+ inline int SqrLength() const { return x * x + y * y + z * z; } // tolua_export
+
+ inline bool Equals( const Vector3i & v ) const { return (x == v.x && y == v.y && z == v.z ); } // tolua_export
+ inline bool Equals( const Vector3i * v ) const { return (x == v->x && y == v->y && z == v->z ); } // tolua_export
+
+ void operator += ( const Vector3i& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; }
+ void operator += ( Vector3i* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; }
+ void operator -= ( const Vector3i& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; }
+ void operator -= ( Vector3i* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; }
+ void operator *= ( int a_f ) { x *= a_f; y *= a_f; z *= a_f; }
+
+ friend Vector3i operator + ( const Vector3i& v1, const Vector3i& v2 ) { return Vector3i( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z ); }
+ friend Vector3i operator + ( const Vector3i& v1, Vector3i* v2 ) { return Vector3i( v1.x + v2->x, v1.y + v2->y, v1.z + v2->z ); }
+ friend Vector3i operator - ( const Vector3i& v1, const Vector3i& v2 ) { return Vector3i( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z ); }
+ friend Vector3i operator - ( const Vector3i& v1, Vector3i* v2 ) { return Vector3i( v1.x - v2->x, v1.y - v2->y, v1.z - v2->z ); }
+ friend Vector3i operator - ( const Vector3i* v1, Vector3i& v2 ) { return Vector3i( v1->x - v2.x, v1->y - v2.y, v1->z - v2.z ); }
+ friend Vector3i operator * ( const Vector3i& v, const int f ) { return Vector3i( v.x * f, v.y * f, v.z * f ); }
+ friend Vector3i operator * ( const Vector3i& v1, const Vector3i& v2 ) { return Vector3i( v1.x * v2.x, v1.y * v2.y, v1.z * v2.z ); }
+ friend Vector3i operator * ( const int f, const Vector3i& v ) { return Vector3i( v.x * f, v.y * f, v.z * f ); }
+ friend bool operator < ( const Vector3i& v1, const Vector3i& v2 ) { return (v1.x<v2.x)||(v1.x==v2.x && v1.y<v2.y)||(v1.x==v2.x && v1.y == v2.y && v1.z<v2.z); }
+
+ int x, y, z; // tolua_export
+}; // tolua_export
+
+typedef std::list<Vector3i> cVector3iList;
+typedef std::vector<Vector3i> cVector3iArray;
+
+
+
+
diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp
new file mode 100644
index 000000000..ecc131d21
--- /dev/null
+++ b/src/WebAdmin.cpp
@@ -0,0 +1,527 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "WebAdmin.h"
+#include "WebPlugin.h"
+
+#include "PluginManager.h"
+#include "Plugin.h"
+
+#include "World.h"
+#include "Entities/Player.h"
+#include "Server.h"
+#include "Root.h"
+
+#include "HTTPServer/HTTPMessage.h"
+#include "HTTPServer/HTTPConnection.h"
+
+
+
+
+
+/// Helper class - appends all player names together in a HTML list
+class cPlayerAccum :
+ public cPlayerListCallback
+{
+ virtual bool Item(cPlayer * a_Player) override
+ {
+ m_Contents.append("<li>");
+ m_Contents.append(a_Player->GetName());
+ m_Contents.append("</li>");
+ return false;
+ }
+
+public:
+
+ AString m_Contents;
+} ;
+
+
+
+
+
+cWebAdmin::cWebAdmin(void) :
+ m_IsInitialized(false),
+ m_TemplateScript("<webadmin_template>")
+{
+}
+
+
+
+
+
+cWebAdmin::~cWebAdmin()
+{
+ if (m_IsInitialized)
+ {
+ LOGD("Stopping WebAdmin...");
+ }
+}
+
+
+
+
+
+void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin )
+{
+ m_Plugins.remove( a_Plugin );
+ m_Plugins.push_back( a_Plugin );
+}
+
+
+
+
+
+void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin )
+{
+ m_Plugins.remove( a_Plugin );
+}
+
+
+
+
+
+bool cWebAdmin::Init(void)
+{
+ if (!m_IniFile.ReadFile("webadmin.ini"))
+ {
+ LOGWARN("Regenerating webadmin.ini, all settings will be reset");
+ m_IniFile.AddHeaderComment(" This file controls the webadmin feature of MCServer");
+ m_IniFile.AddHeaderComment(" Username format: [User:*username*] | Password format: Password=*password*; for example:");
+ m_IniFile.AddHeaderComment(" [User:admin]");
+ m_IniFile.AddHeaderComment(" Password=admin");
+ }
+
+ if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true))
+ {
+ // WebAdmin is disabled, bail out faking a success
+ return true;
+ }
+
+ LOGD("Initialising WebAdmin...");
+
+ AString PortsIPv4 = m_IniFile.GetValueSet("WebAdmin", "Port", "8080");
+ AString PortsIPv6 = m_IniFile.GetValueSet("WebAdmin", "PortsIPv6", "");
+
+ if (!m_HTTPServer.Initialize(PortsIPv4, PortsIPv6))
+ {
+ return false;
+ }
+ m_IsInitialized = true;
+ m_IniFile.WriteFile("webadmin.ini");
+ return true;
+}
+
+
+
+
+
+bool cWebAdmin::Start(void)
+{
+ if (!m_IsInitialized)
+ {
+ // Not initialized
+ return false;
+ }
+
+ LOGD("Starting WebAdmin...");
+
+ // Initialize the WebAdmin template script and load the file
+ m_TemplateScript.Create();
+ if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua"))
+ {
+ LOGWARN("Could not load WebAdmin template \"%s\", using default template.", FILE_IO_PREFIX "webadmin/template.lua");
+ m_TemplateScript.Close();
+ }
+
+ return m_HTTPServer.Start(*this);
+}
+
+
+
+
+
+AString cWebAdmin::GetTemplate()
+{
+ AString retVal = "";
+
+ char SourceFile[] = "webadmin/template.html";
+
+ cFile f;
+ if (!f.Open(SourceFile, cFile::fmRead))
+ {
+ return "";
+ }
+
+ // copy the file into the buffer:
+ f.ReadRestOfFile(retVal);
+
+ return retVal;
+}
+
+
+
+
+
+void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ if (!a_Request.HasAuth())
+ {
+ a_Connection.SendNeedAuth("MCServer WebAdmin");
+ return;
+ }
+
+ // Check auth:
+ AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", "");
+ if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword))
+ {
+ a_Connection.SendNeedAuth("MCServer WebAdmin - bad username or password");
+ return;
+ }
+
+ // Check if the contents should be wrapped in the template:
+ AString URL = a_Request.GetBareURL();
+ ASSERT(URL.length() > 0);
+ bool ShouldWrapInTemplate = ((URL.length() > 1) && (URL[1] != '~'));
+
+ // Retrieve the request data:
+ cWebadminRequestData * Data = (cWebadminRequestData *)(a_Request.GetUserData());
+ if (Data == NULL)
+ {
+ a_Connection.SendStatusAndReason(500, "Bad UserData");
+ return;
+ }
+
+ // Wrap it all up for the Lua call:
+ AString Template;
+ HTTPTemplateRequest TemplateRequest;
+ TemplateRequest.Request.Username = a_Request.GetAuthUsername();
+ TemplateRequest.Request.Method = a_Request.GetMethod();
+ TemplateRequest.Request.Path = URL.substr(1);
+
+ if (Data->m_Form.Finish())
+ {
+ for (cHTTPFormParser::const_iterator itr = Data->m_Form.begin(), end = Data->m_Form.end(); itr != end; ++itr)
+ {
+ HTTPFormData HTTPfd;
+ HTTPfd.Value = itr->second;
+ HTTPfd.Type = "";
+ HTTPfd.Name = itr->first;
+ TemplateRequest.Request.FormData[itr->first] = HTTPfd;
+ TemplateRequest.Request.PostParams[itr->first] = itr->second;
+ } // for itr - Data->m_Form[]
+
+ // Parse the URL into individual params:
+ size_t idxQM = a_Request.GetURL().find('?');
+ if (idxQM != AString::npos)
+ {
+ cHTTPFormParser URLParams(cHTTPFormParser::fpkURL, a_Request.GetURL().c_str() + idxQM + 1, a_Request.GetURL().length() - idxQM - 1, *Data);
+ URLParams.Finish();
+ for (cHTTPFormParser::const_iterator itr = URLParams.begin(), end = URLParams.end(); itr != end; ++itr)
+ {
+ TemplateRequest.Request.Params[itr->first] = itr->second;
+ } // for itr - URLParams[]
+ }
+ }
+
+ // Try to get the template from the Lua template script
+ if (ShouldWrapInTemplate)
+ {
+ if (m_TemplateScript.Call("ShowPage", this, &TemplateRequest, cLuaState::Return, Template))
+ {
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send(Template.c_str(), Template.length());
+ return;
+ }
+ a_Connection.SendStatusAndReason(500, "m_TemplateScript failed");
+ return;
+ }
+
+ AString BaseURL = GetBaseURL(URL);
+ AString Menu;
+ Template = "{CONTENT}";
+ AString FoundPlugin;
+
+ for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
+ {
+ cWebPlugin * WebPlugin = *itr;
+ std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames();
+ for (std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names)
+ {
+ Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>";
+ }
+ }
+
+ sWebAdminPage Page = GetPage(TemplateRequest.Request);
+ AString Content = Page.Content;
+ FoundPlugin = Page.PluginName;
+ if (!Page.TabName.empty())
+ {
+ FoundPlugin += " - " + Page.TabName;
+ }
+
+ if (FoundPlugin.empty()) // Default page
+ {
+ Content = GetDefaultPage();
+ }
+
+ if (ShouldWrapInTemplate && (URL.size() > 1))
+ {
+ Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>";
+ }
+
+ int MemUsageKiB = cRoot::GetPhysicalRAMUsage();
+ if (MemUsageKiB > 0)
+ {
+ ReplaceString(Template, "{MEM}", Printf("%.02f", (double)MemUsageKiB / 1024));
+ ReplaceString(Template, "{MEMKIB}", Printf("%d", MemUsageKiB));
+ }
+ else
+ {
+ ReplaceString(Template, "{MEM}", "unknown");
+ ReplaceString(Template, "{MEMKIB}", "unknown");
+ }
+ ReplaceString(Template, "{USERNAME}", a_Request.GetAuthUsername());
+ ReplaceString(Template, "{MENU}", Menu);
+ ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin);
+ ReplaceString(Template, "{CONTENT}", Content);
+ ReplaceString(Template, "{TITLE}", "MCServer");
+
+ AString NumChunks;
+ Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount());
+ ReplaceString(Template, "{NUMCHUNKS}", NumChunks);
+
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send(Template.c_str(), Template.length());
+}
+
+
+
+
+
+void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ static const char LoginForm[] = \
+ "<h1>MCServer WebAdmin</h1>" \
+ "<center>" \
+ "<form method='get' action='webadmin/'>" \
+ "<input type='submit' value='Log in'>" \
+ "</form>" \
+ "</center>";
+ cHTTPResponse Resp;
+ Resp.SetContentType("text/html");
+ a_Connection.Send(Resp);
+ a_Connection.Send(LoginForm, sizeof(LoginForm) - 1);
+ a_Connection.FinishResponse();
+}
+
+
+
+
+
+sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request)
+{
+ sWebAdminPage Page;
+ AStringVector Split = StringSplit(a_Request.Path, "/");
+
+ // Find the plugin that corresponds to the requested path
+ AString FoundPlugin;
+ if (Split.size() > 1)
+ {
+ for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
+ {
+ if ((*itr)->GetWebTitle() == Split[1])
+ {
+ Page.Content = (*itr)->HandleWebRequest(&a_Request);
+ cWebPlugin * WebPlugin = *itr;
+ FoundPlugin = WebPlugin->GetWebTitle();
+ AString TabName = WebPlugin->GetTabNameForRequest(&a_Request).first;
+ Page.PluginName = FoundPlugin;
+ Page.TabName = TabName;
+ break;
+ }
+ }
+ }
+
+ // Return the page contents
+ return Page;
+}
+
+
+
+
+
+AString cWebAdmin::GetDefaultPage(void)
+{
+ AString Content;
+ Content += "<h4>Server Name:</h4>";
+ Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "</p>";
+
+ Content += "<h4>Plugins:</h4><ul>";
+ cPluginManager * PM = cPluginManager::Get();
+ const cPluginManager::PluginMap & List = PM->GetAllPlugins();
+ for (cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr)
+ {
+ if (itr->second == NULL)
+ {
+ continue;
+ }
+ AString VersionNum;
+ AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion());
+ }
+ Content += "</ul>";
+ Content += "<h4>Players:</h4><ul>";
+
+ cPlayerAccum PlayerAccum;
+ cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players
+ if( World != NULL )
+ {
+ World->ForEachPlayer(PlayerAccum);
+ Content.append(PlayerAccum.m_Contents);
+ }
+ Content += "</ul><br>";
+ return Content;
+}
+
+
+
+
+AString cWebAdmin::GetBaseURL( const AString& a_URL )
+{
+ return GetBaseURL(StringSplit(a_URL, "/"));
+}
+
+
+
+
+
+AString cWebAdmin::GetHTMLEscapedString(const AString & a_Input)
+{
+ AString dst;
+ dst.reserve(a_Input.length());
+
+ // Loop over input and substitute HTML characters for their alternatives:
+ size_t len = a_Input.length();
+ for (size_t i = 0; i < len; i++)
+ {
+ switch (a_Input[i])
+ {
+ case '&': dst.append("&amp;"); break;
+ case '\'': dst.append("&apos;"); break;
+ case '"': dst.append("&quot;"); break;
+ case '<': dst.append("&lt;"); break;
+ case '>': dst.append("&gt;"); break;
+ default:
+ {
+ dst.push_back(a_Input[i]);
+ break;
+ }
+ } // switch (a_Input[i])
+ } // for i - a_Input[]
+
+ return dst;
+}
+
+
+
+
+
+AString cWebAdmin::GetBaseURL(const AStringVector & a_URLSplit)
+{
+ AString BaseURL = "./";
+ if (a_URLSplit.size() > 1)
+ {
+ for (unsigned int i = 0; i < a_URLSplit.size(); i++)
+ {
+ BaseURL += "../";
+ }
+ BaseURL += "webadmin/";
+ }
+ return BaseURL;
+}
+
+
+
+
+
+void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ const AString & URL = a_Request.GetURL();
+ if (
+ (strncmp(URL.c_str(), "/webadmin", 9) == 0) ||
+ (strncmp(URL.c_str(), "/~webadmin", 10) == 0)
+ )
+ {
+ a_Request.SetUserData(new cWebadminRequestData(a_Request));
+ return;
+ }
+ if (URL == "/")
+ {
+ // The root needs no body handler and is fully handled in the OnRequestFinished() call
+ return;
+ }
+ // TODO: Handle other requests
+}
+
+
+
+
+
+void cWebAdmin::OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size)
+{
+ cRequestData * Data = (cRequestData *)(a_Request.GetUserData());
+ if (Data == NULL)
+ {
+ return;
+ }
+ Data->OnBody(a_Data, a_Size);
+}
+
+
+
+
+
+void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
+{
+ const AString & URL = a_Request.GetURL();
+ if (
+ (strncmp(URL.c_str(), "/webadmin", 9) == 0) ||
+ (strncmp(URL.c_str(), "/~webadmin", 10) == 0)
+ )
+ {
+ HandleWebadminRequest(a_Connection, a_Request);
+ }
+ else if (URL == "/")
+ {
+ // The root needs no body handler and is fully handled in the OnRequestFinished() call
+ HandleRootRequest(a_Connection, a_Request);
+ }
+ else
+ {
+ // TODO: Handle other requests
+ }
+
+ // Delete any request data assigned to the request:
+ cRequestData * Data = (cRequestData *)(a_Request.GetUserData());
+ delete Data;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWebAdmin::cWebadminRequestData
+
+void cWebAdmin::cWebadminRequestData::OnBody(const char * a_Data, int a_Size)
+{
+ m_Form.Parse(a_Data, a_Size);
+}
+
+
+
+
diff --git a/src/WebAdmin.h b/src/WebAdmin.h
new file mode 100644
index 000000000..a775acec2
--- /dev/null
+++ b/src/WebAdmin.h
@@ -0,0 +1,215 @@
+
+// WebAdmin.h
+
+// Declares the cWebAdmin class representing the admin interface over http protocol, and related services (API)
+
+#pragma once
+
+#include "OSSupport/Socket.h"
+#include "LuaState.h"
+#include "inifile/iniFile.h"
+#include "HTTPServer/HTTPServer.h"
+#include "HTTPServer/HTTPFormParser.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+
+
+
+
+// fwd:
+class cEvent;
+class cWebPlugin;
+
+
+
+
+
+// tolua_begin
+struct HTTPFormData
+{
+ std::string Name;
+ std::string Value;
+ std::string Type;
+} ;
+// tolua_end
+
+
+
+
+// tolua_begin
+struct HTTPRequest
+{
+ typedef std::map< std::string, std::string > StringStringMap;
+ typedef std::map< std::string, HTTPFormData > FormDataMap;
+
+ AString Method;
+ AString Path;
+ AString Username;
+ // tolua_end
+
+ /// Parameters given in the URL, after the questionmark
+ StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS <<
+
+ /// Parameters posted as a part of a form - either in the URL (GET method) or in the body (POST method)
+ StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS <<
+
+ /// Same as PostParams
+ FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS <<
+} ; // tolua_export
+
+
+
+
+
+// tolua_begin
+struct HTTPTemplateRequest
+{
+ HTTPRequest Request;
+} ;
+// tolua_end
+
+
+
+
+
+// tolua_begin
+struct sWebAdminPage
+{
+ AString Content;
+ AString PluginName;
+ AString TabName;
+};
+// tolua_end
+
+
+
+
+
+// tolua_begin
+class cWebAdmin :
+ public cHTTPServer::cCallbacks
+{
+public:
+ // tolua_end
+
+ typedef std::list< cWebPlugin* > PluginList;
+
+
+ cWebAdmin(void);
+ ~cWebAdmin();
+
+ /// Initializes the object. Returns true if successfully initialized and ready to start
+ bool Init(void);
+
+ /// Starts the HTTP server taking care of the admin. Returns true if successful
+ bool Start(void);
+
+ void AddPlugin( cWebPlugin* a_Plugin );
+ void RemovePlugin( cWebPlugin* a_Plugin );
+
+ // TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such
+ PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS <<
+
+ // tolua_begin
+
+ sWebAdminPage GetPage(const HTTPRequest & a_Request);
+
+ /// Returns the contents of the default page - the list of plugins and players
+ AString GetDefaultPage(void);
+
+ /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style)
+ AString GetBaseURL(const AString & a_URL);
+
+ /// Escapes text passed into it, so it can be embedded into html.
+ static AString GetHTMLEscapedString(const AString & a_Input);
+
+ // tolua_end
+
+ /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style)
+ AString GetBaseURL(const AStringVector& a_URLSplit);
+
+protected:
+ /// Common base class for request body data handlers
+ class cRequestData
+ {
+ public:
+ virtual ~cRequestData() {} // Force a virtual destructor in all descendants
+
+ /// Called when a new chunk of body data is received
+ virtual void OnBody(const char * a_Data, int a_Size) = 0;
+ } ;
+
+ /// The body handler for requests in the "/webadmin" and "/~webadmin" paths
+ class cWebadminRequestData :
+ public cRequestData,
+ public cHTTPFormParser::cCallbacks
+ {
+ public:
+ cHTTPFormParser m_Form;
+
+
+ cWebadminRequestData(cHTTPRequest & a_Request) :
+ m_Form(a_Request, *this)
+ {
+ }
+
+ // cRequestData overrides:
+ virtual void OnBody(const char * a_Data, int a_Size) override;
+
+ // cHTTPFormParser::cCallbacks overrides. Files are ignored:
+ virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override {}
+ virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override {}
+ virtual void OnFileEnd(cHTTPFormParser & a_Parser) override {}
+ } ;
+
+
+ /// Set to true if Init() succeeds and the webadmin isn't to be disabled
+ bool m_IsInitialized;
+
+ /// The webadmin.ini file, used for the settings and allowed logins
+ cIniFile m_IniFile;
+
+ PluginList m_Plugins;
+
+ /// The Lua template script to provide templates:
+ cLuaState m_TemplateScript;
+
+ /// The HTTP server which provides the underlying HTTP parsing, serialization and events
+ cHTTPServer m_HTTPServer;
+
+
+ AString GetTemplate(void);
+
+ /// Handles requests coming to the "/webadmin" or "/~webadmin" URLs
+ void HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
+
+ /// Handles requests for the root page
+ void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
+
+ // cHTTPServer::cCallbacks overrides:
+ virtual void OnRequestBegun (cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override;
+ virtual void OnRequestBody (cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override;
+ virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override;
+} ; // tolua_export
+
+
+
+
+
+// Revert MSVC warnings back to orignal state:
+#if defined(_MSC_VER)
+ #pragma warning(pop)
+#endif
+
+
+
+
diff --git a/src/WebPlugin.cpp b/src/WebPlugin.cpp
new file mode 100644
index 000000000..48ddb2076
--- /dev/null
+++ b/src/WebPlugin.cpp
@@ -0,0 +1,113 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "WebPlugin.h"
+#include "WebAdmin.h"
+#include "Server.h"
+#include "Root.h"
+
+
+
+
+
+cWebPlugin::cWebPlugin()
+{
+ cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin();
+ if (WebAdmin != NULL)
+ {
+ WebAdmin->AddPlugin(this);
+ }
+}
+
+
+
+
+
+cWebPlugin::~cWebPlugin()
+{
+ cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin();
+ if (WebAdmin != NULL)
+ {
+ WebAdmin->RemovePlugin(this);
+ }
+
+ for (TabList::iterator itr = m_Tabs.begin(); itr != m_Tabs.end(); ++itr)
+ {
+ delete *itr;
+ }
+ m_Tabs.clear();
+}
+
+
+
+
+
+std::list<std::pair<AString, AString> > cWebPlugin::GetTabNames(void)
+{
+ std::list< std::pair< AString, AString > > NameList;
+ for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr )
+ {
+ std::pair< AString, AString > StringPair;
+ StringPair.first = (*itr)->Title;
+ StringPair.second = (*itr)->SafeTitle;
+ NameList.push_back( StringPair );
+ }
+ return NameList;
+}
+
+
+
+
+
+std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest * a_Request)
+{
+ std::pair< AString, AString > Names;
+ AStringVector Split = StringSplit(a_Request->Path, "/");
+
+ if( Split.size() > 1 )
+ {
+ sWebPluginTab* Tab = 0;
+ if( Split.size() > 2 ) // If we got the tab name, show that page
+ {
+ for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr )
+ {
+ if( (*itr)->SafeTitle.compare( Split[2] ) == 0 ) // This is the one! Rawr
+ {
+ Tab = *itr;
+ break;
+ }
+ }
+ }
+ else // Otherwise show the first tab
+ {
+ if( GetTabs().size() > 0 )
+ Tab = *GetTabs().begin();
+ }
+
+ if( Tab )
+ {
+ Names.first = Tab->Title;
+ Names.second = Tab->SafeTitle;
+ }
+ }
+
+ return Names;
+}
+
+
+
+
+AString cWebPlugin::SafeString( const AString & a_String )
+{
+ AString RetVal;
+ for( unsigned int i = 0; i < a_String.size(); ++i )
+ {
+ char c = a_String[i];
+ if( c == ' ' )
+ {
+ c = '_';
+ }
+ RetVal.push_back( c );
+ }
+ return RetVal;
+} \ No newline at end of file
diff --git a/src/WebPlugin.h b/src/WebPlugin.h
new file mode 100644
index 000000000..22587b892
--- /dev/null
+++ b/src/WebPlugin.h
@@ -0,0 +1,48 @@
+
+#pragma once
+
+struct lua_State;
+struct HTTPRequest;
+
+
+
+
+
+// tolua_begin
+class cWebPlugin
+{
+public:
+ // tolua_end
+ cWebPlugin();
+ virtual ~cWebPlugin();
+
+ // tolua_begin
+ virtual const AString GetWebTitle(void) const = 0;
+
+ virtual AString HandleWebRequest(const HTTPRequest * a_Request ) = 0;
+
+ static AString SafeString( const AString & a_String );
+ // tolua_end
+
+ struct sWebPluginTab
+ {
+ std::string Title;
+ std::string SafeTitle;
+
+ int UserData;
+ };
+
+ typedef std::list< sWebPluginTab* > TabList;
+ TabList & GetTabs() { return m_Tabs; }
+
+ typedef std::list< std::pair<AString, AString> > TabNameList;
+ TabNameList GetTabNames(); // >> EXPORTED IN MANUALBINDINGS <<
+ std::pair< AString, AString > GetTabNameForRequest(const HTTPRequest* a_Request );
+
+private:
+ TabList m_Tabs;
+}; // tolua_export
+
+
+
+
diff --git a/src/World.cpp b/src/World.cpp
new file mode 100644
index 000000000..96b08a8ec
--- /dev/null
+++ b/src/World.cpp
@@ -0,0 +1,2733 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BlockID.h"
+#include "World.h"
+#include "ChunkDef.h"
+#include "ClientHandle.h"
+#include "Server.h"
+#include "Item.h"
+#include "Root.h"
+#include "inifile/iniFile.h"
+#include "ChunkMap.h"
+#include "OSSupport/Timer.h"
+
+// Entities (except mobs):
+#include "Entities/Pickup.h"
+#include "Entities/Player.h"
+#include "Entities/TNTEntity.h"
+
+// Simulators:
+#include "Simulator/SimulatorManager.h"
+#include "Simulator/FloodyFluidSimulator.h"
+#include "Simulator/FluidSimulator.h"
+#include "Simulator/FireSimulator.h"
+#include "Simulator/NoopFluidSimulator.h"
+#include "Simulator/SandSimulator.h"
+#include "Simulator/RedstoneSimulator.h"
+#include "Simulator/VaporizeFluidSimulator.h"
+
+// Mobs:
+#include "Mobs/IncludeAllMonsters.h"
+#include "MobCensus.h"
+#include "MobSpawner.h"
+
+#include "MersenneTwister.h"
+#include "Generating/Trees.h"
+#include "PluginManager.h"
+#include "Blocks/BlockHandler.h"
+#include "Vector3d.h"
+
+#include "Tracer.h"
+#include "tolua++.h"
+
+// DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
+#include "LineBlockTracer.h"
+
+#ifndef _WIN32
+ #include <stdlib.h>
+#endif
+
+
+
+
+
+/// Up to this many m_SpreadQueue elements are handled each world tick
+const int MAX_LIGHTING_SPREAD_PER_TICK = 10;
+
+const int TIME_SUNSET = 12000;
+const int TIME_NIGHT_START = 13187;
+const int TIME_NIGHT_END = 22812;
+const int TIME_SUNRISE = 23999;
+const int TIME_SPAWN_DIVISOR = 148;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorldLoadProgress:
+
+/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn()
+class cWorldLoadProgress :
+ public cIsThread
+{
+public:
+ cWorldLoadProgress(cWorld * a_World) :
+ cIsThread("cWorldLoadProgress"),
+ m_World(a_World)
+ {
+ Start();
+ }
+
+ void Stop(void)
+ {
+ m_ShouldTerminate = true;
+ Wait();
+ }
+
+protected:
+
+ cWorld * m_World;
+
+ virtual void Execute(void) override
+ {
+ for (;;)
+ {
+ LOG("%d chunks to load, %d chunks to generate",
+ m_World->GetStorage().GetLoadQueueLength(),
+ m_World->GetGenerator().GetQueueLength()
+ );
+
+ // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
+ for (int i = 0; i < 20; i++)
+ {
+ cSleep::MilliSleep(100);
+ if (m_ShouldTerminate)
+ {
+ return;
+ }
+ }
+ } // for (-ever)
+ }
+
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorldLightingProgress:
+
+/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
+class cWorldLightingProgress :
+ public cIsThread
+{
+public:
+ cWorldLightingProgress(cLightingThread * a_Lighting) :
+ cIsThread("cWorldLightingProgress"),
+ m_Lighting(a_Lighting)
+ {
+ Start();
+ }
+
+ void Stop(void)
+ {
+ m_ShouldTerminate = true;
+ Wait();
+ }
+
+protected:
+
+ cLightingThread * m_Lighting;
+
+ virtual void Execute(void) override
+ {
+ for (;;)
+ {
+ LOG("%d chunks remaining to light", m_Lighting->GetQueueLength()
+ );
+
+ // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
+ for (int i = 0; i < 20; i++)
+ {
+ cSleep::MilliSleep(100);
+ if (m_ShouldTerminate)
+ {
+ return;
+ }
+ }
+ } // for (-ever)
+ }
+
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorld::cLock:
+
+cWorld::cLock::cLock(cWorld & a_World) :
+ super(&(a_World.m_ChunkMap->GetCS()))
+{
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorld::cTickThread:
+
+cWorld::cTickThread::cTickThread(cWorld & a_World) :
+ super(Printf("WorldTickThread: %s", a_World.GetName().c_str())),
+ m_World(a_World)
+{
+}
+
+
+
+
+
+void cWorld::cTickThread::Execute(void)
+{
+ cTimer Timer;
+
+ long long msPerTick = 50;
+ long long LastTime = Timer.GetNowTime();
+
+ while (!m_ShouldTerminate)
+ {
+ long long NowTime = Timer.GetNowTime();
+ float DeltaTime = (float)(NowTime - LastTime);
+ m_World.Tick(DeltaTime);
+ long long TickTime = Timer.GetNowTime() - NowTime;
+
+ if (TickTime < msPerTick)
+ {
+ // Stretch tick time until it's at least msPerTick
+ cSleep::MilliSleep((unsigned int)(msPerTick - TickTime));
+ }
+
+ LastTime = NowTime;
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorld:
+
+cWorld::cWorld(const AString & a_WorldName) :
+ m_WorldName(a_WorldName),
+ m_IniFileName(m_WorldName + "/world.ini"),
+ m_StorageSchema("Default"),
+ m_WorldAgeSecs(0),
+ m_TimeOfDaySecs(0),
+ m_WorldAge(0),
+ m_TimeOfDay(0),
+ m_LastTimeUpdate(0),
+ m_RSList(0),
+ m_Weather(eWeather_Sunny),
+ m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
+ m_TickThread(*this),
+ m_SkyDarkness(0)
+{
+ LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str());
+
+ cFile::CreateFolder(FILE_IO_PREFIX + m_WorldName);
+}
+
+
+
+
+
+cWorld::~cWorld()
+{
+ delete m_SimulatorManager;
+ delete m_SandSimulator;
+ delete m_WaterSimulator;
+ delete m_LavaSimulator;
+ delete m_FireSimulator;
+ delete m_RedstoneSimulator;
+
+ UnloadUnusedChunks();
+
+ m_Storage.WaitForFinish();
+
+ delete m_ChunkMap;
+}
+
+
+
+
+
+void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cWorld::SetWeather(eWeather a_NewWeather)
+{
+ // Do the plugins agree? Do they want a different weather?
+ cRoot::Get()->GetPluginManager()->CallHookWeatherChanging(*this, a_NewWeather);
+
+ // Set new period for the selected weather:
+ switch (a_NewWeather)
+ {
+ case eWeather_Sunny: m_WeatherInterval = 14400 + (m_TickRand.randInt() % 4800); break; // 12 - 16 minutes
+ case eWeather_Rain: m_WeatherInterval = 9600 + (m_TickRand.randInt() % 7200); break; // 8 - 14 minutes
+ case eWeather_ThunderStorm: m_WeatherInterval = 2400 + (m_TickRand.randInt() % 4800); break; // 2 - 6 minutes
+ default:
+ {
+ LOGWARNING("Requested unknown weather %d, setting sunny for a minute instead.", a_NewWeather);
+ a_NewWeather = eWeather_Sunny;
+ m_WeatherInterval = 1200;
+ break;
+ }
+ } // switch (NewWeather)
+ m_Weather = a_NewWeather;
+ BroadcastWeather(m_Weather);
+
+ // Let the plugins know about the change:
+ cPluginManager::Get()->CallHookWeatherChanged(*this);
+}
+
+
+
+
+
+void cWorld::ChangeWeather(void)
+{
+ // In the next tick the weather will be changed
+ m_WeatherInterval = 0;
+}
+
+
+
+
+
+void cWorld::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ return m_ChunkMap->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cWorld::InitializeSpawn(void)
+{
+ int ChunkX = 0, ChunkY = 0, ChunkZ = 0;
+ BlockToChunk((int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ);
+
+ // For the debugging builds, don't make the server build too much world upon start:
+ #if defined(_DEBUG) || defined(ANDROID_NDK)
+ int ViewDist = 9;
+ #else
+ int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
+ #endif // _DEBUG
+
+ LOG("Preparing spawn area in world \"%s\"...", m_WorldName.c_str());
+ for (int x = 0; x < ViewDist; x++)
+ {
+ for (int z = 0; z < ViewDist; z++)
+ {
+ m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, ZERO_CHUNK_Y, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader
+ }
+ }
+
+ {
+ // Display progress during this process:
+ cWorldLoadProgress Progress(this);
+
+ // Wait for the loader to finish loading
+ m_Storage.WaitForQueuesEmpty();
+
+ // Wait for the generator to finish generating
+ m_Generator.WaitForQueueEmpty();
+
+ Progress.Stop();
+ }
+
+ // Light all chunks that have been newly generated:
+ LOG("Lighting spawn area in world \"%s\"...", m_WorldName.c_str());
+
+ for (int x = 0; x < ViewDist; x++)
+ {
+ int ChX = x + ChunkX-(ViewDist - 1) / 2;
+ for (int z = 0; z < ViewDist; z++)
+ {
+ int ChZ = z + ChunkZ-(ViewDist - 1) / 2;
+ if (!m_ChunkMap->IsChunkLighted(ChX, ChZ))
+ {
+ m_Lighting.QueueChunk(ChX, ChZ); // Queue the chunk in the lighting thread
+ }
+ } // for z
+ } // for x
+
+ {
+ cWorldLightingProgress Progress(&m_Lighting);
+ m_Lighting.WaitForQueueEmpty();
+ Progress.Stop();
+ }
+
+ // TODO: Better spawn detection - move spawn out of the water if it isn't set in the INI already
+ m_SpawnY = (double)GetHeight((int)m_SpawnX, (int)m_SpawnZ) + 1.6f; // +1.6f eye height
+
+
+ #ifdef TEST_LINEBLOCKTRACER
+ // DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
+ class cTracerCallbacks :
+ public cBlockTracer::cCallbacks
+ {
+ virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
+ {
+ LOGD("Block {%d, %d, %d}: %d:%d (%s)",
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta,
+ ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str()
+ );
+ return false;
+ }
+
+ virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ LOGD("Block {%d, %d, %d}: no data available",
+ a_BlockX, a_BlockY, a_BlockZ
+ );
+ return false;
+ }
+
+ virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override
+ {
+ LOGD("Out of world at {%f, %f, %f}", a_BlockX, a_BlockY, a_BlockZ);
+ return false;
+ }
+
+ virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override
+ {
+ LOGD("Into world at {%f, %f, %f}", a_BlockX, a_BlockY, a_BlockZ);
+ return false;
+ }
+
+ virtual void OnNoMoreHits(void) override
+ {
+ LOGD("No more hits");
+ }
+ } Callbacks;
+ LOGD("Spawn is at {%f, %f, %f}", m_SpawnX, m_SpawnY, m_SpawnZ);
+ LOGD("Tracing a line along +X:");
+ cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 10, m_SpawnY, m_SpawnZ, m_SpawnX + 10, m_SpawnY, m_SpawnZ);
+ LOGD("Tracing a line along -Z:");
+ cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX, m_SpawnY, m_SpawnZ + 10, m_SpawnX, m_SpawnY, m_SpawnZ - 10);
+ LOGD("Tracing a line along -Y, out of world:");
+ cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX, 260, m_SpawnZ, m_SpawnX, -5, m_SpawnZ);
+ LOGD("Tracing a line along XY:");
+ cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 10, m_SpawnY - 10, m_SpawnZ, m_SpawnX + 10, m_SpawnY + 10, m_SpawnZ);
+ LOGD("Tracing a line in generic direction:");
+ cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 15, m_SpawnY - 5, m_SpawnZ + 7.5, m_SpawnX + 13, m_SpawnY - 10, m_SpawnZ + 8.5);
+ LOGD("Tracing tests done");
+ #endif // TEST_LINEBLOCKTRACER
+}
+
+
+
+
+
+void cWorld::Start(void)
+{
+ // TODO: Find a proper spawn location, based on the biomes (not in ocean)
+ m_SpawnX = (double)((m_TickRand.randInt() % 1000) - 500);
+ m_SpawnY = cChunkDef::Height;
+ m_SpawnZ = (double)((m_TickRand.randInt() % 1000) - 500);
+ m_GameMode = eGameMode_Creative;
+
+ cIniFile IniFile;
+ if (!IniFile.ReadFile(m_IniFileName))
+ {
+ LOGWARNING("Cannot read world settings from \"%s\", defaults will be used.", m_IniFileName.c_str());
+ }
+ AString Dimension = IniFile.GetValueSet("General", "Dimension", "Overworld");
+ m_Dimension = StringToDimension(Dimension);
+ switch (m_Dimension)
+ {
+ case dimNether:
+ case dimOverworld:
+ case dimEnd:
+ {
+ break;
+ }
+ default:
+ {
+ LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", Dimension.c_str());
+ m_Dimension = dimOverworld;
+ break;
+ }
+ } // switch (m_Dimension)
+ m_SpawnX = IniFile.GetValueSetF("SpawnPosition", "X", m_SpawnX);
+ m_SpawnY = IniFile.GetValueSetF("SpawnPosition", "Y", m_SpawnY);
+ m_SpawnZ = IniFile.GetValueSetF("SpawnPosition", "Z", m_SpawnZ);
+ m_StorageSchema = IniFile.GetValueSet ("Storage", "Schema", m_StorageSchema);
+ m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3);
+ m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3);
+ m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false);
+ m_IsCarrotsBonemealable = IniFile.GetValueSetB("Plants", "IsCarrotsBonemealable", true);
+ m_IsCropsBonemealable = IniFile.GetValueSetB("Plants", "IsCropsBonemealable", true);
+ m_IsGrassBonemealable = IniFile.GetValueSetB("Plants", "IsGrassBonemealable", true);
+ m_IsMelonStemBonemealable = IniFile.GetValueSetB("Plants", "IsMelonStemBonemealable", true);
+ m_IsMelonBonemealable = IniFile.GetValueSetB("Plants", "IsMelonBonemealable", false);
+ m_IsPotatoesBonemealable = IniFile.GetValueSetB("Plants", "IsPotatoesBonemealable", true);
+ m_IsPumpkinStemBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinStemBonemealable", true);
+ m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false);
+ m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true);
+ m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false);
+ m_bEnabledPVP = IniFile.GetValueSetB("PVP", "Enabled", true);
+ m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", false);
+
+ m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode);
+
+ // Load allowed mobs:
+ const char * 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 dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break;
+ case dimEnd: DefaultMonsters = "enderman"; break;
+ default:
+ {
+ ASSERT(!"Unhandled world dimension");
+ DefaultMonsters = "wither";
+ break;
+ }
+ }
+ m_bAnimals = IniFile.GetValueSetB("Monsters", "AnimalsOn", true);
+ AString AllMonsters = IniFile.GetValueSet("Monsters", "Types", DefaultMonsters);
+ AStringVector SplitList = StringSplitAndTrim(AllMonsters, ",");
+ for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr)
+ {
+ cMonster::eType ToAdd = cMonster::StringToMobType(*itr);
+ if (ToAdd != cMonster::mtInvalidType)
+ {
+ m_AllowedMobs.insert(ToAdd);
+ LOGD("Allowed mob: %s", itr->c_str());
+ }
+ else
+ {
+ LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str());
+ }
+ }
+
+ m_ChunkMap = new cChunkMap(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);
+
+ // Simulators:
+ m_SimulatorManager = new cSimulatorManager(*this);
+ m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
+ m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA);
+ m_SandSimulator = new cSandSimulator(*this, IniFile);
+ m_FireSimulator = new cFireSimulator(*this, IniFile);
+ m_RedstoneSimulator = new cRedstoneSimulator(*this);
+
+ // Water and Lava simulators get registered in InitializeFluidSimulator()
+ m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1);
+ m_SimulatorManager->RegisterSimulator(m_FireSimulator, 1);
+ m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1);
+
+ m_Lighting.Start(this);
+ m_Storage.Start(this, m_StorageSchema);
+ m_Generator.Start(this, IniFile);
+ m_ChunkSender.Start(this);
+ m_TickThread.Start();
+
+ // Init of the spawn monster time (as they are supposed to have different spawn rate)
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfHostile, 0));
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfPassive, 0));
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfAmbient, 0));
+ m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfWater, 0));
+
+
+ // Save any changes that the defaults may have done to the ini file:
+ if (!IniFile.WriteFile(m_IniFileName))
+ {
+ LOGWARNING("Could not write world config to %s", m_IniFileName.c_str());
+ }
+
+}
+
+
+
+
+
+void cWorld::Stop(void)
+{
+ // Delete the clients that have been in this world:
+ {
+ cCSLock Lock(m_CSClients);
+ for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
+ {
+ (*itr)->Destroy();
+ delete *itr;
+ } // for itr - m_Clients[]
+ m_Clients.clear();
+ }
+
+ m_TickThread.Stop();
+ m_Lighting.Stop();
+ m_Generator.Stop();
+ m_ChunkSender.Stop();
+ m_Storage.Stop();
+}
+
+
+
+
+
+void cWorld::Tick(float a_Dt)
+{
+ // Call the plugins
+ cPluginManager::Get()->CallHookWorldTick(*this, a_Dt);
+
+ // We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it
+ m_WorldAgeSecs += (double)a_Dt / 1000.0;
+ m_TimeOfDaySecs += (double)a_Dt / 1000.0;
+
+ // Wrap time of day each 20 minutes (1200 seconds)
+ if (m_TimeOfDaySecs > 1200.0)
+ {
+ m_TimeOfDaySecs -= 1200.0;
+ }
+
+ m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0);
+ m_TimeOfDay = (Int64)(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)
+ {
+ BroadcastTimeUpdate();
+ m_LastTimeUpdate = m_WorldAge;
+ }
+
+ m_ChunkMap->Tick(a_Dt);
+
+ TickClients(a_Dt);
+ TickQueuedBlocks();
+ TickQueuedTasks();
+
+ GetSimulatorManager()->Simulate(a_Dt);
+
+ TickWeather(a_Dt);
+
+ // Asynchronously set blocks:
+ sSetBlockList FastSetBlockQueueCopy;
+ {
+ cCSLock Lock(m_CSFastSetBlock);
+ std::swap(FastSetBlockQueueCopy, m_FastSetBlockQueue);
+ }
+ m_ChunkMap->FastSetBlocks(FastSetBlockQueueCopy);
+ if (!FastSetBlockQueueCopy.empty())
+ {
+ // Some blocks failed, store them for next tick:
+ cCSLock Lock(m_CSFastSetBlock);
+ m_FastSetBlockQueue.splice(m_FastSetBlockQueue.end(), FastSetBlockQueueCopy);
+ }
+
+ if (m_WorldAge - m_LastSave > 60 * 5 * 20) // Save each 5 minutes
+ {
+ SaveAllChunks();
+ }
+
+ if (m_WorldAge - m_LastUnload > 10 * 20) // Unload every 10 seconds
+ {
+ UnloadUnusedChunks();
+ }
+
+ TickMobs(a_Dt);
+
+ std::vector<int> m_RSList_copy(m_RSList);
+
+ m_RSList.clear();
+
+ std::vector<int>::const_iterator cii; // FIXME - Please rename this variable, WTF is cii??? Use human readable variable names or common abbreviations (i, idx, itr, iter)
+ for (cii = m_RSList_copy.begin(); cii != m_RSList_copy.end();)
+ {
+ int tempX = *cii; cii++;
+ int tempY = *cii; cii++;
+ int tempZ = *cii; cii++;
+ int state = *cii; cii++;
+
+ if ((state == 11111) && ((int)GetBlock(tempX, tempY, tempZ) == E_BLOCK_REDSTONE_TORCH_OFF))
+ {
+ FastSetBlock(tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_ON, (int)GetBlockMeta(tempX, tempY, tempZ));
+ }
+ else if ((state == 00000) && ((int)GetBlock(tempX, tempY, tempZ) == E_BLOCK_REDSTONE_TORCH_ON))
+ {
+ FastSetBlock(tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_OFF, (int)GetBlockMeta(tempX, tempY, tempZ));
+ }
+ }
+ m_RSList_copy.erase(m_RSList_copy.begin(),m_RSList_copy.end());
+}
+
+
+
+
+
+void cWorld::TickWeather(float a_Dt)
+{
+ // There are no weather changes anywhere but in the Overworld:
+ if (GetDimension() != dimOverworld)
+ {
+ return;
+ }
+
+ if (m_WeatherInterval > 0)
+ {
+ // Not yet, wait for the weather period to end
+ m_WeatherInterval--;
+ }
+ else
+ {
+ // Change weather:
+
+ // Pick a new weather. Only reasonable transitions allowed:
+ eWeather NewWeather = m_Weather;
+ switch (m_Weather)
+ {
+ case eWeather_Sunny: NewWeather = eWeather_Rain; break;
+ case eWeather_ThunderStorm: NewWeather = eWeather_Rain; break;
+ case eWeather_Rain:
+ {
+ // 1/8 chance of turning into a thunderstorm
+ NewWeather = ((m_TickRand.randInt() % 256) < 32) ? eWeather_ThunderStorm : eWeather_Sunny;
+ break;
+ }
+
+ default:
+ {
+ LOGWARNING("Unknown current weather: %d. Setting sunny.", m_Weather);
+ ASSERT(!"Unknown weather");
+ NewWeather = eWeather_Sunny;
+ }
+ }
+
+ SetWeather(NewWeather);
+ } // else (m_WeatherInterval > 0)
+
+ if (m_Weather == eWeather_ThunderStorm)
+ {
+ // 0.5% chance per tick of thunderbolt
+ if (m_TickRand.randInt() % 199 == 0)
+ {
+ CastThunderbolt(0, 0, 0); // TODO: find random possitions near players to cast thunderbolts.
+ }
+ }
+}
+
+
+
+
+
+void cWorld::TickMobs(float 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);
+
+ // before every Mob action, we have to count them depending on the distance to players, on their family ...
+ cMobCensus MobCensus;
+ m_ChunkMap->CollectMobCensus(MobCensus);
+ if (m_bAnimals)
+ {
+ // Spawning is enabled, spawn now:
+ static const cMonster::eFamily AllFamilies[] =
+ {
+ cMonster::mfHostile,
+ cMonster::mfPassive,
+ cMonster::mfAmbient,
+ cMonster::mfWater,
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(AllFamilies); i++)
+ {
+ cMonster::eFamily Family = AllFamilies[i];
+ int SpawnDelay = cMonster::GetSpawnDelay(Family);
+ if (
+ (m_LastSpawnMonster[Family] > m_WorldAge - SpawnDelay) || // Not reached the needed ticks before the next round
+ MobCensus.IsCapped(Family)
+ )
+ {
+ continue;
+ }
+ m_LastSpawnMonster[Family] = m_WorldAge;
+ cMobSpawner Spawner(Family, m_AllowedMobs);
+ if (Spawner.CanSpawnAnything())
+ {
+ m_ChunkMap->SpawnMobs(Spawner);
+ // do the spawn
+ for (cMobSpawner::tSpawnedContainer::const_iterator itr2 = Spawner.getSpawned().begin(); itr2 != Spawner.getSpawned().end(); itr2++)
+ {
+ SpawnMobFinalize(*itr2);
+ }
+ }
+ } // for i - AllFamilies[]
+ } // if (Spawning enabled)
+
+ // move close mobs
+ cMobProximityCounter::sIterablePair allCloseEnoughToMoveMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(-1, 64 * 16);// MG TODO : deal with this magic number (the 16 is the size of a block)
+ for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allCloseEnoughToMoveMobs.m_Begin; itr != allCloseEnoughToMoveMobs.m_End; itr++)
+ {
+ itr->second.m_Monster.Tick(a_Dt, itr->second.m_Chunk);
+ }
+
+ // remove too far mobs
+ cMobProximityCounter::sIterablePair allTooFarMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(128 * 16, -1);// MG TODO : deal with this magic number (the 16 is the size of a block)
+ for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allTooFarMobs.m_Begin; itr != allTooFarMobs.m_End; itr++)
+ {
+ itr->second.m_Monster.Destroy(true);
+ }
+}
+
+
+
+
+
+void cWorld::TickQueuedTasks(void)
+{
+ // Make a copy of the tasks to avoid deadlocks on accessing m_Tasks
+ cTasks Tasks;
+ {
+ cCSLock Lock(m_CSTasks);
+ std::swap(Tasks, m_Tasks);
+ }
+
+ // Execute and delete each task:
+ for (cTasks::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr)
+ {
+ (*itr)->Run(*this);
+ delete *itr;
+ } // for itr - m_Tasks[]
+}
+
+
+
+
+
+void cWorld::TickClients(float a_Dt)
+{
+ cClientHandleList RemoveClients;
+ {
+ cCSLock Lock(m_CSClients);
+
+ // Remove clients scheduled for removal:
+ for (cClientHandleList::iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr)
+ {
+ m_Clients.remove(*itr);
+ } // for itr - m_ClientsToRemove[]
+ m_ClientsToRemove.clear();
+
+ // Add clients scheduled for adding:
+ for (cClientHandleList::iterator itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr)
+ {
+ if (std::find(m_Clients.begin(), m_Clients.end(), *itr) != m_Clients.end())
+ {
+ ASSERT(!"Adding a client that is already in the clientlist");
+ continue;
+ }
+ m_Clients.push_back(*itr);
+ } // for itr - m_ClientsToRemove[]
+ m_ClientsToAdd.clear();
+
+ // Tick the clients, take out those that have been destroyed into RemoveClients
+ for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();)
+ {
+ if ((*itr)->IsDestroyed())
+ {
+ // Remove the client later, when CS is not held, to avoid deadlock
+ RemoveClients.push_back(*itr);
+ itr = m_Clients.erase(itr);
+ continue;
+ }
+ (*itr)->Tick(a_Dt);
+ ++itr;
+ } // for itr - m_Clients[]
+ }
+
+ // Delete the clients that have been destroyed
+ for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr)
+ {
+ delete *itr;
+ } // for itr - RemoveClients[]
+}
+
+
+
+
+
+void cWorld::UpdateSkyDarkness(void)
+{
+ int TempTime = (int)m_TimeOfDay;
+ if (TempTime <= TIME_SUNSET)
+ {
+ m_SkyDarkness = 0;
+ }
+ else if (TempTime <= TIME_NIGHT_START)
+ {
+ m_SkyDarkness = (TIME_NIGHT_START - TempTime) / TIME_SPAWN_DIVISOR;
+ }
+ else if (TempTime <= TIME_NIGHT_END)
+ {
+ m_SkyDarkness = 8;
+ }
+ else
+ {
+ m_SkyDarkness = (TIME_SUNRISE - TempTime) / TIME_SPAWN_DIVISOR;
+ }
+}
+
+
+
+
+
+void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ return m_ChunkMap->WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+/// Wakes up the simulators for the specified area of blocks
+void cWorld::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ)
+{
+ return m_ChunkMap->WakeUpSimulatorsInArea(a_MinBlockX, a_MaxBlockX, a_MinBlockY, a_MaxBlockY, a_MinBlockZ, a_MaxBlockZ);
+}
+
+
+
+
+
+bool cWorld::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback)
+{
+ return m_ChunkMap->ForEachBlockEntityInChunk(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback)
+{
+ return m_ChunkMap->ForEachChestInChunk(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback)
+{
+ return m_ChunkMap->ForEachDispenserInChunk(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback)
+{
+ return m_ChunkMap->ForEachDropperInChunk(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback)
+{
+ return m_ChunkMap->ForEachDropSpenserInChunk(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback)
+{
+ return m_ChunkMap->ForEachFurnaceInChunk(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData)
+{
+ if (cPluginManager::Get()->CallHookExploding(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData) || (a_ExplosionSize <= 0))
+ {
+ return;
+ }
+
+ // TODO: Add damage to entities, add support for pickups, and implement block hardiness
+ Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ);
+ cVector3iArray BlocksAffected;
+ m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected);
+ BroadcastSoundEffect("random.explode", (int)floor(a_BlockX * 8), (int)floor(a_BlockY * 8), (int)floor(a_BlockZ * 8), 1.0f, 0.6f);
+ {
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ {
+ continue;
+ }
+ Vector3d distance_explosion = (*itr)->GetPosition() - explosion_pos;
+ if (distance_explosion.SqrLength() < 4096.0)
+ {
+ double real_distance = std::max(0.004, sqrt(distance_explosion.SqrLength()));
+ double power = a_ExplosionSize / real_distance;
+ if (power <= 1)
+ {
+ power = 0;
+ }
+ distance_explosion.Normalize();
+ distance_explosion *= power;
+ ch->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, (float)a_ExplosionSize, BlocksAffected, distance_explosion);
+ }
+ }
+ }
+ cPluginManager::Get()->CallHookExploded(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData);
+}
+
+
+
+
+
+bool cWorld::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback)
+{
+ return m_ChunkMap->DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback)
+{
+ return m_ChunkMap->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback)
+{
+ return m_ChunkMap->DoWithDispenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback)
+{
+ return m_ChunkMap->DoWithDropperAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback)
+{
+ return m_ChunkMap->DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback)
+{
+ return m_ChunkMap->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
+{
+ return m_ChunkMap->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4);
+}
+
+
+
+
+
+bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback)
+{
+ return m_ChunkMap->DoWithChunk(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+void cWorld::GrowTree(int a_X, int a_Y, int a_Z)
+{
+ if (GetBlock(a_X, a_Y, a_Z) == E_BLOCK_SAPLING)
+ {
+ // There is a sapling here, grow a tree according to its type:
+ GrowTreeFromSapling(a_X, a_Y, a_Z, GetBlockMeta(a_X, a_Y, a_Z));
+ }
+ else
+ {
+ // There is nothing here, grow a tree based on the current biome here:
+ GrowTreeByBiome(a_X, a_Y, a_Z);
+ }
+}
+
+
+
+
+
+void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_SaplingMeta)
+{
+ cNoise Noise(m_Generator.GetSeed());
+ sSetBlockVector Logs, Other;
+ 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;
+ }
+ Other.insert(Other.begin(), Logs.begin(), Logs.end());
+ Logs.clear();
+ GrowTreeImage(Other);
+}
+
+
+
+
+
+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), (EMCSBiome)GetBiomeAt(a_X, a_Z), Logs, Other);
+ Other.insert(Other.begin(), Logs.begin(), Logs.end());
+ Logs.clear();
+ GrowTreeImage(Other);
+}
+
+
+
+
+
+void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks)
+{
+ // Check that the tree has place to grow
+
+ // Make a copy of the log blocks:
+ sSetBlockVector b2;
+ for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
+ {
+ if (itr->BlockType == E_BLOCK_LOG)
+ {
+ b2.push_back(*itr);
+ }
+ } // for itr - a_Blocks[]
+
+ // Query blocktypes and metas at those log blocks:
+ if (!GetBlocks(b2, false))
+ {
+ return;
+ }
+
+ // 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)
+ {
+ CASE_TREE_ALLOWED_BLOCKS:
+ {
+ break;
+ }
+ default:
+ {
+ return;
+ }
+ }
+ } // for itr - b2[]
+
+ // All ok, replace blocks with the tree image:
+ m_ChunkMap->ReplaceTreeBlocks(a_Blocks);
+}
+
+
+
+
+
+bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal)
+{
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
+ switch (BlockType)
+ {
+ case E_BLOCK_CARROTS:
+ {
+ if (a_IsByBonemeal && !m_IsCarrotsBonemealable)
+ {
+ return false;
+ }
+ if (BlockMeta < 7)
+ {
+ FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7);
+ }
+ return true;
+ }
+
+ case E_BLOCK_CROPS:
+ {
+ if (a_IsByBonemeal && !m_IsCropsBonemealable)
+ {
+ return false;
+ }
+ if (BlockMeta < 7)
+ {
+ FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7);
+ }
+ return true;
+ }
+
+ case E_BLOCK_MELON_STEM:
+ {
+ if (BlockMeta < 7)
+ {
+ if (a_IsByBonemeal && !m_IsMelonStemBonemealable)
+ {
+ return false;
+ }
+ FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7);
+ }
+ else
+ {
+ if (a_IsByBonemeal && !m_IsMelonBonemealable)
+ {
+ return false;
+ }
+ GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType);
+ }
+ return true;
+ }
+
+ case E_BLOCK_POTATOES:
+ {
+ if (a_IsByBonemeal && !m_IsPotatoesBonemealable)
+ {
+ return false;
+ }
+ if (BlockMeta < 7)
+ {
+ FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7);
+ }
+ return true;
+ }
+
+ case E_BLOCK_PUMPKIN_STEM:
+ {
+ if (BlockMeta < 7)
+ {
+ if (a_IsByBonemeal && !m_IsPumpkinStemBonemealable)
+ {
+ return false;
+ }
+ FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7);
+ }
+ else
+ {
+ if (a_IsByBonemeal && !m_IsPumpkinBonemealable)
+ {
+ return false;
+ }
+ GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType);
+ }
+ return true;
+ }
+
+ case E_BLOCK_SAPLING:
+ {
+ if (a_IsByBonemeal && !m_IsSaplingBonemealable)
+ {
+ return false;
+ }
+ GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta);
+ return true;
+ }
+
+ case E_BLOCK_GRASS:
+ {
+ if (a_IsByBonemeal && !m_IsGrassBonemealable)
+ {
+ return false;
+ }
+ MTRand r1;
+ for (int i = 0; i < 60; i++)
+ {
+ int OfsX = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3;
+ int OfsY = r1.randInt(3) + r1.randInt(3) - 3;
+ int OfsZ = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3;
+ BLOCKTYPE Ground = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ);
+ if (Ground != E_BLOCK_GRASS)
+ {
+ continue;
+ }
+ BLOCKTYPE Above = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ);
+ if (Above != E_BLOCK_AIR)
+ {
+ continue;
+ }
+ BLOCKTYPE SpawnType;
+ NIBBLETYPE SpawnMeta = 0;
+ switch (r1.randInt(10))
+ {
+ case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break;
+ case 1: SpawnType = E_BLOCK_RED_ROSE; break;
+ default:
+ {
+ SpawnType = E_BLOCK_TALL_GRASS;
+ SpawnMeta = E_META_TALL_GRASS_GRASS;
+ break;
+ }
+ } // switch (random spawn block type)
+ FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, SpawnType, SpawnMeta);
+ } // for i - 50 times
+ return true;
+ }
+
+ case E_BLOCK_SUGARCANE:
+ {
+ if (a_IsByBonemeal && !m_IsSugarcaneBonemealable)
+ {
+ return false;
+ }
+ m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, m_MaxSugarcaneHeight);
+ return true;
+ }
+
+ case E_BLOCK_CACTUS:
+ {
+ if (a_IsByBonemeal && !m_IsCactusBonemealable)
+ {
+ return false;
+ }
+ m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, m_MaxCactusHeight);
+ return true;
+ }
+ } // switch (BlockType)
+ return false;
+}
+
+
+
+
+
+void cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
+{
+ m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
+}
+
+
+
+
+
+void cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
+{
+ MTRand Rand;
+ m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand);
+}
+
+
+
+
+
+void cWorld::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow)
+{
+ m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
+}
+
+
+
+
+
+int cWorld::GetBiomeAt (int a_BlockX, int a_BlockZ)
+{
+ return m_ChunkMap->GetBiomeAt(a_BlockX, a_BlockZ);
+}
+
+
+
+
+
+void cWorld::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ if (a_BlockType == E_BLOCK_AIR)
+ {
+ BlockHandler(GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnDestroyed(this, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ m_ChunkMap->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
+
+ BlockHandler(a_BlockType)->OnPlaced(this, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cWorld::FastSetBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ cCSLock Lock(m_CSFastSetBlock);
+ m_FastSetBlockQueue.push_back(sSetBlock(a_X, a_Y, a_Z, a_BlockType, a_BlockMeta));
+}
+
+
+
+
+
+void cWorld::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_TickDelay)
+{
+ m_ChunkMap->QueueSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, GetWorldAge() + a_TickDelay);
+}
+
+
+
+
+
+BLOCKTYPE cWorld::GetBlock(int a_X, int a_Y, int a_Z)
+{
+ // First check if it isn't queued in the m_FastSetBlockQueue:
+ {
+ int X = a_X, Y = a_Y, Z = a_Z;
+ int ChunkX, ChunkY, ChunkZ;
+ AbsoluteToRelative(X, Y, Z, ChunkX, ChunkY, 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))
+ {
+ return itr->BlockType;
+ }
+ } // for itr - m_FastSetBlockQueue[]
+ }
+
+ return m_ChunkMap->GetBlock(a_X, a_Y, a_Z);
+}
+
+
+
+
+
+NIBBLETYPE cWorld::GetBlockMeta(int a_X, int a_Y, int a_Z)
+{
+ // 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_X) && (itr->y == a_Y) && (itr->y == a_Y))
+ {
+ return itr->BlockMeta;
+ }
+ } // for itr - m_FastSetBlockQueue[]
+ }
+
+ return m_ChunkMap->GetBlockMeta(a_X, a_Y, a_Z);
+}
+
+
+
+
+
+void cWorld::SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData)
+{
+ m_ChunkMap->SetBlockMeta(a_X, a_Y, a_Z, a_MetaData);
+}
+
+
+
+
+
+NIBBLETYPE cWorld::GetBlockSkyLight(int a_X, int a_Y, int a_Z)
+{
+ return m_ChunkMap->GetBlockSkyLight(a_X, a_Y, a_Z);
+}
+
+
+
+
+
+NIBBLETYPE cWorld::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ return m_ChunkMap->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+bool cWorld::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
+{
+ return m_ChunkMap->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, (BLOCKTYPE &)a_BlockType, (NIBBLETYPE &)a_BlockMeta);
+}
+
+
+
+
+
+bool cWorld::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight)
+{
+ return m_ChunkMap->GetBlockInfo(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Meta, a_SkyLight, a_BlockLight);
+}
+
+
+
+
+
+bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
+{
+ return m_ChunkMap->WriteBlockArea(a_Area, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
+}
+
+
+
+
+
+void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed, bool IsPlayerCreated)
+{
+ MTRand r1;
+ a_FlyAwaySpeed /= 1000; // Pre-divide, so that we don't have to divide each time inside the loop
+ for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr)
+ {
+ float SpeedX = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500));
+ float SpeedY = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500));
+ float SpeedZ = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500));
+
+ cPickup * Pickup = new cPickup(
+ a_BlockX, a_BlockY, a_BlockZ,
+ *itr, IsPlayerCreated, SpeedX, SpeedY, SpeedZ
+ );
+ Pickup->Initialize(this);
+ }
+}
+
+
+
+
+
+void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated)
+{
+ for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr)
+ {
+ cPickup * Pickup = new cPickup(
+ a_BlockX, a_BlockY, a_BlockZ,
+ *itr, IsPlayerCreated, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ
+ );
+ Pickup->Initialize(this);
+ }
+}
+
+
+
+
+
+void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff)
+{
+ cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTimeInSec);
+ TNT->Initialize(this);
+ // TODO: Add a bit of speed in horiz and vert axes, based on the a_InitialVelocityCoeff
+}
+
+
+
+
+
+void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType)
+{
+ m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType);
+}
+
+
+
+
+
+bool cWorld::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
+{
+ return m_ChunkMap->GetBlocks(a_Blocks, a_ContinueOnFailure);
+}
+
+
+
+
+
+bool cWorld::DigBlock(int a_X, int a_Y, int a_Z)
+{
+ cBlockHandler *Handler = cBlockHandler::GetBlockHandler(GetBlock(a_X, a_Y, a_Z));
+ Handler->OnDestroyed(this, a_X, a_Y, a_Z);
+ return m_ChunkMap->DigBlock(a_X, a_Y, a_Z);
+}
+
+
+
+
+
+void cWorld::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player)
+{
+ m_ChunkMap->SendBlockTo(a_X, a_Y, a_Z, a_Player);
+}
+
+
+
+
+
+int cWorld::GetHeight(int a_X, int a_Z)
+{
+ return m_ChunkMap->GetHeight(a_X, a_Z);
+}
+
+
+
+
+
+bool cWorld::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height)
+{
+ return m_ChunkMap->TryGetHeight(a_BlockX, a_BlockZ, a_Height);
+}
+
+
+
+
+
+void cWorld::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ return m_ChunkMap->BroadcastAttachEntity(a_Entity, a_Vehicle);
+}
+
+
+
+
+
+void cWorld::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastBlockBreakAnimation(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ {
+ continue;
+ }
+ ch->SendChat(a_Message);
+ }
+}
+
+
+
+
+
+void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastDestroyEntity(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastEntityHeadLook(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastEntityLook(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastEntityMetadata(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastEntityVelocity(a_Entity, a_Exclude);
+}
+
+
+
+
+void cWorld::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ {
+ continue;
+ }
+ ch->SendPlayerListItem(a_Player, a_IsOnline);
+ }
+}
+
+
+
+
+
+void cWorld::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastSpawnEntity(a_Entity, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastTeleportEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ {
+ continue;
+ }
+ ch->SendTeleportEntity(a_Entity);
+ }
+}
+
+
+
+
+
+void cWorld::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude);
+}
+
+
+
+
+
+void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ {
+ continue;
+ }
+ ch->SendTimeUpdate(m_WorldAge, m_TimeOfDay);
+ }
+}
+
+
+
+
+
+void cWorld::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ m_ChunkMap->BroadcastUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cWorld::BroadcastWeather(eWeather a_Weather, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ {
+ continue;
+ }
+ ch->SendWeather(a_Weather);
+ }
+}
+
+
+
+
+
+void cWorld::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client)
+{
+ m_ChunkMap->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client);
+}
+
+
+
+
+
+void cWorld::MarkChunkDirty (int a_ChunkX, int a_ChunkZ)
+{
+ m_ChunkMap->MarkChunkDirty (a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::MarkChunkSaving(int a_ChunkX, int a_ChunkZ)
+{
+ m_ChunkMap->MarkChunkSaving(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::MarkChunkSaved (int a_ChunkX, int a_ChunkZ)
+{
+ m_ChunkMap->MarkChunkSaved (a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::SetChunkData(
+ int a_ChunkX, int a_ChunkZ,
+ const BLOCKTYPE * a_BlockTypes,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
+ const cChunkDef::HeightMap * a_HeightMap,
+ const cChunkDef::BiomeMap * a_BiomeMap,
+ cEntityList & a_Entities,
+ cBlockEntityList & a_BlockEntities,
+ bool a_MarkDirty
+)
+{
+ // Validate biomes, if needed:
+ cChunkDef::BiomeMap BiomeMap;
+ const cChunkDef::BiomeMap * Biomes = a_BiomeMap;
+ if (a_BiomeMap == NULL)
+ {
+ // The biomes are not assigned, get them from the generator:
+ Biomes = &BiomeMap;
+ m_Generator.GenerateBiomes(a_ChunkX, a_ChunkZ, BiomeMap);
+ }
+
+ m_ChunkMap->SetChunkData(
+ a_ChunkX, a_ChunkZ,
+ a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight,
+ a_HeightMap, *Biomes,
+ a_BlockEntities,
+ a_MarkDirty
+ );
+
+ // Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347):
+ for (cEntityList::iterator itr = a_Entities.begin(), end = a_Entities.end(); itr != end; ++itr)
+ {
+ (*itr)->Initialize(this);
+ }
+
+ // If a client is requesting this chunk, send it to them:
+ if (m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkZ))
+ {
+ m_ChunkSender.ChunkReady(a_ChunkX, a_ChunkZ);
+ }
+
+ // Notify the lighting thread that the chunk has become valid (in case it is a neighbor of a postponed chunk):
+ m_Lighting.ChunkReady(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::ChunkLighted(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
+)
+{
+ m_ChunkMap->ChunkLighted(a_ChunkX, a_ChunkZ, a_BlockLight, a_SkyLight);
+}
+
+
+
+
+
+bool cWorld::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback)
+{
+ return m_ChunkMap->GetChunkData(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes)
+{
+ return m_ChunkMap->GetChunkBlockTypes(a_ChunkX, a_ChunkZ, a_BlockTypes);
+}
+
+
+
+
+
+bool cWorld::IsChunkValid(int a_ChunkX, int a_ChunkZ) const
+{
+ return m_ChunkMap->IsChunkValid(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const
+{
+ return m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::UnloadUnusedChunks(void)
+{
+ m_LastUnload = m_WorldAge;
+ m_ChunkMap->UnloadUnusedChunks();
+}
+
+
+
+
+
+void cWorld::CollectPickupsByPlayer(cPlayer * a_Player)
+{
+ m_ChunkMap->CollectPickupsByPlayer(a_Player);
+}
+
+
+
+
+
+void cWorld::AddPlayer(cPlayer * a_Player)
+{
+ {
+ cCSLock Lock(m_CSPlayers);
+
+ ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW?
+
+ m_Players.remove(a_Player); // Make sure the player is registered only once
+ m_Players.push_back(a_Player);
+ }
+
+ // Add the player's client to the list of clients to be ticked:
+ if (a_Player->GetClientHandle() != NULL)
+ {
+ cCSLock Lock(m_CSClients);
+ m_ClientsToAdd.push_back(a_Player->GetClientHandle());
+ }
+
+ // The player has already been added to the chunkmap as the entity, do NOT add again!
+}
+
+
+
+
+
+void cWorld::RemovePlayer(cPlayer * a_Player)
+{
+ m_ChunkMap->RemoveEntity(a_Player);
+ {
+ cCSLock Lock(m_CSPlayers);
+ m_Players.remove(a_Player);
+ }
+
+ // Remove the player's client from the list of clients to be ticked:
+ if (a_Player->GetClientHandle() != NULL)
+ {
+ cCSLock Lock(m_CSClients);
+ m_ClientsToRemove.push_back(a_Player->GetClientHandle());
+ }
+}
+
+
+
+
+
+bool cWorld::ForEachPlayer(cPlayerListCallback & a_Callback)
+{
+ // Calls the callback for each player in the list
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(), itr2 = itr; itr != m_Players.end(); itr = itr2)
+ {
+ ++itr2;
+ if (a_Callback.Item(*itr))
+ {
+ return false;
+ }
+ } // for itr - m_Players[]
+ return true;
+}
+
+
+
+
+
+bool cWorld::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback)
+{
+ // Calls the callback for each player in the list
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ if (NoCaseCompare((*itr)->GetName(), a_PlayerName) == 0)
+ {
+ a_Callback.Item(*itr);
+ return true;
+ }
+ } // for itr - m_Players[]
+ return false;
+}
+
+
+
+
+
+bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback)
+{
+ cPlayer * BestMatch = NULL;
+ unsigned int BestRating = 0;
+ unsigned int NameLength = a_PlayerNameHint.length();
+
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ unsigned int Rating = RateCompareString (a_PlayerNameHint, (*itr)->GetName());
+ if (Rating >= BestRating)
+ {
+ BestMatch = *itr;
+ BestRating = Rating;
+ }
+ if (Rating == NameLength) // Perfect match
+ {
+ break;
+ }
+ } // for itr - m_Players[]
+
+ if (BestMatch != NULL)
+ {
+ LOG("Compared %s and %s with rating %i", a_PlayerNameHint.c_str(), BestMatch->GetName().c_str(), BestRating);
+ return a_Callback.Item (BestMatch);
+ }
+ return false;
+}
+
+
+
+
+
+// TODO: This interface is dangerous!
+cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit)
+{
+ cTracer LineOfSight(this);
+
+ float ClosestDistance = a_SightLimit;
+ cPlayer* ClosestPlayer = NULL;
+
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ Vector3f Pos = (*itr)->GetPosition();
+ float Distance = (Pos - a_Pos).Length();
+
+ if (Distance < ClosestDistance)
+ {
+ if (!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length()))
+ {
+ ClosestDistance = Distance;
+ ClosestPlayer = *itr;
+ }
+ }
+ }
+ return ClosestPlayer;
+}
+
+
+
+
+
+void cWorld::SendPlayerList(cPlayer * a_DestPlayer)
+{
+ // Sends the playerlist to a_DestPlayer
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch != NULL) && !ch->IsDestroyed())
+ {
+ a_DestPlayer->GetClientHandle()->SendPlayerListItem(*(*itr), true);
+ }
+ }
+}
+
+
+
+
+
+bool cWorld::ForEachEntity(cEntityCallback & a_Callback)
+{
+ return m_ChunkMap->ForEachEntity(a_Callback);
+}
+
+
+
+
+
+bool cWorld::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback)
+{
+ return m_ChunkMap->ForEachEntityInChunk(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback)
+{
+ return m_ChunkMap->DoWithEntityByID(a_UniqueID, a_Callback);
+}
+
+
+
+
+
+void cWorld::CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback)
+{
+ m_ChunkMap->CompareChunkClients(a_ChunkX1, a_ChunkZ1, a_ChunkX2, a_ChunkZ2, a_Callback);
+}
+
+
+
+
+
+bool cWorld::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
+{
+ return m_ChunkMap->AddChunkClient(a_ChunkX, a_ChunkZ, a_Client);
+}
+
+
+
+
+
+void cWorld::RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
+{
+ m_ChunkMap->RemoveChunkClient(a_ChunkX, a_ChunkZ, a_Client);
+}
+
+
+
+
+
+void cWorld::RemoveClientFromChunks(cClientHandle * a_Client)
+{
+ m_ChunkMap->RemoveClientFromChunks(a_Client);
+}
+
+
+
+
+
+void cWorld::SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
+{
+ m_ChunkSender.QueueSendChunkTo(a_ChunkX, a_ChunkZ, a_Client);
+}
+
+
+
+
+
+void cWorld::RemoveClientFromChunkSender(cClientHandle * a_Client)
+{
+ m_ChunkSender.RemoveClient(a_Client);
+}
+
+
+
+
+
+void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+}
+
+
+
+
+
+bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::LoadChunks(const cChunkCoordsList & a_Chunks)
+{
+ m_ChunkMap->LoadChunks(a_Chunks);
+}
+
+
+
+
+
+void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ);
+}
+
+
+
+
+
+bool cWorld::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player)
+{
+ AString Line1(a_Line1);
+ AString Line2(a_Line2);
+ AString Line3(a_Line3);
+ AString Line4(a_Line4);
+ if (cRoot::Get()->GetPluginManager()->CallHookUpdatingSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player))
+ {
+ return false;
+ }
+ if (m_ChunkMap->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4))
+ {
+ cRoot::Get()->GetPluginManager()->CallHookUpdatedSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player);
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+bool cWorld::UpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player)
+{
+ return SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player);
+}
+
+
+
+
+
+void cWorld::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay)
+{
+ m_ChunkMap->ChunksStay(a_Chunks, a_Stay);
+}
+
+
+
+
+
+void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ)
+{
+ m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ);
+
+ // Trick: use Y=1 to force the chunk generation even though the chunk data is already present
+ m_Generator.QueueGenerateChunk(a_ChunkX, 1, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ)
+{
+ m_Generator.QueueGenerateChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback)
+{
+ m_Lighting.QueueChunk(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::IsChunkLighted(int a_ChunkX, int a_ChunkZ)
+{
+ return m_ChunkMap->IsChunkLighted(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+bool cWorld::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback)
+{
+ return m_ChunkMap->ForEachChunkInRect(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ, a_Callback);
+}
+
+
+
+
+
+void cWorld::SaveAllChunks(void)
+{
+ LOGINFO("Saving all chunks...");
+ m_LastSave = m_WorldAge;
+ m_ChunkMap->SaveAllChunks();
+ m_Storage.QueueSavedMessage();
+}
+
+
+
+
+
+void cWorld::QueueSaveAllChunks(void)
+{
+ QueueTask(new cWorld::cTaskSaveAllChunks);
+}
+
+
+
+
+
+void cWorld::QueueTask(cTask * a_Task)
+{
+ cCSLock Lock(m_CSTasks);
+ m_Tasks.push_back(a_Task);
+}
+
+
+
+
+
+void cWorld::AddEntity(cEntity * a_Entity)
+{
+ m_ChunkMap->AddEntity(a_Entity);
+}
+
+
+
+
+
+bool cWorld::HasEntity(int a_UniqueID)
+{
+ return m_ChunkMap->HasEntity(a_UniqueID);
+}
+
+
+
+
+
+void cWorld::RemoveEntity(cEntity * a_Entity)
+{
+ m_ChunkMap->RemoveEntity(a_Entity);
+}
+
+
+
+
+
+/*
+unsigned int cWorld::GetNumPlayers(void)
+{
+ cCSLock Lock(m_CSPlayers);
+ return m_Players.size();
+}
+*/
+
+
+
+
+
+int cWorld::GetNumChunks(void) const
+{
+ return m_ChunkMap->GetNumChunks();
+}
+
+
+
+
+
+void cWorld::GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue)
+{
+ m_ChunkMap->GetChunkStats(a_NumValid, a_NumDirty);
+ a_NumInLightingQueue = (int) m_Lighting.GetQueueLength();
+}
+
+
+
+
+
+void cWorld::TickQueuedBlocks(void)
+{
+ if (m_BlockTickQueue.empty())
+ {
+ return;
+ }
+ m_BlockTickQueueCopy.clear();
+ m_BlockTickQueue.swap(m_BlockTickQueueCopy);
+
+ for (std::vector<BlockTickQueueItem *>::iterator itr = m_BlockTickQueueCopy.begin(); itr != m_BlockTickQueueCopy.end(); itr++)
+ {
+ BlockTickQueueItem *Block = (*itr);
+ Block->TicksToWait -= 1;
+ if (Block->TicksToWait <= 0)
+ {
+ // TODO: Handle the case when the chunk is already unloaded
+ BlockHandler(GetBlock(Block->X, Block->Y, Block->Z))->OnUpdate(this, Block->X, Block->Y, Block->Z);
+ delete Block; // We don't have to remove it from the vector, this will happen automatically on the next tick
+ }
+ else
+ {
+ m_BlockTickQueue.push_back(Block); // Keep the block in the queue
+ }
+ } // for itr - m_BlockTickQueueCopy[]
+}
+
+
+
+
+
+void cWorld::QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait)
+{
+ BlockTickQueueItem * Block = new BlockTickQueueItem;
+ Block->X = a_BlockX;
+ Block->Y = a_BlockY;
+ Block->Z = a_BlockZ;
+ Block->TicksToWait = a_TicksToWait;
+
+ m_BlockTickQueue.push_back(Block);
+}
+
+
+
+
+
+bool cWorld::IsBlockDirectlyWatered(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ return (
+ IsBlockWater(GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ)) ||
+ IsBlockWater(GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ)) ||
+ IsBlockWater(GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1)) ||
+ IsBlockWater(GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1))
+ );
+}
+
+
+
+
+
+int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType)
+{
+ cMonster * Monster = NULL;
+
+ Monster = cMonster::NewMonsterFromType(a_MonsterType);
+ if (Monster != NULL)
+ {
+ Monster->SetPosition(a_PosX, a_PosY, a_PosZ);
+ }
+
+ // Because it's logical that ALL mob spawns need spawn effects, not just spawners
+ BroadcastSoundParticleEffect(2004, (int)a_PosX, (int)a_PosY, (int)a_PosZ, 0);
+
+ return SpawnMobFinalize(Monster);
+}
+
+
+
+
+int cWorld::SpawnMobFinalize(cMonster * a_Monster)
+{
+ if (!a_Monster)
+ return -1;
+ a_Monster->SetHealth(a_Monster->GetMaxHealth());
+ if (cPluginManager::Get()->CallHookSpawningMonster(*this, *a_Monster))
+ {
+ delete a_Monster;
+ return -1;
+ }
+ if (!a_Monster->Initialize(this))
+ {
+ delete a_Monster;
+ return -1;
+ }
+ BroadcastSpawnEntity(*a_Monster);
+ cPluginManager::Get()->CallHookSpawnedMonster(*this, *a_Monster);
+
+ return a_Monster->GetUniqueID();
+}
+
+
+
+
+
+int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed)
+{
+ cProjectileEntity * Projectile = cProjectileEntity::Create(a_Kind, a_Creator, a_PosX, a_PosY, a_PosZ, a_Speed);
+ if (Projectile == NULL)
+ {
+ return -1;
+ }
+ if (!Projectile->Initialize(this))
+ {
+ delete Projectile;
+ return -1;
+ }
+ BroadcastSpawnEntity(*Projectile);
+ return Projectile->GetUniqueID();
+}
+
+
+
+
+
+void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Results)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(), end = m_Players.end(); itr != end; ++itr)
+ {
+ size_t LastSpace = a_Text.find_last_of(" "); //Find the position of the last space
+
+ std::string LastWord = a_Text.substr(LastSpace + 1, a_Text.length()); //Find the last word
+ std::string PlayerName ((*itr)->GetName());
+ std::size_t Found = PlayerName.find(LastWord); //Try to find last word in playername
+
+ if (Found!=0)
+ {
+ continue; //No match
+ }
+
+ a_Results.push_back((*itr)->GetName()); //Match!
+ }
+}
+
+
+
+
+
+cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock)
+{
+ AString SimulatorNameKey;
+ Printf(SimulatorNameKey, "%sSimulator", a_FluidName);
+ AString SimulatorSectionName;
+ Printf(SimulatorSectionName, "%sSimulator", a_FluidName);
+ AString SimulatorName = a_IniFile.GetValueSet("Physics", SimulatorNameKey, "");
+ if (SimulatorName.empty())
+ {
+ LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Floody\".", SimulatorNameKey.c_str(), GetIniFileName().c_str());
+ SimulatorName = "Floody";
+ }
+
+ cFluidSimulator * res = NULL;
+ bool IsWater = (strcmp(a_FluidName, "Water") == 0); // Used for defaults
+ int Rate = 1;
+ if (
+ (NoCaseCompare(SimulatorName, "vaporize") == 0) ||
+ (NoCaseCompare(SimulatorName, "vaporise") == 0)
+ )
+ {
+ res = new cVaporizeFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock);
+ }
+ else if (
+ (NoCaseCompare(SimulatorName, "noop") == 0) ||
+ (NoCaseCompare(SimulatorName, "nop") == 0) ||
+ (NoCaseCompare(SimulatorName, "null") == 0) ||
+ (NoCaseCompare(SimulatorName, "nil") == 0)
+ )
+ {
+ res = new cNoopFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock);
+ }
+ else
+ {
+ if (NoCaseCompare(SimulatorName, "floody") != 0)
+ {
+ // The simulator name doesn't match anything we have, issue a warning:
+ LOGWARNING("%s [Physics]:%s specifies an unknown simulator, using the default \"Floody\".", GetIniFileName().c_str(), SimulatorNameKey.c_str());
+ }
+ int Falloff = a_IniFile.GetValueSetI(SimulatorSectionName, "Falloff", IsWater ? 1 : 2);
+ int TickDelay = a_IniFile.GetValueSetI(SimulatorSectionName, "TickDelay", IsWater ? 5 : 30);
+ int NumNeighborsForSource = a_IniFile.GetValueSetI(SimulatorSectionName, "NumNeighborsForSource", IsWater ? 2 : -1);
+ res = new cFloodyFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource);
+ }
+
+ m_SimulatorManager->RegisterSimulator(res, Rate);
+
+ return res;
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorld::cTaskSaveAllChunks:
+
+void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World)
+{
+ a_World.SaveAllChunks();
+}
+
+
+
+
+
diff --git a/src/World.h b/src/World.h
new file mode 100644
index 000000000..d10aa3b78
--- /dev/null
+++ b/src/World.h
@@ -0,0 +1,750 @@
+
+#pragma once
+
+#ifndef _WIN32
+ #include "BlockID.h"
+#else
+ enum ENUM_ITEM_ID;
+#endif
+
+#define MAX_PLAYERS 65535
+
+#include "Simulator/SimulatorManager.h"
+#include "MersenneTwister.h"
+#include "ChunkMap.h"
+#include "WorldStorage/WorldStorage.h"
+#include "Generating/ChunkGenerator.h"
+#include "Vector3i.h"
+#include "Vector3f.h"
+#include "ChunkSender.h"
+#include "Defines.h"
+#include "LightingThread.h"
+#include "Item.h"
+#include "Mobs/Monster.h"
+#include "Entities/ProjectileEntity.h"
+
+
+
+
+
+class cRedstone;
+class cFireSimulator;
+class cFluidSimulator;
+class cSandSimulator;
+class cRedstoneSimulator;
+class cItem;
+class cPlayer;
+class cClientHandle;
+class cEntity;
+class cBlockEntity;
+class cWorldGenerator; // The generator that actually generates the chunks for a single world
+class cChunkGenerator; // The thread responsible for generating chunks
+class cChestEntity;
+class cDispenserEntity;
+class cFurnaceEntity;
+class cMobCensus;
+
+typedef std::list< cPlayer * > cPlayerList;
+
+typedef cItemCallback<cPlayer> cPlayerListCallback;
+typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cChestEntity> cChestCallback;
+typedef cItemCallback<cDispenserEntity> cDispenserCallback;
+typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
+
+
+
+
+
+
+// tolua_begin
+class cWorld
+{
+public:
+
+ // tolua_end
+
+ /// A simple RAII locker for the chunkmap - locks the chunkmap in its constructor, unlocks it in the destructor
+ class cLock :
+ public cCSLock
+ {
+ typedef cCSLock super;
+ public:
+ cLock(cWorld & a_World);
+ } ;
+
+ /// A common ancestor for all tasks queued onto the tick thread
+ class cTask
+ {
+ public:
+ virtual void Run(cWorld & a_World) = 0;
+ } ;
+
+ typedef std::vector<cTask *> cTasks;
+
+ class cTaskSaveAllChunks :
+ public cTask
+ {
+ protected:
+ // cTask overrides:
+ virtual void Run(cWorld & a_World) override;
+ } ;
+
+
+ static const char * GetClassStatic(void) // Needed for ManualBindings's ForEach templates
+ {
+ return "cWorld";
+ }
+
+ // tolua_begin
+
+ int GetTicksUntilWeatherChange(void) const { return m_WeatherInterval; }
+ Int64 GetWorldAge(void) const { return m_WorldAge; }
+ Int64 GetTimeOfDay(void) const { return m_TimeOfDay; }
+
+ void SetTicksUntilWeatherChange(int a_WeatherInterval)
+ {
+ m_WeatherInterval = a_WeatherInterval;
+ }
+
+ void SetTimeOfDay(Int64 a_TimeOfDay)
+ {
+ m_TimeOfDay = a_TimeOfDay;
+ m_TimeOfDaySecs = (double)a_TimeOfDay / 20.0;
+ BroadcastTimeUpdate();
+ }
+
+ /// Returns the current game mode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable
+ eGameMode GetGameMode(void) const { return m_GameMode; }
+
+ /// Returns true if the world is in Creative mode
+ bool IsGameModeCreative(void) const { return (m_GameMode == gmCreative); }
+
+ /// Returns true if the world is in Survival mode
+ bool IsGameModeSurvival(void) const { return (m_GameMode == gmSurvival); }
+
+ /// Returns true if the world is in Adventure mode
+ bool IsGameModeAdventure(void) const { return (m_GameMode == gmAdventure); }
+
+ bool IsPVPEnabled(void) const { return m_bEnabledPVP; }
+ bool IsDeepSnowEnabled(void) const { return m_IsDeepSnowEnabled; }
+
+ eDimension GetDimension(void) const { return m_Dimension; }
+
+ /// Returns the world height at the specified coords; waits for the chunk to get loaded / generated
+ int GetHeight(int a_BlockX, int a_BlockZ);
+
+ // tolua_end
+
+ /// Retrieves the world height at the specified coords; returns false if chunk not loaded / generated
+ bool TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height); // Exported in ManualBindings.cpp
+
+ // Broadcast respective packets to all clients of the chunk where the event is taking place
+ // (Please keep these alpha-sorted)
+ void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
+ void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL);
+ void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL);
+ void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude
+ void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_export
+ void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL);
+ void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL);
+ void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityMetadata (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL);
+ void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastPlayerAnimation (const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL);
+ void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL);
+ void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // tolua_export a_Src coords are Block * 8
+ void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); // tolua_export
+ void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
+ void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL);
+ void BroadcastTimeUpdate (const cClientHandle * a_Exclude = NULL);
+ void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ );
+ void BroadcastWeather (eWeather a_Weather, const cClientHandle * a_Exclude = NULL);
+
+ /// If there is a block entity at the specified coords, sends it to the client specified
+ void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client);
+
+ void MarkChunkDirty (int a_ChunkX, int a_ChunkZ);
+ void MarkChunkSaving(int a_ChunkX, int a_ChunkZ);
+ void MarkChunkSaved (int a_ChunkX, int a_ChunkZ);
+
+ /** Sets the chunk data as either loaded from the storage or generated.
+ a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted.
+ a_BiomeMap is optional, if not present, biomes will be calculated by the generator
+ a_HeightMap is optional, if not present, will be calculated.
+ If a_MarkDirty is set, the chunk is set as dirty (used after generating)
+ */
+ void SetChunkData(
+ int a_ChunkX, int a_ChunkZ,
+ const BLOCKTYPE * a_BlockTypes,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
+ const cChunkDef::HeightMap * a_HeightMap,
+ const cChunkDef::BiomeMap * a_BiomeMap,
+ cEntityList & a_Entities,
+ cBlockEntityList & a_BlockEntities,
+ bool a_MarkDirty
+ );
+
+ void ChunkLighted(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
+ );
+
+ bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback);
+
+ /// Gets the chunk's blocks, only the block types
+ bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes);
+
+ bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const;
+ bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const;
+
+ void UnloadUnusedChunks(void); // tolua_export
+
+ void CollectPickupsByPlayer(cPlayer * a_Player);
+
+ void AddPlayer( cPlayer* a_Player );
+ void RemovePlayer( cPlayer* a_Player );
+
+ /// Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true
+ bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+
+ /// Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored
+ bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+
+ /// Finds a player from a partial or complete player name and calls the callback - case-insensitive
+ bool FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+
+ // TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action)
+ cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit);
+
+ void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player
+
+ /// Adds the entity into its appropriate chunk; takes ownership of the entity ptr
+ void AddEntity(cEntity * a_Entity);
+
+ bool HasEntity(int a_UniqueID);
+
+ /// Removes the entity, the entity ptr ownership is assumed taken by the caller
+ void RemoveEntity(cEntity * a_Entity);
+
+ /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true
+ bool ForEachEntity(cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true
+ bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false.
+ bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// 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);
+
+ /// Adds client to a chunk, if not already present; returns true if added, false if present
+ bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
+
+ /// Removes client from the chunk specified
+ void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
+
+ /// Removes the client from all chunks it is present in
+ void RemoveClientFromChunks(cClientHandle * a_Client);
+
+ /// Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid+lighted)
+ void SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
+
+ /// Removes client from ChunkSender's queue of chunks to be sent
+ void RemoveClientFromChunkSender(cClientHandle * a_Client);
+
+ /// Touches the chunk, causing it to be loaded or generated
+ void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before)
+ bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid()
+ void LoadChunks(const cChunkCoordsList & a_Chunks);
+
+ /// Marks the chunk as failed-to-load:
+ void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as UpdateSign()
+ bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp
+
+ /// Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as SetSignLines()
+ bool UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp
+
+ /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay!
+ void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true);
+
+ /// Regenerate the given chunk:
+ void RegenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export
+
+ /// Generates the given chunk, if not already generated
+ void GenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export
+
+ /// Queues a chunk for lighting; a_Callback is called after the chunk is lighted
+ void QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = NULL);
+
+ bool IsChunkLighted(int a_ChunkX, int a_ChunkZ);
+
+ /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully
+ bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback);
+
+ // tolua_begin
+
+ /** Sets the block at the specified coords to the specified value.
+ Full processing, incl. updating neighbors, is performed.
+ */
+ void SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /** Sets the block at the specified coords to the specified value.
+ The replacement doesn't trigger block updates.
+ The replaced blocks aren't checked for block entities (block entity is leaked if it exists at this block)
+ */
+ void FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /** Queues a SetBlock() with the specified parameters after the specified number of ticks.
+ Calls SetBlock(), so performs full processing of the replaced block.
+ */
+ void QueueSetBlock(int a_BlockX, int a_BLockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_TickDelay);
+
+ BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ);
+ NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ);
+ void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_MetaData);
+ NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ);
+ NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ // tolua_end
+
+ bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); // TODO: Exported in ManualBindings.cpp
+ bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); // TODO: Exported in ManualBindings.cpp
+ // TODO: NIBBLETYPE GetBlockActualLight(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ // tolua_begin
+
+ // Vector3i variants:
+ void FastSetBlock(const Vector3i & a_Pos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { FastSetBlock( a_Pos.x, a_Pos.y, a_Pos.z, a_BlockType, a_BlockMeta ); }
+ BLOCKTYPE GetBlock (const Vector3i & a_Pos ) { return GetBlock( a_Pos.x, a_Pos.y, a_Pos.z ); }
+ NIBBLETYPE GetBlockMeta(const Vector3i & a_Pos ) { return GetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z ); }
+ void SetBlockMeta(const Vector3i & a_Pos, NIBBLETYPE a_MetaData ) { SetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z, a_MetaData ); }
+ // tolua_end
+
+ /** Writes the block area into the specified coords.
+ Returns true if all chunks have been processed.
+ Prefer cBlockArea::Write() instead, this is the internal implementation; cBlockArea does error checking, too.
+ a_DataTypes is a bitmask of cBlockArea::baXXX constants ORed together.
+ */
+ bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes);
+
+ // tolua_begin
+
+ /// Spawns item pickups for each item in the list. May compress pickups if too many entities:
+ void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0, bool IsPlayerCreated = false);
+
+ /// Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified:
+ void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated = false);
+
+ /// Spawns a new primed TNT entity at the specified block coords and specified fuse duration. Initial velocity is given based on the relative coefficient provided
+ void SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff = 1);
+
+ // tolua_end
+
+ /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType
+ void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType);
+
+ /// 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.
+ bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure);
+
+ // tolua_begin
+ 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 );
+
+ double GetSpawnX(void) const { return m_SpawnX; }
+ double GetSpawnY(void) const { return m_SpawnY; }
+ double GetSpawnZ(void) const { return m_SpawnZ; }
+
+ /// Wakes up the simulators for the specified block
+ void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Wakes up the simulators for the specified area of blocks
+ void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ);
+
+ // tolua_end
+
+ inline cSimulatorManager * GetSimulatorManager(void) { return m_SimulatorManager; }
+
+ inline cFluidSimulator * GetWaterSimulator(void) { return m_WaterSimulator; }
+ inline cFluidSimulator * GetLavaSimulator (void) { return m_LavaSimulator; }
+
+ /// Calls the callback for each block entity in the specified chunk; returns true if all block entities processed, false if the callback aborted by returning true
+ bool ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true
+ bool ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true
+ bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback);
+
+ /// Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true
+ bool ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback);
+
+ /// Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true
+ bool ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback);
+
+ /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true
+ bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /** Does an explosion with the specified strength at the specified coordinate
+ a_SourceData exact type depends on the a_Source:
+ | esOther | void * |
+ | esPrimedTNT | cTNTEntity * |
+ | esCreeper | cCreeper * |
+ | esBed | cVector3i * |
+ | esEnderCrystal | Vector3i * |
+ | esGhastFireball | cGhastFireball * |
+ | esWitherSkullBlack | TBD |
+ | esWitherSkullBlue | TBD |
+ | esWitherBirth | TBD |
+ | esPlugin | void * |
+ */
+ void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData); // tolua_export
+
+ /// Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found
+ bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found
+ bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found
+ bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found
+ bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found
+ bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found
+ bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
+ bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Exported in ManualBindings.cpp
+
+ /// a_Player is using block entity at [x, y, z], handle that:
+ void UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {m_ChunkMap->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); } // tolua_export
+
+ /// Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback
+ bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback);
+
+ void GrowTreeImage(const sSetBlockVector & a_Blocks);
+
+ // tolua_begin
+
+ /// Grows a tree at the specified coords, either from a sapling there, or based on the biome
+ void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Grows a tree at the specified coords, based on the sapling meta provided
+ void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_SaplingMeta);
+
+ /// Grows a tree at the specified coords, based on the biome in the place
+ void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini
+ bool GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false);
+
+ /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config
+ void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
+
+ /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem)
+ void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
+
+ /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config
+ void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
+
+ /// Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value
+ int GetBiomeAt(int a_BlockX, int a_BlockZ);
+
+ /// Returns the name of the world
+ const AString & GetName(void) const { return m_WorldName; }
+
+ /// Returns the name of the world.ini file used by this world
+ const AString & GetIniFileName(void) const {return m_IniFileName; }
+
+ // tolua_end
+
+ inline static void AbsoluteToRelative( int & a_X, int & a_Y, int & a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ )
+ {
+ // TODO: Use floor() instead of weird if statements
+ // Also fix Y
+ a_ChunkX = a_X/cChunkDef::Width;
+ if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--;
+ a_ChunkY = 0;
+ a_ChunkZ = a_Z/cChunkDef::Width;
+ if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--;
+
+ a_X = a_X - a_ChunkX*cChunkDef::Width;
+ a_Y = a_Y - a_ChunkY*cChunkDef::Height;
+ a_Z = a_Z - a_ChunkZ*cChunkDef::Width;
+ }
+
+ inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ )
+ {
+ // TODO: Use floor() instead of weird if statements
+ // Also fix Y
+ (void)a_Y; // not unused anymore
+ a_ChunkX = a_X/cChunkDef::Width;
+ if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--;
+ a_ChunkY = 0;
+ a_ChunkZ = a_Z/cChunkDef::Width;
+ if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--;
+ }
+
+ /// Saves all chunks immediately. Dangerous interface, may deadlock, use QueueSaveAllChunks() instead
+ void SaveAllChunks(void);
+
+ /// Queues a task to save all chunks onto the tick thread. The prefferred way of saving chunks from external sources
+ void QueueSaveAllChunks(void); // tolua_export
+
+ /// Queues a task onto the tick thread. The task object will be deleted once the task is finished
+ void QueueTask(cTask * a_Task); // Exported in ManualBindings.cpp
+
+ /// Returns the number of chunks loaded
+ int GetNumChunks() const; // tolua_export
+
+ /// Returns the number of chunks loaded and dirty, and in the lighting queue
+ void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue);
+
+ // Various queues length queries (cannot be const, they lock their CS):
+ inline int GetGeneratorQueueLength (void) { return m_Generator.GetQueueLength(); } // tolua_export
+ inline int GetLightingQueueLength (void) { return m_Lighting.GetQueueLength(); } // tolua_export
+ inline int GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export
+ inline int GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export
+
+ void InitializeSpawn(void);
+
+ /// Starts threads that belong to this world
+ void Start(void);
+
+ /// Stops threads that belong to this world (part of deinit)
+ void Stop(void);
+
+ /// Processes the blocks queued for ticking with a delay (m_BlockTickQueue[])
+ void TickQueuedBlocks(void);
+
+ struct BlockTickQueueItem
+ {
+ int X;
+ int Y;
+ int Z;
+ int TicksToWait;
+ };
+
+ /// Queues the block to be ticked after the specified number of game ticks
+ void QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait); // tolua_export
+
+ // tolua_begin
+ /// Casts a thunderbolt at the specified coords
+ void CastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Sets the specified weather; resets weather interval; asks and notifies plugins of the change
+ void SetWeather (eWeather a_NewWeather);
+
+ /// Forces a weather change in the next game tick
+ void ChangeWeather (void);
+
+ /// Returns the current weather. Instead of comparing values directly to the weather constants, use IsWeatherXXX() functions, if possible
+ eWeather GetWeather (void) const { return m_Weather; };
+
+ bool IsWeatherSunny(void) const { return (m_Weather == wSunny); }
+ bool IsWeatherRain (void) const { return (m_Weather == wRain); }
+ bool IsWeatherStorm(void) const { return (m_Weather == wStorm); }
+
+ /// Returns true if the current weather has any precipitation - rain or storm
+ bool IsWeatherWet (void) const { return (m_Weather != wSunny); }
+
+ // tolua_end
+
+ cChunkGenerator & GetGenerator(void) { return m_Generator; }
+ cWorldStorage & GetStorage (void) { return m_Storage; }
+ cChunkMap * GetChunkMap (void) { return m_ChunkMap; }
+
+ /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call
+ void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
+
+ int GetMaxSugarcaneHeight(void) const { return m_MaxSugarcaneHeight; } // tolua_export
+ int GetMaxCactusHeight (void) const { return m_MaxCactusHeight; } // tolua_export
+
+ bool IsBlockDirectlyWatered(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
+
+ /// Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise
+ int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType); // tolua_export
+ int SpawnMobFinalize(cMonster* a_Monster);
+
+ /// Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise
+ int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed = NULL); // 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)); }
+
+ /// Appends all usernames starting with a_Text (case-insensitive) into Results
+ void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results);
+
+ /// Get the current darkness level based on the time
+ NIBBLETYPE GetSkyDarkness() { return m_SkyDarkness; }
+
+private:
+
+ friend class cRoot;
+
+ class cTickThread :
+ public cIsThread
+ {
+ typedef cIsThread super;
+ public:
+ cTickThread(cWorld & a_World);
+
+ protected:
+ cWorld & m_World;
+
+ // cIsThread overrides:
+ virtual void Execute(void) override;
+ } ;
+
+
+ AString m_WorldName;
+ AString m_IniFileName;
+
+ /// Name of the storage schema used to load and save chunks
+ AString m_StorageSchema;
+
+ /// The dimension of the world, used by the client to provide correct lighting scheme
+ eDimension m_Dimension;
+
+ /// This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe)
+ MTRand m_TickRand;
+
+ double m_SpawnX;
+ double m_SpawnY;
+ double m_SpawnZ;
+
+ 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
+ Int64 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<cMonster::eFamily,Int64> 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;
+
+ eGameMode m_GameMode;
+ bool m_bEnabledPVP;
+ bool m_IsDeepSnowEnabled;
+
+ // The cRedstone class simulates redstone and needs access to m_RSList
+ // friend class cRedstone;
+ std::vector<int> m_RSList;
+
+ std::vector<BlockTickQueueItem *> m_BlockTickQueue;
+ std::vector<BlockTickQueueItem *> m_BlockTickQueueCopy; // Second is for safely removing the objects from the queue
+
+ cSimulatorManager * m_SimulatorManager;
+ cSandSimulator * m_SandSimulator;
+ cFluidSimulator * m_WaterSimulator;
+ cFluidSimulator * m_LavaSimulator;
+ cFireSimulator * m_FireSimulator;
+ cRedstoneSimulator * m_RedstoneSimulator;
+
+ cCriticalSection m_CSPlayers;
+ cPlayerList m_Players;
+
+ cWorldStorage m_Storage;
+
+ unsigned int m_MaxPlayers;
+
+ cChunkMap * m_ChunkMap;
+
+ bool m_bAnimals;
+ std::set<cMonster::eType> m_AllowedMobs;
+
+ eWeather m_Weather;
+ int m_WeatherInterval;
+
+ int m_MaxCactusHeight;
+ int m_MaxSugarcaneHeight;
+ bool m_IsCactusBonemealable;
+ bool m_IsCarrotsBonemealable;
+ bool m_IsCropsBonemealable;
+ bool m_IsGrassBonemealable;
+ bool m_IsMelonStemBonemealable;
+ bool m_IsMelonBonemealable;
+ bool m_IsPotatoesBonemealable;
+ bool m_IsPumpkinStemBonemealable;
+ bool m_IsPumpkinBonemealable;
+ bool m_IsSaplingBonemealable;
+ bool m_IsSugarcaneBonemealable;
+
+ cCriticalSection m_CSFastSetBlock;
+ sSetBlockList m_FastSetBlockQueue;
+
+ cChunkGenerator m_Generator;
+
+ cChunkSender m_ChunkSender;
+ cLightingThread m_Lighting;
+ cTickThread m_TickThread;
+
+ /// Guards the m_Tasks
+ cCriticalSection m_CSTasks;
+
+ /// Tasks that have been queued onto the tick thread; guarded by m_CSTasks
+ cTasks m_Tasks;
+
+ /// Guards m_Clients
+ cCriticalSection m_CSClients;
+
+ /// List of clients in this world, these will be ticked by this world
+ cClientHandleList m_Clients;
+
+ /// Clients that are scheduled for removal (ticked in another world), waiting for TickClients() to remove them
+ cClientHandleList m_ClientsToRemove;
+
+ /// Clients that are scheduled for adding, waiting for TickClients to add them
+ cClientHandleList m_ClientsToAdd;
+
+
+ cWorld(const AString & a_WorldName);
+ ~cWorld();
+
+ void Tick(float a_Dt);
+
+ /// Handles the weather in each tick
+ void TickWeather(float a_Dt);
+
+ /// Handles the mob spawning/moving/destroying each tick
+ void TickMobs(float a_Dt);
+
+ /// Executes all tasks queued onto the tick thread
+ void TickQueuedTasks(void);
+
+ /// Ticks all clients that are in this world
+ void TickClients(float a_Dt);
+
+ void UpdateSkyDarkness();
+
+ /// Creates a new fluid simulator, loads its settings from the inifile (a_FluidName section)
+ cFluidSimulator * InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock);
+}; // tolua_export
+
+
+
+
diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp
new file mode 100644
index 000000000..e55011069
--- /dev/null
+++ b/src/WorldStorage/FastNBT.cpp
@@ -0,0 +1,547 @@
+
+// FastNBT.cpp
+
+// Implements the fast NBT parser and writer
+
+#include "Globals.h"
+#include "FastNBT.h"
+
+
+
+
+
+// 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
+ #define NBT_RESERVE_SIZE 200
+#endif // NBT_RESERVE_SIZE
+
+#define RETURN_FALSE_IF_FALSE(X) do { if (!X) return false; } while (0)
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cParsedNBT:
+
+#define NEEDBYTES(N) \
+ if (m_Length - m_Pos < N) \
+ { \
+ return false; \
+ }
+
+
+
+
+
+cParsedNBT::cParsedNBT(const char * a_Data, int a_Length) :
+ m_Data(a_Data),
+ m_Length(a_Length),
+ m_Pos(0)
+{
+ m_IsValid = Parse();
+}
+
+
+
+
+
+bool cParsedNBT::Parse(void)
+{
+ if (m_Length < 3)
+ {
+ // Data too short
+ return false;
+ }
+ if (m_Data[0] != TAG_Compound)
+ {
+ // The top-level tag must be a Compound
+ return false;
+ }
+
+ m_Tags.reserve(NBT_RESERVE_SIZE);
+
+ m_Tags.push_back(cFastNBTTag(TAG_Compound, -1));
+
+ m_Pos = 1;
+
+ RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength));
+ RETURN_FALSE_IF_FALSE(ReadCompound());
+
+ return true;
+}
+
+
+
+
+
+bool cParsedNBT::ReadString(int & a_StringStart, int & a_StringLen)
+{
+ NEEDBYTES(2);
+ a_StringStart = m_Pos + 2;
+ a_StringLen = ntohs(*((short *)(m_Data + m_Pos)));
+ if (a_StringLen < 0)
+ {
+ // Invalid string length
+ return false;
+ }
+ m_Pos += 2 + a_StringLen;
+ return true;
+}
+
+
+
+
+
+bool cParsedNBT::ReadCompound(void)
+{
+ // Reads the latest tag as a compound
+ int ParentIdx = m_Tags.size() - 1;
+ int PrevSibling = -1;
+ while (true)
+ {
+ NEEDBYTES(1);
+ eTagType TagType = (eTagType)(m_Data[m_Pos]);
+ m_Pos++;
+ if (TagType == TAG_End)
+ {
+ break;
+ }
+ m_Tags.push_back(cFastNBTTag(TagType, ParentIdx, PrevSibling));
+ if (PrevSibling >= 0)
+ {
+ m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1;
+ }
+ else
+ {
+ m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1;
+ }
+ PrevSibling = m_Tags.size() - 1;
+ RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength));
+ RETURN_FALSE_IF_FALSE(ReadTag());
+ } // while (true)
+ m_Tags[ParentIdx].m_LastChild = PrevSibling;
+ return true;
+}
+
+
+
+
+
+bool cParsedNBT::ReadList(eTagType a_ChildrenType)
+{
+ // Reads the latest tag as a list of items of type a_ChildrenType
+
+ // Read the count:
+ NEEDBYTES(4);
+ int Count = ntohl(*((int *)(m_Data + m_Pos)));
+ m_Pos += 4;
+ if (Count < 0)
+ {
+ return false;
+ }
+
+ // Read items:
+ int ParentIdx = m_Tags.size() - 1;
+ int PrevSibling = -1;
+ for (int i = 0; i < Count; i++)
+ {
+ m_Tags.push_back(cFastNBTTag(a_ChildrenType, ParentIdx, PrevSibling));
+ if (PrevSibling >= 0)
+ {
+ m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1;
+ }
+ else
+ {
+ m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1;
+ }
+ PrevSibling = m_Tags.size() - 1;
+ RETURN_FALSE_IF_FALSE(ReadTag());
+ } // for (i)
+ m_Tags[ParentIdx].m_LastChild = PrevSibling;
+ return true;
+}
+
+
+
+
+
+#define CASE_SIMPLE_TAG(TAGTYPE, LEN) \
+ case TAG_##TAGTYPE: \
+ { \
+ NEEDBYTES(LEN); \
+ Tag.m_DataStart = m_Pos; \
+ Tag.m_DataLength = LEN; \
+ m_Pos += LEN; \
+ return true; \
+ }
+
+bool cParsedNBT::ReadTag(void)
+{
+ cFastNBTTag & Tag = m_Tags.back();
+ switch (Tag.m_Type)
+ {
+ CASE_SIMPLE_TAG(Byte, 1)
+ CASE_SIMPLE_TAG(Short, 2)
+ CASE_SIMPLE_TAG(Int, 4)
+ CASE_SIMPLE_TAG(Long, 8)
+ CASE_SIMPLE_TAG(Float, 4)
+ CASE_SIMPLE_TAG(Double, 8)
+
+ case TAG_String:
+ {
+ return ReadString(Tag.m_DataStart, Tag.m_DataLength);
+ }
+
+ case TAG_ByteArray:
+ {
+ NEEDBYTES(4);
+ int len = ntohl(*((int *)(m_Data + m_Pos)));
+ m_Pos += 4;
+ if (len < 0)
+ {
+ // Invalid length
+ return false;
+ }
+ NEEDBYTES(len);
+ Tag.m_DataLength = len;
+ Tag.m_DataStart = m_Pos;
+ m_Pos += len;
+ return true;
+ }
+
+ case TAG_List:
+ {
+ NEEDBYTES(1);
+ eTagType ItemType = (eTagType)m_Data[m_Pos];
+ m_Pos++;
+ RETURN_FALSE_IF_FALSE(ReadList(ItemType));
+ return true;
+ }
+
+ case TAG_Compound:
+ {
+ RETURN_FALSE_IF_FALSE(ReadCompound());
+ return true;
+ }
+
+ case TAG_IntArray:
+ {
+ NEEDBYTES(4);
+ int len = ntohl(*((int *)(m_Data + m_Pos)));
+ m_Pos += 4;
+ if (len < 0)
+ {
+ // Invalid length
+ return false;
+ }
+ len *= 4;
+ NEEDBYTES(len);
+ Tag.m_DataLength = len;
+ Tag.m_DataStart = m_Pos;
+ m_Pos += len;
+ return true;
+ }
+
+ default:
+ {
+ ASSERT(!"Unhandled NBT tag type");
+ return false;
+ }
+ } // switch (iType)
+}
+
+#undef CASE_SIMPLE_TAG
+
+
+
+
+
+int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength) const
+{
+ if (a_Tag < 0)
+ {
+ return -1;
+ }
+ if (m_Tags[a_Tag].m_Type != TAG_Compound)
+ {
+ return -1;
+ }
+
+ if (a_NameLength == 0)
+ {
+ a_NameLength = strlen(a_Name);
+ }
+ for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling)
+ {
+ if (
+ (m_Tags[Child].m_NameLength == a_NameLength) &&
+ (memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0)
+ )
+ {
+ return Child;
+ }
+ } // for Child - children of a_Tag
+ return -1;
+}
+
+
+
+
+
+int cParsedNBT::FindTagByPath(int a_Tag, const AString & a_Path) const
+{
+ if (a_Tag < 0)
+ {
+ return -1;
+ }
+ size_t Begin = 0;
+ size_t Length = a_Path.length();
+ int Tag = a_Tag;
+ for (size_t i = 0; i < Length; i++)
+ {
+ if (a_Path[i] != '\\')
+ {
+ continue;
+ }
+ Tag = FindChildByName(Tag, a_Path.c_str() + Begin, i - Begin - 1);
+ if (Tag < 0)
+ {
+ return -1;
+ }
+ Begin = i + 1;
+ } // for i - a_Path[]
+
+ if (Begin < Length)
+ {
+ Tag = FindChildByName(Tag, a_Path.c_str() + Begin, Length - Begin);
+ }
+ return Tag;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFastNBTWriter:
+
+cFastNBTWriter::cFastNBTWriter(const AString & a_RootTagName) :
+ m_CurrentStack(0)
+{
+ m_Stack[0].m_Type = TAG_Compound;
+ m_Result.reserve(100 * 1024);
+ m_Result.push_back(TAG_Compound);
+ WriteString(a_RootTagName.data(), a_RootTagName.size());
+}
+
+
+
+
+
+void cFastNBTWriter::BeginCompound(const AString & a_Name)
+{
+ if (m_CurrentStack >= MAX_STACK)
+ {
+ ASSERT(!"Stack overflow");
+ return;
+ }
+
+ TagCommon(a_Name, TAG_Compound);
+
+ ++m_CurrentStack;
+ m_Stack[m_CurrentStack].m_Type = TAG_Compound;
+}
+
+
+
+
+
+void cFastNBTWriter::EndCompound(void)
+{
+ ASSERT(m_CurrentStack > 0);
+ ASSERT(IsStackTopCompound());
+
+ m_Result.push_back(TAG_End);
+ --m_CurrentStack;
+}
+
+
+
+
+
+void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType)
+{
+ if (m_CurrentStack >= MAX_STACK)
+ {
+ ASSERT(!"Stack overflow");
+ return;
+ }
+
+ TagCommon(a_Name, TAG_List);
+
+ m_Result.push_back((char)a_ChildrenType);
+ m_Result.append(4, (char)0);
+
+ ++m_CurrentStack;
+ m_Stack[m_CurrentStack].m_Type = TAG_List;
+ m_Stack[m_CurrentStack].m_Pos = m_Result.size() - 4;
+ m_Stack[m_CurrentStack].m_Count = 0;
+ m_Stack[m_CurrentStack].m_ItemType = a_ChildrenType;
+}
+
+
+
+
+
+void cFastNBTWriter::EndList(void)
+{
+ ASSERT(m_CurrentStack > 0);
+ ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List);
+
+ // Update the list count:
+ *((int *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos)) = htonl(m_Stack[m_CurrentStack].m_Count);
+
+ --m_CurrentStack;
+}
+
+
+
+
+
+void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value)
+{
+ TagCommon(a_Name, TAG_Byte);
+ m_Result.push_back(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);
+}
+
+
+
+
+
+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);
+}
+
+
+
+
+
+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);
+}
+
+
+
+
+
+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);
+}
+
+
+
+
+
+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);
+}
+
+
+
+
+
+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);
+ m_Result.append(a_Value.c_str(), a_Value.size());
+}
+
+
+
+
+
+void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements)
+{
+ TagCommon(a_Name, TAG_ByteArray);
+ Int32 len = htonl(a_NumElements);
+ m_Result.append((const char *)&len, 4);
+ m_Result.append(a_Value, a_NumElements);
+}
+
+
+
+
+
+void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, size_t a_NumElements)
+{
+ TagCommon(a_Name, TAG_IntArray);
+ Int32 len = htonl(a_NumElements);
+ m_Result.append((const char *)&len, 4);
+#if defined(ANDROID_NDK)
+ // Android has alignment issues - cannot byteswap (htonl) an int that is not 32-bit-aligned, which happens in the regular version
+ for (size_t i = 0; i < a_NumElements; i++)
+ {
+ int Element = htonl(a_Value[i]);
+ m_Result.append((const char *)&Element, 4);
+ }
+#else
+ int * Elements = (int *)(m_Result.data() + m_Result.size());
+ m_Result.append(a_NumElements * 4, (char)0);
+ for (size_t i = 0; i < a_NumElements; i++)
+ {
+ Elements[i] = htonl(a_Value[i]);
+ }
+#endif
+}
+
+
+
+
+
+void cFastNBTWriter::Finish(void)
+{
+ ASSERT(m_CurrentStack == 0);
+ m_Result.push_back(TAG_End);
+}
+
+
+
+
+
+void cFastNBTWriter::WriteString(const char * a_Data, short a_Length)
+{
+ Int16 Len = htons(a_Length);
+ m_Result.append((const char *)&Len, 2);
+ m_Result.append(a_Data, a_Length);
+}
+
+
+
+
diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h
new file mode 100644
index 000000000..7323c29cb
--- /dev/null
+++ b/src/WorldStorage/FastNBT.h
@@ -0,0 +1,293 @@
+
+// FastNBT.h
+
+// Interfaces to the fast NBT parser and writer
+
+/*
+The fast parser parses the data into a vector of cFastNBTTag structures. These structures describe the NBT tree,
+but themselves are allocated in a vector, thus minimizing reallocation.
+The structures have a minimal constructor, setting all member "pointers" to "invalid".
+
+The fast writer doesn't need a NBT tree structure built beforehand, it is commanded to open, append and close tags
+(just like XML); it keeps the internal tag stack and reports errors in usage.
+It directly outputs a string containing the serialized NBT data.
+*/
+
+
+
+
+
+#pragma once
+
+#include "../Endianness.h"
+
+
+
+
+
+enum eTagType
+{
+ TAG_Min = 0, // The minimum value for a tag type
+ TAG_End = 0,
+ TAG_Byte = 1,
+ TAG_Short = 2,
+ TAG_Int = 3,
+ TAG_Long = 4,
+ TAG_Float = 5,
+ TAG_Double = 6,
+ TAG_ByteArray = 7,
+ TAG_String = 8,
+ TAG_List = 9,
+ TAG_Compound = 10,
+ TAG_IntArray = 11,
+ TAG_Max = 11, // The maximum value for a tag type
+} ;
+
+
+
+
+
+/** This structure is used for all NBT tags.
+It contains indices to the parent array of tags, building the NBT tree this way.
+Also contains indices into the data stream being parsed, used for values;
+NO dynamically allocated memory is used!
+Structure (all with the tree structure it describes) supports moving in memory (std::vector reallocation)
+*/
+struct cFastNBTTag
+{
+public:
+
+ eTagType m_Type;
+
+ // The following members are indices into the data stream. m_DataLength == 0 if no data available
+ // They must not be pointers, because the datastream may be copied into another AString object in the meantime.
+ int m_NameStart;
+ int m_NameLength;
+ int m_DataStart;
+ int m_DataLength;
+
+ // The following members are indices into the array returned; -1 if not valid
+ // They must not be pointers, because pointers would not survive std::vector reallocation
+ int m_Parent;
+ int m_PrevSibling;
+ int m_NextSibling;
+ int m_FirstChild;
+ int m_LastChild;
+
+ cFastNBTTag(eTagType a_Type, int a_Parent) :
+ m_Type(a_Type),
+ m_NameLength(0),
+ m_DataLength(0),
+ m_Parent(a_Parent),
+ m_PrevSibling(-1),
+ m_NextSibling(-1),
+ m_FirstChild(-1),
+ m_LastChild(-1)
+ {
+ }
+
+ cFastNBTTag(eTagType a_Type, int a_Parent, int a_PrevSibling) :
+ m_Type(a_Type),
+ m_NameLength(0),
+ m_DataLength(0),
+ m_Parent(a_Parent),
+ m_PrevSibling(a_PrevSibling),
+ m_NextSibling(-1),
+ m_FirstChild(-1),
+ m_LastChild(-1)
+ {
+ }
+} ;
+
+
+
+
+
+/** Parses and contains the parsed data
+Also implements data accessor functions for tree traversal and value getters
+The data pointer passed in the constructor is assumed to be valid throughout the object's life. Care must be taken not to initialize from a temporary.
+*/
+class cParsedNBT
+{
+public:
+ cParsedNBT(const char * a_Data, int a_Length);
+
+ bool IsValid(void) const {return m_IsValid; }
+
+ int GetRoot(void) const {return 0; }
+ int GetFirstChild (int a_Tag) const { return m_Tags[a_Tag].m_FirstChild; }
+ int GetLastChild (int a_Tag) const { return m_Tags[a_Tag].m_LastChild; }
+ int GetNextSibling(int a_Tag) const { return m_Tags[a_Tag].m_NextSibling; }
+ int GetPrevSibling(int a_Tag) const { return m_Tags[a_Tag].m_PrevSibling; }
+ int GetDataLength (int a_Tag) const { return m_Tags[a_Tag].m_DataLength; }
+
+ const char * GetData(int a_Tag) const
+ {
+ ASSERT(m_Tags[a_Tag].m_Type != TAG_List);
+ ASSERT(m_Tags[a_Tag].m_Type != TAG_Compound);
+ return m_Data + m_Tags[a_Tag].m_DataStart;
+ }
+
+ int FindChildByName(int a_Tag, const AString & a_Name) const
+ {
+ return FindChildByName(a_Tag, a_Name.c_str(), a_Name.length());
+ }
+
+ int FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength = 0) const;
+ int FindTagByPath (int a_Tag, const AString & a_Path) const;
+
+ eTagType GetType(int a_Tag) const { return m_Tags[a_Tag].m_Type; }
+
+ /// Returns the children type for a list tag; undefined on other tags. If list empty, returns TAG_End
+ eTagType GetChildrenType(int a_Tag) const
+ {
+ ASSERT(m_Tags[a_Tag].m_Type == TAG_List);
+ return (m_Tags[a_Tag].m_FirstChild < 0) ? TAG_End : m_Tags[m_Tags[a_Tag].m_FirstChild].m_Type;
+ }
+
+ inline unsigned char GetByte(int a_Tag) const
+ {
+ ASSERT(m_Tags[a_Tag].m_Type == TAG_Byte);
+ return (unsigned char)(m_Data[m_Tags[a_Tag].m_DataStart]);
+ }
+
+ inline Int16 GetShort(int a_Tag) const
+ {
+ ASSERT(m_Tags[a_Tag].m_Type == TAG_Short);
+ return ntohs(*((Int16 *)(m_Data + m_Tags[a_Tag].m_DataStart)));
+ }
+
+ inline Int32 GetInt(int a_Tag) const
+ {
+ ASSERT(m_Tags[a_Tag].m_Type == TAG_Int);
+ return ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart)));
+ }
+
+ inline Int64 GetLong(int a_Tag) const
+ {
+ ASSERT(m_Tags[a_Tag].m_Type == TAG_Long);
+ return NetworkToHostLong8(m_Data + m_Tags[a_Tag].m_DataStart);
+ }
+
+ inline float GetFloat(int a_Tag) const
+ {
+ ASSERT(m_Tags[a_Tag].m_Type == TAG_Float);
+ Int32 tmp = ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart)));
+ return *((float *)&tmp);
+ }
+
+ inline double GetDouble(int a_Tag) const
+ {
+ ASSERT(m_Tags[a_Tag].m_Type == TAG_Double);
+ return NetworkToHostDouble8(m_Data + m_Tags[a_Tag].m_DataStart);
+ }
+
+ inline AString GetString(int a_Tag) const
+ {
+ ASSERT(m_Tags[a_Tag].m_Type == TAG_String);
+ AString res;
+ res.assign(m_Data + m_Tags[a_Tag].m_DataStart, m_Tags[a_Tag].m_DataLength);
+ return res;
+ }
+
+ inline AString GetName(int a_Tag) const
+ {
+ AString res;
+ res.assign(m_Data + m_Tags[a_Tag].m_NameStart, m_Tags[a_Tag].m_NameLength);
+ return res;
+ }
+
+protected:
+ const char * m_Data;
+ int m_Length;
+ std::vector<cFastNBTTag> m_Tags;
+ bool m_IsValid; // True if parsing succeeded
+
+ // Used while parsing:
+ int m_Pos;
+
+ bool Parse(void);
+ bool ReadString(int & a_StringStart, int & a_StringLen); // Reads a simple string (2 bytes length + data), sets the string descriptors
+ bool ReadCompound(void); // Reads the latest tag as a compound
+ bool ReadList(eTagType a_ChildrenType); // Reads the latest tag as a list of items of type a_ChildrenType
+ bool ReadTag(void); // Reads the latest tag, depending on its m_Type setting
+} ;
+
+
+
+
+
+class cFastNBTWriter
+{
+public:
+ cFastNBTWriter(const AString & a_RootTagName = "");
+
+ void BeginCompound(const AString & a_Name);
+ void EndCompound(void);
+
+ void BeginList(const AString & a_Name, eTagType a_ChildrenType);
+ void EndList(void);
+
+ void AddByte (const AString & a_Name, unsigned char a_Value);
+ void AddShort (const AString & a_Name, Int16 a_Value);
+ void AddInt (const AString & a_Name, Int32 a_Value);
+ void AddLong (const AString & a_Name, Int64 a_Value);
+ void AddFloat (const AString & a_Name, float a_Value);
+ void AddDouble (const AString & a_Name, double a_Value);
+ void AddString (const AString & a_Name, const AString & a_Value);
+ void AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements);
+ void AddIntArray (const AString & a_Name, const int * a_Value, size_t a_NumElements);
+
+ void AddByteArray(const AString & a_Name, const AString & a_Value)
+ {
+ AddByteArray(a_Name, a_Value.data(), a_Value.size());
+ }
+
+ const AString & GetResult(void) const {return m_Result; }
+
+ void Finish(void);
+
+protected:
+
+ struct sParent
+ {
+ int m_Type; // TAG_Compound or TAG_List
+ int m_Pos; // for TAG_List, the position of the list count
+ int m_Count; // for TAG_List, the element count
+ eTagType m_ItemType; // for TAG_List, the element type
+ } ;
+
+ static const int MAX_STACK = 50; // Highliy doubtful that an NBT would be constructed this many levels deep
+
+ // These two fields emulate a stack. A raw array is used due to speed issues - no reallocations are allowed.
+ sParent m_Stack[MAX_STACK];
+ int m_CurrentStack;
+
+ AString m_Result;
+
+ bool IsStackTopCompound(void) const { return (m_Stack[m_CurrentStack].m_Type == TAG_Compound); }
+
+ void WriteString(const char * a_Data, short a_Length);
+
+ inline void TagCommon(const AString & a_Name, eTagType a_Type)
+ {
+ // If we're directly inside a list, check that the list is of the correct type:
+ ASSERT((m_Stack[m_CurrentStack].m_Type != TAG_List) || (m_Stack[m_CurrentStack].m_ItemType == a_Type));
+
+ if (IsStackTopCompound())
+ {
+ // Compound: add the type and name:
+ m_Result.push_back((char)a_Type);
+ WriteString(a_Name.c_str(), (short)a_Name.length());
+ }
+ else
+ {
+ // List: add to the counter
+ m_Stack[m_CurrentStack].m_Count++;
+ }
+ }
+} ;
+
+
+
+
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
new file mode 100644
index 000000000..c9013b1b3
--- /dev/null
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -0,0 +1,533 @@
+
+// NBTChunkSerializer.cpp
+
+
+#include "Globals.h"
+#include "NBTChunkSerializer.h"
+#include "../BlockID.h"
+#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/DispenserEntity.h"
+#include "../BlockEntities/DropperEntity.h"
+#include "../BlockEntities/FurnaceEntity.h"
+#include "../BlockEntities/HopperEntity.h"
+#include "../BlockEntities/JukeboxEntity.h"
+#include "../BlockEntities/NoteEntity.h"
+#include "../BlockEntities/SignEntity.h"
+#include "../ItemGrid.h"
+#include "../StringCompression.h"
+#include "../Entities/Entity.h"
+#include "FastNBT.h"
+#include "../Entities/FallingBlock.h"
+#include "../Entities/Boat.h"
+#include "../Entities/Minecart.h"
+#include "../Mobs/Monster.h"
+#include "../Entities/Pickup.h"
+#include "../Entities/ProjectileEntity.h"
+
+
+
+
+
+cNBTChunkSerializer::cNBTChunkSerializer(cFastNBTWriter & a_Writer) :
+ m_BiomesAreValid(false),
+ m_Writer(a_Writer),
+ m_IsTagOpen(false),
+ m_HasHadEntity(false),
+ m_HasHadBlockEntity(false),
+ m_IsLightValid(false)
+{
+}
+
+
+
+
+
+void cNBTChunkSerializer::Finish(void)
+{
+ if (m_IsTagOpen)
+ {
+ m_Writer.EndList();
+ }
+
+ // If light not valid, reset it to all zeroes:
+ if (!m_IsLightValid)
+ {
+ memset(m_BlockLight, 0, sizeof(m_BlockLight));
+ memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight));
+ }
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName)
+{
+ m_Writer.BeginCompound(a_CompoundName);
+ m_Writer.AddShort("id", (short)(a_Item.m_ItemType));
+ m_Writer.AddShort("Damage", a_Item.m_ItemDamage);
+ m_Writer.AddByte ("Count", a_Item.m_ItemCount);
+ if (a_Slot >= 0)
+ {
+ m_Writer.AddByte ("Slot", (unsigned char)a_Slot);
+ }
+
+ // Write the enchantments:
+ if (!a_Item.m_Enchantments.IsEmpty())
+ {
+ const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
+ m_Writer.BeginCompound("tag");
+ a_Item.m_Enchantments.WriteToNBTCompound(m_Writer, TagName);
+ m_Writer.EndCompound();
+ }
+
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum)
+{
+ int NumSlots = a_Grid.GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+ const cItem & Item = a_Grid.GetSlot(i);
+ if (Item.IsEmpty())
+ {
+ continue;
+ }
+ AddItem(Item, i + a_BeginSlotNum);
+ } // for i - chest slots[]
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID)
+{
+ m_Writer.AddInt ("x", a_Entity->GetPosX());
+ m_Writer.AddInt ("y", a_Entity->GetPosY());
+ m_Writer.AddInt ("z", a_Entity->GetPosZ());
+ m_Writer.AddString("id", a_EntityTypeID);
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Chest");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddDispenserEntity(cDispenserEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Trap");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddDropperEntity(cDropperEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Dropper");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Furnace, "Furnace");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Furnace->GetContents());
+ m_Writer.EndList();
+ m_Writer.AddShort("BurnTime", a_Furnace->GetFuelBurnTimeLeft());
+ m_Writer.AddShort("CookTime", a_Furnace->GetTimeCooked());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Hopper");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Jukebox, "RecordPlayer");
+ m_Writer.AddInt("Record", a_Jukebox->GetRecord());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Note, "Music");
+ m_Writer.AddByte("note", a_Note->GetPitch());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Sign, "Sign");
+ m_Writer.AddString("Text1", a_Sign->GetLine(0));
+ m_Writer.AddString("Text2", a_Sign->GetLine(1));
+ m_Writer.AddString("Text3", a_Sign->GetLine(2));
+ m_Writer.AddString("Text4", a_Sign->GetLine(3));
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName)
+{
+ m_Writer.AddString("id", a_ClassName);
+ m_Writer.BeginList("Pos", TAG_Double);
+ m_Writer.AddDouble("", a_Entity->GetPosX());
+ m_Writer.AddDouble("", a_Entity->GetPosY());
+ m_Writer.AddDouble("", a_Entity->GetPosZ());
+ m_Writer.EndList();
+ m_Writer.BeginList("Motion", TAG_Double);
+ m_Writer.AddDouble("", a_Entity->GetSpeedX());
+ m_Writer.AddDouble("", a_Entity->GetSpeedY());
+ m_Writer.AddDouble("", a_Entity->GetSpeedZ());
+ m_Writer.EndList();
+ m_Writer.BeginList("Rotation", TAG_Double);
+ m_Writer.AddDouble("", a_Entity->GetRotation());
+ m_Writer.AddDouble("", a_Entity->GetPitch());
+ m_Writer.EndList();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddBoatEntity(cBoat * a_Boat)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_Boat, "Boat");
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_FallingBlock, "FallingSand");
+ m_Writer.AddInt("TileID", a_FallingBlock->GetBlockType());
+ m_Writer.AddByte("Data", a_FallingBlock->GetBlockMeta());
+ m_Writer.AddByte("Time", 1); // Unused in MCServer, Vanilla said to need nonzero
+ m_Writer.AddByte("DropItem", 1);
+ m_Writer.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL);
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart)
+{
+ const char * EntityClass = NULL;
+ switch (a_Minecart->GetPayload())
+ {
+ case cMinecart::mpNone: EntityClass = "MinecartRideable"; break;
+ case cMinecart::mpChest: EntityClass = "MinecartChest"; break;
+ case cMinecart::mpFurnace: EntityClass = "MinecartFurnace"; break;
+ case cMinecart::mpTNT: EntityClass = "MinecartTNT"; break;
+ case cMinecart::mpHopper: EntityClass = "MinecartHopper"; break;
+ default:
+ {
+ ASSERT(!"Unhandled minecart payload type");
+ return;
+ }
+ } // switch (payload)
+
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_Minecart, EntityClass);
+ switch (a_Minecart->GetPayload())
+ {
+ case cMinecart::mpChest:
+ {
+ // Add chest contents into the Items tag:
+ AddMinecartChestContents((cMinecartWithChest *)a_Minecart);
+ break;
+ }
+
+ case cMinecart::mpFurnace:
+ {
+ // TODO: Add "Push" and "Fuel" tags
+ break;
+ }
+ } // switch (Payload)
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
+{
+ // TODO
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_Pickup, "Item");
+ AddItem(a_Pickup->GetItem(), -1, "Item");
+ m_Writer.AddShort("Health", a_Pickup->GetHealth());
+ m_Writer.AddShort("Age", a_Pickup->GetAge());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName());
+ Vector3d Pos = a_Projectile->GetPosition();
+ m_Writer.AddShort("xTile", (Int16)floor(Pos.x));
+ m_Writer.AddShort("yTile", (Int16)floor(Pos.y));
+ m_Writer.AddShort("zTile", (Int16)floor(Pos.z));
+ m_Writer.AddShort("inTile", 0); // TODO: Query the block type
+ m_Writer.AddShort("shake", 0); // TODO: Any shake?
+ m_Writer.AddByte ("inGround", a_Projectile->IsInGround() ? 1 : 0);
+
+ switch (a_Projectile->GetProjectileKind())
+ {
+ case cProjectileEntity::pkArrow:
+ {
+ m_Writer.AddByte("inData", 0); // TODO: Query the block meta (is it needed?)
+ m_Writer.AddByte("pickup", ((cArrowEntity *)a_Projectile)->GetPickupState());
+ m_Writer.AddDouble("damage", ((cArrowEntity *)a_Projectile)->GetDamageCoeff());
+ break;
+ }
+ case cProjectileEntity::pkGhastFireball:
+ {
+ m_Writer.AddInt("ExplosionPower", 1);
+ // fall-through:
+ }
+ case cProjectileEntity::pkFireCharge:
+ case cProjectileEntity::pkWitherSkull:
+ case cProjectileEntity::pkEnderPearl:
+ {
+ m_Writer.BeginList("Motion", TAG_Double);
+ m_Writer.AddDouble("", a_Projectile->GetSpeedX());
+ m_Writer.AddDouble("", a_Projectile->GetSpeedY());
+ m_Writer.AddDouble("", a_Projectile->GetSpeedZ());
+ m_Writer.EndList();
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unsaved projectile entity!");
+ }
+ } // switch (ProjectileKind)
+ cEntity * Creator = a_Projectile->GetCreator();
+ if (Creator != NULL)
+ {
+ if (Creator->GetEntityType() == cEntity::etPlayer)
+ {
+ m_Writer.AddString("ownerName", ((cPlayer *)Creator)->GetName());
+ }
+ }
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart)
+{
+ m_Writer.BeginList("Items", TAG_Compound);
+ for (int i = 0; i < cMinecartWithChest::NumSlots; i++)
+ {
+ const cItem & Item = a_Minecart->GetSlot(i);
+ if (Item.IsEmpty())
+ {
+ continue;
+ }
+ AddItem(Item, i);
+ }
+ m_Writer.EndList();
+}
+
+
+
+
+
+bool cNBTChunkSerializer::LightIsValid(bool a_IsLightValid)
+{
+ m_IsLightValid = a_IsLightValid;
+ return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother
+}
+
+
+
+
+
+void cNBTChunkSerializer::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
+{
+ memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes));
+ for (int i = 0; i < ARRAYCOUNT(m_Biomes); i++)
+ {
+ if ((*a_BiomeMap)[i] < 255)
+ {
+ // Normal MC biome, copy as-is:
+ m_VanillaBiomes[i] = (unsigned char)((*a_BiomeMap)[i]);
+ }
+ else
+ {
+ // TODO: MCS-specific biome, need to map to some basic MC biome:
+ ASSERT(!"Unimplemented MCS-specific biome");
+ return;
+ }
+ } // for i - m_BiomeMap[]
+ m_BiomesAreValid = true;
+}
+
+
+
+
+
+void cNBTChunkSerializer::Entity(cEntity * a_Entity)
+{
+ // Add entity into NBT:
+ if (m_IsTagOpen)
+ {
+ if (!m_HasHadEntity)
+ {
+ m_Writer.EndList();
+ m_Writer.BeginList("Entities", TAG_Compound);
+ }
+ }
+ else
+ {
+ m_Writer.BeginList("Entities", TAG_Compound);
+ }
+ m_IsTagOpen = true;
+ m_HasHadEntity = true;
+
+ switch (a_Entity->GetEntityType())
+ {
+ case cEntity::etBoat: AddBoatEntity ((cBoat *) a_Entity); break;
+ case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *) a_Entity); break;
+ case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break;
+ case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break;
+ case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break;
+ case cEntity::etProjectile: AddProjectileEntity ((cProjectileEntity *)a_Entity); break;
+ case cEntity::etPlayer: return; // Players aren't saved into the world
+ default:
+ {
+ ASSERT(!"Unhandled entity type is being saved");
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
+{
+ if (m_IsTagOpen)
+ {
+ if (!m_HasHadBlockEntity)
+ {
+ m_Writer.EndList();
+ m_Writer.BeginList("TileEntities", TAG_Compound);
+ }
+ }
+ else
+ {
+ m_Writer.BeginList("TileEntities", TAG_Compound);
+ }
+ m_IsTagOpen = true;
+
+ // Add tile-entity into NBT:
+ switch (a_Entity->GetBlockType())
+ {
+ case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
+ case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
+ case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break;
+ case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
+ case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
+ case E_BLOCK_SIGN_POST:
+ case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
+ case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
+ case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
+ default:
+ {
+ ASSERT(!"Unhandled block entity saved into Anvil");
+ }
+ }
+ m_HasHadBlockEntity = true;
+}
+
+
+
+
diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h
new file mode 100644
index 000000000..9d4ac208c
--- /dev/null
+++ b/src/WorldStorage/NBTChunkSerializer.h
@@ -0,0 +1,116 @@
+
+// NBTChunkSerializer.h
+
+// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil
+
+
+
+
+
+#pragma once
+
+#include "../ChunkDef.h"
+
+
+
+
+
+// fwd:
+class cFastNBTWriter;
+class cEntity;
+class cBlockEntity;
+class cBoat;
+class cChestEntity;
+class cDispenserEntity;
+class cDropperEntity;
+class cFurnaceEntity;
+class cHopperEntity;
+class cJukeboxEntity;
+class cNoteEntity;
+class cSignEntity;
+class cFallingBlock;
+class cMinecart;
+class cMinecartWithChest;
+class cMinecartWithFurnace;
+class cMinecartWithTNT;
+class cMinecartWithHopper;
+class cMonster;
+class cPickup;
+class cItemGrid;
+class cProjectileEntity;
+
+
+
+
+
+class cNBTChunkSerializer :
+ public cChunkDataSeparateCollector
+{
+public:
+ cChunkDef::BiomeMap m_Biomes;
+ unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width];
+ bool m_BiomesAreValid;
+
+
+ cNBTChunkSerializer(cFastNBTWriter & a_Writer);
+
+ /// Close NBT tags that we've opened
+ void Finish(void);
+
+ bool IsLightValid(void) const {return m_IsLightValid; }
+
+protected:
+
+ /* From cChunkDataSeparateCollector we inherit:
+ - m_BlockTypes[]
+ - m_BlockMetas[]
+ - m_BlockLight[]
+ - m_BlockSkyLight[]
+ */
+
+ cFastNBTWriter & m_Writer;
+
+ bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed.
+ bool m_HasHadEntity; // True if any Entity has already been received and processed
+ bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed
+ bool m_IsLightValid; // True if the chunk lighting is valid
+
+
+ /// Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested.
+ void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = "");
+
+ /// Writes an item grid into the writer; begins the stored slot numbers with a_BeginSlotNum. Note that it doesn't begin nor end the list tag
+ void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0);
+
+ // Block entities:
+ void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID);
+ void AddChestEntity (cChestEntity * a_Entity);
+ void AddDispenserEntity(cDispenserEntity * a_Entity);
+ void AddDropperEntity (cDropperEntity * a_Entity);
+ void AddFurnaceEntity (cFurnaceEntity * a_Furnace);
+ void AddHopperEntity (cHopperEntity * a_Entity);
+ void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
+ void AddNoteEntity (cNoteEntity * a_Note);
+ void AddSignEntity (cSignEntity * a_Sign);
+
+ // Entities:
+ void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName);
+ void AddBoatEntity (cBoat * a_Boat);
+ void AddFallingBlockEntity(cFallingBlock * a_FallingBlock);
+ void AddMinecartEntity (cMinecart * a_Minecart);
+ void AddMonsterEntity (cMonster * a_Monster);
+ void AddPickupEntity (cPickup * a_Pickup);
+ void AddProjectileEntity (cProjectileEntity * a_Projectile);
+
+ void AddMinecartChestContents(cMinecartWithChest * a_Minecart);
+
+ // cChunkDataSeparateCollector overrides:
+ virtual bool LightIsValid(bool a_IsLightValid) override;
+ virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override;
+ virtual void Entity(cEntity * a_Entity) override;
+ virtual void BlockEntity(cBlockEntity * a_Entity) override;
+} ; // class cNBTChunkSerializer
+
+
+
+
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
new file mode 100644
index 000000000..48163e222
--- /dev/null
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -0,0 +1,1555 @@
+
+// WSSAnvil.cpp
+
+// Implements the cWSSAnvil class representing the Anvil world storage scheme
+
+#include "Globals.h"
+#include "WSSAnvil.h"
+#include "NBTChunkSerializer.h"
+#include "../World.h"
+#include "lib/zlib/zlib.h"
+#include "../BlockID.h"
+#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/DispenserEntity.h"
+#include "../BlockEntities/DropperEntity.h"
+#include "../BlockEntities/FurnaceEntity.h"
+#include "../BlockEntities/HopperEntity.h"
+#include "../BlockEntities/JukeboxEntity.h"
+#include "../BlockEntities/NoteEntity.h"
+#include "../BlockEntities/SignEntity.h"
+#include "../Item.h"
+#include "../ItemGrid.h"
+#include "../StringCompression.h"
+#include "FastNBT.h"
+#include "../Mobs/Monster.h"
+#include "../Entities/Boat.h"
+#include "../Entities/FallingBlock.h"
+#include "../Entities/Minecart.h"
+#include "../Entities/Pickup.h"
+#include "../Entities/ProjectileEntity.h"
+
+
+
+
+
+/** If defined, the BlockSkyLight values will be copied over to BlockLight upon chunk saving,
+thus making skylight visible in Minutor's Lighting mode
+*/
+// #define DEBUG_SKYLIGHT
+
+/** Maximum number of MCA files that are cached in memory.
+Since only the header is actually in the memory, this number can be high, but still, each file means an OS FS handle.
+*/
+#define MAX_MCA_FILES 32
+
+/// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities
+#define CHUNK_INFLATE_MAX 256 KiB
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWSSAnvil:
+
+cWSSAnvil::cWSSAnvil(cWorld * a_World) :
+ super(a_World)
+{
+ // Create a level.dat file for mapping tools, if it doesn't already exist:
+ AString fnam;
+ Printf(fnam, "%s/level.dat", a_World->GetName().c_str());
+ if (!cFile::Exists(fnam))
+ {
+ cFastNBTWriter Writer;
+ Writer.BeginCompound("");
+ Writer.AddInt("SpawnX", (int)(a_World->GetSpawnX()));
+ Writer.AddInt("SpawnY", (int)(a_World->GetSpawnY()));
+ Writer.AddInt("SpawnZ", (int)(a_World->GetSpawnZ()));
+ Writer.EndCompound();
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ gzFile gz = gzopen((FILE_IO_PREFIX + fnam).c_str(), "wb");
+ if (gz != NULL)
+ {
+ gzwrite(gz, Writer.GetResult().data(), Writer.GetResult().size());
+ }
+ gzclose(gz);
+ }
+}
+
+
+
+
+
+cWSSAnvil::~cWSSAnvil()
+{
+ cCSLock Lock(m_CS);
+ for (cMCAFiles::iterator itr = m_Files.begin(); itr != m_Files.end(); ++itr)
+ {
+ delete *itr;
+ } // for itr - m_Files[]
+}
+
+
+
+
+
+bool cWSSAnvil::LoadChunk(const cChunkCoords & a_Chunk)
+{
+ AString ChunkData;
+ if (!GetChunkData(a_Chunk, ChunkData))
+ {
+ // The reason for failure is already printed in GetChunkData()
+ return false;
+ }
+
+ return LoadChunkFromData(a_Chunk, ChunkData);
+}
+
+
+
+
+
+bool cWSSAnvil::SaveChunk(const cChunkCoords & a_Chunk)
+{
+ AString ChunkData;
+ if (!SaveChunkToData(a_Chunk, ChunkData))
+ {
+ LOGWARNING("Cannot serialize chunk [%d, %d] into data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
+ return false;
+ }
+ if (!SetChunkData(a_Chunk, ChunkData))
+ {
+ LOGWARNING("Cannot store chunk [%d, %d] data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
+ return false;
+ }
+
+ // Everything successful
+ return true;
+}
+
+
+
+
+
+bool cWSSAnvil::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data)
+{
+ cCSLock Lock(m_CS);
+ cMCAFile * File = LoadMCAFile(a_Chunk);
+ if (File == NULL)
+ {
+ return false;
+ }
+ return File->GetChunkData(a_Chunk, a_Data);
+}
+
+
+
+
+
+bool cWSSAnvil::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data)
+{
+ cCSLock Lock(m_CS);
+ cMCAFile * File = LoadMCAFile(a_Chunk);
+ if (File == NULL)
+ {
+ return false;
+ }
+ return File->SetChunkData(a_Chunk, a_Data);
+}
+
+
+
+
+
+cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk)
+{
+ // ASSUME m_CS is locked
+ ASSERT(m_CS.IsLocked());
+
+ const int RegionX = FAST_FLOOR_DIV(a_Chunk.m_ChunkX, 32);
+ const int RegionZ = FAST_FLOOR_DIV(a_Chunk.m_ChunkZ, 32);
+ ASSERT(a_Chunk.m_ChunkX - RegionX * 32 >= 0);
+ ASSERT(a_Chunk.m_ChunkZ - RegionZ * 32 >= 0);
+ ASSERT(a_Chunk.m_ChunkX - RegionX * 32 < 32);
+ ASSERT(a_Chunk.m_ChunkZ - RegionZ * 32 < 32);
+
+ // Is it already cached?
+ for (cMCAFiles::iterator itr = m_Files.begin(); itr != m_Files.end(); ++itr)
+ {
+ if (((*itr) != NULL) && ((*itr)->GetRegionX() == RegionX) && ((*itr)->GetRegionZ() == RegionZ))
+ {
+ // Move the file to front and return it:
+ cMCAFile * f = *itr;
+ if (itr != m_Files.begin())
+ {
+ m_Files.erase(itr);
+ m_Files.push_front(f);
+ }
+ return f;
+ }
+ }
+
+ // Load it anew:
+ AString FileName;
+ Printf(FileName, "%s/region", m_World->GetName().c_str());
+ cFile::CreateFolder(FILE_IO_PREFIX + FileName);
+ AppendPrintf(FileName, "/r.%d.%d.mca", RegionX, RegionZ);
+ cMCAFile * f = new cMCAFile(FileName, RegionX, RegionZ);
+ if (f == NULL)
+ {
+ return NULL;
+ }
+ m_Files.push_front(f);
+
+ // If there are too many MCA files cached, delete the last one used:
+ if (m_Files.size() > MAX_MCA_FILES)
+ {
+ delete m_Files.back();
+ m_Files.pop_back();
+ }
+ return f;
+}
+
+
+
+
+
+bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data)
+{
+ // Decompress the data:
+ char Uncompressed[CHUNK_INFLATE_MAX];
+ z_stream strm;
+ strm.zalloc = (alloc_func)NULL;
+ strm.zfree = (free_func)NULL;
+ strm.opaque = NULL;
+ inflateInit(&strm);
+ strm.next_out = (Bytef *)Uncompressed;
+ strm.avail_out = sizeof(Uncompressed);
+ strm.next_in = (Bytef *)a_Data.data();
+ strm.avail_in = a_Data.size();
+ int res = inflate(&strm, Z_FINISH);
+ inflateEnd(&strm);
+ if (res != Z_STREAM_END)
+ {
+ return false;
+ }
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Uncompressed, strm.total_out);
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ // Load the data from NBT:
+ return LoadChunkFromNBT(a_Chunk, NBT);
+}
+
+
+
+
+
+bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data)
+{
+ cFastNBTWriter Writer;
+ if (!SaveChunkToNBT(a_Chunk, Writer))
+ {
+ LOGWARNING("Cannot save chunk [%d, %d] to NBT", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
+ return false;
+ }
+ Writer.Finish();
+
+ CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data);
+ return true;
+}
+
+
+
+
+
+bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT)
+{
+ // The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data)
+ cChunkDef::BlockTypes BlockTypes;
+ cChunkDef::BlockNibbles MetaData;
+ cChunkDef::BlockNibbles BlockLight;
+ cChunkDef::BlockNibbles SkyLight;
+
+ memset(BlockTypes, E_BLOCK_AIR, sizeof(BlockTypes));
+ memset(MetaData, 0, sizeof(MetaData));
+ memset(SkyLight, 0xff, sizeof(SkyLight)); // By default, data not present in the NBT means air, which means full skylight
+ memset(BlockLight, 0x00, sizeof(BlockLight));
+
+ // Load the blockdata, blocklight and skylight:
+ int Level = a_NBT.FindChildByName(0, "Level");
+ if (Level < 0)
+ {
+ return false;
+ }
+ int Sections = a_NBT.FindChildByName(Level, "Sections");
+ if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List) || (a_NBT.GetChildrenType(Sections) != TAG_Compound))
+ {
+ return false;
+ }
+ for (int Child = a_NBT.GetFirstChild(Sections); Child >= 0; Child = a_NBT.GetNextSibling(Child))
+ {
+ int y = 0;
+ int SectionY = a_NBT.FindChildByName(Child, "Y");
+ if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte))
+ {
+ continue;
+ }
+ y = a_NBT.GetByte(SectionY);
+ if ((y < 0) || (y > 15))
+ {
+ continue;
+ }
+ CopyNBTData(a_NBT, Child, "Blocks", (char *)&(BlockTypes[y * 4096]), 4096);
+ CopyNBTData(a_NBT, Child, "Data", (char *)&(MetaData[y * 2048]), 2048);
+ CopyNBTData(a_NBT, Child, "SkyLight", (char *)&(SkyLight[y * 2048]), 2048);
+ CopyNBTData(a_NBT, Child, "BlockLight", (char *)&(BlockLight[y * 2048]), 2048);
+ } // for itr - LevelSections[]
+
+ // Load the biomes from NBT, if present and valid. First try MCS-style, then Vanilla-style:
+ cChunkDef::BiomeMap BiomeMap;
+ cChunkDef::BiomeMap * Biomes = LoadBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "MCSBiomes"));
+ if (Biomes == NULL)
+ {
+ // MCS-style biomes not available, load vanilla-style:
+ Biomes = LoadVanillaBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "Biomes"));
+ }
+
+ // Load the entities from NBT:
+ cEntityList Entities;
+ cBlockEntityList BlockEntities;
+ LoadEntitiesFromNBT (Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities"));
+ LoadBlockEntitiesFromNBT(BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities"), BlockTypes, MetaData);
+
+ bool IsLightValid = (a_NBT.FindChildByName(Level, "MCSIsLightValid") > 0);
+
+ /*
+ // Uncomment this block for really cool stuff :)
+ // DEBUG magic: Invert the underground, so that we can see the MC generator in action :)
+ bool ShouldInvert[cChunkDef::Width * cChunkDef::Width];
+ memset(ShouldInvert, 0, sizeof(ShouldInvert));
+ for (int y = cChunkDef::Height - 1; y >= 0; y--)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int Index = cChunkDef::MakeIndexNoCheck(x, y, z);
+ if (ShouldInvert[x + cChunkDef::Width * z])
+ {
+ BlockTypes[Index] = (BlockTypes[Index] == E_BLOCK_AIR) ? E_BLOCK_STONE : E_BLOCK_AIR;
+ }
+ else
+ {
+ switch (BlockTypes[Index])
+ {
+ case E_BLOCK_AIR:
+ case E_BLOCK_LEAVES:
+ {
+ // nothing needed
+ break;
+ }
+ default:
+ {
+ ShouldInvert[x + cChunkDef::Width * z] = true;
+ }
+ }
+ BlockTypes[Index] = E_BLOCK_AIR;
+ }
+ }
+ } // for y
+ //*/
+
+ m_World->SetChunkData(
+ a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
+ BlockTypes, MetaData,
+ IsLightValid ? BlockLight : NULL,
+ IsLightValid ? SkyLight : NULL,
+ NULL, Biomes,
+ Entities, BlockEntities,
+ false
+ );
+ return true;
+}
+
+
+
+
+void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length)
+{
+ int Child = a_NBT.FindChildByName(a_Tag, a_ChildName);
+ if ((Child >= 0) && (a_NBT.GetType(Child) == TAG_ByteArray) && (a_NBT.GetDataLength(Child) == a_Length))
+ {
+ memcpy(a_Destination, a_NBT.GetData(Child), a_Length);
+ }
+}
+
+
+
+
+
+bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer)
+{
+ a_Writer.BeginCompound("Level");
+ a_Writer.AddInt("xPos", a_Chunk.m_ChunkX);
+ a_Writer.AddInt("zPos", a_Chunk.m_ChunkZ);
+ cNBTChunkSerializer Serializer(a_Writer);
+ if (!m_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer))
+ {
+ LOGWARNING("Cannot get chunk [%d, %d] data for NBT saving", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
+ return false;
+ }
+ Serializer.Finish(); // Close NBT tags
+
+ // Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray):
+ if (Serializer.m_BiomesAreValid)
+ {
+ a_Writer.AddByteArray("Biomes", (const char *)(Serializer.m_VanillaBiomes), ARRAYCOUNT(Serializer.m_VanillaBiomes));
+ a_Writer.AddIntArray ("MCSBiomes", (const int *)(Serializer.m_Biomes), ARRAYCOUNT(Serializer.m_Biomes));
+ }
+
+ // Save blockdata:
+ a_Writer.BeginList("Sections", TAG_Compound);
+ int SliceSizeBlock = cChunkDef::Width * cChunkDef::Width * 16;
+ int SliceSizeNibble = SliceSizeBlock / 2;
+ const char * BlockTypes = (const char *)(Serializer.m_BlockTypes);
+ const char * BlockMetas = (const char *)(Serializer.m_BlockMetas);
+ #ifdef DEBUG_SKYLIGHT
+ const char * BlockLight = (const char *)(Serializer.m_BlockSkyLight);
+ #else
+ const char * BlockLight = (const char *)(Serializer.m_BlockLight);
+ #endif
+ const char * BlockSkyLight = (const char *)(Serializer.m_BlockSkyLight);
+ for (int Y = 0; Y < 16; Y++)
+ {
+ a_Writer.BeginCompound("");
+ a_Writer.AddByteArray("Blocks", BlockTypes + Y * SliceSizeBlock, SliceSizeBlock);
+ a_Writer.AddByteArray("Data", BlockMetas + Y * SliceSizeNibble, SliceSizeNibble);
+ a_Writer.AddByteArray("SkyLight", BlockSkyLight + Y * SliceSizeNibble, SliceSizeNibble);
+ a_Writer.AddByteArray("BlockLight", BlockLight + Y * SliceSizeNibble, SliceSizeNibble);
+ a_Writer.AddByte("Y", (unsigned char)Y);
+ a_Writer.EndCompound();
+ }
+ a_Writer.EndList(); // "Sections"
+
+ // Store the information that the lighting is valid.
+ // For compatibility reason, the default is "invalid" (missing) - this means older data is re-lighted upon loading.
+ if (Serializer.IsLightValid())
+ {
+ a_Writer.AddByte("MCSIsLightValid", 1);
+ }
+
+ a_Writer.EndCompound(); // "Level"
+ return true;
+}
+
+
+
+
+
+cChunkDef::BiomeMap * cWSSAnvil::LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_ByteArray))
+ {
+ return NULL;
+ }
+ if (a_NBT.GetDataLength(a_TagIdx) != 16 * 16)
+ {
+ // The biomes stored don't match in size
+ return NULL;
+ }
+ const unsigned char * VanillaBiomeData = (const unsigned char *)(a_NBT.GetData(a_TagIdx));
+ for (int i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++)
+ {
+ if ((VanillaBiomeData)[i] == 0xff)
+ {
+ // Unassigned biomes
+ return NULL;
+ }
+ (*a_BiomeMap)[i] = (EMCSBiome)(VanillaBiomeData[i]);
+ }
+ return a_BiomeMap;
+}
+
+
+
+
+
+cChunkDef::BiomeMap * cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_IntArray))
+ {
+ return NULL;
+ }
+ if (a_NBT.GetDataLength(a_TagIdx) != sizeof(*a_BiomeMap))
+ {
+ // The biomes stored don't match in size
+ return NULL;
+ }
+ const int * BiomeData = (const int *)(a_NBT.GetData(a_TagIdx));
+ for (int i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++)
+ {
+ (*a_BiomeMap)[i] = (EMCSBiome)(ntohl(BiomeData[i]));
+ if ((*a_BiomeMap)[i] == 0xff)
+ {
+ // Unassigned biomes
+ return NULL;
+ }
+ }
+ return a_BiomeMap;
+}
+
+
+
+
+
+void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List))
+ {
+ return;
+ }
+
+ for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child))
+ {
+ if (a_NBT.GetType(Child) != TAG_Compound)
+ {
+ continue;
+ }
+ int sID = a_NBT.FindChildByName(Child, "id");
+ if (sID < 0)
+ {
+ continue;
+ }
+ LoadEntityFromNBT(a_Entities, a_NBT, Child, a_NBT.GetData(sID), a_NBT.GetDataLength(sID));
+ } // for Child - a_NBT[]
+}
+
+
+
+
+
+void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas)
+{
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List))
+ {
+ return;
+ }
+
+ for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child))
+ {
+ if (a_NBT.GetType(Child) != TAG_Compound)
+ {
+ continue;
+ }
+ int sID = a_NBT.FindChildByName(Child, "id");
+ if (sID < 0)
+ {
+ continue;
+ }
+ if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadChestFromNBT(a_BlockEntities, a_NBT, Child);
+ }
+ else if (strncmp(a_NBT.GetData(sID), "Dropper", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadDropperFromNBT(a_BlockEntities, a_NBT, Child);
+ }
+ else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas);
+ }
+ else if (strncmp(a_NBT.GetData(sID), "Hopper", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadHopperFromNBT(a_BlockEntities, a_NBT, Child);
+ }
+ else if (strncmp(a_NBT.GetData(sID), "Music", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadNoteFromNBT(a_BlockEntities, a_NBT, Child);
+ }
+ else if (strncmp(a_NBT.GetData(sID), "RecordPlayer", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadJukeboxFromNBT(a_BlockEntities, a_NBT, Child);
+ }
+ else if (strncmp(a_NBT.GetData(sID), "Sign", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadSignFromNBT(a_BlockEntities, a_NBT, Child);
+ }
+ else if (strncmp(a_NBT.GetData(sID), "Trap", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child);
+ }
+ // TODO: Other block entities
+ } // for Child - tag children
+}
+
+
+
+
+
+bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ int ID = a_NBT.FindChildByName(a_TagIdx, "id");
+ if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
+ {
+ return false;
+ }
+ a_Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
+
+ int Damage = a_NBT.FindChildByName(a_TagIdx, "Damage");
+ if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
+ {
+ return false;
+ }
+ a_Item.m_ItemDamage = a_NBT.GetShort(Damage);
+
+ int Count = a_NBT.FindChildByName(a_TagIdx, "Count");
+ if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte))
+ {
+ return false;
+ }
+ a_Item.m_ItemCount = a_NBT.GetByte(Count);
+
+ // Find the "tag" tag, used for enchantments and other extra data
+ int TagTag = a_NBT.FindChildByName(a_TagIdx, "tag");
+ if (TagTag <= 0)
+ {
+ // No extra data
+ return true;
+ }
+
+ // Load enchantments:
+ const char * EnchName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
+ int EnchTag = a_NBT.FindChildByName(TagTag, EnchName);
+ if (EnchTag > 0)
+ {
+ a_Item.m_Enchantments.ParseFromNBT(a_NBT, EnchTag);
+ }
+
+ return true;
+}
+
+
+
+
+
+void cWSSAnvil::LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int a_SlotOffset)
+{
+ int NumSlots = a_ItemGrid.GetNumSlots();
+ for (int Child = a_NBT.GetFirstChild(a_ItemsTagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child))
+ {
+ int SlotTag = a_NBT.FindChildByName(Child, "Slot");
+ if ((SlotTag < 0) || (a_NBT.GetType(SlotTag) != TAG_Byte))
+ {
+ continue;
+ }
+ int SlotNum = (int)(a_NBT.GetByte(SlotTag)) - a_SlotOffset;
+ if ((SlotNum < 0) || (SlotNum >= NumSlots))
+ {
+ // SlotNum outside of the range
+ continue;
+ }
+ cItem Item;
+ if (LoadItemFromNBT(Item, a_NBT, Child))
+ {
+ a_ItemGrid.SetSlot(SlotNum, Item);
+ }
+ } // for itr - ItemDefs[]
+}
+
+
+
+
+
+void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
+ if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
+ {
+ return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
+ }
+ std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World));
+ LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items);
+ a_BlockEntities.push_back(Chest.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadDispenserFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
+ if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
+ {
+ return; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this
+ }
+ std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(x, y, z, m_World));
+ LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items);
+ a_BlockEntities.push_back(Dispenser.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
+ if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
+ {
+ return; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this
+ }
+ std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(x, y, z, m_World));
+ LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items);
+ a_BlockEntities.push_back(Dropper.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
+ if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
+ {
+ return; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this
+ }
+
+ // Convert coords to relative:
+ int RelX = x;
+ int RelZ = z;
+ int ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(RelX, y, RelZ, ChunkX, ChunkZ);
+
+ // Create the furnace entity, with proper BlockType and BlockMeta info:
+ BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, y, RelZ);
+ NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, y, RelZ);
+ std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(x, y, z, BlockType, BlockMeta, m_World));
+
+ // Load slots:
+ for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
+ {
+ int Slot = a_NBT.FindChildByName(Child, "Slot");
+ if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte))
+ {
+ continue;
+ }
+ cItem Item;
+ if (LoadItemFromNBT(Item, a_NBT, Child))
+ {
+ Furnace->SetSlot(a_NBT.GetByte(Slot), Item);
+ }
+ } // for itr - ItemDefs[]
+
+ // Load burn time:
+ int BurnTime = a_NBT.FindChildByName(a_TagIdx, "BurnTime");
+ if (BurnTime >= 0)
+ {
+ Int16 bt = a_NBT.GetShort(BurnTime);
+ // Anvil doesn't store the time that the fuel can burn. We simply "reset" the current value to be the 100%
+ Furnace->SetBurnTimes(bt, 0);
+ }
+
+ // Load cook time:
+ int CookTime = a_NBT.FindChildByName(a_TagIdx, "CookTime");
+ if (CookTime >= 0)
+ {
+ Int16 ct = a_NBT.GetShort(CookTime);
+ // Anvil doesn't store the time that an item takes to cook. We simply use the default - 10 seconds (200 ticks)
+ Furnace->SetCookTimes(200, ct);
+ }
+
+ // Restart cooking:
+ Furnace->ContinueCooking();
+ a_BlockEntities.push_back(Furnace.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadHopperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
+ if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
+ {
+ return; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this
+ }
+ std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(x, y, z, m_World));
+ LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items);
+ a_BlockEntities.push_back(Hopper.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(x, y, z, m_World));
+ int Record = a_NBT.FindChildByName(a_TagIdx, "Record");
+ if (Record >= 0)
+ {
+ Jukebox->SetRecord(a_NBT.GetInt(Record));
+ }
+ a_BlockEntities.push_back(Jukebox.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadNoteFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ std::auto_ptr<cNoteEntity> Note(new cNoteEntity(x, y, z, m_World));
+ int note = a_NBT.FindChildByName(a_TagIdx, "note");
+ if (note >= 0)
+ {
+ Note->SetPitch(a_NBT.GetByte(note));
+ }
+ a_BlockEntities.push_back(Note.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ std::auto_ptr<cSignEntity> Sign(new cSignEntity(E_BLOCK_SIGN_POST, x, y, z, m_World));
+
+ int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1");
+ if (currentLine >= 0)
+ {
+ Sign->SetLine(0, a_NBT.GetString(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Text2");
+ if (currentLine >= 0)
+ {
+ Sign->SetLine(1, a_NBT.GetString(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Text3");
+ if (currentLine >= 0)
+ {
+ Sign->SetLine(2, a_NBT.GetString(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Text4");
+ if (currentLine >= 0)
+ {
+ Sign->SetLine(3, a_NBT.GetString(currentLine));
+ }
+
+ a_BlockEntities.push_back(Sign.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength)
+{
+ if (strncmp(a_IDTag, "Boat", a_IDTagLength) == 0)
+ {
+ LoadBoatFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "FallingBlock", a_IDTagLength) == 0)
+ {
+ LoadFallingBlockFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "Minecart", a_IDTagLength) == 0)
+ {
+ // It is a minecart, old style, find out the type:
+ int TypeTag = a_NBT.FindChildByName(a_EntityTagIdx, "Type");
+ if ((TypeTag < 0) || (a_NBT.GetType(TypeTag) != TAG_Int))
+ {
+ return;
+ }
+ switch (a_NBT.GetInt(TypeTag))
+ {
+ case 0: LoadMinecartRFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Rideable minecart
+ case 1: LoadMinecartCFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with chest
+ case 2: LoadMinecartFFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with furnace
+ case 3: LoadMinecartTFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with TNT
+ case 4: LoadMinecartHFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with Hopper
+ }
+ }
+ else if (strncmp(a_IDTag, "MinecartRideable", a_IDTagLength) == 0)
+ {
+ LoadMinecartRFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "MinecartChest", a_IDTagLength) == 0)
+ {
+ LoadMinecartCFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "MinecartFurnace", a_IDTagLength) == 0)
+ {
+ LoadMinecartFFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "MinecartTNT", a_IDTagLength) == 0)
+ {
+ LoadMinecartTFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "MinecartHopper", a_IDTagLength) == 0)
+ {
+ LoadMinecartHFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "Item", a_IDTagLength) == 0)
+ {
+ LoadPickupFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "Arrow", a_IDTagLength) == 0)
+ {
+ LoadArrowFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "Snowball", a_IDTagLength) == 0)
+ {
+ LoadSnowballFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "Egg", a_IDTagLength) == 0)
+ {
+ LoadEggFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "Fireball", a_IDTagLength) == 0)
+ {
+ LoadFireballFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "SmallFireball", a_IDTagLength) == 0)
+ {
+ LoadFireChargeFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "ThrownEnderpearl", a_IDTagLength) == 0)
+ {
+ LoadThrownEnderpearlFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ // TODO: other entities
+}
+
+
+
+
+
+void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cBoat> Boat(new cBoat(0, 0, 0));
+ if (!LoadEntityBaseFromNBT(*Boat.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+ a_Entities.push_back(Boat.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ // TODO
+}
+
+
+
+
+
+void cWSSAnvil::LoadMinecartRFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cEmptyMinecart> Minecart(new cEmptyMinecart(0, 0, 0));
+ if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+ a_Entities.push_back(Minecart.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
+ if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
+ {
+ return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
+ }
+ std::auto_ptr<cMinecartWithChest> Minecart(new cMinecartWithChest(0, 0, 0));
+ if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+ for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
+ {
+ int Slot = a_NBT.FindChildByName(Child, "Slot");
+ if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte))
+ {
+ continue;
+ }
+ cItem Item;
+ if (LoadItemFromNBT(Item, a_NBT, Child))
+ {
+ Minecart->SetSlot(a_NBT.GetByte(Slot), Item);
+ }
+ } // for itr - ItemDefs[]
+ a_Entities.push_back(Minecart.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cMinecartWithFurnace> Minecart(new cMinecartWithFurnace(0, 0, 0));
+ if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // TODO: Load the Push and Fuel tags
+
+ a_Entities.push_back(Minecart.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cMinecartWithTNT> Minecart(new cMinecartWithTNT(0, 0, 0));
+ if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // TODO: Everything to do with TNT carts
+
+ a_Entities.push_back(Minecart.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadMinecartHFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cMinecartWithHopper> Minecart(new cMinecartWithHopper(0, 0, 0));
+ if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // TODO: Everything to do with hopper carts
+
+ a_Entities.push_back(Minecart.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ int ItemTag = a_NBT.FindChildByName(a_TagIdx, "Item");
+ if ((ItemTag < 0) || (a_NBT.GetType(ItemTag) != TAG_Compound))
+ {
+ return;
+ }
+ cItem Item;
+ if (!LoadItemFromNBT(Item, a_NBT, ItemTag))
+ {
+ return;
+ }
+ std::auto_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false
+ if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+ a_Entities.push_back(Pickup.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cArrowEntity> Arrow(new cArrowEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
+ if (!LoadProjectileBaseFromNBT(*Arrow.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // Load pickup state:
+ int PickupIdx = a_NBT.FindChildByName(a_TagIdx, "pickup");
+ if (PickupIdx > 0)
+ {
+ Arrow->SetPickupState((cArrowEntity::ePickupState)a_NBT.GetByte(PickupIdx));
+ }
+ else
+ {
+ // Try the older "player" tag:
+ int PlayerIdx = a_NBT.FindChildByName(a_TagIdx, "player");
+ if (PlayerIdx > 0)
+ {
+ Arrow->SetPickupState((a_NBT.GetByte(PlayerIdx) == 0) ? cArrowEntity::psNoPickup : cArrowEntity::psInSurvivalOrCreative);
+ }
+ }
+
+ // Load damage:
+ int DamageIdx = a_NBT.FindChildByName(a_TagIdx, "damage");
+ if (DamageIdx > 0)
+ {
+ Arrow->SetDamageCoeff(a_NBT.GetDouble(DamageIdx));
+ }
+
+ // Store the new arrow in the entities list:
+ a_Entities.push_back(Arrow.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cThrownSnowballEntity> Snowball(new cThrownSnowballEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
+ if (!LoadProjectileBaseFromNBT(*Snowball.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // Store the new snowball in the entities list:
+ a_Entities.push_back(Snowball.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cThrownEggEntity> Egg(new cThrownEggEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
+ if (!LoadProjectileBaseFromNBT(*Egg.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // Store the new egg in the entities list:
+ a_Entities.push_back(Egg.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cGhastFireballEntity> Fireball(new cGhastFireballEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
+ if (!LoadProjectileBaseFromNBT(*Fireball.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // Store the new fireball in the entities list:
+ a_Entities.push_back(Fireball.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cFireChargeEntity> FireCharge(new cFireChargeEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
+ if (!LoadProjectileBaseFromNBT(*FireCharge.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // Store the new FireCharge in the entities list:
+ a_Entities.push_back(FireCharge.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cThrownEnderPearlEntity> Enderpearl(new cThrownEnderPearlEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
+ if (!LoadProjectileBaseFromNBT(*Enderpearl.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // Store the new enderpearl in the entities list:
+ a_Entities.push_back(Enderpearl.release());
+}
+
+
+
+
+
+bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ double Pos[3];
+ if (!LoadDoublesListFromNBT(Pos, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Pos")))
+ {
+ return false;
+ }
+ a_Entity.SetPosition(Pos[0], Pos[1], Pos[2]);
+
+ double Speed[3];
+ if (!LoadDoublesListFromNBT(Speed, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Motion")))
+ {
+ return false;
+ }
+ a_Entity.SetSpeed(Speed[0], Speed[1], Speed[2]);
+
+ double Rotation[3];
+ if (!LoadDoublesListFromNBT(Rotation, 2, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Rotation")))
+ {
+ return false;
+ }
+ a_Entity.SetRotation(Rotation[0]);
+ a_Entity.SetRoll (Rotation[1]);
+
+ return true;
+}
+
+
+
+
+
+bool cWSSAnvil::LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ if (!LoadEntityBaseFromNBT(a_Entity, a_NBT, a_TagIdx))
+ {
+ return false;
+ }
+
+ bool IsInGround = false;
+ int InGroundIdx = a_NBT.FindChildByName(a_TagIdx, "inGround");
+ if (InGroundIdx > 0)
+ {
+ IsInGround = (a_NBT.GetByte(InGroundIdx) != 0);
+ }
+ a_Entity.SetIsInGround(IsInGround);
+
+ // TODO: Load inTile, TileCoords
+
+ return true;
+}
+
+
+
+
+
+bool cWSSAnvil::LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List) || (a_NBT.GetChildrenType(a_TagIdx) != TAG_Double))
+ {
+ return false;
+ }
+ int idx = 0;
+ for (int Tag = a_NBT.GetFirstChild(a_TagIdx); (Tag > 0) && (idx < a_NumDoubles); Tag = a_NBT.GetNextSibling(Tag), ++idx)
+ {
+ a_Doubles[idx] = a_NBT.GetDouble(Tag);
+ } // for Tag - PosTag[]
+ return (idx == a_NumDoubles); // Did we read enough doubles?
+}
+
+
+
+
+
+bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z)
+{
+ int x = a_NBT.FindChildByName(a_TagIdx, "x");
+ if ((x < 0) || (a_NBT.GetType(x) != TAG_Int))
+ {
+ return false;
+ }
+ int y = a_NBT.FindChildByName(a_TagIdx, "y");
+ if ((y < 0) || (a_NBT.GetType(y) != TAG_Int))
+ {
+ return false;
+ }
+ int z = a_NBT.FindChildByName(a_TagIdx, "z");
+ if ((z < 0) || (a_NBT.GetType(z) != TAG_Int))
+ {
+ return false;
+ }
+ a_X = a_NBT.GetInt(x);
+ a_Y = a_NBT.GetInt(y);
+ a_Z = a_NBT.GetInt(z);
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWSSAnvil::cMCAFile:
+
+cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ) :
+ m_RegionX(a_RegionX),
+ m_RegionZ(a_RegionZ),
+ m_FileName(a_FileName)
+{
+}
+
+
+
+
+
+bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading)
+{
+ if (m_File.IsOpen())
+ {
+ // Already open
+ return true;
+ }
+
+ if (a_IsForReading)
+ {
+ if (!cFile::Exists(m_FileName))
+ {
+ // We want to read and the file doesn't exist. Fail.
+ return false;
+ }
+ }
+
+ if (!m_File.Open(m_FileName, cFile::fmReadWrite))
+ {
+ // The file failed to open
+ return false;
+ }
+
+ // Load the header:
+ if (m_File.Read(m_Header, sizeof(m_Header)) != sizeof(m_Header))
+ {
+ // Cannot read the header - perhaps the file has just been created?
+ // Try writing a NULL header (both chunk offsets and timestamps):
+ memset(m_Header, 0, sizeof(m_Header));
+ if (
+ (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Real header - chunk offsets
+ (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) // Bogus data for the chunk timestamps
+ )
+ {
+ LOGWARNING("Cannot process MCA header in file \"%s\", chunks in that file will be lost", m_FileName.c_str());
+ m_File.Close();
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+
+
+bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data)
+{
+ if (!OpenFile(true))
+ {
+ return false;
+ }
+
+ int LocalX = a_Chunk.m_ChunkX % 32;
+ if (LocalX < 0)
+ {
+ LocalX = 32 + LocalX;
+ }
+ int LocalZ = a_Chunk.m_ChunkZ % 32;
+ if (LocalZ < 0)
+ {
+ LocalZ = 32 + LocalZ;
+ }
+ unsigned ChunkLocation = ntohl(m_Header[LocalX + 32 * LocalZ]);
+ unsigned ChunkOffset = ChunkLocation >> 8;
+
+ m_File.Seek(ChunkOffset * 4096);
+
+ int ChunkSize = 0;
+ if (m_File.Read(&ChunkSize, 4) != 4)
+ {
+ return false;
+ }
+ ChunkSize = ntohl(ChunkSize);
+ char CompressionType = 0;
+ if (m_File.Read(&CompressionType, 1) != 1)
+ {
+ return false;
+ }
+ if (CompressionType != 2)
+ {
+ // Chunk is in an unknown compression
+ return false;
+ }
+ ChunkSize--;
+
+ // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly
+ a_Data.assign(ChunkSize, '\0');
+ return (m_File.Read((void *)a_Data.data(), ChunkSize) == ChunkSize);
+}
+
+
+
+
+
+bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data)
+{
+ if (!OpenFile(false))
+ {
+ LOGWARNING("Cannot save chunk [%d, %d], opening file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
+ return false;
+ }
+
+ int LocalX = a_Chunk.m_ChunkX % 32;
+ if (LocalX < 0)
+ {
+ LocalX = 32 + LocalX;
+ }
+ int LocalZ = a_Chunk.m_ChunkZ % 32;
+ if (LocalZ < 0)
+ {
+ LocalZ = 32 + LocalZ;
+ }
+
+ unsigned ChunkSector = FindFreeLocation(LocalX, LocalZ, a_Data);
+
+ // Store the chunk data:
+ m_File.Seek(ChunkSector * 4096);
+ unsigned ChunkSize = htonl(a_Data.size() + 1);
+ if (m_File.Write(&ChunkSize, 4) != 4)
+ {
+ LOGWARNING("Cannot save chunk [%d, %d], writing(1) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
+ return false;
+ }
+ char CompressionType = 2;
+ if (m_File.Write(&CompressionType, 1) != 1)
+ {
+ LOGWARNING("Cannot save chunk [%d, %d], writing(2) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
+ return false;
+ }
+ if (m_File.Write(a_Data.data(), a_Data.size()) != (int)(a_Data.size()))
+ {
+ LOGWARNING("Cannot save chunk [%d, %d], writing(3) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
+ return false;
+ }
+
+ // Store the header:
+ ChunkSize = (a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number
+ ASSERT(ChunkSize < 256);
+ m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize);
+ if (m_File.Seek(0) < 0)
+ {
+ LOGWARNING("Cannot save chunk [%d, %d], seeking in file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
+ return false;
+ }
+ if (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header))
+ {
+ LOGWARNING("Cannot save chunk [%d, %d], writing header to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+unsigned cWSSAnvil::cMCAFile::FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data)
+{
+ // See if it fits the current location:
+ unsigned ChunkLocation = ntohl(m_Header[a_LocalX + 32 * a_LocalZ]);
+ unsigned ChunkLen = ChunkLocation & 0xff;
+ if (a_Data.size() + MCA_CHUNK_HEADER_LENGTH <= (ChunkLen * 4096))
+ {
+ return ChunkLocation >> 8;
+ }
+
+ // Doesn't fit, append to the end of file (we're wasting a lot of space, TODO: fix this later)
+ unsigned MaxLocation = 2 << 8; // Minimum sector is #2 - after the headers
+ for (int i = 0; i < ARRAYCOUNT(m_Header); i++)
+ {
+ ChunkLocation = ntohl(m_Header[i]);
+ ChunkLocation = ChunkLocation + ((ChunkLocation & 0xff) << 8); // Add the number of sectors used; don't care about the 4th byte
+ if (MaxLocation < ChunkLocation)
+ {
+ MaxLocation = ChunkLocation;
+ }
+ } // for i - m_Header[]
+ return MaxLocation >> 8;
+}
+
+
+
+
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
new file mode 100644
index 000000000..7685d2236
--- /dev/null
+++ b/src/WorldStorage/WSSAnvil.h
@@ -0,0 +1,184 @@
+
+// WSSAnvil.h
+
+// Interfaces to the cWSSAnvil class representing the Anvil world storage scheme
+
+
+
+
+#pragma once
+
+#include "WorldStorage.h"
+#include "FastNBT.h"
+
+
+
+
+
+// fwd: ItemGrid.h
+class cItemGrid;
+
+class cProjectileEntity;
+
+
+
+
+
+enum
+{
+ /// Maximum number of chunks in an MCA file - also the count of the header items
+ MCA_MAX_CHUNKS = 32 * 32,
+
+ /// The MCA header is 8 KiB
+ MCA_HEADER_SIZE = MCA_MAX_CHUNKS * 8,
+
+ /// There are 5 bytes of header in front of each chunk
+ MCA_CHUNK_HEADER_LENGTH = 5,
+} ;
+
+
+
+
+
+class cWSSAnvil :
+ public cWSSchema
+{
+ typedef cWSSchema super;
+
+public:
+
+ cWSSAnvil(cWorld * a_World);
+ virtual ~cWSSAnvil();
+
+protected:
+
+ class cMCAFile
+ {
+ public:
+
+ cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ);
+
+ bool GetChunkData (const cChunkCoords & a_Chunk, AString & a_Data);
+ bool SetChunkData (const cChunkCoords & a_Chunk, const AString & a_Data);
+ bool EraseChunkData(const cChunkCoords & a_Chunk);
+
+ int GetRegionX (void) const {return m_RegionX; }
+ int GetRegionZ (void) const {return m_RegionZ; }
+ const AString & GetFileName(void) const {return m_FileName; }
+
+ protected:
+
+ int m_RegionX;
+ int m_RegionZ;
+ cFile m_File;
+ AString m_FileName;
+
+ // The header, copied from the file so we don't have to seek to it all the time
+ // First 1024 entries are chunk locations - the 3 + 1 byte sector-offset and sector-count
+ unsigned m_Header[MCA_MAX_CHUNKS];
+
+ // Chunk timestamps, following the chunk headers, are unused by MCS
+
+ /// Finds a free location large enough to hold a_Data. Gets a hint of the chunk coords, places the data there if it fits. Returns the sector number.
+ unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data);
+
+ /// Opens a MCA file either for a Read operation (fails if doesn't exist) or for a Write operation (creates new if not found)
+ bool OpenFile(bool a_IsForReading);
+ } ;
+ typedef std::list<cMCAFile *> cMCAFiles;
+
+ cCriticalSection m_CS;
+ cMCAFiles m_Files; // a MRU cache of MCA files
+
+ /// Gets chunk data from the correct file; locks file CS as needed
+ bool GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data);
+
+ /// Sets chunk data into the correct file; locks file CS as needed
+ bool SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data);
+
+ /// Loads the chunk from the data (no locking needed)
+ bool LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data);
+
+ /// Saves the chunk into datastream (no locking needed)
+ bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data);
+
+ /// Loads the chunk from NBT data (no locking needed)
+ bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT);
+
+ /// Saves the chunk into NBT data using a_Writer; returns true on success
+ bool SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer);
+
+ /// Loads the chunk's biome map from vanilla-format; returns a_BiomeMap if biomes present and valid, NULL otherwise
+ cChunkDef::BiomeMap * LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx);
+
+ /// Loads the chunk's biome map from MCS format; returns a_BiomeMap if biomes present and valid, NULL otherwise
+ cChunkDef::BiomeMap * LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx);
+
+ /// Loads the chunk's entities from NBT data (a_Tag is the Level\\Entities list tag; may be -1)
+ void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_Tag);
+
+ /// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1)
+ void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
+
+ /// Loads a cItem contents from the specified NBT tag; returns true if successful. Doesn't load the Slot tag
+ bool LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx);
+
+ /** Loads contentents of an Items[] list tag into a cItemGrid
+ ItemGrid begins at the specified slot offset
+ Slots outside the ItemGrid range are ignored
+ */
+ void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0);
+
+ void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
+ void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+
+ void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength);
+
+ void LoadBoatFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadFallingBlockFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadMinecartFFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadMinecartTFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadMinecartHFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadArrowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadSnowballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadEggFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadFireballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadFireChargeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+
+ /// Loads entity common data from the NBT compound; returns true if successful
+ bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx);
+
+ /// Loads projectile common data from the NBT compound; returns true if successful
+ bool LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIx);
+
+ /// Loads an array of doubles of the specified length from the specified NBT list tag a_TagIdx; returns true if successful
+ bool LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx);
+
+ /// Helper function for extracting the X, Y, and Z int subtags of a NBT compound; returns true if successful
+ bool GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z);
+
+ /// Gets the correct MCA file either from cache or from disk, manages the m_MCAFiles cache; assumes m_CS is locked
+ cMCAFile * LoadMCAFile(const cChunkCoords & a_Chunk);
+
+ /// Copies a_Length bytes of data from the specified NBT Tag's Child into the a_Destination buffer
+ void CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length);
+
+ // cWSSchema overrides:
+ virtual bool LoadChunk(const cChunkCoords & a_Chunk) override;
+ virtual bool SaveChunk(const cChunkCoords & a_Chunk) override;
+ virtual const AString GetName(void) const override {return "anvil"; }
+} ;
+
+
+
+
diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp
new file mode 100644
index 000000000..dafa4d1b5
--- /dev/null
+++ b/src/WorldStorage/WSSCompact.cpp
@@ -0,0 +1,1009 @@
+
+// WSSCompact.cpp
+
+// Interfaces to the cWSSCompact class representing the "compact" storage schema (PAK-files)
+
+#include "Globals.h"
+#include "WSSCompact.h"
+#include "../World.h"
+#include "lib/zlib/zlib.h"
+#include "lib/jsoncpp/include/json/json.h"
+#include "../StringCompression.h"
+#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/DispenserEntity.h"
+#include "../BlockEntities/FurnaceEntity.h"
+#include "../BlockEntities/JukeboxEntity.h"
+#include "../BlockEntities/NoteEntity.h"
+#include "../BlockEntities/SignEntity.h"
+
+
+
+
+
+#pragma pack(push, 1)
+/// The chunk header, as stored in the file:
+struct cWSSCompact::sChunkHeader
+{
+ int m_ChunkX;
+ int m_ChunkZ;
+ int m_CompressedSize;
+ int m_UncompressedSize;
+} ;
+#pragma pack(pop)
+
+
+
+
+
+/// The maximum number of PAK files that are cached
+const int MAX_PAK_FILES = 16;
+
+/// The maximum number of unsaved chunks before the cPAKFile saves them to disk
+const int MAX_DIRTY_CHUNKS = 16;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cJsonChunkSerializer:
+
+cJsonChunkSerializer::cJsonChunkSerializer(void) :
+ m_HasJsonData(false)
+{
+}
+
+
+
+
+
+void cJsonChunkSerializer::Entity(cEntity * a_Entity)
+{
+ // TODO: a_Entity->SaveToJson(m_Root);
+}
+
+
+
+
+
+void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity)
+{
+ const char * SaveInto = NULL;
+ switch (a_BlockEntity->GetBlockType())
+ {
+ case E_BLOCK_CHEST: SaveInto = "Chests"; break;
+ case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break;
+ case E_BLOCK_DROPPER: SaveInto = "Droppers"; break;
+ case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break;
+ case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break;
+ case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break;
+ case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break;
+ case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break;
+
+ default:
+ {
+ ASSERT(!"Unhandled blocktype in BlockEntities list while saving to JSON");
+ break;
+ }
+ } // switch (BlockEntity->GetBlockType())
+ if (SaveInto == NULL)
+ {
+ return;
+ }
+
+ Json::Value val;
+ a_BlockEntity->SaveToJson(val);
+ m_Root[SaveInto].append(val);
+ m_HasJsonData = true;
+}
+
+
+
+
+
+bool cJsonChunkSerializer::LightIsValid(bool a_IsLightValid)
+{
+ if (!a_IsLightValid)
+ {
+ return false;
+ }
+ m_Root["IsLightValid"] = true;
+ m_HasJsonData = true;
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWSSCompact:
+
+cWSSCompact::~cWSSCompact()
+{
+ for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr)
+ {
+ delete *itr;
+ }
+}
+
+
+
+
+
+bool cWSSCompact::LoadChunk(const cChunkCoords & a_Chunk)
+{
+ AString ChunkData;
+ int UncompressedSize = 0;
+ if (!GetChunkData(a_Chunk, UncompressedSize, ChunkData))
+ {
+ // The reason for failure is already printed in GetChunkData()
+ return false;
+ }
+
+ return LoadChunkFromData(a_Chunk, UncompressedSize, ChunkData, m_World);
+}
+
+
+
+
+
+bool cWSSCompact::SaveChunk(const cChunkCoords & a_Chunk)
+{
+ cCSLock Lock(m_CS);
+
+ cPAKFile * f = LoadPAKFile(a_Chunk);
+ if (f == NULL)
+ {
+ // For some reason we couldn't locate the file
+ LOG("Cannot locate a proper PAK file for chunk [%d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
+ return false;
+ }
+ return f->SaveChunk(a_Chunk, m_World);
+}
+
+
+
+
+
+cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(const cChunkCoords & a_Chunk)
+{
+ // ASSUMES that m_CS has been locked
+
+ // We need to retain this weird conversion code, because some edge chunks are in the wrong PAK file
+ const int LayerX = FAST_FLOOR_DIV(a_Chunk.m_ChunkX, 32);
+ const int LayerZ = FAST_FLOOR_DIV(a_Chunk.m_ChunkZ, 32);
+
+ // Is it already cached?
+ for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr)
+ {
+ if (((*itr) != NULL) && ((*itr)->GetLayerX() == LayerX) && ((*itr)->GetLayerZ() == LayerZ))
+ {
+ // Move the file to front and return it:
+ cPAKFile * f = *itr;
+ if (itr != m_PAKFiles.begin())
+ {
+ m_PAKFiles.erase(itr);
+ m_PAKFiles.push_front(f);
+ }
+ return f;
+ }
+ }
+
+ // Load it anew:
+ AString FileName;
+ Printf(FileName, "%s/X%i_Z%i.pak", m_World->GetName().c_str(), LayerX, LayerZ );
+ cPAKFile * f = new cPAKFile(FileName, LayerX, LayerZ);
+ if (f == NULL)
+ {
+ return NULL;
+ }
+ m_PAKFiles.push_front(f);
+
+ // If there are too many PAK files cached, delete the last one used:
+ if (m_PAKFiles.size() > MAX_PAK_FILES)
+ {
+ delete m_PAKFiles.back();
+ m_PAKFiles.pop_back();
+ }
+ return f;
+}
+
+
+
+
+
+bool cWSSCompact::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data)
+{
+ cCSLock Lock(m_CS);
+ cPAKFile * f = LoadPAKFile(a_Chunk);
+ if (f == NULL)
+ {
+ return false;
+ }
+ return f->GetChunkData(a_Chunk, a_UncompressedSize, a_Data);
+}
+
+
+
+
+
+/*
+// TODO: Rewrite saving to use the same principles as loading
+bool cWSSCompact::SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data)
+{
+ cCSLock Lock(m_CS);
+ cPAKFile * f = LoadPAKFile(a_Chunk);
+ if (f == NULL)
+ {
+ return false;
+ }
+ return f->SetChunkData(a_Chunk, a_UncompressedSize, a_Data);
+}
+*/
+
+
+
+
+
+bool cWSSCompact::EraseChunkData(const cChunkCoords & a_Chunk)
+{
+ cCSLock Lock(m_CS);
+ cPAKFile * f = LoadPAKFile(a_Chunk);
+ if (f == NULL)
+ {
+ return false;
+ }
+ return f->EraseChunkData(a_Chunk);
+}
+
+
+
+
+
+void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World)
+{
+ // Load chests
+ Json::Value AllChests = a_Value.get("Chests", Json::nullValue);
+ if (!AllChests.empty())
+ {
+ for (Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr )
+ {
+ Json::Value & Chest = *itr;
+ cChestEntity * ChestEntity = new cChestEntity(0,0,0, a_World);
+ if (!ChestEntity->LoadFromJson( Chest ) )
+ {
+ LOGERROR("ERROR READING CHEST FROM JSON!" );
+ delete ChestEntity;
+ }
+ else
+ {
+ a_BlockEntities.push_back( ChestEntity );
+ }
+ } // for itr - AllChests[]
+ }
+
+ // Load dispensers
+ Json::Value AllDispensers = a_Value.get("Dispensers", Json::nullValue);
+ if( !AllDispensers.empty() )
+ {
+ for( Json::Value::iterator itr = AllDispensers.begin(); itr != AllDispensers.end(); ++itr )
+ {
+ Json::Value & Dispenser = *itr;
+ cDispenserEntity * DispenserEntity = new cDispenserEntity(0,0,0, a_World);
+ if( !DispenserEntity->LoadFromJson( Dispenser ) )
+ {
+ LOGERROR("ERROR READING DISPENSER FROM JSON!" );
+ delete DispenserEntity;
+ }
+ else
+ {
+ a_BlockEntities.push_back( DispenserEntity );
+ }
+ } // for itr - AllDispensers[]
+ }
+
+ // Load furnaces
+ Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue);
+ if( !AllFurnaces.empty() )
+ {
+ for( Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr )
+ {
+ Json::Value & Furnace = *itr;
+ // TODO: The block type and meta aren't correct, there's no way to get them here
+ cFurnaceEntity * FurnaceEntity = new cFurnaceEntity(0, 0, 0, E_BLOCK_FURNACE, 0, a_World);
+ if (!FurnaceEntity->LoadFromJson(Furnace))
+ {
+ LOGERROR("ERROR READING FURNACE FROM JSON!" );
+ delete FurnaceEntity;
+ }
+ else
+ {
+ a_BlockEntities.push_back(FurnaceEntity);
+ }
+ } // for itr - AllFurnaces[]
+ }
+
+ // Load signs
+ Json::Value AllSigns = a_Value.get("Signs", Json::nullValue);
+ if( !AllSigns.empty() )
+ {
+ for( Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr )
+ {
+ Json::Value & Sign = *itr;
+ cSignEntity * SignEntity = new cSignEntity( E_BLOCK_SIGN_POST, 0,0,0, a_World);
+ if ( !SignEntity->LoadFromJson( Sign ) )
+ {
+ LOGERROR("ERROR READING SIGN FROM JSON!" );
+ delete SignEntity;
+ }
+ else
+ {
+ a_BlockEntities.push_back( SignEntity );
+ }
+ } // for itr - AllSigns[]
+ }
+
+ // Load note blocks
+ Json::Value AllNotes = a_Value.get("Notes", Json::nullValue);
+ if( !AllNotes.empty() )
+ {
+ for( Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr )
+ {
+ Json::Value & Note = *itr;
+ cNoteEntity * NoteEntity = new cNoteEntity(0, 0, 0, a_World);
+ if ( !NoteEntity->LoadFromJson( Note ) )
+ {
+ LOGERROR("ERROR READING NOTE BLOCK FROM JSON!" );
+ delete NoteEntity;
+ }
+ else
+ {
+ a_BlockEntities.push_back( NoteEntity );
+ }
+ } // for itr - AllNotes[]
+ }
+
+ // Load jukeboxes
+ Json::Value AllJukeboxes = a_Value.get("Jukeboxes", Json::nullValue);
+ if( !AllJukeboxes.empty() )
+ {
+ for( Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr )
+ {
+ Json::Value & Jukebox = *itr;
+ cJukeboxEntity * JukeboxEntity = new cJukeboxEntity(0, 0, 0, a_World);
+ if ( !JukeboxEntity->LoadFromJson( Jukebox ) )
+ {
+ LOGERROR("ERROR READING JUKEBOX FROM JSON!" );
+ delete JukeboxEntity;
+ }
+ else
+ {
+ a_BlockEntities.push_back( JukeboxEntity );
+ }
+ } // for itr - AllJukeboxes[]
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWSSCompact::cPAKFile
+
+#define READ(Var) \
+ if (f.Read(&Var, sizeof(Var)) != sizeof(Var)) \
+ { \
+ LOGERROR("ERROR READING %s FROM FILE %s (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \
+ return; \
+ }
+
+cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ) :
+ m_FileName(a_FileName),
+ m_LayerX(a_LayerX),
+ m_LayerZ(a_LayerZ),
+ m_NumDirty(0),
+ m_ChunkVersion( CHUNK_VERSION ), // Init with latest version
+ m_PakVersion( PAK_VERSION )
+{
+ cFile f;
+ if (!f.Open(m_FileName, cFile::fmRead))
+ {
+ return;
+ }
+
+ // Read headers:
+ READ(m_PakVersion);
+ if (m_PakVersion != 1)
+ {
+ LOGERROR("File \"%s\" is in an unknown pak format (%d)", m_FileName.c_str(), m_PakVersion);
+ return;
+ }
+
+ READ(m_ChunkVersion);
+ switch( m_ChunkVersion )
+ {
+ case 1:
+ m_ChunkSize.Set(16, 128, 16);
+ break;
+ case 2:
+ case 3:
+ m_ChunkSize.Set(16, 256, 16);
+ break;
+ default:
+ LOGERROR("File \"%s\" is in an unknown chunk format (%d)", m_FileName.c_str(), m_ChunkVersion);
+ return;
+ };
+
+ short NumChunks = 0;
+ READ(NumChunks);
+
+ // Read chunk headers:
+ for (int i = 0; i < NumChunks; i++)
+ {
+ sChunkHeader * Header = new sChunkHeader;
+ READ(*Header);
+ m_ChunkHeaders.push_back(Header);
+ } // for i - chunk headers
+
+ // Read chunk data:
+ if (f.ReadRestOfFile(m_DataContents) == -1)
+ {
+ LOGERROR("Cannot read file \"%s\" contents", m_FileName.c_str());
+ return;
+ }
+
+ if( m_ChunkVersion == 1 ) // Convert chunks to version 2
+ {
+ UpdateChunk1To2();
+ }
+#if AXIS_ORDER == AXIS_ORDER_XZY
+ if( m_ChunkVersion == 2 ) // Convert chunks to version 3
+ {
+ UpdateChunk2To3();
+ }
+#endif
+}
+
+
+
+
+
+cWSSCompact::cPAKFile::~cPAKFile()
+{
+ if (m_NumDirty > 0)
+ {
+ SynchronizeFile();
+ }
+ for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
+ {
+ delete *itr;
+ }
+}
+
+
+
+
+
+bool cWSSCompact::cPAKFile::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data)
+{
+ int ChunkX = a_Chunk.m_ChunkX;
+ int ChunkZ = a_Chunk.m_ChunkZ;
+ sChunkHeader * Header = NULL;
+ int Offset = 0;
+ for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
+ {
+ if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ))
+ {
+ Header = *itr;
+ break;
+ }
+ Offset += (*itr)->m_CompressedSize;
+ }
+ if ((Header == NULL) || (Offset + Header->m_CompressedSize > (int)m_DataContents.size()))
+ {
+ // Chunk not found / data invalid
+ return false;
+ }
+
+ a_UncompressedSize = Header->m_UncompressedSize;
+ a_Data.assign(m_DataContents, Offset, Header->m_CompressedSize);
+ return true;
+}
+
+
+
+
+
+bool cWSSCompact::cPAKFile::SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World)
+{
+ if (!SaveChunkToData(a_Chunk, a_World))
+ {
+ return false;
+ }
+ if (m_NumDirty > MAX_DIRTY_CHUNKS)
+ {
+ SynchronizeFile();
+ }
+ return true;
+}
+
+
+
+
+
+void cWSSCompact::cPAKFile::UpdateChunk1To2()
+{
+ int Offset = 0;
+ AString NewDataContents;
+ int ChunksConverted = 0;
+ for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
+ {
+ sChunkHeader * Header = *itr;
+
+ if( ChunksConverted % 32 == 0 )
+ {
+ LOGINFO("Updating \"%s\" version 1 to version 2: %d %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() );
+ }
+ ChunksConverted++;
+
+ AString Data;
+ int UncompressedSize = Header->m_UncompressedSize;
+ Data.assign(m_DataContents, Offset, Header->m_CompressedSize);
+ Offset += Header->m_CompressedSize;
+
+ // Crude data integrity check:
+ int ExpectedSize = (16*128*16)*2 + (16*128*16)/2; // For version 1
+ if (UncompressedSize < ExpectedSize)
+ {
+ LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing",
+ Header->m_ChunkX, Header->m_ChunkZ,
+ UncompressedSize, ExpectedSize
+ );
+ Offset += Header->m_CompressedSize;
+ continue;
+ }
+
+ // Decompress the data:
+ AString UncompressedData;
+ {
+ int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, UncompressedSize);
+ if (errorcode != Z_OK)
+ {
+ LOGERROR("Error %d decompressing data for chunk [%d, %d]",
+ errorcode,
+ Header->m_ChunkX, Header->m_ChunkZ
+ );
+ Offset += Header->m_CompressedSize;
+ continue;
+ }
+ }
+
+ if (UncompressedSize != (int)UncompressedData.size())
+ {
+ LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]",
+ UncompressedSize, UncompressedData.size(),
+ Header->m_ChunkX, Header->m_ChunkZ
+ );
+ Offset += Header->m_CompressedSize;
+ continue;
+ }
+
+
+ // Old version is 128 blocks high with YZX axis order
+ char ConvertedData[cChunkDef::BlockDataSize];
+ int Index = 0;
+ unsigned int InChunkOffset = 0;
+ for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z )
+ {
+ for( int y = 0; y < 128; ++y )
+ {
+ ConvertedData[Index++] = UncompressedData[y + z * 128 + x * 128 * 16 + InChunkOffset];
+ }
+ // Add 128 empty blocks after an old y column
+ memset(ConvertedData + Index, E_BLOCK_AIR, 128);
+ Index += 128;
+ }
+ InChunkOffset += (16 * 128 * 16);
+ for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Metadata
+ {
+ for( int y = 0; y < 64; ++y )
+ {
+ ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset];
+ }
+ memset(ConvertedData + Index, 0, 64);
+ Index += 64;
+ }
+ InChunkOffset += (16 * 128 * 16) / 2;
+ for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Block light
+ {
+ for( int y = 0; y < 64; ++y )
+ {
+ ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset];
+ }
+ memset(ConvertedData + Index, 0, 64);
+ Index += 64;
+ }
+ InChunkOffset += (16*128*16)/2;
+ for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Sky light
+ {
+ for( int y = 0; y < 64; ++y )
+ {
+ ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset];
+ }
+ memset(ConvertedData + Index, 0, 64);
+ Index += 64;
+ }
+ InChunkOffset += (16 * 128 * 16) / 2;
+
+ AString Converted(ConvertedData, ARRAYCOUNT(ConvertedData));
+
+ // Add JSON data afterwards
+ if (UncompressedData.size() > InChunkOffset)
+ {
+ Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end() );
+ }
+
+ // Re-compress data
+ AString CompressedData;
+ {
+ int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData);
+ if (errorcode != Z_OK)
+ {
+ LOGERROR("Error %d compressing data for chunk [%d, %d]",
+ errorcode,
+ Header->m_ChunkX, Header->m_ChunkZ
+ );
+ continue;
+ }
+ }
+
+ // Save into file's cache
+ Header->m_UncompressedSize = Converted.size();
+ Header->m_CompressedSize = CompressedData.size();
+ NewDataContents.append( CompressedData );
+ }
+
+ // Done converting
+ m_DataContents = NewDataContents;
+ m_ChunkVersion = 2;
+ SynchronizeFile();
+
+ LOGINFO("Updated \"%s\" version 1 to version 2", m_FileName.c_str() );
+}
+
+
+
+
+
+void cWSSCompact::cPAKFile::UpdateChunk2To3()
+{
+ int Offset = 0;
+ AString NewDataContents;
+ int ChunksConverted = 0;
+ for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
+ {
+ sChunkHeader * Header = *itr;
+
+ if( ChunksConverted % 32 == 0 )
+ {
+ LOGINFO("Updating \"%s\" version 2 to version 3: %d %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() );
+ }
+ ChunksConverted++;
+
+ AString Data;
+ int UncompressedSize = Header->m_UncompressedSize;
+ Data.assign(m_DataContents, Offset, Header->m_CompressedSize);
+ Offset += Header->m_CompressedSize;
+
+ // Crude data integrity check:
+ const int ExpectedSize = (16*256*16)*2 + (16*256*16)/2; // For version 2
+ if (UncompressedSize < ExpectedSize)
+ {
+ LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing",
+ Header->m_ChunkX, Header->m_ChunkZ,
+ UncompressedSize, ExpectedSize
+ );
+ Offset += Header->m_CompressedSize;
+ continue;
+ }
+
+ // Decompress the data:
+ AString UncompressedData;
+ {
+ int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, UncompressedSize);
+ if (errorcode != Z_OK)
+ {
+ LOGERROR("Error %d decompressing data for chunk [%d, %d]",
+ errorcode,
+ Header->m_ChunkX, Header->m_ChunkZ
+ );
+ Offset += Header->m_CompressedSize;
+ continue;
+ }
+ }
+
+ if (UncompressedSize != (int)UncompressedData.size())
+ {
+ LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]",
+ UncompressedSize, UncompressedData.size(),
+ Header->m_ChunkX, Header->m_ChunkZ
+ );
+ Offset += Header->m_CompressedSize;
+ continue;
+ }
+
+ char ConvertedData[ExpectedSize];
+ memset(ConvertedData, 0, ExpectedSize);
+
+ // Cannot use cChunk::MakeIndex because it might change again?????????
+ // For compatibility, use what we know is current
+ #define MAKE_2_INDEX( x, y, z ) ( y + (z * 256) + (x * 256 * 16) )
+ #define MAKE_3_INDEX( x, y, z ) ( x + (z * 16) + (y * 16 * 16) )
+
+ unsigned int InChunkOffset = 0;
+ for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) // YZX Loop order is important, in 1.1 Y was first then Z then X
+ {
+ ConvertedData[ MAKE_3_INDEX(x, y, z) ] = UncompressedData[InChunkOffset];
+ ++InChunkOffset;
+ } // for y, z, x
+
+
+ unsigned int index2 = 0;
+ for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y )
+ {
+ ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
+ ++index2;
+ }
+ InChunkOffset += index2 / 2;
+ index2 = 0;
+
+ for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y )
+ {
+ ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
+ ++index2;
+ }
+ InChunkOffset += index2 / 2;
+ index2 = 0;
+
+ for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y )
+ {
+ ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
+ ++index2;
+ }
+ InChunkOffset += index2 / 2;
+ index2 = 0;
+
+ AString Converted(ConvertedData, ExpectedSize);
+
+ // Add JSON data afterwards
+ if (UncompressedData.size() > InChunkOffset)
+ {
+ Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end() );
+ }
+
+ // Re-compress data
+ AString CompressedData;
+ {
+ int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData);
+ if (errorcode != Z_OK)
+ {
+ LOGERROR("Error %d compressing data for chunk [%d, %d]",
+ errorcode,
+ Header->m_ChunkX, Header->m_ChunkZ
+ );
+ continue;
+ }
+ }
+
+ // Save into file's cache
+ Header->m_UncompressedSize = Converted.size();
+ Header->m_CompressedSize = CompressedData.size();
+ NewDataContents.append( CompressedData );
+ }
+
+ // Done converting
+ m_DataContents = NewDataContents;
+ m_ChunkVersion = 3;
+ SynchronizeFile();
+
+ LOGINFO("Updated \"%s\" version 2 to version 3", m_FileName.c_str() );
+}
+
+
+
+
+
+bool cWSSCompact::LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, const AString & a_Data, cWorld * a_World)
+{
+ // Crude data integrity check:
+ if (a_UncompressedSize < cChunkDef::BlockDataSize)
+ {
+ LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing",
+ a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
+ a_UncompressedSize, cChunkDef::BlockDataSize
+ );
+ EraseChunkData(a_Chunk);
+ return false;
+ }
+
+ // Decompress the data:
+ AString UncompressedData;
+ int errorcode = UncompressString(a_Data.data(), a_Data.size(), UncompressedData, a_UncompressedSize);
+ if (errorcode != Z_OK)
+ {
+ LOGERROR("Error %d decompressing data for chunk [%d, %d]",
+ errorcode,
+ a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
+ );
+ return false;
+ }
+
+ if (a_UncompressedSize != (int)UncompressedData.size())
+ {
+ LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]",
+ a_UncompressedSize, UncompressedData.size(),
+ a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
+ );
+ return false;
+ }
+
+ cEntityList Entities;
+ cBlockEntityList BlockEntities;
+ bool IsLightValid = false;
+
+ if (a_UncompressedSize > cChunkDef::BlockDataSize)
+ {
+ Json::Value root; // will contain the root value after parsing.
+ Json::Reader reader;
+ if ( !reader.parse( UncompressedData.data() + cChunkDef::BlockDataSize, root, false ) )
+ {
+ LOGERROR("Failed to parse trailing JSON in chunk [%d, %d]!",
+ a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
+ );
+ }
+ else
+ {
+ LoadEntitiesFromJson(root, Entities, BlockEntities, a_World);
+ IsLightValid = root.get("IsLightValid", false).asBool();
+ }
+ }
+
+ BLOCKTYPE * BlockData = (BLOCKTYPE *)UncompressedData.data();
+ NIBBLETYPE * MetaData = (NIBBLETYPE *)(BlockData + cChunkDef::MetaOffset);
+ NIBBLETYPE * BlockLight = (NIBBLETYPE *)(BlockData + cChunkDef::LightOffset);
+ NIBBLETYPE * SkyLight = (NIBBLETYPE *)(BlockData + cChunkDef::SkyLightOffset);
+
+ a_World->SetChunkData(
+ a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
+ BlockData, MetaData,
+ IsLightValid ? BlockLight : NULL,
+ IsLightValid ? SkyLight : NULL,
+ NULL, NULL,
+ Entities, BlockEntities,
+ false
+ );
+
+ return true;
+}
+
+
+
+
+
+bool cWSSCompact::cPAKFile::EraseChunkData(const cChunkCoords & a_Chunk)
+{
+ int ChunkX = a_Chunk.m_ChunkX;
+ int ChunkZ = a_Chunk.m_ChunkZ;
+ int Offset = 0;
+ for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
+ {
+ if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ))
+ {
+ m_DataContents.erase(Offset, (*itr)->m_CompressedSize);
+ delete *itr;
+ itr = m_ChunkHeaders.erase(itr);
+ return true;
+ }
+ Offset += (*itr)->m_CompressedSize;
+ }
+
+ return false;
+}
+
+
+
+
+
+bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World)
+{
+ // Serialize the chunk:
+ cJsonChunkSerializer Serializer;
+ if (!a_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer))
+ {
+ // Chunk not valid
+ LOG("cWSSCompact: Trying to save chunk [%d, %d, %d] that has no data, ignoring request.", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ);
+ return false;
+ }
+
+ AString Data;
+ Data.assign((const char *)Serializer.GetBlockData(), cChunkDef::BlockDataSize);
+ if (Serializer.HasJsonData())
+ {
+ AString JsonData;
+ Json::StyledWriter writer;
+ JsonData = writer.write(Serializer.GetRoot());
+ Data.append(JsonData);
+ }
+
+ // Compress the data:
+ AString CompressedData;
+ int errorcode = CompressString(Data.data(), Data.size(), CompressedData);
+ if ( errorcode != Z_OK )
+ {
+ LOGERROR("Error %i compressing data for chunk [%d, %d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ);
+ return false;
+ }
+
+ // Erase any existing data for the chunk:
+ EraseChunkData(a_Chunk);
+
+ // Save the header:
+ sChunkHeader * Header = new sChunkHeader;
+ if (Header == NULL)
+ {
+ LOGWARNING("Cannot create a new chunk header to save chunk [%d, %d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ);
+ return false;
+ }
+ Header->m_CompressedSize = (int)CompressedData.size();
+ Header->m_ChunkX = a_Chunk.m_ChunkX;
+ Header->m_ChunkZ = a_Chunk.m_ChunkZ;
+ Header->m_UncompressedSize = (int)Data.size();
+ m_ChunkHeaders.push_back(Header);
+
+ m_DataContents.append(CompressedData.data(), CompressedData.size());
+
+ m_NumDirty++;
+ return true;
+}
+
+
+
+
+
+#define WRITE(Var) \
+ if (f.Write(&Var, sizeof(Var)) != sizeof(Var)) \
+ { \
+ LOGERROR("cWSSCompact: ERROR writing %s to file \"%s\" (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \
+ return; \
+ }
+
+void cWSSCompact::cPAKFile::SynchronizeFile(void)
+{
+ cFile f;
+ if (!f.Open(m_FileName, cFile::fmWrite))
+ {
+ LOGERROR("Cannot open PAK file \"%s\" for writing", m_FileName.c_str());
+ return;
+ }
+
+ WRITE(m_PakVersion);
+ WRITE(m_ChunkVersion);
+ short NumChunks = (short)m_ChunkHeaders.size();
+ WRITE(NumChunks);
+ for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
+ {
+ WRITE(**itr);
+ }
+ if (f.Write(m_DataContents.data(), m_DataContents.size()) != (int)m_DataContents.size())
+ {
+ LOGERROR("cWSSCompact: ERROR writing chunk contents to file \"%s\" (line %d); file offset %d", m_FileName.c_str(), __LINE__, f.Tell());
+ return;
+ }
+ m_NumDirty = 0;
+}
+
+
+
+
diff --git a/src/WorldStorage/WSSCompact.h b/src/WorldStorage/WSSCompact.h
new file mode 100644
index 000000000..e6a013eaf
--- /dev/null
+++ b/src/WorldStorage/WSSCompact.h
@@ -0,0 +1,144 @@
+
+// WSSCompact.h
+
+// Interfaces to the cWSSCompact class representing the "Compact" storage schema (PAK-files)
+
+
+
+
+
+#pragma once
+#ifndef WSSCOMPACT_H_INCLUDED
+#define WSSCOMPACT_H_INCLUDED
+
+#include "WorldStorage.h"
+#include "../Vector3i.h"
+
+
+
+
+
+/// Helper class for serializing a chunk into Json
+class cJsonChunkSerializer :
+ public cChunkDataCollector
+{
+public:
+
+ cJsonChunkSerializer(void);
+
+ Json::Value & GetRoot (void) {return m_Root; }
+ BLOCKTYPE * GetBlockData(void) {return (BLOCKTYPE *)m_BlockData; }
+ bool HasJsonData (void) const {return m_HasJsonData; }
+
+protected:
+
+ // NOTE: block data is serialized into inherited cChunkDataCollector's m_BlockData[] array
+
+ // Entities and BlockEntities are serialized to Json
+ Json::Value m_Root;
+ bool m_HasJsonData;
+
+ // cChunkDataCollector overrides:
+ virtual void Entity (cEntity * a_Entity) override;
+ virtual void BlockEntity (cBlockEntity * a_Entity) override;
+ virtual bool LightIsValid (bool a_IsLightValid) override;
+} ;
+
+
+
+
+
+class cWSSCompact :
+ public cWSSchema
+{
+public:
+ cWSSCompact(cWorld * a_World) : cWSSchema(a_World) {}
+ virtual ~cWSSCompact();
+
+protected:
+
+ struct sChunkHeader;
+ typedef std::vector<sChunkHeader *> sChunkHeaders;
+
+ /// Implements a cache for a single PAK file; implements lazy-write in order to be able to write multiple chunks fast
+ class cPAKFile
+ {
+ public:
+
+ cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ);
+ ~cPAKFile();
+
+ bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data);
+ bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data);
+ bool EraseChunkData(const cChunkCoords & a_Chunk);
+
+ bool SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World);
+
+ int GetLayerX(void) const {return m_LayerX; }
+ int GetLayerZ(void) const {return m_LayerZ; }
+
+ static const int PAK_VERSION = 1;
+#if AXIS_ORDER == AXIS_ORDER_XZY
+ static const int CHUNK_VERSION = 3;
+#elif AXIS_ORDER == AXIS_ORDER_YZX
+ static const int CHUNK_VERSION = 2;
+#endif
+ protected:
+
+ AString m_FileName;
+ int m_LayerX;
+ int m_LayerZ;
+
+ sChunkHeaders m_ChunkHeaders;
+ AString m_DataContents; // Data contents of the file, cached
+
+ int m_NumDirty; // Number of chunks that were written into m_DataContents but not into the file
+
+ Vector3i m_ChunkSize; // Is related to m_ChunkVersion
+ char m_ChunkVersion;
+ char m_PakVersion;
+
+ bool SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World); // Saves the chunk to m_DataContents, updates headers and m_NumDirty
+ void SynchronizeFile(void); // Writes m_DataContents along with the headers to file, resets m_NumDirty
+
+ void UpdateChunk1To2(void); // Height from 128 to 256
+ void UpdateChunk2To3(void); // Axis order from YZX to XZY
+ } ;
+
+ typedef std::list<cPAKFile *> cPAKFiles;
+
+ cCriticalSection m_CS;
+ cPAKFiles m_PAKFiles; // A MRU cache of PAK files
+
+ /// Loads the correct PAK file either from cache or from disk, manages the m_PAKFiles cache
+ cPAKFile * LoadPAKFile(const cChunkCoords & a_Chunk);
+
+ /// Gets chunk data from the correct file; locks CS as needed
+ bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data);
+
+ /// Sets chunk data to the correct file; locks CS as needed
+ bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data);
+
+ /// Erases chunk data from the correct file; locks CS as needed
+ bool EraseChunkData(const cChunkCoords & a_Chunk);
+
+ /// Loads the chunk from the data (no locking needed)
+ bool LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, const AString & a_Data, cWorld * a_World);
+
+ void LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World);
+
+ // cWSSchema overrides:
+ virtual bool LoadChunk(const cChunkCoords & a_Chunk) override;
+ virtual bool SaveChunk(const cChunkCoords & a_Chunk) override;
+ virtual const AString GetName(void) const override {return "compact"; }
+} ;
+
+
+
+
+
+#endif // WSSCOMPACT_H_INCLUDED
+
+
+
+
diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp
new file mode 100644
index 000000000..f290ec128
--- /dev/null
+++ b/src/WorldStorage/WorldStorage.cpp
@@ -0,0 +1,409 @@
+
+// WorldStorage.cpp
+
+// Implements the cWorldStorage class representing the chunk loading / saving thread
+
+// To add a new storage schema, implement a cWSSchema descendant and add it to cWorldStorage::InitSchemas()
+
+#include "Globals.h"
+#include "WorldStorage.h"
+#include "WSSCompact.h"
+#include "WSSAnvil.h"
+#include "../World.h"
+#include "../Generating/ChunkGenerator.h"
+#include "../Entities/Entity.h"
+#include "../BlockEntities/BlockEntity.h"
+
+
+
+
+
+/// If a chunk with this Y coord is de-queued, it is a signal to emit the saved-all message (cWorldStorage::QueueSavedMessage())
+#define CHUNK_Y_MESSAGE 2
+
+
+
+
+
+/// Example storage schema - forgets all chunks ;)
+class cWSSForgetful :
+ public cWSSchema
+{
+public:
+ cWSSForgetful(cWorld * a_World) : cWSSchema(a_World) {}
+
+protected:
+ // cWSSchema overrides:
+ virtual bool LoadChunk(const cChunkCoords & a_Chunk) override {return false; }
+ virtual bool SaveChunk(const cChunkCoords & a_Chunk) override {return true; }
+ virtual const AString GetName(void) const override {return "forgetful"; }
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorldStorage:
+
+cWorldStorage::cWorldStorage(void) :
+ super("cWorldStorage"),
+ m_World(NULL),
+ m_SaveSchema(NULL)
+{
+}
+
+
+
+
+
+cWorldStorage::~cWorldStorage()
+{
+ for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr)
+ {
+ delete *itr;
+ } // for itr - m_Schemas[]
+ m_LoadQueue.clear();
+ m_SaveQueue.clear();
+}
+
+
+
+
+
+bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName)
+{
+ m_World = a_World;
+ m_StorageSchemaName = a_StorageSchemaName;
+ InitSchemas();
+
+ return super::Start();
+}
+
+
+
+
+
+void cWorldStorage::Stop(void)
+{
+ WaitForFinish();
+}
+
+
+
+
+
+void cWorldStorage::WaitForFinish(void)
+{
+ LOG("Waiting for the world storage to finish saving");
+
+ {
+ // Cancel all loading requests:
+ cCSLock Lock(m_CSQueues);
+ m_LoadQueue.clear();
+ }
+
+ // Wait for the saving to finish:
+ WaitForQueuesEmpty();
+
+ // Wait for the thread to finish:
+ m_ShouldTerminate = true;
+ m_Event.Set();
+ m_evtRemoved.Set(); // Wake up anybody waiting in the WaitForQueuesEmpty() method
+ super::Wait();
+ LOG("World storage thread finished");
+}
+
+
+
+
+
+void cWorldStorage::WaitForQueuesEmpty(void)
+{
+ cCSLock Lock(m_CSQueues);
+ while (!m_ShouldTerminate && (!m_LoadQueue.empty() || !m_SaveQueue.empty()))
+ {
+ cCSUnlock Unlock(Lock);
+ m_evtRemoved.Wait();
+ }
+}
+
+
+
+
+
+int cWorldStorage::GetLoadQueueLength(void)
+{
+ cCSLock Lock(m_CSQueues);
+ return (int)m_LoadQueue.size();
+}
+
+
+
+
+
+int cWorldStorage::GetSaveQueueLength(void)
+{
+ cCSLock Lock(m_CSQueues);
+ return (int)m_SaveQueue.size();
+}
+
+
+
+
+
+void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate)
+{
+ // Queues the chunk for loading; if not loaded, the chunk will be generated
+ {
+ cCSLock Lock(m_CSQueues);
+
+ // Check if already in the queue:
+ for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr)
+ {
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_Generate == a_Generate))
+ {
+ return;
+ }
+ }
+ m_LoadQueue.push_back(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, a_Generate));
+ }
+
+ m_Event.Set();
+}
+
+
+
+
+
+void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ {
+ cCSLock Lock(m_CSQueues);
+ m_SaveQueue.remove (cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); // Don't add twice
+ m_SaveQueue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
+ }
+ m_Event.Set();
+}
+
+
+
+
+
+void cWorldStorage::QueueSavedMessage(void)
+{
+ // Pushes a special coord pair into the queue, signalizing a message instead:
+ {
+ cCSLock Lock(m_CSQueues);
+ m_SaveQueue.push_back(cChunkCoords(0, CHUNK_Y_MESSAGE, 0));
+ }
+ m_Event.Set();
+}
+
+
+
+
+
+void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSQueues);
+ for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr)
+ {
+ if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkY != a_ChunkY) || (itr->m_ChunkZ != a_ChunkZ))
+ {
+ continue;
+ }
+ m_LoadQueue.erase(itr);
+ Lock.Unlock();
+ m_evtRemoved.Set();
+ return;
+ } // for itr - m_LoadQueue[]
+}
+
+
+
+
+
+void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk)
+{
+ {
+ cCSLock Lock(m_CSQueues);
+ m_SaveQueue.remove(a_Chunk);
+ }
+ m_evtRemoved.Set();
+}
+
+
+
+
+
+void cWorldStorage::InitSchemas(void)
+{
+ // The first schema added is considered the default
+ m_Schemas.push_back(new cWSSAnvil (m_World));
+ m_Schemas.push_back(new cWSSCompact (m_World));
+ m_Schemas.push_back(new cWSSForgetful(m_World));
+ // Add new schemas here
+
+ if (NoCaseCompare(m_StorageSchemaName, "default") == 0)
+ {
+ m_SaveSchema = m_Schemas.front();
+ return;
+ }
+ for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr)
+ {
+ if (NoCaseCompare((*itr)->GetName(), m_StorageSchemaName) == 0)
+ {
+ m_SaveSchema = *itr;
+ return;
+ }
+ } // for itr - m_Schemas[]
+
+ // Unknown schema selected, let the admin know:
+ LOGWARNING("Unknown storage schema name \"%s\". Using default (\"%s\"). Available schemas:",
+ m_StorageSchemaName.c_str(), m_SaveSchema->GetName().c_str()
+ );
+ for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr)
+ {
+ LOGWARNING("\t\"%s\"", (*itr)->GetName().c_str());
+ }
+ m_SaveSchema = m_Schemas.front();
+}
+
+
+
+
+
+void cWorldStorage::Execute(void)
+{
+ while (!m_ShouldTerminate)
+ {
+ m_Event.Wait();
+
+ // Process both queues until they are empty again:
+ bool HasMore;
+ do
+ {
+ HasMore = false;
+ if (m_ShouldTerminate)
+ {
+ return;
+ }
+
+ HasMore = LoadOneChunk();
+ HasMore = HasMore | SaveOneChunk();
+ m_evtRemoved.Set();
+ } while (HasMore);
+ }
+}
+
+
+
+
+
+bool cWorldStorage::LoadOneChunk(void)
+{
+ sChunkLoad ToLoad(0, 0, 0, false);
+ bool HasMore;
+ bool ShouldLoad = false;
+ {
+ cCSLock Lock(m_CSQueues);
+ if (!m_LoadQueue.empty())
+ {
+ ToLoad = m_LoadQueue.front();
+ m_LoadQueue.pop_front();
+ ShouldLoad = true;
+ }
+ HasMore = !m_LoadQueue.empty();
+ }
+
+ if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ))
+ {
+ if (ToLoad.m_Generate)
+ {
+ // The chunk couldn't be loaded, generate it:
+ m_World->GetGenerator().QueueGenerateChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ);
+ }
+ else
+ {
+ // TODO: Notify the world that the load has failed:
+ // m_World->ChunkLoadFailed(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ);
+ }
+ }
+ return HasMore;
+}
+
+
+
+
+
+bool cWorldStorage::SaveOneChunk(void)
+{
+ cChunkCoords Save(0, 0, 0);
+ bool HasMore;
+ bool ShouldSave = false;
+ {
+ cCSLock Lock(m_CSQueues);
+ if (!m_SaveQueue.empty())
+ {
+ Save = m_SaveQueue.front();
+ m_SaveQueue.pop_front();
+ ShouldSave = true;
+ }
+ HasMore = !m_SaveQueue.empty();
+ }
+ if (Save.m_ChunkY == CHUNK_Y_MESSAGE)
+ {
+ LOGINFO("Saved all chunks in world %s", m_World->GetName().c_str());
+ return HasMore;
+ }
+ if (ShouldSave && m_World->IsChunkValid(Save.m_ChunkX, Save.m_ChunkZ))
+ {
+ m_World->MarkChunkSaving(Save.m_ChunkX, Save.m_ChunkZ);
+ if (m_SaveSchema->SaveChunk(Save))
+ {
+ m_World->MarkChunkSaved(Save.m_ChunkX, Save.m_ChunkZ);
+ }
+ }
+ return HasMore;
+}
+
+
+
+
+
+bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ if (m_World->IsChunkValid(a_ChunkX, a_ChunkZ))
+ {
+ // Already loaded (can happen, since the queue is async)
+ return true;
+ }
+
+ cChunkCoords Coords(a_ChunkX, a_ChunkY, a_ChunkZ);
+
+ // First try the schema that is used for saving
+ if (m_SaveSchema->LoadChunk(Coords))
+ {
+ return true;
+ }
+
+ // If it didn't have the chunk, try all the other schemas:
+ for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr)
+ {
+ if (((*itr) != m_SaveSchema) && (*itr)->LoadChunk(Coords))
+ {
+ return true;
+ }
+ }
+
+ // Notify the chunk owner that the chunk failed to load (sets cChunk::m_HasLoadFailed to true):
+ m_World->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ);
+
+ return false;
+}
+
+
+
+
+
diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h
new file mode 100644
index 000000000..b2c6dfb4b
--- /dev/null
+++ b/src/WorldStorage/WorldStorage.h
@@ -0,0 +1,135 @@
+
+// WorldStorage.h
+
+// Interfaces to the cWorldStorage class representing the chunk loading / saving thread
+// This class decides which storage schema to use for saving; it queries all available schemas for loading
+// Also declares the base class for all storage schemas, cWSSchema
+// Helper serialization class cJsonChunkSerializer is declared as well
+
+
+
+
+
+#pragma once
+#ifndef WORLDSTORAGE_H_INCLUDED
+#define WORLDSTORAGE_H_INCLUDED
+
+#include "../ChunkDef.h"
+#include "../OSSupport/IsThread.h"
+#include "lib/jsoncpp/include/json/json.h"
+
+
+
+
+
+// fwd:
+class cWorld;
+
+
+
+
+
+/// Interface that all the world storage schemas need to implement
+class cWSSchema abstract
+{
+public:
+ cWSSchema(cWorld * a_World) : m_World(a_World) {}
+ virtual ~cWSSchema() {} // Force the descendants' destructors to be virtual
+
+ virtual bool LoadChunk(const cChunkCoords & a_Chunk) = 0;
+ virtual bool SaveChunk(const cChunkCoords & a_Chunk) = 0;
+ virtual const AString GetName(void) const = 0;
+
+protected:
+
+ cWorld * m_World;
+} ;
+
+typedef std::list<cWSSchema *> cWSSchemaList;
+
+
+
+
+
+/// The actual world storage class
+class cWorldStorage :
+ public cIsThread
+{
+ typedef cIsThread super;
+
+public:
+
+ cWorldStorage(void);
+ ~cWorldStorage();
+
+ void QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate); // Queues the chunk for loading; if not loaded, the chunk will be generated if a_Generate is true
+ void QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Signals that a message should be output to the console when all the chunks have been saved
+ void QueueSavedMessage(void);
+
+ /// Loads the chunk specified; returns true on success, false on failure
+ bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ void UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ void UnqueueSave(const cChunkCoords & a_Chunk);
+
+ bool Start(cWorld * a_World, const AString & a_StorageSchemaName); // Hide the cIsThread's Start() method, we need to provide args
+ void Stop(void); // Hide the cIsThread's Stop() method, we need to signal the event
+ void WaitForFinish(void);
+ void WaitForQueuesEmpty(void);
+
+ int GetLoadQueueLength(void);
+ int GetSaveQueueLength(void);
+
+protected:
+
+ struct sChunkLoad
+ {
+ int m_ChunkX;
+ int m_ChunkY;
+ int m_ChunkZ;
+ bool m_Generate; // If true, the chunk will be generated if it cannot be loaded
+
+ sChunkLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ), m_Generate(a_Generate) {}
+ } ;
+
+ typedef std::list<sChunkLoad> sChunkLoadQueue;
+
+ cWorld * m_World;
+ AString m_StorageSchemaName;
+
+ // Both queues are locked by the same CS
+ cCriticalSection m_CSQueues;
+ sChunkLoadQueue m_LoadQueue;
+ cChunkCoordsList m_SaveQueue;
+
+ cEvent m_Event; // Set when there's any addition to the queues
+ cEvent m_evtRemoved; // Set when an item has been removed from the queue, either by the worker thread or the Unqueue methods
+
+ /// All the storage schemas (all used for loading)
+ cWSSchemaList m_Schemas;
+
+ /// The one storage schema used for saving
+ cWSSchema * m_SaveSchema;
+
+ void InitSchemas(void);
+
+ virtual void Execute(void) override;
+
+ /// Loads one chunk from the queue (if any queued); returns true if there are more chunks in the load queue
+ bool LoadOneChunk(void);
+
+ /// Saves one chunk from the queue (if any queued); returns true if there are more chunks in the save queue
+ bool SaveOneChunk(void);
+} ;
+
+
+
+
+
+#endif // WORLDSTORAGE_H_INCLUDED
+
+
+
+
diff --git a/src/XMLParser.h b/src/XMLParser.h
new file mode 100644
index 000000000..f492d1a5d
--- /dev/null
+++ b/src/XMLParser.h
@@ -0,0 +1,701 @@
+
+// XMLParser.h
+
+// Interfaces to the CXMLParser class representing the base class for XML parsing
+
+// To use, derive a class from this base and override its OnStartElement(), OnEndElement() and OnCharacters() functions
+
+
+
+
+
+#pragma once
+
+#include "expat/expat.h"
+
+
+
+
+
+class CXMLParser
+{
+public:
+ CXMLParser(void);
+ virtual ~CXMLParser();
+
+ // The actual parsing, may be called several times; the last time needs iIsFinal == true (-> flush)
+ int Parse(const char * iData, size_t iLength, bool iIsFinal = false);
+
+private:
+ // LibExpat stuff:
+ XML_Parser mParser;
+
+ static void StartElementHandler(void * iContext, const XML_Char * iElement, const XML_Char ** iAttributes)
+ {
+ ((CXMLParser *)iContext)->OnStartElement(iElement, iAttributes);
+ }
+
+ static void EndElementHandler (void * iContext, const XML_Char * iElement)
+ {
+ ((CXMLParser *)iContext)->OnEndElement(iElement);
+ }
+
+ static void CharacterDataHandler (void * iContext, const XML_Char * iData, int iLength)
+ {
+ ((CXMLParser *)iContext)->OnCharacters(iData, iLength);
+ }
+
+protected:
+ virtual void OnStartElement(const XML_Char * iElement, const XML_Char ** iAttributes) = 0;
+ virtual void OnEndElement (const XML_Char * iElement) = 0;
+ virtual void OnCharacters (const XML_Char * iCharacters, int iLength) = 0;
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// The following template has been modified from code available at
+// http://www.codeproject.com/Articles/1847/C-Wrappers-for-the-Expat-XML-Parser
+// It uses templates to remove the virtual function call penalty (both size and speed) for each callback
+
+/* Usage:
+1, Declare a subclass:
+ class CMyParser : public CExpatImpl<CMyParser>
+2, Declare handlers that you want in that subclass:
+ void CMyParser::OnEndElement(const XML_Char * iTagName);
+3, Create an instance of your class:
+ CMyParser Parser;
+4, Call Create():
+ Parser.Create(NULL, NULL);
+4, Call Parse(), repeatedly:
+ Parser.Parse(Buffer, Length);
+*/
+
+template <class _T>
+class CExpatImpl
+{
+
+// @access Constructors and destructors
+public:
+
+ // @cmember General constructor
+
+ CExpatImpl ()
+ {
+ m_p = NULL;
+ }
+
+ // @cmember Destructor
+
+ ~CExpatImpl ()
+ {
+ Destroy ();
+ }
+
+// @access Parser creation and deletion methods
+public:
+
+ // @cmember Create a parser
+
+ bool Create (const XML_Char * pszEncoding = NULL, const XML_Char * pszSep = NULL)
+ {
+ // Destroy the old parser
+ Destroy ();
+
+ // If the encoding or seperator are empty, then NULL
+ if (pszEncoding != NULL && pszEncoding [0] == 0)
+ {
+ pszEncoding = NULL;
+ }
+ if (pszSep != NULL && pszSep [0] == 0)
+ {
+ pszSep = NULL;
+ }
+
+ // Create the new parser
+ m_p = XML_ParserCreate_MM (pszEncoding, NULL, pszSep);
+ if (m_p == NULL)
+ {
+ return false;
+ }
+
+ // Invoke the post create routine
+ _T * pThis = static_cast <_T *> (this);
+ pThis ->OnPostCreate ();
+
+ // Set the user data used in callbacks
+ XML_SetUserData (m_p, (void *) this);
+ return true;
+ }
+
+ // @cmember Destroy the parser
+
+ void Destroy (void)
+ {
+ if (m_p != NULL)
+ {
+ XML_ParserFree (m_p);
+ }
+ m_p = NULL;
+ }
+
+
+ // @cmember Parse a block of data
+
+ bool Parse (const char *pszBuffer, int nLength, bool fIsFinal = true)
+ {
+ assert (m_p != NULL);
+ return XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0;
+ }
+
+ // @cmember Parse internal buffer
+
+ bool ParseBuffer (int nLength, bool fIsFinal = true)
+ {
+ assert (m_p != NULL);
+ return XML_ParseBuffer (m_p, nLength, fIsFinal) != 0;
+ }
+
+ // @cmember Get the internal buffer
+
+ void *GetBuffer (int nLength)
+ {
+ assert (m_p != NULL);
+ return XML_GetBuffer (m_p, nLength);
+ }
+
+
+protected:
+ // Parser callback enable/disable methods:
+
+ // @cmember Enable/Disable the start element handler
+
+ void EnableStartElementHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetStartElementHandler (m_p, fEnable ? StartElementHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the end element handler
+
+ void EnableEndElementHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetEndElementHandler (m_p, fEnable ? EndElementHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the element handlers
+
+ void EnableElementHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ EnableStartElementHandler (fEnable);
+ EnableEndElementHandler (fEnable);
+ }
+
+ // @cmember Enable/Disable the character data handler
+
+ void EnableCharacterDataHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetCharacterDataHandler (m_p, fEnable ? CharacterDataHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the processing instruction handler
+
+ void EnableProcessingInstructionHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetProcessingInstructionHandler (m_p, fEnable ? ProcessingInstructionHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the comment handler
+
+ void EnableCommentHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetCommentHandler (m_p, fEnable ? CommentHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the start CDATA section handler
+
+ void EnableStartCdataSectionHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetStartCdataSectionHandler (m_p, fEnable ? StartCdataSectionHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the end CDATA section handler
+
+ void EnableEndCdataSectionHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetEndCdataSectionHandler (m_p, fEnable ? EndCdataSectionHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the CDATA section handlers
+
+ void EnableCdataSectionHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ EnableStartCdataSectionHandler (fEnable);
+ EnableEndCdataSectionHandler (fEnable);
+ }
+
+ // @cmember Enable/Disable default handler
+
+ void EnableDefaultHandler (bool fEnable = true, bool fExpand = true)
+ {
+ assert (m_p != NULL);
+ if (fExpand)
+ {
+ XML_SetDefaultHandlerExpand (m_p, fEnable ? DefaultHandler : NULL);
+ }
+ else
+ XML_SetDefaultHandler (m_p, fEnable ? DefaultHandler : NULL);
+ }
+
+ // @cmember Enable/Disable external entity ref handler
+
+ void EnableExternalEntityRefHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetExternalEntityRefHandler (m_p, fEnable ? ExternalEntityRefHandler : NULL);
+ }
+
+ // @cmember Enable/Disable unknown encoding handler
+
+ void EnableUnknownEncodingHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetUnknownEncodingHandler (m_p, fEnable ? UnknownEncodingHandler : NULL);
+ }
+
+ // @cmember Enable/Disable start namespace handler
+
+ void EnableStartNamespaceDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetStartNamespaceDeclHandler (m_p, fEnable ? StartNamespaceDeclHandler : NULL);
+ }
+
+ // @cmember Enable/Disable end namespace handler
+
+ void EnableEndNamespaceDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetEndNamespaceDeclHandler (m_p, fEnable ? EndNamespaceDeclHandler : NULL);
+ }
+
+ // @cmember Enable/Disable namespace handlers
+
+ void EnableNamespaceDeclHandler (bool fEnable = true)
+ {
+ EnableStartNamespaceDeclHandler (fEnable);
+ EnableEndNamespaceDeclHandler (fEnable);
+ }
+
+ // @cmember Enable/Disable the XML declaration handler
+
+ void EnableXmlDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetXmlDeclHandler (m_p, fEnable ? XmlDeclHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the start DOCTYPE declaration handler
+
+ void EnableStartDoctypeDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetStartDoctypeDeclHandler (m_p, fEnable ? StartDoctypeDeclHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the end DOCTYPE declaration handler
+
+ void EnableEndDoctypeDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetEndDoctypeDeclHandler (m_p,
+ fEnable ? EndDoctypeDeclHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the DOCTYPE declaration handler
+
+ void EnableDoctypeDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ EnableStartDoctypeDeclHandler (fEnable);
+ EnableEndDoctypeDeclHandler (fEnable);
+ }
+
+public:
+ // Parser error reporting methods
+
+ // @cmember Get last error
+
+ enum XML_Error GetErrorCode ()
+ {
+ assert (m_p != NULL);
+ return XML_GetErrorCode (m_p);
+ }
+
+ // @cmember Get the current byte index
+
+ long GetCurrentByteIndex ()
+ {
+ assert (m_p != NULL);
+ return XML_GetCurrentByteIndex (m_p);
+ }
+
+ // @cmember Get the current line number
+
+ int GetCurrentLineNumber ()
+ {
+ assert (m_p != NULL);
+ return XML_GetCurrentLineNumber (m_p);
+ }
+
+ // @cmember Get the current column number
+
+ int GetCurrentColumnNumber ()
+ {
+ assert (m_p != NULL);
+ return XML_GetCurrentColumnNumber (m_p);
+ }
+
+ // @cmember Get the current byte count
+
+ int GetCurrentByteCount ()
+ {
+ assert (m_p != NULL);
+ return XML_GetCurrentByteCount (m_p);
+ }
+
+ // @cmember Get the input context
+
+ const char *GetInputContext (int *pnOffset, int *pnSize)
+ {
+ assert (m_p != NULL);
+ return XML_GetInputContext (m_p, pnOffset, pnSize);
+ }
+
+ // @cmember Get last error string
+
+ const XML_LChar *GetErrorString ()
+ {
+ return XML_ErrorString (GetErrorCode ());
+ }
+
+ // @cmember Return the version string
+
+ static const XML_LChar *GetExpatVersion ()
+ {
+ return XML_ExpatVersion ();
+ }
+
+ // @cmember Get the version information
+
+ static void GetExpatVersion (int *pnMajor, int *pnMinor, int *pnMicro)
+ {
+ XML_expat_version v = XML_ExpatVersionInfo ();
+ if (pnMajor)
+ *pnMajor = v .major;
+ if (pnMinor)
+ *pnMinor = v .minor;
+ if (pnMicro)
+ *pnMicro = v .micro;
+ }
+
+ // @cmember Get last error string
+
+ static const XML_LChar *GetErrorString (enum XML_Error nError)
+ {
+ return XML_ErrorString (nError);
+ }
+
+
+ // Public handler methods:
+ // The template parameter should provide their own implementation for those handlers that they want
+
+ // @cmember Start element handler
+
+ void OnStartElement (const XML_Char *pszName, const XML_Char **papszAttrs)
+ {
+ return;
+ }
+
+ // @cmember End element handler
+
+ void OnEndElement (const XML_Char *pszName)
+ {
+ return;
+ }
+
+ // @cmember Character data handler
+
+ void OnCharacterData (const XML_Char *pszData, int nLength)
+ {
+ return;
+ }
+
+ // @cmember Processing instruction handler
+
+ void OnProcessingInstruction (const XML_Char *pszTarget,
+ const XML_Char *pszData)
+ {
+ return;
+ }
+
+ // @cmember Comment handler
+
+ void OnComment (const XML_Char *pszData)
+ {
+ return;
+ }
+
+ // @cmember Start CDATA section handler
+
+ void OnStartCdataSection ()
+ {
+ return;
+ }
+
+ // @cmember End CDATA section handler
+
+ void OnEndCdataSection ()
+ {
+ return;
+ }
+
+ // @cmember Default handler
+
+ void OnDefault (const XML_Char *pszData, int nLength)
+ {
+ return;
+ }
+
+ // @cmember External entity ref handler
+
+ bool OnExternalEntityRef (const XML_Char *pszContext,
+ const XML_Char *pszBase, const XML_Char *pszSystemID,
+ const XML_Char *pszPublicID)
+ {
+ return false;
+ }
+
+ // @cmember Unknown encoding handler
+
+ bool OnUnknownEncoding (const XML_Char *pszName, XML_Encoding *pInfo)
+ {
+ return false;
+ }
+
+ // @cmember Start namespace declaration handler
+
+ void OnStartNamespaceDecl (const XML_Char *pszPrefix,
+ const XML_Char *pszURI)
+ {
+ return;
+ }
+
+ // @cmember End namespace declaration handler
+
+ void OnEndNamespaceDecl (const XML_Char *pszPrefix)
+ {
+ return;
+ }
+
+ // @cmember XML declaration handler
+
+ void OnXmlDecl (const XML_Char *pszVersion, const XML_Char *pszEncoding,
+ bool fStandalone)
+ {
+ return;
+ }
+
+ // @cmember Start DOCTYPE declaration handler
+
+ void OnStartDoctypeDecl (const XML_Char *pszDoctypeName,
+ const XML_Char *pszSysID, const XML_Char *pszPubID,
+ bool fHasInternalSubset)
+ {
+ return;
+ }
+
+ // @cmember End DOCTYPE declaration handler
+
+ void OnEndDoctypeDecl ()
+ {
+ return;
+ }
+
+// @access Protected methods
+protected:
+
+ // @cmember Handle any post creation
+
+ void OnPostCreate ()
+ {
+ }
+
+// @access Protected static methods
+protected:
+
+ // @cmember Start element handler wrapper
+
+ static void __cdecl StartElementHandler (void *pUserData,
+ const XML_Char *pszName, const XML_Char **papszAttrs)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnStartElement (pszName, papszAttrs);
+ }
+
+ // @cmember End element handler wrapper
+
+ static void __cdecl EndElementHandler (void *pUserData,
+ const XML_Char *pszName)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnEndElement (pszName);
+ }
+
+ // @cmember Character data handler wrapper
+
+ static void __cdecl CharacterDataHandler (void *pUserData,
+ const XML_Char *pszData, int nLength)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnCharacterData (pszData, nLength);
+ }
+
+ // @cmember Processing instruction handler wrapper
+
+ static void __cdecl ProcessingInstructionHandler (void *pUserData,
+ const XML_Char *pszTarget, const XML_Char *pszData)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnProcessingInstruction (pszTarget, pszData);
+ }
+
+ // @cmember Comment handler wrapper
+
+ static void __cdecl CommentHandler (void *pUserData,
+ const XML_Char *pszData)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnComment (pszData);
+ }
+
+ // @cmember Start CDATA section wrapper
+
+ static void __cdecl StartCdataSectionHandler (void *pUserData)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnStartCdataSection ();
+ }
+
+ // @cmember End CDATA section wrapper
+
+ static void __cdecl EndCdataSectionHandler (void *pUserData)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnEndCdataSection ();
+ }
+
+ // @cmember Default wrapper
+
+ static void __cdecl DefaultHandler (void *pUserData,
+ const XML_Char *pszData, int nLength)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnDefault (pszData, nLength);
+ }
+
+ // @cmember External entity ref wrapper
+
+ static int __cdecl ExternalEntityRefHandler (void *pUserData,
+ const XML_Char *pszContext, const XML_Char *pszBase,
+ const XML_Char *pszSystemID, const XML_Char *pszPublicID)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ return pThis ->OnExternalEntityRef (pszContext,
+ pszBase, pszSystemID, pszPublicID) ? 1 : 0;
+ }
+
+ // @cmember Unknown encoding wrapper
+
+ static int __cdecl UnknownEncodingHandler (void * pUserData, const XML_Char * pszName, XML_Encoding * pInfo)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ return pThis ->OnUnknownEncoding (pszName, pInfo) ? 1 : 0;
+ }
+
+ // @cmember Start namespace decl wrapper
+
+ static void __cdecl StartNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix, const XML_Char * pszURI)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnStartNamespaceDecl (pszPrefix, pszURI);
+ }
+
+ // @cmember End namespace decl wrapper
+
+ static void __cdecl EndNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnEndNamespaceDecl (pszPrefix);
+ }
+
+ // @cmember XML declaration wrapper
+
+ static void __cdecl XmlDeclHandler (void *pUserData, const XML_Char *pszVersion, const XML_Char *pszEncoding, int nStandalone)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnXmlDecl (pszVersion, pszEncoding, nStandalone != 0);
+ }
+
+ // @cmember Start Doctype declaration wrapper
+
+ static void __cdecl StartDoctypeDeclHandler (
+ void *pUserData, const XML_Char *pszDoctypeName, const XML_Char *pszSysID,
+ const XML_Char *pszPubID, int nHasInternalSubset
+ )
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnStartDoctypeDecl (pszDoctypeName, pszSysID,
+ pszPubID, nHasInternalSubset != 0);
+ }
+
+ // @cmember End Doctype declaration wrapper
+
+ static void __cdecl EndDoctypeDeclHandler (void *pUserData)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnEndDoctypeDecl ();
+ }
+
+
+protected:
+
+ XML_Parser m_p;
+
+ /// Returns the value of the specified attribute, if found; NULL otherwise
+ static const XML_Char * FindAttr(const XML_Char ** iAttrs, const XML_Char * iAttrToFind)
+ {
+ for (const XML_Char ** Attr = iAttrs; *Attr != NULL; Attr += 2)
+ {
+ if (strcmp(*Attr, iAttrToFind) == 0)
+ {
+ return *(Attr + 1);
+ }
+ } // for Attr - iAttrs[]
+ return NULL;
+ }
+} ;
+
+
+
+
diff --git a/src/lua5.1.dll b/src/lua5.1.dll
new file mode 100644
index 000000000..515cf8b30
--- /dev/null
+++ b/src/lua5.1.dll
Binary files differ
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 000000000..1f6aad24f
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,197 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Root.h"
+
+#include <exception> //std::exception
+#include <csignal> //std::signal
+#include <stdlib.h> //exit()
+
+#ifdef _MSC_VER
+ #include <dbghelp.h>
+#endif // _MSC_VER
+
+
+
+
+
+/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window
+#define ENABLE_LEAK_FINDER
+
+
+
+
+
+#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
+ #pragma warning(push)
+ #pragma warning(disable:4100)
+ #include "LeakFinder.h"
+ #pragma warning(pop)
+#endif
+
+
+
+
+
+
+void ShowCrashReport(int)
+{
+ std::signal(SIGSEGV, SIG_DFL);
+
+ printf("\n\nMCServer has crashed!\n");
+
+ exit(-1);
+}
+
+
+
+
+
+#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Windows 32-bit stuff: when the server crashes, create a "dump file" containing the callstack of each thread and some variables; let the user send us that crash file for analysis
+
+typedef BOOL (WINAPI *pMiniDumpWriteDump)(
+ HANDLE hProcess,
+ DWORD ProcessId,
+ HANDLE hFile,
+ MINIDUMP_TYPE DumpType,
+ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ PMINIDUMP_CALLBACK_INFORMATION CallbackParam
+);
+
+pMiniDumpWriteDump g_WriteMiniDump; // The function in dbghlp DLL that creates dump files
+
+char g_DumpFileName[MAX_PATH]; // Filename of the dump file; hes to be created before the dump handler kicks in
+char g_ExceptionStack[128 * 1024]; // Substitute stack, just in case the handler kicks in because of "insufficient stack space"
+MINIDUMP_TYPE g_DumpFlags = MiniDumpNormal; // By default dump only the stack and some helpers
+
+
+
+
+
+/** This function gets called just before the "program executed an illegal instruction and will be terminated" or similar.
+Its purpose is to create the crashdump using the dbghlp DLLs
+*/
+LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_ExceptionInfo)
+{
+ char * newStack = &g_ExceptionStack[sizeof(g_ExceptionStack)];
+ char * oldStack;
+
+ // Use the substitute stack:
+ // This code is the reason why we don't support 64-bit (yet)
+ _asm
+ {
+ mov oldStack, esp
+ mov esp, newStack
+ }
+
+ MINIDUMP_EXCEPTION_INFORMATION ExcInformation;
+ ExcInformation.ThreadId = GetCurrentThreadId();
+ ExcInformation.ExceptionPointers = a_ExceptionInfo;
+ ExcInformation.ClientPointers = 0;
+
+ // Write the dump file:
+ HANDLE dumpFile = CreateFile(g_DumpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ g_WriteMiniDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, g_DumpFlags, (a_ExceptionInfo) ? &ExcInformation : NULL, NULL, NULL);
+ CloseHandle(dumpFile);
+
+ // Revert to old stack:
+ _asm
+ {
+ mov esp, oldStack
+ }
+
+ return 0;
+}
+
+#endif // _WIN32 && !_WIN64
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// main:
+
+int main( int argc, char **argv )
+{
+ (void)argc;
+ (void)argv;
+
+ #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
+ InitLeakFinder();
+ #endif
+
+ // Magic code to produce dump-files on Windows if the server crashes:
+ #if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
+ HINSTANCE hDbgHelp = LoadLibrary("DBGHELP.DLL");
+ g_WriteMiniDump = (pMiniDumpWriteDump)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
+ if (g_WriteMiniDump != NULL)
+ {
+ _snprintf_s(g_DumpFileName, ARRAYCOUNT(g_DumpFileName), _TRUNCATE, "crash_mcs_%x.dmp", GetCurrentProcessId());
+ SetUnhandledExceptionFilter(LastChanceExceptionFilter);
+
+ // Parse arguments for minidump flags:
+ for (int i = 0; i < argc; i++)
+ {
+ if (_stricmp(argv[i], "/cdg") == 0)
+ {
+ // Add globals to the dump
+ g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithDataSegs);
+ }
+ else if (_stricmp(argv[i], "/cdf") == 0)
+ {
+ // Add full memory to the dump (HUUUGE file)
+ g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithFullMemory);
+ }
+ } // for i - argv[]
+ }
+ #endif // _WIN32 && !_WIN64
+ // End of dump-file magic
+
+ #if defined(_DEBUG) && defined(_MSC_VER)
+ _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
+
+ // _X: The simple built-in CRT leak finder - simply break when allocating the Nth block ({N} is listed in the leak output)
+ // Only useful when the leak is in the same sequence all the time
+ // _CrtSetBreakAlloc(85950);
+
+ #endif // _DEBUG && _MSC_VER
+
+ #ifndef _DEBUG
+ std::signal(SIGSEGV, ShowCrashReport);
+ #endif
+
+ // DEBUG: test the dumpfile creation:
+ // *((int *)0) = 0;
+
+ #if !defined(ANDROID_NDK)
+ try
+ #endif
+ {
+ cRoot Root;
+ Root.Start();
+ }
+ #if !defined(ANDROID_NDK)
+ catch( std::exception& e )
+ {
+ LOGERROR("Standard exception: %s", e.what() );
+ }
+ catch( ... )
+ {
+ LOGERROR("Unknown exception!");
+ }
+ #endif
+
+
+ #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
+ DeinitLeakFinder();
+ #endif
+
+ return 0;
+}
+
+
+
+
diff --git a/src/tolua++.exe b/src/tolua++.exe
new file mode 100644
index 000000000..e5cec6d78
--- /dev/null
+++ b/src/tolua++.exe
Binary files differ
diff --git a/src/tolua++.h b/src/tolua++.h
new file mode 100644
index 000000000..8da427fe3
--- /dev/null
+++ b/src/tolua++.h
@@ -0,0 +1,186 @@
+/* tolua
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Apr 2003
+** $Id: $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+#ifndef TOLUA_H
+#define TOLUA_H
+
+#ifndef TOLUA_API
+#define TOLUA_API extern
+#endif
+
+#define TOLUA_VERSION "tolua++-1.0.92"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define tolua_pushcppstring(x,y) tolua_pushstring(x,y.c_str())
+#define tolua_iscppstring tolua_isstring
+
+#define tolua_iscppstringarray tolua_isstringarray
+#define tolua_pushfieldcppstring(L,lo,idx,s) tolua_pushfieldstring(L, lo, idx, s.c_str())
+
+#ifndef TEMPLATE_BIND
+ #define TEMPLATE_BIND(p)
+#endif
+
+#define TOLUA_TEMPLATE_BIND(p)
+
+#define TOLUA_PROTECTED_DESTRUCTOR
+#define TOLUA_PROPERTY_TYPE(p)
+
+typedef int lua_Object;
+
+#include "lua/src/lua.h"
+#include "lua/src/lauxlib.h"
+
+struct tolua_Error
+{
+ int index;
+ int array;
+ const char* type;
+};
+typedef struct tolua_Error tolua_Error;
+
+#define TOLUA_NOPEER LUA_REGISTRYINDEX /* for lua 5.1 */
+
+TOLUA_API const char* tolua_typename (lua_State* L, int lo);
+TOLUA_API void tolua_error (lua_State* L, const char* msg, tolua_Error* err);
+TOLUA_API int tolua_isnoobj (lua_State* L, int lo, tolua_Error* err);
+TOLUA_API int tolua_isvalue (lua_State* L, int lo, int def, tolua_Error* err);
+TOLUA_API int tolua_isvaluenil (lua_State* L, int lo, tolua_Error* err);
+TOLUA_API int tolua_isboolean (lua_State* L, int lo, int def, tolua_Error* err);
+TOLUA_API int tolua_isnumber (lua_State* L, int lo, int def, tolua_Error* err);
+TOLUA_API int tolua_isstring (lua_State* L, int lo, int def, tolua_Error* err);
+TOLUA_API int tolua_istable (lua_State* L, int lo, int def, tolua_Error* err);
+TOLUA_API int tolua_isusertable (lua_State* L, int lo, const char* type, int def, tolua_Error* err);
+TOLUA_API int tolua_isuserdata (lua_State* L, int lo, int def, tolua_Error* err);
+TOLUA_API int tolua_isusertype (lua_State* L, int lo, const char* type, int def, tolua_Error* err);
+TOLUA_API int tolua_isvaluearray
+ (lua_State* L, int lo, int dim, int def, tolua_Error* err);
+TOLUA_API int tolua_isbooleanarray
+ (lua_State* L, int lo, int dim, int def, tolua_Error* err);
+TOLUA_API int tolua_isnumberarray
+ (lua_State* L, int lo, int dim, int def, tolua_Error* err);
+TOLUA_API int tolua_isstringarray
+ (lua_State* L, int lo, int dim, int def, tolua_Error* err);
+TOLUA_API int tolua_istablearray
+ (lua_State* L, int lo, int dim, int def, tolua_Error* err);
+TOLUA_API int tolua_isuserdataarray
+ (lua_State* L, int lo, int dim, int def, tolua_Error* err);
+TOLUA_API int tolua_isusertypearray
+ (lua_State* L, int lo, const char* type, int dim, int def, tolua_Error* err);
+
+TOLUA_API void tolua_open (lua_State* L);
+
+TOLUA_API void* tolua_copy (lua_State* L, void* value, unsigned int size);
+TOLUA_API int tolua_register_gc (lua_State* L, int lo);
+TOLUA_API int tolua_default_collect (lua_State* tolua_S);
+
+TOLUA_API void tolua_usertype (lua_State* L, const char* type);
+TOLUA_API void tolua_beginmodule (lua_State* L, const char* name);
+TOLUA_API void tolua_endmodule (lua_State* L);
+TOLUA_API void tolua_module (lua_State* L, const char* name, int hasvar);
+TOLUA_API void tolua_class (lua_State* L, const char* name, const char* base);
+TOLUA_API void tolua_cclass (lua_State* L, const char* lname, const char* name, const char* base, lua_CFunction col);
+TOLUA_API void tolua_function (lua_State* L, const char* name, lua_CFunction func);
+TOLUA_API void tolua_constant (lua_State* L, const char* name, lua_Number value);
+TOLUA_API void tolua_variable (lua_State* L, const char* name, lua_CFunction get, lua_CFunction set);
+TOLUA_API void tolua_array (lua_State* L,const char* name, lua_CFunction get, lua_CFunction set);
+
+/* TOLUA_API void tolua_set_call_event(lua_State* L, lua_CFunction func, char* type); */
+/* TOLUA_API void tolua_addbase(lua_State* L, char* name, char* base); */
+
+TOLUA_API void tolua_pushvalue (lua_State* L, int lo);
+TOLUA_API void tolua_pushboolean (lua_State* L, int value);
+TOLUA_API void tolua_pushnumber (lua_State* L, lua_Number value);
+TOLUA_API void tolua_pushstring (lua_State* L, const char* value);
+TOLUA_API void tolua_pushuserdata (lua_State* L, void* value);
+TOLUA_API void tolua_pushusertype (lua_State* L, void* value, const char* type);
+TOLUA_API void tolua_pushusertype_and_takeownership(lua_State* L, void* value, const char* type);
+TOLUA_API void tolua_pushfieldvalue (lua_State* L, int lo, int index, int v);
+TOLUA_API void tolua_pushfieldboolean (lua_State* L, int lo, int index, int v);
+TOLUA_API void tolua_pushfieldnumber (lua_State* L, int lo, int index, lua_Number v);
+TOLUA_API void tolua_pushfieldstring (lua_State* L, int lo, int index, const char* v);
+TOLUA_API void tolua_pushfielduserdata (lua_State* L, int lo, int index, void* v);
+TOLUA_API void tolua_pushfieldusertype (lua_State* L, int lo, int index, void* v, const char* type);
+TOLUA_API void tolua_pushfieldusertype_and_takeownership (lua_State* L, int lo, int index, void* v, const char* type);
+
+TOLUA_API lua_Number tolua_tonumber (lua_State* L, int narg, lua_Number def);
+TOLUA_API const char* tolua_tostring (lua_State* L, int narg, const char* def);
+TOLUA_API void* tolua_touserdata (lua_State* L, int narg, void* def);
+TOLUA_API void* tolua_tousertype (lua_State* L, int narg, void* def);
+TOLUA_API int tolua_tovalue (lua_State* L, int narg, int def);
+TOLUA_API int tolua_toboolean (lua_State* L, int narg, int def);
+TOLUA_API lua_Number tolua_tofieldnumber (lua_State* L, int lo, int index, lua_Number def);
+TOLUA_API const char* tolua_tofieldstring (lua_State* L, int lo, int index, const char* def);
+TOLUA_API void* tolua_tofielduserdata (lua_State* L, int lo, int index, void* def);
+TOLUA_API void* tolua_tofieldusertype (lua_State* L, int lo, int index, void* def);
+TOLUA_API int tolua_tofieldvalue (lua_State* L, int lo, int index, int def);
+TOLUA_API int tolua_getfieldboolean (lua_State* L, int lo, int index, int def);
+
+TOLUA_API void tolua_dobuffer(lua_State* L, char* B, unsigned int size, const char* name);
+
+TOLUA_API int class_gc_event (lua_State* L);
+
+#ifdef __cplusplus
+static inline const char* tolua_tocppstring (lua_State* L, int narg, const char* def) {
+
+ const char* s = tolua_tostring(L, narg, def);
+ return s?s:"";
+};
+
+static inline const char* tolua_tofieldcppstring (lua_State* L, int lo, int index, const char* def) {
+
+ const char* s = tolua_tofieldstring(L, lo, index, def);
+ return s?s:"";
+};
+
+#else
+#define tolua_tocppstring tolua_tostring
+#define tolua_tofieldcppstring tolua_tofieldstring
+#endif
+
+TOLUA_API int tolua_fast_isa(lua_State *L, int mt_indexa, int mt_indexb, int super_index);
+
+#ifndef Mtolua_new
+#define Mtolua_new(EXP) new EXP
+#endif
+
+#ifndef Mtolua_delete
+#define Mtolua_delete(EXP) delete EXP
+#endif
+
+#ifndef Mtolua_new_dim
+#define Mtolua_new_dim(EXP, len) new EXP[len]
+#endif
+
+#ifndef Mtolua_delete_dim
+#define Mtolua_delete_dim(EXP) delete [] EXP
+#endif
+
+#ifndef tolua_outside
+#define tolua_outside
+#endif
+
+#ifndef tolua_owned
+#define tolua_owned
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/tolua_base.h b/src/tolua_base.h
new file mode 100644
index 000000000..4f1038c09
--- /dev/null
+++ b/src/tolua_base.h
@@ -0,0 +1,128 @@
+#ifndef TOLUA_BASE_H
+#define TOLUA_BASE_H
+
+#pragma warning(disable:4800) // This file is ONLY included by Bindings.cpp and it throws lots of C4800 warnings
+
+#include "tolua++.h"
+
+
+
+
+
+class ToluaBase {
+
+ int lua_instance;
+
+protected:
+
+ lua_State* lua_state;
+
+ void lua_stacktrace(lua_State* L) const
+ {
+ lua_Debug entry;
+ int depth = 0;
+
+ while (lua_getstack(L, depth, &entry))
+ {
+ lua_getinfo(L, "Sln", &entry);
+
+ LOGERROR("%s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?");
+ depth++;
+ }
+ }
+
+
+ bool report_errors(int status) const
+ {
+ if ( status!=0 )
+ {
+ const char* s = lua_tostring(lua_state, -1);
+ LOGERROR("-- %s", s );
+ //lua_pop(lua_state, 1);
+ LOGERROR("Stack:");
+ lua_stacktrace( lua_state );
+ return true;
+ }
+ return false;
+ }
+
+ bool push_method(const char* name, lua_CFunction f) const {
+
+ if (!lua_state) return false;
+
+ lua_getref(lua_state, lua_instance);
+ lua_pushstring(lua_state, name);
+ //LOGINFO("1. push_method() Stack size: %i", lua_gettop( lua_state ) );
+ lua_gettable(lua_state, -2);
+ //LOGINFO("2. push_method() Stack size: %i", lua_gettop( lua_state ) );
+
+ if (lua_isnil(lua_state, -1)) {
+
+ // pop the table
+ lua_pop(lua_state, 2);
+ return false;
+
+ } else {
+
+ if (f) {
+ if (lua_iscfunction(lua_state, -1)) {
+ lua_pop(lua_state, 2);
+ return false;
+ };
+ /* // not for now
+ lua_pushcfunction(lua_state, f);
+ if (lua_rawequal(lua_state, -1, -2)) {
+
+ // avoid recursion, pop both functions and the table
+ lua_pop(lua_state, 3);
+ return false;
+ };
+
+ // pop f
+ lua_pop(lua_state, 1);
+ */
+ };
+
+ // swap table with function
+ lua_insert(lua_state, -2);
+ };
+
+ return true;
+ };
+
+ void dbcall(lua_State* L, int nargs, int nresults) const {
+
+ // using lua_call for now
+ int s = lua_pcall(L, nargs, nresults, 0);
+ report_errors( s );
+ };
+public:
+
+ int GetInstance() { return lua_instance; }
+ lua_State* GetLuaState() { return lua_state; }
+
+ void tolua__set_instance(lua_State* L, lua_Object lo) {
+
+ lua_state = L;
+
+ lua_pushvalue(L, lo);
+ lua_instance = lua_ref(lua_state, 1);
+ };
+
+ ToluaBase() {
+
+ lua_state = NULL;
+ };
+
+ ~ToluaBase() {
+
+ if (lua_state) {
+
+ lua_unref(lua_state, lua_instance);
+ };
+ };
+};
+
+#endif
+
+
diff --git a/src/virtual_method_hooks.lua b/src/virtual_method_hooks.lua
new file mode 100644
index 000000000..15ff1d7f8
--- /dev/null
+++ b/src/virtual_method_hooks.lua
@@ -0,0 +1,506 @@
+-- flags
+local disable_virtual_hooks = true
+local enable_pure_virtual = true
+local default_private_access = false
+
+local access = {public = 0, protected = 1, private = 2}
+
+function preparse_hook(p)
+
+ if default_private_access then
+ -- we need to make all structs 'public' by default
+ p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n")
+ end
+end
+
+
+function parser_hook(s)
+
+ local container = classContainer.curr -- get the current container
+
+ if default_private_access then
+ if not container.curr_member_access and container.classtype == 'class' then
+ -- default access for classes is private
+ container.curr_member_access = access.private
+ end
+ end
+
+ -- try labels (public, private, etc)
+ do
+ local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
+ if b then
+
+ -- found a label, get the new access value from the global 'access' table
+ if access[label] then
+ container.curr_member_access = access[label]
+ end -- else ?
+
+ return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:]
+ end
+ end
+
+
+ local ret = nil
+
+ if disable_virtual_hooks then
+
+ return ret
+ end
+
+ local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())")
+ local const
+ if b then
+ local ret = string.sub(s, e+1)
+ if string.find(ret, "^%s*const") then
+ const = "const"
+ ret = string.gsub(ret, "^%s*const", "")
+ end
+ local purev = false
+ if string.find(ret, "^%s*=%s*0") then
+ purev = true
+ ret = string.gsub(ret, "^%s*=%s*0", "")
+ end
+ ret = string.gsub(ret, "^%s*%b{}", "")
+
+ local func = Function(decl, arg, const)
+ func.pure_virtual = purev
+ --func.access = access
+ func.original_sig = decl
+
+ local curflags = classContainer.curr.flags
+ if not curflags.virtual_class then
+
+ curflags.virtual_class = VirtualClass()
+ end
+ curflags.virtual_class:add(func)
+ curflags.pure_virtual = curflags.pure_virtual or purev
+
+ return ret
+ end
+
+ return ret
+end
+
+
+-- class VirtualClass
+classVirtualClass = {
+ classtype = 'class',
+ name = '',
+ base = '',
+ type = '',
+ btype = '',
+ ctype = '',
+}
+classVirtualClass.__index = classVirtualClass
+setmetatable(classVirtualClass,classClass)
+
+function classVirtualClass:add(f)
+
+ local parent = classContainer.curr
+ pop()
+
+ table.insert(self.methods, {f=f})
+
+ local name,sig
+
+ -- doble negative means positive
+ if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then
+
+ name = self.original_name
+ elseif f.name == 'delete' then
+ name = '~'..self.original_name
+ else
+ if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then
+ name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name
+ end
+ end
+
+ if name then
+ sig = name..self:get_arg_list(f, true)..";\n"
+ push(self)
+ sig = preprocess(sig)
+ self:parse(sig)
+ pop()
+ end
+
+ push(parent)
+end
+
+function preprocess(sig)
+
+ sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
+ sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
+ sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*'
+ sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*'
+
+ return sig
+end
+
+function classVirtualClass:get_arg_list(f, decl)
+
+ local ret = ""
+ local sep = ""
+ local i=1
+ while f.args[i] do
+
+ local arg = f.args[i]
+ if decl then
+ local ptr
+ if arg.ret ~= '' then
+ ptr = arg.ret
+ else
+ ptr = arg.ptr
+ end
+ local def = ""
+ if arg.def and arg.def ~= "" then
+
+ def = " = "..arg.def
+ end
+ ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def
+ else
+ ret = ret..sep..arg.name
+ end
+
+ sep = ","
+ i = i+1
+ end
+
+ return "("..ret..")"
+end
+
+function classVirtualClass:add_parent_virtual_methods(parent)
+
+ parent = parent or _global_classes[self.flags.parent_object.btype]
+
+ if not parent then return end
+
+ if parent.flags.virtual_class then
+
+ local vclass = parent.flags.virtual_class
+ for k,v in ipairs(vclass.methods) do
+ if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then
+ table.insert(self.methods, {f=v.f})
+ end
+ end
+ end
+
+ parent = _global_classes[parent.btype]
+ if parent then
+ self:add_parent_virtual_methods(parent)
+ end
+end
+
+function classVirtualClass:has_method(f)
+
+ for k,v in pairs(self.methods) do
+ -- just match name for now
+ if v.f.name == f.name then
+ return true
+ end
+ end
+
+ return false
+end
+
+function classVirtualClass:add_constructors()
+
+ local i=1
+ while self.flags.parent_object[i] do
+
+ local v = self.flags.parent_object[i]
+ if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then
+
+ self:add(v)
+ end
+
+ i = i+1
+ end
+
+end
+
+--[[
+function classVirtualClass:requirecollection(t)
+
+ self:add_constructors()
+ local req = classClass.requirecollection(self, t)
+ if req then
+ output('class ',self.name,";")
+ end
+ return req
+end
+--]]
+
+function classVirtualClass:supcode()
+
+ -- pure virtual classes can have no default constructors on gcc 4
+
+ if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
+ output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n')
+ end
+
+ local ns
+ if self.prox.classtype == 'namespace' then
+ output('namespace ',self.prox.name, " {")
+ ns = true
+ end
+
+ output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {")
+
+ output("public:\n")
+
+ self:add_parent_virtual_methods()
+
+ self:output_methods(self.btype)
+ self:output_parent_methods()
+
+ self:add_constructors()
+
+ -- no constructor for pure virtual classes
+ if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then
+
+ self:output_constructors()
+ end
+
+ output("};\n\n")
+
+ if ns then
+ output("};")
+ end
+
+ classClass.supcode(self)
+
+ if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
+ output('#endif // __GNUC__ >= 4\n')
+ end
+
+ -- output collector for custom class if required
+ if self:requirecollection(_collect) and _collect[self.type] then
+
+ output('\n')
+ output('/* function to release collected object via destructor */')
+ output('#ifdef __cplusplus\n')
+ --for i,v in pairs(collect) do
+ i,v = self.type, _collect[self.type]
+ output('\nstatic int '..v..' (lua_State* tolua_S)')
+ output('{')
+ output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);')
+ output(' delete self;')
+ output(' return 0;')
+ output('}')
+ --end
+ output('#endif\n\n')
+ end
+
+end
+
+function classVirtualClass:register(pre)
+
+ -- pure virtual classes can have no default constructors on gcc 4
+ if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
+ output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n')
+ end
+
+ classClass.register(self, pre)
+
+ if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
+ output('#endif // __GNUC__ >= 4\n')
+ end
+end
+
+
+--function classVirtualClass:requirecollection(_c)
+-- if self.flags.parent_object.flags.pure_virtual then
+-- return false
+-- end
+-- return classClass.requirecollection(self, _c)
+--end
+
+function classVirtualClass:output_parent_methods()
+
+ for k,v in ipairs(self.methods) do
+
+ if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then
+
+ local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." "
+ local parent_name = rettype..self.btype.."__"..v.f.name
+
+ local par_list = self:get_arg_list(v.f, true)
+ local var_list = self:get_arg_list(v.f, false)
+
+ -- the parent's virtual function
+ output("\t"..parent_name..par_list.." {")
+
+ output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";")
+ output("\t};")
+ end
+ end
+end
+
+function classVirtualClass:output_methods(btype)
+
+ for k,v in ipairs(self.methods) do
+
+ if v.f.name ~= 'new' and v.f.name ~= 'delete' then
+
+ self:output_method(v.f, btype)
+ end
+ end
+ output("\n")
+end
+
+function classVirtualClass:output_constructors()
+
+ for k,v in ipairs(self.methods) do
+
+ if v.f.name == 'new' then
+
+ local par_list = self:get_arg_list(v.f, true)
+ local var_list = self:get_arg_list(v.f, false)
+
+ output("\t",self.original_name,par_list,":",self.btype,var_list,"{};")
+ end
+ end
+end
+
+function classVirtualClass:output_method(f, btype)
+
+ if f.access == 2 then -- private
+ return
+ end
+
+ local ptr
+ if f.ret ~= '' then
+ ptr = f.ret
+ else
+ ptr = f.ptr
+ end
+
+ local rettype = f.mod.." "..f.type..f.ptr.." "
+ local par_list = self:get_arg_list(f, true)
+ local var_list = self:get_arg_list(f, false)
+
+ if string.find(rettype, "%s*LuaQtGenericFlags%s*") then
+
+ _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+")
+ end
+
+ -- the caller of the lua method
+ output("\t"..rettype.." "..f.name..par_list..f.const.." {")
+ local fn = f.cname
+ if f.access == 1 then
+ fn = "NULL"
+ end
+ output('\t\tif (push_method("',f.lname,'", ',fn,')) {')
+
+ --if f.type ~= 'void' then
+ -- output("\t\t\tint top = lua_gettop(lua_state)-1;")
+ --end
+
+ -- push the parameters
+ local argn = 0
+ for i,arg in ipairs(f.args) do
+ if arg.type ~= 'void' then
+ local t,ct = isbasic(arg.type)
+ if t and t ~= '' then
+ if arg.ret == "*" then
+ t = 'userdata'
+ ct = 'void*'
+ end
+ output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");");
+ else
+ local m = arg.ptr
+ if m and m~= "" then
+ if m == "*" then m = "" end
+ output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");")
+ else
+ output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n")
+ output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n')
+ end
+ end
+ argn = argn+1
+ end
+ end
+
+ -- call the function
+ output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ")
+
+ -- return value
+ if f.type ~= 'void' then
+ output("1);")
+
+ local t,ct = isbasic(f.type)
+ if t and t ~= '' then
+ --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);")
+ output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);")
+ else
+
+ local mod = ""
+ if f.ptr ~= "*" then
+ mod = "*("..f.type.."*)"
+ end
+
+ --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);")
+ output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);")
+ end
+ output("\t\t\tlua_pop(lua_state, 1);")
+ output("\t\t\treturn tolua_ret;")
+ else
+ output("0);")
+ end
+
+ -- handle non-implemeted function
+ output("\t\t} else {")
+
+ if f.pure_virtual then
+
+ output('\t\t\tif (lua_state)')
+ --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);')
+ output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");')
+ output('\t\t\telse {')
+ output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");')
+ output('\t\t\t\t::abort();')
+ output('\t\t\t};')
+ if( rettype == " std::string " ) then
+ output('\t\t\treturn "";')
+ else
+ output('\t\t\treturn (',rettype,')0;')
+ end
+ else
+
+ output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';')
+ end
+
+ output("\t\t};")
+
+ output("\t};")
+end
+
+function VirtualClass()
+
+ local parent = classContainer.curr
+ pop()
+
+ local name = "Lua__"..parent.original_name
+
+ local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil})
+ setmetatable(c, classVirtualClass)
+
+ local ft = getnamespace(c.parent)..c.original_name
+ append_global_type(ft, c)
+
+ push(parent)
+
+ c.flags.parent_object = parent
+ c.methods = {}
+
+ push(c)
+ c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n")
+ pop()
+
+ return c
+end
+
+
+
+
+