Merge branch 'master' into cPlayerSetSpeed
1
.gitignore
vendored
@ -48,6 +48,7 @@ world_nether
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
CMakeCache.txt
|
||||
CTestTestfile.cmake
|
||||
Makefile
|
||||
|
||||
*.a
|
||||
|
14
.travis.yml
@ -2,8 +2,15 @@ language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
before_install:
|
||||
- if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi
|
||||
|
||||
# Build MCServer
|
||||
script: cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1 && make -j 2 && cd MCServer/ && (echo stop | $MCSERVER_PATH)
|
||||
script: ./CIbuild.sh
|
||||
|
||||
after_success:
|
||||
- ./uploadCoverage.sh
|
||||
|
||||
env:
|
||||
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer
|
||||
@ -11,6 +18,11 @@ env:
|
||||
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE TRAVIS_MCSERVER_FORCE32=1 MCSERVER_PATH=./MCServer
|
||||
- TRAVIS_MCSERVER_BUILD_TYPE=DEBUG TRAVIS_MCSERVER_FORCE32=1 MCSERVER_PATH=./MCServer_debug
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
env: TRAVIS_MCSERVER_BUILD_TYPE=COVERAGE MCSERVER_PATH=./MCServer
|
||||
|
||||
# Notification Settings
|
||||
notifications:
|
||||
email:
|
||||
|
11
CIbuild.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1;
|
||||
make -j 2;
|
||||
make -j 2 test;
|
||||
cd MCServer/;
|
||||
if [ "$TRAVIS_MCSERVER_BUILD_TYPE" != "COVERAGE" ]
|
||||
then echo stop | $MCSERVER_PATH;
|
||||
fi
|
@ -1,4 +1,4 @@
|
||||
cmake_minimum_required (VERSION 2.6)
|
||||
cmake_minimum_required (VERSION 2.8.2)
|
||||
|
||||
# Without this, the MSVC variable isn't defined for MSVC builds ( http://www.cmake.org/pipermail/cmake/2011-November/047130.html )
|
||||
enable_language(CXX C)
|
||||
@ -14,6 +14,10 @@ if(DEFINED ENV{TRAVIS_MCSERVER_FORCE32})
|
||||
set(FORCE32 $ENV{TRAVIS_MCSERVER_FORCE32})
|
||||
endif()
|
||||
|
||||
if(DEFINED ENV{TRAVIS_BUILD_WITH_COVERAGE})
|
||||
set(BUILD_WITH_COVERAGE $ENV{TRAVIS_BUILD_WITH_COVERAGE})
|
||||
endif()
|
||||
|
||||
# This has to be done before any flags have been set up.
|
||||
if(${BUILD_TOOLS})
|
||||
add_subdirectory(Tools/MCADefrag/)
|
||||
@ -69,3 +73,8 @@ set_exe_flags()
|
||||
|
||||
add_subdirectory (src)
|
||||
|
||||
if(${SELF_TEST})
|
||||
enable_testing()
|
||||
add_subdirectory (tests)
|
||||
endif()
|
||||
|
||||
|
@ -27,5 +27,4 @@ UltraCoderRU
|
||||
worktycho
|
||||
xoft
|
||||
|
||||
|
||||
Please add yourself to this list if you contribute to MCServer.
|
||||
|
@ -3,14 +3,14 @@ Hello! Thanks for wanting to work on this project :smile:, and I hope that this
|
||||
Minecraft Basics
|
||||
----------------
|
||||
|
||||
If you don't play Minecraft or don't have a great knowledge of the basic systems, you should get to know them. The [Minecraft Wiki](http://minecraft.gamepedia.com/Minecraft_Wiki) is quite useful for this task, although some youtubers are also fairly good at teaching the basics and just playing is quite good too.
|
||||
If you don't play Minecraft or don't have a great knowledge of the basic systems, you should get to know them. The [Minecraft Wiki](http://minecraft.gamepedia.com/Minecraft_Wiki) is quite useful for this task, although some youtubers are also fairly good at teaching the basics and just playing is quite good too. It is possible to contribute without knowing minecraft in detail though.
|
||||
|
||||
I'd say that the important topics are:
|
||||
|
||||
* Differnt types of blocks and how they act.
|
||||
* Mobs, what they do and how.
|
||||
* Redstone, pistons, and automation.
|
||||
* Farming
|
||||
* Farming.
|
||||
* Fighting, health and the hunger system.
|
||||
|
||||
Useful Resources
|
||||
@ -39,7 +39,7 @@ You'll also need CMake to generate the makefile to build from.
|
||||
|
||||
**Windows:**
|
||||
|
||||
If you use Windows, your best bet is the MSVC2008 (available as a free download in the Express edition from MS) or MSVS2013 (ditto), solution files for both are currently in the repo.
|
||||
If you use Windows, your best bet is the MSVC2008 (available as a free download in the Express edition from MS) or MSVS2013 (ditto), solution files for which can be generated with cmake. You'll also need cmake to generate the project files.
|
||||
|
||||
Setting up the Repo
|
||||
-------------------
|
||||
@ -85,7 +85,7 @@ Basically, the process is:
|
||||
|
||||
**Windows:**
|
||||
|
||||
You need to first execute the `src/Bindings/AllToLua.bat` script file, then just open the solution file in your MSVC of choice and build.
|
||||
You need to first generate a project file with `cmake . -DCMAKE_BUILD_TYPE=DEBUG` then execute the `src/Bindings/AllToLua.bat` script file, then just open the solution file in your MSVC of choice and build.
|
||||
|
||||
How to Run
|
||||
----------
|
||||
@ -99,18 +99,18 @@ There are a few fairly easy issues for you to get started with, as well as some
|
||||
|
||||
**Easy**:
|
||||
|
||||
* #288
|
||||
* #385
|
||||
* #402
|
||||
* #380
|
||||
* #503
|
||||
* #491
|
||||
* #140
|
||||
* #493
|
||||
* #577
|
||||
* #381
|
||||
* #752
|
||||
* Clean up some of the compiler warnings. (Check [Travis CI](http://travis-ci.org/mc-server/MCServer) for a list of them.) With clang, there are over 10000 lines of warnings to clean up.
|
||||
|
||||
**More Difficult**:
|
||||
|
||||
* #17
|
||||
* #398
|
||||
* #133
|
||||
* #134
|
||||
* #215
|
||||
|
||||
You may also want to write some plugins. They are written in lua, with excellent API documentation available via [APIDump](http://mc-server.xoft.cz/LuaAPI). The [Core](https://github.com/mc-server/Core) plugin should also help quite a bit here.
|
||||
|
||||
|
@ -114,6 +114,7 @@ g_APIDesc =
|
||||
GetBlockSkyLight = { Params = "BlockX, BlockY, BlockZ", Return = "NIBBLETYPE", Notes = "Returns the skylight at the specified absolute coords" },
|
||||
GetBlockType = { Params = "BlockX, BlockY, BlockZ", Return = "BLOCKTYPE", Notes = "Returns the block type at the specified absolute coords" },
|
||||
GetBlockTypeMeta = { Params = "BlockX, BlockY, BlockZ", Return = "BLOCKTYPE, NIBBLETYPE", Notes = "Returns the block type and meta at the specified absolute coords" },
|
||||
GetCoordRange = {Params = "", Return = "MaxX, MaxY, MaxZ", Notes = "Returns the maximum relative coords in all 3 axes. See also GetSize()." },
|
||||
GetDataTypes = { Params = "", Return = "number", Notes = "Returns the mask of datatypes that the object is currently holding" },
|
||||
GetOrigin = { Params = "", Return = "OriginX, OriginY, OriginZ", Notes = "Returns the origin coords of where the area was read from." },
|
||||
GetOriginX = { Params = "", Return = "number", Notes = "Returns the origin x-coord" },
|
||||
@ -124,7 +125,7 @@ g_APIDesc =
|
||||
GetRelBlockSkyLight = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the skylight at the specified relative coords" },
|
||||
GetRelBlockType = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "BLOCKTYPE", Notes = "Returns the block type at the specified relative coords" },
|
||||
GetRelBlockTypeMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "BLOCKTYPE, NIBBLETYPE", Notes = "Returns the block type and meta at the specified relative coords" },
|
||||
GetSize = { Params = "", Return = "SizeX, SizeY, SizeZ", Notes = "Returns the size of the area in all 3 axes." },
|
||||
GetSize = { Params = "", Return = "SizeX, SizeY, SizeZ", Notes = "Returns the size of the area in all 3 axes. See also GetCoordRange()." },
|
||||
GetSizeX = { Params = "", Return = "number", Notes = "Returns the size of the held data in the x-axis" },
|
||||
GetSizeY = { Params = "", Return = "number", Notes = "Returns the size of the held data in the y-axis" },
|
||||
GetSizeZ = { Params = "", Return = "number", Notes = "Returns the size of the held data in the z-axis" },
|
||||
@ -1969,7 +1970,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
|
||||
BroadcastChatSuccess = { Params = "Message", Return = "", Notes = "Prepends Green [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For success messages." },
|
||||
BroadcastChatWarning = { Params = "Message", Return = "", Notes = "Prepends Rose [WARN] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For concerning events, such as plugin reload etc." },
|
||||
CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil." },
|
||||
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for the given player." },
|
||||
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for all players with names partially (or fully) matching the name string provided." },
|
||||
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
|
||||
ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" },
|
||||
GetCraftingRecipes = { Params = "", Return = "{{cCraftingRecipe|cCraftingRecipe}}", Notes = "Returns the CraftingRecipes object" },
|
||||
@ -2294,10 +2295,14 @@ end
|
||||
IsGameModeCreative = { Params = "", Return = "bool", Notes = "Returns true if the current gamemode is gmCreative." },
|
||||
IsGameModeSurvival = { Params = "", Return = "bool", Notes = "Returns true if the current gamemode is gmSurvival." },
|
||||
IsPVPEnabled = { Params = "", Return = "bool", Notes = "Returns whether PVP is enabled in the world settings." },
|
||||
IsWeatherRain = { Params = "", Return = "bool", Notes = "Returns true if the current weather is rain." },
|
||||
IsWeatherStorm = { Params = "", Return = "bool", Notes = "Returns true if the current weather is a storm." },
|
||||
IsWeatherRain = { Params = "", Return = "bool", Notes = "Returns true if the current world is raining." },
|
||||
IsWeatherRainAt = { Params = "BlockX, BlockZ", Return = "bool", Notes = "Returns true if the specified location is raining (takes into account biomes)." },
|
||||
IsWeatherStorm = { Params = "", Return = "bool", Notes = "Returns true if the current world is stormy." },
|
||||
IsWeatherStormAt = { Params = "BlockX, BlockZ", Return = "bool", Notes = "Returns true if the specified location is stormy (takes into account biomes)." },
|
||||
IsWeatherSunny = { Params = "", Return = "bool", Notes = "Returns true if the current weather is sunny." },
|
||||
IsWeatherWet = { Params = "", Return = "bool", Notes = "Returns true if the current weather has any precipitation (rain or storm)." },
|
||||
IsWeatherSunnyAt = { Params = "BlockX, BlockZ", Return = "bool", Notes = "Returns true if the current weather is sunny at the specified location (takes into account biomes)." },
|
||||
IsWeatherWet = { Params = "", Return = "bool", Notes = "Returns true if the current world has any precipitation (rain or storm)." },
|
||||
IsWeatherWetAt = { Params = "BlockX, BlockZ", Return = "bool", Notes = "Returns true if the specified location has any precipitation (rain or storm) (takes into account biomes)." },
|
||||
QueueBlockForTick = { Params = "BlockX, BlockY, BlockZ, TicksToWait", Return = "", Notes = "Queues the specified block to be ticked after the specified number of gameticks." },
|
||||
QueueSaveAllChunks = { Params = "", Return = "", Notes = "Queues all chunks to be saved in the world storage thread" },
|
||||
QueueSetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta, TickDelay", Return = "", Notes = "Queues the block to be set to the specified blocktype and meta after the specified amount of game ticks. Uses SetBlock() for the actual setting, so simulators are woken up and block entities are handled correctly." },
|
||||
@ -2929,6 +2934,7 @@ end
|
||||
{
|
||||
-- No sorting is provided for these, they will be output in the same order as defined here
|
||||
{ FileName = "Writing-a-MCServer-plugin.html", Title = "Writing a MCServer plugin" },
|
||||
{ FileName = "InfoFile.html", Title = "Using the Info.lua file" },
|
||||
{ FileName = "SettingUpDecoda.html", Title = "Setting up the Decoda Lua IDE" },
|
||||
{ FileName = "SettingUpZeroBrane.html", Title = "Setting up the ZeroBrane Studio Lua IDE" },
|
||||
{ FileName = "UsingChunkStays.html", Title = "Using ChunkStays" },
|
||||
|
@ -10,6 +10,11 @@ return
|
||||
Params =
|
||||
{
|
||||
{ Name = "ProjectileEntity", Type = "{{cProjectileEntity}}", Notes = "The projectile that hit an entity." },
|
||||
{ Name = "BlockX", Type = "number", Notes = "The X-coord where the projectile hit." },
|
||||
{ Name = "BlockY", Type = "number", Notes = "The Y-coord where the projectile hit." },
|
||||
{ Name = "BlockZ", Type = "number", Notes = "The Z-coord where the projectile hit." },
|
||||
{ Name = "BlockFace", Type = "number", Notes = "The side of the block where the projectile hit." },
|
||||
{ Name = "BlockHitPos", Type = "Vector3d", Notes = "The exact position where the projectile hit." },
|
||||
},
|
||||
Returns = [[
|
||||
If the function returns false or no value, the next plugin's callback is called. If the function
|
||||
|
246
MCServer/Plugins/APIDump/InfoFile.html
Normal file
@ -0,0 +1,246 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MCServer - Info.lua file</title>
|
||||
<link rel="stylesheet" type="text/css" href="main.css" />
|
||||
<link rel="stylesheet" type="text/css" href="prettify.css" />
|
||||
<script src="prettify.js"></script>
|
||||
<script src="lang-lua.js"></script>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<h1>Info.lua file</h1>
|
||||
<h2>Contents</h2>
|
||||
<ul>
|
||||
<li><a href="#Introduction">Introduction</a></li>
|
||||
<li><a href="#Overall">The overall structure</a></li>
|
||||
<li><a href="#AdditionalInformation">AdditionalInformation table</a></li>
|
||||
<li><a href="#Commands">Commands table</a></li>
|
||||
<li><a href="#ConsoleCommands">ConsoleCommands table</a></li>
|
||||
<li><a href="#Permissions">Permissions table</a></li>
|
||||
<li><a href="#Using">Using the file in code</a></li>
|
||||
<li><a href="#Examples">Examples</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<hr />
|
||||
<a name="Introduction"><h2>Introduction</h2></a>
|
||||
|
||||
<p>For a long time MCServer plugins were plagued by poor documentation. The plugins worked, people who wrote them knew how to use them, but for anyone new to the plugin it was a terrible ordeal learning how to use it. Most of the times, the plugin authors only wrote what commands the plugin supported, sometimes not even that. Then, there was a call to action to put an end to this, to make documenting the plugins easy and at the same time centralized. Thus, the Info.lua file was born.</p>
|
||||
|
||||
<p>Most plugins have some parts that are the same across all the plugins. These are commands, console commands and their permissions. If a plugin implemented a command, it would practically copy & paste the same code over and over again. So it makes sense to extract only unique information, centralize it and automate all the parts around it. This was another reason for the Info.lua file - it is a central hub of commands, console commands and their permissions.</p>
|
||||
|
||||
<p>Last, but not least, we want to make a plugin repository on the web in the future, a repository that would store plugins, their descriptions, comments. It makes sense that the centralized information can be parsed by the repository automatically, so that advanced things, such as searching for a plugin based on a command, or determining whether two plugins collide command-wise, are possible.</p>
|
||||
|
||||
<p>After this file format has been devised, a tool has been written that allows for an easy generation of the documentation for the plugin in various formats. It outputs the documentation in a format that is perfect for pasting into the forum. It generates documentation in a Markup format to use in README.md on GitHub and similar sites. The clever thing is that you don't need to keep all those formats in sync manually - you edit the Info.lua file and this tool will re-generate the documentation for you.</p>
|
||||
|
||||
<p>So to sum up, the Info.lua file contains the plugins' commands, console commands, their permissions and possibly the overall plugin documentation, in a structured manner that can be parsed by a program, yet is human readable and editable.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
<a name="Overall"><h2>The overall structure</h2></a>
|
||||
|
||||
<p>The file consist of a declaration of a single Lua table, g_PluginInfo. This table contains all the information, structured, as its members. Each member can be a structure by itself. The entire file is a valid Lua source file, so any tool that syntax-checks Lua source can syntax-check this file. The file is somewhat forward- and backward- compatible, in the sense that it can be extended in any way without breaking.</p>
|
||||
<p>Here's a skeleton of the file:</p>
|
||||
<pre class="prettyprint lang-lua">
|
||||
g_PluginInfo =
|
||||
{
|
||||
Name = "Example Plugin",
|
||||
Date = "2014-06-12",
|
||||
Description = "This is an example plugin that shows how to use the Info.lua file",
|
||||
|
||||
-- The following members will be documented in greater detail later:
|
||||
AdditionalInformation = {},
|
||||
Commands = {},
|
||||
ConsoleCommands = {},
|
||||
Permissions = {},
|
||||
}
|
||||
</pre>
|
||||
<p>As you can see, the structure is pretty straightforward. Note that the order of the elements inside the table is not important (Lua property).</p>
|
||||
|
||||
<p>The first few elements are for book-keeping. They declare the plugin's name, the date in ISO-format, representing the version of the plugin, and the description. The idea is that the description sums up what the plugin is all about, within some two or three sentences.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
<a name="AdditionalInformation"><h2>AdditionalInformation table</h2></a>
|
||||
|
||||
<p>This table is used for more detailed description of the plugin. If there is any non-trivial setup process, dependencies, describe them here. This is where the description should get detailed. Don't worry about using several paragraphs of text here, if it makes the plugin easier to understand.</p>
|
||||
|
||||
<p>The table should have the following layout:</p>
|
||||
<pre class="prettyprint lang-lua">
|
||||
AdditionalInformation =
|
||||
{
|
||||
{
|
||||
Title = "Chapter 1",
|
||||
Contents = "Describe one big aspect of the plugin here",
|
||||
},
|
||||
{
|
||||
Title = "Chapter 2",
|
||||
Contents = "Describe another big topic",
|
||||
},
|
||||
}
|
||||
</pre>
|
||||
<p>The idea here is that the tool that is used to generate the documentation from the Info.lua file will create a linkified table of contents and then each of the information elements' contents. This information should be all that is needed to successfully configure, run and manage the plugin.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
<a name="Commands"><h2>Commands table</h2></a>
|
||||
|
||||
<p>The commands table lists all the commands that the plugin implements, together with their handler functions, required permissions, help strings and further information. The table supports recursion, which allows plugins to create multi-word commands easily (such as "//schematic load" and "//schematic save"), each having its own separate handler.</p>
|
||||
|
||||
<p>The table uses structure similar to the following:</p>
|
||||
<pre class="prettyprint lang-lua">
|
||||
Commands =
|
||||
{
|
||||
["/cmd1"] =
|
||||
{
|
||||
HelpString = "Performs the first action",
|
||||
Permission = "firstplugin.cmds.1",
|
||||
Alias = "/c1",
|
||||
Handler = HandleCmd1,
|
||||
ParameterCombinations =
|
||||
{
|
||||
{
|
||||
Params = "x y z",
|
||||
Help = "Performs the first action at the specified coordinates",
|
||||
},
|
||||
{
|
||||
Params = "-p",
|
||||
Help = "Performs the first action at the player's coordinates",
|
||||
}
|
||||
},
|
||||
},
|
||||
["/cmd2"] =
|
||||
{
|
||||
Alias = {"/c2", "//c2" },
|
||||
Subcommands =
|
||||
{
|
||||
sub1 = -- This declares a "/cmd2 sub1" command
|
||||
{
|
||||
HelpString = "Performs the second action's first subcommand",
|
||||
Permission = "firstplugin.cmds.2.1",
|
||||
Alias = "1",
|
||||
Handler = HandleCmd2Sub1,
|
||||
ParameterCombinations =
|
||||
{
|
||||
{
|
||||
Params = "x y z",
|
||||
Help = "Performs the second action's first subcommand at the specified coordinates",
|
||||
},
|
||||
{
|
||||
Params = "-p",
|
||||
Help = "Performs the second action's first subcommand at the player's coordinates",
|
||||
}
|
||||
},
|
||||
},
|
||||
sub2 = -- Declares a "/cmd2 sub2" command
|
||||
{
|
||||
HelpString = "Performs the second action's second subcommand",
|
||||
Permission = "firstplugin.cmds.2.2",
|
||||
Handler = HandleCmd2Sub2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Although it may seem overwhelming at first, there is a "method to this madness". Each element of the Commands table defines one command. Most commands start with a slash, so the special Lua syntax for table elements with non-standard names needs to be applied (<code>["/cmd1"] =</code>). The command can either specify subcommands, or a handler function (specifying both is UndefinedBehavior). Subcommands uses the same structure as the entire Commands table, recursively.</p>
|
||||
|
||||
<p>The permission element specifies that the command is only available with the specified permission. Note that the permission for subcommand's parent isn't checked when the subcommand is called. This means that specifying the permission for a command that has subcommands has no effect whatsoever, but is discouraged because we may add processing for that in the future.</p>
|
||||
|
||||
<p>The ParameterCombinations table is used only for generating the documentation, it lists the various combinations of parameters that the command supports. It's worth specifying even if the command supports only one combination, because that combination will get documented this way.</p>
|
||||
|
||||
<p>The Alias member specifies any possible aliases for the command. Each alias is registered separately and if there is a subcommand table, it is applied to all aliases, just as one would expect. You can specify either a single string as the value (if there's only one alias), or a table of strings for multiple aliases. Commands with no aliases do not need to specify this member at all.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
<a name="ConsoleCommands"><h2>ConsoleCommands table</h2>
|
||||
|
||||
<p>This table serves a purpose similar to that of the Commands table, only these commands are provided for the server console. Therefore, there are no permissions specified for these commands. Since most console commands don't use a leading slash, the command names don't need the special syntax. Also, the handler function doesn't receive the Player parameter.</p>
|
||||
|
||||
<p>Here's an example of a ConsoleCommands table:</p>
|
||||
<pre class="prettyprint lang-lua">
|
||||
ConsoleCommands =
|
||||
{
|
||||
concmd =
|
||||
{
|
||||
HelpString = "Performs the console action",
|
||||
Subcommands =
|
||||
{
|
||||
sub1 =
|
||||
{
|
||||
HelpString = "Performs the console action's first subcommand",
|
||||
Handler = HandleConCmdSub1,
|
||||
ParameterCombinations =
|
||||
{
|
||||
{
|
||||
Params = "x y z",
|
||||
Help = "Performs the console action's first subcommand at the specified coordinates",
|
||||
},
|
||||
},
|
||||
},
|
||||
sub2 =
|
||||
{
|
||||
HelpString = "Performs the console action's second subcommand",
|
||||
Handler = HandleConCmdSub2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
<hr />
|
||||
<a name="Permissions"><h2>Permissions table</h2></a>
|
||||
|
||||
<p>The purpose of this table is to document permissions that the plugin uses. The documentation generator automatically collects the permissions specified in the Command table; the Permissions table adds a description for these permissions and may declare other permissions that aren't specifically included in the Command table.</p>
|
||||
|
||||
<pre class="prettyprint lang-lua">
|
||||
Permissions =
|
||||
{
|
||||
["firstplugin.cmd.1.1"] =
|
||||
{
|
||||
Description = "Allows the players to build high towers using the first action.",
|
||||
RecommendedGroups = "players",
|
||||
},
|
||||
["firstplugin.cmd.2.1"] =
|
||||
{
|
||||
Description = "Allows the players to kill entities using the second action. Note that this may be misused to kill other players, too.",
|
||||
RecommendedGroups = "admins, mods",
|
||||
},
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The RecommendedGroup element lists, in plain English, the intended groups for which the permission should be enabled on a typical server. Plugin authors are advised to create reasonable defaults, prefering security to openness, so that admins using these settings blindly don't expose their servers to malicious users.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
<a name="Using"><h2>Using the file in code</h2></a>
|
||||
|
||||
<p>Just writing the Info.lua file and saving it to the plugin folder is not enough for it to actually be used. Your plugin needs to include the following boilerplate code, preferably in its Initialize() function:</p>
|
||||
<pre class="prettyprint lang-lua">
|
||||
-- Use the InfoReg shared library to process the Info.lua file:
|
||||
dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua")
|
||||
RegisterPluginInfoCommands()
|
||||
RegisterPluginInfoConsoleCommands()
|
||||
</pre>
|
||||
|
||||
<p>Of course, if your plugin doesn't have any console commands, it doesn't need to call the RegisterPluginInfoConsoleCommands() function, and similarly if it doesn't have any in-game commands, it doesn't need to call the RegisterPluginInfoCommands() function.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
<a name="Examples"><h2>Examples</h2></a>
|
||||
|
||||
<p>There are several plugins that already implement this approach. You can visit them for inspiration and to see what the generated documentation looks like:</p>
|
||||
<ul>
|
||||
<li>Gallery plugin: <a href="https://github.com/mc-server/Gallery/blob/master/Info.lua">Info.lua</a>, <a href="http://forum.mc-server.org/showthread.php?tid=1306">Forum</a> documentation</li>
|
||||
<li>WorldEdit plugin: <a href="https://github.com/mc-server/WorldEdit/blob/master/Info.lua">Info.lua</a>, <a href="http://forum.mc-server.org/showthread.php?tid=870">Forum</a> and <a href="https://github.com/mc-server/WorldEdit">MarkDown</a> documentation</li>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
prettyPrint();
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -39,7 +39,8 @@ pre
|
||||
|
||||
body
|
||||
{
|
||||
min-width: 800px;
|
||||
min-width: 400px;
|
||||
max-width: 1200px;
|
||||
width: 95%;
|
||||
margin: 10px auto;
|
||||
background-color: white;
|
||||
|
@ -43,7 +43,7 @@ function HandleRequest_Generation( Request )
|
||||
local Content = ""
|
||||
if (Request.PostParams["AGHRRRR"] ~= nil) then
|
||||
GENERATION_STATE = 0
|
||||
WW_instance:SaveAllChunks()
|
||||
WW_instance:QueueSaveAllChunks()
|
||||
WW_instance:QueueUnloadUnusedChunks()
|
||||
LOGERROR("" .. PLUGIN:GetName() .. " v" .. PLUGIN:GetVersion() .. ": works ABORTED by admin")
|
||||
end
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 5c8557d4fdfa580c100510cde07a1a778ea2e244
|
||||
Subproject commit 3790f78d3f7503ff33a423b8e73e81a275562783
|
@ -29,7 +29,8 @@ function Initialize(Plugin)
|
||||
PM:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick);
|
||||
PM:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded);
|
||||
PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage);
|
||||
PM:AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined)
|
||||
PM:AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined);
|
||||
PM:AddHook(cPluginManager.HOOK_PROJECTILE_HIT_BLOCK, OnProjectileHitBlock);
|
||||
|
||||
-- _X: Disabled so that the normal operation doesn't interfere with anything
|
||||
-- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
|
||||
@ -1379,3 +1380,14 @@ end
|
||||
|
||||
|
||||
|
||||
|
||||
function OnProjectileHitBlock(a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockHitPos)
|
||||
local BlockX, BlockY, BlockZ = AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)
|
||||
local World = a_Projectile:GetWorld()
|
||||
|
||||
World:SetBlock(BlockX, BlockY, BlockZ, E_BLOCK_FIRE, 0)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@ function GetHandyVersion()
|
||||
return HANDY_VERSION
|
||||
end
|
||||
-- Checks if handy is in proper version
|
||||
function CheckForRequiedVersion( inVersion )
|
||||
function CheckForRequiredVersion( inVersion )
|
||||
if( inVersion > HANDY_VERSION ) then return false end
|
||||
return true
|
||||
end
|
||||
@ -213,4 +213,4 @@ end
|
||||
function BoolToString( inValue )
|
||||
if( inValue == true ) then return 1 end
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
@ -61,18 +61,30 @@
|
||||
#--------------------------
|
||||
# Fuels
|
||||
|
||||
! 263:1 = 1600 # 1 Coal -> 80 sec
|
||||
! 263:1:1 = 1600 # 1 Charcoal -> 80 sec
|
||||
! 42:126:1 = 150 # 1 Halfslab -> 7.5 seconds
|
||||
! 5:1 = 300 # 1 Planks -> 15 sec
|
||||
! 280:1 = 100 # 1 Stick -> 5 sec
|
||||
! 85:1 = 300 # 1 Fence -> 15 sec
|
||||
! 53:1 = 300 # 1 Wooden Stairs -> 15 sec
|
||||
! 58:1 = 300 # 1 Crafting Table -> 15 sec
|
||||
! 47:1 = 300 # 1 Bookshelf -> 15 sec
|
||||
! 54:1 = 300 # 1 Chest -> 15 sec
|
||||
! 84:1 = 300 # 1 Jukebox -> 15 sec
|
||||
! 327:1 = 200000 # 1 Lava Bucket -> 1000 sec
|
||||
! 17:1 = 300 # 1 Wood -> 15 sec
|
||||
! 6:1 = 100 # 1 Sapling -> 5 sec
|
||||
! 173:1 = 7400 # 1 Coal Block -> 370 sec, based on https://github.com/minetest/common/commit/e0f5a6fd6936052756e27a05a2bfdd6aa86b38e1 which is a clone of MC
|
||||
! 263:1 = 1600 # 1 Coal -> 80 sec
|
||||
! 263:1:1 = 1600 # 1 Charcoal -> 80 sec
|
||||
! 126:1 = 15 # 1 Halfslab -> 7.5 sec
|
||||
! 5:1 = 300 # 1 Planks -> 15 sec
|
||||
! 280:1 = 100 # 1 Stick -> 5 sec
|
||||
! 85:1 = 300 # 1 Fence -> 15 sec
|
||||
! 53:1 = 300 # 1 Wooden Stairs -> 15 sec
|
||||
! 58:1 = 300 # 1 Crafting Table -> 15 sec
|
||||
! 47:1 = 300 # 1 Bookshelf -> 15 sec
|
||||
! 54:1 = 300 # 1 Chest -> 15 sec
|
||||
! 84:1 = 300 # 1 Jukebox -> 15 sec
|
||||
! 327:1 = 20000 # 1 Lava Bucket -> 1000 sec
|
||||
! 17:1 = 300 # 1 Wood -> 15 sec
|
||||
! 6:1 = 100 # 1 Sapling -> 5 sec
|
||||
! 173:1 = 16000 # 1 Coal Block -> 800 sec
|
||||
! 369:1 = 2400 # 1 Blaze Rod -> 120 sec
|
||||
! 25:1 = 300 # 1 Note Block -> 15 sec
|
||||
! 151:1 = 300 # 1 Daylight Sensor -> 15 sec
|
||||
! 107:1 = 300 # 1 Fence Gate -> 15 sec
|
||||
! 167:1 = 300 # 1 Trapdoor -> 15 sec
|
||||
! 146:1 = 300 # 1 Trapped Chest -> 15 sec
|
||||
! 72:1 = 300 # 1 Pressure Plate -> 15 sec
|
||||
! 270:1 = 200 # 1 Wooden Pickaxe -> 10 sec
|
||||
! 271:1 = 200 # 1 Wooden Axe -> 10 sec
|
||||
! 269:1 = 200 # 1 Wooden Shovel -> 10 sec
|
||||
! 290:1 = 200 # 1 Wooden Hoe -> 10 sec
|
||||
! 268:1 = 200 # 1 Wooden Sword -> 10 sec
|
||||
|
29
README.md
@ -1,38 +1,39 @@
|
||||
MCServer
|
||||
MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer.svg)](https://travis-ci.org/mc-server/MCServer) [![Coverity Scan Build Status](https://scan.coverity.com/projects/1930/badge.svg)](https://scan.coverity.com/projects/1930) [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74)
|
||||
========
|
||||
|
||||
**Current Protocol Supported:** Minecraft v1.2 -> v1.7
|
||||
|
||||
MCServer is a performant C++ Minecraft server designed for use in memory and cpu-limited places, or just to make regular server perform better.
|
||||
MCServer is a Minecraft server that is written in C++ and designed to be efficient with memory and CPU, as well as having a flexible Lua Plugin API.
|
||||
|
||||
MCServer can run on PCs, Macs, and *nix. This includes android phones and tablets as well as Raspberry Pis.
|
||||
|
||||
We currently support the protocol from Minecraft 1.2 all the way up to Minecraft 1.7.9.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To install MCServer, you can either download the repository and compile it, or download a pre-compiled version.
|
||||
Normally, you will want to download a pre-compiled version of MCServer from one of the buildservers:
|
||||
|
||||
If you've cloned the repository using Git, you need to pull down the submodules (core plugins, some dependencies). This can be achieved with `git submodule init` and then on a regular basis (to keep up to date) `git submodule update`.
|
||||
* [Linux and Raspberry Pi](http://ci.bearbin.net) (Bearbin's CI Server)
|
||||
* [Windows](http://mc-server.xoft.cz) (xoft's nightly build service)
|
||||
|
||||
If you downloaded a ZIP file of the sources instead, you will need to download PolarSSL, too, from https://github.com/polarssl/polarssl , and unpack it into the `lib/polarssl` folder. You will also need to manually download all the plugins that you want included.
|
||||
You simply need to download and extract these files before you can use the server.
|
||||
|
||||
Compilation instructions are available in the COMPILING file.
|
||||
|
||||
Linux builds can be downloaded from [Bearbin's CI Service](http://ci.bearbin.net) and Windows builds from xoft's [nightly build service](http://mc-server.xoft.cz).
|
||||
|
||||
After you've extracted the files, simply run the MCServer executable.
|
||||
If you're a more advanced user, you may want to compile the server yourself for more performance. See the [COMPILING.md](https://github.com/mc-server/MCServer/blob/master/COMPILING.md) file for more details.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
MCServer is licensed under the Apache license V2, and we welcome anybody to fork and submit a Pull Request back with their changes, and if you want to join as a permanent member we can add you to the team.
|
||||
|
||||
Check out the [CONTRIBUTING.md](https://github.com/mc-server/MCServer/blob/master/CONTRIBUTING.md) file for more details.
|
||||
|
||||
Other Stuff
|
||||
-----------
|
||||
|
||||
For other stuff, including plugins and discussion, check the [forums](http://forum.mc-server.org) and [wiki](http://wiki.mc-server.org/).
|
||||
For other stuff, including plugins and discussion, check the [forums](http://forum.mc-server.org) and [Plugin API](http://mc-server.xoft.cz/LuaAPI/).
|
||||
|
||||
Earn bitcoins for commits or donate to reward the MCServer developers: [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74)
|
||||
|
||||
Travis CI: [![Build Status](https://travis-ci.org/mc-server/MCServer.png?branch=master)](https://travis-ci.org/mc-server/MCServer)
|
||||
Support Us on Gittip: [![Support via Gittip](http://img.shields.io/gittip/mcs_team.svg)](https://www.gittip.com/mcs_team)
|
||||
|
||||
Travis CI: [![Build Status](http://img.shields.io/travis/mc-server/MCServer.svg)](https://travis-ci.org/mc-server/MCServer)
|
||||
|
||||
|
@ -1,32 +1,41 @@
|
||||
macro (add_flags_lnk FLAGS)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_COVERAGE} ${FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "${CMAKE_SHARED_LINKER_FLAGS_COVERAGE} ${FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_COVERAGE "${CMAKE_MODULE_LINKER_FLAGS_COVERAGE} ${FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${FLAGS}")
|
||||
endmacro()
|
||||
|
||||
macro(add_flags_cxx FLAGS)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE} ${FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}")
|
||||
endmacro()
|
||||
|
||||
|
||||
macro(set_flags)
|
||||
# Add the preprocessor macros used for distinguishing between debug and release builds (CMake does this automatically for MSVC):
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG")
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/lib/cmake-coverage/")
|
||||
include(CodeCoverage)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -D_DEBUG")
|
||||
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE} -D_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
@ -42,9 +51,10 @@ macro(set_flags)
|
||||
elseif(APPLE)
|
||||
#on os x clang adds pthread for us but we need to add it for gcc
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
|
||||
add_flags_cxx("-stdlib=libc++")
|
||||
add_flags_lnk("-stdlib=libc++")
|
||||
else()
|
||||
@ -57,6 +67,7 @@ macro(set_flags)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
|
||||
endif()
|
||||
|
||||
@ -97,10 +108,12 @@ macro(set_lib_flags)
|
||||
string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
string(REPLACE "/W3" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -w")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -w")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -w")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -w")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -w")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -w")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -w")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -w")
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -w")
|
||||
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE} -w")
|
||||
endif()
|
||||
|
||||
# On Unix we use two dynamic loading libraries dl and ltdl.
|
||||
@ -118,8 +131,8 @@ endmacro()
|
||||
macro(enable_profile)
|
||||
# Declare the flags used for profiling builds:
|
||||
if (MSVC)
|
||||
set (CXX_PROFILING "")
|
||||
set (LNK_PROFILING "/PROFILE")
|
||||
set (CXX_PROFILING "/Zi")
|
||||
set (LNK_PROFILING "/PROFILE /DEBUG")
|
||||
else()
|
||||
set (CXX_PROFILING "-pg")
|
||||
set (LNK_PROFILING "-pg")
|
||||
@ -128,7 +141,7 @@ macro(enable_profile)
|
||||
|
||||
# Declare the profiling configurations:
|
||||
SET(CMAKE_CXX_FLAGS_DEBUGPROFILE
|
||||
"${CMAKE_CXX_FLAGS_DEBUG} ${PCXX_ROFILING}"
|
||||
"${CMAKE_CXX_FLAGS_DEBUG} ${CXX_PROFILING}"
|
||||
CACHE STRING "Flags used by the C++ compiler during profile builds."
|
||||
FORCE )
|
||||
SET(CMAKE_C_FLAGS_DEBUGPROFILE
|
||||
@ -171,7 +184,11 @@ macro(enable_profile)
|
||||
CMAKE_EXE_LINKER_FLAGS_RELEASEPROFILE
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELEASEPROFILE )
|
||||
# The configuration types need to be set after their respective c/cxx/linker flags and before the project directive
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile" CACHE STRING "" FORCE)
|
||||
if(MSVC)
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile" CACHE STRING "" FORCE)
|
||||
else()
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile;Coverage" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(set_exe_flags)
|
||||
@ -180,10 +197,12 @@ macro(set_exe_flags)
|
||||
# We do not do that for MSVC since MSVC produces an awful lot of warnings for its own STL headers;
|
||||
# the important warnings are turned on using #pragma in Globals.h
|
||||
if (NOT MSVC)
|
||||
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
string(REPLACE "-w" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
string(REPLACE "-w" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE}")
|
||||
string(REPLACE "-w" "" CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE}")
|
||||
add_flags_cxx("-Wall -Wextra -Wno-unused-parameter -Wno-error=switch")
|
||||
|
||||
# we support non-IEEE 754 fpus so can make no guarentees about error
|
||||
|
@ -1,32 +1,53 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual C++ Express 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AnvilStats", "AnvilStats.vcproj", "{CF996A5E-0A86-4004-9710-682B06B5AEBA}"
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Express 2013 for Windows Desktop
|
||||
VisualStudioVersion = 12.0.21005.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AnvilStats", "AnvilStats.vcxproj", "{CF996A5E-0A86-4004-9710-682B06B5AEBA}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA} = {EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821} = {B61007AC-B557-4B67-A765-E468C0C3A821}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\..\VC2008\zlib.vcproj", "{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\..\lib\zlib\zlib.vcxproj", "{B61007AC-B557-4B67-A765-E468C0C3A821}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
DebugProfile|Win32 = DebugProfile|Win32
|
||||
MinSizeRel|Win32 = MinSizeRel|Win32
|
||||
Release profiled|Win32 = Release profiled|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
ReleaseProfile|Win32 = ReleaseProfile|Win32
|
||||
RelWithDebInfo|Win32 = RelWithDebInfo|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.DebugProfile|Win32.ActiveCfg = Debug|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.DebugProfile|Win32.Build.0 = Debug|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.MinSizeRel|Win32.ActiveCfg = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.MinSizeRel|Win32.Build.0 = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release profiled|Win32.ActiveCfg = Release profiled|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release profiled|Win32.Build.0 = Release profiled|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release|Win32.Build.0 = Release|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release profiled|Win32.ActiveCfg = Release profiled|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release profiled|Win32.Build.0 = Release profiled|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release|Win32.Build.0 = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.ReleaseProfile|Win32.ActiveCfg = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.ReleaseProfile|Win32.Build.0 = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.RelWithDebInfo|Win32.ActiveCfg = Release|Win32
|
||||
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.RelWithDebInfo|Win32.Build.0 = Release|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.DebugProfile|Win32.ActiveCfg = DebugProfile|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.DebugProfile|Win32.Build.0 = DebugProfile|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.MinSizeRel|Win32.ActiveCfg = MinSizeRel|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.MinSizeRel|Win32.Build.0 = MinSizeRel|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release profiled|Win32.ActiveCfg = Release|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release profiled|Win32.Build.0 = Release|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.Release|Win32.Build.0 = Release|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.ReleaseProfile|Win32.ActiveCfg = ReleaseProfile|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.ReleaseProfile|Win32.Build.0 = ReleaseProfile|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.RelWithDebInfo|Win32.ActiveCfg = RelWithDebInfo|Win32
|
||||
{B61007AC-B557-4B67-A765-E468C0C3A821}.RelWithDebInfo|Win32.Build.0 = RelWithDebInfo|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -5,38 +5,7 @@
|
||||
|
||||
#include "Globals.h"
|
||||
#include "BiomeMap.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static const int g_BiomePalette[] =
|
||||
{
|
||||
// ARGB:
|
||||
0xff0000ff, /* Ocean */
|
||||
0xff00cf3f, /* Plains */
|
||||
0xffffff00, /* Desert */
|
||||
0xff7f7f7f, /* Extreme Hills */
|
||||
0xff00cf00, /* Forest */
|
||||
0xff007f3f, /* Taiga */
|
||||
0xff3f7f00, /* Swampland */
|
||||
0xff003fff, /* River */
|
||||
0xff7f0000, /* Hell */
|
||||
0xff007fff, /* Sky */
|
||||
0xff3f3fff, /* Frozen Ocean */
|
||||
0xff3f3fff, /* Frozen River */
|
||||
0xff7fffcf, /* Ice Plains */
|
||||
0xff3fcf7f, /* Ice Mountains */
|
||||
0xffcf00cf, /* Mushroom Island */
|
||||
0xff7f00ff, /* Mushroom Island Shore */
|
||||
0xffffff3f, /* Beach */
|
||||
0xffcfcf00, /* Desert Hills */
|
||||
0xff00cf3f, /* Forest Hills */
|
||||
0xff006f1f, /* Taiga Hills */
|
||||
0xff7f8f7f, /* Extreme Hills Edge */
|
||||
0xff004f00, /* Jungle */
|
||||
0xff003f00, /* Jungle Hills */
|
||||
} ;
|
||||
#include "../BiomeVisualiser/BiomeColors.h"
|
||||
|
||||
|
||||
|
||||
@ -139,7 +108,7 @@ void cBiomeMap::StartNewRegion(int a_RegionX, int a_RegionZ)
|
||||
unsigned char * BiomeRow = (unsigned char *)m_Biomes + z * 512;
|
||||
for (int x = 0; x < 512; x++)
|
||||
{
|
||||
RowData[x] = g_BiomePalette[BiomeRow[x]];
|
||||
RowData[x] = g_BiomeColors[BiomeRow[x]];
|
||||
}
|
||||
f.Write(RowData, sizeof(RowData));
|
||||
} // for z
|
||||
|
@ -41,7 +41,7 @@ protected:
|
||||
virtual bool OnDecompressedData(const char * a_DecompressedNBT, int a_DataSize) override { return false; }
|
||||
virtual bool OnRealCoords(int a_ChunkX, int a_ChunkZ) override { return false; }
|
||||
virtual bool OnLastUpdate(Int64 a_LastUpdate) override { return false; }
|
||||
virtual bool OnTerrainPopulated(bool a_Populated) override { return !a_Populated; } // If not populated, we don't want it!
|
||||
virtual bool OnTerrainPopulated(bool a_Populated) override { return false; } // We don't care about "populated", the biomes are the same
|
||||
virtual bool OnBiomes(const unsigned char * a_BiomeData) override;
|
||||
|
||||
void StartNewRegion(int a_RegionX, int a_RegionZ);
|
||||
|
@ -24,6 +24,15 @@
|
||||
#define ALIGN_8
|
||||
#define ALIGN_16
|
||||
|
||||
#define FORMATSTRING(formatIndex, va_argsIndex)
|
||||
|
||||
// MSVC has its own custom version of zu format
|
||||
#define SIZE_T_FMT "%Iu"
|
||||
#define SIZE_T_FMT_PRECISION(x) "%" #x "Iu"
|
||||
#define SIZE_T_FMT_HEX "%Ix"
|
||||
|
||||
#define NORETURN __declspec(noreturn)
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
// TODO: Can GCC explicitly mark classes as abstract (no instances can be created)?
|
||||
@ -40,6 +49,14 @@
|
||||
// Some portability macros :)
|
||||
#define stricmp strcasecmp
|
||||
|
||||
#define FORMATSTRING(formatIndex, va_argsIndex) __attribute__((format (printf, formatIndex, va_argsIndex)))
|
||||
|
||||
#define SIZE_T_FMT "%zu"
|
||||
#define SIZE_T_FMT_PRECISION(x) "%" #x "zu"
|
||||
#define SIZE_T_FMT_HEX "%zx"
|
||||
|
||||
#define NORETURN __attribute((__noreturn__))
|
||||
|
||||
#else
|
||||
|
||||
#error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler"
|
||||
@ -194,6 +211,8 @@ typedef unsigned short UInt16;
|
||||
/// Faster than (int)floorf((float)x / (float)div)
|
||||
#define FAST_FLOOR_DIV( x, div ) ( (x) < 0 ? (((int)x / div) - 1) : ((int)x / div) )
|
||||
|
||||
#define TOLUA_TEMPLATE_BIND(...)
|
||||
|
||||
// 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 ) )
|
||||
@ -204,6 +223,8 @@ typedef unsigned short UInt16;
|
||||
// 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 ) )
|
||||
|
||||
typedef unsigned char Byte;
|
||||
|
||||
|
||||
|
||||
|
||||
@ -227,3 +248,4 @@ public:
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -109,7 +109,7 @@ bool cSpringStats::OnSectionsFinished(void)
|
||||
int Base = BaseY + z * 16;
|
||||
for (int x = 1; x < 15; x++)
|
||||
{
|
||||
if (cChunkDef::GetNibble(m_BlockMetas, Base + x) != 0)
|
||||
if (cChunkDef::GetNibble(m_BlockMetas, x, y, z) != 0)
|
||||
{
|
||||
// Not a source block
|
||||
continue;
|
||||
|
@ -240,6 +240,7 @@ 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;
|
||||
virtual ~cItemCallback() {};
|
||||
} ;
|
||||
|
||||
|
||||
|
529
docs/Generator.html
Normal file
@ -0,0 +1,529 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Generating terrain in MCServer</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Generating terrain in MCServer</h1>
|
||||
<p>This article explains the principles behind the terrain generator in MCServer. It is not strictly
|
||||
specific to MCServer, though, it can be viewed as a generic guide to various terrain-generating algorithms,
|
||||
with specific implementation notes regarding MCServer.</p>
|
||||
|
||||
<p>Contents:
|
||||
<ul>
|
||||
<li><a href="#preface">Preface: How it's done in real life</a></li>
|
||||
<li><a href="#expectedprops">Expected properties</a></li>
|
||||
<li><a href="#reversingflow">Reversing the flow</a></li>
|
||||
<li><a href="#composablegen">The ComposableGenerator pipeline</a></li>
|
||||
<li><a href="#coherentnoise">Using coherent noise</a></li>
|
||||
<li><a href="#biomegen">Generating biomes</a></li>
|
||||
<li><a href="#heightgen">Terrain height</a></li>
|
||||
<li><a href="#compositiongen">Terrain composition</a></li>
|
||||
<li><a href="#finishgen">Finishers</a></li>
|
||||
<li><a href="#makefaster">Making it all faster</a></li>
|
||||
<li><a href="#GPU">Executing on a GPU</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="preface"><h2>Preface: How it's done in real life</h2></a>
|
||||
<p>The nature has many complicated geological, physical and biological processes working on all scales from
|
||||
microscopic to planet-wide scale, that have shaped the terrain into what we see today. The tectonic plates
|
||||
collide, push mountain ranges up and ocean trenches down. Erosion dulls the sharp shapes. Plantlife takes
|
||||
over to further change the overall look of the world.</p>
|
||||
|
||||
<p>Generally speaking, the processes take what's there and change it. Unlike computer generating, which
|
||||
usually creates a finished terrain from scratch, or maybe with only a few iterations. It would be unfeasible
|
||||
for software to emulate all the natural processes in enough detail to provide world generation for a game,
|
||||
mainly because in the nature everything interacts with everything. If a mountain range rises, it changes the
|
||||
way that the precipitation is carried by the wind to the lands beyond the mountains, thus changing the
|
||||
erosion rate there and the vegetation type. </p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="expectedprops"><h2>Expected properties</h2></a>
|
||||
<p>For a MineCraft-like game terrain generator we need the generator to have several properties:
|
||||
<ul>
|
||||
<li>The generator must be able to generate terrain in small chunks. This means it must be possible to
|
||||
generate each of the chunks separately, without dependencies on the neighboring chunks. Note that this
|
||||
doesn't mean chunks cannot coordinate together, it means that "a tree in one chunk cannot ask if there's
|
||||
a building in the neighbor chunk", simply because the neighbor chunk may not be generated yet.</li>
|
||||
<li>The generated chunk needs to be the same if re-generated. This property is not exactly required, but it
|
||||
makes available several techniques that wouldn't be possible otherwise.</li>
|
||||
<li>The generator needs to be reasonably fast. For a server application this means at least some 20 chunks
|
||||
per second for chunks close to each other, and 5 chunks per second for distant chunks. The reason for this
|
||||
distinction will be discussed later.</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="reversingflow"><h2>Reversing the flow</h2></a>
|
||||
<p>As already mentioned, the nature works basically by generating raw terrain composition, then "applying"
|
||||
erosion, vegetation and finally this leads to biomes being formed. Let's now try a somewhat inverse
|
||||
approach: First generate biomes, then fit them with appropriate terrain, and finally cover in vegetation
|
||||
and all the other stuff.</p>
|
||||
|
||||
<p>Splitting the parts like this suddenly makes it possible to create a generator with the required
|
||||
properties. We can generate a reasonable biome map chunk-wise, independently of all the other data. Once we
|
||||
have the biomes, we can compose the terrain for the chunk by using the biome data for the chunk, and
|
||||
possibly even for neighboring chunks. Note that we're not breaking the first property, the biomes can be
|
||||
generated separately so a neighboring chunk's biome map can be generated without the need for the entire
|
||||
neighboring chunk to be present. Similarly, once we have the terrain composition for a chunk, we can
|
||||
generate all the vegetation and structures in it, and those can again use the terrain composition in
|
||||
neighboring chunks.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="composablegen"><h2>The ComposableGenerator pipeline</h2></a>
|
||||
<p>This leads us directly to the main pipeline that is used for generating terrain in MCServer. For
|
||||
technical reasons, the terrain composition step is further subdivided into Height generation and Composition
|
||||
generation, and the structures are really called Finishers. For each chunk the generator generates, in this
|
||||
sequence:
|
||||
<ul>
|
||||
<li>Biomes</li>
|
||||
<li>Terrain height</li>
|
||||
<li>Terrain composition</li>
|
||||
<li>Finishers</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<img src="img/biomes.jpg" />
|
||||
<img src="img/terrainheight.jpg" />
|
||||
<img src="img/terraincomposition.jpg" />
|
||||
<img src="img/finishers.jpg" />
|
||||
<p>The beautiful thing about this is that the individual components can be changed independently. You can
|
||||
have 5 biome generators and 3 height generators and you can let the users mix'n'match.
|
||||
</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="coherentnoise"><h2>Using coherent noise for the generation</h2></a>
|
||||
<p>For a great tutorial on coherent noise, see the <a href="http://libnoise.sourceforge.net/">LibNoise
|
||||
documentation</a>.</p>
|
||||
<p>Coherent noise is a type of noise that has three important properties that we can use to our advantage:
|
||||
<ul>
|
||||
<li>The noise is smooth</li>
|
||||
<li>The noise is algorithmically generated, which means that the same data is generated when the same
|
||||
parameters are given to the noise functions.</li>
|
||||
<li>The noise can be seamlessly extended in any direction</li>
|
||||
</ul></p>
|
||||
|
||||
<p>We'll be mostly using Perlin noise in this article. It is the easiest one to visualise and use and is one
|
||||
of the most useful kinds of coherent noises. Here's an example of a Perlin noise generated in 2 dimensions:</p>
|
||||
<img src="img/perlin.jpg" />
|
||||
|
||||
<p>It comes only naturally that such a 2D noise can be used as a terrain height map directly:</p>
|
||||
<img src="img/perlinheightmap.jpg" />
|
||||
|
||||
<p>However, this is not the only use for this noise, and 2 dimensions is not the limit - this noise can be
|
||||
generated for any number of dimensions.</p>
|
||||
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="biomegen"><h2>Generating biomes</h2></a>
|
||||
<p>The easiest way to generate biomes is to not generate them at all - simply assign a single constant biome
|
||||
to everywhere. And indeed there are times when this kind of "generator" is useful - for the MineCraft's Flat
|
||||
world type, or for testing purposes, or for tematic maps. In MCServer, this is exactly what the Constant
|
||||
biome generator does.</p>
|
||||
|
||||
<p>Of course, there are more interesting test scenarios for which multiple biomes must be generated as easy
|
||||
as possible. For these special needs, there's a CheckerBoard biome generator. As the name suggests, it
|
||||
generates a grid of alternating biomes.</p>
|
||||
|
||||
<h3>Voronoi diagram</h3>
|
||||
<p>Those two generators were more of a technicality, we need to make something more interesting if we're
|
||||
going for a natural look. The Voronoi generator is the first step towards such a change. Recall that a
|
||||
<a href="http://en.wikipedia.org/wiki/Voronoi_diagram">Voronoi diagram</a> is a construct that creates a
|
||||
set of areas where each point in an area is closer to the appropriate seed of the area than the seeds of any
|
||||
other area:</p>
|
||||
<img src="img/voronoi.png" />
|
||||
|
||||
<p>To generate biomes using this approach, you select random "seeds", assign a biome to each one, and then
|
||||
for each "column" of the world you find the seed that is the nearest to that column, and use that seed's
|
||||
biome.</p>
|
||||
|
||||
<p>The overall shape of a Voronoi diagram is governed by the placement of the seeds. In extreme cases, a
|
||||
seed could affect the entire diagram, which is what we don't want - we need our locality, so that we can
|
||||
generate a chunk's worth of biome data. We also don't want the too much irregular diagrams that are produced
|
||||
when the seeds are in small clusters. We need our seeds to come in random, yet somewhat uniform fashion.</p>
|
||||
|
||||
<p>Luckily, we have just the tool: Grid with jitter. Originally used in antialiasing techniques, they can be
|
||||
successfully applied as a source of the seeds for a Voronoi diagram. Simply take a regular 2D grid of seeds
|
||||
with the grid distance being N, and move each seed along the X and Y axis by a random distance, usually in
|
||||
the range [-N / 2, +N / 2]:</p>
|
||||
<img src="img/jittergrid.jpg" />
|
||||
|
||||
<p>Such a grid is the ideal seed source for a Voronoi biome generator, because not
|
||||
only are the Voronoi cells "reasonable", but the seed placement's effect on the diagram is localized - each
|
||||
pixel in the diagram depends on at most 4 x 4 seeds around it. In the following picture, the seed for the
|
||||
requested point (blue) must be within the indicated circle. Even the second-nearest seed, which we will need
|
||||
later, is inside that circle.</p>
|
||||
<img src="img/jittergridlocality.jpg" />
|
||||
|
||||
<p>Calculating the jitter for each cell can be done easily by using a 2D Perlin noise for each coord. We
|
||||
calculate the noise's value at [X, Z], which gives us a number in the range [-1; 1]. We then multiply the
|
||||
number by N / 2, this gives us the required range of [-N / 2, +N / 2]. Adding this number to the X coord
|
||||
gives us the seed's X position. We use another Perlin noise and the same calculation for the Z coord of the
|
||||
seed.</p>
|
||||
|
||||
<p>Here's an example of a biome map generated using the Voronoi + jitter grid, as implemented by the Voronoi
|
||||
biome generator in MCServer:</p>
|
||||
<img src="img/voronoijitterbiomes.png" />
|
||||
|
||||
<h3>Distorted Voronoi</h3>
|
||||
<p>The biomes are starting to look interesting, but now they have straight-line borders, which looks rather
|
||||
weird and the players will most likely notice very soon. We need to somehow distort the borders to make them
|
||||
look more natural. By far the easiest way to achieve that is to use a little trick: When the generator is
|
||||
asked for the biome at column [X, Z], instead of calculating the Voronoi biome for column [X, Z], we first
|
||||
calculate a random offset for each coord, and add it to the coordinates. So the generator actually responds
|
||||
with the biome for [X + rndX, Z + rndZ].</p>
|
||||
|
||||
<p>In order to keep the property that generating for the second time gives us the same result, we need the
|
||||
"random offset" to be replicatable - same output for the same input. This is where we use yet another Perlin
|
||||
noise - just like with the jitter for the Voronoi grid, we add a value from a separate noise to each
|
||||
coordinate before sending the coordinates down to the Voronoi generator:</p>
|
||||
<code>
|
||||
DistortedVoronoiBiome(X, Z) := VoronoiBiome(X + PerlinX(X, Z), Z + PerlinZ(X, Z))
|
||||
</code>
|
||||
|
||||
<p>The following image shows the effects of the change, as generated by MCServer's DistortedVoronoi biome
|
||||
generator. It is actually using the very same Voronoi map as the previous image, the only change has been
|
||||
the addition of the distortion:</p>
|
||||
<img src="img/distortedvoronoibiomes.png" />
|
||||
|
||||
<p>As you can see, this already looks reasonable enough, it could be considered natural biomes, if it
|
||||
weren't for several drawbacks:
|
||||
<ul>
|
||||
<li>There's no way to limit the neighbors. A desert biome can neighbor a tundra biome. </li>
|
||||
<li>All the biomes are considered equal. There's no way to make oceans larger. A mushroom biome is
|
||||
generated right next to other land biomes.</li>
|
||||
</ul></p>
|
||||
|
||||
<h3>Adding relativity</h3>
|
||||
<p>Our next goal is to remove the first defect of the distorted Voronoi generator: unrelated biomes
|
||||
generating next to each other. It is highly unlikely to find a jungle biome next to a desert biome, so we
|
||||
want to have as few of those borders as possible. We could further improve on the selection of
|
||||
biome-to-seed in the Voronoi generator. Or we can try a completely different idea altogether.</p>
|
||||
|
||||
<p>Recall how we talked about the nature, where the biomes are formed by the specific conditions of a place.
|
||||
What if we could make a similar dependency, but without the terrain? It turns out this is possible rather
|
||||
easily - instead of depending on the terrain, we choose two completely artificial measures. Let's call them
|
||||
Temperature and Humidity. If we knew the temperature of the place, we know what set of biomes are possible
|
||||
for such temperatures - we won't place deserts in the cold and tundra in the hot anymore. Similarly, the
|
||||
humidity will help us sort out the desert vs jungle issue. But how do we get a temperature and humidity?
|
||||
Once again, the Perlin noise comes to the rescue. We can use a simple 2D Perlin noise as the temperature
|
||||
map, and another one as the humidity map.</p>
|
||||
|
||||
<p>What we need next is a decision of what biome to generate in certain temperature and humidity
|
||||
combinations. The fastest way for a computer is to have a 2D array, where the temperature is one dimension
|
||||
and humidity the other, and the values in the array specify the biome to generate:</p>
|
||||
<img src="img/temperaturehumiditydecisionsimple.jpg" />
|
||||
|
||||
<p>We can even "misuse" the above diagram to include the hill variants of the biomes and have those hills
|
||||
neighbor each other properly, simply by declaring some of the decision diagram's parts as hills:</p>
|
||||
<img src="img/temperaturehumiditydecisionhills.jpg" />
|
||||
|
||||
<p>The problem with this approach is that there are biomes that should not depend on temperature or
|
||||
humidity, they generate across all of their values. Biomes like Oceans, Rivers and Mushroom. We could
|
||||
either add them somewhere into the decision diagram, or we can make the generator use a multi-step decision:
|
||||
<ul>
|
||||
<li>Decide whether the point is in the ocean, land or mushroom</li>
|
||||
<li>If it's land, decide if it's real land or river.</li>
|
||||
<li>If it's real land, use a TemperatureHumidity approach to generate land-biomes</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>This is the approach implemented in MCServer's MultiStepMap biome generator. It generates biome maps like
|
||||
this:</p>
|
||||
<img src="img/multistepmapbiomes.png" />
|
||||
|
||||
<p>To decide whether the point is in the ocean, land or mushroom, the generator first chooses seeds in a grid
|
||||
that will be later fed to a DistortedVoronoi algorithm, the seeds get the "ocean" and "land" values. Then it
|
||||
considers all the "ocean" seeds that are surrounded by 8 other "ocean" seeds and turns a random few of them
|
||||
into "mushroom". This special seed processing makes the mushroom biomes mostly surrounded by ocean. The
|
||||
following image shows an example seeds grid that the generator might consider, only the two framed cells are
|
||||
allowed to change into mushroom. L = land, O = ocean:</p>
|
||||
<img src="img/multistepmapgrid.jpg" />
|
||||
|
||||
<p>Next, the generator calculates the DistortedVoronoi for the seeds. For the areas that are calculated as
|
||||
mushroom, the distance to the nearest-seed is used to further shrink the mushroom biome and then to
|
||||
distinguish between mushroom and mushroom-shore (image depicts a Voronoi cell for illustration purposes, it
|
||||
works similarly with DistortedVoronoi). O = ocean, M = mushroom, MS = mushroom shore:</p>
|
||||
<img src="img/multistepmapdistance.jpg" />
|
||||
|
||||
<a name="perlinrivers">
|
||||
<p>The rivers are added only to the areas that have been previously marked as land. A simple 2D Perlin noise
|
||||
is used as the base, where its value is between 0 and a configured threshold value, a river is created. This
|
||||
creates the rivers in a closed-loop-like shapes, occasionally splitting two branches off:</p>
|
||||
<img src="img/perlinrivers1.jpg" />
|
||||
<img src="img/perlinrivers2.jpg" />
|
||||
<img src="img/perlinrivers3.jpg" />
|
||||
</a>
|
||||
|
||||
<p>For the leftover land biomes, the two Perlin noises, representing temperature and humidity, are used to
|
||||
generate the biomes, as described earlier. Additionally, the temperature map is used to turn the Ocean biome
|
||||
into FrozenOcean, and the River biome into FrozenRiver, wherever the temperature drops below a threshold.</p>
|
||||
|
||||
<h3>Two-level Voronoi</h3>
|
||||
<p>The 1.7 MineCraft update brought a completely new terrain generation, which has sparked renewed interest
|
||||
in the biome generation. A new, potentially simpler way of generating biomes was found, the two-level
|
||||
DistortedVoronoi generator.</p>
|
||||
|
||||
<p>The main idea behind it all is that we create large areas of similar biomes. There are several groups of
|
||||
related biomes that can be generated near each other: Desert biomes, Ice biomes, Forest biomes, Mesa biomes.
|
||||
Technically, the Ocean biomes were added as yet another group, so that the oceans will generate in
|
||||
approximately the size of the larger areas, too.</p>
|
||||
|
||||
<p>For each column a DistortedVoronoi is used to select, which large area to use. This in turn results in
|
||||
the list of biomes from which to choose. Another DistortedVoronoi, this time with a smaller grid size, is
|
||||
used to select one biome out of that list. Additionally, the smaller DistortedVoronoi calculates not only
|
||||
the nearest seed's distance, but also the distance to the second-nearest seed; the ratio between these two
|
||||
is used as an indicator whether the column is in the "inside" or on the "outskirt" of the smaller Voronoi
|
||||
cell. This allows us to give certain biomes an "edge" biome - the Mushroom biome has a MushroomShore edge,
|
||||
the ExtremeHills biome have an ExtremeHillsEdge biome on the edge, etc.</p>
|
||||
|
||||
<p>The images below illustrate the process with regular Voronoi diagrams, for clarity purposes. The real
|
||||
generator uses distortion before querying the small areas.</p>
|
||||
<img src="img/twolevellargeareas.jpg" /><br />
|
||||
<img src="img/twolevelsmallgrid.jpg" /><br />
|
||||
<img src="img/twolevelsmallareas.jpg" /><br />
|
||||
|
||||
<p>The following image shows an example output of a TwoLevel biome generator in MCServer:</p>
|
||||
<img src="img/twolevelbiomes.png" />
|
||||
|
||||
<p>Note that rivers are currently not implemented in this generator in MCServer, but they could be added
|
||||
using the same approach as in MultiStepMap - by using a thresholded 2D Perlin noise.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="heightgen"><h2>Terrain height</h2></a>
|
||||
<p>As with biomes, the easiest way to generate terrain height is not generating at all - assigning a constant
|
||||
height value to all columns. This is again useful either for internal tests, and for worlds like MineCraft's
|
||||
Flat world.</p>
|
||||
|
||||
<p>For a somewhat more realistic landscape, we will employ the good old 2D Perlin noise. We can use it
|
||||
directly as a heightmap - each value we get from the noise is stretched into the desired range (usually from
|
||||
40 to 120 blocks for regular MineCraft worlds) and used as the height value. However, this doesn't play too
|
||||
well with the biomes we've just generated. If the biome says "ocean" and the Perlin noise says "mountain",
|
||||
the end result will be unpleasant.</p>
|
||||
|
||||
<p>So we want a height generator that is biome-aware. The easiest way of doing this is to have a separate
|
||||
generator for each biome. Simply use the biome map to select which generator to use, then ask the appropriate
|
||||
generator for the height value. Again, this doesn't work too well - imagine an ExtremeHills biome right next
|
||||
to an Ocean biome. If no extra care is taken, the border between these two will be a high wall. The following
|
||||
image shows a 2D representation (for simplification purposes) of the problem:</p>
|
||||
<img src="img/biomeheights.jpg" />
|
||||
|
||||
<p>This requires some further processing. What we need is for the terrain height to be dependent not only on
|
||||
the immediate biome for that column, but also on the close surroundings of the column. This is exactly the
|
||||
kind of task that averaging is designed for. If we take the area of 9x9 biomes centered around the queried
|
||||
column, generate height for each of the biomes therein, sum them up and divide by 81 (the number of biomes
|
||||
summed), we will be effectively making a 9-long running average over the terrain, and all the borders will
|
||||
suddenly become smooth. The following image shows the situation from the previous paragraph after applying
|
||||
the averaging process: </p>
|
||||
<img src="img/biomeheightsavg.jpg" />
|
||||
|
||||
<p>The approach used in MCServer's Biomal generator is based on this idea, with two slight modifications.
|
||||
Instead of using a separate generator for each biome, one generator is used with a different set of input
|
||||
parameters for each biomes. These input parameters modify the overall amplitude and frequency of the Perlin
|
||||
noise that the generator produces, thus modifying the final terrain with regards to biomes. Additionally, the
|
||||
averaging process is weighted - columns closer to the queried column get a more powerful weight in the sum
|
||||
than the columns further away. The following image shows the output of MCServer's Biomal terrain height
|
||||
generator (each block type represents a different biome - ocean in the front (stone), plains and ice plains
|
||||
behind it (lapis, whitewool), extreme hills back right (soulsand), desert hills back left (mossy
|
||||
cobble)):</p>
|
||||
<a name="biomalheights"><img src="img/biomalheights.jpg" /></a>
|
||||
|
||||
<p>One key observation about this whole approach is that in order for it to work, the biomes must be
|
||||
available for columns outside the currently generated chunk, otherwise the columns at the chunk's edge would
|
||||
not be able to properly average their height. This requirement can be fulfilled only by biome generators that
|
||||
adhere to the second <a href="#expectedproperties">Expected property</a> - that re-generating will produce
|
||||
the same data. If the biome generator returned different data for the same chunk each time it was invoked, it
|
||||
would become impossible to apply the averaging.</p>
|
||||
|
||||
<p>(TODO: height with variations (N/A in MCS yet)</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="compositiongen"><h2>Terrain composition</h2></a>
|
||||
<p>As with the other generators, the composition generator category has its easy and debugging items, too.
|
||||
There's the "special" composition of "all the blocks are the same type", which fills the entire column, from
|
||||
the bottom to the height, with a single blocktype. This generator is useful when testing the generators in
|
||||
the other categories, to speed up the generation by leaving out unnecessary calculations. Another special
|
||||
compositor is a similar one, that fills all blocks with the same type, but the type varies for each biome.
|
||||
This way it's easy to see the generated biomes and possibly the heights for those biomes, as shown in the
|
||||
previous section on the <a href="#biomalheights">height averaging screenshot</a>.</p>
|
||||
|
||||
<p>For a natural look, we need to put together a more complicated algorithm. The standard set forth in
|
||||
MineCraft is that the top of the world is covered in grass, then there are a few blocks of dirt and finally
|
||||
stone. This basic layout is then varied for different biomes - deserts have sand and sandstone instead of the
|
||||
grass and dirt layer. Mushroom biomes have mycelium in place of the grass. This per-biome dependency is
|
||||
trivial to implement - when compositing, simply use the appropriate layout for the column's biome.</p>
|
||||
|
||||
<p>The next change concerns oceans. The generated heightmap doesn't include any waterlevel indication
|
||||
whatsoever. So it's up to the terrain compositor to actually decide where to place water. We do this by
|
||||
configuration - simply have a value in the config file specifying the sealevel height. The compositor then
|
||||
has to add water above any column which has a height lower than that. Additionally, the water needs to
|
||||
override per-biome layout selection - we don't want grass blocks to generate under water when the terrain
|
||||
height in the plains biome drops below the sealevel accidentally.</p>
|
||||
|
||||
<p>The final feature in the compositor is the decision between multiple composition layouts within a single
|
||||
biome. A megataiga biome contains patches of non-grass dirt and podzol blocks, and the ocean floor can be
|
||||
made of dirt, gravel, sand or clay. A simple 2D Perlin noise can be used to select the layout to use for a
|
||||
specific column - simply threshold the noise's value by as many thresholds as there are layout variations,
|
||||
and use the layout corresponding to the threshold:</p>
|
||||
<img src="img/perlincompositor1.jpg" />
|
||||
<img src="img/perlincompositor2.jpg" />
|
||||
<img src="img/perlincompositor3.jpg" />
|
||||
|
||||
<h3>Nether composition</h3>
|
||||
<p>So far we've been discussing only the Overworld generator. But MineCraft contains more than that. The
|
||||
Nether has a completely different look and feel, and quite different processes are required to generate that.
|
||||
Recall that MineCraft's Nether is 128 blocks high, with bedrock both at the top and the bottom. Between these
|
||||
two, the terrain looks more like a cavern than a surface. Not surprisingly, the Nether doesn't need a
|
||||
complicated height generator, it can use the flat height. However, the terrain composition must take an
|
||||
altogether different approach.</p>
|
||||
|
||||
<p>The very first idea is to use the Perlin noise, but generate it in 3D, rather than 2D. Then, for each
|
||||
block, evaluate the noise value, if below 0, make it air, if not, make it netherrack.
|
||||
|
||||
<p>To make it so that the bedrock at the top and at the bottom is never revealed, we can add a value
|
||||
increasing the more the Y coord gets towards the bottom or the top. This way the thresholding then guarantees
|
||||
that there will be no air anywhere near the bedrock.</p>
|
||||
|
||||
<p>(TODO)</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="finishgen"><h2>Finishers</h2></a>
|
||||
<p>Finishers are a vast category of various additions to the terrain generator. They range from very easy
|
||||
ones, such as generating snow on top of the terrain in cold biomes, through medium ones, such as growing
|
||||
patches of flowers, complicated ones, such as placing trees and generating caves, all the way to very
|
||||
complicated ones such as villages and nether fortresses. There is no formal distinction between all these
|
||||
"categories", the only thing they have in common is that they take a chunk of blocks and modify it in some
|
||||
way.</p>
|
||||
|
||||
<h3>Snow</h3>
|
||||
<p>Snow is probably the easiest of the finishers. It generates a block of snow on top of each block that is
|
||||
on top of the terrain and is not marked as non-snowable. It checks the chunk's heightmap to determine the top
|
||||
block, then checks whether the block supports snow on its top. Rails, levers and tall grass don't support
|
||||
snow, for example.</p>
|
||||
|
||||
<h3>Ice</h3>
|
||||
<p>Another example of an easy finisher. This scans through the world and turn each water block on the surface
|
||||
into an ice block if the biome is cold. This means that any water block that is under any kind of other
|
||||
block, such as under a tree's leaves, will still stay water. Thus an additional improvement could be made by
|
||||
scanning down from the surface block through blocks that we deem as non-surface, such as leaves, torches,
|
||||
ladders, fences etc. Note that MCServer currently implements only the easy solution.</p>
|
||||
|
||||
<h3>Bottom lava</h3>
|
||||
<p>Most worlds in MineCraft have lava lakes at their bottom. Generating these is pretty straightforward: Use
|
||||
the user-configured depth and replace all the air blocks below this depth with lava blocks. Note however,
|
||||
that this makes this generator dependent on the order in which the finishers are applied. If the mineshafts
|
||||
generate before bottom lava, the mineshafts that are below the lava level will get filled with lava. On the
|
||||
other hand, if bottom lava is generated before the mineshafts, it is possible for a mineshaft to "drill
|
||||
through" a lake of lava. MCServer doesn't try to solve this and instead lets the admin choose whichever they
|
||||
prefer.</p>
|
||||
|
||||
<h3>Specific foliage</h3>
|
||||
<p>There are generators for specific kinds of foliage. The dead bushes in the desert biome and lilypads in
|
||||
the swamp biome both share the same generating pattern. They are both specific to a single biome and they
|
||||
both require a specific block underneath them in order to generate. Their implementation is simple: pick
|
||||
several random columns in the chunk. If the column is of the correct biome and has the correct top block,
|
||||
add the foliage block on top.</p>
|
||||
|
||||
<p>In order to generate the same set of coordinates when the chunk is re-generated, we use the Perlin noise's
|
||||
basis functions (the ones providing the random values for Perlin cell vertices). These basically work as a
|
||||
hash function for the coorinates - the same input coordinates generate the same output value. We use the
|
||||
chunk's coordinates as two of the coords, and the iteration number as the third coordinate, to generate a
|
||||
random number. We then check the biome and the top block at those coordinates, if they allow, we generate the
|
||||
foliage block on top.</p>
|
||||
|
||||
<p>Another example of specific foliage is the tall grass in the plains biome. There are quite a lot of these
|
||||
tall grass blocks, it would be inefficient to generate them using the random-coords approach described above.
|
||||
Instead, we will use a 2D Perlin noise again, with a threshold defining where to put the grass and where
|
||||
not.</p>
|
||||
|
||||
<h3>Small foliage</h3>
|
||||
<p>For the flowers, grass, mushrooms in caves etc. we want to use a slightly different algorithm. These
|
||||
foliage blocks are customarily generated in small "clumps" - there are several blocks of the same type near
|
||||
together. To generate these, we first select random coords, using the coord hash functions, for a center of a
|
||||
clump. Then we select the type of block to generate. Finally, we loop over adding a random (coord hash)
|
||||
number to the clump center coords to get the block where to generate the foliage block:</p>
|
||||
<img src="img/smallfoliageclumps.jpg" />
|
||||
|
||||
<p>In order to make the clump more "round" and "centered", we want the offsets to be closer to the clump
|
||||
center more often. This is done using a thing called Gaussian function distribution. Instead of having each
|
||||
random number generate with the same probability, we want higher probability for the numbers around zero,
|
||||
like this:</p>
|
||||
<img src="img/gaussprobability.jpg" />
|
||||
|
||||
<p>Instead of doing complicated calculations to match this shape exactly, we will use a much easier shape.
|
||||
By adding together two random numbers in the same range, we get the probability distribution that has a
|
||||
"roof" shape, enough for our needs:</p>
|
||||
<img src="img/roofprobability.jpg" />
|
||||
|
||||
<p>(For the curious, there is a proof that adding together infinitely many uniform-distributed random numbers
|
||||
produces random numbers with the Gaussian distribution.)</p>
|
||||
|
||||
<p>This scheme can be used to produce clumps of flowers, when we select the 2D coords of the clump center on
|
||||
the top surface of the terrain. We simply generate the 2D coords of the foliage blocks and use the terrain
|
||||
height to find the third coord. If we want to generate clumps of mushrooms in the caves, however, we need to
|
||||
generate the clump center coords in 3D and either use 3 offsets for the mushrooms, or use 2 offsets plus
|
||||
searching for the closest opening Y-wise in the terrain.</p>
|
||||
|
||||
<p>Note that the clumps generated by this scheme may overlap several chunks. Therefore it's crucial to
|
||||
actually check the surrounding chunks if their clumps overlap into the currently generated chunk, and apply
|
||||
those as well, otherwise there will be visible cuts in the foliage along the chunks borders.</p>
|
||||
|
||||
<h3>Springs</h3>
|
||||
<p>Water and lava springs are essential for making the underground quite a lot more interesting. They are
|
||||
rather easy to generate, but a bit more difficult to get right. Generating simply means that a few random
|
||||
locations (obtained by our familiar coord hashing) are checked and if the block type in there is stone. Then
|
||||
we see all the horizontal neighbors of the block, plus the block underneath. If all of them except one are
|
||||
stone, and the one left is air, our block is suitable for turning into a spring. If there were more air
|
||||
neighbors, the spring would look somewhat unnatural; if there were no air neighbors, the spring won't flow
|
||||
anywhere, so it would be rather useless.</p>
|
||||
|
||||
<p>The difficult part about springs is the amount of them to generate. There should be a few springs on the
|
||||
surface, perhaps a bit more in the mountaineous biomes. There should be quite a few more springs underground,
|
||||
but there should definitely be more water springs than lava springs in the upper levels of the terrain, while
|
||||
there should be more lava springs and almost no water springs near the bottom. To accomodate this, the
|
||||
MCServer team has made a tool that scanned through MineCraft's terrain and counted the amount of both types
|
||||
of springs in relation to their height. Two curves have been found for the distribution of each type of the
|
||||
spring:</p>
|
||||
<img src="http://mc-server.xoft.cz/img/vanilla_springs_huge.png" />
|
||||
|
||||
<p>MCServer uses an approximation of the above curves to choose the height at which to generate the
|
||||
spring.</p>
|
||||
|
||||
<!--
|
||||
<h3>Caves</h3>
|
||||
<p>Caves are definitely one of the main things people notice about MineCraft terrain. There are quite a lot
|
||||
of different algorithms available to generate terrain with caves.
|
||||
-->
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="makefaster"><h2>Making it all faster</h2></a>
|
||||
<p>(TODO)</p>
|
||||
|
||||
<a name="GPU"><h2>Executing on a GPU</h2></a>
|
||||
<p>Much of the terain generation consists of doing the same thing for every single column or block in a chunk. This
|
||||
sort of computation is much faster on a GPU as GPUs are massively parallel. High end GPUs can execute up to 30,000
|
||||
threads simultaneously, which would allow them to generate every block in half a chunk in parallel or every column
|
||||
in over 100 chunks in parallel. A naive comparison suggests that a 800MHz GPU with 15,000 threads can execute parallel
|
||||
code 250 times faster than a 3GHz CPU with 128 bit SIMD. Obviously we want to harness that power.</p>
|
||||
</body>
|
||||
</html>
|
BIN
docs/img/biomalheights.jpg
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
docs/img/biomeheights.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/img/biomeheightsavg.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/biomes.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
docs/img/distortedvoronoibiomes.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
docs/img/finishers.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
docs/img/gaussprobability.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
docs/img/jittergrid.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/img/jittergridlocality.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/multistepmapbiomes.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/multistepmapdistance.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/img/multistepmapgrid.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
docs/img/perlin.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
docs/img/perlincompositor1.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/perlincompositor2.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
docs/img/perlincompositor3.jpg
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
docs/img/perlinheightmap.jpg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
docs/img/perlinrivers1.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
docs/img/perlinrivers2.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
docs/img/perlinrivers3.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
docs/img/roofprobability.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/img/smallfoliageclumps.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/img/temperaturehumiditydecisionhills.jpg
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
docs/img/temperaturehumiditydecisionsimple.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/img/terraincomposition.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/terrainheight.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/twolevelbiomes.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
docs/img/twolevellargeareas.jpg
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
docs/img/twolevelsmallareas.jpg
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
docs/img/twolevelsmallgrid.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
docs/img/voronoi.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
docs/img/voronoijitterbiomes.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
160
lib/cmake-coverage/CodeCoverage.cmake
Normal file
@ -0,0 +1,160 @@
|
||||
#
|
||||
# 2012-01-31, Lars Bilke
|
||||
# - Enable Code Coverage
|
||||
#
|
||||
# 2013-09-17, Joakim Söderberg
|
||||
# - Added support for Clang.
|
||||
# - Some additional usage instructions.
|
||||
#
|
||||
# USAGE:
|
||||
# 1. Copy this file into your cmake modules path.
|
||||
#
|
||||
# 2. Add the following line to your CMakeLists.txt:
|
||||
# INCLUDE(CodeCoverage)
|
||||
#
|
||||
# 3. Set compiler flags to turn off optimization and enable coverage:
|
||||
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
#
|
||||
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
|
||||
# which runs your test executable and produces a lcov code coverage report:
|
||||
# Example:
|
||||
# SETUP_TARGET_FOR_COVERAGE(
|
||||
# my_coverage_target # Name for custom target.
|
||||
# test_driver # Name of the test driver executable that runs the tests.
|
||||
# # NOTE! This should always have a ZERO as exit code
|
||||
# # otherwise the coverage generation will not complete.
|
||||
# coverage # Name of output directory.
|
||||
# )
|
||||
#
|
||||
# 4. Build a Debug build:
|
||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
# make
|
||||
# make my_coverage_target
|
||||
#
|
||||
#
|
||||
|
||||
# Check prereqs
|
||||
FIND_PROGRAM( GCOV_PATH gcov )
|
||||
FIND_PROGRAM( LCOV_PATH lcov )
|
||||
FIND_PROGRAM( GENHTML_PATH genhtml )
|
||||
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
|
||||
|
||||
IF(NOT GCOV_PATH)
|
||||
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
|
||||
ENDIF() # NOT GCOV_PATH
|
||||
|
||||
IF(NOT CMAKE_COMPILER_IS_GNUCXX)
|
||||
# Clang version 3.0.0 and greater now supports gcov as well.
|
||||
MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.")
|
||||
|
||||
IF(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
|
||||
ENDIF()
|
||||
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
|
||||
|
||||
SET(CMAKE_CXX_FLAGS_COVERAGE
|
||||
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
|
||||
CACHE STRING "Flags used by the C++ compiler during coverage builds."
|
||||
FORCE )
|
||||
SET(CMAKE_C_FLAGS_COVERAGE
|
||||
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
|
||||
CACHE STRING "Flags used by the C compiler during coverage builds."
|
||||
FORCE )
|
||||
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used for linking binaries during coverage builds."
|
||||
FORCE )
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
|
||||
FORCE )
|
||||
MARK_AS_ADVANCED(
|
||||
CMAKE_CXX_FLAGS_COVERAGE
|
||||
CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
||||
|
||||
IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage"))
|
||||
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
|
||||
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
|
||||
|
||||
|
||||
# Param _targetname The name of new the custom make target
|
||||
# Param _testrunner The name of the target which runs the tests.
|
||||
# MUST return ZERO always, even on errors.
|
||||
# If not, no coverage report will be created!
|
||||
# Param _outputname lcov output is generated as _outputname.info
|
||||
# HTML report is generated in _outputname/index.html
|
||||
# Optional fourth parameter is passed as arguments to _testrunner
|
||||
# Pass them in list form, e.g.: "-j;2" for -j 2
|
||||
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname)
|
||||
|
||||
IF(NOT LCOV_PATH)
|
||||
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
|
||||
ENDIF() # NOT LCOV_PATH
|
||||
|
||||
IF(NOT GENHTML_PATH)
|
||||
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
ENDIF() # NOT GENHTML_PATH
|
||||
|
||||
# Setup target
|
||||
ADD_CUSTOM_TARGET(${_targetname}
|
||||
|
||||
# Cleanup lcov
|
||||
${LCOV_PATH} --directory . --zerocounters
|
||||
|
||||
# Run tests
|
||||
COMMAND ${_testrunner} ${ARGV3}
|
||||
|
||||
# Capturing lcov counters and generating report
|
||||
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info
|
||||
COMMAND ${LCOV_PATH} --remove ${_outputname}.info 'tests/*' '/usr/*' --output-file ${_outputname}.info.cleaned
|
||||
COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned
|
||||
COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned
|
||||
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
|
||||
|
||||
# Param _targetname The name of new the custom make target
|
||||
# Param _testrunner The name of the target which runs the tests
|
||||
# Param _outputname cobertura output is generated as _outputname.xml
|
||||
# Optional fourth parameter is passed as arguments to _testrunner
|
||||
# Pass them in list form, e.g.: "-j;2" for -j 2
|
||||
FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname)
|
||||
|
||||
IF(NOT PYTHON_EXECUTABLE)
|
||||
MESSAGE(FATAL_ERROR "Python not found! Aborting...")
|
||||
ENDIF() # NOT PYTHON_EXECUTABLE
|
||||
|
||||
IF(NOT GCOVR_PATH)
|
||||
MESSAGE(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
ENDIF() # NOT GCOVR_PATH
|
||||
|
||||
ADD_CUSTOM_TARGET(${_targetname}
|
||||
|
||||
# Run tests
|
||||
${_testrunner} ${ARGV3}
|
||||
|
||||
# Running gcovr
|
||||
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Running gcovr to produce Cobertura code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Cobertura code coverage report saved in ${_outputname}.xml."
|
||||
)
|
||||
|
||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA
|
23
lib/cmake-coverage/LICENSE
Normal file
@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
@ -623,7 +623,7 @@ Reader::decodeDouble( Token &token )
|
||||
const int bufferSize = 32;
|
||||
int count;
|
||||
int length = int(token.end_ - token.start_);
|
||||
if ( length <= bufferSize )
|
||||
if ( length < bufferSize )
|
||||
{
|
||||
Char buffer[bufferSize];
|
||||
memcpy( buffer, token.start_, length );
|
||||
|
@ -20,6 +20,12 @@ endif()
|
||||
|
||||
# Lua needs to be linked dynamically on Windows and statically on *nix, so that LuaRocks work
|
||||
if (WIN32)
|
||||
|
||||
#for compiliers other than msvc we need to tell lua that its building as a dll
|
||||
if (NOT MSVC)
|
||||
add_flags_cxx(-DLUA_BUILD_AS_DLL=1)
|
||||
endif()
|
||||
|
||||
add_library(lua SHARED ${SOURCE})
|
||||
set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/MCServer)
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
|
||||
if(NOT TARGET polarssl)
|
||||
message("including polarssl")
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl EXCLUDE_FROM_ALL )
|
||||
if (SELF_TEST)
|
||||
set(ENABLE_TESTING OFF CACHE BOOL "Disable tests")
|
||||
set(ENABLE_PROGRAMS OFF CACHE BOOL "Disable programs")
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl)
|
||||
else()
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
endif()
|
||||
|
@ -17,6 +17,10 @@ if (WIN32)
|
||||
source_group("Sources" FILES ${SOURCE})
|
||||
endif()
|
||||
|
||||
# FreeBSD requires us to define this to get POSIX 2001 standard
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
add_flags_cxx(-D__POSIX_VISIBLE=200112)
|
||||
endif()
|
||||
|
||||
add_library(sqlite ${SOURCE})
|
||||
|
||||
|
@ -688,73 +688,80 @@ static const unsigned char lua_basic_lua[] = {
|
||||
0x74, 0x70, 0x75, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x68,
|
||||
0x6f, 0x6f, 0x6b, 0x28, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x09, 0x72, 0x65,
|
||||
0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
|
||||
0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x2e, 0x2e, 0x2e, 0x29, 0x0a,
|
||||
0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x63, 0x75, 0x73, 0x74,
|
||||
0x6f, 0x6d, 0x20, 0x70, 0x75, 0x73, 0x68, 0x65, 0x72, 0x73, 0x0a, 0x0a,
|
||||
0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x5f, 0x69, 0x73,
|
||||
0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x2e, 0x2e, 0x2e, 0x29, 0x20,
|
||||
0x20, 0x2d, 0x2d, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68, 0x61,
|
||||
0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x20,
|
||||
0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x6e, 0x64,
|
||||
0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x69, 0x70,
|
||||
0x6c, 0x65, 0x2d, 0x64, 0x6f, 0x74, 0x2d, 0x70, 0x61, 0x72, 0x65, 0x6e,
|
||||
0x74, 0x68, 0x65, 0x73, 0x69, 0x73, 0x20, 0x64, 0x75, 0x65, 0x20, 0x74,
|
||||
0x6f, 0x20, 0x70, 0x72, 0x65, 0x2d, 0x70, 0x61, 0x72, 0x73, 0x69, 0x6e,
|
||||
0x67, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x63, 0x75,
|
||||
0x73, 0x74, 0x6f, 0x6d, 0x20, 0x70, 0x75, 0x73, 0x68, 0x65, 0x72, 0x73,
|
||||
0x0a, 0x0a, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x66, 0x75, 0x6e, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x5f,
|
||||
0x69, 0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x73,
|
||||
0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x75,
|
||||
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d,
|
||||
0x0a, 0x0a, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x75, 0x73, 0x68,
|
||||
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d,
|
||||
0x20, 0x7b, 0x7d, 0x0a, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x20, 0x3d,
|
||||
0x20, 0x7b, 0x7d, 0x0a, 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x75, 0x6e, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x0a,
|
||||
0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x66,
|
||||
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x7b,
|
||||
0x7d, 0x0a, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x73, 0x5f, 0x66,
|
||||
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x7b,
|
||||
0x7d, 0x0a, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x66,
|
||||
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x7b,
|
||||
0x7d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x5f, 0x62, 0x61, 0x73, 0x65, 0x28, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e,
|
||||
0x63, 0x73, 0x29, 0x0a, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
|
||||
0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x67, 0x6c, 0x6f,
|
||||
0x62, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x5b,
|
||||
0x74, 0x5d, 0x0a, 0x0a, 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x63,
|
||||
0x6c, 0x61, 0x73, 0x73, 0x20, 0x64, 0x6f, 0x0a, 0x09, 0x09, 0x69, 0x66,
|
||||
0x20, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x5b, 0x63, 0x6c, 0x61, 0x73, 0x73,
|
||||
0x2e, 0x74, 0x79, 0x70, 0x65, 0x5d, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75,
|
||||
0x6e, 0x63, 0x73, 0x5b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x74, 0x79,
|
||||
0x70, 0x65, 0x5d, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09,
|
||||
0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x67, 0x6c, 0x6f,
|
||||
0x62, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x5b,
|
||||
0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x62, 0x74, 0x79, 0x70, 0x65, 0x5d,
|
||||
0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72,
|
||||
0x6e, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x66,
|
||||
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x74, 0x5f,
|
||||
0x70, 0x75, 0x73, 0x68, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x28, 0x74, 0x29, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
|
||||
0x20, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x74, 0x5d, 0x20, 0x6f, 0x72, 0x20, 0x73,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x28, 0x74,
|
||||
0x2c, 0x20, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x75, 0x73, 0x68,
|
||||
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x20,
|
||||
0x6f, 0x72, 0x20, 0x22, 0x74, 0x6f, 0x6c, 0x75, 0x61, 0x5f, 0x70, 0x75,
|
||||
0x73, 0x68, 0x75, 0x73, 0x65, 0x72, 0x74, 0x79, 0x70, 0x65, 0x22, 0x0a,
|
||||
0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x75, 0x6e,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x0a, 0x09, 0x72, 0x65,
|
||||
0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x75, 0x6e,
|
||||
0x20, 0x7b, 0x7d, 0x0a, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x73,
|
||||
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d,
|
||||
0x20, 0x7b, 0x7d, 0x0a, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x6f,
|
||||
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d,
|
||||
0x20, 0x7b, 0x7d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66,
|
||||
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x61, 0x72,
|
||||
0x63, 0x68, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x28, 0x74, 0x2c, 0x20, 0x66,
|
||||
0x75, 0x6e, 0x63, 0x73, 0x29, 0x0a, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61,
|
||||
0x6c, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x67,
|
||||
0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65,
|
||||
0x73, 0x5b, 0x74, 0x5d, 0x0a, 0x0a, 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65,
|
||||
0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x64, 0x6f, 0x0a, 0x09, 0x09,
|
||||
0x69, 0x66, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x5b, 0x63, 0x6c, 0x61,
|
||||
0x73, 0x73, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x5d, 0x20, 0x74, 0x68, 0x65,
|
||||
0x6e, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
|
||||
0x66, 0x75, 0x6e, 0x63, 0x73, 0x5b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e,
|
||||
0x74, 0x79, 0x70, 0x65, 0x5d, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x09, 0x09, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x67,
|
||||
0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65,
|
||||
0x73, 0x5b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x62, 0x74, 0x79, 0x70,
|
||||
0x65, 0x5d, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x72, 0x65, 0x74,
|
||||
0x75, 0x72, 0x6e, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65,
|
||||
0x74, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75,
|
||||
0x72, 0x6e, 0x20, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x66, 0x75, 0x6e,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x74, 0x5d, 0x20, 0x6f, 0x72,
|
||||
0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x62, 0x61, 0x73, 0x65,
|
||||
0x28, 0x74, 0x2c, 0x20, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x6f,
|
||||
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x20,
|
||||
0x6f, 0x72, 0x20, 0x22, 0x74, 0x6f, 0x6c, 0x75, 0x61, 0x5f, 0x74, 0x6f,
|
||||
0x75, 0x73, 0x65, 0x72, 0x74, 0x79, 0x70, 0x65, 0x22, 0x0a, 0x65, 0x6e,
|
||||
0x64, 0x0a, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
|
||||
0x67, 0x65, 0x74, 0x5f, 0x69, 0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x5f,
|
||||
0x65, 0x6e, 0x75, 0x6d, 0x73, 0x5b, 0x74, 0x5d, 0x20, 0x74, 0x68, 0x65,
|
||||
0x6e, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22,
|
||||
0x74, 0x6f, 0x6c, 0x75, 0x61, 0x5f, 0x69, 0x73, 0x22, 0x20, 0x2e, 0x2e,
|
||||
0x20, 0x74, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x72, 0x65, 0x74,
|
||||
0x75, 0x72, 0x6e, 0x20, 0x5f, 0x69, 0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x74, 0x5d, 0x20, 0x6f, 0x72, 0x20,
|
||||
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x28,
|
||||
0x74, 0x2c, 0x20, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x73, 0x5f,
|
||||
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x20, 0x6f,
|
||||
0x72, 0x20, 0x22, 0x74, 0x6f, 0x6c, 0x75, 0x61, 0x5f, 0x69, 0x73, 0x75,
|
||||
0x73, 0x65, 0x72, 0x74, 0x79, 0x70, 0x65, 0x22, 0x0a, 0x65, 0x6e, 0x64,
|
||||
0x0a
|
||||
0x28, 0x74, 0x2c, 0x20, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x75,
|
||||
0x73, 0x68, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x29, 0x20, 0x6f, 0x72, 0x20, 0x22, 0x74, 0x6f, 0x6c, 0x75, 0x61, 0x5f,
|
||||
0x70, 0x75, 0x73, 0x68, 0x75, 0x73, 0x65, 0x72, 0x74, 0x79, 0x70, 0x65,
|
||||
0x22, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x66,
|
||||
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x0a, 0x09,
|
||||
0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x74, 0x6f, 0x5f, 0x66,
|
||||
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x74, 0x5d, 0x20,
|
||||
0x6f, 0x72, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x62, 0x61,
|
||||
0x73, 0x65, 0x28, 0x74, 0x2c, 0x20, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f,
|
||||
0x74, 0x6f, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x29, 0x20, 0x6f, 0x72, 0x20, 0x22, 0x74, 0x6f, 0x6c, 0x75, 0x61, 0x5f,
|
||||
0x74, 0x6f, 0x75, 0x73, 0x65, 0x72, 0x74, 0x79, 0x70, 0x65, 0x22, 0x0a,
|
||||
0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x73, 0x5f, 0x66, 0x75, 0x6e,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x0a, 0x09, 0x69, 0x66,
|
||||
0x20, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x5b, 0x74, 0x5d, 0x20, 0x74,
|
||||
0x68, 0x65, 0x6e, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
|
||||
0x20, 0x22, 0x74, 0x6f, 0x6c, 0x75, 0x61, 0x5f, 0x69, 0x73, 0x22, 0x20,
|
||||
0x2e, 0x2e, 0x20, 0x74, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x72,
|
||||
0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x69, 0x73, 0x5f, 0x66, 0x75,
|
||||
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x74, 0x5d, 0x20, 0x6f,
|
||||
0x72, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x62, 0x61, 0x73,
|
||||
0x65, 0x28, 0x74, 0x2c, 0x20, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x69,
|
||||
0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29,
|
||||
0x20, 0x6f, 0x72, 0x20, 0x22, 0x74, 0x6f, 0x6c, 0x75, 0x61, 0x5f, 0x69,
|
||||
0x73, 0x75, 0x73, 0x65, 0x72, 0x74, 0x79, 0x70, 0x65, 0x22, 0x0a, 0x65,
|
||||
0x6e, 0x64, 0x0a
|
||||
};
|
||||
unsigned int lua_basic_lua_len = 9073;
|
||||
unsigned int lua_basic_lua_len = 9159;
|
||||
|
@ -1154,7 +1154,7 @@ static const unsigned char lua_declaration_lua[] = {
|
||||
0x72, 0x6d, 0x3a, 0x20, 0x6d, 0x6f, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65,
|
||||
0x2a, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x0a, 0x20, 0x6c, 0x6f, 0x63, 0x61,
|
||||
0x6c, 0x20, 0x73, 0x31, 0x20, 0x3d, 0x20, 0x67, 0x73, 0x75, 0x62, 0x28,
|
||||
0x73, 0x2c, 0x22, 0x28, 0x25, 0x62, 0x5c, 0x5b, 0x5c, 0x5d, 0x29, 0x22,
|
||||
0x73, 0x2c, 0x22, 0x28, 0x25, 0x62, 0x25, 0x5b, 0x25, 0x5d, 0x29, 0x22,
|
||||
0x2c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6e,
|
||||
0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x73, 0x75,
|
||||
0x62, 0x28, 0x6e, 0x2c, 0x27, 0x25, 0x2a, 0x27, 0x2c, 0x27, 0x5c, 0x31,
|
||||
|
93
lib/tolua++/src/bin/lua/_driver.lua
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
-- Allow debugging by ZBS, if run under the IDE:
|
||||
local mobdebugfound, mobdebug = pcall(require, "mobdebug")
|
||||
if mobdebugfound then mobdebug.start() end
|
||||
|
||||
-- The list of valid arguments that the ToLua scripts can process:
|
||||
local KnownArgs = {
|
||||
['v'] = true,
|
||||
['h'] = true,
|
||||
['p'] = true,
|
||||
['P'] = true,
|
||||
['o'] = true,
|
||||
['n'] = true,
|
||||
['H'] = true,
|
||||
['S'] = true,
|
||||
['1'] = true,
|
||||
['L'] = true,
|
||||
['D'] = true,
|
||||
['W'] = true,
|
||||
['C'] = true,
|
||||
['E'] = true,
|
||||
['t'] = true,
|
||||
['q'] = true,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- The flags table used by ToLua scripts, to be filled from the cmdline params:
|
||||
flags = {}
|
||||
|
||||
-- Te extra parameters used by ToLua scripts:
|
||||
_extra_parameters = {}
|
||||
|
||||
-- ToLua version required by the scripts:
|
||||
TOLUA_VERSION = "tolua++-1.0.92"
|
||||
|
||||
-- Lua version used by ToLua, required by the scripts:
|
||||
TOLUA_LUA_VERSION = "Lua 5.1"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Process the cmdline params into the flags table:
|
||||
local args = arg or {}
|
||||
local argc = #args
|
||||
local i = 1
|
||||
while (i <= argc) do
|
||||
local argv = args[i]
|
||||
if (argv:sub(1, 1) == "-") then
|
||||
if (KnownArgs[argv:sub(2)]) then
|
||||
print("Setting flag \"" .. argv:sub(2) .. "\" to \"" .. args[i + 1] .. "\".")
|
||||
flags[argv:sub(2)] = args[i + 1]
|
||||
i = i + 1
|
||||
else
|
||||
print("Unknown option (" .. i .. "): " .. argv)
|
||||
print("Aborting.")
|
||||
os.exit(1)
|
||||
end
|
||||
else
|
||||
print("Setting flag \"f\" to \"" .. argv .. "\".")
|
||||
flags['f'] = argv
|
||||
break
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- Get the path where the scripts are located:
|
||||
path = args[0] or ""
|
||||
local index = path:find("/[^/]*$")
|
||||
if (index == nil) then
|
||||
index = path:find("\\[^\\]*$")
|
||||
end
|
||||
if (index ~= nil) then
|
||||
path = path:sub(1, index)
|
||||
end
|
||||
|
||||
print("path is set to \"" .. path .. "\".")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Call the ToLua processor:
|
||||
dofile(path .. "all.lua")
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -383,7 +383,7 @@ end
|
||||
|
||||
-- called to output an error message
|
||||
function output_error_hook(...)
|
||||
return string.format(...)
|
||||
return string.format(...) -- Note that this line must not end in the triple-dot-parenthesis due to pre-parsing
|
||||
end
|
||||
|
||||
-- custom pushers
|
||||
|
@ -18,17 +18,16 @@ local function pp_dofile(path)
|
||||
local ret = file:read("*a")
|
||||
file:close()
|
||||
|
||||
ret = string.gsub(ret, "%.%.%.%s*%)", "...) local arg = {n=select('#', ...), ...};")
|
||||
|
||||
ret = string.gsub(ret, "%.%.%.%s*%)$", "...) local arg = {n=select('#', ...), ...};")
|
||||
|
||||
loaded = true
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
local f = load(getfile, path)
|
||||
local f, err = load(getfile, path)
|
||||
if not f then
|
||||
|
||||
error("error loading file "..path)
|
||||
error("error loading file " .. path .. ": " .. err)
|
||||
end
|
||||
return f()
|
||||
end
|
||||
|
@ -524,7 +524,7 @@ function Declaration (s,kind,is_parameter)
|
||||
end
|
||||
|
||||
-- check the form: mod type* name
|
||||
local s1 = gsub(s,"(%b\[\])",function (n) return gsub(n,'%*','\1') end)
|
||||
local s1 = gsub(s,"(%b%[%])",function (n) return gsub(n,'%*','\1') end)
|
||||
t = split_c_tokens(s1,'%*')
|
||||
if t.n == 2 then
|
||||
t[2] = gsub(t[2],'\1','%*') -- restore * in dimension expression
|
||||
|
@ -132,7 +132,7 @@ function classFeature:cfuncname (n)
|
||||
if not fname or fname == '' then
|
||||
fname = self.name
|
||||
end
|
||||
n = string.gsub(n..'_'.. (fname), "[<>:, \.%*&]", "_")
|
||||
n = string.gsub(n..'_'.. (fname), "[<>:, %.%*&]", "_")
|
||||
|
||||
return n
|
||||
end
|
||||
|
27
src/Bindings/AllToLua_lua.bat.bat
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
:: AllToLua_Lua.bat
|
||||
:: This scripts updates the automatically-generates Lua bindings in Bindings.cpp / Bindings.h
|
||||
:: When called without any parameters, it will pause for a keypress at the end
|
||||
:: Call with any parameter to disable the wait (for buildserver use)
|
||||
:: This script assumes "lua" executable to be in PATH, it uses a pure-lua implementation of the ToLua processor
|
||||
|
||||
@echo off
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
:: Regenerate the files:
|
||||
echo Regenerating LUA bindings . . .
|
||||
lua ..\..\lib\tolua++\src\bin\lua\_driver.lua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
|
||||
|
||||
|
||||
|
||||
|
||||
: Wait for keypress, if no param given:
|
||||
echo.
|
||||
if %ALLTOLUA_WAIT%N == N pause
|
||||
|
||||
|
||||
|
||||
|
@ -372,11 +372,11 @@ void cLuaState::Push(const AStringVector & a_Vector)
|
||||
|
||||
|
||||
|
||||
void cLuaState::PushUserType(void * a_Object, const char * a_Type)
|
||||
void cLuaState::Push(const cCraftingGrid * a_Grid)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Object, a_Type);
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -384,23 +384,11 @@ void cLuaState::PushUserType(void * a_Object, const char * a_Type)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(int a_Value)
|
||||
void cLuaState::Push(const cCraftingRecipe * a_Recipe)
|
||||
{
|
||||
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);
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -420,35 +408,11 @@ void cLuaState::Push(const char * a_Value)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(bool a_Value)
|
||||
void cLuaState::Push(const cItems & a_Items)
|
||||
{
|
||||
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");
|
||||
tolua_pushusertype(m_LuaState, (void *)&a_Items, "cItems");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -468,6 +432,90 @@ void cLuaState::Push(const cPlayer * a_Player)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const HTTPRequest * a_Request)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPRequest");
|
||||
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(const Vector3d & a_Vector)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)&a_Vector, "Vector3d");
|
||||
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(cBlockEntity * a_BlockEntity)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_BlockEntity, "cBlockEntity");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cChunkDesc * a_ChunkDesc)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_ChunkDesc, "cChunkDesc");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cClientHandle * a_Client)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Client, "cClientHandle");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cEntity * a_Entity)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
@ -480,23 +528,11 @@ void cLuaState::Push(cEntity * a_Entity)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cProjectileEntity * a_ProjectileEntity)
|
||||
void cLuaState::Push(cHopperEntity * a_Hopper)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_ProjectileEntity, "cProjectileEntity");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cMonster * a_Monster)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Monster, "cMonster");
|
||||
tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -528,23 +564,11 @@ void cLuaState::Push(cItems * a_Items)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const cItems & a_Items)
|
||||
void cLuaState::Push(cMonster * a_Monster)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, (void *)&a_Items, "cItems");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cClientHandle * a_Client)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Client, "cClientHandle");
|
||||
tolua_pushusertype(m_LuaState, a_Monster, "cMonster");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -564,59 +588,11 @@ void cLuaState::Push(cPickup * a_Pickup)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cChunkDesc * a_ChunkDesc)
|
||||
void cLuaState::Push(cPlayer * a_Player)
|
||||
{
|
||||
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");
|
||||
tolua_pushusertype(m_LuaState, a_Player, "cPlayer");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -636,35 +612,11 @@ void cLuaState::Push(cPluginLua * a_Plugin)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(const HTTPRequest * a_Request)
|
||||
void cLuaState::Push(cProjectileEntity * a_ProjectileEntity)
|
||||
{
|
||||
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");
|
||||
tolua_pushusertype(m_LuaState, a_ProjectileEntity, "cProjectileEntity");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
@ -684,6 +636,78 @@ void cLuaState::Push(cTNTEntity * a_TNTEntity)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cWebAdmin * a_WebAdmin)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_WebAdmin, "cWebAdmin");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cWindow * a_Window)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Window, "cWindow");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cWorld * a_World)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_World, "cWorld");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(double a_Value)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushnumber(m_LuaState, a_Value);
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(int a_Value)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushnumber(m_LuaState, a_Value);
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(TakeDamageInfo * a_TDI)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(Vector3i * a_Vector)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
@ -696,6 +720,18 @@ void cLuaState::Push(Vector3i * a_Vector)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(Vector3d * a_Vector)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
tolua_pushusertype(m_LuaState, a_Vector, "Vector3d");
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(void * a_Ptr)
|
||||
{
|
||||
UNUSED(a_Ptr);
|
||||
@ -715,23 +751,12 @@ void cLuaState::Push(void * a_Ptr)
|
||||
|
||||
|
||||
|
||||
void cLuaState::Push(cHopperEntity * a_Hopper)
|
||||
|
||||
void cLuaState::PushUserType(void * a_Object, const char * a_Type)
|
||||
{
|
||||
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");
|
||||
tolua_pushusertype(m_LuaState, a_Object, a_Type);
|
||||
m_NumCurrentFunctionArgs += 1;
|
||||
}
|
||||
|
||||
|
@ -173,38 +173,42 @@ public:
|
||||
/** Returns true if a_FunctionName is a valid Lua function that can be called */
|
||||
bool HasFunction(const char * a_FunctionName);
|
||||
|
||||
// Push a value onto the stack
|
||||
// Push a const value onto the stack (keep alpha-sorted):
|
||||
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(cProjectileEntity * a_ProjectileEntity);
|
||||
void Push(cMonster * a_Monster);
|
||||
void Push(cItem * a_Item);
|
||||
void Push(cItems * a_Items);
|
||||
void Push(const 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 char * a_Value);
|
||||
void Push(const cItems & a_Items);
|
||||
void Push(const cPlayer * a_Player);
|
||||
void Push(const HTTPRequest * a_Request);
|
||||
void Push(cWebAdmin * a_WebAdmin);
|
||||
void Push(const HTTPTemplateRequest * a_Request);
|
||||
void Push(const Vector3d & a_Vector);
|
||||
|
||||
// Push a value onto the stack (keep alpha-sorted):
|
||||
void Push(bool a_Value);
|
||||
void Push(cBlockEntity * a_BlockEntity);
|
||||
void Push(cChunkDesc * a_ChunkDesc);
|
||||
void Push(cClientHandle * a_ClientHandle);
|
||||
void Push(cEntity * a_Entity);
|
||||
void Push(cHopperEntity * a_Hopper);
|
||||
void Push(cItem * a_Item);
|
||||
void Push(cItems * a_Items);
|
||||
void Push(cMonster * a_Monster);
|
||||
void Push(cPickup * a_Pickup);
|
||||
void Push(cPlayer * a_Player);
|
||||
void Push(cPluginLua * a_Plugin);
|
||||
void Push(cProjectileEntity * a_ProjectileEntity);
|
||||
void Push(cTNTEntity * a_TNTEntity);
|
||||
void Push(cWebAdmin * a_WebAdmin);
|
||||
void Push(cWindow * a_Window);
|
||||
void Push(cWorld * a_World);
|
||||
void Push(double a_Value);
|
||||
void Push(int a_Value);
|
||||
void Push(TakeDamageInfo * a_TDI);
|
||||
void Push(Vector3d * a_Vector);
|
||||
void Push(Vector3i * a_Vector);
|
||||
void Push(void * a_Ptr);
|
||||
void Push(cHopperEntity * a_Hopper);
|
||||
void Push(cBlockEntity * a_BlockEntity);
|
||||
|
||||
/** Retrieve value at a_StackPos, if it is a valid bool. If not, a_Value is unchanged */
|
||||
void GetStackValue(int a_StackPos, bool & a_Value);
|
||||
|
@ -2538,6 +2538,37 @@ static int tolua_cBlockArea_GetSize(lua_State * tolua_S)
|
||||
|
||||
|
||||
|
||||
static int tolua_cBlockArea_GetCoordRange(lua_State * tolua_S)
|
||||
{
|
||||
// function cBlockArea::GetCoordRange()
|
||||
// Returns all three sizes of the area, miuns one, so that they represent the maximum coord value
|
||||
// Exported manually because there's no direct C++ equivalent,
|
||||
// plus tolua would generate extra input params for the outputs
|
||||
|
||||
cLuaState L(tolua_S);
|
||||
if (!L.CheckParamUserType(1, "cBlockArea"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL);
|
||||
if (self == NULL)
|
||||
{
|
||||
tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetSize'", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Push the three origin coords:
|
||||
lua_pushnumber(tolua_S, self->GetSizeX() - 1);
|
||||
lua_pushnumber(tolua_S, self->GetSizeY() - 1);
|
||||
lua_pushnumber(tolua_S, self->GetSizeZ() - 1);
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S)
|
||||
{
|
||||
// function cBlockArea::LoadFromSchematicFile
|
||||
@ -2864,8 +2895,8 @@ static int tolua_cCompositeChat_SetMessageType(lua_State * tolua_S)
|
||||
}
|
||||
|
||||
// Set the type:
|
||||
int MessageType;
|
||||
L.GetStackValue(1, MessageType);
|
||||
int MessageType = mtCustom;
|
||||
L.GetStackValue(2, MessageType);
|
||||
self->SetMessageType((eMessageType)MessageType);
|
||||
|
||||
// Cut away everything from the stack except for the cCompositeChat instance; return that:
|
||||
@ -2926,6 +2957,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
|
||||
|
||||
tolua_beginmodule(tolua_S, "cBlockArea");
|
||||
tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta);
|
||||
tolua_function(tolua_S, "GetCoordRange", tolua_cBlockArea_GetCoordRange);
|
||||
tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin);
|
||||
tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta);
|
||||
tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize);
|
||||
|
@ -90,7 +90,7 @@ public:
|
||||
virtual bool OnPluginsLoaded (void) = 0;
|
||||
virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
|
||||
virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
|
||||
virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile) = 0;
|
||||
virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos) = 0;
|
||||
virtual bool OnProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity) = 0;
|
||||
virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) = 0;
|
||||
virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) = 0;
|
||||
|
@ -1113,14 +1113,14 @@ bool cPluginLua::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a
|
||||
|
||||
|
||||
|
||||
bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile)
|
||||
bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos)
|
||||
{
|
||||
cCSLock Lock(m_CriticalSection);
|
||||
bool res = false;
|
||||
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_BLOCK];
|
||||
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
|
||||
{
|
||||
m_LuaState.Call((int)(**itr), &a_Projectile, cLuaState::Return, res);
|
||||
m_LuaState.Call((int)(**itr), &a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, a_BlockHitPos, cLuaState::Return, res);
|
||||
if (res)
|
||||
{
|
||||
return true;
|
||||
|
@ -113,7 +113,7 @@ public:
|
||||
virtual bool OnPluginsLoaded (void) override;
|
||||
virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
|
||||
virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
|
||||
virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile) override;
|
||||
virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos) override;
|
||||
virtual bool OnProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity) override;
|
||||
virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) override;
|
||||
virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) override;
|
||||
|
@ -14,6 +14,13 @@
|
||||
#include "inifile/iniFile.h"
|
||||
#include "../Entities/Player.h"
|
||||
|
||||
#define FIND_HOOK(a_HookName) HookMap::iterator Plugins = m_Hooks.find(a_HookName);
|
||||
#define VERIFY_HOOK \
|
||||
if (Plugins == m_Hooks.end()) \
|
||||
{ \
|
||||
return false; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -192,7 +199,7 @@ void cPluginManager::Tick(float a_Dt)
|
||||
ReloadPluginsNow();
|
||||
}
|
||||
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_TICK);
|
||||
FIND_HOOK(HOOK_TICK);
|
||||
if (Plugins != m_Hooks.end())
|
||||
{
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
@ -208,11 +215,9 @@ void cPluginManager::Tick(float a_Dt)
|
||||
|
||||
bool cPluginManager::CallHookBlockSpread(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_BLOCK_SPREAD);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_BLOCK_SPREAD);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnBlockSpread(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Source))
|
||||
@ -233,11 +238,9 @@ bool cPluginManager::CallHookBlockToPickups(
|
||||
cItems & a_Pickups
|
||||
)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_BLOCK_TO_PICKUPS);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_BLOCK_TO_PICKUPS);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -275,11 +278,8 @@ bool cPluginManager::CallHookChat(cPlayer * a_Player, AString & a_Message)
|
||||
return true; // Cancel sending
|
||||
}
|
||||
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_CHAT);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_CHAT);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
@ -298,11 +298,9 @@ bool cPluginManager::CallHookChat(cPlayer * a_Player, AString & a_Message)
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_CHUNK_AVAILABLE);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnChunkAvailable(a_World, a_ChunkX, a_ChunkZ))
|
||||
@ -319,11 +317,9 @@ bool cPluginManager::CallHookChunkAvailable(cWorld * a_World, int a_ChunkX, int
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_CHUNK_GENERATED);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc))
|
||||
@ -340,11 +336,9 @@ bool cPluginManager::CallHookChunkGenerated(cWorld * a_World, int a_ChunkX, int
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_CHUNK_GENERATING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnChunkGenerating(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc))
|
||||
@ -361,11 +355,9 @@ bool cPluginManager::CallHookChunkGenerating(cWorld * a_World, int a_ChunkX, int
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_CHUNK_UNLOADED);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnChunkUnloaded(a_World, a_ChunkX, a_ChunkZ))
|
||||
@ -382,11 +374,9 @@ bool cPluginManager::CallHookChunkUnloaded(cWorld * a_World, int a_ChunkX, int a
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_CHUNK_UNLOADING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnChunkUnloading(a_World, a_ChunkX, a_ChunkZ))
|
||||
@ -403,11 +393,9 @@ bool cPluginManager::CallHookChunkUnloading(cWorld * a_World, int a_ChunkX, int
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_COLLECTING_PICKUP);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnCollectingPickup(a_Player, &a_Pickup))
|
||||
@ -424,11 +412,9 @@ bool cPluginManager::CallHookCollectingPickup(cPlayer * a_Player, cPickup & a_Pi
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_CRAFTING_NO_RECIPE);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnCraftingNoRecipe(a_Player, a_Grid, a_Recipe))
|
||||
@ -445,11 +431,9 @@ bool cPluginManager::CallHookCraftingNoRecipe(const cPlayer * a_Player, const cC
|
||||
|
||||
bool cPluginManager::CallHookDisconnect(cClientHandle & a_Client, const AString & a_Reason)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_DISCONNECT);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_DISCONNECT);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnDisconnect(a_Client, a_Reason))
|
||||
@ -466,11 +450,9 @@ bool cPluginManager::CallHookDisconnect(cClientHandle & a_Client, const AString
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_EXECUTE_COMMAND);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnExecuteCommand(a_Player, a_Split))
|
||||
@ -487,11 +469,9 @@ bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVec
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_EXPLODED);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -508,11 +488,9 @@ bool cPluginManager::CallHookExploded(cWorld & a_World, double a_ExplosionSize,
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_EXPLODING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -529,11 +507,9 @@ bool cPluginManager::CallHookExploding(cWorld & a_World, double & a_ExplosionSiz
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_HANDSHAKE);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnHandshake(a_ClientHandle, a_Username))
|
||||
@ -550,11 +526,9 @@ bool cPluginManager::CallHookHandshake(cClientHandle * a_ClientHandle, const ASt
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_HOPPER_PULLING_ITEM);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -571,11 +545,9 @@ bool cPluginManager::CallHookHopperPullingItem(cWorld & a_World, cHopperEntity &
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_HOPPER_PUSHING_ITEM);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -592,11 +564,9 @@ bool cPluginManager::CallHookHopperPushingItem(cWorld & a_World, cHopperEntity &
|
||||
|
||||
bool cPluginManager::CallHookKilling(cEntity & a_Victim, cEntity * a_Killer)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_KILLING);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_KILLING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnKilling(a_Victim, a_Killer))
|
||||
@ -613,11 +583,9 @@ bool cPluginManager::CallHookKilling(cEntity & a_Victim, cEntity * a_Killer)
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_LOGIN);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnLogin(a_Client, a_ProtocolVersion, a_Username))
|
||||
@ -634,11 +602,9 @@ bool cPluginManager::CallHookLogin(cClientHandle * a_Client, int a_ProtocolVersi
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_ANIMATION);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerAnimation(a_Player, a_Animation))
|
||||
@ -655,11 +621,9 @@ bool cPluginManager::CallHookPlayerAnimation(cPlayer & a_Player, int a_Animation
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_BREAKING_BLOCK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -676,11 +640,9 @@ bool cPluginManager::CallHookPlayerBreakingBlock(cPlayer & a_Player, int a_Block
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_BROKEN_BLOCK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -697,11 +659,9 @@ bool cPluginManager::CallHookPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX,
|
||||
|
||||
bool cPluginManager::CallHookPlayerDestroyed(cPlayer & a_Player)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_DESTROYED);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_DESTROYED);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerDestroyed(a_Player))
|
||||
@ -718,11 +678,9 @@ bool cPluginManager::CallHookPlayerDestroyed(cPlayer & a_Player)
|
||||
|
||||
bool cPluginManager::CallHookPlayerEating(cPlayer & a_Player)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_EATING);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_EATING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerEating(a_Player))
|
||||
@ -739,11 +697,9 @@ bool cPluginManager::CallHookPlayerEating(cPlayer & a_Player)
|
||||
|
||||
bool cPluginManager::CallHookPlayerFished(cPlayer & a_Player, const cItems a_Reward)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_FISHED);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_FISHED);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerFished(a_Player, a_Reward))
|
||||
@ -760,11 +716,9 @@ bool cPluginManager::CallHookPlayerFished(cPlayer & a_Player, const cItems a_Rew
|
||||
|
||||
bool cPluginManager::CallHookPlayerFishing(cPlayer & a_Player, cItems a_Reward)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_FISHING);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_FISHING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerFishing(a_Player, a_Reward))
|
||||
@ -781,11 +735,9 @@ bool cPluginManager::CallHookPlayerFishing(cPlayer & a_Player, cItems a_Reward)
|
||||
|
||||
bool cPluginManager::CallHookPlayerJoined(cPlayer & a_Player)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_JOINED);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_JOINED);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerJoined(a_Player))
|
||||
@ -802,11 +754,9 @@ bool cPluginManager::CallHookPlayerJoined(cPlayer & a_Player)
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_LEFT_CLICK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -823,11 +773,9 @@ bool cPluginManager::CallHookPlayerLeftClick(cPlayer & a_Player, int a_BlockX, i
|
||||
|
||||
bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_MOVING);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_MOVING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerMoved(a_Player))
|
||||
@ -844,11 +792,9 @@ bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player)
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_PLACED_BLOCK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta))
|
||||
@ -865,11 +811,9 @@ bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX,
|
||||
|
||||
bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_PLACING_BLOCK);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_PLACING_BLOCK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta))
|
||||
@ -886,11 +830,9 @@ bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_RIGHT_CLICK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -907,11 +849,9 @@ bool cPluginManager::CallHookPlayerRightClick(cPlayer & a_Player, int a_BlockX,
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_RIGHT_CLICKING_ENTITY);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerRightClickingEntity(a_Player, a_Entity))
|
||||
@ -928,11 +868,9 @@ bool cPluginManager::CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEnti
|
||||
|
||||
bool cPluginManager::CallHookPlayerShooting(cPlayer & a_Player)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SHOOTING);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_SHOOTING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerShooting(a_Player))
|
||||
@ -949,11 +887,9 @@ bool cPluginManager::CallHookPlayerShooting(cPlayer & a_Player)
|
||||
|
||||
bool cPluginManager::CallHookPlayerSpawned(cPlayer & a_Player)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SPAWNED);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_SPAWNED);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerSpawned(a_Player))
|
||||
@ -970,11 +906,9 @@ bool cPluginManager::CallHookPlayerSpawned(cPlayer & a_Player)
|
||||
|
||||
bool cPluginManager::CallHookPlayerTossingItem(cPlayer & a_Player)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_TOSSING_ITEM);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_TOSSING_ITEM);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerTossingItem(a_Player))
|
||||
@ -991,11 +925,9 @@ bool cPluginManager::CallHookPlayerTossingItem(cPlayer & a_Player)
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_USED_BLOCK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -1012,11 +944,9 @@ bool cPluginManager::CallHookPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, i
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_USED_ITEM);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -1033,11 +963,9 @@ bool cPluginManager::CallHookPlayerUsedItem(cPlayer & a_Player, int a_BlockX, in
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_USING_BLOCK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -1054,11 +982,9 @@ bool cPluginManager::CallHookPlayerUsingBlock(cPlayer & a_Player, int a_BlockX,
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLAYER_USING_ITEM);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -1075,11 +1001,9 @@ bool cPluginManager::CallHookPlayerUsingItem(cPlayer & a_Player, int a_BlockX, i
|
||||
|
||||
bool cPluginManager::CallHookPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLUGIN_MESSAGE);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLUGIN_MESSAGE);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPluginMessage(a_Client, a_Channel, a_Message))
|
||||
@ -1096,11 +1020,9 @@ bool cPluginManager::CallHookPluginMessage(cClientHandle & a_Client, const AStri
|
||||
|
||||
bool cPluginManager::CallHookPluginsLoaded(void)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLUGINS_LOADED);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PLUGINS_LOADED);
|
||||
VERIFY_HOOK;
|
||||
|
||||
bool res = false;
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
@ -1115,11 +1037,9 @@ bool cPluginManager::CallHookPluginsLoaded(void)
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_POST_CRAFTING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPostCrafting(a_Player, a_Grid, a_Recipe))
|
||||
@ -1136,11 +1056,9 @@ bool cPluginManager::CallHookPostCrafting(const cPlayer * a_Player, const cCraft
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_PRE_CRAFTING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPreCrafting(a_Player, a_Grid, a_Recipe))
|
||||
@ -1155,16 +1073,14 @@ bool cPluginManager::CallHookPreCrafting(const cPlayer * a_Player, const cCrafti
|
||||
|
||||
|
||||
|
||||
bool cPluginManager::CallHookProjectileHitBlock(cProjectileEntity & a_Projectile)
|
||||
bool cPluginManager::CallHookProjectileHitBlock(cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PROJECTILE_HIT_BLOCK);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PROJECTILE_HIT_BLOCK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnProjectileHitBlock(a_Projectile))
|
||||
if ((*itr)->OnProjectileHitBlock(a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, a_BlockHitPos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -1178,11 +1094,9 @@ bool cPluginManager::CallHookProjectileHitBlock(cProjectileEntity & a_Projectile
|
||||
|
||||
bool cPluginManager::CallHookProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity & a_HitEntity)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_PROJECTILE_HIT_ENTITY);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_PROJECTILE_HIT_ENTITY);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnProjectileHitEntity(a_Projectile, a_HitEntity))
|
||||
@ -1199,11 +1113,9 @@ bool cPluginManager::CallHookProjectileHitEntity(cProjectileEntity & a_Projectil
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_SPAWNED_ENTITY);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnSpawnedEntity(a_World, a_Entity))
|
||||
@ -1219,11 +1131,9 @@ bool cPluginManager::CallHookSpawnedEntity(cWorld & a_World, cEntity & a_Entity)
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_SPAWNED_MONSTER);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnSpawnedMonster(a_World, a_Monster))
|
||||
@ -1239,11 +1149,9 @@ bool cPluginManager::CallHookSpawnedMonster(cWorld & a_World, cMonster & a_Monst
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_SPAWNING_ENTITY);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnSpawningEntity(a_World, a_Entity))
|
||||
@ -1260,11 +1168,9 @@ bool cPluginManager::CallHookSpawningEntity(cWorld & a_World, cEntity & a_Entity
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_SPAWNING_MONSTER);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnSpawningMonster(a_World, a_Monster))
|
||||
@ -1281,11 +1187,9 @@ bool cPluginManager::CallHookSpawningMonster(cWorld & a_World, cMonster & a_Mons
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_TAKE_DAMAGE);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnTakeDamage(a_Receiver, a_TDI))
|
||||
@ -1302,11 +1206,9 @@ bool cPluginManager::CallHookTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_UPDATING_SIGN);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -1323,11 +1225,9 @@ bool cPluginManager::CallHookUpdatingSign(cWorld * a_World, int a_BlockX, int a_
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_UPDATED_SIGN);
|
||||
VERIFY_HOOK;
|
||||
|
||||
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))
|
||||
@ -1344,11 +1244,9 @@ bool cPluginManager::CallHookUpdatedSign(cWorld * a_World, int a_BlockX, int a_B
|
||||
|
||||
bool cPluginManager::CallHookWeatherChanged(cWorld & a_World)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGED);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_WEATHER_CHANGED);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnWeatherChanged(a_World))
|
||||
@ -1365,11 +1263,9 @@ bool cPluginManager::CallHookWeatherChanged(cWorld & a_World)
|
||||
|
||||
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;
|
||||
}
|
||||
FIND_HOOK(HOOK_WEATHER_CHANGING);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnWeatherChanging(a_World, a_NewWeather))
|
||||
@ -1386,11 +1282,9 @@ bool cPluginManager::CallHookWeatherChanging(cWorld & a_World, eWeather & a_NewW
|
||||
|
||||
bool cPluginManager::CallHookWorldStarted(cWorld & a_World)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_WORLD_STARTED);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_WORLD_STARTED);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnWorldStarted(a_World))
|
||||
@ -1407,11 +1301,9 @@ bool cPluginManager::CallHookWorldStarted(cWorld & a_World)
|
||||
|
||||
bool cPluginManager::CallHookWorldTick(cWorld & a_World, float a_Dt, int a_LastTickDurationMSec)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_WORLD_TICK);
|
||||
if (Plugins == m_Hooks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FIND_HOOK(HOOK_WORLD_TICK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnWorldTick(a_World, a_Dt, a_LastTickDurationMSec))
|
||||
|
@ -206,7 +206,7 @@ public: // tolua_export
|
||||
bool CallHookPluginsLoaded (void);
|
||||
bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
|
||||
bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
|
||||
bool CallHookProjectileHitBlock (cProjectileEntity & a_Projectile);
|
||||
bool CallHookProjectileHitBlock (cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos);
|
||||
bool CallHookProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity);
|
||||
bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity);
|
||||
bool CallHookSpawnedMonster (cWorld & a_World, cMonster & a_Monster);
|
||||
|
@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#include "StringUtils.h"
|
||||
|
||||
|
||||
// tolua_begin
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "OSSupport/GZipFile.h"
|
||||
#include "Blocks/BlockHandler.h"
|
||||
#include "Cuboid.h"
|
||||
|
||||
#include "ChunkData.h"
|
||||
|
||||
|
||||
|
||||
@ -309,6 +309,14 @@ void cBlockArea::Clear(void)
|
||||
|
||||
void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
|
||||
{
|
||||
if ((a_SizeX < 0) || (a_SizeY < 0) || (a_SizeZ < 0))
|
||||
{
|
||||
LOGWARNING("Creating a cBlockArea with a negative size! Call to Create ignored. (%d, %d, %d)",
|
||||
a_SizeX, a_SizeY, a_SizeZ
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Clear();
|
||||
int BlockCount = a_SizeX * a_SizeY * a_SizeZ;
|
||||
if ((a_DataTypes & baTypes) != 0)
|
||||
@ -1835,18 +1843,12 @@ bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
|
||||
|
||||
|
||||
|
||||
void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes)
|
||||
void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer)
|
||||
{
|
||||
if (m_Area.m_BlockTypes == NULL)
|
||||
{
|
||||
// Don't want BlockTypes
|
||||
return;
|
||||
}
|
||||
|
||||
int SizeY = m_Area.m_Size.y;
|
||||
int MinY = m_Origin.y;
|
||||
|
||||
// SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
|
||||
|
||||
// SizeX, SizeZ are the dimensions 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;
|
||||
@ -1884,67 +1886,91 @@ void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes)
|
||||
{
|
||||
SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
|
||||
}
|
||||
|
||||
for (int y = 0; y < SizeY; y++)
|
||||
|
||||
// Copy the blocktypes:
|
||||
if (m_Area.m_BlockTypes != NULL)
|
||||
{
|
||||
int ChunkY = MinY + y;
|
||||
int AreaY = y;
|
||||
for (int z = 0; z < SizeZ; z++)
|
||||
for (int y = 0; y < SizeY; y++)
|
||||
{
|
||||
int ChunkZ = BaseZ + z;
|
||||
int AreaZ = OffZ + z;
|
||||
for (int x = 0; x < SizeX; x++)
|
||||
int InChunkY = MinY + y;
|
||||
int AreaY = y;
|
||||
for (int z = 0; z < SizeZ; z++)
|
||||
{
|
||||
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;
|
||||
int InChunkZ = BaseZ + z;
|
||||
int AreaZ = OffZ + z;
|
||||
for (int x = 0; x < SizeX; x++)
|
||||
{
|
||||
int InChunkX = BaseX + x;
|
||||
int AreaX = OffX + x;
|
||||
m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlock(InChunkX, InChunkY, InChunkZ);
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
}
|
||||
CopyNibbles(m_Area.m_BlockMetas, a_BlockMetas);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockArea::cChunkReader::BlockLight(const NIBBLETYPE * a_BlockLight)
|
||||
{
|
||||
if (m_Area.m_BlockLight == NULL)
|
||||
// Copy the block metas:
|
||||
if (m_Area.m_BlockMetas != NULL)
|
||||
{
|
||||
// Don't want light
|
||||
return;
|
||||
for (int y = 0; y < SizeY; y++)
|
||||
{
|
||||
int InChunkY = MinY + y;
|
||||
int AreaY = y;
|
||||
for (int z = 0; z < SizeZ; z++)
|
||||
{
|
||||
int InChunkZ = BaseZ + z;
|
||||
int AreaZ = OffZ + z;
|
||||
for (int x = 0; x < SizeX; x++)
|
||||
{
|
||||
int InChunkX = BaseX + x;
|
||||
int AreaX = OffX + x;
|
||||
m_Area.m_BlockMetas[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetMeta(InChunkX, InChunkY, InChunkZ);
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
}
|
||||
CopyNibbles(m_Area.m_BlockLight, a_BlockLight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockArea::cChunkReader::BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight)
|
||||
{
|
||||
if (m_Area.m_BlockSkyLight == NULL)
|
||||
// Copy the blocklight:
|
||||
if (m_Area.m_BlockLight != NULL)
|
||||
{
|
||||
// Don't want skylight
|
||||
return;
|
||||
for (int y = 0; y < SizeY; y++)
|
||||
{
|
||||
int InChunkY = MinY + y;
|
||||
int AreaY = y;
|
||||
for (int z = 0; z < SizeZ; z++)
|
||||
{
|
||||
int InChunkZ = BaseZ + z;
|
||||
int AreaZ = OffZ + z;
|
||||
for (int x = 0; x < SizeX; x++)
|
||||
{
|
||||
int InChunkX = BaseX + x;
|
||||
int AreaX = OffX + x;
|
||||
m_Area.m_BlockLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlockLight(InChunkX, InChunkY, InChunkZ);
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
}
|
||||
CopyNibbles(m_Area.m_BlockSkyLight, a_BlockSkyLight);
|
||||
}
|
||||
|
||||
// Copy the skylight:
|
||||
if (m_Area.m_BlockSkyLight != NULL)
|
||||
{
|
||||
for (int y = 0; y < SizeY; y++)
|
||||
{
|
||||
int InChunkY = MinY + y;
|
||||
int AreaY = y;
|
||||
for (int z = 0; z < SizeZ; z++)
|
||||
{
|
||||
int InChunkZ = BaseZ + z;
|
||||
int AreaZ = OffZ + z;
|
||||
for (int x = 0; x < SizeX; x++)
|
||||
{
|
||||
int InChunkX = BaseX + x;
|
||||
int AreaX = OffX + x;
|
||||
m_Area.m_BlockSkyLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetSkyLight(InChunkX, InChunkY, InChunkZ);
|
||||
} // for x
|
||||
} // for z
|
||||
} // for y
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "ForEachChunkProvider.h"
|
||||
#include "Vector3.h"
|
||||
#include "ChunkDataCallback.h"
|
||||
|
||||
|
||||
|
||||
@ -316,11 +317,8 @@ protected:
|
||||
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;
|
||||
virtual bool Coords(int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual void ChunkData(const cChunkData & a_BlockTypes) override;
|
||||
} ;
|
||||
|
||||
typedef NIBBLETYPE * NIBBLEARRAY;
|
||||
|
@ -6,6 +6,10 @@
|
||||
#include "../Simulator/FluidSimulator.h"
|
||||
#include "../Chunk.h"
|
||||
|
||||
#include "../World.h"
|
||||
#include "../Entities/ArrowEntity.h"
|
||||
#include "../Entities/FireChargeEntity.h"
|
||||
#include "../Entities/ProjectileEntity.h"
|
||||
|
||||
|
||||
|
||||
@ -33,7 +37,10 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
|
||||
// Would dispense into / interact with a non-loaded chunk, ignore the tick
|
||||
return;
|
||||
}
|
||||
|
||||
BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ);
|
||||
int BlockX = (DispX + DispChunk->GetPosX() * cChunkDef::Width);
|
||||
int BlockZ = (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
|
||||
|
||||
// Dispense the item:
|
||||
switch (m_Contents.GetSlot(a_SlotNum).m_ItemType)
|
||||
@ -69,7 +76,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
|
||||
}
|
||||
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);
|
||||
@ -83,7 +90,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int 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);
|
||||
@ -97,7 +104,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case E_ITEM_SPAWN_EGG:
|
||||
{
|
||||
double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
|
||||
@ -108,7 +115,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case E_BLOCK_TNT:
|
||||
{
|
||||
// Spawn a primed TNT entity, if space allows:
|
||||
@ -128,7 +135,7 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
|
||||
if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
|
||||
{
|
||||
DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0);
|
||||
|
||||
|
||||
bool ItemBroke = m_Contents.DamageItem(a_SlotNum, 1);
|
||||
|
||||
if (ItemBroke)
|
||||
@ -138,13 +145,41 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case E_ITEM_FIRE_CHARGE:
|
||||
{
|
||||
// TODO: Spawn fireball entity
|
||||
SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkFireCharge, GetShootVector(Meta) * 20);
|
||||
m_Contents.ChangeSlotCount(a_SlotNum, -1);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case E_ITEM_ARROW:
|
||||
{
|
||||
SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkArrow, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0));
|
||||
m_Contents.ChangeSlotCount(a_SlotNum, -1);
|
||||
break;
|
||||
}
|
||||
|
||||
case E_ITEM_SNOWBALL:
|
||||
{
|
||||
SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkSnowball, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0));
|
||||
m_Contents.ChangeSlotCount(a_SlotNum, -1);
|
||||
break;
|
||||
}
|
||||
|
||||
case E_ITEM_EGG:
|
||||
{
|
||||
SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkEgg, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0));
|
||||
m_Contents.ChangeSlotCount(a_SlotNum, -1);
|
||||
break;
|
||||
}
|
||||
|
||||
case E_ITEM_FIREWORK_ROCKET:
|
||||
{
|
||||
// TODO: Add the fireworks entity
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
DropFromSlot(a_Chunk, a_SlotNum);
|
||||
@ -157,6 +192,34 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
|
||||
|
||||
|
||||
|
||||
void cDispenserEntity::SpawnProjectileFromDispenser(int a_BlockX, int a_BlockY, int a_BlockZ, cProjectileEntity::eKind a_Kind, const Vector3d & a_ShootVector)
|
||||
{
|
||||
m_World->CreateProjectile((double)a_BlockX + 0.5, (double)a_BlockY + 0.5, (double)a_BlockZ + 0.5, a_Kind, NULL, NULL, &a_ShootVector);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Vector3d cDispenserEntity::GetShootVector(NIBBLETYPE a_Meta)
|
||||
{
|
||||
switch (a_Meta)
|
||||
{
|
||||
case E_META_DROPSPENSER_FACING_YP: return Vector3d( 0, 1, 0);
|
||||
case E_META_DROPSPENSER_FACING_YM: return Vector3d( 0, -1, 0);
|
||||
case E_META_DROPSPENSER_FACING_XM: return Vector3d(-1, 0, 0);
|
||||
case E_META_DROPSPENSER_FACING_XP: return Vector3d( 1, 0, 0);
|
||||
case E_META_DROPSPENSER_FACING_ZM: return Vector3d( 0, 0, -1);
|
||||
case E_META_DROPSPENSER_FACING_ZP: return Vector3d( 0, 0, 1);
|
||||
}
|
||||
LOGWARNING("Unhandled dispenser meta: %d", a_Meta);
|
||||
ASSERT(!"Unhandled dispenser facing");
|
||||
return Vector3d(0, 1, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType)
|
||||
{
|
||||
@ -167,14 +230,14 @@ bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType)
|
||||
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;
|
||||
@ -195,7 +258,7 @@ bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum
|
||||
// Not a suitable block in front
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
cItem EmptyBucket(E_ITEM_BUCKET, 1);
|
||||
if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
|
||||
{
|
||||
@ -203,14 +266,14 @@ bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum
|
||||
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);
|
||||
@ -219,4 +282,3 @@ bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -12,27 +12,37 @@ class cDispenserEntity :
|
||||
public cDropSpenserEntity
|
||||
{
|
||||
typedef cDropSpenserEntity super;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// tolua_end
|
||||
|
||||
/// Constructor used for normal operation
|
||||
|
||||
/** 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"; }
|
||||
|
||||
// tolua_begin
|
||||
|
||||
/** Spawns a projectile of the given kind in front of the dispenser with the specified speed. */
|
||||
void SpawnProjectileFromDispenser(int a_BlockX, int a_BlockY, int a_BlockZ, cProjectileEntity::eKind a_Kind, const Vector3d & a_Speed);
|
||||
|
||||
/** Returns a unit vector in the cardinal direction of where the dispenser is facing. */
|
||||
Vector3d GetShootVector(NIBBLETYPE a_Meta);
|
||||
|
||||
// tolua_end
|
||||
|
||||
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
|
||||
|
||||
/** 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
|
||||
|
||||
/** If the a_BlockInFront can be washed away by liquid and the empty bucket can fit,
|
||||
does the m_Contents processing and returns true. Returns false otherwise. */
|
||||
bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum);
|
||||
} ; // tolua_export
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -790,6 +790,7 @@ enum eDimension
|
||||
dimNether = -1,
|
||||
dimOverworld = 0,
|
||||
dimEnd = 1,
|
||||
dimNotSet = 255, // For things that need an "indeterminate" state, such as cProtocol's LastSentDimension
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -62,7 +62,7 @@ void cBlockDoorHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, c
|
||||
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
|
||||
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
|
||||
if (Meta & 8)
|
||||
if (Meta & 0x8)
|
||||
{
|
||||
// Current block is top of the door
|
||||
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, a_Player);
|
||||
|
@ -20,12 +20,12 @@ public:
|
||||
virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
|
||||
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override;
|
||||
virtual const char * GetStepSound(void) override;
|
||||
|
||||
|
||||
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override;
|
||||
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override;
|
||||
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override;
|
||||
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override;
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface, cPlayer * a_Player,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
|
||||
@ -52,7 +52,7 @@ public:
|
||||
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));
|
||||
@ -77,8 +77,8 @@ public:
|
||||
{
|
||||
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)
|
||||
@ -99,7 +99,7 @@ public:
|
||||
}
|
||||
|
||||
|
||||
/// Converts the player's yaw to placed door's blockmeta
|
||||
/** Converts the player's yaw to placed door's blockmeta */
|
||||
inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
|
||||
{
|
||||
ASSERT((a_Yaw >= -180) && (a_Yaw < 180));
|
||||
@ -111,67 +111,109 @@ public:
|
||||
}
|
||||
if ((a_Yaw >= 0) && (a_Yaw < 90))
|
||||
{
|
||||
return 0x0;
|
||||
return 0x00;
|
||||
}
|
||||
else if ((a_Yaw >= 180) && (a_Yaw < 270))
|
||||
{
|
||||
return 0x2;
|
||||
return 0x02;
|
||||
}
|
||||
else if ((a_Yaw >= 90) && (a_Yaw < 180))
|
||||
{
|
||||
return 0x1;
|
||||
return 0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0x3;
|
||||
return 0x03;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns true if the specified blocktype is any kind of door
|
||||
/** 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)
|
||||
static NIBBLETYPE IsOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
return a_MetaData ^ 4;
|
||||
NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
|
||||
return ((Meta & 0x04) != 0);
|
||||
}
|
||||
|
||||
|
||||
/// Changes the door at the specified coords from open to close or vice versa
|
||||
static void ChangeDoor(cChunkInterface & a_ChunkInterface, int a_X, int a_Y, int a_Z)
|
||||
/** Returns the complete meta composed from the both parts of the door as (TopMeta << 4) | BottomMeta
|
||||
The coords may point to either part of the door.
|
||||
The returned value has bit 3 (0x08) set iff the coords point to the top part of the door.
|
||||
Fails gracefully for (invalid) doors on the world's top and bottom. */
|
||||
static NIBBLETYPE GetCompleteDoorMeta(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
NIBBLETYPE OldMetaData = a_ChunkInterface.GetBlockMeta(a_X, a_Y, a_Z);
|
||||
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
|
||||
a_ChunkInterface.SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData(OldMetaData));
|
||||
|
||||
if (OldMetaData & 8)
|
||||
if ((Meta & 0x08) != 0)
|
||||
{
|
||||
// Current block is top of the door
|
||||
BLOCKTYPE BottomBlock = a_ChunkInterface.GetBlock(a_X, a_Y - 1, a_Z);
|
||||
NIBBLETYPE BottomMeta = a_ChunkInterface.GetBlockMeta(a_X, a_Y - 1, a_Z);
|
||||
|
||||
if (IsDoor(BottomBlock) && !(BottomMeta & 8))
|
||||
// The coords are pointing at the top part of the door
|
||||
if (a_BlockX > 0)
|
||||
{
|
||||
a_ChunkInterface.SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData(BottomMeta));
|
||||
NIBBLETYPE DownMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ);
|
||||
return (DownMeta & 0x07) | 0x08 | (Meta << 4);
|
||||
}
|
||||
// This is the top part of the door at the bottommost layer of the world, there's no bottom:
|
||||
return 0x08 | (Meta << 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Current block is bottom of the door
|
||||
BLOCKTYPE TopBlock = a_ChunkInterface.GetBlock(a_X, a_Y + 1, a_Z);
|
||||
NIBBLETYPE TopMeta = a_ChunkInterface.GetBlockMeta(a_X, a_Y + 1, a_Z);
|
||||
|
||||
if (IsDoor(TopBlock) && (TopMeta & 8))
|
||||
// The coords are pointing at the bottom part of the door
|
||||
if (a_BlockY < cChunkDef::Height - 1)
|
||||
{
|
||||
a_ChunkInterface.SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData(TopMeta));
|
||||
NIBBLETYPE UpMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ);
|
||||
return Meta | (UpMeta << 4);
|
||||
}
|
||||
// This is the bottom part of the door at the topmost layer of the world, there's no top:
|
||||
return Meta;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Sets the door to the specified state. If the door is already in that state, does nothing. */
|
||||
static void SetOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open)
|
||||
{
|
||||
BLOCKTYPE Block = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ);
|
||||
if (!IsDoor(Block))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
|
||||
bool IsOpened = ((Meta & 0x04) != 0);
|
||||
if (IsOpened == a_Open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Change the door
|
||||
NIBBLETYPE NewMeta = (Meta & 0x07) ^ 0x04; // Flip the "IsOpen" bit (0x04)
|
||||
if ((Meta & 0x08) == 0)
|
||||
{
|
||||
// The block is the bottom part of the door
|
||||
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, NewMeta);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The block is the top part of the door, set the meta to the corresponding top part
|
||||
if (a_BlockY > 0)
|
||||
{
|
||||
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ, NewMeta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Changes the door at the specified coords from open to close or vice versa */
|
||||
static void ChangeDoor(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
SetOpen(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ, !IsOpen(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ));
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Piston.h"
|
||||
#include "../Blocks/BlockPiston.h"
|
||||
#include "MetaRotator.h"
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ public:
|
||||
a_BlockType = m_BlockType;
|
||||
|
||||
// FIXME: Do not use cPiston class for dispenser placement!
|
||||
a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
|
||||
a_BlockMeta = cBlockPistonHandler::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "BlockEntity.h"
|
||||
#include "../World.h"
|
||||
#include "../Piston.h"
|
||||
#include "../Blocks/BlockPiston.h"
|
||||
#include "MetaRotator.h"
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ public:
|
||||
a_BlockType = m_BlockType;
|
||||
|
||||
// FIXME: Do not use cPiston class for furnace placement!
|
||||
a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), 0);
|
||||
a_BlockMeta = cBlockPistonHandler::RotationPitchToMetaData(a_Player->GetYaw(), 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -46,8 +46,30 @@ public:
|
||||
bool IsWither(void) const { return m_IsWither; }
|
||||
|
||||
void Reset(void) { m_IsWither = false; }
|
||||
|
||||
} CallbackA, CallbackB;
|
||||
|
||||
class cPlayerCallback : public cPlayerListCallback
|
||||
{
|
||||
Vector3f m_Pos;
|
||||
|
||||
virtual bool Item(cPlayer * a_Player)
|
||||
{
|
||||
// TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one
|
||||
double Dist = (a_Player->GetPosition() - m_Pos).Length();
|
||||
if (Dist < 50.0)
|
||||
{
|
||||
// If player is close, award achievement
|
||||
a_Player->AwardAchievement(achSpawnWither);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
|
||||
|
||||
} PlayerCallback(Vector3f((float)a_BlockX, (float)a_BlockY, (float)a_BlockZ));
|
||||
|
||||
a_World->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ, CallbackA);
|
||||
|
||||
if (!CallbackA.IsWither())
|
||||
@ -86,6 +108,9 @@ public:
|
||||
// Spawn the wither:
|
||||
a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtWither);
|
||||
|
||||
// Award Achievement
|
||||
a_World->ForEachPlayer(PlayerCallback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -113,6 +138,9 @@ public:
|
||||
// Spawn the wither:
|
||||
a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtWither);
|
||||
|
||||
// Award Achievement
|
||||
a_World->ForEachPlayer(PlayerCallback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,14 @@
|
||||
#include "../Item.h"
|
||||
#include "../World.h"
|
||||
#include "../Entities/Player.h"
|
||||
#include "../Piston.h"
|
||||
#include "BlockInServerPluginInterface.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define AddPistonDir(x, y, z, dir, amount) \
|
||||
switch (dir) \
|
||||
switch (dir & 0x07) \
|
||||
{ \
|
||||
case 0: (y) -= (amount); break; \
|
||||
case 1: (y) += (amount); break; \
|
||||
@ -19,8 +19,16 @@
|
||||
case 3: (z) += (amount); break; \
|
||||
case 4: (x) -= (amount); break; \
|
||||
case 5: (x) += (amount); break; \
|
||||
default: \
|
||||
{ \
|
||||
LOGWARNING("%s: invalid direction %d, ignoring", __FUNCTION__, dir & 0x07); \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PISTON_TICK_DELAY 1
|
||||
#define PISTON_MAX_PUSH_DISTANCE 12
|
||||
|
||||
|
||||
|
||||
|
||||
@ -40,7 +48,7 @@ void cBlockPistonHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorld
|
||||
int newX = a_BlockX;
|
||||
int newY = a_BlockY;
|
||||
int newZ = a_BlockZ;
|
||||
AddPistonDir(newX, newY, newZ, OldMeta & ~(8), 1);
|
||||
AddPistonDir(newX, newY, newZ, OldMeta, 1);
|
||||
|
||||
if (a_ChunkInterface.GetBlock(newX, newY, newZ) == E_BLOCK_PISTON_EXTENSION)
|
||||
{
|
||||
@ -60,7 +68,7 @@ bool cBlockPistonHandler::GetPlacementBlockTypeMeta(
|
||||
)
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
|
||||
a_BlockMeta = RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -68,6 +76,165 @@ bool cBlockPistonHandler::GetPlacementBlockTypeMeta(
|
||||
|
||||
|
||||
|
||||
int cBlockPistonHandler::FirstPassthroughBlock(int a_PistonX, int a_PistonY, int a_PistonZ, NIBBLETYPE pistonmeta, cWorld * a_World)
|
||||
{
|
||||
// Examine each of the 12 blocks ahead of the piston:
|
||||
for (int ret = 0; ret < PISTON_MAX_PUSH_DISTANCE; ret++)
|
||||
{
|
||||
BLOCKTYPE currBlock;
|
||||
NIBBLETYPE currMeta;
|
||||
AddPistonDir(a_PistonX, a_PistonY, a_PistonZ, pistonmeta, 1);
|
||||
a_World->GetBlockTypeMeta(a_PistonX, a_PistonY, a_PistonZ, currBlock, currMeta);
|
||||
if (CanBreakPush(currBlock))
|
||||
{
|
||||
// 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 cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
|
||||
{
|
||||
BLOCKTYPE pistonBlock;
|
||||
NIBBLETYPE pistonMeta;
|
||||
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta);
|
||||
|
||||
if (IsExtended(pistonMeta))
|
||||
{
|
||||
// Already extended, bail out
|
||||
return;
|
||||
}
|
||||
|
||||
int dist = FirstPassthroughBlock(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, a_World);
|
||||
if (dist < 0)
|
||||
{
|
||||
// FirstPassthroughBlock says piston can't push anything, bail out
|
||||
return;
|
||||
}
|
||||
|
||||
a_World->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, 0, pistonMeta, pistonBlock);
|
||||
a_World->BroadcastSoundEffect("tile.piston.out", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.7f);
|
||||
|
||||
// Drop the breakable block in the line, if appropriate:
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, dist + 1); // "a_Block" now at the breakable / empty block
|
||||
BLOCKTYPE currBlock;
|
||||
NIBBLETYPE currMeta;
|
||||
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currMeta);
|
||||
if (currBlock != E_BLOCK_AIR)
|
||||
{
|
||||
cBlockHandler * Handler = BlockHandler(currBlock);
|
||||
if (Handler->DoesDropOnUnsuitable())
|
||||
{
|
||||
cChunkInterface ChunkInterface(a_World->GetChunkMap());
|
||||
cBlockInServerPluginInterface PluginInterface(*a_World);
|
||||
Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, NULL, a_BlockX, a_BlockY, a_BlockZ);
|
||||
}
|
||||
}
|
||||
|
||||
// Push blocks, from the furthest to the nearest:
|
||||
int oldx = a_BlockX, oldy = a_BlockY, oldz = a_BlockZ;
|
||||
NIBBLETYPE currBlockMeta;
|
||||
std::vector<Vector3i> ScheduledBlocks;
|
||||
ScheduledBlocks.reserve(PISTON_MAX_PUSH_DISTANCE);
|
||||
|
||||
for (int i = dist + 1; i > 1; i--)
|
||||
{
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1);
|
||||
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currBlockMeta);
|
||||
a_World->SetBlock(oldx, oldy, oldz, currBlock, currBlockMeta, false);
|
||||
ScheduledBlocks.push_back(Vector3i(oldx, oldy, oldz));
|
||||
oldx = a_BlockX;
|
||||
oldy = a_BlockY;
|
||||
oldz = a_BlockZ;
|
||||
}
|
||||
|
||||
int extx = a_BlockX;
|
||||
int exty = a_BlockY;
|
||||
int extz = a_BlockZ;
|
||||
ScheduledBlocks.push_back(Vector3i(extx, exty, extz));
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1);
|
||||
// "a_Block" now at piston body, "ext" at future extension
|
||||
|
||||
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta | 0x8);
|
||||
a_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), false);
|
||||
a_World->ScheduleTask(PISTON_TICK_DELAY, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockPistonHandler::RetractPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
|
||||
{
|
||||
BLOCKTYPE pistonBlock;
|
||||
NIBBLETYPE pistonMeta;
|
||||
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta);
|
||||
|
||||
if (!IsExtended(pistonMeta))
|
||||
{
|
||||
// Already retracted, bail out
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the extension:
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, 1);
|
||||
if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_PISTON_EXTENSION)
|
||||
{
|
||||
LOGD("%s: Piston without an extension - still extending, or just in an invalid state?", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1);
|
||||
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta & ~(8));
|
||||
a_World->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, 1, pistonMeta & ~(8), pistonBlock);
|
||||
a_World->BroadcastSoundEffect("tile.piston.in", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.7f);
|
||||
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, 1);
|
||||
|
||||
// Retract the extension, pull block if appropriate
|
||||
if (IsSticky(pistonBlock))
|
||||
{
|
||||
int tempx = a_BlockX, tempy = a_BlockY, tempz = a_BlockZ;
|
||||
AddPistonDir(tempx, tempy, tempz, pistonMeta, 1);
|
||||
BLOCKTYPE tempBlock;
|
||||
NIBBLETYPE tempMeta;
|
||||
a_World->GetBlockTypeMeta(tempx, tempy, tempz, tempBlock, tempMeta);
|
||||
if (CanPull(tempBlock, tempMeta))
|
||||
{
|
||||
// Pull the block
|
||||
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, tempBlock, tempMeta, false);
|
||||
a_World->SetBlock(tempx, tempy, tempz, E_BLOCK_AIR, 0, false);
|
||||
|
||||
std::vector<Vector3i> ScheduledBlocks;
|
||||
ScheduledBlocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
|
||||
ScheduledBlocks.push_back(Vector3i(tempx, tempy, tempz));
|
||||
a_World->ScheduleTask(PISTON_TICK_DELAY + 1, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Retract without pulling
|
||||
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0, false);
|
||||
|
||||
std::vector<Vector3i> ScheduledBlocks;
|
||||
ScheduledBlocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
|
||||
a_World->ScheduleTask(PISTON_TICK_DELAY + 1, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cBlockPistonHeadHandler:
|
||||
|
||||
@ -87,7 +254,7 @@ void cBlockPistonHeadHandler::OnDestroyedByPlayer(cChunkInterface & a_ChunkInter
|
||||
int newX = a_BlockX;
|
||||
int newY = a_BlockY;
|
||||
int newZ = a_BlockZ;
|
||||
AddPistonDir(newX, newY, newZ, OldMeta & ~(8), -1);
|
||||
AddPistonDir(newX, newY, newZ, OldMeta, -1);
|
||||
|
||||
BLOCKTYPE Block = a_ChunkInterface.GetBlock(newX, newY, newZ);
|
||||
if ((Block == E_BLOCK_STICKY_PISTON) || (Block == E_BLOCK_PISTON))
|
||||
|
@ -21,6 +21,138 @@ public:
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static eBlockFace MetaDataToDirection(NIBBLETYPE a_MetaData)
|
||||
{
|
||||
switch (a_MetaData)
|
||||
{
|
||||
case 0x0: return BLOCK_FACE_YM;
|
||||
case 0x1: return BLOCK_FACE_YP;
|
||||
case 0x2: return BLOCK_FACE_ZM;
|
||||
case 0x3: return BLOCK_FACE_ZP;
|
||||
case 0x4: return BLOCK_FACE_XM;
|
||||
case 0x5: return BLOCK_FACE_XP;
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Invalid Metadata");
|
||||
return BLOCK_FACE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
|
||||
static void RetractPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
|
||||
|
||||
private:
|
||||
|
||||
/// Returns true if the piston (specified by blocktype) is a sticky piston
|
||||
static inline bool IsSticky(BLOCKTYPE a_BlockType) { return (a_BlockType == E_BLOCK_STICKY_PISTON); }
|
||||
|
||||
/// Returns true if the piston (with the specified meta) is extended
|
||||
static inline bool IsExtended(NIBBLETYPE a_PistonMeta) { return ((a_PistonMeta & 0x8) != 0x0); }
|
||||
|
||||
/// Returns true if the specified block can be pushed by a piston (and left intact)
|
||||
static inline bool 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;
|
||||
}
|
||||
|
||||
/// Returns true if the specified block can be pushed by a piston and broken / replaced
|
||||
static inline bool CanBreakPush(BLOCKTYPE a_BlockType) { return cBlockInfo::IsPistonBreakable(a_BlockType); }
|
||||
|
||||
/// Returns true if the specified block can be pulled by a sticky piston
|
||||
static inline bool 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))
|
||||
{
|
||||
return false; // CanBreakPush returns true, but we need false to prevent pulling
|
||||
}
|
||||
|
||||
return CanPush(a_BlockType, a_BlockMeta);
|
||||
}
|
||||
|
||||
/// Returns how many blocks the piston has to push (where the first free space is); < 0 when unpushable
|
||||
static int FirstPassthroughBlock(int a_PistonX, int a_PistonY, int a_PistonZ, NIBBLETYPE a_PistonMeta, cWorld * a_World);
|
||||
} ;
|
||||
|
||||
|
||||
@ -40,7 +172,7 @@ public:
|
||||
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
|
||||
{
|
||||
// No pickups
|
||||
// Also with 1.7, the item forms of these tecnical blocks have been removed, so giving someone this will crash their client...
|
||||
// Also with 1.7, the item forms of these technical blocks have been removed, so giving someone this will crash their client...
|
||||
}
|
||||
} ;
|
||||
|
||||
|
@ -762,7 +762,6 @@ bool cByteBuffer::ReadUTF16String(AString & a_String, size_t 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))
|
||||
{
|
||||
|
@ -8,6 +8,91 @@ include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/polarssl/include")
|
||||
set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++)
|
||||
set(FOLDERS ${FOLDERS} WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs)
|
||||
|
||||
set(BINDING_DEPENDECIES
|
||||
tolua
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Bindings/virtual_method_hooks.lua
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Bindings/AllToLua.pkg
|
||||
Bindings/LuaFunctions.h
|
||||
Bindings/LuaWindow.h
|
||||
Bindings/Plugin.h
|
||||
Bindings/PluginLua.h
|
||||
Bindings/PluginManager.h
|
||||
Bindings/WebPlugin.h
|
||||
BiomeDef.h
|
||||
BlockArea.h
|
||||
BlockEntities/BlockEntity.h
|
||||
BlockEntities/BlockEntityWithItems.h
|
||||
BlockEntities/ChestEntity.h
|
||||
BlockEntities/DispenserEntity.h
|
||||
BlockEntities/DropSpenserEntity.h
|
||||
BlockEntities/DropperEntity.h
|
||||
BlockEntities/FurnaceEntity.h
|
||||
BlockEntities/HopperEntity.h
|
||||
BlockEntities/JukeboxEntity.h
|
||||
BlockEntities/NoteEntity.h
|
||||
BlockEntities/SignEntity.h
|
||||
BlockEntities/MobHeadEntity.h
|
||||
BlockEntities/FlowerPotEntity.h
|
||||
BlockID.h
|
||||
BoundingBox.h
|
||||
ChatColor.h
|
||||
ChunkDef.h
|
||||
ClientHandle.h
|
||||
CraftingRecipes.h
|
||||
Cuboid.h
|
||||
Defines.h
|
||||
Enchantments.h
|
||||
Entities/Effects.h
|
||||
Entities/Entity.h
|
||||
Entities/Floater.h
|
||||
Entities/Pawn.h
|
||||
Entities/Painting.h
|
||||
Entities/Pickup.h
|
||||
Entities/Player.h
|
||||
Entities/ProjectileEntity.h
|
||||
Entities/ArrowEntity.h
|
||||
Entities/ThrownEggEntity.h
|
||||
Entities/ThrownEnderPearlEntity.h
|
||||
Entities/ExpBottleEntity.h
|
||||
Entities/ThrownSnowballEntity.h
|
||||
Entities/FireChargeEntity.h
|
||||
Entities/FireworkEntity.h
|
||||
Entities/GhastFireballEntity.h
|
||||
Entities/TNTEntity.h
|
||||
Entities/ExpOrb.h
|
||||
Entities/HangingEntity.h
|
||||
Entities/ItemFrame.h
|
||||
Generating/ChunkDesc.h
|
||||
Group.h
|
||||
Inventory.h
|
||||
Item.h
|
||||
ItemGrid.h
|
||||
Mobs/Monster.h
|
||||
OSSupport/File.h
|
||||
Root.h
|
||||
Server.h
|
||||
StringUtils.h
|
||||
Tracer.h
|
||||
UI/Window.h
|
||||
Vector3.h
|
||||
WebAdmin.h
|
||||
World.h
|
||||
)
|
||||
|
||||
include_directories(Bindings)
|
||||
include_directories(.)
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
# add any new generated bindings here
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Bindings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Bindings.h
|
||||
|
||||
# command execuded to regerate bindings
|
||||
COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
|
||||
|
||||
# add any new generation dependencies here
|
||||
DEPENDS ${BINDING_DEPENDECIES}
|
||||
)
|
||||
|
||||
if (NOT MSVC)
|
||||
|
||||
@ -15,91 +100,7 @@ if (NOT MSVC)
|
||||
|
||||
# lib dependencies are not included
|
||||
|
||||
set(BINDING_DEPENDECIES
|
||||
tolua
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Bindings/virtual_method_hooks.lua
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Bindings/AllToLua.pkg
|
||||
Bindings/LuaFunctions.h
|
||||
Bindings/LuaWindow.h
|
||||
Bindings/Plugin.h
|
||||
Bindings/PluginLua.h
|
||||
Bindings/PluginManager.h
|
||||
Bindings/WebPlugin.h
|
||||
BiomeDef.h
|
||||
BlockArea.h
|
||||
BlockEntities/BlockEntity.h
|
||||
BlockEntities/BlockEntityWithItems.h
|
||||
BlockEntities/ChestEntity.h
|
||||
BlockEntities/DispenserEntity.h
|
||||
BlockEntities/DropSpenserEntity.h
|
||||
BlockEntities/DropperEntity.h
|
||||
BlockEntities/FurnaceEntity.h
|
||||
BlockEntities/HopperEntity.h
|
||||
BlockEntities/JukeboxEntity.h
|
||||
BlockEntities/NoteEntity.h
|
||||
BlockEntities/SignEntity.h
|
||||
BlockEntities/MobHeadEntity.h
|
||||
BlockEntities/FlowerPotEntity.h
|
||||
BlockID.h
|
||||
BoundingBox.h
|
||||
ChatColor.h
|
||||
ChunkDef.h
|
||||
ClientHandle.h
|
||||
CraftingRecipes.h
|
||||
Cuboid.h
|
||||
Defines.h
|
||||
Enchantments.h
|
||||
Entities/Effects.h
|
||||
Entities/Entity.h
|
||||
Entities/Floater.h
|
||||
Entities/Pawn.h
|
||||
Entities/Painting.h
|
||||
Entities/Pickup.h
|
||||
Entities/Player.h
|
||||
Entities/ProjectileEntity.h
|
||||
Entities/ArrowEntity.h
|
||||
Entities/ThrownEggEntity.h
|
||||
Entities/ThrownEnderPearlEntity.h
|
||||
Entities/ExpBottleEntity.h
|
||||
Entities/ThrownSnowballEntity.h
|
||||
Entities/FireChargeEntity.h
|
||||
Entities/FireworkEntity.h
|
||||
Entities/GhastFireballEntity.h
|
||||
Entities/TNTEntity.h
|
||||
Entities/ExpOrb.h
|
||||
Entities/HangingEntity.h
|
||||
Entities/ItemFrame.h
|
||||
Generating/ChunkDesc.h
|
||||
Group.h
|
||||
Inventory.h
|
||||
Item.h
|
||||
ItemGrid.h
|
||||
Mobs/Monster.h
|
||||
OSSupport/File.h
|
||||
Root.h
|
||||
Server.h
|
||||
StringUtils.h
|
||||
Tracer.h
|
||||
UI/Window.h
|
||||
Vector3.h
|
||||
WebAdmin.h
|
||||
World.h
|
||||
)
|
||||
|
||||
include_directories(Bindings)
|
||||
include_directories(.)
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
# add any new generated bindings here
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Bindings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Bindings.h
|
||||
|
||||
# command execuded to regerate bindings
|
||||
COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
|
||||
|
||||
# add any new generation dependencies here
|
||||
DEPENDS ${BINDING_DEPENDECIES}
|
||||
)
|
||||
#add cpp files here
|
||||
add_library(Bindings
|
||||
Bindings/Bindings
|
||||
|
216
src/Chunk.cpp
@ -238,26 +238,12 @@ void cChunk::MarkLoadFailed(void)
|
||||
|
||||
void cChunk::GetAllData(cChunkDataCallback & a_Callback)
|
||||
{
|
||||
a_Callback.HeightMap (&m_HeightMap);
|
||||
a_Callback.BiomeData (&m_BiomeMap);
|
||||
a_Callback.HeightMap(&m_HeightMap);
|
||||
a_Callback.BiomeData(&m_BiomeMap);
|
||||
|
||||
COMPRESSED_BLOCKTYPE Blocks = m_BlockTypes;
|
||||
Blocks.resize(NumBlocks);
|
||||
a_Callback.BlockTypes (&Blocks[0]);
|
||||
a_Callback.LightIsValid(m_IsLightValid);
|
||||
|
||||
COMPRESSED_NIBBLETYPE Metas = m_BlockMeta;
|
||||
Metas.resize(NumBlocks / 2);
|
||||
a_Callback.BlockMeta (&Metas[0]);
|
||||
|
||||
a_Callback.LightIsValid (m_IsLightValid);
|
||||
|
||||
COMPRESSED_NIBBLETYPE BlockLights = m_BlockLight;
|
||||
BlockLights.resize(NumBlocks / 2);
|
||||
a_Callback.BlockLight (&BlockLights[0]);
|
||||
|
||||
COMPRESSED_NIBBLETYPE BlockSkyLights = m_BlockSkyLight;
|
||||
BlockSkyLights.resize(NumBlocks / 2, 0xff);
|
||||
a_Callback.BlockSkyLight(&BlockSkyLights[0]);
|
||||
a_Callback.ChunkData(m_ChunkData);
|
||||
|
||||
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
|
||||
{
|
||||
@ -296,48 +282,10 @@ void cChunk::SetAllData(
|
||||
CalculateHeightmap(a_BlockTypes);
|
||||
}
|
||||
|
||||
int IdxWhereNonEmptyStarts = 0;
|
||||
{ // Blocktype compression
|
||||
unsigned char Highest = 0;
|
||||
int X = 0, Z = 0;
|
||||
m_BlockTypes.clear();
|
||||
|
||||
for (int x = 0; x < Width; x++)
|
||||
{
|
||||
for (int z = 0; z < Width; z++)
|
||||
{
|
||||
unsigned char Height = m_HeightMap[x + z * Width];
|
||||
if (Height > Highest)
|
||||
{
|
||||
Highest = Height;
|
||||
X = x; Z = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IdxWhereNonEmptyStarts = MakeIndexNoCheck(X, Highest + 1, Z);
|
||||
|
||||
m_BlockTypes.insert(m_BlockTypes.end(), &a_BlockTypes[0], &a_BlockTypes[IdxWhereNonEmptyStarts]);
|
||||
}
|
||||
|
||||
{ // Blockmeta compression
|
||||
m_BlockMeta.clear();
|
||||
m_BlockMeta.insert(m_BlockMeta.end(), &a_BlockMeta[0], &a_BlockMeta[IdxWhereNonEmptyStarts / 2]);
|
||||
}
|
||||
|
||||
if (a_BlockLight != NULL)
|
||||
{
|
||||
// Compress blocklight
|
||||
m_BlockLight.clear();
|
||||
m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[IdxWhereNonEmptyStarts / 2]);
|
||||
}
|
||||
|
||||
if (a_BlockSkyLight != NULL)
|
||||
{
|
||||
// Compress skylight
|
||||
m_BlockSkyLight.clear();
|
||||
m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_BlockSkyLight[0], &a_BlockSkyLight[IdxWhereNonEmptyStarts / 2]);
|
||||
}
|
||||
m_ChunkData.SetBlockTypes(a_BlockTypes);
|
||||
m_ChunkData.SetMetas(a_BlockMeta);
|
||||
m_ChunkData.SetBlockLight(a_BlockLight);
|
||||
m_ChunkData.SetSkyLight(a_BlockSkyLight);
|
||||
|
||||
m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL);
|
||||
|
||||
@ -378,15 +326,9 @@ void cChunk::SetLight(
|
||||
// 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 :)
|
||||
|
||||
{ // Compress blocklight
|
||||
m_BlockLight.clear();
|
||||
m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[m_BlockTypes.size() / 2]);
|
||||
}
|
||||
m_ChunkData.SetBlockLight(a_BlockLight);
|
||||
|
||||
{ // Compress skylight
|
||||
m_BlockSkyLight.clear();
|
||||
m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_SkyLight[0], &a_SkyLight[m_BlockTypes.size() / 2]);
|
||||
}
|
||||
m_ChunkData.SetSkyLight(a_SkyLight);
|
||||
|
||||
m_IsLightValid = true;
|
||||
}
|
||||
@ -397,8 +339,7 @@ void cChunk::SetLight(
|
||||
|
||||
void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes)
|
||||
{
|
||||
std::copy(m_BlockTypes.begin(), m_BlockTypes.end(), a_BlockTypes);
|
||||
std::fill_n(&a_BlockTypes[m_BlockTypes.size()], NumBlocks - m_BlockTypes.size(), E_BLOCK_AIR);
|
||||
m_ChunkData.CopyBlockTypes(a_BlockTypes);
|
||||
}
|
||||
|
||||
|
||||
@ -684,8 +625,7 @@ void cChunk::Tick(float a_Dt)
|
||||
|
||||
void cChunk::TickBlock(int a_RelX, int a_RelY, int a_RelZ)
|
||||
{
|
||||
unsigned Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(Index));
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(a_RelX, a_RelY, a_RelZ));
|
||||
ASSERT(Handler != NULL); // Happenned on server restart, FS #243
|
||||
cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap());
|
||||
cBlockInServerPluginInterface PluginInterface(*this->GetWorld());
|
||||
@ -810,19 +750,18 @@ void cChunk::CheckBlocks()
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::vector<unsigned int> ToTickBlocks;
|
||||
std::vector<Vector3i> ToTickBlocks;
|
||||
std::swap(m_ToTickBlocks, ToTickBlocks);
|
||||
|
||||
cChunkInterface ChunkInterface(m_World->GetChunkMap());
|
||||
cBlockInServerPluginInterface PluginInterface(*m_World);
|
||||
|
||||
for (std::vector<unsigned int>::const_iterator itr = ToTickBlocks.begin(), end = ToTickBlocks.end(); itr != end; ++itr)
|
||||
for (std::vector<Vector3i>::const_iterator itr = ToTickBlocks.begin(), end = ToTickBlocks.end(); itr != end; ++itr)
|
||||
{
|
||||
unsigned int index = (*itr);
|
||||
Vector3i BlockPos = IndexToCoordinate(index);
|
||||
Vector3i Pos = (*itr);
|
||||
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(index));
|
||||
Handler->Check(ChunkInterface, PluginInterface, BlockPos.x, BlockPos.y, BlockPos.z, *this);
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(Pos));
|
||||
Handler->Check(ChunkInterface, PluginInterface, Pos.x, Pos.y, Pos.z, *this);
|
||||
} // for itr - ToTickBlocks[]
|
||||
}
|
||||
|
||||
@ -865,8 +804,7 @@ void cChunk::TickBlocks(void)
|
||||
continue; // It's all air up here
|
||||
}
|
||||
|
||||
unsigned int Index = MakeIndexNoCheck(m_BlockTickX, m_BlockTickY, m_BlockTickZ);
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(Index));
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ));
|
||||
ASSERT(Handler != NULL); // Happenned on server restart, FS #243
|
||||
Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, m_BlockTickX, m_BlockTickY, m_BlockTickZ);
|
||||
} // for i - tickblocks
|
||||
@ -1258,9 +1196,8 @@ bool cChunk::UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBB
|
||||
// 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);
|
||||
a_BlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
|
||||
a_SkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1450,7 +1387,7 @@ void cChunk::CalculateHeightmap(const BLOCKTYPE * a_BlockTypes)
|
||||
int index = MakeIndex( x, y, z );
|
||||
if (a_BlockTypes[index] != E_BLOCK_AIR)
|
||||
{
|
||||
m_HeightMap[x + z * Width] = (unsigned char)y;
|
||||
m_HeightMap[x + z * Width] = (HEIGHTTYPE)y;
|
||||
break;
|
||||
}
|
||||
} // for y
|
||||
@ -1462,14 +1399,12 @@ void cChunk::CalculateHeightmap(const BLOCKTYPE * a_BlockTypes)
|
||||
|
||||
|
||||
|
||||
void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, bool a_SendToClients)
|
||||
{
|
||||
FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
|
||||
|
||||
const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta, a_SendToClients);
|
||||
|
||||
// Tick this block and its neighbors:
|
||||
m_ToTickBlocks.push_back(index);
|
||||
m_ToTickBlocks.push_back(Vector3i(a_RelX, a_RelY, a_RelZ));
|
||||
QueueTickBlockNeighbors(a_RelX, a_RelY, a_RelZ);
|
||||
|
||||
// If there was a block entity, remove it:
|
||||
@ -1533,7 +1468,7 @@ void cChunk::QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ)
|
||||
return;
|
||||
}
|
||||
|
||||
m_ToTickBlocks.push_back(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ));
|
||||
m_ToTickBlocks.push_back(Vector3i(a_RelX, a_RelY, a_RelZ));
|
||||
}
|
||||
|
||||
|
||||
@ -1565,43 +1500,41 @@ void cChunk::QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ)
|
||||
|
||||
|
||||
|
||||
void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
|
||||
void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients)
|
||||
{
|
||||
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 = GetBlock(index);
|
||||
const BLOCKTYPE OldBlockMeta = GetNibble(m_BlockMeta, index);
|
||||
const BLOCKTYPE OldBlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
|
||||
const BLOCKTYPE OldBlockMeta = m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MarkDirty();
|
||||
m_IsRedstoneDirty = true;
|
||||
|
||||
if ((size_t)index >= m_BlockTypes.size())
|
||||
{
|
||||
m_BlockTypes.resize(index + 1);
|
||||
}
|
||||
m_BlockTypes[index] = a_BlockType;
|
||||
m_ChunkData.SetBlock(a_RelX, a_RelY, a_RelZ, 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
|
||||
if ( // Queue block to be sent only if ...
|
||||
a_SendToClients && // ... we are told to do so AND ...
|
||||
(
|
||||
(OldBlockMeta != a_BlockMeta) || // ... the meta value is different OR ...
|
||||
!( // ... the old and new blocktypes AREN'T liquids (because client doesn't need to distinguish betwixt them); see below for specifics:
|
||||
((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);
|
||||
m_ChunkData.SetMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta);
|
||||
|
||||
// ONLY recalculate lighting if it's necessary!
|
||||
if (
|
||||
@ -1618,15 +1551,15 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
|
||||
{
|
||||
if (a_BlockType != E_BLOCK_AIR)
|
||||
{
|
||||
m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)a_RelY;
|
||||
m_HeightMap[a_RelX + a_RelZ * Width] = (HEIGHTTYPE)a_RelY;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int y = a_RelY - 1; y > 0; --y)
|
||||
{
|
||||
if (GetBlock(MakeIndexNoCheck(a_RelX, y, a_RelZ)) != E_BLOCK_AIR)
|
||||
if (GetBlock(a_RelX, y, a_RelZ) != E_BLOCK_AIR)
|
||||
{
|
||||
m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)y;
|
||||
m_HeightMap[a_RelX + a_RelZ * Width] = (HEIGHTTYPE)y;
|
||||
break;
|
||||
}
|
||||
} // for y - column in m_BlockData
|
||||
@ -1638,39 +1571,18 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
|
||||
|
||||
|
||||
|
||||
void cChunk::SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta)
|
||||
{
|
||||
if (GetNibble(m_BlockMeta, a_BlockIdx) == a_Meta)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MarkDirty();
|
||||
SetNibble(m_BlockMeta, a_BlockIdx, a_Meta);
|
||||
Vector3i Coords(IndexToCoordinate(a_BlockIdx));
|
||||
|
||||
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, Coords.x, Coords.y, Coords.z, GetBlock(a_BlockIdx), a_Meta));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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)));
|
||||
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ)));
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ);
|
||||
a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(index), GetMeta(index));
|
||||
a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ));
|
||||
|
||||
// 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)
|
||||
@ -2529,27 +2441,7 @@ BLOCKTYPE cChunk::GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
return 0; // Clip
|
||||
}
|
||||
|
||||
return GetBlock(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;
|
||||
}
|
||||
|
||||
if ((size_t)a_BlockIdx >= m_BlockTypes.size())
|
||||
{
|
||||
return E_BLOCK_AIR;
|
||||
}
|
||||
|
||||
return m_BlockTypes[a_BlockIdx];
|
||||
return m_ChunkData.GetBlock(a_RelX, a_RelY, a_RelZ);
|
||||
}
|
||||
|
||||
|
||||
@ -2558,9 +2450,8 @@ BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
|
||||
|
||||
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 = GetBlock(Idx);
|
||||
a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, Idx);
|
||||
a_BlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
|
||||
a_BlockMeta = m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
}
|
||||
|
||||
|
||||
@ -2569,11 +2460,10 @@ void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_
|
||||
|
||||
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 = GetBlock(Idx);
|
||||
a_Meta = cChunkDef::GetNibble(m_BlockMeta, Idx);
|
||||
a_SkyLight = cChunkDef::GetNibble(m_BlockSkyLight, Idx);
|
||||
a_BlockLight = cChunkDef::GetNibble(m_BlockLight, Idx);
|
||||
a_BlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
|
||||
a_Meta = m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
a_SkyLight = m_ChunkData.GetSkyLight(a_RelX, a_RelY, a_RelZ);
|
||||
a_BlockLight = m_ChunkData.GetBlockLight(a_RelX, a_RelY, a_RelZ);
|
||||
}
|
||||
|
||||
|
||||
|
50
src/Chunk.h
@ -3,6 +3,7 @@
|
||||
|
||||
#include "Entities/Entity.h"
|
||||
#include "ChunkDef.h"
|
||||
#include "ChunkData.h"
|
||||
|
||||
#include "Simulator/FireSimulator.h"
|
||||
#include "Simulator/SandSimulator.h"
|
||||
@ -66,6 +67,7 @@ public:
|
||||
cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects
|
||||
cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP // Neighbor chunks
|
||||
);
|
||||
cChunk(cChunk & other);
|
||||
~cChunk();
|
||||
|
||||
bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated)
|
||||
@ -139,7 +141,7 @@ public:
|
||||
|
||||
cWorld * GetWorld(void) const { return m_World; }
|
||||
|
||||
void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta );
|
||||
void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, bool a_SendToClients = true);
|
||||
// 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 ); }
|
||||
|
||||
@ -152,9 +154,9 @@ public:
|
||||
/** 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.
|
||||
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients = true); // 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;
|
||||
BLOCKTYPE GetBlock(Vector3i a_cords) const { return GetBlock(a_cords.x, a_cords.y, a_cords.z);}
|
||||
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);
|
||||
|
||||
@ -320,15 +322,24 @@ public:
|
||||
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) { SetMeta(MakeIndex(a_RelX, a_RelY, a_RelZ), a_Meta); }
|
||||
void SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta);
|
||||
inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
{
|
||||
return m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
}
|
||||
inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta)
|
||||
{
|
||||
bool hasChanged = m_ChunkData.SetMeta(a_RelX, a_RelY, a_RelZ, a_Meta);
|
||||
if (hasChanged)
|
||||
{
|
||||
MarkDirty();
|
||||
m_IsRedstoneDirty = true;
|
||||
|
||||
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(a_RelX, a_RelY, a_RelZ), 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, true); }
|
||||
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, true); }
|
||||
inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return m_ChunkData.GetBlockLight(a_RelX, a_RelY, a_RelZ); }
|
||||
inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return m_ChunkData.GetSkyLight(a_RelX, a_RelY, a_RelZ); }
|
||||
|
||||
/** 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;
|
||||
@ -368,10 +379,13 @@ public:
|
||||
cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; }
|
||||
|
||||
cRedstoneSimulatorChunkData * GetRedstoneSimulatorData(void) { return &m_RedstoneSimulatorData; }
|
||||
cRedstoneSimulatorChunkData * GetRedstoneSimulatorQueuedData(void) { return &m_RedstoneSimulatorQueuedData; }
|
||||
cIncrementalRedstoneSimulator::PoweredBlocksList * GetRedstoneSimulatorPoweredBlocksList(void) { return &m_RedstoneSimulatorPoweredBlocksList; }
|
||||
cIncrementalRedstoneSimulator::LinkedBlocksList * GetRedstoneSimulatorLinkedBlocksList(void) { return &m_RedstoneSimulatorLinkedBlocksList; };
|
||||
cIncrementalRedstoneSimulator::SimulatedPlayerToggleableList * GetRedstoneSimulatorSimulatedPlayerToggleableList(void) { return &m_RedstoneSimulatorSimulatedPlayerToggleableList; };
|
||||
cIncrementalRedstoneSimulator::RepeatersDelayList * GetRedstoneSimulatorRepeatersDelayList(void) { return &m_RedstoneSimulatorRepeatersDelayList; };
|
||||
bool IsRedstoneDirty(void) const { return m_IsRedstoneDirty; }
|
||||
void SetIsRedstoneDirty(bool a_Flag) { m_IsRedstoneDirty = a_Flag; }
|
||||
|
||||
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); }
|
||||
@ -403,8 +417,8 @@ private:
|
||||
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
|
||||
std::vector<Vector3i> 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
|
||||
|
||||
@ -420,10 +434,7 @@ private:
|
||||
cWorld * m_World;
|
||||
cChunkMap * m_ChunkMap;
|
||||
|
||||
COMPRESSED_BLOCKTYPE m_BlockTypes;
|
||||
COMPRESSED_NIBBLETYPE m_BlockMeta;
|
||||
COMPRESSED_NIBBLETYPE m_BlockLight;
|
||||
COMPRESSED_NIBBLETYPE m_BlockSkyLight;
|
||||
cChunkData m_ChunkData;
|
||||
|
||||
cChunkDef::HeightMap m_HeightMap;
|
||||
cChunkDef::BiomeMap m_BiomeMap;
|
||||
@ -442,13 +453,16 @@ private:
|
||||
cSandSimulatorChunkData m_SandSimulatorData;
|
||||
|
||||
cRedstoneSimulatorChunkData m_RedstoneSimulatorData;
|
||||
cRedstoneSimulatorChunkData m_RedstoneSimulatorQueuedData;
|
||||
cIncrementalRedstoneSimulator::PoweredBlocksList m_RedstoneSimulatorPoweredBlocksList;
|
||||
cIncrementalRedstoneSimulator::LinkedBlocksList m_RedstoneSimulatorLinkedBlocksList;
|
||||
cIncrementalRedstoneSimulator::SimulatedPlayerToggleableList m_RedstoneSimulatorSimulatedPlayerToggleableList;
|
||||
cIncrementalRedstoneSimulator::RepeatersDelayList m_RedstoneSimulatorRepeatersDelayList;
|
||||
|
||||
/** Indicates if simulate-once blocks should be updated by the redstone simulator */
|
||||
bool m_IsRedstoneDirty;
|
||||
|
||||
// pick up a random block of this chunk
|
||||
// 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);
|
||||
|
||||
|
592
src/ChunkData.cpp
Normal file
@ -0,0 +1,592 @@
|
||||
|
||||
// ChunkData.cpp
|
||||
|
||||
// Implements the cChunkData class that represents the block's type, meta, blocklight and skylight storage for a chunk
|
||||
|
||||
#include "Globals.h"
|
||||
#include "ChunkData.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Returns true if all a_Array's elements between [0] and [a_NumElements - 1] are equal to a_Value. */
|
||||
template <typename T> inline bool IsAllValue(const T * a_Array, size_t a_NumElements, T a_Value)
|
||||
{
|
||||
for (size_t i = 0; i < a_NumElements; i++)
|
||||
{
|
||||
if (a_Array[i] != a_Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData::cChunkData(void)
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
: m_IsOwner(true)
|
||||
#endif
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
m_Sections[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData::~cChunkData()
|
||||
{
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
if (!m_IsOwner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
Free(m_Sections[i]);
|
||||
m_Sections[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
cChunkData::cChunkData(const cChunkData & a_Other) :
|
||||
m_IsOwner(true)
|
||||
{
|
||||
// Move contents and ownership from a_Other to this, pointer-wise:
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
m_Sections[i] = a_Other.m_Sections[i];
|
||||
}
|
||||
a_Other.m_IsOwner = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData & cChunkData::operator =(const cChunkData & a_Other)
|
||||
{
|
||||
// If assigning to self, no-op
|
||||
if (&a_Other == this)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Free any currently held contents:
|
||||
if (m_IsOwner)
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
Free(m_Sections[i]);
|
||||
m_Sections[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Move contents and ownership from a_Other to this, pointer-wise:
|
||||
m_IsOwner = true;
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
m_Sections[i] = a_Other.m_Sections[i];
|
||||
}
|
||||
a_Other.m_IsOwner = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// unique_ptr style interface for memory management
|
||||
cChunkData::cChunkData(cChunkData && other)
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
m_Sections[i] = other.m_Sections[i];
|
||||
other.m_Sections[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData & cChunkData::operator =(cChunkData && other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
Free(m_Sections[i]);
|
||||
m_Sections[i] = other.m_Sections[i];
|
||||
other.m_Sections[i] = NULL;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BLOCKTYPE cChunkData::GetBlock(int a_X, int a_Y, int a_Z) const
|
||||
{
|
||||
ASSERT((a_X >= 0) && (a_X < cChunkDef::Width));
|
||||
ASSERT((a_Y >= 0) && (a_Y < cChunkDef::Height));
|
||||
ASSERT((a_Z >= 0) && (a_Z < cChunkDef::Width));
|
||||
int Section = a_Y / SectionHeight;
|
||||
if (m_Sections[Section] != NULL)
|
||||
{
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_X, a_Y - (Section * SectionHeight), a_Z);
|
||||
return m_Sections[Section]->m_BlockTypes[Index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block)
|
||||
{
|
||||
if (
|
||||
(a_RelX >= cChunkDef::Width) || (a_RelX < 0) ||
|
||||
(a_RelY >= cChunkDef::Height) || (a_RelY < 0) ||
|
||||
(a_RelZ >= cChunkDef::Width) || (a_RelZ < 0)
|
||||
)
|
||||
{
|
||||
ASSERT(!"cChunkData::SetMeta(): index out of range!");
|
||||
return;
|
||||
}
|
||||
|
||||
int Section = a_RelY / SectionHeight;
|
||||
if (m_Sections[Section] == NULL)
|
||||
{
|
||||
if (a_Block == 0x00)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_Sections[Section] = Allocate();
|
||||
if (m_Sections[Section] == NULL)
|
||||
{
|
||||
ASSERT(!"Failed to allocate a new section in Chunkbuffer");
|
||||
return;
|
||||
}
|
||||
ZeroSection(m_Sections[Section]);
|
||||
}
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
|
||||
m_Sections[Section]->m_BlockTypes[Index] = a_Block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE cChunkData::GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
{
|
||||
if (
|
||||
(a_RelX < cChunkDef::Width) && (a_RelX > -1) &&
|
||||
(a_RelY < cChunkDef::Height) && (a_RelY > -1) &&
|
||||
(a_RelZ < cChunkDef::Width) && (a_RelZ > -1))
|
||||
{
|
||||
int Section = a_RelY / SectionHeight;
|
||||
if (m_Sections[Section] != NULL)
|
||||
{
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
|
||||
return (m_Sections[Section]->m_BlockMetas[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cChunkData::SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Nibble)
|
||||
{
|
||||
if (
|
||||
(a_RelX >= cChunkDef::Width) || (a_RelX < 0) ||
|
||||
(a_RelY >= cChunkDef::Height) || (a_RelY < 0) ||
|
||||
(a_RelZ >= cChunkDef::Width) || (a_RelZ < 0)
|
||||
)
|
||||
{
|
||||
ASSERT(!"cChunkData::SetMeta(): index out of range!");
|
||||
return false;
|
||||
}
|
||||
|
||||
int Section = a_RelY / SectionHeight;
|
||||
if (m_Sections[Section] == NULL)
|
||||
{
|
||||
if ((a_Nibble & 0xf) == 0x00)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_Sections[Section] = Allocate();
|
||||
if (m_Sections[Section] == NULL)
|
||||
{
|
||||
ASSERT(!"Failed to allocate a new section in Chunkbuffer");
|
||||
return false;
|
||||
}
|
||||
ZeroSection(m_Sections[Section]);
|
||||
}
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
|
||||
NIBBLETYPE oldval = m_Sections[Section]->m_BlockMetas[Index / 2] >> ((Index & 1) * 4) & 0xf;
|
||||
m_Sections[Section]->m_BlockMetas[Index / 2] = static_cast<NIBBLETYPE>(
|
||||
(m_Sections[Section]->m_BlockMetas[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
|
||||
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
|
||||
);
|
||||
return oldval != a_Nibble;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE cChunkData::GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
{
|
||||
if (
|
||||
(a_RelX < cChunkDef::Width) && (a_RelX > -1) &&
|
||||
(a_RelY < cChunkDef::Height) && (a_RelY > -1) &&
|
||||
(a_RelZ < cChunkDef::Width) && (a_RelZ > -1)
|
||||
)
|
||||
{
|
||||
int Section = a_RelY / SectionHeight;
|
||||
if (m_Sections[Section] != NULL)
|
||||
{
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
|
||||
return (m_Sections[Section]->m_BlockLight[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE cChunkData::GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
{
|
||||
if ((a_RelX < cChunkDef::Width) && (a_RelX > -1) && (a_RelY < cChunkDef::Height) && (a_RelY > -1) && (a_RelZ < cChunkDef::Width) && (a_RelZ > -1))
|
||||
{
|
||||
int Section = a_RelY / SectionHeight;
|
||||
if (m_Sections[Section] != NULL)
|
||||
{
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
|
||||
return (m_Sections[Section]->m_BlockSkyLight[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0xF;
|
||||
}
|
||||
}
|
||||
ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData cChunkData::Copy(void) const
|
||||
{
|
||||
cChunkData copy;
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
copy.m_Sections[i] = Allocate();
|
||||
*copy.m_Sections[i] = *m_Sections[i];
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx, size_t a_Length) const
|
||||
{
|
||||
size_t ToSkip = a_Idx;
|
||||
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
size_t StartPos = 0;
|
||||
if (ToSkip > 0)
|
||||
{
|
||||
StartPos = std::min(ToSkip, +SectionBlockCount);
|
||||
ToSkip -= StartPos;
|
||||
}
|
||||
if (StartPos < SectionBlockCount)
|
||||
{
|
||||
size_t ToCopy = std::min(+SectionBlockCount - StartPos, a_Length);
|
||||
a_Length -= ToCopy;
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
BLOCKTYPE * blockbuffer = m_Sections[i]->m_BlockTypes;
|
||||
memcpy(&a_Dest[(i * SectionBlockCount) + StartPos - a_Idx], blockbuffer + StartPos, sizeof(BLOCKTYPE) * ToCopy);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&a_Dest[(i * SectionBlockCount) - a_Idx], 0, sizeof(BLOCKTYPE) * ToCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::CopyMetas(NIBBLETYPE * a_Dest) const
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockMetas, sizeof(m_Sections[i]->m_BlockMetas));
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&a_Dest[i * SectionBlockCount / 2], 0, sizeof(m_Sections[i]->m_BlockMetas));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::CopyBlockLight(NIBBLETYPE * a_Dest) const
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockLight, sizeof(m_Sections[i]->m_BlockLight));
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&a_Dest[i * SectionBlockCount / 2], 0, sizeof(m_Sections[i]->m_BlockLight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::CopySkyLight(NIBBLETYPE * a_Dest) const
|
||||
{
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockSkyLight, sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&a_Dest[i * SectionBlockCount / 2], 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetBlockTypes(const BLOCKTYPE * a_Src)
|
||||
{
|
||||
ASSERT(a_Src != NULL);
|
||||
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
// If the section is already allocated, copy the data into it:
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(m_Sections[i]->m_BlockTypes, &a_Src[i * SectionBlockCount], sizeof(m_Sections[i]->m_BlockTypes));
|
||||
continue;
|
||||
}
|
||||
|
||||
// The section doesn't exist, find out if it is needed:
|
||||
if (IsAllValue(a_Src + i * SectionBlockCount, SectionBlockCount, (const BLOCKTYPE)0))
|
||||
{
|
||||
// No need for the section, the data is all-air
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate the section and copy the data into it:
|
||||
m_Sections[i] = Allocate();
|
||||
memcpy(m_Sections[i]->m_BlockTypes, &a_Src[i * SectionBlockCount], sizeof(m_Sections[i]->m_BlockTypes));
|
||||
memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas));
|
||||
memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight));
|
||||
memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
} // for i - m_Sections[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetMetas(const NIBBLETYPE * a_Src)
|
||||
{
|
||||
ASSERT(a_Src != NULL);
|
||||
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
// If the section is already allocated, copy the data into it:
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(m_Sections[i]->m_BlockMetas, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockMetas));
|
||||
continue;
|
||||
}
|
||||
|
||||
// The section doesn't exist, find out if it is needed:
|
||||
if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, (NIBBLETYPE)0))
|
||||
{
|
||||
// No need for the section, the data is all zeroes
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate the section and copy the data into it:
|
||||
m_Sections[i] = Allocate();
|
||||
memcpy(m_Sections[i]->m_BlockMetas, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockMetas));
|
||||
memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes));
|
||||
memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight));
|
||||
memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
} // for i - m_Sections[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetBlockLight(const NIBBLETYPE * a_Src)
|
||||
{
|
||||
if (a_Src == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
// If the section is already allocated, copy the data into it:
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(m_Sections[i]->m_BlockLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockLight));
|
||||
continue;
|
||||
}
|
||||
|
||||
// The section doesn't exist, find out if it is needed:
|
||||
if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, (NIBBLETYPE)0))
|
||||
{
|
||||
// No need for the section, the data is all zeroes
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate the section and copy the data into it:
|
||||
m_Sections[i] = Allocate();
|
||||
memcpy(m_Sections[i]->m_BlockLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockLight));
|
||||
memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes));
|
||||
memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas));
|
||||
memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
} // for i - m_Sections[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::SetSkyLight(const NIBBLETYPE * a_Src)
|
||||
{
|
||||
if (a_Src == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NumSections; i++)
|
||||
{
|
||||
// If the section is already allocated, copy the data into it:
|
||||
if (m_Sections[i] != NULL)
|
||||
{
|
||||
memcpy(m_Sections[i]->m_BlockSkyLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
continue;
|
||||
}
|
||||
|
||||
// The section doesn't exist, find out if it is needed:
|
||||
if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, (NIBBLETYPE)0xff))
|
||||
{
|
||||
// No need for the section, the data is all zeroes
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate the section and copy the data into it:
|
||||
m_Sections[i] = Allocate();
|
||||
memcpy(m_Sections[i]->m_BlockSkyLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockSkyLight));
|
||||
memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes));
|
||||
memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas));
|
||||
memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight));
|
||||
} // for i - m_Sections[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkData::sChunkSection * cChunkData::Allocate(void)
|
||||
{
|
||||
// TODO: Use an allocation pool
|
||||
return new cChunkData::sChunkSection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::Free(cChunkData::sChunkSection * a_Section)
|
||||
{
|
||||
// TODO: Use an allocation pool
|
||||
delete a_Section;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkData::ZeroSection(cChunkData::sChunkSection * a_Section) const
|
||||
{
|
||||
memset(a_Section->m_BlockTypes, 0x00, sizeof(a_Section->m_BlockTypes));
|
||||
memset(a_Section->m_BlockMetas, 0x00, sizeof(a_Section->m_BlockMetas));
|
||||
memset(a_Section->m_BlockLight, 0x00, sizeof(a_Section->m_BlockLight));
|
||||
memset(a_Section->m_BlockSkyLight, 0xff, sizeof(a_Section->m_BlockSkyLight));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
125
src/ChunkData.h
Normal file
@ -0,0 +1,125 @@
|
||||
|
||||
// ChunkData.h
|
||||
|
||||
// Declares the cChunkData class that represents the block's type, meta, blocklight and skylight storage for a chunk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
#include "ChunkDef.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
#else
|
||||
// unique_ptr style interface for memory management
|
||||
#endif
|
||||
|
||||
class cChunkData
|
||||
{
|
||||
public:
|
||||
|
||||
cChunkData(void);
|
||||
~cChunkData();
|
||||
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
cChunkData(const cChunkData & a_Other);
|
||||
cChunkData & operator =(const cChunkData & a_Other);
|
||||
#else
|
||||
// unique_ptr style interface for memory management
|
||||
cChunkData(cChunkData && a_Other);
|
||||
cChunkData & operator =(cChunkData && a_ther);
|
||||
#endif
|
||||
|
||||
BLOCKTYPE GetBlock(int a_X, int a_Y, int a_Z) const;
|
||||
void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block);
|
||||
|
||||
NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const;
|
||||
bool SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Nibble);
|
||||
|
||||
NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const;
|
||||
|
||||
NIBBLETYPE GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) const;
|
||||
|
||||
/** Creates a (deep) copy of self. */
|
||||
cChunkData Copy(void) const;
|
||||
|
||||
/** Copies the blocktype data into the specified flat array.
|
||||
Optionally, only a part of the data is copied, as specified by the a_Idx and a_Length parameters. */
|
||||
void CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx = 0, size_t a_Length = cChunkDef::NumBlocks) const;
|
||||
|
||||
/** Copies the metadata into the specified flat array. */
|
||||
void CopyMetas(NIBBLETYPE * a_Dest) const;
|
||||
|
||||
/** Copies the block light data into the specified flat array. */
|
||||
void CopyBlockLight(NIBBLETYPE * a_Dest) const;
|
||||
|
||||
/** Copies the skylight data into the specified flat array. */
|
||||
void CopySkyLight (NIBBLETYPE * a_Dest) const;
|
||||
|
||||
/** Copies the blocktype data from the specified flat array into the internal representation.
|
||||
Allocates sections that are needed for the operation.
|
||||
Requires that a_Src is a valid pointer. */
|
||||
void SetBlockTypes(const BLOCKTYPE * a_Src);
|
||||
|
||||
/** Copies the metadata from the specified flat array into the internal representation.
|
||||
Allocates sectios that are needed for the operation.
|
||||
Requires that a_Src is a valid pointer. */
|
||||
void SetMetas(const NIBBLETYPE * a_Src);
|
||||
|
||||
/** Copies the blocklight data from the specified flat array into the internal representation.
|
||||
Allocates sectios that are needed for the operation.
|
||||
Allows a_Src to be NULL, in which case it doesn't do anything. */
|
||||
void SetBlockLight(const NIBBLETYPE * a_Src);
|
||||
|
||||
/** Copies the skylight data from the specified flat array into the internal representation.
|
||||
Allocates sectios that are needed for the operation.
|
||||
Allows a_Src to be NULL, in which case it doesn't do anything. */
|
||||
void SetSkyLight(const NIBBLETYPE * a_Src);
|
||||
|
||||
private:
|
||||
|
||||
static const size_t SectionHeight = 16;
|
||||
static const size_t NumSections = (cChunkDef::Height / SectionHeight);
|
||||
static const size_t SectionBlockCount = SectionHeight * cChunkDef::Width * cChunkDef::Width;
|
||||
|
||||
#if __cplusplus < 201103L
|
||||
// auto_ptr style interface for memory management
|
||||
mutable bool m_IsOwner;
|
||||
#endif
|
||||
|
||||
struct sChunkSection {
|
||||
BLOCKTYPE m_BlockTypes [SectionBlockCount];
|
||||
NIBBLETYPE m_BlockMetas [SectionBlockCount / 2];
|
||||
NIBBLETYPE m_BlockLight [SectionBlockCount / 2];
|
||||
NIBBLETYPE m_BlockSkyLight[SectionBlockCount / 2];
|
||||
};
|
||||
|
||||
sChunkSection * m_Sections[NumSections];
|
||||
|
||||
/** Allocates a new section. Entry-point to custom allocators. */
|
||||
static sChunkSection * Allocate(void);
|
||||
|
||||
/** Frees the specified section, previously allocated using Allocate().
|
||||
Note that a_Section may be NULL. */
|
||||
static void Free(sChunkSection * a_Section);
|
||||
|
||||
/** Sets the data in the specified section to their default values. */
|
||||
void ZeroSection(sChunkSection * a_Section) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
127
src/ChunkDataCallback.h
Normal file
@ -0,0 +1,127 @@
|
||||
|
||||
// ChunkDataCallback.h
|
||||
|
||||
// Declares the cChunkDataCallback interface and several trivial descendants, for reading chunk data
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ChunkData.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** 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:
|
||||
|
||||
virtual ~cChunkDataCallback() {}
|
||||
|
||||
/** 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 let know if the chunk lighting is valid. Return value is ignored
|
||||
virtual void LightIsValid(bool a_IsLightValid) {UNUSED(a_IsLightValid); };
|
||||
|
||||
/// Called once to export block info
|
||||
virtual void ChunkData(const cChunkData & a_Buffer) {UNUSED(a_Buffer); };
|
||||
|
||||
/// 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 buffer
|
||||
*/
|
||||
class cChunkDataCollector :
|
||||
public cChunkDataCallback
|
||||
{
|
||||
public:
|
||||
|
||||
cChunkData m_BlockData;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void ChunkData(const cChunkData & a_BlockData) override
|
||||
{
|
||||
m_BlockData = a_BlockData.Copy();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer
|
||||
*/
|
||||
class cChunkDataArrayCollector :
|
||||
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 ChunkData(const cChunkData & a_ChunkBuffer) override
|
||||
{
|
||||
a_ChunkBuffer.CopyBlockTypes(m_BlockData);
|
||||
a_ChunkBuffer.CopyMetas(m_BlockData + cChunkDef::NumBlocks);
|
||||
a_ChunkBuffer.CopyBlockLight(m_BlockData + 3 * cChunkDef::NumBlocks / 2);
|
||||
a_ChunkBuffer.CopySkyLight(m_BlockData + 2 * cChunkDef::NumBlocks);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A simple implementation of the cChunkDataCallback interface that collects all block data into 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 ChunkData(const cChunkData & a_ChunkBuffer) override
|
||||
{
|
||||
a_ChunkBuffer.CopyBlockTypes(m_BlockTypes);
|
||||
a_ChunkBuffer.CopyMetas(m_BlockMetas);
|
||||
a_ChunkBuffer.CopyBlockLight(m_BlockLight);
|
||||
a_ChunkBuffer.CopySkyLight(m_BlockSkyLight);
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
130
src/ChunkDef.h
@ -330,136 +330,6 @@ private:
|
||||
|
||||
|
||||
|
||||
/** 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:
|
||||
|
||||
virtual ~cChunkDataCallback() {}
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
|