1
0

Merge branch 'master' of github.com:mc-server/MCServer

This commit is contained in:
Alexander Harkness 2015-05-19 18:43:19 +01:00
commit cbb425f027
191 changed files with 4066 additions and 2983 deletions

3
.gitmodules vendored
View File

@ -28,3 +28,6 @@
[submodule "lib/libevent"] [submodule "lib/libevent"]
path = lib/libevent path = lib/libevent
url = https://github.com/mc-server/libevent.git url = https://github.com/mc-server/libevent.git
[submodule "lib/TCLAP"]
path = lib/TCLAP
url = https://github.com/mc-server/TCLAP.git

View File

@ -2,7 +2,7 @@ language: cpp
compiler: clang compiler: clang
before_install: before_install:
- if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi # - if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi
# g++4.8 # g++4.8
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
@ -21,17 +21,17 @@ install:
# Build MCServer # Build MCServer
script: ./CIbuild.sh script: ./CIbuild.sh
after_success: #after_success:
- ./uploadCoverage.sh # - ./uploadCoverage.sh
env: env:
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer - TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer
- TRAVIS_MCSERVER_BUILD_TYPE=DEBUG MCSERVER_PATH=./MCServer_debug - TRAVIS_MCSERVER_BUILD_TYPE=DEBUG MCSERVER_PATH=./MCServer_debug
matrix: #matrix:
include: # include:
- compiler: gcc # - compiler: gcc
env: TRAVIS_MCSERVER_BUILD_TYPE=COVERAGE MCSERVER_PATH=./MCServer # env: TRAVIS_MCSERVER_BUILD_TYPE=COVERAGE MCSERVER_PATH=./MCServer
# Notification Settings # Notification Settings
notifications: notifications:

View File

@ -7,9 +7,13 @@ export MCSERVER_BUILD_ID=$TRAVIS_JOB_NUMBER
export MCSERVER_BUILD_DATETIME=`date` export MCSERVER_BUILD_DATETIME=`date`
cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1; cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1;
echo "Checking basic style..."
cd src cd src
lua CheckBasicStyle.lua lua CheckBasicStyle.lua
cd .. cd ..
echo "Building..."
make -j 2; make -j 2;
make -j 2 test ARGS="-V"; make -j 2 test ARGS="-V";
cd MCServer/; cd MCServer/;

View File

@ -78,6 +78,9 @@ endif()
# The Expat library is linked in statically, make the source files aware of that: # The Expat library is linked in statically, make the source files aware of that:
add_definitions(-DXML_STATIC) add_definitions(-DXML_STATIC)
# Let Lua use additional checks on its C API. This is only compiled into Debug builds:
add_definitions(-DLUA_USE_APICHECK)
# Self Test Mode enables extra checks at startup # Self Test Mode enables extra checks at startup
if(${SELF_TEST}) if(${SELF_TEST})
add_definitions(-DSELF_TEST) add_definitions(-DSELF_TEST)

View File

@ -1,7 +1,27 @@
Code Stuff Code Conventions
---------- ----------
When contributing, you must follow our code conventions. Otherwise, the CI builds will automatically fail and your PR will not be merged until the non-conforming code is fixed. Due to this, we strongly advise you to run `src/CheckBasicStyle.lua` before commiting, it will perform various code style checks and warn you if your code does not conform to our conventions. `CheckBasicStyle.lua` can be configured to run automatically before every commit via a pre-commit hook, **this is highly recommended**. The way to do it is listed at the bottom of this file.
Here are the conventions:
* We use the subset of C++11 supported by MSVC 2013 (ask if you think that something would be useful) * We use the subset of C++11 supported by MSVC 2013 (ask if you think that something would be useful)
* All new public functions in all classes need documenting comments on what they do and what behavior they follow, use doxy-comments formatted as `/** Description */`. Do not use asterisks on additional lines in multi-line comments.
* Use spaces after the comment markers: `// Comment` instead of `//Comment`. A comment must be prefixed with two spaces if it's on the same line with code:
- `SomeFunction()<Space><Space>//<Space>Note the two spaces prefixed to me and the space after the slashes.`
* All variable names and function names use CamelCase style, with the exception of single letter variables.
- `ThisIsAProperFunction()` `This_is_bad()` `this_is_bad` `GoodVariableName` `badVariableName`.
* All member variables start with `m_`, all function parameters start with `a_`, all class names start with `c`.
- `class cMonster { int m_Health; int DecreaseHealth(int a_Amount); }`
* Put spaces after commas. `Vector3d(1, 2, 3)` instead of `Vector3d(1,2,3)`
* Put spaces before and after every operator.
- `a = b + c;`
- `if (a == b)`
* Keep individual functions spaced out by 5 empty lines, this enhances readability and makes navigation in the source file easier.
* Add those extra parentheses to conditions, especially in C++:
- `if ((a == 1) && ((b == 2) || (c == 3)))` instead of ambiguous `if (a == 1 && b == 2 || c == 3)`
- This helps prevent mistakes such as `if (a & 1 == 0)`
*
* Use the provided wrappers for OS stuff: * Use the provided wrappers for OS stuff:
- Threading is done by inheriting from `cIsThread`, thread synchronization through `cCriticalSection`, `cSemaphore` and `cEvent`, file access and filesystem operations through the `cFile` class, high-precision timers through `cTimer`, high-precision sleep through `cSleep` - Threading is done by inheriting from `cIsThread`, thread synchronization through `cCriticalSection`, `cSemaphore` and `cEvent`, file access and filesystem operations through the `cFile` class, high-precision timers through `cTimer`, high-precision sleep through `cSleep`
* No magic numbers, use named constants: * No magic numbers, use named constants:
@ -16,10 +36,6 @@ Code Stuff
- `cPlayer:IsGameModeCreative()` instead of` (cPlayer:GetGameMode() == gmCreative)` (the player can also inherit the gamemode from the world, which the value-d condition doesn't catch) - `cPlayer:IsGameModeCreative()` instead of` (cPlayer:GetGameMode() == gmCreative)` (the player can also inherit the gamemode from the world, which the value-d condition doesn't catch)
* Please use **tabs for indentation and spaces for alignment**. This means that if it's at line start, it's a tab; if it's in the middle of a line, it's a space * Please use **tabs for indentation and spaces for alignment**. This means that if it's at line start, it's a tab; if it's in the middle of a line, it's a space
* Alpha-sort stuff that makes sense alpha-sorting - long lists of similar items etc. * Alpha-sort stuff that makes sense alpha-sorting - long lists of similar items etc.
* Keep individual functions spaced out by 5 empty lines, this enhances readability and makes navigation in the source file easier.
* Add those extra parentheses to conditions, especially in C++
- `if ((a == 1) && ((b == 2) || (c == 3)))` instead of ambiguous `if (a == 1 && b == 2 || c == 3)`
- This helps prevent mistakes such as `if (a & 1 == 0)`
* White space is free, so use it freely * White space is free, so use it freely
- "freely" as in "plentifully", not "arbitrarily" - "freely" as in "plentifully", not "arbitrarily"
* All `case` statements inside a `switch` need an extra indent. * All `case` statements inside a `switch` need an extra indent.
@ -27,9 +43,22 @@ Code Stuff
- The only exception: a `switch` statement with all `case` statements being a single short statement is allowed to use the short brace-less form. - The only exception: a `switch` statement with all `case` statements being a single short statement is allowed to use the short brace-less form.
- These two rules really mean that indent is governed by braces - These two rules really mean that indent is governed by braces
* Add an empty last line in all source files (GCC and GIT can complain otherwise) * Add an empty last line in all source files (GCC and GIT can complain otherwise)
* All new public functions in all classes need documenting comments on what they do and what behavior they follow, use doxy-comments formatted as `/** Description */`. Do not use asterisks on additional lines in multi-line comments.
* Use spaces after the comment markers: `// Comment` instead of `//Comment`
Pre-commit hook
---------
When contributing, the code conventions above *must* be followed. Otherwise, the CI builds will automatically fail and your PR will not be merged until the non-conforming code is fixed. It is highly recommended to set up a pre-commit hook which will check your code style before every commit. Here is how to do that:
* Clone the repository as usual.
* Go to your `<clone location>/.git/hooks` folder, create a text file named "pre-commit" there with the following contents:
```
#!/usr/sh
src/CheckBasicStyle.lua 1>&2 -g
```
* If on Linux/Unix, you need to give the newly created file an execute permission: `chmod +x .git/hooks/pre-commit`
* Lua must be installed.
* You're done. Now, `src/CheckBasicStyle.lua` will check the changed files before every commit. If a problem is found, it will point you to that problem and will cancel the commit.
Note that the check script is not smart enough to catch everything, so not having any warnings does not necessarily imply that you followed the conventions fully. The other humans working on this will perform more checks before merging.
Copyright Copyright
--------- ---------

View File

@ -7,6 +7,7 @@ Diusrex
Duralex Duralex
FakeTruth (founder) FakeTruth (founder)
Howaner Howaner
jasperarmstrong
keyboard keyboard
Lapayo Lapayo
Luksor Luksor

View File

@ -8,7 +8,8 @@
g_APIDesc = g_APIDesc =
{ {
Classes = Classes =
F --[[ {
--[[
-- What the APIDump plugin understands / how to document stuff: -- What the APIDump plugin understands / how to document stuff:
ExampleClassName = ExampleClassName =
{ {
@ -2046,7 +2047,7 @@ a_Player:OpenWindow(Window);
BroadcastChatSuccess = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtSuccess. Use for success messages." }, BroadcastChatSuccess = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtSuccess. Use for success messages." },
BroadcastChatWarning = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtWarning. Use for concerning events, such as plugin reload etc." }, BroadcastChatWarning = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtWarning. Use 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.<br><br><b>NOTE</b>This function is currently unsafe, do not use!" }, 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.<br><br><b>NOTE</b>This function is currently unsafe, do not use!" },
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for all players with names partially (or fully) matching the name string provided.<br>This function is not case-sensitive." }, FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "bool", Notes = "Calls the given callback function for the player with the name best matching the name string provided.<br>This function is case-insensitive and will match partial names.<br>Returns false if player not found or there is ambiguity, true otherwise. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre>" },
DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." }, DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
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>" }, 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>" }, 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>" },
@ -2330,7 +2331,7 @@ local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress")
{ Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" }, { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" },
{ Params = "{{Vector3i|BlockCoords}}, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" }, { Params = "{{Vector3i|BlockCoords}}, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, without waking up the simulators or replacing the block entities for the previous block type. Do not use if the block being replaced has a block entity tied to it!" },
}, },
FindAndDoWithPlayer = { Params = "PlayerNameHint, CallbackFunction", Return = "bool", Notes = "If there is a player of a name similar to the specified name (weighted-match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found. Note that the name matching is very loose, so it is a good idea to check the player name in the callback function." }, FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "bool", Notes = "Calls the given callback function for the player with the name best matching the name string provided.<br>This function is case-insensitive and will match partial names.<br>Returns false if player not found or there is ambiguity, true otherwise. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre>" },
ForEachBlockEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}})</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." }, ForEachBlockEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}})</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." },
ForEachChestInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each chest in the chunk. Returns true if all chests in the chunk have been processed (including when there are zero chests), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}})</pre> The callback should return false or no value to continue with the next chest, or true to abort the enumeration." }, ForEachChestInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each chest in the chunk. Returns true if all chests in the chunk have been processed (including when there are zero chests), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}})</pre> The callback should return false or no value to continue with the next chest, or true to abort the enumeration." },
ForEachEntity = { Params = "CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each entity in the loaded world. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}})</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." }, ForEachEntity = { Params = "CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each entity in the loaded world. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}})</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." },

View File

@ -68,6 +68,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." }, CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." },
DoWithPlugin = { Params = "PluginName, CallbackFn", Return = "bool", Notes = "(STATIC) Calls the CallbackFn for the specified plugin, if found. A plugin can be found even if it is currently unloaded, disabled or errored, the callback should check the plugin status. If the plugin is not found, this function returns false, otherwise it returns the bool value that the callback has returned. The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function ({{cPlugin|Plugin}})</pre>" }, DoWithPlugin = { Params = "PluginName, CallbackFn", Return = "bool", Notes = "(STATIC) Calls the CallbackFn for the specified plugin, if found. A plugin can be found even if it is currently unloaded, disabled or errored, the callback should check the plugin status. If the plugin is not found, this function returns false, otherwise it returns the bool value that the callback has returned. The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function ({{cPlugin|Plugin}})</pre>" },
ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Executes the command as if given by the specified Player. Checks permissions." }, ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Executes the command as if given by the specified Player. Checks permissions." },
ExecuteConsoleCommand = { Params = "CommandStr", Return = "bool, string", Notes = "Executes the console command as if given by the admin on the console. If the command is successfully executed, returns true and the text that would be output to the console normally. On error it returns false and an error message." },
FindPlugins = { Params = "", Return = "", Notes = "<b>OBSOLETE</b>, use RefreshPluginList() instead"}, FindPlugins = { Params = "", Return = "", Notes = "<b>OBSOLETE</b>, use RefreshPluginList() instead"},
ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Same as ExecuteCommand, but doesn't check permissions" }, ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Same as ExecuteCommand, but doesn't check permissions" },
ForEachCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function(Command, Permission, HelpString)</pre> If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, ForEachCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function(Command, Permission, HelpString)</pre> If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." },

View File

@ -7,7 +7,8 @@ return
Desc = [[ Desc = [[
A plugin may implement an OnChat() function and register it as a Hook to process chat messages from A plugin may implement an OnChat() function and register it as a Hook to process chat messages from
the players. The function is then called for every in-game message sent from any player. Note that the players. The function is then called for every in-game message sent from any player. Note that
commands are handled separately using a command framework API. registered in-game commands are not sent through this hook. Use the
{{OnExecuteCommand|HOOK_EXECUTE_COMMAND}} to intercept registered in-game commands.
]], ]],
Params = { Params = {
{ Name = "Player", Type = "{{cPlayer}}", Notes = "The player who sent the message" }, { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who sent the message" },

View File

@ -0,0 +1,29 @@
return
{
HOOK_ENTITY_CHANGE_WORLD =
{
CalledWhen = "Before a entity is changing the world.",
DefaultFnName = "OnEntityChangeWorld", -- also used as pagename
Desc = [[
This hook is called before the server moves the {{cEntity|entity}} to the given world. Plugins may
refuse the changing of the entity to the new world.<p>
See also the {{OnEntityChangedWorld|HOOK_ENTITY_CHANGED_WORLD}} hook for a similar hook is called after the
entity has been moved to the world.
]],
Params =
{
{ Name = "Entity", Type = "{{cEntity}}", Notes = "The entity that wants to change the world" },
{ Name = "World", Type = "{{cWorld}}", Notes = "The world to which the entity wants to change" },
},
Returns = [[
If the function returns false or no value, the next plugin's callback is called. If the function
returns true, no other callback is called for this event and the change of the entity to the world is
cancelled.
]],
}, -- HOOK_ENTITY_CHANGE_WORLD
}

View File

@ -0,0 +1,28 @@
return
{
HOOK_ENTITY_CHANGED_WORLD =
{
CalledWhen = "After a entity has changed the world.",
DefaultFnName = "OnEntityChangedWorld", -- also used as pagename
Desc = [[
This hook is called after the server has moved the {{cEntity|entity}} to the given world. This is an information-only
callback, the entity is already in the new world.<p>
See also the {{OnEntityChangeWorld|HOOK_ENTITY_CHANGE_WORLD}} hook for a similar hook called before the
entity is moved to the new world.
]],
Params =
{
{ Name = "Entity", Type = "{{cEntity}}", Notes = "The entity that has changed the world" },
{ Name = "World", Type = "{{cWorld}}", Notes = "The world from which the entity has come" },
},
Returns = [[
If the function returns false or no value, the next plugin's callback is called. If the function
returns true, no other callback is called for this event.
]],
}, -- HOOK_ENTITY_CHANGED_WORLD
}

View File

@ -2,7 +2,10 @@ return
{ {
HOOK_EXECUTE_COMMAND = HOOK_EXECUTE_COMMAND =
{ {
CalledWhen = "A player executes an in-game command, or the admin issues a console command. Note that built-in console commands are exempt to this hook - they are always performed and the hook is not called.", CalledWhen = [[
A player executes an in-game command, or the admin issues a console command. Note that built-in
console commands are exempt to this hook - they are always performed and the hook is not called.
]],
DefaultFnName = "OnExecuteCommand", -- also used as pagename DefaultFnName = "OnExecuteCommand", -- also used as pagename
Desc = [[ Desc = [[
A plugin may implement a callback for this hook to intercept both in-game commands executed by the A plugin may implement a callback for this hook to intercept both in-game commands executed by the
@ -11,17 +14,24 @@ return
server.</p> server.</p>
<p> <p>
If the command is in-game, the first parameter to the hook function is the {{cPlayer|player}} who's If the command is in-game, the first parameter to the hook function is the {{cPlayer|player}} who's
executing the command. If the command comes from the server console, the first parameter is nil. executing the command. If the command comes from the server console, the first parameter is nil.</p>
<p>
The server calls this hook even for unregistered (unknown) console commands. It also calls the hook
for unknown in-game commands, as long as they begin with a slash ('/'). If a plugin needs to intercept
in-game chat messages not beginning with a slash, it should use the {{OnChat|HOOK_CHAT}} hook.
]], ]],
Params = Params =
{ {
{ Name = "Player", Type = "{{cPlayer}}", Notes = "For in-game commands, the player who has sent the message. For console commands, nil" }, { Name = "Player", Type = "{{cPlayer}}", Notes = "For in-game commands, the player who has sent the message. For console commands, nil" },
{ Name = "Command", Type = "table of strings", Notes = "The command and its parameters, broken into a table by spaces" }, { Name = "CommandSplit", Type = "array-table of strings", Notes = "The command and its parameters, broken into a table by spaces" },
{ Name = "EntireCommand", Type = "string", Notes = "The entire command as a single string" },
}, },
Returns = [[ Returns = [[
If the plugin returns true, the command will be blocked and none of the remaining hook handlers will If the plugin returns false, MCServer calls all the remaining hook handlers and finally the command
be called. If the plugin returns false, MCServer calls all the remaining hook handlers and finally will be executed. If the plugin returns true, the none of the remaining hook handlers will be called.
the command will be executed. In this case the plugin can return a second value, specifying whether what the command result should
be set to, one of the {{cPluginManager#CommandResult|CommandResult}} constants. If not
provided, the value defaults to crBlocked.
]], ]],
}, -- HOOK_EXECUTE_COMMAND }, -- HOOK_EXECUTE_COMMAND
} }

View File

@ -285,7 +285,7 @@ local function WriteHtmlHook(a_Hook, a_HookNav)
for _, param in ipairs(a_Hook.Params) do for _, param in ipairs(a_Hook.Params) do
f:write("<tr><td>", param.Name, "</td><td>", LinkifyString(param.Type, HookName), "</td><td>", LinkifyString(param.Notes, HookName), "</td></tr>\n"); f:write("<tr><td>", param.Name, "</td><td>", LinkifyString(param.Type, HookName), "</td><td>", LinkifyString(param.Notes, HookName), "</td></tr>\n");
end end
f:write("</table>\n<p>" .. (a_Hook.Returns or "") .. "</p>\n\n"); f:write("</table>\n<p>" .. LinkifyString(a_Hook.Returns or "", HookName) .. "</p>\n\n");
f:write([[<hr /><h1>Code examples</h1><h2>Registering the callback</h2>]]); f:write([[<hr /><h1>Code examples</h1><h2>Registering the callback</h2>]]);
f:write("<pre class=\"prettyprint lang-lua\">\n"); f:write("<pre class=\"prettyprint lang-lua\">\n");
f:write([[cPluginManager:AddHook(cPluginManager.]] .. a_Hook.Name .. ", My" .. a_Hook.DefaultFnName .. [[);]]); f:write([[cPluginManager:AddHook(cPluginManager.]] .. a_Hook.Name .. ", My" .. a_Hook.DefaultFnName .. [[);]]);
@ -971,7 +971,7 @@ end
--- Writes a list of undocumented objects into a file --- Writes a list of undocumented objects into a file
local function ListUndocumentedObjects(API, UndocumentedHooks) local function ListUndocumentedObjects(API, UndocumentedHooks)
f = io.open("API/_undocumented.lua", "w"); local f = io.open("API/_undocumented.lua", "w");
if (f ~= nil) then if (f ~= nil) then
f:write("\n-- This is the list of undocumented API objects, automatically generated by APIDump\n\n"); f:write("\n-- This is the list of undocumented API objects, automatically generated by APIDump\n\n");
f:write("g_APIDesc =\n{\n\tClasses =\n\t{\n"); f:write("g_APIDesc =\n{\n\tClasses =\n\t{\n");

View File

@ -54,7 +54,7 @@ function Initialize(a_Plugin)
-- TestBlockAreas() -- TestBlockAreas()
-- TestSQLiteBindings() -- TestSQLiteBindings()
-- TestExpatBindings() -- TestExpatBindings()
-- TestPluginCalls() TestPluginCalls()
TestBlockAreasString() TestBlockAreasString()
TestStringBase64() TestStringBase64()
@ -157,26 +157,18 @@ function TestPluginCalls()
-- The Split parameter should be a table, but it is not used in that function anyway, -- The Split parameter should be a table, but it is not used in that function anyway,
-- so we can get away with passing nil to it. -- so we can get away with passing nil to it.
-- Use the old, deprecated and unsafe method: LOG("Debuggers: Calling NoSuchPlugin.FnName()...")
local Core = cPluginManager:Get():GetPlugin("Core") cPluginManager:CallPlugin("NoSuchPlugin", "FnName", "SomeParam")
if (Core ~= nil) then LOG("Debuggers: Calling Core.NoSuchFunction()...")
LOGINFO("Calling Core::ReturnColorFromChar() the old-fashioned way...") cPluginManager:CallPlugin("Core", "NoSuchFunction", "SomeParam")
local Gray = Core:Call("ReturnColorFromChar", nil, "8") LOG("Debuggers: Calling Core.ReturnColorFromChar(..., \"8\")...")
if (Gray ~= cChatColor.Gray) then local Gray = cPluginManager:CallPlugin("Core", "ReturnColorFromChar", "split", "8")
LOGWARNING("Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "<nil>"))
else
LOGINFO("Call succeeded")
end
end
-- Use the new method:
LOGINFO("Calling Core::ReturnColorFromChar() the recommended way...")
local Gray = cPluginManager:CallPlugin("Core", "ReturnColorFromChar", nil, "8")
if (Gray ~= cChatColor.Gray) then if (Gray ~= cChatColor.Gray) then
LOGWARNING("Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "<nil>")) LOGWARNING("Debuggers: Call failed, exp " .. cChatColor.Gray .. ", got " .. (Gray or "<nil>"))
else else
LOGINFO("Call succeeded") LOG("Debuggers: Call succeeded")
end end
LOG("Debuggers: Inter-plugin calls done.")
end end

View File

@ -65,8 +65,12 @@ local function ForumizeString(a_Str)
a_Str = a_Str:gsub("{%%p}", "\n\n") a_Str = a_Str:gsub("{%%p}", "\n\n")
a_Str = a_Str:gsub("{%%b}", "[b]"):gsub("{%%/b}", "[/b]") a_Str = a_Str:gsub("{%%b}", "[b]"):gsub("{%%/b}", "[/b]")
a_Str = a_Str:gsub("{%%i}", "[i]"):gsub("{%%/i}", "[/i]") a_Str = a_Str:gsub("{%%i}", "[i]"):gsub("{%%/i}", "[/i]")
a_Str = a_Str:gsub("{%%list}", "[list]"):gsub("{%%/list}", "[/list]") a_Str = a_Str:gsub("{%%list}", "\n[list]"):gsub("{%%/list}", "[/list]")
a_Str = a_Str:gsub("{%%li}", "[*]"):gsub("{%%/li}", "") a_Str = a_Str:gsub("{%%li}", "\n[*]"):gsub("{%%/li}", "\n")
-- Process links: {%a LinkDestination}LinkText{%/a}
a_Str = a_Str:gsub("{%%a%s([^}]*)}([^{]*){%%/a}", "[url=%1]%2[/url]")
-- TODO: Other formatting -- TODO: Other formatting
return a_Str return a_Str
@ -106,8 +110,12 @@ local function GithubizeString(a_Str)
a_Str = a_Str:gsub("{%%p}", "\n\n") a_Str = a_Str:gsub("{%%p}", "\n\n")
a_Str = a_Str:gsub("{%%b}", "**"):gsub("{%%/b}", "**") a_Str = a_Str:gsub("{%%b}", "**"):gsub("{%%/b}", "**")
a_Str = a_Str:gsub("{%%i}", "*"):gsub("{%%/i}", "*") a_Str = a_Str:gsub("{%%i}", "*"):gsub("{%%/i}", "*")
a_Str = a_Str:gsub("{%%list}", ""):gsub("{%%/list}", "") a_Str = a_Str:gsub("{%%list}", "\n"):gsub("{%%/list}", "\n")
a_Str = a_Str:gsub("{%%li}", " - "):gsub("{%%/li}", "") a_Str = a_Str:gsub("{%%li}", "\n - "):gsub("{%%/li}", "")
-- Process links: {%a LinkDestination}LinkText{%/a}
a_Str = a_Str:gsub("{%%a%s([^}]*)}([^{]*){%%/a}", "[%2](%1)")
-- TODO: Other formatting -- TODO: Other formatting
return a_Str return a_Str
@ -592,7 +600,10 @@ local function DumpPluginInfoForum(a_PluginFolder, a_PluginInfo)
DumpCommandsForum(a_PluginInfo, f) DumpCommandsForum(a_PluginInfo, f)
DumpPermissionsForum(a_PluginInfo, f) DumpPermissionsForum(a_PluginInfo, f)
if (a_PluginInfo.SourceLocation ~= nil) then if (a_PluginInfo.SourceLocation ~= nil) then
f:write("[b][color=blue]Source:[/color] [url=", a_PluginInfo.SourceLocation, "]Link[/url][/b]") f:write("\n[b]Source[/b]: ", a_PluginInfo.SourceLocation, "\n")
end
if (a_PluginInfo.DownloadLocation ~= nil) then
f:write("[b]Download[/b]: ", a_PluginInfo.DownloadLocation)
end end
f:close() f:close()
return true return true

View File

@ -43,21 +43,21 @@ end
--- This is a generic command callback used for handling multicommands' parent commands --- This is a generic command callback used for handling multicommands' parent commands
-- For example, if there are "/gal save" and "/gal load" commands, this callback handles the "/gal" command -- For example, if there are "/gal save" and "/gal load" commands, this callback handles the "/gal" command
-- It is used for both console and in-game commands; the console version has a_Player set to nil -- It is used for both console and in-game commands; the console version has a_Player set to nil
local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_Level) local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_Level, a_EntireCommand)
local Verb = a_Split[a_Level + 1]; local Verb = a_Split[a_Level + 1]
if (Verb == nil) then if (Verb == nil) then
-- No verb was specified. If there is a handler for the upper level command, call it: -- No verb was specified. If there is a handler for the upper level command, call it:
if (a_CmdInfo.Handler ~= nil) then if (a_CmdInfo.Handler ~= nil) then
return a_CmdInfo.Handler(a_Split, a_Player); return a_CmdInfo.Handler(a_Split, a_Player, a_EntireCommand)
end end
-- Let the player know they need to give a subcommand: -- Let the player know they need to give a subcommand:
assert(type(a_CmdInfo.Subcommands) == "table", "Info.lua error: There is no handler for command \"" .. a_CmdString .. "\" and there are no subcommands defined at level " .. a_Level) assert(type(a_CmdInfo.Subcommands) == "table", "Info.lua error: There is no handler for command \"" .. a_CmdString .. "\" and there are no subcommands defined at level " .. a_Level)
ListSubcommands(a_Player, a_CmdInfo.Subcommands, a_CmdString); ListSubcommands(a_Player, a_CmdInfo.Subcommands, a_CmdString)
return true; return true
end end
-- A verb was specified, look it up in the subcommands table: -- A verb was specified, look it up in the subcommands table:
local Subcommand = a_CmdInfo.Subcommands[Verb]; local Subcommand = a_CmdInfo.Subcommands[Verb]
if (Subcommand == nil) then if (Subcommand == nil) then
if (a_Level > 1) then if (a_Level > 1) then
-- This is a true subcommand, display the message and make MCS think the command was handled -- This is a true subcommand, display the message and make MCS think the command was handled
@ -67,7 +67,7 @@ local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_
else else
a_Player:SendMessage("The " .. a_CmdString .. " command doesn't support verb " .. Verb) a_Player:SendMessage("The " .. a_CmdString .. " command doesn't support verb " .. Verb)
end end
return true; return true
end end
-- This is a top-level command, let MCS handle the unknown message -- This is a top-level command, let MCS handle the unknown message
return false; return false;
@ -76,22 +76,22 @@ local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_
-- Check the permission: -- Check the permission:
if (a_Player ~= nil) then if (a_Player ~= nil) then
if not(a_Player:HasPermission(Subcommand.Permission or "")) then if not(a_Player:HasPermission(Subcommand.Permission or "")) then
a_Player:SendMessage("You don't have permission to execute this command"); a_Player:SendMessage("You don't have permission to execute this command")
return true; return true
end end
end end
-- If the handler is not valid, check the next sublevel: -- If the handler is not valid, check the next sublevel:
if (Subcommand.Handler == nil) then if (Subcommand.Handler == nil) then
if (Subcommand.Subcommands == nil) then if (Subcommand.Subcommands == nil) then
LOG("Cannot find handler for command " .. a_CmdString .. " " .. Verb); LOG("Cannot find handler for command " .. a_CmdString .. " " .. Verb)
return false; return false
end end
return MultiCommandHandler(a_Split, a_Player, a_CmdString .. " " .. Verb, Subcommand, a_Level + 1); return MultiCommandHandler(a_Split, a_Player, a_CmdString .. " " .. Verb, Subcommand, a_Level + 1, a_EntireCommand)
end end
-- Execute: -- Execute:
return Subcommand.Handler(a_Split, a_Player); return Subcommand.Handler(a_Split, a_Player, a_EntireCommand)
end end
@ -104,39 +104,39 @@ function RegisterPluginInfoCommands()
-- The a_Prefix param already contains the space after the previous command -- The a_Prefix param already contains the space after the previous command
-- a_Level is the depth of the subcommands being registered, with 1 being the top level command -- a_Level is the depth of the subcommands being registered, with 1 being the top level command
local function RegisterSubcommands(a_Prefix, a_Subcommands, a_Level) local function RegisterSubcommands(a_Prefix, a_Subcommands, a_Level)
assert(a_Subcommands ~= nil); assert(a_Subcommands ~= nil)
-- A table that will hold aliases to subcommands temporarily, during subcommand iteration -- A table that will hold aliases to subcommands temporarily, during subcommand iteration
local AliasTable = {} local AliasTable = {}
-- Iterate through the subcommands, register them, and accumulate aliases: -- Iterate through the subcommands, register them, and accumulate aliases:
for cmd, info in pairs(a_Subcommands) do for cmd, info in pairs(a_Subcommands) do
local CmdName = a_Prefix .. cmd; local CmdName = a_Prefix .. cmd
local Handler = info.Handler; local Handler = info.Handler
-- Provide a special handler for multicommands: -- Provide a special handler for multicommands:
if (info.Subcommands ~= nil) then if (info.Subcommands ~= nil) then
Handler = function(a_Split, a_Player) Handler = function(a_Split, a_Player, a_EntireCommand)
return MultiCommandHandler(a_Split, a_Player, CmdName, info, a_Level); return MultiCommandHandler(a_Split, a_Player, CmdName, info, a_Level, a_EntireCommand)
end end
end end
if (Handler == nil) then if (Handler == nil) then
LOGWARNING(g_PluginInfo.Name .. ": Invalid handler for command " .. CmdName .. ", command will not be registered."); LOGWARNING(g_PluginInfo.Name .. ": Invalid handler for command " .. CmdName .. ", command will not be registered.")
else else
local HelpString; local HelpString
if (info.HelpString ~= nil) then if (info.HelpString ~= nil) then
HelpString = " - " .. info.HelpString; HelpString = " - " .. info.HelpString
else else
HelpString = ""; HelpString = ""
end end
cPluginManager.BindCommand(CmdName, info.Permission or "", Handler, HelpString); cPluginManager.BindCommand(CmdName, info.Permission or "", Handler, HelpString)
-- Register all aliases for the command: -- Register all aliases for the command:
if (info.Alias ~= nil) then if (info.Alias ~= nil) then
if (type(info.Alias) == "string") then if (type(info.Alias) == "string") then
info.Alias = {info.Alias}; info.Alias = {info.Alias}
end end
for idx, alias in ipairs(info.Alias) do for idx, alias in ipairs(info.Alias) do
cPluginManager.BindCommand(a_Prefix .. alias, info.Permission or "", Handler, HelpString); cPluginManager.BindCommand(a_Prefix .. alias, info.Permission or "", Handler, HelpString)
-- Also copy the alias's info table as a separate subcommand, -- Also copy the alias's info table as a separate subcommand,
-- so that MultiCommandHandler() handles it properly. Need to off-load into a separate table -- so that MultiCommandHandler() handles it properly. Need to off-load into a separate table
-- than the one we're currently iterating and join after the iterating. -- than the one we're currently iterating and join after the iterating.
@ -147,7 +147,7 @@ function RegisterPluginInfoCommands()
-- Recursively register any subcommands: -- Recursively register any subcommands:
if (info.Subcommands ~= nil) then if (info.Subcommands ~= nil) then
RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands, a_Level + 1); RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands, a_Level + 1)
end end
end -- for cmd, info - a_Subcommands[] end -- for cmd, info - a_Subcommands[]
@ -159,7 +159,7 @@ function RegisterPluginInfoCommands()
end end
-- Loop through all commands in the plugin info, register each: -- Loop through all commands in the plugin info, register each:
RegisterSubcommands("", g_PluginInfo.Commands, 1); RegisterSubcommands("", g_PluginInfo.Commands, 1)
end end
@ -171,26 +171,26 @@ function RegisterPluginInfoConsoleCommands()
-- A sub-function that registers all subcommands of a single command, using the command's Subcommands table -- A sub-function that registers all subcommands of a single command, using the command's Subcommands table
-- The a_Prefix param already contains the space after the previous command -- The a_Prefix param already contains the space after the previous command
local function RegisterSubcommands(a_Prefix, a_Subcommands, a_Level) local function RegisterSubcommands(a_Prefix, a_Subcommands, a_Level)
assert(a_Subcommands ~= nil); assert(a_Subcommands ~= nil)
for cmd, info in pairs(a_Subcommands) do for cmd, info in pairs(a_Subcommands) do
local CmdName = a_Prefix .. cmd; local CmdName = a_Prefix .. cmd
local Handler = info.Handler local Handler = info.Handler
if (Handler == nil) then if (Handler == nil) then
Handler = function(a_Split) Handler = function(a_Split, a_EntireCommand)
return MultiCommandHandler(a_Split, nil, CmdName, info, a_Level); return MultiCommandHandler(a_Split, nil, CmdName, info, a_Level, a_EntireCommand)
end end
end end
cPluginManager.BindConsoleCommand(CmdName, Handler, info.HelpString or ""); cPluginManager.BindConsoleCommand(CmdName, Handler, info.HelpString or "")
-- Recursively register any subcommands: -- Recursively register any subcommands:
if (info.Subcommands ~= nil) then if (info.Subcommands ~= nil) then
RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands, a_Level + 1); RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands, a_Level + 1)
end end
end end
end end
-- Loop through all commands in the plugin info, register each: -- Loop through all commands in the plugin info, register each:
RegisterSubcommands("", g_PluginInfo.ConsoleCommands, 1); RegisterSubcommands("", g_PluginInfo.ConsoleCommands, 1)
end end

View File

@ -304,7 +304,7 @@ Dropper = Cobblestone, 1:1, 2:1, 3:1, 1:2, 1:3, 3:2, 3:3 | Hopper, 2:2
Repeater = Stone, 1:2, 2:2, 3:2 | RedstoneTorchOn, 1:1, 3:1 | RedstoneDust, 2:1 Repeater = Stone, 1:2, 2:2, 3:2 | RedstoneTorchOn, 1:1, 3:1 | RedstoneDust, 2:1
Comparator = RedstoneTorchOn, 2:1, 1:2, 3:2 | NetherQuartz, 2:2 | Stone, 1:3, 2:3, 3:3 Comparator = RedstoneTorchOn, 2:1, 1:2, 3:2 | NetherQuartz, 2:2 | Stone, 1:3, 2:3, 3:3
DaylightSensor = Glass, 1:1, 2:1, 3:1 | NetherQuartz, 1:2, 2:2, 3:2 | Woodslab, 1:3, 2:3, 3:3 DaylightSensor = Glass, 1:1, 2:1, 3:1 | NetherQuartz, 1:2, 2:2, 3:2 | Woodslab, 1:3, 2:3, 3:3
Hopper = Ironbars, 1:1, 3:1, 1:2, 3:2, 2:3 | Chest, 2:2 Hopper = IronIngot, 1:1, 3:1, 1:2, 3:2, 2:3 | Chest, 2:2
Piston = Planks^-1, 1:1, 2:1, 3:1 | RedstoneDust, 2:3 | Cobblestone, 1:2, 3:2, 1:3, 3:3 | IronIngot, 2:2 Piston = Planks^-1, 1:1, 2:1, 3:1 | RedstoneDust, 2:3 | Cobblestone, 1:2, 3:2, 1:3, 3:3 | IronIngot, 2:2
StickyPiston = Piston, * | SlimeBall, * StickyPiston = Piston, * | SlimeBall, *
RedstoneLamp = RedstoneDust, 2:1, 1:2, 3:2, 2:3 | Glowstone, 2:2 RedstoneLamp = RedstoneDust, 2:1, 1:2, 3:2, 2:3 | Glowstone, 2:2

View File

@ -1,12 +1,14 @@
MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer/master.svg?style=flat)](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) [![weekly tips](http://img.shields.io/gratipay/cuberite_team.svg?style=flat)](http://gratipay.com/cuberite_team) [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74) MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer/master.svg?style=flat)](https://travis-ci.org/mc-server/MCServer) [![Coverity Scan Build Status](https://img.shields.io/coverity/scan/1930.svg)](https://scan.coverity.com/projects/1930) [![weekly tips](http://img.shields.io/gratipay/cuberite_team.svg?style=flat)](http://gratipay.com/cuberite_team)
======== ========
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 is a Minecraft-compatible multiplayer game 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 is compatible with the vanilla Minecraft client.
MCServer can run on Windows, *nix and Android operating systems. This includes Android phones and tablets as well as Raspberry Pis. MCServer can run on Windows, *nix and Android operating systems. This includes Android phones and tablets as well as Raspberry Pis.
We currently support Release 1.7 and 1.8 (not beta) Minecraft protocol versions. We currently support Release 1.7 and 1.8 (not beta) Minecraft protocol versions.
Subscribe to [the newsletter](http://newsletter.cuberite.org/subscribe.htm) for important updates and project news.
Installation Installation
------------ ------------
Hosted MCServer is available DIY on DigitalOcean: [![Install on DigitalOcean](http://doinstall.bearbin.net/button.svg)](http://doinstall.bearbin.net/install?url=https://github.com/mc-server/MCServer) and [Gamososm](https://gamocosm.com) also offers MCServer support. Hosted MCServer is available DIY on DigitalOcean: [![Install on DigitalOcean](http://doinstall.bearbin.net/button.svg)](http://doinstall.bearbin.net/install?url=https://github.com/mc-server/MCServer) and [Gamososm](https://gamocosm.com) also offers MCServer support.
@ -34,9 +36,7 @@ Check out the [CONTRIBUTING.md](https://github.com/mc-server/MCServer/blob/maste
Other Stuff Other Stuff
----------- -----------
For other stuff, including plugins and discussion, check the [forums](http://forum.mc-server.org) and [Plugin API](http://mc-server.xoft.cz/LuaAPI/). For other stuff, including plugins and discussion, check out the [forums](http://forum.mc-server.org) and [Plugin API](http://mc-server.xoft.cz/LuaAPI/).
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)
Support Us on Gratipay: [![Support via Gratipay](http://img.shields.io/gittip/cuberite_team.svg)](https://www.gratipay.com/cuberite_team) Support Us on Gratipay: [![Support via Gratipay](http://img.shields.io/gittip/cuberite_team.svg)](https://www.gratipay.com/cuberite_team)

View File

@ -4,10 +4,10 @@ PLATFORM=$(uname -m)
echo "Identifying platform: $PLATFORM" echo "Identifying platform: $PLATFORM"
case $PLATFORM in case $PLATFORM in
"i686") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20x86/lastSuccessfulBuild/artifact/MCServer.tar" ;; "i686") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20x86/lastSuccessfulBuild/artifact/MCServer.tar.gz" ;;
"x86_64") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20x64/lastSuccessfulBuild/artifact/MCServer.tar" ;; "x86_64") DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20x64/lastSuccessfulBuild/artifact/MCServer.tar.gz" ;;
# Assume that all arm devices are a raspi for now. # Assume that all arm devices are a raspi for now.
arm*) DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20armhf/lastSuccessfulBuild/artifact/MCServer/MCServer.tar" arm*) DOWNLOADURL="http://builds.cuberite.org/job/MCServer%20Linux%20armhf/lastSuccessfulBuild/artifact/MCServer/MCServer.tar.gz"
esac esac
echo "Downloading precompiled binaries." echo "Downloading precompiled binaries."

1
lib/TCLAP Submodule

@ -0,0 +1 @@
Subproject commit 12cee38782897cfe60a1611615c200c45cd99eaf

View File

@ -54,3 +54,11 @@ string.repl = ogsub
-- Lua 5.2+ and LuaJit don't have string.gfind(). Use string.gmatch() instead:
if not(string.gfind) then
string.gfind = string.gmatch
end

View File

@ -1,2 +1,4 @@
lua51.dll lua51.dll
LuaState_Call.inc LuaState_Declaration.inc
LuaState_Implementation.cpp
LuaState_Typedefs.inc

View File

@ -12,7 +12,7 @@
:: Regenerate the files: :: Regenerate the files:
echo Regenerating LUA bindings . . . echo Regenerating LUA bindings . . .
"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg "tolua++.exe" -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg

View File

@ -1,2 +1,2 @@
#!/bin/bash #!/bin/bash
/usr/bin/tolua++ -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg /usr/bin/tolua++ -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg

View File

@ -13,7 +13,7 @@
:: Regenerate the files: :: Regenerate the files:
echo Regenerating LUA bindings . . . 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 lua ..\..\lib\tolua++\src\bin\lua\_driver.lua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg

View File

@ -0,0 +1,161 @@
-- BindingsProcessor.lua
-- Implements additional processing that is done while generating the Lua bindings
local access = {public = 0, protected = 1, private = 2}
--- Defines classes that have a custom manual Push() implementation and should not generate the automatic one
-- Map of classname -> true
local g_HasCustomPushImplementation =
{
cEntity = true
}
function parser_hook(s)
local container = classContainer.curr -- get the current container
-- process access-specifying labels (public, private, etc)
do
local b, e, label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
if b then
-- found a label, get the new access value from the global 'access' table
if access[label] then
container.curr_member_access = access[label]
end -- else ?
return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:]
end
end
end
--- Outputs the helper files supplementing the cLuaState class
-- Writes:
-- LuaState_Declaration.inc
-- LuaState_Implementation.cpp
-- LuaState_Typedefs.inc
local function OutputLuaStateHelpers(a_Package)
-- Collect all class types from ToLua:
local types = {}
for idx, item in ipairs(a_Package) do
local mt = getmetatable(item) or {}
if (mt.classtype == "class") then
table.insert(types, {name = item.name, lname = item.lname})
end
end
table.sort(types,
function(a_Item1, a_Item2)
return (a_Item1.name:lower() < a_Item2.name:lower())
end
)
-- Output the typedefs:
do
local f = assert(io.open("LuaState_Typedefs.inc", "w"))
f:write("\n// LuaState_Typedefs.inc\n\n// This file is generated along with the Lua bindings by ToLua. Do not edit manually, do not commit to repo.\n")
f:write("// Provides a forward declaration and a typedef for a pointer to each class exported to the Lua API.\n")
f:write("\n\n\n\n\n")
for _, item in ipairs(types) do
if not(item.name:match(".*<.*")) then -- Skip templates altogether
-- Classes start with a "c", everything else is a struct:
if (item.name:sub(1, 1) == "c") then
f:write("class " .. item.name .. ";\n")
else
f:write("struct " .. item.name .. ";\n")
end
end
end
f:write("\n\n\n\n\n")
for _, item in ipairs(types) do
f:write("typedef " .. item.name .. " * Ptr" .. item.lname .. ";\n")
end
f:write("\n\n\n\n\n")
f:close()
end
-- Output the Push() and GetStackValue() function declarations:
do
local f = assert(io.open("LuaState_Declaration.inc", "w"))
f:write("\n// LuaState_Declaration.inc\n\n// This file is generated along with the Lua bindings by ToLua. Do not edit manually, do not commit to repo.\n")
f:write("// Implements a Push() and GetStackValue() function for each class exported to the Lua API.\n")
f:write("// This file expects to be included form inside the cLuaState class definition\n")
f:write("\n\n\n\n\n")
for _, item in ipairs(types) do
f:write("void Push(" .. item.name .. " * a_Value);\n")
end
for _, item in ipairs(types) do
f:write("void GetStackValue(int a_StackPos, Ptr" .. item.lname .. " & a_ReturnedVal);\n")
end
f:write("\n\n\n\n\n")
f:close()
end
-- Output the Push() and GetStackValue() function implementations:
do
local f = assert(io.open("LuaState_Implementation.cpp", "w"))
f:write("\n// LuaState_Implementation.cpp\n\n// This file is generated along with the Lua bindings by ToLua. Do not edit manually, do not commit to repo.\n")
f:write("// Implements a Push() and GetStackValue() function for each class exported to the Lua API.\n")
f:write("// This file expects to be compiled as a separate translation unit\n")
f:write("\n\n\n\n\n")
f:write("#include \"Globals.h\"\n#include \"LuaState.h\"\n#include \"tolua++/include/tolua++.h\"\n")
f:write("\n\n\n\n\n")
for _, item in ipairs(types) do
if not(g_HasCustomPushImplementation[item.name]) then
f:write("void cLuaState::Push(" .. item.name .. " * a_Value)\n{\n\tASSERT(IsValid());\n")
f:write("\ttolua_pushusertype(m_LuaState, a_Value, \"" .. item.name .. "\");\n");
f:write("\tm_NumCurrentFunctionArgs += 1;\n")
f:write("}\n\n\n\n\n\n")
end
end
for _, item in ipairs(types) do
f:write("void cLuaState::GetStackValue(int a_StackPos, Ptr" .. item.lname .. " & a_ReturnedVal)\n{\n\tASSERT(IsValid());\n")
f:write("\tif (lua_isnil(m_LuaState, a_StackPos))\n\t{\n")
f:write("\t a_ReturnedVal = nullptr;\n")
f:write("\t return;\n\t}\n")
f:write("\ttolua_Error err;\n")
f:write("\tif (tolua_isusertype(m_LuaState, a_StackPos, \"" .. item.name .. "\", false, &err))\n")
f:write("\t{\n")
f:write("\t a_ReturnedVal = *(reinterpret_cast<" .. item.name .. " **>(lua_touserdata(m_LuaState, a_StackPos)));\n")
f:write("\t}\n")
f:write("}\n\n\n\n\n\n")
end
f:close()
end
end
function pre_output_hook(a_Package)
OutputLuaStateHelpers(a_Package)
end
function post_output_hook()
print("Bindings have been generated.")
end

View File

@ -11,12 +11,14 @@ SET (SRCS
LuaNameLookup.cpp LuaNameLookup.cpp
LuaServerHandle.cpp LuaServerHandle.cpp
LuaState.cpp LuaState.cpp
LuaState_Implementation.cpp
LuaTCPLink.cpp LuaTCPLink.cpp
LuaUDPEndpoint.cpp LuaUDPEndpoint.cpp
LuaWindow.cpp LuaWindow.cpp
ManualBindings.cpp ManualBindings.cpp
ManualBindings_Network.cpp ManualBindings_Network.cpp
ManualBindings_RankManager.cpp ManualBindings_RankManager.cpp
ManualBindings_World.cpp
Plugin.cpp Plugin.cpp
PluginLua.cpp PluginLua.cpp
PluginManager.cpp PluginManager.cpp
@ -31,6 +33,8 @@ SET (HDRS
LuaNameLookup.h LuaNameLookup.h
LuaServerHandle.h LuaServerHandle.h
LuaState.h LuaState.h
LuaState_Declaration.inc
LuaState_Typedefs.inc
LuaTCPLink.h LuaTCPLink.h
LuaUDPEndpoint.h LuaUDPEndpoint.h
LuaWindow.h LuaWindow.h
@ -46,12 +50,15 @@ SET (HDRS
set (BINDING_OUTPUTS set (BINDING_OUTPUTS
${CMAKE_CURRENT_SOURCE_DIR}/Bindings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Bindings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Bindings.h ${CMAKE_CURRENT_SOURCE_DIR}/Bindings.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaState_Declaration.inc
${CMAKE_CURRENT_SOURCE_DIR}/LuaState_Implementation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/LuaState_Typedefs.inc
) )
set(BINDING_DEPENDENCIES set(BINDING_DEPENDENCIES
tolua tolua
../Bindings/virtual_method_hooks.lua
../Bindings/AllToLua.pkg ../Bindings/AllToLua.pkg
../Bindings/BindingsProcessor.lua
../Bindings/LuaFunctions.h ../Bindings/LuaFunctions.h
../Bindings/LuaWindow.h ../Bindings/LuaWindow.h
../Bindings/Plugin.h ../Bindings/Plugin.h
@ -126,7 +133,7 @@ if (NOT MSVC)
OUTPUT ${BINDING_OUTPUTS} OUTPUT ${BINDING_OUTPUTS}
# Regenerate bindings: # Regenerate bindings:
COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg COMMAND tolua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
# add any new generation dependencies here # add any new generation dependencies here
@ -134,8 +141,7 @@ if (NOT MSVC)
) )
endif () endif ()
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.cpp PROPERTIES GENERATED TRUE) set_source_files_properties(${BINDING_OUTPUTS} PROPERTIES GENERATED TRUE)
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.h PROPERTIES GENERATED TRUE)
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS -Wno-error) set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS -Wno-error)

View File

@ -19,13 +19,13 @@ extern "C"
#include "../Entities/Entity.h" #include "../Entities/Entity.h"
#include "../BlockEntities/BlockEntity.h" #include "../BlockEntities/BlockEntity.h"
// fwd: SQLite/lsqlite3.c // fwd: "SQLite/lsqlite3.c"
extern "C" extern "C"
{ {
int luaopen_lsqlite3(lua_State * L); int luaopen_lsqlite3(lua_State * L);
} }
// fwd: LuaExpat/lxplib.c: // fwd: "LuaExpat/lxplib.c":
extern "C" extern "C"
{ {
int luaopen_lxp(lua_State * L); int luaopen_lxp(lua_State * L);
@ -107,7 +107,7 @@ void cLuaState::Create(void)
void cLuaState::RegisterAPILibs(void) void cLuaState::RegisterAPILibs(void)
{ {
tolua_AllToLua_open(m_LuaState); tolua_AllToLua_open(m_LuaState);
ManualBindings::Bind(m_LuaState); cManualBindings::Bind(m_LuaState);
DeprecatedBindings::Bind(m_LuaState); DeprecatedBindings::Bind(m_LuaState);
luaopen_lsqlite3(m_LuaState); luaopen_lsqlite3(m_LuaState);
luaopen_lxp(m_LuaState); luaopen_lxp(m_LuaState);
@ -530,42 +530,6 @@ void cLuaState::Push(bool a_Value)
void cLuaState::Push(cBlockEntity * a_BlockEntity)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_BlockEntity, (a_BlockEntity == nullptr) ? "cBlockEntity" : a_BlockEntity->GetClass());
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) void cLuaState::Push(cEntity * a_Entity)
{ {
ASSERT(IsValid()); ASSERT(IsValid());
@ -632,42 +596,6 @@ void cLuaState::Push(cEntity * a_Entity)
void cLuaState::Push(cHopperEntity * a_Hopper)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(cItem * a_Item)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Item, "cItem");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(cItems * a_Items)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Items, "cItems");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(cLuaServerHandle * a_ServerHandle) void cLuaState::Push(cLuaServerHandle * a_ServerHandle)
{ {
ASSERT(IsValid()); ASSERT(IsValid());
@ -704,126 +632,6 @@ void cLuaState::Push(cLuaUDPEndpoint * a_UDPEndpoint)
void cLuaState::Push(cMonster * a_Monster)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Monster, "cMonster");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(cPickup * a_Pickup)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Pickup, "cPickup");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(cPlayer * a_Player)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Player, "cPlayer");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(cPlugin * a_Plugin)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Plugin, "cPlugin");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(cPluginLua * a_Plugin)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Plugin, "cPluginLua");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(cProjectileEntity * a_ProjectileEntity)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_ProjectileEntity, "cProjectileEntity");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(cTNTEntity * a_TNTEntity)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_TNTEntity, "cTNTEntity");
m_NumCurrentFunctionArgs += 1;
}
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) void cLuaState::Push(double a_Value)
{ {
ASSERT(IsValid()); ASSERT(IsValid());
@ -848,42 +656,6 @@ void cLuaState::Push(int a_Value)
void cLuaState::Push(TakeDamageInfo * a_TDI)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(Vector3d * a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Vector, "Vector3<double>");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(Vector3i * a_Vector)
{
ASSERT(IsValid());
tolua_pushusertype(m_LuaState, a_Vector, "Vector3<int>");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::Push(void * a_Ptr) void cLuaState::Push(void * a_Ptr)
{ {
UNUSED(a_Ptr); UNUSED(a_Ptr);
@ -899,6 +671,10 @@ void cLuaState::Push(void * a_Ptr)
m_NumCurrentFunctionArgs += 1; m_NumCurrentFunctionArgs += 1;
} }
void cLuaState::Push(std::chrono::milliseconds a_Value) void cLuaState::Push(std::chrono::milliseconds a_Value)
{ {
ASSERT(IsValid()); ASSERT(IsValid());
@ -911,6 +687,7 @@ void cLuaState::Push(std::chrono::milliseconds a_Value)
/*
void cLuaState::PushUserType(void * a_Object, const char * a_Type) void cLuaState::PushUserType(void * a_Object, const char * a_Type)
{ {
ASSERT(IsValid()); ASSERT(IsValid());
@ -918,6 +695,7 @@ void cLuaState::PushUserType(void * a_Object, const char * a_Type)
tolua_pushusertype(m_LuaState, a_Object, a_Type); tolua_pushusertype(m_LuaState, a_Object, a_Type);
m_NumCurrentFunctionArgs += 1; m_NumCurrentFunctionArgs += 1;
} }
*/
@ -958,6 +736,18 @@ void cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal)
void cLuaState::GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result)
{
if (lua_isnumber(m_LuaState, a_StackPos))
{
a_Result = static_cast<cPluginManager::CommandResult>(static_cast<int>((tolua_tonumber(m_LuaState, a_StackPos, a_Result))));
}
}
void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref) void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref)
{ {
a_Ref.RefStack(*this, a_StackPos); a_Ref.RefStack(*this, a_StackPos);
@ -979,6 +769,18 @@ void cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal)
void cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal)
{
if (lua_isnumber(m_LuaState, a_StackPos))
{
a_ReturnedVal = static_cast<float>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal));
}
}
void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal) void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal)
{ {
if (!lua_isnumber(m_LuaState, a_StackPos)) if (!lua_isnumber(m_LuaState, a_StackPos))
@ -1007,132 +809,6 @@ void cLuaState::GetStackValue(int a_StackPos, int & a_ReturnedVal)
void cLuaState::GetStackValue(int a_StackPos, pBlockArea & a_ReturnedVal)
{
if (lua_isnil(m_LuaState, a_StackPos))
{
a_ReturnedVal = nullptr;
return;
}
tolua_Error err;
if (tolua_isusertype(m_LuaState, a_StackPos, "cBlockArea", false, &err))
{
a_ReturnedVal = *(reinterpret_cast<cBlockArea **>(lua_touserdata(m_LuaState, a_StackPos)));
}
}
void cLuaState::GetStackValue(int a_StackPos, pBoundingBox & a_ReturnedVal)
{
if (lua_isnil(m_LuaState, a_StackPos))
{
a_ReturnedVal = nullptr;
return;
}
tolua_Error err;
if (tolua_isusertype(m_LuaState, a_StackPos, "cBoundingBox", false, &err))
{
a_ReturnedVal = *(reinterpret_cast<cBoundingBox **>(lua_touserdata(m_LuaState, a_StackPos)));
}
}
void cLuaState::GetStackValue(int a_StackPos, pMapManager & a_ReturnedVal)
{
if (lua_isnil(m_LuaState, a_StackPos))
{
a_ReturnedVal = nullptr;
return;
}
tolua_Error err;
if (tolua_isusertype(m_LuaState, a_StackPos, "cMapManager", false, &err))
{
a_ReturnedVal = *(reinterpret_cast<cMapManager **>(lua_touserdata(m_LuaState, a_StackPos)));
}
}
void cLuaState::GetStackValue(int a_StackPos, pPluginManager & a_ReturnedVal)
{
if (lua_isnil(m_LuaState, a_StackPos))
{
a_ReturnedVal = nullptr;
return;
}
tolua_Error err;
if (tolua_isusertype(m_LuaState, a_StackPos, "cPluginManager", false, &err))
{
a_ReturnedVal = *(reinterpret_cast<cPluginManager **>(lua_touserdata(m_LuaState, a_StackPos)));
}
}
void cLuaState::GetStackValue(int a_StackPos, pRoot & a_ReturnedVal)
{
if (lua_isnil(m_LuaState, a_StackPos))
{
a_ReturnedVal = nullptr;
return;
}
tolua_Error err;
if (tolua_isusertype(m_LuaState, a_StackPos, "cRoot", false, &err))
{
a_ReturnedVal = *(reinterpret_cast<cRoot **>(lua_touserdata(m_LuaState, a_StackPos)));
}
}
void cLuaState::GetStackValue(int a_StackPos, pScoreboard & a_ReturnedVal)
{
if (lua_isnil(m_LuaState, a_StackPos))
{
a_ReturnedVal = nullptr;
return;
}
tolua_Error err;
if (tolua_isusertype(m_LuaState, a_StackPos, "cScoreboard", false, &err))
{
a_ReturnedVal = *(reinterpret_cast<cScoreboard **>(lua_touserdata(m_LuaState, a_StackPos)));
}
}
void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal)
{
if (lua_isnil(m_LuaState, a_StackPos))
{
a_ReturnedVal = nullptr;
return;
}
tolua_Error err;
if (tolua_isusertype(m_LuaState, a_StackPos, "cWorld", false, &err))
{
a_ReturnedVal = *(reinterpret_cast<cWorld **>(lua_touserdata(m_LuaState, a_StackPos)));
}
}
bool cLuaState::CallFunction(int a_NumResults) bool cLuaState::CallFunction(int a_NumResults)
{ {
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
@ -1251,6 +927,9 @@ bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam)
VERIFY(lua_getstack(m_LuaState, 0, &entry)); VERIFY(lua_getstack(m_LuaState, 0, &entry));
VERIFY(lua_getinfo (m_LuaState, "n", &entry)); VERIFY(lua_getinfo (m_LuaState, "n", &entry));
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?"); AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
BreakIntoDebugger(m_LuaState);
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false; return false;
} // for i - Param } // for i - Param
@ -1398,7 +1077,7 @@ bool cLuaState::CheckParamFunctionOrNil(int a_StartParam, int a_EndParam)
bool cLuaState::CheckParamEnd(int a_Param) bool cLuaState::CheckParamEnd(int a_Param)
{ {
tolua_Error tolua_err; tolua_Error tolua_err;
if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err)) if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err) == 1)
{ {
return true; return true;
} }
@ -1415,6 +1094,30 @@ bool cLuaState::CheckParamEnd(int a_Param)
bool cLuaState::IsParamUserType(int a_Param, AString a_UserType)
{
ASSERT(IsValid());
tolua_Error tolua_err;
return (tolua_isusertype(m_LuaState, a_Param, a_UserType.c_str(), 0, &tolua_err) == 1);
}
bool cLuaState::IsParamNumber(int a_Param)
{
ASSERT(IsValid());
tolua_Error tolua_err;
return (tolua_isnumber(m_LuaState, a_Param, 0, &tolua_err) == 1);
}
bool cLuaState::ReportErrors(int a_Status) bool cLuaState::ReportErrors(int a_Status)
{ {
return ReportErrors(m_LuaState, a_Status); return ReportErrors(m_LuaState, a_Status);
@ -1494,7 +1197,7 @@ int cLuaState::CallFunctionWithForeignParams(
if (!PushFunction(a_FunctionName.c_str())) if (!PushFunction(a_FunctionName.c_str()))
{ {
LOGWARNING("Function '%s' not found", a_FunctionName.c_str()); LOGWARNING("Function '%s' not found", a_FunctionName.c_str());
lua_pop(m_LuaState, 2); lua_settop(m_LuaState, OldTop);
return -1; return -1;
} }
@ -1502,7 +1205,7 @@ int cLuaState::CallFunctionWithForeignParams(
if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0) if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0)
{ {
// Something went wrong, fix the stack and exit // Something went wrong, fix the stack and exit
lua_pop(m_LuaState, 2); lua_settop(m_LuaState, OldTop);
m_NumCurrentFunctionArgs = -1; m_NumCurrentFunctionArgs = -1;
m_CurrentFunctionName.clear(); m_CurrentFunctionName.clear();
return -1; return -1;
@ -1513,13 +1216,8 @@ int cLuaState::CallFunctionWithForeignParams(
if (ReportErrors(s)) if (ReportErrors(s))
{ {
LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str()); LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str());
// Fix the stack. // Reset the stack:
// We don't know how many values have been pushed, so just get rid of any that weren't there initially lua_settop(m_LuaState, OldTop);
int CurTop = lua_gettop(m_LuaState);
if (CurTop > OldTop)
{
lua_pop(m_LuaState, CurTop - OldTop);
}
// Reset the internal checking mechanisms: // Reset the internal checking mechanisms:
m_NumCurrentFunctionArgs = -1; m_NumCurrentFunctionArgs = -1;
@ -1671,6 +1369,7 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
{ {
LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1)); LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
LogStackTrace(a_LuaState, 1); LogStackTrace(a_LuaState, 1);
BreakIntoDebugger(a_LuaState);
return 1; // We left the error message on the stack as the return value return 1; // We left the error message on the stack as the return value
} }
@ -1678,6 +1377,28 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
int cLuaState::BreakIntoDebugger(lua_State * a_LuaState)
{
// Call the BreakIntoDebugger function, if available:
lua_getglobal(a_LuaState, "BreakIntoDebugger");
if (!lua_isfunction(a_LuaState, -1))
{
LOGD("LUA: BreakIntoDebugger() not found / not a function");
lua_pop(a_LuaState, 1);
return 1;
}
lua_insert(a_LuaState, -2); // Copy the string that has been passed to us
LOGD("Calling BreakIntoDebugger()...");
lua_call(a_LuaState, 1, 0);
LOGD("Returned from BreakIntoDebugger().");
return 0;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cLuaState::cRef: // cLuaState::cRef:

View File

@ -32,50 +32,13 @@ extern "C"
#include "../Vector3.h" #include "../Vector3.h"
#include "../Defines.h" #include "../Defines.h"
#include "PluginManager.h"
#include "LuaState_Typedefs.inc"
// fwd:
class cBlockArea;
class cBlockEntity;
class cBoundingBox;
class cChunkDesc;
class cClientHandle;
class cCraftingGrid;
class cCraftingRecipe;
class cEntity;
class cHopperEntity;
class cItem;
class cItems;
class cLuaServerHandle; class cLuaServerHandle;
class cLuaTCPLink; class cLuaTCPLink;
class cLuaUDPEndpoint; class cLuaUDPEndpoint;
class cMapManager;
class cMonster;
class cPickup;
class cPlayer;
class cPlugin;
class cPluginLua;
class cPluginManager;
class cProjectileEntity;
class cRoot;
class cScoreboard;
class cTNTEntity;
class cWebAdmin;
class cWindow;
class cWorld;
struct HTTPRequest;
struct HTTPTemplateRequest;
struct TakeDamageInfo;
typedef cBlockArea * pBlockArea;
typedef cBoundingBox * pBoundingBox;
typedef cMapManager * pMapManager;
typedef cPluginManager * pPluginManager;
typedef cRoot * pRoot;
typedef cScoreboard * pScoreboard;
typedef cWorld * pWorld;
@ -213,52 +176,30 @@ public:
void Push(const Vector3i & a_Vector); void Push(const Vector3i & a_Vector);
void Push(const Vector3i * a_Vector); void Push(const Vector3i * a_Vector);
// Push a value onto the stack (keep alpha-sorted): // Push a simple value onto the stack (keep alpha-sorted):
void Push(bool a_Value); void Push(bool a_Value);
void Push(cBlockEntity * a_BlockEntity); void Push(double a_Value);
void Push(cChunkDesc * a_ChunkDesc); void Push(int a_Value);
void Push(cClientHandle * a_ClientHandle); void Push(void * a_Ptr);
void Push(cEntity * a_Entity); void Push(std::chrono::milliseconds a_time);
void Push(cHopperEntity * a_Hopper);
void Push(cItem * a_Item);
void Push(cItems * a_Items);
void Push(cLuaServerHandle * a_ServerHandle); void Push(cLuaServerHandle * a_ServerHandle);
void Push(cLuaTCPLink * a_TCPLink); void Push(cLuaTCPLink * a_TCPLink);
void Push(cLuaUDPEndpoint * a_UDPEndpoint); void Push(cLuaUDPEndpoint * a_UDPEndpoint);
void Push(cMonster * a_Monster);
void Push(cPickup * a_Pickup);
void Push(cPlayer * a_Player);
void Push(cPlugin * a_Plugin);
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(std::chrono::milliseconds a_time);
// GetStackValue() retrieves the value at a_StackPos, if it is a valid type. If not, a_Value is unchanged. // GetStackValue() retrieves the value at a_StackPos, if it is a valid type. If not, a_Value is unchanged.
// Enum values are clamped to their allowed range. // Enum values are clamped to their allowed range.
void GetStackValue(int a_StackPos, AString & a_Value); void GetStackValue(int a_StackPos, AString & a_Value);
void GetStackValue(int a_StackPos, BLOCKTYPE & a_Value); void GetStackValue(int a_StackPos, BLOCKTYPE & a_Value);
void GetStackValue(int a_StackPos, bool & a_Value); void GetStackValue(int a_StackPos, bool & a_Value);
void GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result);
void GetStackValue(int a_StackPos, cRef & a_Ref); void GetStackValue(int a_StackPos, cRef & a_Ref);
void GetStackValue(int a_StackPos, double & a_Value); void GetStackValue(int a_StackPos, double & a_Value);
void GetStackValue(int a_StackPos, eWeather & a_Value); void GetStackValue(int a_StackPos, eWeather & a_Value);
void GetStackValue(int a_StackPos, float & a_ReturnedVal);
void GetStackValue(int a_StackPos, int & a_Value); void GetStackValue(int a_StackPos, int & a_Value);
void GetStackValue(int a_StackPos, pBlockArea & a_Value);
void GetStackValue(int a_StackPos, pBoundingBox & a_Value); // Include the auto-generated Push and GetStackValue() functions:
void GetStackValue(int a_StackPos, pMapManager & a_Value); #include "LuaState_Declaration.inc"
void GetStackValue(int a_StackPos, pPluginManager & a_Value);
void GetStackValue(int a_StackPos, pRoot & a_Value);
void GetStackValue(int a_StackPos, pScoreboard & a_Value);
void GetStackValue(int a_StackPos, pWorld & a_Value);
/** Call the specified Lua function. /** Call the specified Lua function.
Returns true if call succeeded, false if there was an error. Returns true if call succeeded, false if there was an error.
@ -307,6 +248,10 @@ public:
/** Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) */ /** Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) */
bool CheckParamEnd(int a_Param); bool CheckParamEnd(int a_Param);
bool IsParamUserType(int a_Param, AString a_UserType);
bool IsParamNumber(int a_Param);
/** If the status is nonzero, prints the text on the top of Lua stack and returns true */ /** If the status is nonzero, prints the text on the top of Lua stack and returns true */
bool ReportErrors(int status); bool ReportErrors(int status);
@ -433,7 +378,7 @@ protected:
bool PushFunction(const cTableRef & a_TableRef); bool PushFunction(const cTableRef & a_TableRef);
/** Pushes a usertype of the specified class type onto the stack */ /** Pushes a usertype of the specified class type onto the stack */
void PushUserType(void * a_Object, const char * a_Type); // void PushUserType(void * a_Object, const char * a_Type);
/** /**
Calls the function that has been pushed onto the stack by PushFunction(), Calls the function that has been pushed onto the stack by PushFunction(),
@ -444,6 +389,9 @@ protected:
/** Used as the error reporting function for function calls */ /** Used as the error reporting function for function calls */
static int ReportFnCallErrors(lua_State * a_LuaState); static int ReportFnCallErrors(lua_State * a_LuaState);
/** Tries to break into the MobDebug debugger, if it is installed. */
static int BreakIntoDebugger(lua_State * a_LuaState);
} ; } ;

View File

@ -159,7 +159,7 @@ void cLuaWindow::Destroy(void)
m_Plugin->Unreference(m_LuaRef); m_Plugin->Unreference(m_LuaRef);
} }
// Lua will take care of this object, it will garbage-collect it, so we *must not* delete it! // Lua will take care of this object, it will garbage-collect it, so we must not delete it!
m_IsDestroyed = false; m_IsDestroyed = false;
} }
@ -167,10 +167,10 @@ void cLuaWindow::Destroy(void)
void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer& a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
{ {
cSlotAreas Areas; cSlotAreas Areas;
for (auto Area : m_SlotAreas) for (auto && Area : m_SlotAreas)
{ {
if (Area != a_ClickedArea) if (Area != a_ClickedArea)
{ {

File diff suppressed because it is too large Load Diff

View File

@ -1,34 +1,567 @@
// ManualBindings.h
// Declares the cManualBindings class used as a namespace for functions exported to the Lua API manually
#pragma once #pragma once
struct lua_State; #include "LuaState.h"
class cPluginLua;
// fwd:
struct tolua_Error;
/** Provides namespace for the bindings. */ /** Provides namespace for the bindings. */
class ManualBindings class cManualBindings
{ {
public: public:
/** Binds all the manually implemented functions to tolua_S. */ /** Binds all the manually implemented functions to tolua_S. */
static void Bind(lua_State * tolua_S); static void Bind(lua_State * tolua_S);
protected: protected:
/** Binds the manually implemented cNetwork-related API to tolua_S.
Implemented in ManualBindings_Network.cpp. */
static void BindNetwork(lua_State * tolua_S);
/** Binds the manually implemented cRankManager glue code to tolua_S. /** Binds the manually implemented cRankManager glue code to tolua_S.
Implemented in ManualBindings_RankManager.cpp. */ Implemented in ManualBindings_RankManager.cpp. */
static void BindRankManager(lua_State * tolua_S); static void BindRankManager(lua_State * tolua_S);
/** Binds the manually implemented cNetwork-related API to tolua_S. /** Binds the manually implemented cWorld API functions to tolua_S.
Implemented in ManualBindings_Network.cpp. */ Implemented in ManualBindings_World.cpp. */
static void BindNetwork(lua_State * tolua_S); static void BindWorld(lua_State * tolua_S);
public:
// Helper functions:
static cPluginLua * GetLuaPlugin(lua_State * L);
static int tolua_do_error(lua_State * L, const char * a_pMsg, tolua_Error * a_pToLuaError);
static int lua_do_error(lua_State * L, const char * a_pFormat, ...);
/** Binds the DoWith(ItemName) functions of regular classes. */
template <
class Ty1,
class Ty2,
bool (Ty1::*DoWithFn)(const AString &, cItemCallback<Ty2> &)
>
static int DoWith(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamString(2) ||
!L.CheckParamFunction(3)
)
{
return 0;
}
// Get parameters:
Ty1 * Self;
AString ItemName;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, ItemName, FnRef);
if (Self == nullptr)
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
}
if (ItemName.empty() || (ItemName[0] == 0))
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1");
}
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2");
}
class cLuaCallback : public cItemCallback<Ty2>
{
public:
cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
m_LuaState(a_LuaState),
m_FnRef(a_FnRef)
{
}
private:
virtual bool Item(Ty2 * a_Item) override
{
bool ret = false;
m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
return ret;
}
cLuaState & m_LuaState;
cLuaState::cRef & m_FnRef;
} Callback(L, FnRef);
// Call the DoWith function:
bool res = (Self->*DoWithFn)(ItemName, Callback);
// Push the result as the return value:
L.Push(res);
return 1;
}
/** Template for static functions DoWith(ItemName), on a type that has a static ::Get() function. */
template <
class Ty1,
class Ty2,
bool (Ty1::*DoWithFn)(const AString &, cItemCallback<Ty2> &)
>
static int StaticDoWith(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamString(2) ||
!L.CheckParamFunction(3)
)
{
return 0;
}
// Get parameters:
AString ItemName;
cLuaState::cRef FnRef;
L.GetStackValues(2, ItemName, FnRef);
if (ItemName.empty() || (ItemName[0] == 0))
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1");
}
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2");
}
class cLuaCallback : public cItemCallback<Ty2>
{
public:
cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
m_LuaState(a_LuaState),
m_FnRef(a_FnRef)
{
}
private:
virtual bool Item(Ty2 * a_Item) override
{
bool ret = false;
m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
return ret;
}
cLuaState & m_LuaState;
cLuaState::cRef & m_FnRef;
} Callback(L, FnRef);
// Call the DoWith function:
bool res = (Ty1::Get()->*DoWithFn)(ItemName, Callback);
// Push the result as the return value:
L.Push(res);
return 1;
}
template <
class Ty1,
class Ty2,
bool (Ty1::*DoWithFn)(UInt32, cItemCallback<Ty2> &)
>
static int DoWithID(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamNumber(2) ||
!L.CheckParamFunction(3)
)
{
return 0;
}
// Get parameters:
Ty1 * Self = nullptr;
int ItemID;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, ItemID, FnRef);
if (Self == nullptr)
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
}
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2");
}
class cLuaCallback : public cItemCallback<Ty2>
{
public:
cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
m_LuaState(a_LuaState),
m_FnRef(a_FnRef)
{
}
private:
virtual bool Item(Ty2 * a_Item) override
{
bool ret = false;
m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
return ret;
}
cLuaState & m_LuaState;
cLuaState::cRef & m_FnRef;
} Callback(L, FnRef);
// Call the DoWith function:
bool res = (Self->*DoWithFn)(ItemID, Callback);
// Push the result as the return value:
L.Push(res);
return 1;
}
template <
class Ty1,
class Ty2,
bool (Ty1::*DoWithFn)(int, int, int, cItemCallback<Ty2> &)
>
static int DoWithXYZ(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamNumber(2, 5) ||
!L.CheckParamFunction(6)
)
{
return 0;
}
// Get parameters:
Ty1 * Self = nullptr;
int BlockX, BlockY, BlockZ;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, BlockX, BlockY, BlockZ, FnRef);
if (Self == nullptr)
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
}
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #5");
}
class cLuaCallback : public cItemCallback<Ty2>
{
public:
cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
m_LuaState(a_LuaState),
m_FnRef(a_FnRef)
{
}
private:
virtual bool Item(Ty2 * a_Item) override
{
bool ret = false;
m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
return ret;
}
cLuaState & m_LuaState;
cLuaState::cRef & m_FnRef;
} Callback(L, FnRef);
// Call the DoWith function:
bool res = (Self->*DoWithFn)(BlockX, BlockY, BlockZ, Callback);
// Push the result as the return value:
L.Push(res);
return 1;
}
template <
class Ty1,
class Ty2,
bool (Ty1::*ForEachFn)(int, int, cItemCallback<Ty2> &)
>
static int ForEachInChunk(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamNumber(2, 4) ||
!L.CheckParamFunction(5)
)
{
return 0;
}
// Get parameters:
Ty1 * Self = nullptr;
int ChunkX, ChunkZ;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, ChunkX, ChunkZ, FnRef);
if (Self == nullptr)
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
}
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #4");
}
class cLuaCallback : public cItemCallback<Ty2>
{
public:
cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
m_LuaState(a_LuaState),
m_FnRef(a_FnRef)
{
}
private:
virtual bool Item(Ty2 * a_Item) override
{
bool ret = false;
m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret);
return ret;
}
cLuaState & m_LuaState;
cLuaState::cRef & m_FnRef;
} Callback(L, FnRef);
// Call the DoWith function:
bool res = (Self->*ForEachFn)(ChunkX, ChunkZ, Callback);
// Push the result as the return value:
L.Push(res);
return 1;
}
template <
class Ty1,
class Ty2,
bool (Ty1::*ForEachFn)(const cBoundingBox &, cItemCallback<Ty2> &)
>
static int ForEachInBox(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamUserType(2, "cBoundingBox") ||
!L.CheckParamFunction(3) ||
!L.CheckParamEnd(4)
)
{
return 0;
}
// Get the params:
Ty1 * Self = nullptr;
cBoundingBox * Box = nullptr;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, Box, FnRef);
if ((Self == nullptr) || (Box == nullptr))
{
LOGWARNING("Invalid world (%p) or boundingbox (%p)", Self, Box);
L.LogStackTrace();
return 0;
}
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2");
}
// Callback wrapper for the Lua function:
class cLuaCallback : public cItemCallback<Ty2>
{
public:
cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FuncRef) :
m_LuaState(a_LuaState),
m_FnRef(a_FuncRef)
{
}
private:
cLuaState & m_LuaState;
cLuaState::cRef & m_FnRef;
// cItemCallback<Ty2> overrides:
virtual bool Item(Ty2 * a_Item) override
{
bool res = false;
if (!m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res))
{
LOGWARNING("Failed to call Lua callback");
m_LuaState.LogStackTrace();
return true; // Abort enumeration
}
return res;
}
} Callback(L, FnRef);
bool res = (Self->*ForEachFn)(*Box, Callback);
// Push the result as the return value:
L.Push(res);
return 1;
}
template <
class Ty1,
class Ty2,
bool (Ty1::*ForEachFn)(cItemCallback<Ty2> &)
>
static int ForEach(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamFunction(2) ||
!L.CheckParamEnd(3)
)
{
return 0;
}
// Get the params:
Ty1 * Self = nullptr;
cLuaState::cRef FnRef;
L.GetStackValues(1, Self, FnRef);
if (Self == nullptr)
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'.");
}
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #1");
}
class cLuaCallback : public cItemCallback<Ty2>
{
public:
cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
m_LuaState(a_LuaState),
m_FnRef(a_FnRef)
{
}
private:
cLuaState & m_LuaState;
cLuaState::cRef & m_FnRef;
virtual bool Item(Ty2 * a_Item) override
{
bool res = false; // By default continue the enumeration
m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res);
return res;
}
} Callback(L, FnRef);
// Call the enumeration:
bool res = (Self->*ForEachFn)(Callback);
// Push the return value:
L.Push(res);
return 1;
}
/** Implements bindings for ForEach() functions in a class that is static (has a ::Get() static function). */
template <
class Ty1,
class Ty2,
bool (Ty1::*ForEachFn)(cItemCallback<Ty2> &)
>
static int StaticForEach(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamFunction(2) ||
!L.CheckParamEnd(3)
)
{
return 0;
}
// Get the params:
cLuaState::cRef FnRef(L, 2);
if (!FnRef.IsValid())
{
return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #1");
}
class cLuaCallback : public cItemCallback<Ty2>
{
public:
cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
m_LuaState(a_LuaState),
m_FnRef(a_FnRef)
{
}
private:
cLuaState & m_LuaState;
cLuaState::cRef & m_FnRef;
virtual bool Item(Ty2 * a_Item) override
{
bool res = false; // By default continue the enumeration
m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res);
return res;
}
} Callback(L, FnRef);
// Call the enumeration:
bool res = (Ty1::Get()->*ForEachFn)(Callback);
// Push the return value:
L.Push(res);
return 1;
}
}; };
extern cPluginLua * GetLuaPlugin(lua_State * L);

View File

@ -39,7 +39,7 @@ static int tolua_cNetwork_Connect(lua_State * L)
} }
// Get the plugin instance: // Get the plugin instance:
cPluginLua * Plugin = GetLuaPlugin(L); cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr) if (Plugin == nullptr)
{ {
// An error message has been already printed in GetLuaPlugin() // An error message has been already printed in GetLuaPlugin()
@ -92,7 +92,7 @@ static int tolua_cNetwork_CreateUDPEndpoint(lua_State * L)
} }
// Get the plugin instance: // Get the plugin instance:
cPluginLua * Plugin = GetLuaPlugin(L); cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr) if (Plugin == nullptr)
{ {
// An error message has been already printed in GetLuaPlugin() // An error message has been already printed in GetLuaPlugin()
@ -171,7 +171,7 @@ static int tolua_cNetwork_HostnameToIP(lua_State * L)
} }
// Get the plugin instance: // Get the plugin instance:
cPluginLua * Plugin = GetLuaPlugin(L); cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr) if (Plugin == nullptr)
{ {
// An error message has been already printed in GetLuaPlugin() // An error message has been already printed in GetLuaPlugin()
@ -212,7 +212,7 @@ static int tolua_cNetwork_IPToHostname(lua_State * L)
} }
// Get the plugin instance: // Get the plugin instance:
cPluginLua * Plugin = GetLuaPlugin(L); cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr) if (Plugin == nullptr)
{ {
// An error message has been already printed in GetLuaPlugin() // An error message has been already printed in GetLuaPlugin()
@ -253,7 +253,7 @@ static int tolua_cNetwork_Listen(lua_State * L)
} }
// Get the plugin instance: // Get the plugin instance:
cPluginLua * Plugin = GetLuaPlugin(L); cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L);
if (Plugin == nullptr) if (Plugin == nullptr)
{ {
// An error message has been already printed in GetLuaPlugin() // An error message has been already printed in GetLuaPlugin()
@ -913,17 +913,17 @@ static int tolua_cUDPEndpoint_Send(lua_State * L)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Register the bindings: // Register the bindings:
void ManualBindings::BindNetwork(lua_State * tolua_S) void cManualBindings::BindNetwork(lua_State * tolua_S)
{ {
// Create the cNetwork API classes: // Create the cNetwork API classes:
tolua_usertype(tolua_S, "cNetwork"); tolua_usertype(tolua_S, "cNetwork");
tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr);
tolua_usertype(tolua_S, "cTCPLink");
tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr);
tolua_usertype(tolua_S, "cServerHandle"); tolua_usertype(tolua_S, "cServerHandle");
tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle); tolua_usertype(tolua_S, "cTCPLink");
tolua_usertype(tolua_S, "cUDPEndpoint"); tolua_usertype(tolua_S, "cUDPEndpoint");
tolua_cclass(tolua_S, "cUDPEndpoint", "cUDPEndpoint", "", tolua_collect_cUDPEndpoint); tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr);
tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle);
tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr);
tolua_cclass(tolua_S, "cUDPEndpoint", "cUDPEndpoint", "", tolua_collect_cUDPEndpoint);
// Fill in the functions (alpha-sorted): // Fill in the functions (alpha-sorted):
tolua_beginmodule(tolua_S, "cNetwork"); tolua_beginmodule(tolua_S, "cNetwork");

View File

@ -1280,7 +1280,7 @@ static int tolua_cRankManager_SetRankVisuals(lua_State * L)
void ManualBindings::BindRankManager(lua_State * tolua_S) void cManualBindings::BindRankManager(lua_State * tolua_S)
{ {
// Create the cRankManager class in the API: // Create the cRankManager class in the API:
tolua_usertype(tolua_S, "cRankManager"); tolua_usertype(tolua_S, "cRankManager");

View File

@ -0,0 +1,588 @@
// ManualBindings_World.cpp
// Implements the manual Lua API bindings for the cWorld class
#include "Globals.h"
#include "tolua++/include/tolua++.h"
#include "../World.h"
#include "../Broadcaster.h"
#include "ManualBindings.h"
#include "LuaState.h"
#include "PluginLua.h"
#include "LuaChunkStay.h"
static int tolua_cWorld_BroadcastParticleEffect(lua_State * tolua_S)
{
/* Function signature:
World:BroadcastParticleEffect("Name", PosX, PosY, PosZ, OffX, OffY, OffZ, ParticleData, ParticleAmount, [ExcludeClient], [OptionalParam1], [OptionalParam2]
*/
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamString (2) ||
!L.CheckParamNumber (3, 10)
)
{
return 0;
}
// Read the params:
cWorld * World = nullptr;
AString Name;
float PosX, PosY, PosZ, OffX, OffY, OffZ;
float ParticleData;
int ParticleAmmount;
cClientHandle * ExcludeClient = nullptr;
L.GetStackValues(1, World, Name, PosX, PosY, PosZ, OffX, OffY, OffZ, ParticleData, ParticleAmmount, ExcludeClient);
if (World == nullptr)
{
LOGWARNING("World:BroadcastParticleEffect(): invalid world parameter");
L.LogStackTrace();
return 0;
}
// Read up to 2 more optional data params:
std::array<int, 2> data;
for (int i = 0; (i < 2) && L.IsParamNumber(11 + i); i++)
{
L.GetStackValue(11 + i, data[i]);
}
World->GetBroadcaster().BroadcastParticleEffect(Name, Vector3f(PosX, PosY, PosZ), Vector3f(OffX, OffY, OffZ), ParticleData, ParticleAmmount, ExcludeClient);
return 0;
}
static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
{
/* Function signature:
World:ChunkStay(ChunkCoordTable, OnChunkAvailable, OnAllChunksAvailable)
ChunkCoordTable == { {Chunk1x, Chunk1z}, {Chunk2x, Chunk2z}, ... }
*/
cLuaState L(tolua_S);
if (
!L.CheckParamUserType (1, "cWorld") ||
!L.CheckParamTable (2) ||
!L.CheckParamFunctionOrNil(3, 4)
)
{
return 0;
}
cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S);
if (Plugin == nullptr)
{
return 0;
}
// Read the params:
cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr);
if (World == nullptr)
{
LOGWARNING("World:ChunkStay(): invalid world parameter");
L.LogStackTrace();
return 0;
}
cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin);
if (!ChunkStay->AddChunks(2))
{
delete ChunkStay;
ChunkStay = nullptr;
return 0;
}
ChunkStay->Enable(*World->GetChunkMap(), 3, 4);
return 0;
}
static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S)
{
// Exported manually, because tolua would generate useless additional parameters (a_BlockType .. a_BlockSkyLight)
// Function signature: GetBlockInfo(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta, BlockSkyLight, BlockBlockLight]
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamNumber(2, 4) ||
!L.CheckParamEnd(5)
)
{
return 0;
}
// Get params:
cWorld * Self = nullptr;
int BlockX, BlockY, BlockZ;
L.GetStackValues(1, Self, BlockX, BlockY, BlockZ);
if (Self == nullptr)
{
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
}
// Call the function:
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta, BlockSkyLight, BlockBlockLight;
bool res = Self->GetBlockInfo(BlockX, BlockY, BlockZ, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight);
// Push the returned values:
L.Push(res);
if (res)
{
L.Push(BlockType);
L.Push(BlockMeta);
L.Push(BlockSkyLight);
L.Push(BlockBlockLight);
return 5;
}
return 1;
}
static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S)
{
// Exported manually, because tolua would generate useless additional parameters (a_BlockType, a_BlockMeta)
// Function signature: GetBlockTypeMeta(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta]
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamNumber(2, 4) ||
!L.CheckParamEnd(5)
)
{
return 0;
}
// Get params:
cWorld * Self = nullptr;
int BlockX, BlockY, BlockZ;
L.GetStackValues(1, Self, BlockX, BlockY, BlockZ);
if (Self == nullptr)
{
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
}
// Call the function:
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
bool res = Self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta);
// Push the returned values:
L.Push(res);
if (res)
{
L.Push(BlockType);
L.Push(BlockMeta);
return 3;
}
return 1;
}
static int tolua_cWorld_GetSignLines(lua_State * tolua_S)
{
// Exported manually, because tolua would generate useless additional parameters (a_Line1 .. a_Line4)
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamNumber(2, 4) ||
!L.CheckParamEnd(5)
)
{
return 0;
}
// Get params:
cWorld * Self = nullptr;
int BlockX, BlockY, BlockZ;
L.GetStackValues(1, Self, BlockX, BlockY, BlockZ);
if (Self == nullptr)
{
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
}
// Call the function:
AString Line1, Line2, Line3, Line4;
bool res = Self->GetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
// Push the returned values:
L.Push(res);
if (res)
{
L.Push(Line1);
L.Push(Line2);
L.Push(Line3);
L.Push(Line4);
return 5;
}
return 1;
}
static int tolua_cWorld_PrepareChunk(lua_State * tolua_S)
{
/* Function signature:
World:PrepareChunk(ChunkX, ChunkZ, Callback)
*/
// Check the param types:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType (1, "cWorld") ||
!L.CheckParamNumber (2, 3) ||
!L.CheckParamFunctionOrNil(4)
)
{
return 0;
}
// Read the params:
cWorld * world = nullptr;
int chunkX = 0, chunkZ = 0;
L.GetStackValues(1, world, chunkX, chunkZ);
if (world == nullptr)
{
LOGWARNING("World:PrepareChunk(): invalid world parameter");
L.LogStackTrace();
return 0;
}
// Wrap the Lua callback inside a C++ callback class:
class cCallback:
public cChunkCoordCallback
{
public:
cCallback(lua_State * a_LuaState):
m_LuaState(a_LuaState),
m_Callback(m_LuaState, 4)
{
}
// cChunkCoordCallback override:
virtual void Call(int a_CBChunkX, int a_CBChunkZ) override
{
if (m_Callback.IsValid())
{
m_LuaState.Call(m_Callback, a_CBChunkX, a_CBChunkZ);
}
// This is the last reference of this object, we must delete it so that it doesn't leak:
delete this;
}
protected:
cLuaState m_LuaState;
cLuaState::cRef m_Callback;
};
cCallback * callback = new cCallback(tolua_S);
// Call the chunk preparation:
world->PrepareChunk(chunkX, chunkZ, callback);
return 0;
}
class cLuaWorldTask :
public cWorld::cTask,
public cPluginLua::cResettable
{
public:
cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) :
cPluginLua::cResettable(a_Plugin),
m_FnRef(a_FnRef)
{
}
protected:
int m_FnRef;
// cWorld::cTask overrides:
virtual void Run(cWorld & a_World) override
{
cCSLock Lock(m_CSPlugin);
if (m_Plugin != nullptr)
{
m_Plugin->Call(m_FnRef, &a_World);
}
}
} ;
static int tolua_cWorld_QueueTask(lua_State * tolua_S)
{
// Binding for cWorld::QueueTask
// Params: function
// Retrieve the cPlugin from the LuaState:
cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S);
if (Plugin == nullptr)
{
// An error message has been already printed in GetLuaPlugin()
return 0;
}
// Retrieve the args:
cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr);
if (self == nullptr)
{
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
}
if (!lua_isfunction(tolua_S, 2))
{
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1");
}
// Create a reference to the function:
int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
if (FnRef == LUA_REFNIL)
{
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
}
auto task = std::make_shared<cLuaWorldTask>(*Plugin, FnRef);
Plugin->AddResettable(task);
self->QueueTask(task);
return 0;
}
static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
{
// Exported manually, because tolua would generate useless additional return values (a_Line1 .. a_Line4)
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamNumber(2, 4) ||
!L.CheckParamString(5, 8) ||
!L.CheckParamEnd(9)
)
{
return 0;
}
// Get params:
cWorld * Self = nullptr;
int BlockX, BlockY, BlockZ;
AString Line1, Line2, Line3, Line4;
L.GetStackValues(1, Self, BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
if (Self == nullptr)
{
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'");
}
// Call the function:
bool res = Self->SetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
// Push the returned values:
L.Push(res);
return 1;
}
class cLuaScheduledWorldTask :
public cWorld::cTask,
public cPluginLua::cResettable
{
public:
cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef) :
cPluginLua::cResettable(a_Plugin),
m_FnRef(a_FnRef)
{
}
protected:
int m_FnRef;
// cWorld::cTask overrides:
virtual void Run(cWorld & a_World) override
{
cCSLock Lock(m_CSPlugin);
if (m_Plugin != nullptr)
{
m_Plugin->Call(m_FnRef, &a_World);
}
}
};
static int tolua_cWorld_ScheduleTask(lua_State * tolua_S)
{
// Binding for cWorld::ScheduleTask
// Params: function, Ticks
// Retrieve the cPlugin from the LuaState:
cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S);
if (Plugin == nullptr)
{
// An error message has been already printed in GetLuaPlugin()
return 0;
}
// Retrieve the args:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamNumber (2) ||
!L.CheckParamFunction(3)
)
{
return 0;
}
cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr);
if (World == nullptr)
{
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
}
// Create a reference to the function:
int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
if (FnRef == LUA_REFNIL)
{
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
}
int DelayTicks = (int)tolua_tonumber(tolua_S, 2, 0);
auto task = std::make_shared<cLuaScheduledWorldTask>(*Plugin, FnRef);
Plugin->AddResettable(task);
World->ScheduleTask(DelayTicks, task);
return 0;
}
static int tolua_cWorld_TryGetHeight(lua_State * tolua_S)
{
/* Exported manually, because tolua would require the out-only param a_Height to be used when calling
Function signature: world:TryGetHeight(a_World, a_BlockX, a_BlockZ) -> IsValid, Height
*/
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamNumber(2, 3) ||
!L.CheckParamEnd(4)
)
{
return 0;
}
// Get params:
cWorld * self = nullptr;
int BlockX, BlockZ;
L.GetStackValues(1, self, BlockX, BlockZ);
if (self == nullptr)
{
tolua_error(tolua_S, "Invalid 'self' in function 'TryGetHeight'", nullptr);
return 0;
}
// Call the implementation:
int Height = 0;
bool res = self->TryGetHeight(BlockX, BlockZ, Height);
L.Push(res ? 1 : 0);
if (res)
{
L.Push(Height);
return 2;
}
return 1;
}
void cManualBindings::BindWorld(lua_State * tolua_S)
{
tolua_beginmodule(tolua_S, nullptr);
tolua_beginmodule(tolua_S, "cWorld");
tolua_function(tolua_S, "BroadcastParticleEffect", tolua_cWorld_BroadcastParticleEffect);
tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay);
tolua_function(tolua_S, "DoWithBlockEntityAt", DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
tolua_function(tolua_S, "DoWithBeaconAt", DoWithXYZ<cWorld, cBeaconEntity, &cWorld::DoWithBeaconAt>);
tolua_function(tolua_S, "DoWithChestAt", DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
tolua_function(tolua_S, "DoWithDispenserAt", DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
tolua_function(tolua_S, "DoWithDropSpenserAt", DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
tolua_function(tolua_S, "DoWithDropperAt", DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>);
tolua_function(tolua_S, "DoWithEntityByID", DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>);
tolua_function(tolua_S, "DoWithFurnaceAt", DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
tolua_function(tolua_S, "DoWithNoteBlockAt", DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
tolua_function(tolua_S, "DoWithCommandBlockAt", DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>);
tolua_function(tolua_S, "DoWithMobHeadAt", DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadAt>);
tolua_function(tolua_S, "DoWithFlowerPotAt", DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>);
tolua_function(tolua_S, "DoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
tolua_function(tolua_S, "FindAndDoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
tolua_function(tolua_S, "DoWithPlayerByUUID", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayerByUUID>);
tolua_function(tolua_S, "ForEachBlockEntityInChunk", ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
tolua_function(tolua_S, "ForEachChestInChunk", ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
tolua_function(tolua_S, "ForEachEntity", ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
tolua_function(tolua_S, "ForEachEntityInBox", ForEachInBox< cWorld, cEntity, &cWorld::ForEachEntityInBox>);
tolua_function(tolua_S, "ForEachEntityInChunk", ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>);
tolua_function(tolua_S, "ForEachFurnaceInChunk", ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>);
tolua_function(tolua_S, "ForEachPlayer", ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>);
tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo);
tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta);
tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines);
tolua_function(tolua_S, "PrepareChunk", tolua_cWorld_PrepareChunk);
tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask);
tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask);
tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines);
tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight);
tolua_endmodule(tolua_S);
tolua_endmodule(tolua_S);
}

View File

@ -56,7 +56,9 @@ public:
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0;
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0;
virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0; virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; virtual bool OnEntityChangeWorld (cEntity & a_Entity, cWorld & a_World) = 0;
virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) = 0;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) = 0;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
virtual bool OnHandshake (cClientHandle & a_Client, const AString & a_Username) = 0; virtual bool OnHandshake (cClientHandle & a_Client, const AString & a_Username) = 0;

View File

@ -534,7 +534,55 @@ bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_E
bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) bool cPluginLua::OnEntityChangeWorld(cEntity & a_Entity, cWorld & a_World)
{
cCSLock Lock(m_CriticalSection);
if (!m_LuaState.IsValid())
{
return false;
}
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGE_WORLD];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
m_LuaState.Call((int)(**itr), &a_Entity, &a_World, cLuaState::Return, res);
if (res)
{
return true;
}
}
return false;
}
bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World)
{
cCSLock Lock(m_CriticalSection);
if (!m_LuaState.IsValid())
{
return false;
}
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGED_WORLD];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
m_LuaState.Call((int)(**itr), &a_Entity, &a_World, res);
if (res)
{
return true;
}
}
return false;
}
bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result)
{ {
cCSLock Lock(m_CriticalSection); cCSLock Lock(m_CriticalSection);
if (!m_LuaState.IsValid()) if (!m_LuaState.IsValid())
@ -545,7 +593,7 @@ bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Sp
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{ {
m_LuaState.Call((int)(**itr), a_Player, a_Split, cLuaState::Return, res); m_LuaState.Call((int)(**itr), a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result);
if (res) if (res)
{ {
return true; return true;
@ -1884,6 +1932,8 @@ const char * cPluginLua::GetHookFnName(int a_HookType)
case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect"; case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect";
case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation"; case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation";
case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect"; case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect";
case cPluginManager::HOOK_ENTITY_CHANGE_WORLD: return "OnEntityChangeWorld";
case cPluginManager::HOOK_ENTITY_CHANGED_WORLD: return "OnEntityChangedWorld";
case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport"; case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport";
case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand"; case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand";
case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake"; case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake";

View File

@ -20,7 +20,7 @@
// fwd: UI/Window.h // fwd: "UI/Window.h"
class cWindow; class cWindow;
@ -115,7 +115,9 @@ public:
virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override; virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override;
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override;
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override; virtual bool OnEntityChangeWorld (cEntity & a_Entity, cWorld & a_World) override;
virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) override;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) override;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
virtual bool OnHandshake (cClientHandle & a_Client, const AString & a_Username) override; virtual bool OnHandshake (cClientHandle & a_Client, const AString & a_Username) override;

View File

@ -118,7 +118,7 @@ void cPluginManager::ReloadPluginsNow(void)
void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) void cPluginManager::ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings)
{ {
LOG("-- Loading Plugins --"); LOG("-- Loading Plugins --");
@ -130,7 +130,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
RefreshPluginList(); RefreshPluginList();
// Load the plugins: // Load the plugins:
AStringVector ToLoad = GetFoldersToLoad(a_SettingsIni); AStringVector ToLoad = GetFoldersToLoad(a_Settings);
for (auto & pluginFolder: ToLoad) for (auto & pluginFolder: ToLoad)
{ {
LoadPlugin(pluginFolder); LoadPlugin(pluginFolder);
@ -157,16 +157,16 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni) void cPluginManager::InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings)
{ {
a_SettingsIni.AddKeyName("Plugins"); a_Settings.AddKeyName("Plugins");
a_SettingsIni.AddKeyComment("Plugins", " Plugin=Debuggers"); a_Settings.AddKeyComment("Plugins", " Plugin=Debuggers");
a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify"); a_Settings.AddKeyComment("Plugins", " Plugin=HookNotify");
a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx"); a_Settings.AddKeyComment("Plugins", " Plugin=ChunkWorx");
a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump"); a_Settings.AddKeyComment("Plugins", " Plugin=APIDump");
a_SettingsIni.AddValue("Plugins", "Plugin", "Core"); a_Settings.AddValue("Plugins", "Plugin", "Core");
a_SettingsIni.AddValue("Plugins", "Plugin", "TransAPI"); a_Settings.AddValue("Plugins", "Plugin", "TransAPI");
a_SettingsIni.AddValue("Plugins", "Plugin", "ChatLog"); a_Settings.AddValue("Plugins", "Plugin", "ChatLog");
} }
@ -525,14 +525,50 @@ bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d &
bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) bool cPluginManager::CallHookEntityChangeWorld(cEntity & a_Entity, cWorld & a_World)
{
FIND_HOOK(HOOK_ENTITY_CHANGE_WORLD);
VERIFY_HOOK;
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
{
if ((*itr)->OnEntityChangeWorld(a_Entity, a_World))
{
return true;
}
}
return false;
}
bool cPluginManager::CallHookEntityChangedWorld(cEntity & a_Entity, cWorld & a_World)
{
FIND_HOOK(HOOK_ENTITY_CHANGED_WORLD);
VERIFY_HOOK;
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
{
if ((*itr)->OnEntityChangedWorld(a_Entity, a_World))
{
return true;
}
}
return false;
}
bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result)
{ {
FIND_HOOK(HOOK_EXECUTE_COMMAND); FIND_HOOK(HOOK_EXECUTE_COMMAND);
VERIFY_HOOK; VERIFY_HOOK;
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
{ {
if ((*itr)->OnExecuteCommand(a_Player, a_Split)) if ((*itr)->OnExecuteCommand(a_Player, a_Split, a_EntireCommand, a_Result))
{ {
return true; return true;
} }
@ -1445,14 +1481,25 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player,
if (cmd == m_Commands.end()) if (cmd == m_Commands.end())
{ {
// Command not found // Command not found
// If it started with a slash, ask the plugins if they still want to handle it:
if (!a_Command.empty() && (a_Command[0] == '/'))
{
CommandResult Result = crUnknownCommand;
CallHookExecuteCommand(&a_Player, Split, a_Command, Result);
return Result;
}
return crUnknownCommand; return crUnknownCommand;
} }
// Ask plugins first if a command is okay to execute the command: // Ask plugins first if a command is okay to execute the command:
if (CallHookExecuteCommand(&a_Player, Split)) CommandResult Result = crBlocked;
if (CallHookExecuteCommand(&a_Player, Split, a_Command, Result))
{ {
LOGINFO("Player %s tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player.GetName().c_str(), Split[0].c_str()); if (Result == crBlocked)
return crBlocked; {
LOGINFO("Player %s tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player.GetName().c_str(), Split[0].c_str());
}
return Result;
} }
if ( if (
@ -1750,7 +1797,10 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma
if (cmd == m_ConsoleCommands.end()) if (cmd == m_ConsoleCommands.end())
{ {
// Command not found // Command not found
return false; // Still notify the plugins (so that plugins such as Aliases can intercept unknown commands).
CommandResult res = crBlocked;
CallHookExecuteCommand(nullptr, a_Split, a_Command, res);
return (res == crExecuted);
} }
if (cmd->second.m_Plugin == nullptr) if (cmd->second.m_Plugin == nullptr)
@ -1760,10 +1810,10 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma
} }
// Ask plugins first if a command is okay to execute the console command: // Ask plugins first if a command is okay to execute the console command:
if (CallHookExecuteCommand(nullptr, a_Split)) CommandResult res = crBlocked;
if (CallHookExecuteCommand(nullptr, a_Split, a_Command, res))
{ {
a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str()); return (res == crExecuted);
return false;
} }
return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command); return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command);
@ -1882,25 +1932,23 @@ size_t cPluginManager::GetNumLoadedPlugins(void) const
AStringVector cPluginManager::GetFoldersToLoad(cIniFile & a_SettingsIni) AStringVector cPluginManager::GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings)
{ {
// Check if the Plugins section exists. // Check if the Plugins section exists.
int KeyNum = a_SettingsIni.FindKey("Plugins"); if (a_Settings.KeyExists("Plugins"))
if (KeyNum == -1)
{ {
InsertDefaultPlugins(a_SettingsIni); InsertDefaultPlugins(a_Settings);
KeyNum = a_SettingsIni.FindKey("Plugins");
} }
// Get the list of plugins to load: // Get the list of plugins to load:
AStringVector res; AStringVector res;
int NumPlugins = a_SettingsIni.GetNumValues(KeyNum); auto Values = a_Settings.GetValues("Plugins");
for (int i = 0; i < NumPlugins; i++) for (auto NameValue : Values)
{ {
AString ValueName = a_SettingsIni.GetValueName(KeyNum, i); AString ValueName = NameValue.first;
if (ValueName.compare("Plugin") == 0) if (ValueName.compare("Plugin") == 0)
{ {
AString PluginFile = a_SettingsIni.GetValue(KeyNum, i); AString PluginFile = NameValue.second;
if (!PluginFile.empty()) if (!PluginFile.empty())
{ {
res.push_back(PluginFile); res.push_back(PluginFile);

View File

@ -24,6 +24,7 @@ class cPlayer;
class cPlugin; class cPlugin;
class cProjectileEntity; class cProjectileEntity;
class cWorld; class cWorld;
class cSettingsRepositoryInterface;
struct TakeDamageInfo; struct TakeDamageInfo;
typedef SharedPtr<cPlugin> cPluginPtr; typedef SharedPtr<cPlugin> cPluginPtr;
@ -85,6 +86,8 @@ public:
HOOK_DISCONNECT, HOOK_DISCONNECT,
HOOK_PLAYER_ANIMATION, HOOK_PLAYER_ANIMATION,
HOOK_ENTITY_ADD_EFFECT, HOOK_ENTITY_ADD_EFFECT,
HOOK_ENTITY_CHANGE_WORLD,
HOOK_ENTITY_CHANGED_WORLD,
HOOK_EXECUTE_COMMAND, HOOK_EXECUTE_COMMAND,
HOOK_EXPLODED, HOOK_EXPLODED,
HOOK_EXPLODING, HOOK_EXPLODING,
@ -200,7 +203,9 @@ public:
bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason); bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason);
bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier); bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier);
bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition); bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition);
bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd bool CallHookEntityChangeWorld (cEntity & a_Entity, cWorld & a_World);
bool CallHookEntityChangedWorld (cEntity & a_Entity, cWorld & a_World);
bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result); // If a_Player == nullptr, it is a console cmd
bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
bool CallHookHandshake (cClientHandle & a_ClientHandle, const AString & a_Username); bool CallHookHandshake (cClientHandle & a_ClientHandle, const AString & a_Username);
@ -299,7 +304,9 @@ public:
/** Returns true if the console command is in the command map */ /** Returns true if the console command is in the command map */
bool IsConsoleCommandBound(const AString & a_Command); // tolua_export bool IsConsoleCommandBound(const AString & a_Command); // tolua_export
/** Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback */ /** Executes the command split into a_Split, as if it was given on the console.
Returns true if executed. Output is sent to the a_Output callback
Exported in ManualBindings.cpp with a different signature. */
bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command); bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command);
/** Appends all commands beginning with a_Text (case-insensitive) into a_Results. /** Appends all commands beginning with a_Text (case-insensitive) into a_Results.
@ -362,20 +369,20 @@ private:
/** Reloads all plugins, defaulting to settings.ini for settings location */ /** Reloads all plugins, defaulting to settings.ini for settings location */
void ReloadPluginsNow(void); void ReloadPluginsNow(void);
/** Reloads all plugins with a cIniFile object expected to be initialised to settings.ini */ /** Reloads all plugins with a settings repo expected to be initialised to settings.ini */
void ReloadPluginsNow(cIniFile & a_SettingsIni); void ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings);
/** Unloads all plugins */ /** Unloads all plugins */
void UnloadPluginsNow(void); void UnloadPluginsNow(void);
/** Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini */ /** Handles writing default plugins if 'Plugins' key not found using a settings repo expected to be intialised to settings.ini */
void InsertDefaultPlugins(cIniFile & a_SettingsIni); void InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings);
/** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns crExecuted if the command is executed. */ /** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns crExecuted if the command is executed. */
CommandResult HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions); CommandResult HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions);
/** Returns the folders that are specified in the settings ini to load plugins from. */ /** Returns the folders that are specified in the settings ini to load plugins from. */
AStringVector GetFoldersToLoad(cIniFile & a_SettingsIni); AStringVector GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings);
} ; // tolua_export } ; // tolua_export

View File

@ -1,518 +0,0 @@
-- flags
local disable_virtual_hooks = true
local enable_pure_virtual = true
local default_private_access = false
local access = {public = 0, protected = 1, private = 2}
function preparse_hook(p)
if default_private_access then
-- we need to make all structs 'public' by default
p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n")
end
end
function parser_hook(s)
local container = classContainer.curr -- get the current container
if default_private_access then
if not container.curr_member_access and container.classtype == 'class' then
-- default access for classes is private
container.curr_member_access = access.private
end
end
-- try labels (public, private, etc)
do
local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
if b then
-- found a label, get the new access value from the global 'access' table
if access[label] then
container.curr_member_access = access[label]
end -- else ?
return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:]
end
end
local ret = nil
if disable_virtual_hooks then
return ret
end
local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())")
local const
if b then
local ret = string.sub(s, e+1)
if string.find(ret, "^%s*const") then
const = "const"
ret = string.gsub(ret, "^%s*const", "")
end
local purev = false
if string.find(ret, "^%s*=%s*0") then
purev = true
ret = string.gsub(ret, "^%s*=%s*0", "")
end
ret = string.gsub(ret, "^%s*%b{}", "")
local func = Function(decl, arg, const)
func.pure_virtual = purev
--func.access = access
func.original_sig = decl
local curflags = classContainer.curr.flags
if not curflags.virtual_class then
curflags.virtual_class = VirtualClass()
end
curflags.virtual_class:add(func)
curflags.pure_virtual = curflags.pure_virtual or purev
return ret
end
return ret
end
-- class VirtualClass
classVirtualClass = {
classtype = 'class',
name = '',
base = '',
type = '',
btype = '',
ctype = '',
}
classVirtualClass.__index = classVirtualClass
setmetatable(classVirtualClass,classClass)
function classVirtualClass:add(f)
local parent = classContainer.curr
pop()
table.insert(self.methods, {f=f})
local name,sig
-- doble negative means positive
if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then
name = self.original_name
elseif f.name == 'delete' then
name = '~'..self.original_name
else
if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then
name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name
end
end
if name then
sig = name..self:get_arg_list(f, true)..";\n"
push(self)
sig = preprocess(sig)
self:parse(sig)
pop()
end
push(parent)
end
function preprocess(sig)
sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*'
sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*'
return sig
end
function classVirtualClass:get_arg_list(f, decl)
local ret = ""
local sep = ""
local i=1
while f.args[i] do
local arg = f.args[i]
if decl then
local ptr
if arg.ret ~= '' then
ptr = arg.ret
else
ptr = arg.ptr
end
local def = ""
if arg.def and arg.def ~= "" then
def = " = "..arg.def
end
ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def
else
ret = ret..sep..arg.name
end
sep = ","
i = i+1
end
return "("..ret..")"
end
function classVirtualClass:add_parent_virtual_methods(parent)
parent = parent or _global_classes[self.flags.parent_object.btype]
if not parent then return end
if parent.flags.virtual_class then
local vclass = parent.flags.virtual_class
for k,v in ipairs(vclass.methods) do
if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then
table.insert(self.methods, {f=v.f})
end
end
end
parent = _global_classes[parent.btype]
if parent then
self:add_parent_virtual_methods(parent)
end
end
function classVirtualClass:has_method(f)
for k,v in pairs(self.methods) do
-- just match name for now
if v.f.name == f.name then
return true
end
end
return false
end
function classVirtualClass:add_constructors()
local i=1
while self.flags.parent_object[i] do
local v = self.flags.parent_object[i]
if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then
self:add(v)
end
i = i+1
end
end
--[[
function classVirtualClass:requirecollection(t)
self:add_constructors()
local req = classClass.requirecollection(self, t)
if req then
output('class ',self.name,";")
end
return req
end
--]]
function classVirtualClass:supcode()
-- pure virtual classes can have no default constructors on gcc 4
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n')
end
local ns
if self.prox.classtype == 'namespace' then
output('namespace ',self.prox.name, " {")
ns = true
end
output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {")
output("public:\n")
self:add_parent_virtual_methods()
self:output_methods(self.btype)
self:output_parent_methods()
self:add_constructors()
-- no constructor for pure virtual classes
if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then
self:output_constructors()
end
output("};\n\n")
if ns then
output("};")
end
classClass.supcode(self)
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
output('#endif // __GNUC__ >= 4\n')
end
-- output collector for custom class if required
if self:requirecollection(_collect) and _collect[self.type] then
output('\n')
output('/* function to release collected object via destructor */')
output('#ifdef __cplusplus\n')
--for i,v in pairs(collect) do
i,v = self.type, _collect[self.type]
output('\nstatic int '..v..' (lua_State* tolua_S)')
output('{')
output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);')
output(' delete self;')
output(' return 0;')
output('}')
--end
output('#endif\n\n')
end
end
function classVirtualClass:register(pre)
-- pure virtual classes can have no default constructors on gcc 4
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n')
end
classClass.register(self, pre)
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
output('#endif // __GNUC__ >= 4\n')
end
end
--function classVirtualClass:requirecollection(_c)
-- if self.flags.parent_object.flags.pure_virtual then
-- return false
-- end
-- return classClass.requirecollection(self, _c)
--end
function classVirtualClass:output_parent_methods()
for k,v in ipairs(self.methods) do
if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then
local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." "
local parent_name = rettype..self.btype.."__"..v.f.name
local par_list = self:get_arg_list(v.f, true)
local var_list = self:get_arg_list(v.f, false)
-- the parent's virtual function
output("\t"..parent_name..par_list.." {")
output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";")
output("\t};")
end
end
end
function classVirtualClass:output_methods(btype)
for k,v in ipairs(self.methods) do
if v.f.name ~= 'new' and v.f.name ~= 'delete' then
self:output_method(v.f, btype)
end
end
output("\n")
end
function classVirtualClass:output_constructors()
for k,v in ipairs(self.methods) do
if v.f.name == 'new' then
local par_list = self:get_arg_list(v.f, true)
local var_list = self:get_arg_list(v.f, false)
output("\t",self.original_name,par_list,":",self.btype,var_list,"{};")
end
end
end
function classVirtualClass:output_method(f, btype)
if f.access == 2 then -- private
return
end
local ptr
if f.ret ~= '' then
ptr = f.ret
else
ptr = f.ptr
end
local rettype = f.mod.." "..f.type..f.ptr.." "
local par_list = self:get_arg_list(f, true)
local var_list = self:get_arg_list(f, false)
if string.find(rettype, "%s*LuaQtGenericFlags%s*") then
_,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+")
end
-- the caller of the lua method
output("\t"..rettype.." "..f.name..par_list..f.const.." {")
local fn = f.cname
if f.access == 1 then
fn = "NULL"
end
output('\t\tif (push_method("',f.lname,'", ',fn,')) {')
--if f.type ~= 'void' then
-- output("\t\t\tint top = lua_gettop(lua_state)-1;")
--end
-- push the parameters
local argn = 0
for i,arg in ipairs(f.args) do
if arg.type ~= 'void' then
local t,ct = isbasic(arg.type)
if t and t ~= '' then
if arg.ret == "*" then
t = 'userdata'
ct = 'void*'
end
output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");");
else
local m = arg.ptr
if m and m~= "" then
if m == "*" then m = "" end
output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");")
else
output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n")
output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n')
end
end
argn = argn+1
end
end
-- call the function
output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ")
-- return value
if f.type ~= 'void' then
output("1);")
local t,ct = isbasic(f.type)
if t and t ~= '' then
--output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);")
output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);")
else
local mod = ""
if f.ptr ~= "*" then
mod = "*("..f.type.."*)"
end
--output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);")
output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);")
end
output("\t\t\tlua_pop(lua_state, 1);")
output("\t\t\treturn tolua_ret;")
else
output("0);")
end
-- handle non-implemeted function
output("\t\t} else {")
if f.pure_virtual then
output('\t\t\tif (lua_state)')
--output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);')
output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");')
output('\t\t\telse {')
output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");')
output('\t\t\t\t::abort();')
output('\t\t\t};')
if( rettype == " std::string " ) then
output('\t\t\treturn "";')
else
output('\t\t\treturn (',rettype,')0;')
end
else
output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';')
end
output("\t\t};")
output("\t};")
end
function VirtualClass()
local parent = classContainer.curr
pop()
local name = "Lua__"..parent.original_name
local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil})
setmetatable(c, classVirtualClass)
local ft = getnamespace(c.parent)..c.original_name
append_global_type(ft, c)
push(parent)
c.flags.parent_object = parent
c.methods = {}
push(c)
c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n")
pop()
return c
end
function post_output_hook()
print("Bindings have been generated.")
end

View File

@ -430,7 +430,7 @@ bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
} }
// Try move from the output slot: // Try move from the output slot:
if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput, true)) if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput))
{ {
cItem NewOutput(Furnace->GetOutputSlot()); cItem NewOutput(Furnace->GetOutputSlot());
Furnace->SetOutputSlot(NewOutput.AddCount(-1)); Furnace->SetOutputSlot(NewOutput.AddCount(-1));
@ -440,7 +440,7 @@ bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
// No output moved, check if we can move an empty bucket out of the fuel slot: // No output moved, check if we can move an empty bucket out of the fuel slot:
if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET) if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET)
{ {
if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel, true)) if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel))
{ {
Furnace->SetFuelSlot(cItem()); Furnace->SetFuelSlot(cItem());
return true; return true;
@ -460,28 +460,13 @@ bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity)
cItemGrid & Grid = a_Entity.GetContents(); cItemGrid & Grid = a_Entity.GetContents();
int NumSlots = Grid.GetNumSlots(); int NumSlots = Grid.GetNumSlots();
// First try adding items of types already in the hopper:
for (int i = 0; i < NumSlots; i++) for (int i = 0; i < NumSlots; i++)
{ {
if (Grid.IsSlotEmpty(i)) if (Grid.IsSlotEmpty(i))
{ {
continue; continue;
} }
if (MoveItemsFromSlot(a_Entity, i, false)) if (MoveItemsFromSlot(a_Entity, i))
{
Grid.ChangeSlotCount(i, -1);
return true;
}
}
// No already existing stack can be topped up, try again with allowing new stacks:
for (int i = 0; i < NumSlots; i++)
{
if (Grid.IsSlotEmpty(i))
{
continue;
}
if (MoveItemsFromSlot(a_Entity, i, true))
{ {
Grid.ChangeSlotCount(i, -1); Grid.ChangeSlotCount(i, -1);
return true; return true;
@ -495,21 +480,19 @@ bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity)
/// Moves one piece of the specified a_Entity's slot itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. /// Moves one piece of the specified a_Entity's slot itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum, bool a_AllowNewStacks) bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum)
{ {
cItem One(a_Entity.GetSlot(a_SlotNum).CopyOne()); cItem One(a_Entity.GetSlot(a_SlotNum).CopyOne());
for (int i = 0; i < ContentsWidth * ContentsHeight; i++) for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
{ {
if (m_Contents.IsSlotEmpty(i)) if (m_Contents.IsSlotEmpty(i))
{ {
if (a_AllowNewStacks) if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum))
{ {
if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum)) // Plugin disagrees with the move
{ continue;
// Plugin disagrees with the move
continue;
}
} }
m_Contents.SetSlot(i, One); m_Contents.SetSlot(i, One);
return true; return true;
} }
@ -521,8 +504,14 @@ bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_Sl
continue; continue;
} }
auto PreviousCount = m_Contents.GetSlot(i).m_ItemCount;
m_Contents.ChangeSlotCount(i, 1); m_Contents.ChangeSlotCount(i, 1);
return true;
if (PreviousCount == m_Contents.GetSlot(i).m_ItemCount + 1)
{
// Successfully added a new item. (Failure condition consistutes: stack full)
return true;
}
} }
} }
return false; return false;

View File

@ -74,7 +74,7 @@ protected:
bool MoveItemsFromGrid(cBlockEntityWithItems & a_Entity); bool MoveItemsFromGrid(cBlockEntityWithItems & a_Entity);
/// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. /// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
bool MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SrcSlotNum, bool a_AllowNewStacks); bool MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SrcSlotNum);
/// Moves items to the chest at the specified coords. Returns true if contents have changed /// Moves items to the chest at the specified coords. Returns true if contents have changed
bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ); bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ);

View File

@ -1,7 +1,7 @@
// MobHeadEntity.cpp // MobHeadEntity.cpp
// Implements the cMobHeadEntity class representing a single skull/head in the world // Implements the cMobHeadEntity class representing a single skull / head in the world
#include "Globals.h" #include "Globals.h"
#include "MobHeadEntity.h" #include "MobHeadEntity.h"

View File

@ -1,6 +1,6 @@
// MobHeadEntity.h // MobHeadEntity.h
// Declares the cMobHeadEntity class representing a single skull/head in the world // Declares the cMobHeadEntity class representing a single skull / head in the world

View File

@ -14,7 +14,7 @@ void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInt
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ); Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ);
Vector3i Direction = MetaDataToDirection( OldMeta & 0x7); Vector3i Direction = MetaDataToDirection( OldMeta & 0x3);
if (OldMeta & 0x8) if (OldMeta & 0x8)
{ {
// Was pillow // Was pillow
@ -111,7 +111,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
// Is foot end // Is foot end
VERIFY((Meta & 0x4) != 0x4); // Occupied flag should never be set, else our compilator (intended) is broken VERIFY((Meta & 0x4) != 0x4); // Occupied flag should never be set, else our compilator (intended) is broken
PillowDirection = MetaDataToDirection(Meta & 0x7); PillowDirection = MetaDataToDirection(Meta & 0x3);
if (a_ChunkInterface.GetBlock(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z) == E_BLOCK_BED) // Must always use pillow location for sleeping if (a_ChunkInterface.GetBlock(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z) == E_BLOCK_BED) // Must always use pillow location for sleeping
{ {
a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z); a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z);

View File

@ -22,7 +22,7 @@ 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 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
{ {
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
Meta ^= 0x04; // Toggle 3rd (addition/subtraction) bit with XOR Meta ^= 0x04; // Toggle 3rd (addition / subtraction) bit with XOR
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
} }

View File

@ -134,7 +134,7 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta)
return a_Meta; return a_Meta;
} }
// Holds open/closed meta data. 0x0C == 1100. // Holds open / closed meta data. 0x0C == 1100.
NIBBLETYPE OtherMeta = a_Meta & 0x0C; NIBBLETYPE OtherMeta = a_Meta & 0x0C;
// Mirrors according to a table. 0x03 == 0011. // Mirrors according to a table. 0x03 == 0011.
@ -152,7 +152,7 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta)
NIBBLETYPE cBlockDoorHandler::MetaMirrorYZ(NIBBLETYPE a_Meta) NIBBLETYPE cBlockDoorHandler::MetaMirrorYZ(NIBBLETYPE a_Meta)
{ {
// Top bit (0x08) contains door panel type (Top/Bottom panel) Only Bottom panels contain position data // Top bit (0x08) contains door panel type (Top / Bottom panel) Only Bottom panels contain position data
// Return a_Meta if panel is a top panel (0x08 bit is set to 1) // Return a_Meta if panel is a top panel (0x08 bit is set to 1)
// Note: Currently, you can not properly mirror the hinges on a double door. The orientation of the door is stored // Note: Currently, you can not properly mirror the hinges on a double door. The orientation of the door is stored
@ -165,7 +165,7 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorYZ(NIBBLETYPE a_Meta)
return a_Meta; return a_Meta;
} }
// Holds open/closed meta data. 0x0C == 1100. // Holds open / closed meta data. 0x0C == 1100.
NIBBLETYPE OtherMeta = a_Meta & 0x0C; NIBBLETYPE OtherMeta = a_Meta & 0x0C;
// Mirrors according to a table. 0x03 == 0011. // Mirrors according to a table. 0x03 == 0011.

View File

@ -106,7 +106,7 @@ public:
void FindAndSetPortalFrame(int X, int Y, int Z, cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface) void FindAndSetPortalFrame(int X, int Y, int Z, cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface)
{ {
int MaxY = FindObsidianCeiling(X, Y, Z, a_ChunkInterface); // Get topmost obsidian block as reference for all other checks int MaxY = FindObsidianCeiling(X, Y, Z, a_ChunkInterface); // Get topmost obsidian block as reference for all other checks
int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add/subtract one as we've checked the original already the line above int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add / subtract one as we've checked the original already the line above
if (MaxY == 0) // Oh noes! Not a portal coordinate :( if (MaxY == 0) // Oh noes! Not a portal coordinate :(
{ {
@ -139,7 +139,7 @@ public:
return; return;
} }
/** Evaluates if coordinates are a portal going XP/XM; returns true if so, and writes boundaries to variable /** Evaluates if coordinates are a portal going XP / XM; returns true if so, and writes boundaries to variable
Takes coordinates of base block and Y coord of target obsidian ceiling */ Takes coordinates of base block and Y coord of target obsidian ceiling */
bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cChunkInterface & a_ChunkInterface) bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cChunkInterface & a_ChunkInterface)
{ {
@ -179,7 +179,7 @@ public:
return (FoundFrameXP && FoundFrameXM); return (FoundFrameXP && FoundFrameXM);
} }
/// Evaluates if coords are a portal going ZP/ZM; returns true if so, and writes boundaries to variable /// Evaluates if coords are a portal going ZP / ZM; returns true if so, and writes boundaries to variable
bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cChunkInterface & a_ChunkInterface) bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cChunkInterface & a_ChunkInterface)
{ {
Dir = 2; Dir = 2;

View File

@ -40,29 +40,41 @@ public:
{ {
cFastRandom rand; cFastRandom rand;
// Old leaves - 3 bits contain display; new leaves - 1st bit, shifted left two for saplings to understand // There is a chance to drop a sapling that varies depending on the type of leaf broken.
if (rand.NextInt(6) == 0) // TODO: Take into account fortune for sapling drops.
int chance;
if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE))
{
// Jungle leaves have a 2.5% chance of dropping a sapling.
chance = rand.NextInt(40);
}
else
{
// Other leaves have a 5% chance of dropping a sapling.
chance = rand.NextInt(20);
}
if (chance == 0)
{ {
a_Pickups.push_back( a_Pickups.push_back(
cItem( cItem(
E_BLOCK_SAPLING, E_BLOCK_SAPLING,
1, 1,
(m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (2 << (a_BlockMeta & 0x01)) (m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (4 + (a_BlockMeta & 0x01))
) )
); );
} }
// 1 % chance of dropping an apple, if the leaves' type is Apple Leaves // 0.5 % chance of dropping an apple, if the leaves' type is Apple Leaves
if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE)) if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE))
{ {
if (rand.NextInt(101) == 0) if (rand.NextInt(200) == 0)
{ {
a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
} }
} }
} }
virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
{ {
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);

View File

@ -19,7 +19,7 @@ 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 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
{ {
// Flip the ON bit on/off using the XOR bitwise operation // Flip the ON bit on / off using the XOR bitwise operation
NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08); NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);

View File

@ -108,7 +108,7 @@ private:
case E_BLOCK_ENCHANTMENT_TABLE: case E_BLOCK_ENCHANTMENT_TABLE:
case E_BLOCK_END_PORTAL: case E_BLOCK_END_PORTAL:
case E_BLOCK_END_PORTAL_FRAME: case E_BLOCK_END_PORTAL_FRAME:
// Notice the lack of an E_BLOCK_ENDER_CHEST here; its because ender chests can totally be pushed/pulled in MCS :) // Notice the lack of an E_BLOCK_ENDER_CHEST here; its because ender chests can totally be pushed / pulled in MCS :)
case E_BLOCK_FURNACE: case E_BLOCK_FURNACE:
case E_BLOCK_LIT_FURNACE: case E_BLOCK_LIT_FURNACE:
case E_BLOCK_INVERTED_DAYLIGHT_SENSOR: case E_BLOCK_INVERTED_DAYLIGHT_SENSOR:

View File

@ -502,11 +502,11 @@ public:
// Save powered rail flag. // Save powered rail flag.
NIBBLETYPE OtherMeta = a_Meta & 0x08; NIBBLETYPE OtherMeta = a_Meta & 0x08;
// Rotates according to table; 0x07 == 0111. // Rotates according to table; 0x07 == 0111.
// Rails can either be flat (North/South) or Ascending (Asc. East) // Rails can either be flat (North / South) or Ascending (Asc. East)
switch (a_Meta & 0x07) switch (a_Meta & 0x07)
{ {
case 0x00: return 0x01 + OtherMeta; // North/South -> East/West case 0x00: return 0x01 + OtherMeta; // North / South -> East / West
case 0x01: return 0x00 + OtherMeta; // East/West -> North/South case 0x01: return 0x00 + OtherMeta; // East / West -> North / South
case 0x02: return 0x04 + OtherMeta; // Asc. East -> Asc. North case 0x02: return 0x04 + OtherMeta; // Asc. East -> Asc. North
case 0x04: return 0x03 + OtherMeta; // Asc. North -> Asc. West case 0x04: return 0x03 + OtherMeta; // Asc. North -> Asc. West
@ -538,11 +538,11 @@ public:
// Save powered rail flag. // Save powered rail flag.
NIBBLETYPE OtherMeta = a_Meta & 0x08; NIBBLETYPE OtherMeta = a_Meta & 0x08;
// Rotates according to table; 0x07 == 0111. // Rotates according to table; 0x07 == 0111.
// Rails can either be flat (North/South) or Ascending (Asc. East) // Rails can either be flat (North / South) or Ascending (Asc. East)
switch (a_Meta & 0x07) switch (a_Meta & 0x07)
{ {
case 0x00: return 0x01 + OtherMeta; // North/South -> East/West case 0x00: return 0x01 + OtherMeta; // North / South -> East / West
case 0x01: return 0x00 + OtherMeta; // East/West -> North/South case 0x01: return 0x00 + OtherMeta; // East / West -> North / South
case 0x02: return 0x05 + OtherMeta; // Asc. East -> Asc. South case 0x02: return 0x05 + OtherMeta; // Asc. East -> Asc. South
case 0x05: return 0x03 + OtherMeta; // Asc. South -> Asc. West case 0x05: return 0x03 + OtherMeta; // Asc. South -> Asc. West
@ -574,7 +574,7 @@ public:
// Save powered rail flag. // Save powered rail flag.
NIBBLETYPE OtherMeta = a_Meta & 0x08; NIBBLETYPE OtherMeta = a_Meta & 0x08;
// Mirrors according to table; 0x07 == 0111. // Mirrors according to table; 0x07 == 0111.
// Rails can either be flat (North/South) or Ascending (Asc. East) // Rails can either be flat (North / South) or Ascending (Asc. East)
switch (a_Meta & 0x07) switch (a_Meta & 0x07)
{ {
case 0x05: return 0x04 + OtherMeta; // Asc. South -> Asc. North case 0x05: return 0x04 + OtherMeta; // Asc. South -> Asc. North
@ -605,7 +605,7 @@ public:
// Save powered rail flag. // Save powered rail flag.
NIBBLETYPE OtherMeta = a_Meta & 0x08; NIBBLETYPE OtherMeta = a_Meta & 0x08;
// Mirrors according to table; 0x07 == 0111. // Mirrors according to table; 0x07 == 0111.
// Rails can either be flat (North/South) or Ascending (Asc. East) // Rails can either be flat (North / South) or Ascending (Asc. East)
switch (a_Meta & 0x07) switch (a_Meta & 0x07)
{ {
case 0x02: return 0x03 + OtherMeta; // Asc. East -> Asc. West case 0x02: return 0x03 + OtherMeta; // Asc. East -> Asc. West

View File

@ -35,7 +35,7 @@ public:
return; return;
} }
// Flip the ON bit on/off using the XOR bitwise operation // Flip the ON bit on / off using the XOR bitwise operation
NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x04); NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x04);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);

View File

@ -57,10 +57,10 @@ public:
/// Returns true if a boundingbox specified by a_Min and a_Max is inside this bounding box /// Returns true if a boundingbox specified by a_Min and a_Max is inside this bounding box
bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max); bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max);
/// Returns true if the specified point is inside the bounding box specified by its min/max corners /// Returns true if the specified point is inside the bounding box specified by its min / max corners
static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point); static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point);
/// Returns true if the specified point is inside the bounding box specified by its min/max corners /// Returns true if the specified point is inside the bounding box specified by its min / max corners
static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z); static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z);
/** Returns true if this bounding box is intersected by the line specified by its two points /** Returns true if this bounding box is intersected by the line specified by its two points

47
src/Broadcaster.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "Globals.h"
#include "Broadcaster.h"
#include "World.h"
#include "Chunk.h"
cBroadcaster::cBroadcaster(cWorld * a_World) :
m_World(a_World)
{
}
void cBroadcaster::BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude)
{
m_World->DoWithChunkAt(a_Src,
[=](cChunk & a_Chunk) -> bool
{
for (auto && client : a_Chunk.GetAllClients())
{
if (client == a_Exclude)
{
continue;
}
client->SendParticleEffect(a_ParticleName, a_Src.x, a_Src.y, a_Src.z, a_Offset.x, a_Offset.y, a_Offset.z, a_ParticleData, a_ParticleAmount);
};
return true;
});
}
void cBroadcaster::BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data, cClientHandle * a_Exclude)
{
m_World->DoWithChunkAt(a_Src,
[=](cChunk & a_Chunk) -> bool
{
for (auto && client : a_Chunk.GetAllClients())
{
if (client == a_Exclude)
{
continue;
}
client->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data);
};
return true;
});
}

20
src/Broadcaster.h Normal file
View File

@ -0,0 +1,20 @@
class cWorld;
#include <array>
class cBroadcaster
{
public:
cBroadcaster(cWorld * a_World);
void BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude = nullptr);
void BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data, cClientHandle * a_Exclude = nullptr);
private:
cWorld * m_World;
};

View File

@ -18,6 +18,7 @@ SET (SRCS
BlockArea.cpp BlockArea.cpp
BlockID.cpp BlockID.cpp
BlockInfo.cpp BlockInfo.cpp
Broadcaster.cpp
BoundingBox.cpp BoundingBox.cpp
ByteBuffer.cpp ByteBuffer.cpp
ChatColor.cpp ChatColor.cpp
@ -47,11 +48,13 @@ SET (SRCS
Logger.cpp Logger.cpp
Map.cpp Map.cpp
MapManager.cpp MapManager.cpp
MemorySettingsRepository.cpp
MobCensus.cpp MobCensus.cpp
MobFamilyCollecter.cpp MobFamilyCollecter.cpp
MobProximityCounter.cpp MobProximityCounter.cpp
MobSpawner.cpp MobSpawner.cpp
MonsterConfig.cpp MonsterConfig.cpp
OverridesSettingsRepository.cpp
ProbabDistrib.cpp ProbabDistrib.cpp
RankManager.cpp RankManager.cpp
RCONServer.cpp RCONServer.cpp
@ -77,6 +80,7 @@ SET (HDRS
BlockInServerPluginInterface.h BlockInServerPluginInterface.h
BlockInfo.h BlockInfo.h
BlockTracer.h BlockTracer.h
Broadcaster.h
BoundingBox.h BoundingBox.h
BuildInfo.h.cmake BuildInfo.h.cmake
ByteBuffer.h ByteBuffer.h
@ -114,11 +118,13 @@ SET (HDRS
Map.h Map.h
MapManager.h MapManager.h
Matrix4.h Matrix4.h
MemorySettingsRepository.h
MobCensus.h MobCensus.h
MobFamilyCollecter.h MobFamilyCollecter.h
MobProximityCounter.h MobProximityCounter.h
MobSpawner.h MobSpawner.h
MonsterConfig.h MonsterConfig.h
OverridesSettingsRepository.h
ProbabDistrib.h ProbabDistrib.h
RankManager.h RankManager.h
RCONServer.h RCONServer.h
@ -126,6 +132,7 @@ SET (HDRS
Scoreboard.h Scoreboard.h
Server.h Server.h
SetChunkData.h SetChunkData.h
SettingsRepositoryInterface.h
Statistics.h Statistics.h
StringCompression.h StringCompression.h
StringUtils.h StringUtils.h
@ -140,6 +147,7 @@ SET (HDRS
include_directories(".") include_directories(".")
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite") include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite")
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/SQLiteCpp/include") include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/SQLiteCpp/include")
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/TCLAP/include")
configure_file("BuildInfo.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.h") configure_file("BuildInfo.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.h")
@ -276,7 +284,7 @@ if (MSVC)
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/MCServer/lua51.dll ./lua51.dll COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/MCServer/lua51.dll ./lua51.dll
# Regenerate bindings: # Regenerate bindings:
COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg COMMAND tolua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
# add any new generation dependencies here # add any new generation dependencies here

View File

@ -43,6 +43,7 @@ local g_IgnoredFiles =
{ {
"Bindings/Bindings.h", "Bindings/Bindings.h",
"Bindings/Bindings.cpp", "Bindings/Bindings.cpp",
"Bindings/LuaState_Implementation.cpp",
"LeakFinder.cpp", "LeakFinder.cpp",
"LeakFinder.h", "LeakFinder.h",
"MersenneTwister.h", "MersenneTwister.h",
@ -149,6 +150,39 @@ local g_ViolationPatterns =
-- No space before a closing parenthesis: -- No space before a closing parenthesis:
{" %)", "Remove the space before \")\""}, {" %)", "Remove the space before \")\""},
-- Check spaces around "+":
{"^[a-zA-Z0-9]+%+[a-zA-Z0-9]+", "Add space around +"},
{"[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%+[a-zA-Z0-9]+", "Add space around +"},
--[[
-- Cannot check these because of text such as "X+" and "+2" appearing in some comments.
{"^[a-zA-Z0-9]+ %+[a-zA-Z0-9]+", "Add space after +"},
{"[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+ %+[a-zA-Z0-9]+", "Add space after +"},
{"^[a-zA-Z0-9]+%+ [a-zA-Z0-9]+", "Add space before +"},
{"[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%+ [a-zA-Z0-9]+", "Add space before +"},
--]]
-- Cannot check spaces around "-", because the minus is sometimes used as a hyphen between-words
-- Check spaces around "*":
{"^[a-zA-Z0-9]+%*[a-zA-Z0-9]+", "Add space around *"},
{"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%*[a-zA-Z0-9]+", "Add space around *"},
{"^[a-zB-Z0-9]+%* [a-zA-Z0-9]+", "Add space before *"},
{"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zB-Z0-9]+%* [a-zA-Z0-9]+", "Add space before *"},
-- Check spaces around "/":
{"^[a-zA-Z0-9]+%/[a-zA-Z0-9]+", "Add space around /"},
{"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%/[a-zA-Z0-9]+", "Add space around /"},
-- Check spaces around "&":
{"^[a-zA-Z0-9]+%&[a-zA-Z0-9]+", "Add space around /"},
{"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%&[a-zA-Z0-9]+", "Add space around /"},
{"^[a-zA-Z0-9]+%& [a-zA-Z0-9]+", "Add space before &"},
{"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%& [a-zA-Z0-9]+", "Add space before &"},
-- We don't like "Type const *" and "Type const &". Use "const Type *" and "const Type &" instead:
{"const %&", "Use 'const Type &' instead of 'Type const &'"},
{"const %*", "Use 'const Type *' instead of 'Type const *'"},
} }

View File

@ -472,26 +472,26 @@ void cChunk::Stay(bool a_Stay)
void cChunk::CollectMobCensus(cMobCensus& toFill) void cChunk::CollectMobCensus(cMobCensus & toFill)
{ {
toFill.CollectSpawnableChunk(*this); toFill.CollectSpawnableChunk(*this);
std::list<const Vector3d*> playerPositions; std::list<const Vector3d *> playerPositions;
cPlayer* currentPlayer; cPlayer * currentPlayer;
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) for (auto itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
{ {
currentPlayer = (*itr)->GetPlayer(); currentPlayer = (*itr)->GetPlayer();
playerPositions.push_back(&(currentPlayer->GetPosition())); playerPositions.push_back(&(currentPlayer->GetPosition()));
} }
Vector3d currentPosition; Vector3d currentPosition;
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) for (auto itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
{ {
// LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass()); // LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
if ((*itr)->IsMob()) if ((*itr)->IsMob())
{ {
cMonster& Monster = (cMonster&)(**itr); auto & Monster = reinterpret_cast<cMonster &>(**itr);
currentPosition = Monster.GetPosition(); currentPosition = Monster.GetPosition();
for (std::list<const Vector3d*>::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); ++itr2) for (auto itr2 = playerPositions.cbegin(); itr2 != playerPositions.cend(); ++itr2)
{ {
toFill.CollectMob(Monster, *this, (currentPosition - **itr2).SqrLength()); toFill.CollectMob(Monster, *this, (currentPosition - **itr2).SqrLength());
} }
@ -531,7 +531,7 @@ void cChunk::GetRandomBlockCoords(int & a_X, int & a_Y, int & a_Z)
void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) void cChunk::SpawnMobs(cMobSpawner & a_MobSpawner)
{ {
int CenterX, CenterY, CenterZ; int CenterX, CenterY, CenterZ;
GetRandomBlockCoords(CenterX, CenterY, CenterZ); GetRandomBlockCoords(CenterX, CenterY, CenterZ);
@ -737,7 +737,7 @@ void cChunk::ProcessQueuedSetBlocks(void)
{ {
if (GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ) == itr->m_PreviousType) if (GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ) == itr->m_PreviousType)
{ {
// Current world age is bigger than/equal to target world age - delay time reached AND // Current world age is bigger than / equal to target world age - delay time reached AND
// Previous block type was the same as current block type (to prevent duplication) // Previous block type was the same as current block type (to prevent duplication)
SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); // SetMeta doesn't send to client SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); // SetMeta doesn't send to client
itr = m_SetBlockQueue.erase(itr); itr = m_SetBlockQueue.erase(itr);
@ -751,7 +751,7 @@ void cChunk::ProcessQueuedSetBlocks(void)
} }
else else
{ {
// Current world age is bigger than/equal to target world age - delay time reached // Current world age is bigger than / equal to target world age - delay time reached
SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
itr = m_SetBlockQueue.erase(itr); itr = m_SetBlockQueue.erase(itr);
LOGD("Successfully set queued block - previous type ignored"); LOGD("Successfully set queued block - previous type ignored");
@ -1026,7 +1026,7 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl
case E_BLOCK_FARMLAND: case E_BLOCK_FARMLAND:
{ {
// DEBUG: This is here to catch FS #349 - melons growing over other crops. // DEBUG: This is here to catch FS #349 - melons growing over other crops.
LOG("Growing melon/pumpkin overwriting %s, growing on %s", LOG("Growing melon / pumpkin overwriting %s, growing on %s",
ItemTypeToString(BlockType[CheckType]).c_str(), ItemTypeToString(BlockType[CheckType]).c_str(),
ItemTypeToString(Soil).c_str() ItemTypeToString(Soil).c_str()
); );
@ -1827,7 +1827,7 @@ bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_
) )
{ {
MarkDirty(); MarkDirty();
(reinterpret_cast<cSignEntity *>(*itr))->SetLines(a_Line1, a_Line2, a_Line3, a_Line4); reinterpret_cast<cSignEntity *>(*itr)->SetLines(a_Line1, a_Line2, a_Line3, a_Line4);
m_World->BroadcastBlockEntity(a_PosX, a_PosY, a_PosZ); m_World->BroadcastBlockEntity(a_PosX, a_PosY, a_PosZ);
return true; return true;
} }
@ -1839,7 +1839,7 @@ bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_
void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity) void cChunk::RemoveBlockEntity(cBlockEntity * a_BlockEntity)
{ {
MarkDirty(); MarkDirty();
m_BlockEntities.remove(a_BlockEntity); m_BlockEntities.remove(a_BlockEntity);

View File

@ -155,10 +155,10 @@ public:
void Stay(bool a_Stay = true); void Stay(bool a_Stay = true);
/** Recence all mobs proximities to players in order to know what to do with them */ /** Recence all mobs proximities to players in order to know what to do with them */
void CollectMobCensus(cMobCensus& toFill); void CollectMobCensus(cMobCensus & toFill);
/** Try to Spawn Monsters inside chunk */ /** Try to Spawn Monsters inside chunk */
void SpawnMobs(cMobSpawner& a_MobSpawner); void SpawnMobs(cMobSpawner & a_MobSpawner);
void Tick(std::chrono::milliseconds a_Dt); void Tick(std::chrono::milliseconds a_Dt);
@ -439,6 +439,9 @@ public:
as at least one requests is active the chunk will be ticked). */ as at least one requests is active the chunk will be ticked). */
void SetAlwaysTicked(bool a_AlwaysTicked); void SetAlwaysTicked(bool a_AlwaysTicked);
// Makes a copy of the list
cClientHandleList GetAllClients(void) const {return m_LoadedByClient; }
private: private:
friend class cChunkMap; friend class cChunkMap;
@ -530,9 +533,6 @@ private:
/** Wakes up each simulator for its specific blocks; through all the blocks in the chunk */ /** Wakes up each simulator for its specific blocks; through all the blocks in the chunk */
void WakeUpSimulators(void); void WakeUpSimulators(void);
// Makes a copy of the list
cClientHandleList GetAllClients(void) const {return m_LoadedByClient; }
/** Sends m_PendingSendBlocks to all clients */ /** Sends m_PendingSendBlocks to all clients */
void BroadcastPendingBlockChanges(void); void BroadcastPendingBlockChanges(void);

View File

@ -65,7 +65,7 @@ cChunkMap::~cChunkMap()
void cChunkMap::RemoveLayer( cChunkLayer* a_Layer) void cChunkMap::RemoveLayer(cChunkLayer * a_Layer)
{ {
cCSLock Lock(m_CSLayers); cCSLock Lock(m_CSLayers);
m_Layers.remove(a_Layer); m_Layers.remove(a_Layer);
@ -791,6 +791,28 @@ bool cChunkMap::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callb
} }
bool cChunkMap::DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback)
{
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockPos.x, a_BlockPos.z, ChunkX, ChunkZ);
struct cCallBackWrapper : cChunkCallback
{
cCallBackWrapper(std::function<bool(cChunk &)> a_InnerCallback) :
m_Callback(a_InnerCallback)
{
}
virtual bool Item(cChunk * a_Chunk)
{
return m_Callback(*a_Chunk);
}
private:
std::function<bool(cChunk &)> m_Callback;
} callback(a_Callback);
return DoWithChunk(ChunkX, ChunkZ, callback);
}
@ -2694,12 +2716,12 @@ void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ)
void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill) void cChunkMap::CollectMobCensus(cMobCensus & a_ToFill)
{ {
cCSLock Lock(m_CSLayers); cCSLock Lock(m_CSLayers);
for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) for (auto && layer: m_Layers)
{ {
(*itr)->CollectMobCensus(a_ToFill); layer->CollectMobCensus(a_ToFill);
} // for itr - m_Layers } // for itr - m_Layers
} }
@ -2708,12 +2730,12 @@ void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill)
void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner) void cChunkMap::SpawnMobs(cMobSpawner & a_MobSpawner)
{ {
cCSLock Lock(m_CSLayers); cCSLock Lock(m_CSLayers);
for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) for (auto && layer: m_Layers)
{ {
(*itr)->SpawnMobs(a_MobSpawner); layer->SpawnMobs(a_MobSpawner);
} // for itr - m_Layers } // for itr - m_Layers
} }
@ -2914,7 +2936,7 @@ cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ)
void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill) void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus & a_ToFill)
{ {
for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++) for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++)
{ {
@ -2933,7 +2955,7 @@ void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill)
void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner & a_MobSpawner)
{ {
for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++) for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++)
{ {

View File

@ -64,7 +64,7 @@ public:
static const int LAYER_SIZE = 32; static const int LAYER_SIZE = 32;
cChunkMap(cWorld* a_World); cChunkMap(cWorld * a_World);
~cChunkMap(); ~cChunkMap();
// Broadcast respective packets to all clients of the chunk where the event is taking place // Broadcast respective packets to all clients of the chunk where the event is taking place
@ -104,6 +104,9 @@ public:
/** Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback */ /** Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback */
bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback); bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback);
/** Calls the callback for the chunk at the block position specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback **/
bool DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback);
/** Wakes up simulators for the specified block */ /** Wakes up simulators for the specified block */
void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ); void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ);
@ -361,11 +364,11 @@ public:
/** Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call */ /** Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call */
void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ);
/** Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player */ /** Make a Mob census, of all mobs, their family, their chunk and their distance to closest player */
void CollectMobCensus(cMobCensus& a_ToFill); void CollectMobCensus(cMobCensus & a_ToFill);
/** Try to Spawn Monsters inside all Chunks */ /** Try to Spawn Monsters inside all Chunks */
void SpawnMobs(cMobSpawner& a_MobSpawner); void SpawnMobs(cMobSpawner & a_MobSpawner);
void Tick(std::chrono::milliseconds a_Dt); void Tick(std::chrono::milliseconds a_Dt);
@ -430,9 +433,10 @@ private:
void UnloadUnusedChunks(void); void UnloadUnusedChunks(void);
/** Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player */ /** Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player */
void CollectMobCensus(cMobCensus& a_ToFill); void CollectMobCensus(cMobCensus & a_ToFill);
/** Try to Spawn Monsters inside all Chunks */ /** Try to Spawn Monsters inside all Chunks */
void SpawnMobs(cMobSpawner& a_MobSpawner); void SpawnMobs(cMobSpawner & a_MobSpawner);
void Tick(std::chrono::milliseconds a_Dt); void Tick(std::chrono::milliseconds a_Dt);

View File

@ -13,7 +13,7 @@ And once they do, it requests the chunk data and sends it all away, either
sends to a specific client (QueueSendChunkTo) sends to a specific client (QueueSendChunkTo)
Chunk data is queried using the cChunkDataCallback interface. Chunk data is queried using the cChunkDataCallback interface.
It is cached inside the ChunkSender object during the query and then processed after the query ends. It is cached inside the ChunkSender object during the query and then processed after the query ends.
Note that the data needs to be compressed only *after* the query finishes, Note that the data needs to be compressed only after the query finishes,
because the query callbacks run with ChunkMap's CS locked. because the query callbacks run with ChunkMap's CS locked.
A client may remove itself from all direct requests(QueueSendChunkTo()) by calling RemoveClient(); A client may remove itself from all direct requests(QueueSendChunkTo()) by calling RemoveClient();

View File

@ -436,7 +436,7 @@ bool cClientHandle::StreamNextChunk(void)
{ {
Vector3d Vector = Position + LookVector * cChunkDef::Width * Range; Vector3d Vector = Position + LookVector * cChunkDef::Width * Range;
// Get the chunk from the x/z coords. // Get the chunk from the x / z coords.
int RangeX, RangeZ = 0; int RangeX, RangeZ = 0;
cChunkDef::BlockToChunk(FloorC(Vector.x), FloorC(Vector.z), RangeX, RangeZ); cChunkDef::BlockToChunk(FloorC(Vector.x), FloorC(Vector.z), RangeX, RangeZ);
@ -454,7 +454,7 @@ bool cClientHandle::StreamNextChunk(void)
continue; continue;
} }
// If the chunk already loading/loaded -> skip // If the chunk already loading / loaded -> skip
if ( if (
(std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) || (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) ||
(std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end()) (std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end())
@ -492,7 +492,7 @@ bool cClientHandle::StreamNextChunk(void)
{ {
cChunkCoords Coords = *itr; cChunkCoords Coords = *itr;
// If the chunk already loading/loaded -> skip // If the chunk already loading / loaded -> skip
if ( if (
(std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) || (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) ||
(std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end()) (std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end())
@ -1154,7 +1154,7 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
return; return;
} }
// Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig/aim bug in the client: // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig / aim bug in the client:
m_HasStartedDigging = true; m_HasStartedDigging = true;
m_LastDigBlockX = a_BlockX; m_LastDigBlockX = a_BlockX;
m_LastDigBlockY = a_BlockY; m_LastDigBlockY = a_BlockY;
@ -1201,7 +1201,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
(m_LastDigBlockZ != a_BlockZ) (m_LastDigBlockZ != a_BlockZ)
) )
{ {
LOGD("Prevented a dig/aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)", LOGD("Prevented a dig / aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)",
a_BlockX, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ,
m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ, m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ,
(m_HasStartedDigging ? "True" : "False") (m_HasStartedDigging ? "True" : "False")
@ -1359,7 +1359,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
if (!CheckBlockInteractionsRate()) if (!CheckBlockInteractionsRate())
{ {
Kick("Too many blocks were placed/interacted with per unit time - hacked client?"); Kick("Too many blocks were placed / interacted with per unit time - hacked client?");
return; return;
} }
@ -1473,7 +1473,7 @@ void cClientHandle::HandleChat(const AString & a_Message)
Color.clear(); Color.clear();
} }
Msg.AddTextPart(AString("<") + m_Player->GetName() + "> ", Color); Msg.AddTextPart(AString("<") + m_Player->GetName() + "> ", Color);
Msg.ParseText(a_Message); Msg.ParseText(Message);
Msg.UnderlineUrls(); Msg.UnderlineUrls();
m_Player->GetWorld()->BroadcastChat(Msg); m_Player->GetWorld()->BroadcastChat(Msg);
} }
@ -2374,6 +2374,15 @@ void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_S
void cClientHandle::SendParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data)
{
m_Protocol->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data);
}
void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup) void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup)
{ {
m_Protocol->SendPickupSpawn(a_Pickup); m_Protocol->SendPickupSpawn(a_Pickup);

View File

@ -22,6 +22,7 @@
#include "ChunkSender.h" #include "ChunkSender.h"
#include <array>
@ -177,6 +178,7 @@ public: // tolua_export
void SendMapInfo (int a_ID, unsigned int a_Scale); void SendMapInfo (int a_ID, unsigned int a_Scale);
void SendPaintingSpawn (const cPainting & a_Painting); void SendPaintingSpawn (const cPainting & a_Painting);
void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount); void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount);
void SendParticleEffect (const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data);
void SendPickupSpawn (const cPickup & a_Pickup); void SendPickupSpawn (const cPickup & a_Pickup);
void SendPlayerAbilities (void); void SendPlayerAbilities (void);
void SendPlayerListAddPlayer (const cPlayer & a_Player); void SendPlayerListAddPlayer (const cPlayer & a_Player);
@ -389,7 +391,7 @@ private:
cPlayer * m_Player; cPlayer * m_Player;
bool m_HasSentDC; ///< True if a D/C packet has been sent in either direction bool m_HasSentDC; ///< True if a Disconnect packet has been sent in either direction
// Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk // Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk
int m_LastStreamedChunkX; int m_LastStreamedChunkX;
@ -414,7 +416,7 @@ private:
int m_BlockDigAnimY; int m_BlockDigAnimY;
int m_BlockDigAnimZ; int m_BlockDigAnimZ;
// To avoid dig/aim bug in the client, store the last position given in a DIG_START packet and compare to that when processing the DIG_FINISH packet: // To avoid dig / aim bug in the client, store the last position given in a DIG_START packet and compare to that when processing the DIG_FINISH packet:
bool m_HasStartedDigging; bool m_HasStartedDigging;
int m_LastDigBlockX; int m_LastDigBlockX;
int m_LastDigBlockY; int m_LastDigBlockY;

View File

@ -29,29 +29,32 @@ void cCommandOutputCallback::Out(const char * a_Fmt, ...)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cLogCommandOutputCallback: // cStringAccumCommandOutputCallback:
void cLogCommandOutputCallback::Out(const AString & a_Text) void cStringAccumCommandOutputCallback::Out(const AString & a_Text)
{ {
m_Buffer.append(a_Text); m_Accum.append(a_Text);
} }
////////////////////////////////////////////////////////////////////////////////
// cLogCommandOutputCallback:
void cLogCommandOutputCallback::Finished(void) void cLogCommandOutputCallback::Finished(void)
{ {
// Log each line separately: // Log each line separately:
size_t len = m_Buffer.length(); size_t len = m_Accum.length();
size_t last = 0; size_t last = 0;
for (size_t i = 0; i < len; i++) for (size_t i = 0; i < len; i++)
{ {
switch (m_Buffer[i]) switch (m_Accum[i])
{ {
case '\n': case '\n':
{ {
LOG("%s", m_Buffer.substr(last, i - last).c_str()); LOG("%s", m_Accum.substr(last, i - last).c_str());
last = i + 1; last = i + 1;
break; break;
} }
@ -59,11 +62,11 @@ void cLogCommandOutputCallback::Finished(void)
} // for i - m_Buffer[] } // for i - m_Buffer[]
if (last < len) if (last < len)
{ {
LOG("%s", m_Buffer.substr(last).c_str()); LOG("%s", m_Accum.substr(last).c_str());
} }
// Clear the buffer for the next command output: // Clear the buffer for the next command output:
m_Buffer.clear(); m_Accum.clear();
} }

View File

@ -47,18 +47,36 @@ class cNullCommandOutputCallback :
/// Sends all command output to a log, line by line, when the command finishes processing /** Accumulates all command output into a string. */
class cLogCommandOutputCallback : class cStringAccumCommandOutputCallback:
public cCommandOutputCallback public cCommandOutputCallback
{ {
typedef cCommandOutputCallback super;
public: public:
// cCommandOutputCallback overrides: // cCommandOutputCallback overrides:
virtual void Out(const AString & a_Text) override; virtual void Out(const AString & a_Text) override;
virtual void Finished(void) override; virtual void Finished(void) override {}
/** Returns the accumulated command output in a string. */
const AString & GetAccum(void) const { return m_Accum; }
protected: protected:
/// Output is stored here until the command finishes processing /** Output is stored here until the command finishes processing */
AString m_Buffer; AString m_Accum;
} ;
/// Sends all command output to a log, line by line, when the command finishes processing
class cLogCommandOutputCallback :
public cStringAccumCommandOutputCallback
{
public:
// cStringAccumCommandOutputCallback overrides:
virtual void Finished(void) override;
} ; } ;

View File

@ -229,7 +229,7 @@ inline const char * ClickActionToString(eClickAction a_ClickAction)
/** Returns a blockface mirrored around the Y axis (doesn't change up/down). */ /** Returns a blockface mirrored around the Y axis (doesn't change up / down). */
inline eBlockFace MirrorBlockFaceY(eBlockFace a_BlockFace) inline eBlockFace MirrorBlockFaceY(eBlockFace a_BlockFace)
{ {
switch (a_BlockFace) switch (a_BlockFace)

View File

@ -14,7 +14,7 @@
// fwd: WorldStorage/FastNBT.h // fwd: "WorldStorage/FastNBT.h"
class cFastNBTWriter; class cFastNBTWriter;
class cParsedNBT; class cParsedNBT;
@ -138,10 +138,10 @@ public:
bool operator !=(const cEnchantments & a_Other) const; bool operator !=(const cEnchantments & a_Other) const;
/** Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") */ /** Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") */
friend void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName); friend void EnchantmentSerializer::WriteToNBTCompound(const cEnchantments & a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName);
/** Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) */ /** Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) */
friend void EnchantmentSerializer::ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx); friend void EnchantmentSerializer::ParseFromNBT(cEnchantments & a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx);
protected: protected:
/** Maps enchantment ID -> enchantment level */ /** Maps enchantment ID -> enchantment level */

View File

@ -21,7 +21,7 @@ inline UInt64 HostToNetwork8(const void * a_Value)
inline UInt32 HostToNetwork4(const void* a_Value) inline UInt32 HostToNetwork4(const void * a_Value)
{ {
UInt32 buf; UInt32 buf;
memcpy( &buf, a_Value, sizeof( buf)); memcpy( &buf, a_Value, sizeof( buf));

View File

@ -1309,7 +1309,8 @@ bool cEntity::DetectPortal()
if (IsPlayer()) if (IsPlayer())
{ {
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld); // Send a respawn packet before world is loaded/generated so the client isn't left in limbo // Send a respawn packet before world is loaded / generated so the client isn't left in limbo
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld);
} }
return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false); return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
@ -1402,14 +1403,25 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
return false; return false;
} }
// Ask the plugins if the entity is allowed to change the world
if (cRoot::Get()->GetPluginManager()->CallHookEntityChangeWorld(*this, *a_World))
{
// A Plugin doesn't allow the entity to change the world
return false;
}
// Remove all links to the old world // Remove all links to the old world
SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal
GetWorld()->BroadcastDestroyEntity(*this); GetWorld()->BroadcastDestroyEntity(*this);
// Queue add to new world // Queue add to new world
a_World->AddEntity(this); a_World->AddEntity(this);
cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
SetWorld(a_World); SetWorld(a_World);
// Entity changed the world, call the hook
cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld);
return true; return true;
} }
@ -1688,8 +1700,8 @@ void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude)
{ {
m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ, a_Exclude); m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ, a_Exclude);
} }
// Clients seem to store two positions, one for the velocity packet and one for the teleport/relmove packet // Clients seem to store two positions, one for the velocity packet and one for the teleport / relmove packet
// The latter is only changed with a relmove/teleport, and m_LastPos stores this position // The latter is only changed with a relmove / teleport, and m_LastPos stores this position
m_LastPos = GetPosition(); m_LastPos = GetPosition();
} }
else else

View File

@ -474,7 +474,7 @@ protected:
static cCriticalSection m_CSCount; static cCriticalSection m_CSCount;
static UInt32 m_EntityCount; static UInt32 m_EntityCount;
/** Measured in meter/second (m/s) */ /** Measured in meters / second (m / s) */
Vector3d m_Speed; Vector3d m_Speed;
/** The ID of the entity that is guaranteed to be unique within a single run of the server. /** The ID of the entity that is guaranteed to be unique within a single run of the server.
@ -494,7 +494,7 @@ protected:
/** Stores whether head yaw has been set manually */ /** Stores whether head yaw has been set manually */
bool m_bDirtyHead; bool m_bDirtyHead;
/** Stores whether our yaw/pitch/roll (body orientation) has been set manually */ /** Stores whether our yaw / pitch / roll (body orientation) has been set manually */
bool m_bDirtyOrientation; bool m_bDirtyOrientation;
/** Stores whether we have sent a Velocity packet with a speed of zero (no speed) to the client /** Stores whether we have sent a Velocity packet with a speed of zero (no speed) to the client

View File

@ -102,11 +102,11 @@ int cEntityEffect::GetPotionEffectDuration(short a_ItemDamage)
// If potion is level II, half the duration. If not, stays the same // If potion is level II, half the duration. If not, stays the same
TierCoeff = (GetPotionEffectIntensity(a_ItemDamage) > 0) ? 0.5 : 1; TierCoeff = (GetPotionEffectIntensity(a_ItemDamage) > 0) ? 0.5 : 1;
// If potion is extended, multiply duration by 8/3. If not, stays the same // If potion is extended, multiply duration by 8 / 3. If not, stays the same
// Extended potion if sixth lowest bit is set // Extended potion if sixth lowest bit is set
ExtCoeff = (a_ItemDamage & 0x40) ? (8.0 / 3.0) : 1; ExtCoeff = (a_ItemDamage & 0x40) ? (8.0 / 3.0) : 1;
// If potion is splash potion, multiply duration by 3/4. If not, stays the same // If potion is splash potion, multiply duration by 3 / 4. If not, stays the same
SplashCoeff = IsPotionDrinkable(a_ItemDamage) ? 1 : 0.75; SplashCoeff = IsPotionDrinkable(a_ItemDamage) ? 1 : 0.75;
// Ref.: // Ref.:

View File

@ -6,7 +6,7 @@
#include "Floater.h" #include "Floater.h"
#include "Player.h" #include "Player.h"
#include "../ClientHandle.h" #include "../ClientHandle.h"
#include "Broadcaster.h"
@ -145,12 +145,12 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{ {
LOGD("Started producing particles for floater %i", GetUniqueID()); LOGD("Started producing particles for floater %i", GetUniqueID());
m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8))); m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8)));
m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15); m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15);
} }
else if (m_CountDownTime < 20) else if (m_CountDownTime < 20)
{ {
m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6); m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6);
m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15); m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15);
} }
m_CountDownTime--; m_CountDownTime--;

View File

@ -905,7 +905,7 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
} }
/* Check to which side the minecart is to be pushed. /* Check to which side the minecart is to be pushed.
Let's consider a z-x-coordinate system where the minecart is the center (0/0). Let's consider a z-x-coordinate system where the minecart is the center (0, 0).
The minecart moves along the line x = -z, the perpendicular line to this is x = z. The minecart moves along the line x = -z, the perpendicular line to this is x = z.
In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */ In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
if ( if (
@ -954,7 +954,7 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
} }
/* Check to which side the minecart is to be pushed. /* Check to which side the minecart is to be pushed.
Let's consider a z-x-coordinate system where the minecart is the center (0/0). Let's consider a z-x-coordinate system where the minecart is the center (0, 0).
The minecart moves along the line x = z, the perpendicular line to this is x = -z. The minecart moves along the line x = z, the perpendicular line to this is x = -z.
In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */ In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
if ( if (

View File

@ -29,7 +29,7 @@ public:
enum ePayload enum ePayload
{ {
mpNone = 0, // Empty minecart, ridable by player or mobs mpNone = 0, // Empty minecart, ridable by player or mobs
mpChest = 1, // Minecart-with-chest, can store a grid of 3*8 items mpChest = 1, // Minecart-with-chest, can store a grid of 3 * 8 items
mpFurnace = 2, // Minecart-with-furnace, can be powered mpFurnace = 2, // Minecart-with-furnace, can be powered
mpTNT = 3, // Minecart-with-TNT, can be blown up with activator rail mpTNT = 3, // Minecart-with-TNT, can be blown up with activator rail
mpHopper = 5, // Minecart-with-hopper, can be hopper mpHopper = 5, // Minecart-with-hopper, can be hopper
@ -54,8 +54,7 @@ protected:
cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z);
/** Handles physics on normal rails /** Handles physics on normal rails
For each tick, slow down on flat rails, speed up or slow down on ascending/descending rails (depending on direction), and turn on curved rails For each tick, slow down on flat rails, speed up or slow down on ascending / descending rails (depending on direction), and turn on curved rails. */
*/
void HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt); void HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt);
/** Handles powered rail physics /** Handles powered rail physics

View File

@ -355,7 +355,7 @@ float cPlayer::GetXpPercentage()
int currentLevel_XpBase = XpForLevel(currentLevel); int currentLevel_XpBase = XpForLevel(currentLevel);
return static_cast<float>(m_CurrentXp - currentLevel_XpBase) / return static_cast<float>(m_CurrentXp - currentLevel_XpBase) /
static_cast<float>(XpForLevel(1+currentLevel) - currentLevel_XpBase); static_cast<float>(XpForLevel(1 + currentLevel) - currentLevel_XpBase);
} }
@ -364,7 +364,7 @@ float cPlayer::GetXpPercentage()
bool cPlayer::SetCurrentExperience(int a_CurrentXp) bool cPlayer::SetCurrentExperience(int a_CurrentXp)
{ {
if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<int>().max() - m_LifetimeTotalXp))) if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<int>::max() - m_LifetimeTotalXp)))
{ {
LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp); LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp);
return false; // oops, they gave us a dodgey number return false; // oops, they gave us a dodgey number
@ -403,7 +403,7 @@ int cPlayer::DeltaExperience(int a_Xp_delta)
m_LifetimeTotalXp += a_Xp_delta; m_LifetimeTotalXp += a_Xp_delta;
} }
LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp); LOGD("Player \"%s\" gained / lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp);
// Set experience to be updated // Set experience to be updated
m_bDirtyExperience = true; m_bDirtyExperience = true;
@ -1606,6 +1606,12 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
return false; return false;
} }
if (cRoot::Get()->GetPluginManager()->CallHookEntityChangeWorld(*this, *a_World))
{
// A Plugin doesn't allow the player to change the world
return false;
}
// Send the respawn packet: // Send the respawn packet:
if (a_ShouldSendRespawn && (m_ClientHandle != nullptr)) if (a_ShouldSendRespawn && (m_ClientHandle != nullptr))
{ {
@ -1621,6 +1627,7 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
// Queue adding player to the new world, including all the necessary adjustments to the object // Queue adding player to the new world, including all the necessary adjustments to the object
a_World->AddPlayer(this); a_World->AddPlayer(this);
cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
// Update the view distance. // Update the view distance.
@ -1635,6 +1642,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
// Broadcast the player into the new world. // Broadcast the player into the new world.
a_World->BroadcastSpawnEntity(*this); a_World->BroadcastSpawnEntity(*this);
// Player changed the world, call the hook
cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld);
return true; return true;
} }
@ -1771,7 +1781,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
m_LastBedPos.z = root.get("SpawnZ", a_World->GetSpawnZ()).asInt(); m_LastBedPos.z = root.get("SpawnZ", a_World->GetSpawnZ()).asInt();
// Load the player stats. // Load the player stats.
// We use the default world name (like bukkit) because stats are shared between dimensions/worlds. // We use the default world name (like bukkit) because stats are shared between dimensions / worlds.
cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats);
StatSerializer.Load(); StatSerializer.Load();
@ -1868,7 +1878,7 @@ bool cPlayer::SaveToDisk()
} }
// Save the player stats. // Save the player stats.
// We use the default world name (like bukkit) because stats are shared between dimensions/worlds. // We use the default world name (like bukkit) because stats are shared between dimensions / worlds.
cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats);
if (!StatSerializer.Save()) if (!StatSerializer.Save())
{ {
@ -2076,7 +2086,7 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos)
} }
else else
{ {
if (Value >= 25) // Ignore small/slow movement if (Value >= 25) // Ignore small / slow movement
{ {
m_Stats.AddValue(statDistFlown, Value); m_Stats.AddValue(statDistFlown, Value);
} }

View File

@ -644,7 +644,7 @@ protected:
virtual void Destroyed(void); virtual void Destroyed(void);
/** Filters out damage for creative mode/friendly fire */ /** Filters out damage for creative mode / friendly fire */
virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; virtual bool DoTakeDamage(TakeDamageInfo & TDI) override;
/** Stops players from burning in creative mode */ /** Stops players from burning in creative mode */

View File

@ -109,7 +109,7 @@ protected:
eKind m_ProjectileKind; eKind m_ProjectileKind;
/** The structure for containing the entity ID and name who has created this projectile /** The structure for containing the entity ID and name who has created this projectile
The ID and/or name may be nullptr (e.g. for dispensers/mobs). */ The ID and / or name may be nullptr (e.g. for dispensers / mobs). */
CreatorData m_CreatorData; CreatorData m_CreatorData;
/** True if the projectile has hit the ground and is stuck there */ /** True if the projectile has hit the ground and is stuck there */

View File

@ -6,12 +6,28 @@
#include "Globals.h" #include "Globals.h"
#include "FastRandom.h" #include "FastRandom.h"
#include <random>
#ifdef _WIN32 #ifdef _WIN32
#define thread_local __declspec(thread) #define thread_local static __declspec(thread)
#elif defined __APPLE__
#define thread_local static __thread
#endif #endif
thread_local unsigned int m_Counter = 0; static unsigned int GetRandomSeed()
{
thread_local bool SeedCounterInitialized = 0;
thread_local unsigned int SeedCounter = 0;
if (!SeedCounterInitialized)
{
std::random_device rd;
std::uniform_int_distribution<unsigned int> dist;
SeedCounter = dist(rd);
SeedCounterInitialized = true;
}
return ++SeedCounter;
}
@ -92,7 +108,7 @@ public:
cFastRandom::cFastRandom(void) : cFastRandom::cFastRandom(void) :
m_LinearRand(m_Counter++) m_LinearRand(GetRandomSeed())
{ {
} }
@ -136,7 +152,7 @@ int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End)
// MTRand: // MTRand:
MTRand::MTRand() : MTRand::MTRand() :
m_MersenneRand(m_Counter++) m_MersenneRand(GetRandomSeed())
{ {
} }

View File

@ -237,7 +237,7 @@ bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints &
return true; return true;
} }
// Smoothing: for each line segment, add points on its 1/4 lengths // Smoothing: for each line segment, add points on its 1 / 4 lengths
bool res = false; bool res = false;
size_t Num = a_Src.size() - 2; // this many intermediary points size_t Num = a_Src.size() - 2; // this many intermediary points
a_Dst.clear(); a_Dst.clear();
@ -488,7 +488,7 @@ void cCaveTunnel::ProcessChunk(
continue; continue;
} }
// Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom: // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3 / 7 off the top and bottom:
int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
int DifY = itr->m_BlockY; int DifY = itr->m_BlockY;
int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
@ -529,7 +529,7 @@ void cCaveTunnel::ProcessChunk(
/* /*
#ifdef _DEBUG #ifdef _DEBUG
// For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave: // For debugging purposes, outline the shape of the cave using glowstone, after carving the entire cave:
for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
{ {
int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc

View File

@ -209,7 +209,7 @@ void cChunkGenerator::Execute(void)
{ {
if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC)) if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC))
{ {
LOG("Chunk generator performance: %.2f ch/s (%d ch total)", LOG("Chunk generator performance: %.2f ch / sec (%d ch total)",
(double)NumChunksGenerated * CLOCKS_PER_SEC/ (clock() - GenerationStart), (double)NumChunksGenerated * CLOCKS_PER_SEC/ (clock() - GenerationStart),
NumChunksGenerated NumChunksGenerated
); );
@ -241,7 +241,7 @@ void cChunkGenerator::Execute(void)
// Display perf info once in a while: // Display perf info once in a while:
if ((NumChunksGenerated > 16) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC)) if ((NumChunksGenerated > 16) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC))
{ {
LOG("Chunk generator performance: %.2f ch/s (%d ch total)", LOG("Chunk generator performance: %.2f ch / sec (%d ch total)",
(double)NumChunksGenerated * CLOCKS_PER_SEC / (clock() - GenerationStart), (double)NumChunksGenerated * CLOCKS_PER_SEC / (clock() - GenerationStart),
NumChunksGenerated NumChunksGenerated
); );

View File

@ -195,7 +195,7 @@ void cFinishGenGlowStone::GenFinish(cChunkDesc & a_ChunkDesc)
// The maximum size for a string of glowstone can get 3 - 5 blocks long // The maximum size for a string of glowstone can get 3 - 5 blocks long
int Size = 3 + m_Noise.IntNoise3DInt(ChunkX, i, ChunkZ) % 3; int Size = 3 + m_Noise.IntNoise3DInt(ChunkX, i, ChunkZ) % 3;
// Generate X/Z coordinates. // Generate X / Z coordinates.
int X = Size + (m_Noise.IntNoise2DInt(i, Size) % (cChunkDef::Width - Size * 2)); int X = Size + (m_Noise.IntNoise2DInt(i, Size) % (cChunkDef::Width - Size * 2));
int Z = Size + (m_Noise.IntNoise2DInt(X, i) % (cChunkDef::Width - Size * 2)); int Z = Size + (m_Noise.IntNoise2DInt(X, i) % (cChunkDef::Width - Size * 2));
@ -1274,6 +1274,7 @@ bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX
double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5); double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5);
cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn); cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn);
NewMob->SetHealth(NewMob->GetMaxHealth());
NewMob->SetPosition(AnimalX, AnimalY, AnimalZ); NewMob->SetPosition(AnimalX, AnimalY, AnimalZ);
a_ChunkDesc.GetEntities().push_back(NewMob); a_ChunkDesc.GetEntities().push_back(NewMob);
LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ); LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ);

View File

@ -667,7 +667,7 @@ public:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
{ {
// Generate the biomes for the 3*3 neighbors: // Generate the biomes for the 3 * 3 neighbors:
cChunkDef::BiomeMap neighborBiomes[3][3]; cChunkDef::BiomeMap neighborBiomes[3][3];
for (int z = 0; z < 3; z++) for (int x = 0; x < 3; x++) for (int z = 0; z < 3; z++) for (int x = 0; x < 3; x++)
{ {

View File

@ -5,7 +5,7 @@
/* /*
The integers generated may be interpreted in several ways: The integers generated may be interpreted in several ways:
- land/see designators - land / sea designators
- 0 = ocean - 0 = ocean
- >0 = land - >0 = land
- biome group - biome group
@ -310,7 +310,7 @@ public:
} }
} }
// Copy from Cache into a_Values; take into account the even/odd offsets in a_Min: // Copy from Cache into a_Values; take into account the even / odd offsets in a_Min:
for (int z = 0; z < SizeZ; ++z) for (int z = 0; z < SizeZ; ++z)
{ {
memcpy(a_Values + z * SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), SizeX * sizeof(int)); memcpy(a_Values + z * SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), SizeX * sizeof(int));

View File

@ -680,7 +680,7 @@ void cBiomalNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_Ch
void cBiomalNoise3DComposable::CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint) void cBiomalNoise3DComposable::CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint)
{ {
// Generate the 3*3 chunks of biomes around this chunk: // Generate the 3 * 3 chunks of biomes around this chunk:
cChunkDef::BiomeMap neighborBiomes[3 * 3]; cChunkDef::BiomeMap neighborBiomes[3 * 3];
for (int z = 0; z < 3; z++) for (int z = 0; z < 3; z++)
{ {

View File

@ -229,7 +229,7 @@ public:
} }
} }
// Copy from Cache into a_Values; take into account the even/odd offsets in a_Min: // Copy from Cache into a_Values; take into account the even / odd offsets in a_Min:
for (int z = 0; z < a_SizeZ; ++z) for (int z = 0; z < a_SizeZ; ++z)
{ {
memcpy(a_Values + z * a_SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), a_SizeX * sizeof(int)); memcpy(a_Values + z * a_SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), a_SizeX * sizeof(int));

View File

@ -61,7 +61,7 @@ protected:
/** The noise used as a pseudo-random generator */ /** The noise used as a pseudo-random generator */
cNoise m_Noise; cNoise m_Noise;
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */ /** Maximum size, in X / Z blocks, of the village (radius from the origin) */
int m_MaxSize; int m_MaxSize;
/** Borders of the vilalge - no item may reach out of this cuboid. */ /** Borders of the vilalge - no item may reach out of this cuboid. */

View File

@ -34,7 +34,7 @@ protected:
/** Maximum depth of the generator tree*/ /** Maximum depth of the generator tree*/
int m_MaxDepth; int m_MaxDepth;
/** Maximum size, in X/Z blocks, of the base (radius from the origin) */ /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */
int m_MaxSize; int m_MaxSize;

View File

@ -124,7 +124,7 @@ cStructGenRavines::cRavine::cRavine(int a_GridX, int a_GridZ, int a_OriginX, int
void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise)
{ {
// Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size): // Modify the size slightly to have different-sized ravines (1 / 2 to 1 / 1 of a_Size):
a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024; a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024;
// The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction // The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction
@ -177,7 +177,7 @@ void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cR
return; return;
} }
// Smoothing: for each line segment, add points on its 1/4 lengths // Smoothing: for each line segment, add points on its 1 / 4 lengths
size_t Num = a_Src.size() - 2; // this many intermediary points size_t Num = a_Src.size() - 2; // this many intermediary points
a_Dst.clear(); a_Dst.clear();
a_Dst.reserve(Num * 2 + 2); a_Dst.reserve(Num * 2 + 2);

View File

@ -311,7 +311,7 @@ void cStructGenOreNests::GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_Ore
rnd /= cChunkDef::Width; rnd /= cChunkDef::Width;
int BaseY = rnd % a_MaxHeight; int BaseY = rnd % a_MaxHeight;
rnd /= a_MaxHeight; rnd /= a_MaxHeight;
int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1/4 larger int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1 / 4 larger
int Num = 0; int Num = 0;
while (Num < NestSize) while (Num < NestSize)
{ {

View File

@ -61,7 +61,7 @@ protected:
/** The noise used as a pseudo-random generator */ /** The noise used as a pseudo-random generator */
cNoise m_Noise; cNoise m_Noise;
/** Maximum size, in X/Z blocks, of the structure (radius from the origin) */ /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */
int m_MaxSize; int m_MaxSize;
/** Borders of the structure - no item may reach out of this cuboid. */ /** Borders of the structure - no item may reach out of this cuboid. */

View File

@ -31,10 +31,10 @@ protected:
/** The noise used for generating random numbers */ /** The noise used for generating random numbers */
cNoise m_Noise; cNoise m_Noise;
/** Maximum depth of the generator tree*/ /** Maximum depth of the generator tree */
int m_MaxDepth; int m_MaxDepth;
/** Maximum size, in X/Z blocks, of the base (radius from the origin) */ /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */
int m_MaxSize; int m_MaxSize;

View File

@ -499,7 +499,7 @@ void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Nois
PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
h--; h--;
// Third and fourth layers - BigO2 and maybe 2*Corners: // Third and fourth layers - BigO2 and maybe 2 * Corners:
for (int Row = 0; Row < 2; Row++) for (int Row = 0; Row < 2; Row++)
{ {
PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
@ -673,7 +673,7 @@ void GetTallBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_
PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
h--; h--;
// Third and fourth layers - BigO2 and maybe 2*Corners: // Third and fourth layers - BigO2 and maybe 2 * Corners:
for (int Row = 0; Row < 2; Row++) for (int Row = 0; Row < 2; Row++)
{ {
PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);

View File

@ -61,7 +61,7 @@ protected:
/** The noise used as a pseudo-random generator */ /** The noise used as a pseudo-random generator */
cNoise m_Noise; cNoise m_Noise;
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */ /** Maximum size, in X / Z blocks, of the base (radius from the origin) */
int m_MaxSize; int m_MaxSize;
/** Borders of the vilalge - no item may reach out of this cuboid. */ /** Borders of the vilalge - no item may reach out of this cuboid. */

View File

@ -34,7 +34,7 @@ protected:
/** Maximum depth of the generator tree*/ /** Maximum depth of the generator tree*/
int m_MaxDepth; int m_MaxDepth;
/** Maximum size, in X/Z blocks, of the base (radius from the origin) */ /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */
int m_MaxSize; int m_MaxSize;
/** The underlying biome generator that defines whether the base is created or not */ /** The underlying biome generator that defines whether the base is created or not */

View File

@ -162,7 +162,7 @@ protected:
/** The noise used as a pseudo-random generator */ /** The noise used as a pseudo-random generator */
cNoise m_Noise; cNoise m_Noise;
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */ /** Maximum size, in X / Z blocks, of the village (radius from the origin) */
int m_MaxSize; int m_MaxSize;
/** The density for this village. Used to refrain from populating all house connectors. Range [0, 100] */ /** The density for this village. Used to refrain from populating all house connectors. Range [0, 100] */

View File

@ -32,7 +32,7 @@ protected:
/** Maximum depth of the generator tree*/ /** Maximum depth of the generator tree*/
int m_MaxDepth; int m_MaxDepth;
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */ /** Maximum size, in X / Z blocks, of the village (radius from the origin) */
int m_MaxSize; int m_MaxSize;
/** Minimum density - percentage of allowed house connections. Range [0, 100] */ /** Minimum density - percentage of allowed house connections. Range [0, 100] */

View File

@ -18,9 +18,9 @@
// Useful warnings from warning level 4: // Useful warnings from warning level 4:
#pragma warning(3 : 4189) // Local variable is initialized but not referenced #pragma warning(3 : 4189) // Local variable is initialized but not referenced
#pragma warning(3 : 4245) // Conversion from 'type1' to 'type2', signed/unsigned mismatch #pragma warning(3 : 4245) // Conversion from 'type1' to 'type2', signed / unsigned mismatch
#pragma warning(3 : 4310) // Cast truncates constant value #pragma warning(3 : 4310) // Cast truncates constant value
#pragma warning(3 : 4389) // Signed/unsigned mismatch #pragma warning(3 : 4389) // Signed / unsigned mismatch
#pragma warning(3 : 4505) // Unreferenced local function has been removed #pragma warning(3 : 4505) // Unreferenced local function has been removed
#pragma warning(3 : 4701) // Potentially unitialized local variable used #pragma warning(3 : 4701) // Potentially unitialized local variable used
#pragma warning(3 : 4702) // Unreachable code #pragma warning(3 : 4702) // Unreachable code
@ -433,10 +433,14 @@ typename std::enable_if<std::is_arithmetic<T>::value, C>::type CeilC(T a_Value)
//temporary replacement for std::make_unique until we get c++14 //temporary replacement for std::make_unique until we get c++14
template <class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args) namespace cpp14
{ {
return std::unique_ptr<T>(new T(args...)); template <class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
} }
// a tick is 50 ms // a tick is 50 ms

Some files were not shown because too many files have changed in this diff Show More