summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Server/Plugins/APIDump/APIDesc.lua156
-rw-r--r--Server/Plugins/APIDump/Classes/RankManager.lua16
-rw-r--r--Server/Plugins/APIDump/Classes/World.lua4
-rw-r--r--Tools/ProtoProxy/CMakeLists.txt2
-rwxr-xr-xcompile.sh87
-rw-r--r--src/Bindings/AllToLua.pkg9
-rw-r--r--src/Bindings/CMakeLists.txt1
-rw-r--r--src/Bindings/LuaState.cpp76
-rw-r--r--src/Bindings/LuaState.h5
-rw-r--r--src/Bindings/ManualBindings.cpp285
-rw-r--r--src/Bindings/ManualBindings_RankManager.cpp71
-rw-r--r--src/Bindings/ManualBindings_World.cpp50
-rw-r--r--src/BlockEntities/MobHeadEntity.cpp5
-rw-r--r--src/BlockEntities/MobHeadEntity.h18
-rw-r--r--src/Blocks/BlockBed.cpp4
-rw-r--r--src/Blocks/BlockBed.h2
-rw-r--r--src/Blocks/BlockHandler.cpp4
-rw-r--r--src/Blocks/BlockHandler.h4
-rw-r--r--src/Blocks/BlockPiston.cpp5
-rw-r--r--src/Blocks/WorldInterface.h2
-rw-r--r--src/ByteBuffer.cpp24
-rw-r--r--src/ByteBuffer.h4
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/ChunkData.cpp123
-rw-r--r--src/ChunkData.h12
-rw-r--r--src/ClientHandle.cpp41
-rw-r--r--src/ClientHandle.h28
-rw-r--r--src/Entities/Entity.cpp4
-rw-r--r--src/Entities/Player.cpp13
-rw-r--r--src/Entities/Player.h16
-rw-r--r--src/Mobs/Guardian.cpp9
-rw-r--r--src/Mobs/Ocelot.h7
-rw-r--r--src/Mobs/Squid.cpp34
-rw-r--r--src/Mobs/Wolf.cpp2
-rw-r--r--src/Mobs/Wolf.h101
-rw-r--r--src/Protocol/Authenticator.cpp13
-rw-r--r--src/Protocol/Authenticator.h8
-rw-r--r--src/Protocol/MojangAPI.cpp192
-rw-r--r--src/Protocol/MojangAPI.h50
-rw-r--r--src/Protocol/Packetizer.cpp53
-rw-r--r--src/Protocol/Packetizer.h6
-rw-r--r--src/Protocol/Protocol_1_10.cpp2
-rw-r--r--src/Protocol/Protocol_1_11.cpp2
-rw-r--r--src/Protocol/Protocol_1_12.cpp72
-rw-r--r--src/Protocol/Protocol_1_12.h2
-rw-r--r--src/Protocol/Protocol_1_8.cpp16
-rw-r--r--src/Protocol/Protocol_1_9.cpp15
-rw-r--r--src/RankManager.cpp86
-rw-r--r--src/RankManager.h28
-rw-r--r--src/Root.cpp4
-rw-r--r--src/Root.h5
-rw-r--r--src/Server.cpp2
-rw-r--r--src/Server.h3
-rw-r--r--src/UUID.cpp283
-rw-r--r--src/UUID.h100
-rw-r--r--src/World.cpp5
-rw-r--r--src/World.h7
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp17
-rw-r--r--src/WorldStorage/NBTChunkSerializer.h13
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.cpp67
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.h5
-rw-r--r--tests/ByteBuffer/CMakeLists.txt1
-rw-r--r--tests/ByteBuffer/Stubs.cpp16
-rw-r--r--tests/Generating/LuaState_Declaration.inc5
-rw-r--r--tests/Generating/LuaState_Typedefs.inc1
-rw-r--r--tests/Generating/Stubs.cpp20
-rw-r--r--tests/LuaThreadStress/LuaState_Declaration.inc5
-rw-r--r--tests/LuaThreadStress/LuaState_Typedefs.inc1
-rw-r--r--tests/LuaThreadStress/Stubs.cpp21
69 files changed, 1698 insertions, 654 deletions
diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua
index df2d93785..d7dc83043 100644
--- a/Server/Plugins/APIDump/APIDesc.lua
+++ b/Server/Plugins/APIDump/APIDesc.lua
@@ -1463,7 +1463,7 @@ end
{
{
Name = "UUID",
- Type = "string",
+ Type = "cUUID",
},
},
Returns =
@@ -1472,7 +1472,7 @@ end
Type = "boolean",
},
},
- Notes = "Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID. We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart. Accepts both 32-char and 36-char UUIDs (with and without dashes). If the string given is not a valid UUID, returns false.",
+ Notes = "Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID. We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart. Accepts both 32-char and 36-char UUIDs (with and without dashes).",
},
Kick =
{
@@ -8485,10 +8485,9 @@ a_Player:OpenWindow(Window);
<p>
All the functions are static, call them using the <code>cMojangAPI:Function()</code> convention.</p>
<p>
- Mojang uses two formats for UUIDs, short and dashed. Cuberite works with short UUIDs internally, but
- will convert to dashed UUIDs where needed - in the protocol login for example. The MakeUUIDShort()
- and MakeUUIDDashed() functions are provided for plugins to use for conversion between the two
- formats.</p>
+ Mojang uses two formats for UUIDs, short and dashed. Cuberite will accept either format for any
+ functions taking a UUID. The MakeUUIDShort() and MakeUUIDDashed() functions are provided for plugins
+ to use for conversion between the two formats.</p>
<p>
This class will cache values returned by the API service. The cache will hold the values for 7 days
by default, after that, they will no longer be available. This is in order to not let the server get
@@ -8509,10 +8508,10 @@ a_Player:OpenWindow(Window);
},
{
Name = "UUID",
- Type = "string",
+ Type = "cUUID",
},
},
- Notes = "Adds the specified PlayerName-to-UUID mapping into the cache, with current timestamp. Accepts both short or dashed UUIDs. ",
+ Notes = "Adds the specified PlayerName-to-UUID mapping into the cache, with current timestamp.",
},
GetPlayerNameFromUUID =
{
@@ -8521,7 +8520,7 @@ a_Player:OpenWindow(Window);
{
{
Name = "UUID",
- Type = "string",
+ Type = "cUUID",
},
{
Name = "UseOnlyCached",
@@ -8592,7 +8591,7 @@ a_Player:OpenWindow(Window);
{
{
Name = "UUID",
- Type = "string",
+ Type = "cUUID",
},
},
Returns =
@@ -8602,7 +8601,7 @@ a_Player:OpenWindow(Window);
Type = "string",
},
},
- Notes = "Converts the UUID to a dashed format (\"01234567-8901-2345-6789-012345678901\"). Accepts both dashed or short UUIDs. Logs a warning and returns an empty string if UUID format not recognized.",
+ Notes = "Converts the UUID to a dashed format (\"01234567-8901-2345-6789-012345678901\"). An alias for cUUID:ToLongString()",
},
MakeUUIDShort =
{
@@ -8611,7 +8610,7 @@ a_Player:OpenWindow(Window);
{
{
Name = "UUID",
- Type = "string",
+ Type = "cUUID",
},
},
Returns =
@@ -8621,7 +8620,7 @@ a_Player:OpenWindow(Window);
Type = "string",
},
},
- Notes = "Converts the UUID to a short format (without dashes, \"01234567890123456789012345678901\"). Accepts both dashed or short UUIDs. Logs a warning and returns an empty string if UUID format not recognized.",
+ Notes = "Converts the UUID to a short format (without dashes, \"01234567890123456789012345678901\"). An alias for cUUID:ToShortString()",
},
},
},
@@ -11181,7 +11180,7 @@ a_Player:OpenWindow(Window);
{
{
Name = "PlayerUUID",
- Type = "string",
+ Type = "cUUID",
},
{
Name = "CallbackFunction",
@@ -12441,6 +12440,135 @@ end
},
},
},
+ cUUID =
+ {
+ Desc = [[
+ Class representing a Universally Unique Identifier.
+ Note that all Cuberite's API functions that take a cUUID parameter will also
+ accept a string in its place, as long as that string can be converted to a cUUID
+ (using the {{#FromString_1|cUUID:FromString}} function).
+ ]],
+ Functions =
+ {
+ constructor =
+ {
+ Returns =
+ {
+ {
+ Type = "cUUID",
+ },
+ },
+ Notes = "Constructs a nil-valued UUID (all zeros)",
+ },
+ Compare =
+ {
+ Params =
+ {
+ {
+ Name = "Other",
+ Type = "cUUID",
+ },
+ },
+ Returns =
+ {
+ {
+ Type = "number",
+ },
+ },
+ Notes = [[
+ Compares this UUID with the specified Other UUID, Returns:
+ 0 when equal to Other,
+ < 0 when less than Other,
+ > 0 when greater than Other
+ ]],
+ },
+ IsNil =
+ {
+ Returns =
+ {
+ {
+ Type = "boolean",
+ },
+ },
+ Notes = "Returns true if this contains the \"nil\" UUID with all bits set to 0",
+ },
+ FromString =
+ {
+ Params =
+ {
+ {
+ Name = "StringUUID",
+ Type = "string",
+ },
+ },
+ Returns =
+ {
+ {
+ Type = "boolean",
+ },
+ },
+ Notes = "Tries to interpret the string as a short or long form UUID and assign from it. On error, returns false and does not set the value.",
+ },
+ ToShortString =
+ {
+ Returns =
+ {
+ {
+ Type = "string",
+ },
+ },
+ Notes = "Converts the UUID to a short form string (i.e without dashes).",
+ },
+ ToLongString =
+ {
+ Returns =
+ {
+ {
+ Type = "string",
+ },
+ },
+ Notes = "Converts the UUID to a long form string (i.e with dashes).",
+ },
+ Version =
+ {
+ Returns =
+ {
+ {
+ Type = "number",
+ },
+ },
+ Notes = "Returns the version number of the UUID.",
+ },
+ Variant =
+ {
+ Returns =
+ {
+ {
+ Type = "number",
+ },
+ },
+ Notes = "Returns the variant number of the UUID",
+ },
+ GenerateVersion3 =
+ {
+ IsStatic = true,
+ Params =
+ {
+ {
+ Name = "Name",
+ Type = "string",
+ },
+ },
+ Returns =
+ {
+ {
+ Type = "cUUID",
+ },
+ },
+ Notes = "Generates a version 3, variant 1 UUID based on the md5 hash of Name."
+ },
+ },
+ },
cWebPlugin =
{
Desc = "",
diff --git a/Server/Plugins/APIDump/Classes/RankManager.lua b/Server/Plugins/APIDump/Classes/RankManager.lua
index be2969298..d41201ca9 100644
--- a/Server/Plugins/APIDump/Classes/RankManager.lua
+++ b/Server/Plugins/APIDump/Classes/RankManager.lua
@@ -258,7 +258,7 @@ return
{
{
Name = "PlayerUUID",
- Type = "string",
+ Type = "cUUID",
},
},
Returns =
@@ -276,7 +276,7 @@ return
{
{
Name = "PlayerUUID",
- Type = "string",
+ Type = "cUUID",
},
},
Returns =
@@ -303,7 +303,7 @@ return
{
{
Name = "PlayerUUID",
- Type = "string",
+ Type = "cUUID",
},
},
Returns =
@@ -322,7 +322,7 @@ return
{
{
Name = "PlayerUUID",
- Type = "string",
+ Type = "cUUID",
},
},
Returns =
@@ -340,7 +340,7 @@ return
{
{
Name = "PlayerUUID",
- Type = "string",
+ Type = "cUUID",
},
},
Returns =
@@ -502,7 +502,7 @@ return
{
{
Name = "PlayerUUID",
- Type = "string",
+ Type = "cUUID",
},
},
Returns =
@@ -604,7 +604,7 @@ return
{
{
Name = "PlayerUUID",
- Type = "string",
+ Type = "cUUID",
},
},
Notes = "Removes the player's rank; the player's left without a rank. Note that this doesn't change the {{cPlayer}} instances for the already connected players, you need to update all the instances manually. No action if the player has no rank assigned to them already.",
@@ -699,7 +699,7 @@ return
{
{
Name = "PlayerUUID",
- Type = "string",
+ Type = "cUUID",
},
{
Name = "PlayerName",
diff --git a/Server/Plugins/APIDump/Classes/World.lua b/Server/Plugins/APIDump/Classes/World.lua
index 62d71828f..d523f3881 100644
--- a/Server/Plugins/APIDump/Classes/World.lua
+++ b/Server/Plugins/APIDump/Classes/World.lua
@@ -18,7 +18,7 @@ return
Each world runs several separate threads used for various housekeeping purposes, the most important
of those is the Tick thread. This thread updates the game logic 20 times per second, and it is
the thread where all the gameplay actions are evaluated. Liquid physics, entity interactions,
- player ovement etc., all are applied in this thread.</p>
+ player movement etc., all are applied in this thread.</p>
<p>
Additional threads include the generation thread (generates new chunks as needed, storage thread
(saves and loads chunk from the disk), lighting thread (updates block light values) and the
@@ -901,7 +901,7 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
{
{
Name = "PlayerUUID",
- Type = "string",
+ Type = "cUUID",
},
{
Name = "CallbackFunction",
diff --git a/Tools/ProtoProxy/CMakeLists.txt b/Tools/ProtoProxy/CMakeLists.txt
index d20f570c3..76e9fa609 100644
--- a/Tools/ProtoProxy/CMakeLists.txt
+++ b/Tools/ProtoProxy/CMakeLists.txt
@@ -29,6 +29,7 @@ set_exe_flags()
set(SHARED_SRC
../../src/ByteBuffer.cpp
../../src/StringUtils.cpp
+ ../../src/UUID.cpp
../../src/PolarSSL++/AesCfb128Decryptor.cpp
../../src/PolarSSL++/AesCfb128Encryptor.cpp
../../src/PolarSSL++/CryptoKey.cpp
@@ -41,6 +42,7 @@ set(SHARED_SRC
set(SHARED_HDR
../../src/ByteBuffer.h
../../src/StringUtils.h
+ ../../src/UUID.h
../../src/PolarSSL++/AesCfb128Decryptor.h
../../src/PolarSSL++/AesCfb128Encryptor.h
../../src/PolarSSL++/CryptoKey.h
diff --git a/compile.sh b/compile.sh
index ab36726a2..5626227d5 100755
--- a/compile.sh
+++ b/compile.sh
@@ -17,7 +17,6 @@ DEFAULT_BRANCH="master" # Other options: None currently
# Constants not modifiable through command line:
UPSTREAM_REPO="origin"
UPSTREAM_LINK="https://github.com/cuberite/cuberite.git"
-DRY_RUN="no"
#=================== Error functions ===================
@@ -55,10 +54,12 @@ errorArguments ()
echo "options:"
echo " -m The compilation mode. Either \"Release\" or \"Debug\". Defaults to \"$DEFAULT_BUILDTYPE\""
echo ' -t The number of threads to use for compiling'
- echo ' If unspecified, a "smart guess" is attempted'
+ echo " If unspecified, at most $MAX_DEFAULT_THREADS threads are used. The special value CORES attempts to set the number of"
+ echo ' threads to the number of computer cores.'
echo ' -b The branch to compile. (Currently unused and pinned to MASTER)'
- echo ' -n Prevent interactive mode'
- echo ' -d Dry run. Print the chosen settings and exit'
+ echo ' -n yes: Prevent interactive mode. Unnecessary in combination with other arguments.'
+ echo ' Use without any other argument to build with the default settings.'
+ echo ' -d yes: Dry run. Print the chosen settings and exit'
echo
echo "Usage examples:"
echo " ./compile.sh"
@@ -114,7 +115,7 @@ echoErr () # Echo to stderr.
STATE_INTERACTIVE=1 # Interactive, unless one or more command line options are passed.
-while getopts ":m:t:b:" name; do
+while getopts ":m:t:b:d:n:" name; do
value=$OPTARG
STATE_INTERACTIVE=0
case "$name" in
@@ -130,6 +131,8 @@ while getopts ":m:t:b:" name; do
if [ ! -z "$CHOICE_THREADS" ]; then errorArguments; fi # Argument duplication.
if [ "$value" -gt 0 ] 2>/dev/null; then # If a positive integer.
CHOICE_THREADS="$value"
+ elif [ "$value" = "CORES" ]; then
+ CHOICE_THREADS="CORES"
else
errorArguments
fi
@@ -139,9 +142,13 @@ while getopts ":m:t:b:" name; do
CHOICE_BRANCH=1 # Only used for dupe checking, overridden below.
echoErr "Warning: The -b option is currently unused, it was ignored"
;;
+ d)
+ if [ ! -z "$DRY_RUN" ]; then errorArguments; fi # Argument duplication.
+ DRY_RUN="yes"
+ ;;
n)
- if [ "$dummy" = "1" ]; then errorArguments; fi # Argument duplication.
- dummy=1
+ if [ "$dummy" = "1" ]; then errorArguments; fi # Argument duplication.
+ dummy=1 # we just want to disable interactive mode, passing an argument already did this. No need to do anything.
;;
*)
errorArguments
@@ -149,6 +156,7 @@ while getopts ":m:t:b:" name; do
esac
done
+if [ -z "$DRY_RUN" ]; then DRY_RUN="no"; fi
#=================== Dependency checks and greeting ===================
@@ -343,45 +351,63 @@ if [ $STATE_INTERACTIVE -eq 1 ]; then
r|N)
CHOICE_BUILDTYPE="Release"
;;
- ""|N)
- CHOICE_BUILDTYPE="$DEFAULT_BUILDTYPE"
- ;;
*)
errorInput
;;
esac
-elif [ -z "$CHOICE_BUILDTYPE" ]; then
- CHOICE_BUILDTYPE="$DEFAULT_BUILDTYPE" # Non interactive mode with no buildtype specified.
+fi
+
+if [ -z "$CHOICE_BUILDTYPE" ]; then # No buildtype specified.
+ CHOICE_BUILDTYPE="$DEFAULT_BUILDTYPE"
fi
#=================== Choice: Thread amount ===================
-autoChooseThreads()
+
+numberOfCores()
{
KERNEL=$(uname -s)
if [ "$KERNEL" = "Linux" ] || [ "$KERNEL" = "Darwin" ]; then
echo $(getconf _NPROCESSORS_ONLN)
else
- echo "$DEFAULT_THREADS"
+ echo "unknown"
fi
}
+CORE_COUNT=`numberOfCores`
+
if [ $STATE_INTERACTIVE -eq 1 ]; then
- printf %s "Enter the number of threads to use. Leave empty for an automatic choice: "
- read CHOICE_THREADS
- if [ "$CHOICE_THREADS" = "" ] 2> /dev/null; then
- CHOICE_THREADS=`autoChooseThreads`
- elif [ "$CHOICE_THREADS" -lt 0 ] 2> /dev/null; then
- errorInput
- fi
+ echo "Choose the number of compilation threads."
-elif [ -z "$CHOICE_THREADS" ]; then .
- CHOICE_THREADS=`autoChooseThreads` # Non interactive mode with no thread amount specified.
+ if [ "$CORE_COUNT" = "unknown" ]; then
+ printf %s "Could not detect the number of cores. "
+ elif [ "$CORE_COUNT" -eq 1 ]; then
+ echo "You have 1 core."
+ else
+ echo "You have $CORE_COUNT cores."
+ fi
+
+ echo "If you have enough RAM, it is wise to choose a number as high as your core count. "
+ echo "Otherwise choose lower. Raspberry Pis should choose 1. If in doubt, choose 1."
+ printf %s "Please enter the number of compilation threads to use (Default: 1): "
+ read CHOICE_THREADS
fi
+if [ -z "$CHOICE_THREADS" ] 2> /dev/null; then
+ CHOICE_THREADS=1
+elif [ "$CHOICE_THREADS" = "CORES" ] 2> /dev/null; then
+ if [ $CORE_COUNT = "unknown" ]; then
+ CHOICE_THREADS=1
+ echo "WARNING: could not detect number of cores. Using 1 thread." >&2
+ else
+ CHOICE_THREADS="$CORE_COUNT"
+ fi
+elif [ "$CHOICE_THREADS" -lt 0 ] 2> /dev/null; then
+ errorInput
+fi
#=================== Print settings summary ===================
@@ -392,18 +418,27 @@ else
previousCompilation="Detected. This should make fetching and compiling faster."
fi
-# Ask the user's permission to connect to the net.
+CORE_WARNING=""
+if [ "$CORE_COUNT" != "unknown" ] && [ "$CORE_COUNT" -lt "$CHOICE_THREADS" ]; then
+ CORE_WARNING=" - Warning: More threads than cores."
+fi
+
echo ""
echoInt "#### Settings Summary ####"
echo "Build Type: " "$CHOICE_BUILDTYPE"
echo "Branch: " "$CHOICE_BRANCH" "(Currently the only choice)"
-echo "Compilation threads: " "$CHOICE_THREADS"
+echo "Compilation threads: " "$CHOICE_THREADS$CORE_WARNING"
+echo "Cores: " "$CORE_COUNT"
echo "Previous compilation: " "$previousCompilation"
echo "Upstream Link: " "$UPSTREAM_LINK"
echo "Upstream Repo: " "$UPSTREAM_REPO"
-if [ "$DRY_RUN" = "yes" ]; then exit 0; fi
+if [ "$DRY_RUN" = "yes" ]; then
+ echo "This is a dry run. Exiting now."
+ exit 0;
+fi
+# Ask the user's permission to connect to the net.
if [ $STATE_INTERACTIVE -eq 1 ]; then
echo
echo "After pressing ENTER, the script will connect to $UPSTREAM_LINK"
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index b99ac2f88..778cdf42c 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -17,13 +17,15 @@ inheritance doesn't work properly (#1789).
$#include "../Globals.h"
// Typedefs from Globals.h, so that we don't have to process that file:
-typedef long long Int64;
-typedef int Int32;
-typedef short Int16;
+typedef signed long long Int64;
+typedef signed int Int32;
+typedef signed short Int16;
+typedef signed char Int8;
typedef unsigned long long UInt64;
typedef unsigned int UInt32;
typedef unsigned short UInt16;
+typedef unsigned char UInt8;
$cfile "../Vector3.h"
@@ -69,6 +71,7 @@ $cfile "../MapManager.h"
$cfile "../Scoreboard.h"
$cfile "../Statistics.h"
$cfile "../Protocol/MojangAPI.h"
+$cfile "../UUID.h"
// Entities:
$cfile "../Entities/Entity.h"
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
index 6b2ad7ab0..0ab21467b 100644
--- a/src/Bindings/CMakeLists.txt
+++ b/src/Bindings/CMakeLists.txt
@@ -139,6 +139,7 @@ set(BINDING_DEPENDENCIES
../StringUtils.h
../Tracer.h
../UI/Window.h
+ ../UUID.h
../Vector3.h
../WebAdmin.h
../World.h
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index b443325ec..7bd4becb6 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -20,6 +20,7 @@ extern "C"
#include "../Entities/Entity.h"
#include "../BlockEntities/BlockEntity.h"
#include "../DeadlockDetect.h"
+#include "../UUID.h"
@@ -1410,6 +1411,40 @@ bool cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal)
+bool cLuaState::GetStackValue(int a_StackPos, cUUID & a_Value)
+{
+ if (lua_isnil(m_LuaState, a_StackPos))
+ {
+ return false;
+ }
+
+ tolua_Error tolua_Err;
+ if (tolua_isusertype(m_LuaState, a_StackPos, "cUUID", 0, &tolua_Err))
+ {
+ // Found a cUUID, copy into output value
+ cUUID * PtrUUID = nullptr;
+ GetStackValue(a_StackPos, PtrUUID);
+ if (PtrUUID == nullptr)
+ {
+ return false;
+ }
+ a_Value = *PtrUUID;
+ return true;
+ }
+
+ // Try to get a string and parse as a UUID into the output
+ AString StrUUID;
+ if (!GetStackValue(a_StackPos, StrUUID))
+ {
+ return false;
+ }
+ return a_Value.FromString(StrUUID);
+}
+
+
+
+
+
cLuaState::cStackValue cLuaState::WalkToValue(const AString & a_Name)
{
// There needs to be at least one value on the stack:
@@ -1785,6 +1820,47 @@ bool cLuaState::CheckParamFunctionOrNil(int a_StartParam, int a_EndParam)
+bool cLuaState::CheckParamUUID(int a_StartParam, int a_EndParam)
+{
+ ASSERT(IsValid());
+
+ if (a_EndParam < 0)
+ {
+ a_EndParam = a_StartParam;
+ }
+
+ cUUID tempUUID;
+ AString tempStr;
+ // Accept either a cUUID or a string that contains a valid UUID
+ for (int i = a_StartParam; i <= a_EndParam; ++i)
+ {
+ tolua_Error err;
+ if (tolua_isusertype(m_LuaState, i, "cUUID", 0, &err) && !lua_isnil(m_LuaState, i))
+ {
+ continue;
+ }
+
+ if (!tolua_iscppstring(m_LuaState, i, 0, &err))
+ {
+ ApiParamError("Failed to read parameter #%d. UUID expected, got %s", i, GetTypeText(i).c_str());
+ return false;
+ }
+
+ // Check string is a valid UUID
+ GetStackValue(i, tempStr);
+ if (!tempUUID.FromString(tempStr))
+ {
+ ApiParamError("Failed to read parameter #%d. UUID expected, got non-UUID string:\n\t\"%s\"", i, tempStr.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+
+
bool cLuaState::CheckParamEnd(int a_Param)
{
tolua_Error tolua_err;
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index 8014f40d4..98f1cbc28 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -659,6 +659,7 @@ public:
bool GetStackValue(int a_StackPos, eBlockFace & a_Value);
bool GetStackValue(int a_StackPos, eWeather & a_Value);
bool GetStackValue(int a_StackPos, float & a_ReturnedVal);
+ bool GetStackValue(int a_StackPos, cUUID & a_Value);
// template to catch all of the various c++ integral types without overload conflicts
template <class T>
@@ -787,6 +788,10 @@ public:
/** Returns true if the specified parameters on the stack are functions or nils; also logs warning if not */
bool CheckParamFunctionOrNil(int a_StartParam, int a_EndParam = -1);
+ /** Returns true if the specified parameters on the stack are UUIDs; also logs warning if not
+ Accepts either cUUID instances or strings that contain UUIDs */
+ bool CheckParamUUID(int a_StartParam, int a_EndParam = -1);
+
/** Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) */
bool CheckParamEnd(int a_Param);
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 6fe133e1e..c87e9ed20 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -1665,6 +1665,28 @@ static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S)
+static int tolua_cPlayer_GetUUID(lua_State * tolua_S)
+{
+ // Check the params:
+ cLuaState L(tolua_S);
+ if (!L.CheckParamSelf("cPlayer"))
+ {
+ return 0;
+ }
+
+ // Get the params:
+ cPlayer * Self = nullptr;
+ L.GetStackValue(1, Self);
+
+ // Return the UUID as a string
+ L.Push(Self->GetUUID().ToShortString());
+ return 1;
+}
+
+
+
+
+
template <
class OBJTYPE,
void (OBJTYPE::*SetCallback)(cLuaState::cCallbackPtr && a_CallbackFn)
@@ -2318,7 +2340,7 @@ static int tolua_cClientHandle_SendPluginMessage(lua_State * L)
{
cLuaState S(L);
if (
- !S.CheckParamUserType(1, "cClientHandle") ||
+ !S.CheckParamSelf("cClientHandle") ||
!S.CheckParamString(2, 3) ||
!S.CheckParamEnd(4)
)
@@ -2343,13 +2365,145 @@ static int tolua_cClientHandle_SendPluginMessage(lua_State * L)
+static int tolua_cClientHandle_GetUUID(lua_State * tolua_S)
+{
+ // Check the params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamSelf("cClientHandle") ||
+ !L.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ cClientHandle * Self;
+ L.GetStackValue(1, Self);
+
+ // Return the UUID as a string:
+ L.Push(Self->GetUUID().ToShortString());
+ return 1;
+}
+
+
+
+
+
+static int tolua_cClientHandle_GenerateOfflineUUID(lua_State * tolua_S)
+{
+ // Check the params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamStaticSelf("cClientHandle") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString Username;
+ L.GetStackValue(2, Username);
+
+ // Return the UUID as a string:
+ L.Push(cClientHandle::GenerateOfflineUUID(Username).ToShortString());
+ return 1;
+}
+
+
+
+
+
+static int tolua_cClientHandle_IsUUIDOnline(lua_State * tolua_S)
+{
+ // Check the params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamStaticSelf("cClientHandle") ||
+ !L.CheckParamUUID(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ cUUID UUID;
+ L.GetStackValue(2, UUID);
+
+ // Return the result:
+ L.Push(cClientHandle::IsUUIDOnline(UUID));
+ return 1;
+}
+
+
+
+
+static int tolua_cMobHeadEntity_SetOwner(lua_State * tolua_S)
+{
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamSelf("cMobHeadEntity") ||
+ !L.CheckParamUUID(2) ||
+ !L.CheckParamString(3, 5) ||
+ !L.CheckParamEnd(6)
+ )
+ {
+ return 0;
+ }
+
+
+ // Get the params:
+ cMobHeadEntity * Self;
+ cUUID OwnerUUID;
+ AString OwnerName, OwnerTexture, OwnerTextureSignature;
+ L.GetStackValues(1, Self, OwnerUUID, OwnerName, OwnerTexture, OwnerTextureSignature);
+
+ // Set the owner:
+ Self->SetOwner(OwnerUUID, OwnerName, OwnerTexture, OwnerTextureSignature);
+ return 0;
+}
+
+
+
+
+
+static int tolua_cMobHeadEntity_GetOwnerUUID(lua_State * tolua_S)
+{
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamSelf("cMobHeadEntity") ||
+ !L.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ cMobHeadEntity * Self;
+ L.GetStackValue(1, Self);
+
+ // Return the UUID as a string:
+ cUUID Owner = Self->GetOwnerUUID();
+ L.Push(Owner.IsNil() ? AString{} : Owner.ToShortString());
+ return 1;
+}
+
+
+
+
+
static int tolua_cMojangAPI_AddPlayerNameToUUIDMapping(lua_State * L)
{
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cMojangAPI") ||
+ !S.CheckParamStaticSelf("cMojangAPI") ||
!S.CheckParamString(2) ||
- !S.CheckParamString(3) ||
+ !S.CheckParamUUID(3) ||
!S.CheckParamEnd(4)
)
{
@@ -2357,9 +2511,9 @@ static int tolua_cMojangAPI_AddPlayerNameToUUIDMapping(lua_State * L)
}
// Retrieve the parameters:
- AString UUID, PlayerName;
- S.GetStackValue(2, PlayerName);
- S.GetStackValue(3, UUID);
+ AString PlayerName;
+ cUUID UUID;
+ S.GetStackValues(2, PlayerName, UUID);
// Store in the cache:
cRoot::Get()->GetMojangAPI().AddPlayerNameToUUIDMapping(PlayerName, UUID);
@@ -2374,15 +2528,15 @@ static int tolua_cMojangAPI_GetPlayerNameFromUUID(lua_State * L)
{
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cMojangAPI") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamStaticSelf("cMojangAPI") ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(4)
)
{
return 0;
}
- AString UUID;
+ cUUID UUID;
S.GetStackValue(2, UUID);
// If the UseOnlyCached param was given, read it; default to false
@@ -2407,7 +2561,7 @@ static int tolua_cMojangAPI_GetUUIDFromPlayerName(lua_State * L)
{
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cMojangAPI") ||
+ !S.CheckParamStaticSelf("cMojangAPI") ||
!S.CheckParamString(2) ||
!S.CheckParamEnd(4)
)
@@ -2426,9 +2580,9 @@ static int tolua_cMojangAPI_GetUUIDFromPlayerName(lua_State * L)
lua_pop(L, 1);
}
- // Return the UUID:
- AString UUID = cRoot::Get()->GetMojangAPI().GetUUIDFromPlayerName(PlayerName, ShouldUseCacheOnly);
- S.Push(UUID);
+ // Return the UUID as a string:
+ cUUID UUID = cRoot::Get()->GetMojangAPI().GetUUIDFromPlayerName(PlayerName, ShouldUseCacheOnly);
+ S.Push(UUID.IsNil() ? AString{} : UUID.ToShortString());
return 1;
}
@@ -2440,7 +2594,7 @@ static int tolua_cMojangAPI_GetUUIDsFromPlayerNames(lua_State * L)
{
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cMojangAPI") ||
+ !S.CheckParamStaticSelf("cMojangAPI") ||
!S.CheckParamTable(2) ||
!S.CheckParamEnd(4)
)
@@ -2476,23 +2630,23 @@ static int tolua_cMojangAPI_GetUUIDsFromPlayerNames(lua_State * L)
lua_newtable(L);
// Get the UUIDs:
- AStringVector UUIDs = cRoot::Get()->GetMojangAPI().GetUUIDsFromPlayerNames(PlayerNames, ShouldUseCacheOnly);
+ auto UUIDs = cRoot::Get()->GetMojangAPI().GetUUIDsFromPlayerNames(PlayerNames, ShouldUseCacheOnly);
if (UUIDs.size() != PlayerNames.size())
{
// A hard error has occured while processing the request, no UUIDs were returned. Return an empty table:
return 1;
}
- // Convert to output table, PlayerName -> UUID:
+ // Convert to output table, PlayerName -> UUID string:
size_t len = UUIDs.size();
for (size_t i = 0; i < len; i++)
{
- if (UUIDs[i].empty())
+ if (UUIDs[i].IsNil())
{
// No UUID was provided for PlayerName[i], skip it in the resulting table
continue;
}
- lua_pushlstring(L, UUIDs[i].c_str(), UUIDs[i].length());
+ S.Push(UUIDs[i].ToShortString());
lua_setfield(L, 3, PlayerNames[i].c_str());
}
return 1;
@@ -2504,13 +2658,13 @@ static int tolua_cMojangAPI_GetUUIDsFromPlayerNames(lua_State * L)
static int tolua_cMojangAPI_MakeUUIDDashed(lua_State * L)
{
- // Function signature: cMojangAPI:MakeUUIDDashed(UUID) -> string
+ // Function now non-existant but kept for API compatibility
// Check params:
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cMojangAPI") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamStaticSelf("cMojangAPI") ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(3)
)
{
@@ -2518,11 +2672,11 @@ static int tolua_cMojangAPI_MakeUUIDDashed(lua_State * L)
}
// Get the params:
- AString UUID;
+ cUUID UUID;
S.GetStackValue(2, UUID);
// Push the result:
- S.Push(cRoot::Get()->GetMojangAPI().MakeUUIDDashed(UUID));
+ S.Push(UUID.ToLongString());
return 1;
}
@@ -2532,13 +2686,13 @@ static int tolua_cMojangAPI_MakeUUIDDashed(lua_State * L)
static int tolua_cMojangAPI_MakeUUIDShort(lua_State * L)
{
- // Function signature: cMojangAPI:MakeUUIDShort(UUID) -> string
+ // Function now non-existant but kept for API compatibility
// Check params:
cLuaState S(L);
if (
!S.CheckParamUserTable(1, "cMojangAPI") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(3)
)
{
@@ -2546,11 +2700,11 @@ static int tolua_cMojangAPI_MakeUUIDShort(lua_State * L)
}
// Get the params:
- AString UUID;
+ cUUID UUID;
S.GetStackValue(2, UUID);
// Push the result:
- S.Push(cRoot::Get()->GetMojangAPI().MakeUUIDShort(UUID));
+ S.Push(UUID.ToShortString());
return 1;
}
@@ -3053,6 +3207,66 @@ static int tolua_cLuaWindow_new_local(lua_State * tolua_S)
+static int tolua_cRoot_DoWithPlayerByUUID(lua_State * tolua_S)
+{
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamSelf("cRoot") ||
+ !L.CheckParamUUID(2) ||
+ !L.CheckParamFunction(3) ||
+ !L.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ class cCallback :
+ public cPlayerListCallback
+ {
+ public:
+ cCallback(cLuaState & a_LuaState) :
+ m_LuaState(a_LuaState)
+ {
+ }
+
+ virtual bool Item(cPlayer * a_Player) override
+ {
+ bool ret = false;
+ m_LuaState.Call(m_FnRef, a_Player, cLuaState::Return, ret);
+ return ret;
+ }
+
+ cLuaState & m_LuaState;
+ cLuaState::cRef m_FnRef;
+ } Callback(L);
+
+ // Get parameters:
+ cRoot * Self;
+ cUUID PlayerUUID;
+ L.GetStackValues(1, Self, PlayerUUID, Callback.m_FnRef);
+
+ if (PlayerUUID.IsNil())
+ {
+ return L.ApiParamError("Expected a non-nil UUID for parameter #1");
+ }
+ if (!Callback.m_FnRef.IsValid())
+ {
+ return L.ApiParamError("Expected a valid callback function for parameter #2");
+ }
+
+ // Call the function:
+ bool res = Self->DoWithPlayerByUUID(PlayerUUID, Callback);
+
+ // Push the result as the return value:
+ L.Push(res);
+ return 1;
+}
+
+
+
+
+
static int tolua_cRoot_GetBuildCommitID(lua_State * tolua_S)
{
cLuaState L(tolua_S);
@@ -3791,9 +4005,12 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cClientHandle");
- tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE);
- tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE);
- tolua_function(tolua_S, "SendPluginMessage", tolua_cClientHandle_SendPluginMessage);
+ tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE);
+ tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE);
+ tolua_function(tolua_S, "SendPluginMessage", tolua_cClientHandle_SendPluginMessage);
+ tolua_function(tolua_S, "GetUUID", tolua_cClientHandle_GetUUID);
+ tolua_function(tolua_S, "GenerateOfflineUUID", tolua_cClientHandle_GenerateOfflineUUID);
+ tolua_function(tolua_S, "IsUUIDOnline", tolua_cClientHandle_IsUUIDOnline);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cColor");
@@ -3881,6 +4098,11 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "DoWithMap", DoWithID<cMapManager, cMap, &cMapManager::DoWithMap>);
tolua_endmodule(tolua_S);
+ tolua_beginmodule(tolua_S, "cMobHeadEntity");
+ tolua_function(tolua_S, "SetOwner", tolua_cMobHeadEntity_SetOwner);
+ tolua_function(tolua_S, "GetOwnerUUID", tolua_cMobHeadEntity_GetOwnerUUID);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cMojangAPI");
tolua_function(tolua_S, "AddPlayerNameToUUIDMapping", tolua_cMojangAPI_AddPlayerNameToUUIDMapping);
tolua_function(tolua_S, "GetPlayerNameFromUUID", tolua_cMojangAPI_GetPlayerNameFromUUID);
@@ -3894,6 +4116,7 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions);
tolua_function(tolua_S, "GetRestrictions", tolua_cPlayer_GetRestrictions);
tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches);
+ tolua_function(tolua_S, "GetUUID", tolua_cPlayer_GetUUID);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cPlugin");
@@ -3923,8 +4146,8 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cRoot");
+ tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_cRoot_DoWithPlayerByUUID);
tolua_function(tolua_S, "FindAndDoWithPlayer", DoWith <cRoot, cPlayer, &cRoot::FindAndDoWithPlayer>);
- tolua_function(tolua_S, "DoWithPlayerByUUID", DoWith <cRoot, cPlayer, &cRoot::DoWithPlayerByUUID>);
tolua_function(tolua_S, "ForEachPlayer", ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>);
tolua_function(tolua_S, "ForEachWorld", ForEach<cRoot, cWorld, &cRoot::ForEachWorld>);
tolua_function(tolua_S, "GetBrewingRecipe", tolua_cRoot_GetBrewingRecipe);
diff --git a/src/Bindings/ManualBindings_RankManager.cpp b/src/Bindings/ManualBindings_RankManager.cpp
index 84ca67c4e..d24685d2b 100644
--- a/src/Bindings/ManualBindings_RankManager.cpp
+++ b/src/Bindings/ManualBindings_RankManager.cpp
@@ -8,6 +8,7 @@
#include "../Root.h"
#include "tolua++/include/tolua++.h"
#include "LuaState.h"
+#include "UUID.h"
@@ -266,7 +267,7 @@ static int tolua_cRankManager_GetAllPlayerUUIDs(lua_State * L)
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamStaticSelf("cRankManager") ||
!S.CheckParamEnd(2)
)
{
@@ -274,10 +275,18 @@ static int tolua_cRankManager_GetAllPlayerUUIDs(lua_State * L)
}
// Get the player uuid's:
- AStringVector Players = cRoot::Get()->GetRankManager()->GetAllPlayerUUIDs();
+ std::vector<cUUID> Players = cRoot::Get()->GetRankManager()->GetAllPlayerUUIDs();
+
+ // Convert to string UUIDs
+ std::vector<AString> StrUUIDs;
+ StrUUIDs.reserve(Players.size());
+ for (const auto & UUID : Players)
+ {
+ StrUUIDs.push_back(UUID.ToShortString());
+ }
// Push the results:
- S.Push(Players);
+ S.Push(StrUUIDs);
return 1;
}
@@ -430,8 +439,8 @@ static int tolua_cRankManager_GetPlayerGroups(lua_State * L)
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cRankManager") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamStaticSelf("cRankManager") ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(3)
)
{
@@ -439,7 +448,7 @@ static int tolua_cRankManager_GetPlayerGroups(lua_State * L)
}
// Get the params:
- AString PlayerUUID;
+ cUUID PlayerUUID;
S.GetStackValue(2, PlayerUUID);
// Get the groups:
@@ -462,8 +471,8 @@ static int tolua_cRankManager_GetPlayerMsgVisuals(lua_State * L)
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cRankManager") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamStaticSelf("cRankManager") ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(3)
)
{
@@ -471,7 +480,7 @@ static int tolua_cRankManager_GetPlayerMsgVisuals(lua_State * L)
}
// Get the params:
- AString PlayerUUID;
+ cUUID PlayerUUID;
S.GetStackValue(2, PlayerUUID);
// Get the permissions:
@@ -498,8 +507,8 @@ static int tolua_cRankManager_GetPlayerPermissions(lua_State * L)
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cRankManager") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamStaticSelf("cRankManager") ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(3)
)
{
@@ -507,7 +516,7 @@ static int tolua_cRankManager_GetPlayerPermissions(lua_State * L)
}
// Get the params:
- AString PlayerUUID;
+ cUUID PlayerUUID;
S.GetStackValue(2, PlayerUUID);
// Get the permissions:
@@ -530,8 +539,8 @@ static int tolua_cRankManager_GetPlayerRestrictions(lua_State * L)
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cRankManager") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamStaticSelf("cRankManager") ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(3)
)
{
@@ -539,7 +548,7 @@ static int tolua_cRankManager_GetPlayerRestrictions(lua_State * L)
}
// Get the params:
- AString PlayerUUID;
+ cUUID PlayerUUID;
S.GetStackValue(2, PlayerUUID);
// Get the permissions:
@@ -562,8 +571,8 @@ static int tolua_cRankManager_GetPlayerRankName(lua_State * L)
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cRankManager") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamStaticSelf("cRankManager") ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(3)
)
{
@@ -571,7 +580,7 @@ static int tolua_cRankManager_GetPlayerRankName(lua_State * L)
}
// Get the params:
- AString PlayerUUID;
+ cUUID PlayerUUID;
S.GetStackValue(2, PlayerUUID);
// Get the rank name:
@@ -594,8 +603,8 @@ static int tolua_cRankManager_GetPlayerName(lua_State * L)
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cRankManager") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamStaticSelf("cRankManager") ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(3)
)
{
@@ -603,7 +612,7 @@ static int tolua_cRankManager_GetPlayerName(lua_State * L)
}
// Get the params:
- AString PlayerUUID;
+ cUUID PlayerUUID;
S.GetStackValue(2, PlayerUUID);
// Get the player name:
@@ -887,8 +896,8 @@ static int tolua_cRankManager_IsPlayerRankSet(lua_State * L)
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cRankManager") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamStaticSelf("cRankManager") ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(3)
)
{
@@ -896,7 +905,7 @@ static int tolua_cRankManager_IsPlayerRankSet(lua_State * L)
}
// Get the params:
- AString PlayerUUID;
+ cUUID PlayerUUID;
S.GetStackValue(2, PlayerUUID);
// Get the response:
@@ -1067,8 +1076,8 @@ static int tolua_cRankManager_RemovePlayerRank(lua_State * L)
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cRankManager") ||
- !S.CheckParamString(2) ||
+ !S.CheckParamStaticSelf("cRankManager") ||
+ !S.CheckParamUUID(2) ||
!S.CheckParamEnd(3)
)
{
@@ -1076,7 +1085,7 @@ static int tolua_cRankManager_RemovePlayerRank(lua_State * L)
}
// Get the params:
- AString PlayerUUID;
+ cUUID PlayerUUID;
S.GetStackValue(2, PlayerUUID);
// Remove the player's rank:
@@ -1219,8 +1228,9 @@ static int tolua_cRankManager_SetPlayerRank(lua_State * L)
cLuaState S(L);
if (
- !S.CheckParamUserTable(1, "cRankManager") ||
- !S.CheckParamString(2, 4) ||
+ !S.CheckParamStaticSelf("cRankManager") ||
+ !S.CheckParamUUID(2) ||
+ !S.CheckParamString(3, 4) ||
!S.CheckParamEnd(5)
)
{
@@ -1228,7 +1238,8 @@ static int tolua_cRankManager_SetPlayerRank(lua_State * L)
}
// Get the params:
- AString PlayerUUID, PlayerName, RankName;
+ AString PlayerName, RankName;
+ cUUID PlayerUUID;
S.GetStackValues(2, PlayerUUID, PlayerName, RankName);
// Set the rank:
diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp
index 88e3917da..10b5daf1e 100644
--- a/src/Bindings/ManualBindings_World.cpp
+++ b/src/Bindings/ManualBindings_World.cpp
@@ -7,6 +7,7 @@
#include "tolua++/include/tolua++.h"
#include "../World.h"
#include "../Broadcaster.h"
+#include "../UUID.h"
#include "ManualBindings.h"
#include "LuaState.h"
#include "PluginLua.h"
@@ -206,6 +207,53 @@ static int tolua_cWorld_DoExplosionAt(lua_State * tolua_S)
+static int tolua_cWorld_DoWithPlayerByUUID(lua_State * tolua_S)
+{
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamSelf("cWorld") ||
+ !L.CheckParamUUID(2) ||
+ !L.CheckParamFunction(3) ||
+ !L.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get parameters:
+ cWorld * Self;
+ cUUID PlayerUUID;
+ cLuaState::cRef FnRef;
+ L.GetStackValues(1, Self, PlayerUUID, FnRef);
+
+ if (PlayerUUID.IsNil())
+ {
+ return L.ApiParamError("Expected a non-nil UUID for parameter #1");
+ }
+ if (!FnRef.IsValid())
+ {
+ return L.ApiParamError("Expected a valid callback function for parameter #2");
+ }
+
+ // Call the function:
+ bool res = Self->DoWithPlayerByUUID(PlayerUUID, [&](cPlayer * a_Player)
+ {
+ bool ret = false;
+ L.Call(FnRef, a_Player, cLuaState::Return, ret);
+ return ret;
+ }
+ );
+
+ // Push the result as the return value:
+ L.Push(res);
+ return 1;
+}
+
+
+
+
+
static int tolua_cWorld_ForEachLoadedChunk(lua_State * tolua_S)
{
// Exported manually, because tolua doesn't support converting functions to functor types.
@@ -640,7 +688,7 @@ void cManualBindings::BindWorld(lua_State * tolua_S)
tolua_function(tolua_S, "DoWithMobHeadAt", DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadAt>);
tolua_function(tolua_S, "DoWithNoteBlockAt", DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
tolua_function(tolua_S, "DoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
- tolua_function(tolua_S, "DoWithPlayerByUUID", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayerByUUID>);
+ tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_cWorld_DoWithPlayerByUUID);
tolua_function(tolua_S, "FindAndDoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
tolua_function(tolua_S, "ForEachBlockEntityInChunk", ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
tolua_function(tolua_S, "ForEachBrewingstandInChunk", ForEachInChunk<cWorld, cBrewingstandEntity, &cWorld::ForEachBrewingstandInChunk>);
diff --git a/src/BlockEntities/MobHeadEntity.cpp b/src/BlockEntities/MobHeadEntity.cpp
index 3bc7839b7..28e9febed 100644
--- a/src/BlockEntities/MobHeadEntity.cpp
+++ b/src/BlockEntities/MobHeadEntity.cpp
@@ -54,7 +54,8 @@ void cMobHeadEntity::SetType(const eMobHeadType & a_Type)
{
if ((!m_OwnerName.empty()) && (a_Type != SKULL_TYPE_PLAYER))
{
- m_OwnerName = m_OwnerUUID = m_OwnerTexture = m_OwnerTextureSignature = "";
+ m_OwnerName = m_OwnerTexture = m_OwnerTextureSignature = "";
+ m_OwnerUUID = cUUID{};
}
m_Type = a_Type;
m_World->BroadcastBlockEntity(m_PosX, m_PosY, m_PosZ);
@@ -102,7 +103,7 @@ void cMobHeadEntity::SetOwner(const cPlayer & a_Owner)
-void cMobHeadEntity::SetOwner(const AString & a_OwnerUUID, const AString & a_OwnerName, const AString & a_OwnerTexture, const AString & a_OwnerTextureSignature)
+void cMobHeadEntity::SetOwner(const cUUID & a_OwnerUUID, const AString & a_OwnerName, const AString & a_OwnerTexture, const AString & a_OwnerTextureSignature)
{
if (m_Type != SKULL_TYPE_PLAYER)
{
diff --git a/src/BlockEntities/MobHeadEntity.h b/src/BlockEntities/MobHeadEntity.h
index c8bfeb357..fe0ae71e4 100644
--- a/src/BlockEntities/MobHeadEntity.h
+++ b/src/BlockEntities/MobHeadEntity.h
@@ -10,6 +10,7 @@
#include "BlockEntity.h"
#include "Defines.h"
+#include "UUID.h"
@@ -42,9 +43,6 @@ public:
/** Set the player for mob heads with player type */
void SetOwner(const cPlayer & a_Owner);
- /** Sets the player components for the mob heads with player type. */
- void SetOwner(const AString & a_OwnerUUID, const AString & a_OwnerName, const AString & a_OwnerTexture, const AString & a_OwnerTextureSignature);
-
/** Returns the type of the mob head */
eMobHeadType GetType(void) const { return m_Type; }
@@ -54,9 +52,6 @@ public:
/** Returns the player name of the mob head */
AString GetOwnerName(void) const { return m_OwnerName; }
- /** Returns the player UUID of the mob head */
- AString GetOwnerUUID(void) const { return m_OwnerUUID; }
-
/** Returns the texture of the mob head */
AString GetOwnerTexture(void) const { return m_OwnerTexture; }
@@ -65,6 +60,15 @@ public:
// tolua_end
+ /** Sets the player components for the mob heads with player type. */
+ void SetOwner(
+ const cUUID & a_OwnerUUID, const AString & a_OwnerName,
+ const AString & a_OwnerTexture, const AString & a_OwnerTextureSignature
+ ); // Exported in ManualBindings.cpp
+
+ /** Returns the player UUID of the mob head */
+ cUUID GetOwnerUUID(void) const { return m_OwnerUUID; } // Exported in ManualBindings.cpp
+
// cBlockEntity overrides:
virtual void CopyFrom(const cBlockEntity & a_Src) override;
virtual bool UsedBy(cPlayer * a_Player) override;
@@ -76,7 +80,7 @@ private:
eMobHeadRotation m_Rotation;
AString m_OwnerName;
- AString m_OwnerUUID;
+ cUUID m_OwnerUUID;
AString m_OwnerTexture;
AString m_OwnerTextureSignature;
} ; // tolua_export
diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp
index 82f89a497..77572a254 100644
--- a/src/Blocks/BlockBed.cpp
+++ b/src/Blocks/BlockBed.cpp
@@ -209,7 +209,7 @@ void cBlockBedHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWor
-void cBlockBedHandler::ConvertToPickups(cEntity * a_Digger, cItems & a_Pickups, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ)
+void cBlockBedHandler::ConvertToPickups(cWorldInterface & a_WorldInterface, cItems & a_Pickups, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ)
{
class cBedColor :
public cBedCallback
@@ -224,6 +224,6 @@ void cBlockBedHandler::ConvertToPickups(cEntity * a_Digger, cItems & a_Pickups,
}
};
cBedColor BedCallback;
- a_Digger->GetWorld()->DoWithBedAt(a_BlockX, a_BlockY, a_BlockZ, BedCallback);
+ a_WorldInterface.DoWithBedAt(a_BlockX, a_BlockY, a_BlockZ, BedCallback);
a_Pickups.Add(cItem(E_ITEM_BED, 1, BedCallback.m_Color));
}
diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h
index f2cbfde18..a5b256b0b 100644
--- a/src/Blocks/BlockBed.h
+++ b/src/Blocks/BlockBed.h
@@ -35,7 +35,7 @@ public:
virtual void ConvertToPickups(cItems & Pickups, NIBBLETYPE Meta) override {}
- virtual void ConvertToPickups(cEntity * a_Digger, cItems & a_Pickups, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void ConvertToPickups(cWorldInterface & a_WorldInterface, cItems & a_Pickups, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ) override;
// Bed specific helper functions
static NIBBLETYPE RotationToMetaData(double a_Rotation)
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index 4c2209383..76cfc763f 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -523,7 +523,7 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac
case E_BLOCK_BED:
{
// Need to access the bed entity to get the color for the item damage
- ConvertToPickups(a_Digger, Pickups, Meta, a_BlockX, a_BlockY, a_BlockZ);
+ ConvertToPickups(a_WorldInterface, Pickups, Meta, a_BlockX, a_BlockY, a_BlockZ);
}
default: Pickups.Add(m_BlockType, 1, Meta); break;
}
@@ -531,7 +531,7 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac
else if (m_BlockType == E_BLOCK_BED)
{
// Need to access the bed entity to get the color for the item damage
- ConvertToPickups(a_Digger, Pickups, Meta, a_BlockX, a_BlockY, a_BlockZ);
+ ConvertToPickups(a_WorldInterface, Pickups, Meta, a_BlockX, a_BlockY, a_BlockZ);
}
else
{
diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h
index b96b3b770..1638abf8f 100644
--- a/src/Blocks/BlockHandler.h
+++ b/src/Blocks/BlockHandler.h
@@ -88,8 +88,8 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta);
/** Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents.
- Overloaded method with coords and digger, for blocks that needs to access the block entity, e.g. a bed */
- virtual void ConvertToPickups(cEntity * a_Digger, cItems & a_Pickups, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ) {}
+ Overloaded method with coords and world interface for blocks that needs to access the block entity, e.g. a bed. */
+ virtual void ConvertToPickups(cWorldInterface & a_WorldInterface, cItems & a_Pickups, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ) {}
/** Handles the dropping, but not destruction, of a block based on what ConvertTo(Verbatim)Pickups() returns, including the spawning of pickups and alertion of plugins
@param a_Digger The entity causing the drop; it may be nullptr
diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp
index eda155265..56d335044 100644
--- a/src/Blocks/BlockPiston.cpp
+++ b/src/Blocks/BlockPiston.cpp
@@ -115,9 +115,7 @@ void cBlockPistonHandler::PushBlocks(
for (auto & moveBlockPos : sortedBlocks)
{
a_World.GetBlockTypeMeta(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, moveBlock, moveMeta);
- a_World.SetBlock(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, E_BLOCK_AIR, 0);
- moveBlockPos += a_PushDir;
if (cBlockInfo::IsPistonBreakable(moveBlock))
{
// Block is breakable, drop it
@@ -130,10 +128,13 @@ void cBlockPistonHandler::PushBlocks(
moveBlockPos.x, moveBlockPos.y, moveBlockPos.z
);
}
+ a_World.SetBlock(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, E_BLOCK_AIR, 0);
}
else
{
// Not breakable, just move it
+ a_World.SetBlock(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, E_BLOCK_AIR, 0);
+ moveBlockPos += a_PushDir;
a_World.SetBlock(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, moveBlock, moveMeta);
}
}
diff --git a/src/Blocks/WorldInterface.h b/src/Blocks/WorldInterface.h
index 0c92a64e5..524e8d642 100644
--- a/src/Blocks/WorldInterface.h
+++ b/src/Blocks/WorldInterface.h
@@ -28,6 +28,8 @@ public:
virtual void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData) = 0;
+ virtual bool DoWithBedAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBedCallback & a_Callback) = 0;
+
/** Spawns item pickups for each item in the list. May compress pickups if too many entities: */
virtual void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0, bool IsPlayerCreated = false) = 0;
diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp
index 1a4dc8bb8..5b4cdb745 100644
--- a/src/ByteBuffer.cpp
+++ b/src/ByteBuffer.cpp
@@ -7,6 +7,7 @@
#include "ByteBuffer.h"
#include "Endianness.h"
+#include "UUID.h"
#include "OSSupport/IsThread.h"
@@ -32,16 +33,6 @@ Unfortunately it is very slow, so it is disabled even for regular DEBUG builds.
-static char ValueToHexDigit(UInt8 digit)
-{
- ASSERT(digit < 16);
- return "0123456789abcdef"[digit];
-}
-
-
-
-
-
#ifdef DEBUG_SINGLE_THREAD_ACCESS
/** Simple RAII class that is used for checking that no two threads are using an object simultanously.
@@ -506,22 +497,17 @@ bool cByteBuffer::ReadPosition64(int & a_BlockX, int & a_BlockY, int & a_BlockZ)
-bool cByteBuffer::ReadUUID(AString & a_Value)
+bool cByteBuffer::ReadUUID(cUUID & a_Value)
{
CHECK_THREAD
- if (!ReadString(a_Value, 16))
+ std::array<Byte, 16> UUIDBuf;
+ if (!ReadBuf(UUIDBuf.data(), UUIDBuf.size()))
{
return false;
}
- a_Value.resize(32);
- for (unsigned int i = 15; i < 16; i--)
- {
- a_Value[i * 2 + 1] = ValueToHexDigit(a_Value[i] & 0xf);
- a_Value[i * 2] = ValueToHexDigit(static_cast<UInt8>(a_Value[i]) >> 4);
- }
-
+ a_Value.FromRaw(UUIDBuf);
return true;
}
diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h
index 761112721..e47f665a5 100644
--- a/src/ByteBuffer.h
+++ b/src/ByteBuffer.h
@@ -11,6 +11,8 @@
+// fwd:
+class cUUID;
/** An object that can store incoming bytes and lets its clients read the bytes sequentially
@@ -68,7 +70,7 @@ public:
bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8
bool ReadLEInt (int & a_Value);
bool ReadPosition64 (int & a_BlockX, int & a_BlockY, int & a_BlockZ);
- bool ReadUUID (AString & a_Value); // UUID without dashes
+ bool ReadUUID (cUUID & a_Value);
/** Reads VarInt, assigns it to anything that can be assigned from an UInt64 (unsigned short, char, Byte, double, ...) */
template <typename T> bool ReadVarInt(T & a_Value)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4b3c71931..080cca119 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -69,6 +69,7 @@ SET (SRCS
StringCompression.cpp
StringUtils.cpp
Tracer.cpp
+ UUID.cpp
VoronoiMap.cpp
WebAdmin.cpp
World.cpp
@@ -147,6 +148,7 @@ SET (HDRS
StringCompression.h
StringUtils.h
Tracer.h
+ UUID.h
Vector3.h
VoronoiMap.h
WebAdmin.h
diff --git a/src/ChunkData.cpp b/src/ChunkData.cpp
index c2b09bafb..310945f17 100644
--- a/src/ChunkData.cpp
+++ b/src/ChunkData.cpp
@@ -422,6 +422,129 @@ void cChunkData::CopySkyLight(NIBBLETYPE * a_Dest) const
+void cChunkData::FillBlockTypes(BLOCKTYPE a_Value)
+{
+ // If needed, allocate any missing sections
+ if (a_Value != 0x00)
+ {
+ for (auto & Section : m_Sections)
+ {
+ if (Section == nullptr)
+ {
+ Section = Allocate();
+ std::fill(std::begin(Section->m_BlockMetas), std::end(Section->m_BlockMetas), 0x00);
+ std::fill(std::begin(Section->m_BlockLight), std::end(Section->m_BlockLight), 0x00);
+ std::fill(std::begin(Section->m_BlockSkyLight), std::end(Section->m_BlockSkyLight), 0xff);
+ }
+ }
+ }
+
+ for (auto Section : m_Sections)
+ {
+ if (Section != nullptr)
+ {
+ std::fill(std::begin(Section->m_BlockTypes), std::end(Section->m_BlockTypes), a_Value);
+ }
+ }
+}
+
+
+
+
+
+void cChunkData::FillMetas(NIBBLETYPE a_Value)
+{
+ // If needed, allocate any missing sections
+ if (a_Value != 0x00)
+ {
+ for (auto & Section : m_Sections)
+ {
+ if (Section == nullptr)
+ {
+ Section = Allocate();
+ std::fill(std::begin(Section->m_BlockTypes), std::end(Section->m_BlockTypes), 0x00);
+ std::fill(std::begin(Section->m_BlockLight), std::end(Section->m_BlockLight), 0x00);
+ std::fill(std::begin(Section->m_BlockSkyLight), std::end(Section->m_BlockSkyLight), 0xff);
+ }
+ }
+ }
+
+ NIBBLETYPE NewMeta = static_cast<NIBBLETYPE>((a_Value << 4) | a_Value);
+ for (auto Section : m_Sections)
+ {
+ if (Section != nullptr)
+ {
+ std::fill(std::begin(Section->m_BlockMetas), std::end(Section->m_BlockMetas), NewMeta);
+ }
+ }
+}
+
+
+
+
+
+void cChunkData::FillBlockLight(NIBBLETYPE a_Value)
+{
+ // If needed, allocate any missing sections
+ if (a_Value != 0x00)
+ {
+ for (auto & Section : m_Sections)
+ {
+ if (Section == nullptr)
+ {
+ Section = Allocate();
+ std::fill(std::begin(Section->m_BlockTypes), std::end(Section->m_BlockTypes), 0x00);
+ std::fill(std::begin(Section->m_BlockMetas), std::end(Section->m_BlockMetas), 0x00);
+ std::fill(std::begin(Section->m_BlockSkyLight), std::end(Section->m_BlockSkyLight), 0xff);
+ }
+ }
+ }
+
+ NIBBLETYPE NewLight = static_cast<NIBBLETYPE>((a_Value << 4) | a_Value);
+ for (auto Section : m_Sections)
+ {
+ if (Section != nullptr)
+ {
+ std::fill(std::begin(Section->m_BlockLight), std::end(Section->m_BlockLight), NewLight);
+ }
+ }
+}
+
+
+
+
+
+void cChunkData::FillSkyLight(NIBBLETYPE a_Value)
+{
+ // If needed, allocate any missing sections
+ if (a_Value != 0x0f)
+ {
+ for (auto & Section : m_Sections)
+ {
+ if (Section == nullptr)
+ {
+ Section = Allocate();
+ std::fill(std::begin(Section->m_BlockTypes), std::end(Section->m_BlockTypes), 0x00);
+ std::fill(std::begin(Section->m_BlockMetas), std::end(Section->m_BlockMetas), 0x00);
+ std::fill(std::begin(Section->m_BlockLight), std::end(Section->m_BlockLight), 0x00);
+ }
+ }
+ }
+
+ NIBBLETYPE NewSkyLight = static_cast<NIBBLETYPE>((a_Value << 4) | a_Value);
+ for (auto Section : m_Sections)
+ {
+ if (Section != nullptr)
+ {
+ std::fill(std::begin(Section->m_BlockSkyLight), std::end(Section->m_BlockSkyLight), NewSkyLight);
+ }
+ }
+}
+
+
+
+
+
void cChunkData::SetBlockTypes(const BLOCKTYPE * a_Src)
{
ASSERT(a_Src != nullptr);
diff --git a/src/ChunkData.h b/src/ChunkData.h
index 49a03fb4a..6bc2b5366 100644
--- a/src/ChunkData.h
+++ b/src/ChunkData.h
@@ -80,6 +80,18 @@ public:
/** Copies the skylight data into the specified flat array. */
void CopySkyLight (NIBBLETYPE * a_Dest) const;
+ /** Fills the chunk with the specified block. */
+ void FillBlockTypes(BLOCKTYPE a_Value);
+
+ /** Fills the chunk with the specified meta value. */
+ void FillMetas (NIBBLETYPE a_Value);
+
+ /** Fills the chunk with the specified block light. */
+ void FillBlockLight(NIBBLETYPE a_Value);
+
+ /** Fills the chunk with the specified sky light. */
+ void FillSkyLight (NIBBLETYPE a_Value);
+
/** Copies the blocktype data from the specified flat array into the internal representation.
Allocates sections that are needed for the operation.
Requires that a_Src is a valid pointer. */
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 5fc659fe1..caa2d8fd8 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -274,55 +274,28 @@ AString cClientHandle::FormatMessageType(bool ShouldAppendChatPrefixes, eMessage
-AString cClientHandle::GenerateOfflineUUID(const AString & a_Username)
+cUUID cClientHandle::GenerateOfflineUUID(const AString & a_Username)
{
// Online UUIDs are always version 4 (random)
// We use Version 3 (MD5 hash) UUIDs for the offline UUIDs
// This guarantees that they will never collide with an online UUID and can be distinguished.
- // Proper format for a version 3 UUID is:
- // xxxxxxxx-xxxx-3xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal digit and y is one of 8, 9, A, or B
- // Note that we generate a short UUID (without the dashes)
// First make the username lowercase:
AString lcUsername = StrToLower(a_Username);
- // Generate an md5 checksum, and use it as base for the ID:
- unsigned char MD5[16];
- md5(reinterpret_cast<const unsigned char *>(lcUsername.c_str()), lcUsername.length(), MD5);
- MD5[6] &= 0x0f; // Need to trim to 4 bits only...
- MD5[8] &= 0x0f; // ... otherwise %01x overflows into two chars
- return Printf("%02x%02x%02x%02x%02x%02x3%01x%02x8%01x%02x%02x%02x%02x%02x%02x%02x",
- MD5[0], MD5[1], MD5[2], MD5[3],
- MD5[4], MD5[5], MD5[6], MD5[7],
- MD5[8], MD5[9], MD5[10], MD5[11],
- MD5[12], MD5[13], MD5[14], MD5[15]
- );
+ return cUUID::GenerateVersion3(lcUsername);
}
-bool cClientHandle::IsUUIDOnline(const AString & a_UUID)
+bool cClientHandle::IsUUIDOnline(const cUUID & a_UUID)
{
// Online UUIDs are always version 4 (random)
// We use Version 3 (MD5 hash) UUIDs for the offline UUIDs
// This guarantees that they will never collide with an online UUID and can be distinguished.
- // The version-specifying char is at pos #12 of raw UUID, pos #14 in dashed-UUID.
- switch (a_UUID.size())
- {
- case 32:
- {
- // This is the UUID format without dashes, the version char is at pos #12:
- return (a_UUID[12] == '4');
- }
- case 36:
- {
- // This is the UUID format with dashes, the version char is at pos #14:
- return (a_UUID[14] == '4');
- }
- }
- return false;
+ return (a_UUID.Version() == 4);
}
@@ -342,7 +315,7 @@ void cClientHandle::Kick(const AString & a_Reason)
-void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties)
+void cClientHandle::Authenticate(const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties)
{
// Atomically increment player count (in server thread)
cRoot::Get()->GetServer()->PlayerCreated();
@@ -366,7 +339,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
m_Username = a_Name;
// Only assign UUID and properties if not already pre-assigned (BungeeCord sends those in the Handshake packet):
- if (m_UUID.empty())
+ if (m_UUID.IsNil())
{
m_UUID = a_UUID;
}
@@ -1661,7 +1634,7 @@ void cClientHandle::HandleSlotSelected(Int16 a_SlotNum)
-void cClientHandle::HandleSpectate(const AString & a_PlayerUUID)
+void cClientHandle::HandleSpectate(const cUUID & a_PlayerUUID)
{
m_Player->GetWorld()->DoWithPlayerByUUID(a_PlayerUUID, [=](cPlayer * a_ToSpectate)
{
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 2a661e283..09188f2ae 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -16,6 +16,7 @@
#include "json/json.h"
#include "ChunkSender.h"
#include "EffectID.h"
+#include "UUID.h"
@@ -72,13 +73,13 @@ public: // tolua_export
cPlayer * GetPlayer(void) { return m_Player; } // tolua_export
- /** Returns the player's UUID, as used by the protocol, in the short form (no dashes) */
- const AString & GetUUID(void) const { return m_UUID; } // tolua_export
+ /** Returns the player's UUID, as used by the protocol */
+ const cUUID & GetUUID(void) const { return m_UUID; } // Exported in ManualBindings.cpp
- /** Sets the player's UUID, as used by the protocol. Short UUID form (no dashes) is expected.
+ /** Sets the player's UUID, as used by the protocol.
Used mainly by BungeeCord compatibility code - when authenticating is done on the BungeeCord server
and the results are passed to MCS running in offline mode. */
- void SetUUID(const AString & a_UUID) { ASSERT(a_UUID.size() == 32); m_UUID = a_UUID; }
+ void SetUUID(const cUUID & a_UUID) { ASSERT(!a_UUID.IsNil()); m_UUID = a_UUID; }
const Json::Value & GetProperties(void) const { return m_Properties; }
@@ -95,15 +96,12 @@ public: // tolua_export
/** Generates an UUID based on the player name provided.
This is used for the offline (non-auth) mode, when there's no UUID source.
- Each username generates a unique and constant UUID, so that when the player reconnects with the same name, their UUID is the same.
- Returns a 32-char UUID (no dashes). */
- static AString GenerateOfflineUUID(const AString & a_Username); // tolua_export
+ Each username generates a unique and constant UUID, so that when the player reconnects with the same name, their UUID is the same. */
+ static cUUID GenerateOfflineUUID(const AString & a_Username); // Exported in ManualBindings.cpp
/** Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID.
- We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart.
- Accepts both 32-char and 36-char UUIDs (with and without dashes).
- If the string given is not a valid UUID, returns false. */
- static bool IsUUIDOnline(const AString & a_UUID); // tolua_export
+ We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart. */
+ static bool IsUUIDOnline(const cUUID & a_UUID); // Exported in ManualBindings.cpp
/** Formats the type of message with the proper color and prefix for sending to the client. */
static AString FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString & a_AdditionalData);
@@ -113,7 +111,7 @@ public: // tolua_export
void Kick(const AString & a_Reason); // tolua_export
/** Authenticates the specified user, called by cAuthenticator */
- void Authenticate(const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties);
+ void Authenticate(const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties);
/** This function sends a new unloaded chunk to the player. Returns true if all chunks are loaded. */
bool StreamNextChunk();
@@ -336,7 +334,7 @@ public: // tolua_export
void HandleRespawn (void);
void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem);
void HandleSlotSelected (Int16 a_SlotNum);
- void HandleSpectate (const AString & a_PlayerUUID);
+ void HandleSpectate (const cUUID & a_PlayerUUID);
void HandleSteerVehicle (float Forward, float Sideways);
void HandleTabCompletion (const AString & a_Text);
void HandleUpdateSign (
@@ -498,8 +496,8 @@ private:
/** ID used for identification during authenticating. Assigned sequentially for each new instance. */
int m_UniqueID;
- /** Contains the UUID used by Mojang to identify the player's account. Short UUID stored here (without dashes) */
- AString m_UUID;
+ /** Contains the UUID used by Mojang to identify the player's account. */
+ cUUID m_UUID;
/** Set to true when the chunk where the player is is sent to the client. Used for spawning the player */
bool m_HasSentPlayerChunk;
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 4f35315f7..347c0ec91 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -56,8 +56,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
m_TicksSinceLastVoidDamage(0),
m_IsSwimming(false),
m_IsSubmerged(false),
- m_AirLevel(0),
- m_AirTickTimer(0),
+ m_AirLevel(MAX_AIR_LEVEL),
+ m_AirTickTimer(DROWNING_TICKS),
m_TicksAlive(0),
m_IsTicking(false),
m_ParentChunk(nullptr),
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 60da8c758..fb2274cad 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -84,7 +84,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) :
m_bIsInBed(false),
m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL),
m_bIsTeleporting(false),
- m_UUID((a_Client != nullptr) ? a_Client->GetUUID() : ""),
+ m_UUID((a_Client != nullptr) ? a_Client->GetUUID() : cUUID{}),
m_CustomName(""),
m_SkinParts(0),
m_MainHand(mhRight)
@@ -2077,7 +2077,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
}
// Load from the offline UUID file, if allowed:
- AString OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName());
+ cUUID OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName());
const char * OfflineUsage = " (unused)";
if (cRoot::Get()->GetServer()->ShouldLoadOfflinePlayerData())
{
@@ -2105,7 +2105,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
// None of the files loaded successfully
LOG("Player data file not found for %s (%s, offline %s%s), will be reset to defaults.",
- GetName().c_str(), m_UUID.c_str(), OfflineUUID.c_str(), OfflineUsage
+ GetName().c_str(), m_UUID.ToShortString().c_str(), OfflineUUID.ToShortString().c_str(), OfflineUsage
);
if (a_World == nullptr)
@@ -2220,7 +2220,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
bool cPlayer::SaveToDisk()
{
cFile::CreateFolder(FILE_IO_PREFIX + AString("players/")); // Create the "players" folder, if it doesn't exist yet (#1268)
- cFile::CreateFolder(FILE_IO_PREFIX + AString("players/") + m_UUID.substr(0, 2));
+ cFile::CreateFolder(FILE_IO_PREFIX + AString("players/") + m_UUID.ToShortString().substr(0, 2));
// create the JSON data
Json::Value JSON_PlayerPosition;
@@ -2879,10 +2879,9 @@ void cPlayer::RemoveClientHandle(void)
-AString cPlayer::GetUUIDFileName(const AString & a_UUID)
+AString cPlayer::GetUUIDFileName(const cUUID & a_UUID)
{
- AString UUID = cMojangAPI::MakeUUIDDashed(a_UUID);
- ASSERT(UUID.length() == 36);
+ AString UUID = a_UUID.ToLongString();
AString res("players/");
res.append(UUID, 0, 2);
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 5b0aa84a8..5c08151c8 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -8,6 +8,8 @@
#include "../Statistics.h"
+#include "../UUID.h"
+
@@ -488,14 +490,14 @@ public:
/** Whether placing the given blocks would intersect any entitiy */
bool DoesPlacingBlocksIntersectEntity(const sSetBlockVector & a_Blocks);
+ /** Returns the UUID that has been read from the client, or nil if not available. */
+ const cUUID & GetUUID(void) const { return m_UUID; } // Exported in ManualBindings.cpp
+
// tolua_begin
/** Returns wheter the player can fly or not. */
virtual bool CanFly(void) const { return m_CanFly; }
- /** Returns the UUID (short format) that has been read from the client, or empty string if not available. */
- const AString & GetUUID(void) const { return m_UUID; }
-
/** (Re)loads the rank and permissions from the cRankManager.
Expects the m_UUID member to be valid.
Loads the m_Rank, m_Permissions, m_MsgPrefix, m_MsgSuffix and m_MsgNameColorCode members. */
@@ -694,9 +696,9 @@ protected:
*/
bool m_bIsTeleporting;
- /** The short UUID (no dashes) of the player, as read from the ClientHandle.
- If no ClientHandle is given, the UUID is initialized to empty. */
- AString m_UUID;
+ /** The UUID of the player, as read from the ClientHandle.
+ If no ClientHandle is given, the UUID is nil. */
+ cUUID m_UUID;
AString m_CustomName;
@@ -731,7 +733,7 @@ protected:
/** Returns the filename for the player data based on the UUID given.
This can be used both for online and offline UUIDs. */
- AString GetUUIDFileName(const AString & a_UUID);
+ AString GetUUIDFileName(const cUUID & a_UUID);
private:
diff --git a/src/Mobs/Guardian.cpp b/src/Mobs/Guardian.cpp
index 3dbec9928..f36f98ea8 100644
--- a/src/Mobs/Guardian.cpp
+++ b/src/Mobs/Guardian.cpp
@@ -42,20 +42,11 @@ void cGuardian::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
// that is not where the entity currently resides (FS #411)
Vector3d Pos = GetPosition();
- // TODO: Not a real behavior, but cool :D
int RelY = FloorC(Pos.y);
if ((RelY < 0) || (RelY >= cChunkDef::Height))
{
return;
}
- int RelX = FloorC(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width;
- int RelZ = FloorC(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width;
- BLOCKTYPE BlockType;
- if (a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockType) && !IsBlockWater(BlockType) && !IsOnFire())
- {
- // Burn for 10 ticks, then decide again
- StartBurning(10);
- }
super::Tick(a_Dt, a_Chunk);
}
diff --git a/src/Mobs/Ocelot.h b/src/Mobs/Ocelot.h
index 59b4f25af..75758a973 100644
--- a/src/Mobs/Ocelot.h
+++ b/src/Mobs/Ocelot.h
@@ -2,6 +2,7 @@
#pragma once
#include "PassiveMonster.h"
+#include "../UUID.h"
@@ -40,14 +41,14 @@ public:
bool IsTame (void) const override { return m_IsTame; }
bool IsBegging (void) const { return m_IsBegging; }
AString GetOwnerName (void) const { return m_OwnerName; }
- AString GetOwnerUUID (void) const { return m_OwnerUUID; }
+ cUUID GetOwnerUUID (void) const { return m_OwnerUUID; }
eCatType GetOcelotType (void) const { return m_CatType; }
// Set functions
void SetIsSitting (bool a_IsSitting) { m_IsSitting = a_IsSitting; }
void SetIsTame (bool a_IsTame) { m_IsTame = a_IsTame; }
void SetIsBegging (bool a_IsBegging) { m_IsBegging = a_IsBegging; }
- void SetOwner (const AString & a_NewOwnerName, const AString & a_NewOwnerUUID)
+ void SetOwner (const AString & a_NewOwnerName, const cUUID & a_NewOwnerUUID)
{
m_OwnerName = a_NewOwnerName;
m_OwnerUUID = a_NewOwnerUUID;
@@ -66,7 +67,7 @@ protected:
/** Only check for a nearby player holding the breeding items every 23 ticks. */
int m_CheckPlayerTickCount;
AString m_OwnerName;
- AString m_OwnerUUID;
+ cUUID m_OwnerUUID;
} ;
diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp
index 00cc07e14..8ae688a1b 100644
--- a/src/Mobs/Squid.cpp
+++ b/src/Mobs/Squid.cpp
@@ -46,13 +46,35 @@ void cSquid::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
return;
}
- int RelX = FloorC(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width;
- int RelZ = FloorC(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width;
- BLOCKTYPE BlockType;
- if (a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockType) && !IsBlockWater(BlockType) && !IsOnFire())
+
+ if (!IsSubmerged())
+ {
+ if (m_AirLevel <= 0)
+ {
+ // Runs the air tick timer to check whether the squid should be damaged
+ if (m_AirTickTimer <= 0)
+ {
+ // Damage squid
+ TakeDamage(dtSuffocating, nullptr, 1, 1, 0);
+ // Reset timer
+ m_AirTickTimer = DROWNING_TICKS;
+ }
+ else
+ {
+ m_AirTickTimer--;
+ }
+ }
+ else
+ {
+ // Reduce air supply
+ m_AirLevel--;
+ }
+ }
+ else
{
- // Burn for 10 ticks, then decide again
- StartBurning(10);
+ // Set the air back to maximum
+ m_AirLevel = MAX_AIR_LEVEL;
+ m_AirTickTimer = DROWNING_TICKS;
}
super::Tick(a_Dt, a_Chunk);
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index d7ecb3d47..243507855 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -117,7 +117,7 @@ void cWolf::NotifyAlliesOfFight(cPawn * a_Opponent)
-void cWolf::ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool a_IsPlayerInvolved)
+void cWolf::ReceiveNearbyFightInfo(const cUUID & a_PlayerID, cPawn * a_Opponent, bool a_IsPlayerInvolved)
{
if (
(a_Opponent == nullptr) || IsSitting() || (!IsTame()) ||
diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h
index 120e10942..07d0b5f27 100644
--- a/src/Mobs/Wolf.h
+++ b/src/Mobs/Wolf.h
@@ -2,6 +2,7 @@
#pragma once
#include "PassiveAggressiveMonster.h"
+#include "../UUID.h"
class cEntity;
@@ -9,62 +10,62 @@ class cEntity;
class cWolf :
- public cPassiveAggressiveMonster
+ public cPassiveAggressiveMonster
{
- typedef cPassiveAggressiveMonster super;
+ typedef cPassiveAggressiveMonster super;
public:
- cWolf(void);
-
- CLASS_PROTODEF(cWolf)
-
- void NotifyAlliesOfFight(cPawn * a_Opponent);
- virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
- virtual void OnRightClicked(cPlayer & a_Player) override;
- virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
- virtual void TickFollowPlayer();
- //virtual bool Attack(std::chrono::milliseconds a_Dt) override;
-
- // Get functions
- bool IsSitting (void) const override { return m_IsSitting; }
- bool IsTame (void) const override { return m_IsTame; }
- bool IsBegging (void) const { return m_IsBegging; }
- bool IsAngry (void) const { return m_IsAngry; }
- AString GetOwnerName (void) const { return m_OwnerName; }
- AString GetOwnerUUID (void) const { return m_OwnerUUID; }
- int GetCollarColor(void) const { return m_CollarColor; }
-
- // Set functions
- void SetIsSitting (bool a_IsSitting) { m_IsSitting = a_IsSitting; }
- void SetIsTame (bool a_IsTame) { m_IsTame = a_IsTame; }
- void SetIsBegging (bool a_IsBegging) { m_IsBegging = a_IsBegging; }
- void SetIsAngry (bool a_IsAngry) { m_IsAngry = a_IsAngry; }
- void SetCollarColor(int a_CollarColor) { m_CollarColor = a_CollarColor; }
- void SetOwner (const AString & a_NewOwnerName, const AString & a_NewOwnerUUID)
- {
- m_OwnerName = a_NewOwnerName;
- m_OwnerUUID = a_NewOwnerUUID;
- }
-
- /** Notfies the wolf of a nearby fight.
- The wolf may then decide to attack a_Opponent.
- If a_IsPlayer is true, then the player whose ID is a_PlayerID is fighting a_Opponent
- If false, then a wolf owned by the player whose ID is a_PlayerID is fighting a_Opponent
- @param a_PlayerID The ID of the fighting player, or the ID of the owner whose wolf is fighting.
- @param a_Opponent The opponent who is being faught.
- @param a_IsPlayerInvolved Whether the fighter a player or a wolf. */
- void ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool a_IsPlayerInvolved);
+ cWolf(void);
+
+ CLASS_PROTODEF(cWolf)
+
+ void NotifyAlliesOfFight(cPawn * a_Opponent);
+ virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ virtual void TickFollowPlayer();
+ virtual bool Attack(std::chrono::milliseconds a_Dt) override;
+
+ // Get functions
+ bool IsSitting (void) const override { return m_IsSitting; }
+ bool IsTame (void) const override { return m_IsTame; }
+ bool IsBegging (void) const { return m_IsBegging; }
+ bool IsAngry (void) const { return m_IsAngry; }
+ AString GetOwnerName (void) const { return m_OwnerName; }
+ cUUID GetOwnerUUID (void) const { return m_OwnerUUID; }
+ int GetCollarColor(void) const { return m_CollarColor; }
+
+ // Set functions
+ void SetIsSitting (bool a_IsSitting) { m_IsSitting = a_IsSitting; }
+ void SetIsTame (bool a_IsTame) { m_IsTame = a_IsTame; }
+ void SetIsBegging (bool a_IsBegging) { m_IsBegging = a_IsBegging; }
+ void SetIsAngry (bool a_IsAngry) { m_IsAngry = a_IsAngry; }
+ void SetCollarColor(int a_CollarColor) { m_CollarColor = a_CollarColor; }
+ void SetOwner (const AString & a_NewOwnerName, const cUUID & a_NewOwnerUUID)
+ {
+ m_OwnerName = a_NewOwnerName;
+ m_OwnerUUID = a_NewOwnerUUID;
+ }
+
+ /** Notfies the wolf of a nearby fight.
+ The wolf may then decide to attack a_Opponent.
+ If a_IsPlayerInvolved is true, then the player whose UUID is a_PlayerUUID is fighting a_Opponent
+ If false, then a wolf owned by the player whose UUID is a_PlayerUUID is fighting a_Opponent
+ @param a_PlayerUUID The UUID of the fighting player, or the UUID of the owner whose wolf is fighting.
+ @param a_Opponent The opponent who is being faught.
+ @param a_IsPlayerInvolved Whether the fighter a player or a wolf. */
+ void ReceiveNearbyFightInfo(const cUUID & a_PlayerUUID, cPawn * a_Opponent, bool a_IsPlayerInvolved);
protected:
- bool m_IsSitting;
- bool m_IsTame;
- bool m_IsBegging;
- bool m_IsAngry;
- AString m_OwnerName;
- AString m_OwnerUUID;
- int m_CollarColor;
- int m_NotificationCooldown;
+ bool m_IsSitting;
+ bool m_IsTame;
+ bool m_IsBegging;
+ bool m_IsAngry;
+ AString m_OwnerName;
+ cUUID m_OwnerUUID;
+ int m_CollarColor;
+ int m_NotificationCooldown;
} ;
diff --git a/src/Protocol/Authenticator.cpp b/src/Protocol/Authenticator.cpp
index 12e963143..d46127d34 100644
--- a/src/Protocol/Authenticator.cpp
+++ b/src/Protocol/Authenticator.cpp
@@ -6,6 +6,7 @@
#include "../Root.h"
#include "../Server.h"
#include "../ClientHandle.h"
+#include "../UUID.h"
#include "../IniFile.h"
#include "json/json.h"
@@ -119,11 +120,11 @@ void cAuthenticator::Execute(void)
Lock.Unlock();
AString NewUserName = UserName;
- AString UUID;
+ cUUID UUID;
Json::Value Properties;
if (AuthWithYggdrasil(NewUserName, ServerID, UUID, Properties))
{
- LOGINFO("User %s authenticated with UUID %s", NewUserName.c_str(), UUID.c_str());
+ LOGINFO("User %s authenticated with UUID %s", NewUserName.c_str(), UUID.ToShortString().c_str());
cRoot::Get()->AuthenticateUser(ClientID, NewUserName, UUID, Properties);
}
else
@@ -137,7 +138,7 @@ void cAuthenticator::Execute(void)
-bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, AString & a_UUID, Json::Value & a_Properties)
+bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, cUUID & a_UUID, Json::Value & a_Properties)
{
LOGD("Trying to authenticate user %s", a_UserName.c_str());
@@ -192,8 +193,12 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S
return false;
}
a_UserName = root.get("name", "Unknown").asString();
- a_UUID = cMojangAPI::MakeUUIDShort(root.get("id", "").asString());
a_Properties = root["properties"];
+ if (!a_UUID.FromString(root.get("id", "").asString()))
+ {
+ LOGWARNING("cAuthenticator: Recieved invalid UUID format");
+ return false;
+ }
// Store the player's profile in the MojangAPI caches:
cRoot::Get()->GetMojangAPI().AddPlayerProfile(a_UserName, a_UUID, a_Properties);
diff --git a/src/Protocol/Authenticator.h b/src/Protocol/Authenticator.h
index 5ce06093c..98bf17512 100644
--- a/src/Protocol/Authenticator.h
+++ b/src/Protocol/Authenticator.h
@@ -14,12 +14,10 @@
#include "../OSSupport/IsThread.h"
+// fwd:
+class cUUID;
class cSettingsRepositoryInterface;
-
-
-
-
namespace Json
{
class Value;
@@ -90,7 +88,7 @@ private:
/** Returns true if the user authenticated okay, false on error
Returns the case-corrected username, UUID, and properties (eg. skin). */
- bool AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, AString & a_UUID, Json::Value & a_Properties);
+ bool AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, cUUID & a_UUID, Json::Value & a_Properties);
};
diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp
index 4d5361479..5a11356c1 100644
--- a/src/Protocol/MojangAPI.cpp
+++ b/src/Protocol/MojangAPI.cpp
@@ -154,7 +154,7 @@ static const AString & GetCACerts(void)
cMojangAPI::sProfile::sProfile(
const AString & a_PlayerName,
- const AString & a_UUID,
+ const cUUID & a_UUID,
const Json::Value & a_Properties,
Int64 a_DateTime
) :
@@ -291,7 +291,7 @@ void cMojangAPI::Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldA
-AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_UseOnlyCached)
+cUUID cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_UseOnlyCached)
{
// Convert the playername to lowercase:
AString lcPlayerName = StrToLower(a_PlayerName);
@@ -299,8 +299,7 @@ AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_U
// Request the cache to query the name if not yet cached:
if (!a_UseOnlyCached)
{
- AStringVector PlayerNames;
- PlayerNames.push_back(lcPlayerName);
+ AStringVector PlayerNames{ lcPlayerName };
CacheNamesToUUIDs(PlayerNames);
}
@@ -310,7 +309,7 @@ AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_U
if (itr == m_NameToUUID.end())
{
// No UUID found
- return "";
+ return {};
}
return itr->second.m_UUID;
}
@@ -319,15 +318,12 @@ AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_U
-AString cMojangAPI::GetPlayerNameFromUUID(const AString & a_UUID, bool a_UseOnlyCached)
+AString cMojangAPI::GetPlayerNameFromUUID(const cUUID & a_UUID, bool a_UseOnlyCached)
{
- // Normalize the UUID to lowercase short format that is used as the map key:
- AString UUID = MakeUUIDShort(a_UUID);
-
// Retrieve from caches:
{
cCSLock Lock(m_CSUUIDToProfile);
- cProfileMap::const_iterator itr = m_UUIDToProfile.find(UUID);
+ auto itr = m_UUIDToProfile.find(a_UUID);
if (itr != m_UUIDToProfile.end())
{
return itr->second.m_PlayerName;
@@ -335,7 +331,7 @@ AString cMojangAPI::GetPlayerNameFromUUID(const AString & a_UUID, bool a_UseOnly
}
{
cCSLock Lock(m_CSUUIDToName);
- cProfileMap::const_iterator itr = m_UUIDToName.find(UUID);
+ auto itr = m_UUIDToName.find(a_UUID);
if (itr != m_UUIDToName.end())
{
return itr->second.m_PlayerName;
@@ -345,19 +341,19 @@ AString cMojangAPI::GetPlayerNameFromUUID(const AString & a_UUID, bool a_UseOnly
// Name not yet cached, request cache and retry:
if (!a_UseOnlyCached)
{
- CacheUUIDToProfile(UUID);
+ CacheUUIDToProfile(a_UUID);
return GetPlayerNameFromUUID(a_UUID, true);
}
// No value found, none queried. Return an error:
- return "";
+ return {};
}
-AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_PlayerNames, bool a_UseOnlyCached)
+std::vector<cUUID> cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_PlayerNames, bool a_UseOnlyCached)
{
// Convert all playernames to lowercase:
AStringVector PlayerNames;
@@ -374,7 +370,7 @@ AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_Player
// Retrieve from cache:
size_t idx = 0;
- AStringVector res;
+ std::vector<cUUID> res;
res.resize(PlayerNames.size());
cCSLock Lock(m_CSNameToUUID);
for (AStringVector::const_iterator itr = PlayerNames.begin(), end = PlayerNames.end(); itr != end; ++itr, ++idx)
@@ -392,17 +388,16 @@ AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_Player
-void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const AString & a_UUID)
+void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const cUUID & a_UUID)
{
- AString UUID = MakeUUIDShort(a_UUID);
Int64 Now = time(nullptr);
{
cCSLock Lock(m_CSNameToUUID);
- m_NameToUUID[StrToLower(a_PlayerName)] = sProfile(a_PlayerName, UUID, "", "", Now);
+ m_NameToUUID[StrToLower(a_PlayerName)] = sProfile(a_PlayerName, a_UUID, "", "", Now);
}
{
cCSLock Lock(m_CSUUIDToName);
- m_UUIDToName[UUID] = sProfile(a_PlayerName, UUID, "", "", Now);
+ m_UUIDToName[a_UUID] = sProfile(a_PlayerName, a_UUID, "", "", Now);
}
NotifyNameUUID(a_PlayerName, a_UUID);
}
@@ -411,21 +406,20 @@ void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const
-void cMojangAPI::AddPlayerProfile(const AString & a_PlayerName, const AString & a_UUID, const Json::Value & a_Properties)
+void cMojangAPI::AddPlayerProfile(const AString & a_PlayerName, const cUUID & a_UUID, const Json::Value & a_Properties)
{
- AString UUID = MakeUUIDShort(a_UUID);
Int64 Now = time(nullptr);
{
cCSLock Lock(m_CSNameToUUID);
- m_NameToUUID[StrToLower(a_PlayerName)] = sProfile(a_PlayerName, UUID, "", "", Now);
+ m_NameToUUID[StrToLower(a_PlayerName)] = sProfile(a_PlayerName, a_UUID, "", "", Now);
}
{
cCSLock Lock(m_CSUUIDToName);
- m_UUIDToName[UUID] = sProfile(a_PlayerName, UUID, "", "", Now);
+ m_UUIDToName[a_UUID] = sProfile(a_PlayerName, a_UUID, "", "", Now);
}
{
cCSLock Lock(m_CSUUIDToProfile);
- m_UUIDToProfile[UUID] = sProfile(a_PlayerName, UUID, a_Properties, Now);
+ m_UUIDToProfile[a_UUID] = sProfile(a_PlayerName, a_UUID, a_Properties, Now);
}
NotifyNameUUID(a_PlayerName, a_UUID);
}
@@ -488,74 +482,6 @@ bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_R
-AString cMojangAPI::MakeUUIDShort(const AString & a_UUID)
-{
- // Note: we only check the string's length, not the actual content
- switch (a_UUID.size())
- {
- case 32:
- {
- // Already is a short UUID, only lowercase
- return StrToLower(a_UUID);
- }
-
- case 36:
- {
- // Remove the dashes from the string by appending together the parts between them:
- AString res;
- res.reserve(32);
- res.append(a_UUID, 0, 8);
- res.append(a_UUID, 9, 4);
- res.append(a_UUID, 14, 4);
- res.append(a_UUID, 19, 4);
- res.append(a_UUID, 24, 12);
- return StrToLower(res);
- }
- }
- LOGWARNING("%s: Not an UUID: \"%s\".", __FUNCTION__, a_UUID.c_str());
- return "";
-}
-
-
-
-
-
-AString cMojangAPI::MakeUUIDDashed(const AString & a_UUID)
-{
- // Note: we only check the string's length, not the actual content
- switch (a_UUID.size())
- {
- case 36:
- {
- // Already is a dashed UUID, only lowercase
- return StrToLower(a_UUID);
- }
-
- case 32:
- {
- // Insert dashes at the proper positions:
- AString res;
- res.reserve(36);
- res.append(a_UUID, 0, 8);
- res.push_back('-');
- res.append(a_UUID, 8, 4);
- res.push_back('-');
- res.append(a_UUID, 12, 4);
- res.push_back('-');
- res.append(a_UUID, 16, 4);
- res.push_back('-');
- res.append(a_UUID, 20, 12);
- return StrToLower(res);
- }
- }
- LOGWARNING("%s: Not an UUID: \"%s\".", __FUNCTION__, a_UUID.c_str());
- return "";
-}
-
-
-
-
-
void cMojangAPI::LoadCachesFromDisk(void)
{
try
@@ -571,9 +497,15 @@ void cMojangAPI::LoadCachesFromDisk(void)
while (stmt.executeStep())
{
AString PlayerName = stmt.getColumn(0);
- AString UUID = stmt.getColumn(1);
+ AString StringUUID = stmt.getColumn(1);
Int64 DateTime = stmt.getColumn(2);
- UUID = MakeUUIDShort(UUID);
+
+ cUUID UUID;
+ if (!UUID.FromString(StringUUID))
+ {
+ continue; // Invalid UUID
+ }
+
m_NameToUUID[StrToLower(PlayerName)] = sProfile(PlayerName, UUID, "", "", DateTime);
m_UUIDToName[UUID] = sProfile(PlayerName, UUID, "", "", DateTime);
}
@@ -583,11 +515,17 @@ void cMojangAPI::LoadCachesFromDisk(void)
while (stmt.executeStep())
{
AString PlayerName = stmt.getColumn(0);
- AString UUID = stmt.getColumn(1);
+ AString StringUUID = stmt.getColumn(1);
AString Textures = stmt.getColumn(2);
AString TexturesSignature = stmt.getColumn(2);
Int64 DateTime = stmt.getColumn(4);
- UUID = MakeUUIDShort(UUID);
+
+ cUUID UUID;
+ if (!UUID.FromString(StringUUID))
+ {
+ continue; // Invalid UUID
+ }
+
m_UUIDToProfile[UUID] = sProfile(PlayerName, UUID, Textures, TexturesSignature, DateTime);
}
}
@@ -620,16 +558,17 @@ void cMojangAPI::SaveCachesToDisk(void)
{
SQLite::Statement stmt(db, "INSERT INTO PlayerNameToUUID(PlayerName, UUID, DateTime) VALUES (?, ?, ?)");
cCSLock Lock(m_CSNameToUUID);
- for (cProfileMap::const_iterator itr = m_NameToUUID.begin(), end = m_NameToUUID.end(); itr != end; ++itr)
+ for (auto & NameToUUID : m_NameToUUID)
{
- if (itr->second.m_DateTime < LimitDateTime)
+ auto & Profile = NameToUUID.second;
+ if (Profile.m_DateTime < LimitDateTime)
{
// This item is too old, do not save
continue;
}
- stmt.bind(1, itr->second.m_PlayerName);
- stmt.bind(2, itr->second.m_UUID);
- stmt.bind(3, itr->second.m_DateTime);
+ stmt.bind(1, Profile.m_PlayerName);
+ stmt.bind(2, Profile.m_UUID.ToShortString());
+ stmt.bind(3, Profile.m_DateTime);
stmt.exec();
stmt.reset();
}
@@ -639,18 +578,19 @@ void cMojangAPI::SaveCachesToDisk(void)
{
SQLite::Statement stmt(db, "INSERT INTO UUIDToProfile(UUID, PlayerName, Textures, TexturesSignature, DateTime) VALUES (?, ?, ?, ?, ?)");
cCSLock Lock(m_CSUUIDToProfile);
- for (cProfileMap::const_iterator itr = m_UUIDToProfile.begin(), end = m_UUIDToProfile.end(); itr != end; ++itr)
+ for (auto & UUIDToProfile : m_UUIDToProfile)
{
- if (itr->second.m_DateTime < LimitDateTime)
+ auto & Profile = UUIDToProfile.second;
+ if (Profile.m_DateTime < LimitDateTime)
{
// This item is too old, do not save
continue;
}
- stmt.bind(1, itr->second.m_UUID);
- stmt.bind(2, itr->second.m_PlayerName);
- stmt.bind(3, itr->second.m_Textures);
- stmt.bind(4, itr->second.m_TexturesSignature);
- stmt.bind(5, itr->second.m_DateTime);
+ stmt.bind(1, Profile.m_UUID.ToShortString());
+ stmt.bind(2, Profile.m_PlayerName);
+ stmt.bind(3, Profile.m_Textures);
+ stmt.bind(4, Profile.m_TexturesSignature);
+ stmt.bind(5, Profile.m_DateTime);
stmt.exec();
stmt.reset();
}
@@ -762,8 +702,8 @@ void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery)
{
Json::Value & Val = root[idx];
AString JsonName = Val.get("name", "").asString();
- AString JsonUUID = MakeUUIDShort(Val.get("id", "").asString());
- if (JsonUUID.empty())
+ cUUID JsonUUID;
+ if (!JsonUUID.FromString(Val.get("id", "").asString()))
{
continue;
}
@@ -779,8 +719,8 @@ void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery)
{
Json::Value & Val = root[idx];
AString JsonName = Val.get("name", "").asString();
- AString JsonUUID = MakeUUIDShort(Val.get("id", "").asString());
- if (JsonUUID.empty())
+ cUUID JsonUUID;
+ if (!JsonUUID.FromString(Val.get("id", "").asString()))
{
continue;
}
@@ -794,10 +734,8 @@ void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery)
-void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID)
+void cMojangAPI::CacheUUIDToProfile(const cUUID & a_UUID)
{
- ASSERT(a_UUID.size() == 32);
-
// Check if already present:
{
cCSLock Lock(m_CSUUIDToProfile);
@@ -814,11 +752,11 @@ void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID)
-void cMojangAPI::QueryUUIDToProfile(const AString & a_UUID)
+void cMojangAPI::QueryUUIDToProfile(const cUUID & a_UUID)
{
// Create the request address:
AString Address = m_UUIDToProfileAddress;
- ReplaceString(Address, "%UUID%", a_UUID);
+ ReplaceString(Address, "%UUID%", a_UUID.ToShortString());
// Create the HTTP request:
AString Request;
@@ -909,7 +847,7 @@ void cMojangAPI::QueryUUIDToProfile(const AString & a_UUID)
-void cMojangAPI::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID)
+void cMojangAPI::NotifyNameUUID(const AString & a_PlayerName, const cUUID & a_UUID)
{
// Notify the rank manager:
cCSLock Lock(m_CSRankMgr);
@@ -931,11 +869,11 @@ void cMojangAPI::Update(void)
AStringVector PlayerNames;
{
cCSLock Lock(m_CSNameToUUID);
- for (cProfileMap::const_iterator itr = m_NameToUUID.begin(), end = m_NameToUUID.end(); itr != end; ++itr)
+ for (const auto & NameToUUID : m_NameToUUID)
{
- if (itr->second.m_DateTime < LimitDateTime)
+ if (NameToUUID.second.m_DateTime < LimitDateTime)
{
- PlayerNames.push_back(itr->first);
+ PlayerNames.push_back(NameToUUID.first);
}
} // for itr - m_NameToUUID[]
}
@@ -946,23 +884,23 @@ void cMojangAPI::Update(void)
}
// Re-query all profiles that are stale:
- AStringVector ProfileUUIDs;
+ std::vector<cUUID> ProfileUUIDs;
{
cCSLock Lock(m_CSUUIDToProfile);
- for (cProfileMap::const_iterator itr = m_UUIDToProfile.begin(), end = m_UUIDToProfile.end(); itr != end; ++itr)
+ for (auto & UUIDToProfile : m_UUIDToProfile)
{
- if (itr->second.m_DateTime < LimitDateTime)
+ if (UUIDToProfile.second.m_DateTime < LimitDateTime)
{
- ProfileUUIDs.push_back(itr->first);
+ ProfileUUIDs.push_back(UUIDToProfile.first);
}
} // for itr - m_UUIDToProfile[]
}
if (!ProfileUUIDs.empty())
{
LOG("cMojangAPI: Updating uuid-to-profile cache for %u uuids", static_cast<unsigned>(ProfileUUIDs.size()));
- for (AStringVector::const_iterator itr = ProfileUUIDs.begin(), end = ProfileUUIDs.end(); itr != end; ++itr)
+ for (const auto & UUID : ProfileUUIDs)
{
- QueryUUIDToProfile(*itr);
+ QueryUUIDToProfile(UUID);
}
}
}
diff --git a/src/Protocol/MojangAPI.h b/src/Protocol/MojangAPI.h
index 3cd0376be..4d1751f1c 100644
--- a/src/Protocol/MojangAPI.h
+++ b/src/Protocol/MojangAPI.h
@@ -11,6 +11,8 @@
#include <time.h>
+#include "../UUID.h"
+
@@ -45,49 +47,38 @@ public:
Returns true if all was successful, false on failure. */
static bool SecureRequest(const AString & a_ServerName, const AString & a_Request, AString & a_Response);
- /** Normalizes the given UUID to its short form (32 bytes, no dashes, lowercase).
- Logs a warning and returns empty string if not a UUID.
- Note: only checks the string's length, not the actual content. */
- static AString MakeUUIDShort(const AString & a_UUID);
-
- /** Normalizes the given UUID to its dashed form (36 bytes, 4 dashes, lowercase).
- Logs a warning and returns empty string if not a UUID.
- Note: only checks the string's length, not the actual content. */
- static AString MakeUUIDDashed(const AString & a_UUID);
-
/** Converts a player name into a UUID.
- The UUID will be empty on error.
+ The UUID will be nil on error.
If a_UseOnlyCached is true, the function only consults the cached values.
If a_UseOnlyCached is false and the name is not found in the cache, it is looked up online, which is a blocking
operation, do not use this in world-tick thread!
If you have multiple names to resolve, use the GetUUIDsFromPlayerNames() function, it uses a single request for multiple names. */
- AString GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_UseOnlyCached = false);
+ cUUID GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_UseOnlyCached = false);
/** Converts a UUID into a playername.
The returned playername will be empty on error.
- Both short and dashed UUID formats are accepted.
Uses both m_UUIDToName and m_UUIDToProfile to search for the value. Uses m_UUIDToProfile for cache.
If a_UseOnlyCached is true, the function only consults the cached values.
If a_UseOnlyCached is false and the name is not found in the cache, it is looked up online, which is a blocking
operation, do not use this in world-tick thread! */
- AString GetPlayerNameFromUUID(const AString & a_UUID, bool a_UseOnlyCached = false);
+ AString GetPlayerNameFromUUID(const cUUID & a_UUID, bool a_UseOnlyCached = false);
/** Converts the player names into UUIDs.
a_PlayerName[idx] will be converted to UUID and returned as idx-th value
- The UUID will be empty on error.
+ The UUID will be nil on error.
If a_UseOnlyCached is true, only the cached values are returned.
If a_UseOnlyCached is false, the names not found in the cache are looked up online, which is a blocking
operation, do not use this in world-tick thread! */
- AStringVector GetUUIDsFromPlayerNames(const AStringVector & a_PlayerName, bool a_UseOnlyCached = false);
+ std::vector<cUUID> GetUUIDsFromPlayerNames(const AStringVector & a_PlayerName, bool a_UseOnlyCached = false);
/** Called by the Authenticator to add a PlayerName -> UUID mapping that it has received from
authenticating a user. This adds the cache item and "refreshes" it if existing, adjusting its datetime
stamp to now. */
- void AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const AString & a_UUID);
+ void AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const cUUID & a_UUID);
/** Called by the Authenticator to add a profile that it has received from authenticating a user. Adds
the profile to the respective mapping caches and updtes their datetime stamp to now. */
- void AddPlayerProfile(const AString & a_PlayerName, const AString & a_UUID, const Json::Value & a_Properties);
+ void AddPlayerProfile(const AString & a_PlayerName, const cUUID & a_UUID, const Json::Value & a_Properties);
/** Sets the m_RankMgr that is used for name-uuid notifications. Accepts nullptr to remove the binding. */
void SetRankManager(cRankManager * a_RankManager) { m_RankMgr = a_RankManager; }
@@ -101,7 +92,7 @@ protected:
struct sProfile
{
AString m_PlayerName; // Case-correct playername
- AString m_UUID; // Short lowercased UUID
+ cUUID m_UUID; // Player UUID
AString m_Textures; // The Textures field of the profile properties
AString m_TexturesSignature; // The signature of the Textures field of the profile properties
Int64 m_DateTime; // UNIXtime of the profile lookup
@@ -119,7 +110,7 @@ protected:
/** Constructor for the storage creation. */
sProfile(
const AString & a_PlayerName,
- const AString & a_UUID,
+ const cUUID & a_UUID,
const AString & a_Textures,
const AString & a_TexturesSignature,
Int64 a_DateTime
@@ -135,12 +126,13 @@ protected:
/** Constructor that parses the values from the Json profile. */
sProfile(
const AString & a_PlayerName,
- const AString & a_UUID,
+ const cUUID & a_UUID,
const Json::Value & a_Properties,
Int64 a_DateTime
);
};
typedef std::map<AString, sProfile> cProfileMap;
+ typedef std::map<cUUID, sProfile> cUUIDProfileMap;
/** The server to connect to when converting player names to UUIDs. For example "api.mojang.com". */
@@ -164,14 +156,14 @@ protected:
cCriticalSection m_CSNameToUUID;
/** Cache for the Name-to-UUID lookups. The map key is lowercased short UUID. Protected by m_CSUUIDToName. */
- cProfileMap m_UUIDToName;
+ cUUIDProfileMap m_UUIDToName;
/** Protects m_UUIDToName against simultaneous multi-threaded access. */
cCriticalSection m_CSUUIDToName;
/** Cache for the UUID-to-profile lookups. The map key is lowercased short UUID.
Protected by m_CSUUIDToProfile. */
- cProfileMap m_UUIDToProfile;
+ cUUIDProfileMap m_UUIDToProfile;
/** Protects m_UUIDToProfile against simultaneous multi-threaded access. */
cCriticalSection m_CSUUIDToProfile;
@@ -204,18 +196,16 @@ protected:
void QueryNamesToUUIDs(AStringVector & a_PlayerNames);
/** Makes sure the specified UUID is in the m_UUIDToProfile cache. If missing, downloads it from Mojang API servers.
- UUIDs that are not valid will not be added into the cache.
- ASSUMEs that a_UUID is a lowercased short UUID. */
- void CacheUUIDToProfile(const AString & a_UUID);
+ UUIDs that are not valid will not be added into the cache. */
+ void CacheUUIDToProfile(const cUUID & a_UUID);
/** Queries the specified UUID's profile and stores it in the m_UUIDToProfile cache. If already present, updates the cache entry.
- UUIDs that are not valid will not be added into the cache.
- ASSUMEs that a_UUID is a lowercased short UUID. */
- void QueryUUIDToProfile(const AString & a_UUID);
+ UUIDs that are not valid will not be added into the cache. */
+ void QueryUUIDToProfile(const cUUID & a_UUID);
/** Called for each name-uuid pairing that is discovered.
If assigned, notifies the m_RankManager of the event. */
- void NotifyNameUUID(const AString & a_PlayerName, const AString & a_PlayerUUID);
+ void NotifyNameUUID(const AString & a_PlayerName, const cUUID & a_PlayerUUID);
/** Updates the stale values in the DB from the Mojang servers. Called from the cUpdateThread, blocks on the HTTPS API calls. */
void Update(void);
diff --git a/src/Protocol/Packetizer.cpp b/src/Protocol/Packetizer.cpp
index 0a84d4678..5cae1fad5 100644
--- a/src/Protocol/Packetizer.cpp
+++ b/src/Protocol/Packetizer.cpp
@@ -5,46 +5,7 @@
#include "Globals.h"
#include "Packetizer.h"
-
-
-
-
-
-/** Converts the hex digit character to its value. */
-static UInt8 HexDigitValue(char a_Character)
-{
- switch (a_Character)
- {
- case '0': return 0;
- case '1': return 1;
- case '2': return 2;
- case '3': return 3;
- case '4': return 4;
- case '5': return 5;
- case '6': return 6;
- case '7': return 7;
- case '8': return 8;
- case '9': return 9;
- case 'a': return 10;
- case 'b': return 11;
- case 'c': return 12;
- case 'd': return 13;
- case 'e': return 14;
- case 'f': return 15;
- case 'A': return 10;
- case 'B': return 11;
- case 'C': return 12;
- case 'D': return 13;
- case 'E': return 14;
- case 'F': return 15;
- default:
- {
- LOGWARNING("Bad hex digit: %c", a_Character);
- ASSERT(!"Bad hex digit");
- return 0;
- }
- }
-}
+#include "UUID.h"
@@ -80,18 +41,10 @@ void cPacketizer::WriteFPInt(double a_Value)
-void cPacketizer::WriteUUID(const AString & a_UUID)
+void cPacketizer::WriteUUID(const cUUID & a_UUID)
{
- if (a_UUID.length() != 32)
- {
- LOGWARNING("%s: Attempt to send a bad uuid (length isn't 32): %s", __FUNCTION__, a_UUID.c_str());
- ASSERT(!"Wrong uuid length!");
- return;
- }
-
- for (size_t i = 0; i < 32; i += 2)
+ for (auto val : a_UUID.ToRaw())
{
- auto val = static_cast<UInt8>(HexDigitValue(a_UUID[i]) << 4 | HexDigitValue(a_UUID[i + 1]));
VERIFY(m_Out.WriteBEUInt8(val));
}
}
diff --git a/src/Protocol/Packetizer.h b/src/Protocol/Packetizer.h
index 26b3a7ec7..6d2284976 100644
--- a/src/Protocol/Packetizer.h
+++ b/src/Protocol/Packetizer.h
@@ -17,6 +17,10 @@
class cByteBuffer;
+// fwd:
+class cUUID;
+
+
@@ -134,7 +138,7 @@ public:
void WriteFPInt(double a_Value);
/** Writes the specified UUID as a 128-bit BigEndian integer. */
- void WriteUUID(const AString & a_UUID);
+ void WriteUUID(const cUUID & a_UUID);
UInt32 GetPacketType(void) const { return m_PacketType; }
diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp
index 2291eb6fc..936f5d8a7 100644
--- a/src/Protocol/Protocol_1_10.cpp
+++ b/src/Protocol/Protocol_1_10.cpp
@@ -619,7 +619,7 @@ void cProtocol_1_10_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity
// The new Block Entity format for a Mob Head. See: https://minecraft.gamepedia.com/Head#Block_entity
Writer.BeginCompound("Owner");
- Writer.AddString("Id", MobHeadEntity.GetOwnerUUID());
+ Writer.AddString("Id", MobHeadEntity.GetOwnerUUID().ToShortString());
Writer.AddString("Name", MobHeadEntity.GetOwnerName());
Writer.BeginCompound("Properties");
Writer.BeginList("textures", TAG_Compound);
diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp
index 9212e97ed..c562503bd 100644
--- a/src/Protocol/Protocol_1_11.cpp
+++ b/src/Protocol/Protocol_1_11.cpp
@@ -466,7 +466,7 @@ void cProtocol_1_11_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity
// The new Block Entity format for a Mob Head. See: https://minecraft.gamepedia.com/Head#Block_entity
Writer.BeginCompound("Owner");
- Writer.AddString("Id", MobHeadEntity.GetOwnerUUID());
+ Writer.AddString("Id", MobHeadEntity.GetOwnerUUID().ToShortString());
Writer.AddString("Name", MobHeadEntity.GetOwnerName());
Writer.BeginCompound("Properties");
Writer.BeginList("textures", TAG_Compound);
diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp
index 0dba45e15..43ab682eb 100644
--- a/src/Protocol/Protocol_1_12.cpp
+++ b/src/Protocol/Protocol_1_12.cpp
@@ -1125,6 +1125,30 @@ void cProtocol_1_12::SendEntityMetadata(const cEntity & a_Entity)
+void cProtocol_1_12::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
+{
+ ASSERT(m_State == 3); // In game mode?
+ cPacketizer Pkt(*this, 0x3c); // Set Attach Entity packet
+ Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
+ Pkt.WriteBEUInt32(a_EntityLeashedTo.GetUniqueID());
+}
+
+
+
+
+
+void cProtocol_1_12::SendUnleashEntity(const cEntity & a_Entity)
+{
+ ASSERT(m_State == 3); // In game mode?
+ cPacketizer Pkt(*this, 0x3c); // Set Attach Entity packet
+ Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
+ Pkt.WriteBEInt32(-1); // Unleash a_Entity
+}
+
+
+
+
+
void cProtocol_1_12::SendEntityVelocity(const cEntity & a_Entity)
{
ASSERT(m_State == 3); // In game mode?
@@ -1252,30 +1276,6 @@ void cProtocol_1_12::SendScoreUpdate(const AString & a_Objective, const AString
-void cProtocol_1_12::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
-{
- ASSERT(m_State == 3); // In game mode?
- cPacketizer Pkt(*this, 0x3c); // Set Attach Entity packet
- Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
- Pkt.WriteBEUInt32(a_EntityLeashedTo.GetUniqueID());
-}
-
-
-
-
-
-void cProtocol_1_12::SendUnleashEntity(const cEntity & a_Entity)
-{
- ASSERT(m_State == 3); // In game mode?
- cPacketizer Pkt(*this, 0x3c); // Set Attach Entity packet
- Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
- Pkt.WriteBEInt32(-1); // Unleash a_Entity
-}
-
-
-
-
-
void cProtocol_1_12::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
{
// Send the Join Game packet:
@@ -1945,6 +1945,30 @@ void cProtocol_1_12_1::SendEntityMetadata(const cEntity & a_Entity)
+void cProtocol_1_12_1::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
+{
+ ASSERT(m_State == 3); // In game mode?
+ cPacketizer Pkt(*this, 0x3d); // Set Attach Entity packet
+ Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
+ Pkt.WriteBEUInt32(a_EntityLeashedTo.GetUniqueID());
+}
+
+
+
+
+
+void cProtocol_1_12_1::SendUnleashEntity(const cEntity & a_Entity)
+{
+ ASSERT(m_State == 3); // In game mode?
+ cPacketizer Pkt(*this, 0x3d); // Set Attach Entity packet
+ Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
+ Pkt.WriteBEInt32(-1); // Unleash a_Entity
+}
+
+
+
+
+
void cProtocol_1_12_1::SendEntityVelocity(const cEntity & a_Entity)
{
ASSERT(m_State == 3); // In game mode?
diff --git a/src/Protocol/Protocol_1_12.h b/src/Protocol/Protocol_1_12.h
index e1fc12149..17d7d4e86 100644
--- a/src/Protocol/Protocol_1_12.h
+++ b/src/Protocol/Protocol_1_12.h
@@ -101,6 +101,8 @@ public:
virtual void SendCameraSetTo(const cEntity & a_Entity) override;
virtual void SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
virtual void SendEntityMetadata(const cEntity & a_Entity) override;
+ virtual void SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo) override;
+ virtual void SendUnleashEntity(const cEntity & a_Entity) override;
virtual void SendEntityVelocity(const cEntity & a_Entity) override;
virtual void SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
virtual void SendExperience(void) override;
diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp
index e3932db7f..2824c4a13 100644
--- a/src/Protocol/Protocol_1_8.cpp
+++ b/src/Protocol/Protocol_1_8.cpp
@@ -22,6 +22,7 @@ Implements the 1.8 protocol classes:
#include "../StringCompression.h"
#include "../CompositeChat.h"
#include "../Statistics.h"
+#include "../UUID.h"
#include "../WorldStorage/FastNBT.h"
#include "../WorldStorage/EnchantmentSerializer.h"
@@ -118,7 +119,11 @@ cProtocol_1_8_0::cProtocol_1_8_0(cClientHandle * a_Client, const AString & a_Ser
LOGD("Player at %s connected via BungeeCord", Params[1].c_str());
m_ServerAddress = Params[0];
m_Client->SetIPString(Params[1]);
- m_Client->SetUUID(cMojangAPI::MakeUUIDShort(Params[2]));
+
+ cUUID UUID;
+ UUID.FromString(Params[2]);
+ m_Client->SetUUID(UUID);
+
m_Client->SetProperties(Params[3]);
}
@@ -705,7 +710,7 @@ void cProtocol_1_8_0::SendLoginSuccess(void)
{
cPacketizer Pkt(*this, 0x02); // Login success packet
- Pkt.WriteString(cMojangAPI::MakeUUIDDashed(m_Client->GetUUID()));
+ Pkt.WriteString(m_Client->GetUUID().ToLongString());
Pkt.WriteString(m_Client->GetUsername());
}
}
@@ -1071,7 +1076,7 @@ void cProtocol_1_8_0::SendPlayerSpawn(const cPlayer & a_Player)
// Called to spawn another player for the client
cPacketizer Pkt(*this, 0x0c); // Spawn Player packet
Pkt.WriteVarInt32(a_Player.GetUniqueID());
- Pkt.WriteUUID(cMojangAPI::MakeUUIDShort(a_Player.GetUUID()));
+ Pkt.WriteUUID(a_Player.GetUUID());
Pkt.WriteFPInt(a_Player.GetPosX());
Pkt.WriteFPInt(a_Player.GetPosY() + 0.001); // The "+ 0.001" is there because otherwise the player falls through the block they were standing on.
Pkt.WriteFPInt(a_Player.GetPosZ());
@@ -1542,6 +1547,7 @@ void cProtocol_1_8_0::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
case E_BLOCK_BEACON: Action = 3; break; // Update beacon entity
case E_BLOCK_HEAD: Action = 4; break; // Update Mobhead entity
case E_BLOCK_FLOWER_POT: Action = 5; break; // Update flower pot
+ case E_BLOCK_BED: Action = 11; break; // Update bed color
default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
}
Pkt.WriteBEUInt8(Action);
@@ -2551,7 +2557,7 @@ void cProtocol_1_8_0::HandlePacketSlotSelect(cByteBuffer & a_ByteBuffer)
void cProtocol_1_8_0::HandlePacketSpectate(cByteBuffer &a_ByteBuffer)
{
- AString playerUUID;
+ cUUID playerUUID;
if (!a_ByteBuffer.ReadUUID(playerUUID))
{
return;
@@ -3183,7 +3189,7 @@ void cProtocol_1_8_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity &
// The new Block Entity format for a Mob Head. See: https://minecraft.gamepedia.com/Head#Block_entity
Writer.BeginCompound("Owner");
- Writer.AddString("Id", MobHeadEntity.GetOwnerUUID());
+ Writer.AddString("Id", MobHeadEntity.GetOwnerUUID().ToShortString());
Writer.AddString("Name", MobHeadEntity.GetOwnerName());
Writer.BeginCompound("Properties");
Writer.BeginList("textures", TAG_Compound);
diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp
index 3d4fee425..b9310bea5 100644
--- a/src/Protocol/Protocol_1_9.cpp
+++ b/src/Protocol/Protocol_1_9.cpp
@@ -133,7 +133,11 @@ cProtocol_1_9_0::cProtocol_1_9_0(cClientHandle * a_Client, const AString & a_Ser
LOGD("Player at %s connected via BungeeCord", Params[1].c_str());
m_ServerAddress = Params[0];
m_Client->SetIPString(Params[1]);
- m_Client->SetUUID(cMojangAPI::MakeUUIDShort(Params[2]));
+
+ cUUID UUID;
+ UUID.FromString(Params[2]);
+ m_Client->SetUUID(UUID);
+
m_Client->SetProperties(Params[3]);
}
@@ -720,7 +724,7 @@ void cProtocol_1_9_0::SendLoginSuccess(void)
{
cPacketizer Pkt(*this, 0x02); // Login success packet
- Pkt.WriteString(cMojangAPI::MakeUUIDDashed(m_Client->GetUUID()));
+ Pkt.WriteString(m_Client->GetUUID().ToLongString());
Pkt.WriteString(m_Client->GetUsername());
}
}
@@ -1094,7 +1098,7 @@ void cProtocol_1_9_0::SendPlayerSpawn(const cPlayer & a_Player)
// Called to spawn another player for the client
cPacketizer Pkt(*this, 0x05); // Spawn Player packet
Pkt.WriteVarInt32(a_Player.GetUniqueID());
- Pkt.WriteUUID(cMojangAPI::MakeUUIDShort(a_Player.GetUUID()));
+ Pkt.WriteUUID(a_Player.GetUUID());
Pkt.WriteBEDouble(a_Player.GetPosX());
Pkt.WriteBEDouble(a_Player.GetPosY() + 0.001); // The "+ 0.001" is there because otherwise the player falls through the block they were standing on.
Pkt.WriteBEDouble(a_Player.GetPosZ());
@@ -1563,6 +1567,7 @@ void cProtocol_1_9_0::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
case E_BLOCK_BEACON: Action = 3; break; // Update beacon entity
case E_BLOCK_HEAD: Action = 4; break; // Update Mobhead entity
case E_BLOCK_FLOWER_POT: Action = 5; break; // Update flower pot
+ case E_BLOCK_BED: Action = 11; break; // Update bed color
default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
}
Pkt.WriteBEUInt8(Action);
@@ -2624,7 +2629,7 @@ void cProtocol_1_9_0::HandlePacketSlotSelect(cByteBuffer & a_ByteBuffer)
void cProtocol_1_9_0::HandlePacketSpectate(cByteBuffer & a_ByteBuffer)
{
- AString playerUUID;
+ cUUID playerUUID;
if (!a_ByteBuffer.ReadUUID(playerUUID))
{
return;
@@ -3513,7 +3518,7 @@ void cProtocol_1_9_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity &
// The new Block Entity format for a Mob Head. See: https://minecraft.gamepedia.com/Head#Block_entity
Writer.BeginCompound("Owner");
- Writer.AddString("Id", MobHeadEntity.GetOwnerUUID());
+ Writer.AddString("Id", MobHeadEntity.GetOwnerUUID().ToShortString());
Writer.AddString("Name", MobHeadEntity.GetOwnerName());
Writer.BeginCompound("Properties");
Writer.BeginList("textures", TAG_Compound);
diff --git a/src/RankManager.cpp b/src/RankManager.cpp
index 0c6ef508e..53083fa7a 100644
--- a/src/RankManager.cpp
+++ b/src/RankManager.cpp
@@ -92,10 +92,10 @@ protected:
AStringVector m_Groups;
/** Assigned by ResolveUserUUIDs(), contains the online (Mojang) UUID of the player. */
- AString m_UUID;
+ cUUID m_UUID;
/** Assigned by ResolveUserUUIDs(), contains the offline (generated) UUID of the player. */
- AString m_OfflineUUID;
+ cUUID m_OfflineUUID;
sUser(void) {}
@@ -282,15 +282,15 @@ protected:
m_MojangAPI.GetUUIDsFromPlayerNames(PlayerNames);
// Assign the UUIDs back to players, remove those not resolved:
- for (sUserMap::iterator itr = m_Users.begin(); itr != m_Users.end(); ++itr)
+ for (auto & User : m_Users)
{
- AString UUID = m_MojangAPI.GetUUIDFromPlayerName(itr->second.m_Name);
- if (UUID.empty())
+ cUUID UUID = m_MojangAPI.GetUUIDFromPlayerName(User.second.m_Name);
+ if (UUID.IsNil())
{
- LOGWARNING("RankMigrator: Cannot resolve player %s to online UUID, player will be left unranked in online mode", itr->second.m_Name.c_str());
+ LOGWARNING("RankMigrator: Cannot resolve player %s to online UUID, player will be left unranked in online mode", User.second.m_Name.c_str());
}
- itr->second.m_UUID = UUID;
- itr->second.m_OfflineUUID = cClientHandle::GenerateOfflineUUID(itr->second.m_Name);
+ User.second.m_UUID = UUID;
+ User.second.m_OfflineUUID = cClientHandle::GenerateOfflineUUID(User.second.m_Name);
}
}
@@ -469,7 +469,7 @@ void cRankManager::Initialize(cMojangAPI & a_MojangAPI)
-AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
+AString cRankManager::GetPlayerRankName(const cUUID & a_PlayerUUID)
{
ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
@@ -477,7 +477,7 @@ AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
try
{
SQLite::Statement stmt(m_DB, "SELECT Rank.Name FROM Rank LEFT JOIN PlayerRank ON Rank.RankID = PlayerRank.RankID WHERE PlayerRank.PlayerUUID = ?");
- stmt.bind(1, a_PlayerUUID);
+ stmt.bind(1, a_PlayerUUID.ToShortString());
// executeStep returns false on no data
if (!stmt.executeStep())
{
@@ -497,7 +497,7 @@ AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
-AString cRankManager::GetPlayerName(const AString & a_PlayerUUID)
+AString cRankManager::GetPlayerName(const cUUID & a_PlayerUUID)
{
ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
@@ -506,7 +506,7 @@ AString cRankManager::GetPlayerName(const AString & a_PlayerUUID)
{
// Prepare the DB statement:
SQLite::Statement stmt(m_DB, "SELECT PlayerName FROM PlayerRank WHERE PlayerUUID = ?");
- stmt.bind(1, a_PlayerUUID);
+ stmt.bind(1, a_PlayerUUID.ToShortString());
if (stmt.executeStep())
{
@@ -524,7 +524,7 @@ AString cRankManager::GetPlayerName(const AString & a_PlayerUUID)
-AStringVector cRankManager::GetPlayerGroups(const AString & a_PlayerUUID)
+AStringVector cRankManager::GetPlayerGroups(const cUUID & a_PlayerUUID)
{
ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
@@ -539,7 +539,7 @@ AStringVector cRankManager::GetPlayerGroups(const AString & a_PlayerUUID)
"LEFT JOIN PlayerRank ON PlayerRank.RankID = RankPermGroup.RankID "
"WHERE PlayerRank.PlayerUUID = ?"
);
- stmt.bind(1, a_PlayerUUID);
+ stmt.bind(1, a_PlayerUUID.ToShortString());
// Execute and get results:
while (stmt.executeStep())
@@ -558,7 +558,7 @@ AStringVector cRankManager::GetPlayerGroups(const AString & a_PlayerUUID)
-AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID)
+AStringVector cRankManager::GetPlayerPermissions(const cUUID & a_PlayerUUID)
{
AString Rank = GetPlayerRankName(a_PlayerUUID);
if (Rank.empty())
@@ -572,7 +572,7 @@ AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID)
-AStringVector cRankManager::GetPlayerRestrictions(const AString & a_PlayerUUID)
+AStringVector cRankManager::GetPlayerRestrictions(const cUUID & a_PlayerUUID)
{
AString Rank = GetPlayerRankName(a_PlayerUUID);
if (Rank.empty())
@@ -739,18 +739,24 @@ AStringVector cRankManager::GetRankRestrictions(const AString & a_RankName)
-AStringVector cRankManager::GetAllPlayerUUIDs(void)
+std::vector<cUUID> cRankManager::GetAllPlayerUUIDs(void)
{
ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
- AStringVector res;
+ cUUID tempUUID;
+ std::vector<cUUID> res;
try
{
SQLite::Statement stmt(m_DB, "SELECT PlayerUUID FROM PlayerRank ORDER BY PlayerName COLLATE NOCASE");
while (stmt.executeStep())
{
- res.push_back(stmt.getColumn(0).getText());
+ if (!tempUUID.FromString(stmt.getColumn(0).getText()))
+ {
+ // Invalid UUID, ignore
+ continue;
+ }
+ res.push_back(tempUUID);
}
}
catch (const SQLite::Exception & ex)
@@ -881,7 +887,7 @@ AStringVector cRankManager::GetAllPermissionsRestrictions(void)
bool cRankManager::GetPlayerMsgVisuals(
- const AString & a_PlayerUUID,
+ const cUUID & a_PlayerUUID,
AString & a_MsgPrefix,
AString & a_MsgSuffix,
AString & a_MsgNameColorCode
@@ -1746,11 +1752,13 @@ bool cRankManager::RenameGroup(const AString & a_OldName, const AString & a_NewN
-void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName)
+void cRankManager::SetPlayerRank(const cUUID & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName)
{
ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
+ AString StrUUID = a_PlayerUUID.ToShortString();
+
try
{
// Get the rank ID:
@@ -1771,7 +1779,7 @@ void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a
SQLite::Statement stmt(m_DB, "UPDATE PlayerRank SET RankID = ?, PlayerName = ? WHERE PlayerUUID = ?");
stmt.bind(1, RankID);
stmt.bind(2, a_PlayerName);
- stmt.bind(3, a_PlayerUUID);
+ stmt.bind(3, StrUUID);
if (stmt.exec() > 0)
{
// Successfully updated the player's rank
@@ -1782,7 +1790,7 @@ void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a
// The player is not yet in the DB, add them:
SQLite::Statement stmt(m_DB, "INSERT INTO PlayerRank (RankID, PlayerUUID, PlayerName) VALUES (?, ?, ?)");
stmt.bind(1, RankID);
- stmt.bind(2, a_PlayerUUID);
+ stmt.bind(2, StrUUID);
stmt.bind(3, a_PlayerName);
if (stmt.exec() > 0)
{
@@ -1791,13 +1799,13 @@ void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a
}
LOGWARNING("%s: Failed to set player UUID %s to rank %s.",
- __FUNCTION__, a_PlayerUUID.c_str(), a_RankName.c_str()
+ __FUNCTION__, StrUUID.c_str(), a_RankName.c_str()
);
}
catch (const SQLite::Exception & ex)
{
LOGWARNING("%s: Failed to set player UUID %s to rank %s: %s",
- __FUNCTION__, a_PlayerUUID.c_str(), a_RankName.c_str(), ex.what()
+ __FUNCTION__, StrUUID.c_str(), a_RankName.c_str(), ex.what()
);
}
}
@@ -1806,21 +1814,23 @@ void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a
-void cRankManager::RemovePlayerRank(const AString & a_PlayerUUID)
+void cRankManager::RemovePlayerRank(const cUUID & a_PlayerUUID)
{
ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
+ AString StrUUID = a_PlayerUUID.ToShortString();
+
try
{
SQLite::Statement stmt(m_DB, "DELETE FROM PlayerRank WHERE PlayerUUID = ?");
- stmt.bind(1, a_PlayerUUID);
+ stmt.bind(1, StrUUID);
stmt.exec();
}
catch (const SQLite::Exception & ex)
{
LOGWARNING("%s: Failed to remove rank from player UUID %s: %s",
- __FUNCTION__, a_PlayerUUID.c_str(), ex.what()
+ __FUNCTION__, StrUUID.c_str(), ex.what()
);
}
}
@@ -1948,15 +1958,17 @@ bool cRankManager::GroupExists(const AString & a_GroupName)
-bool cRankManager::IsPlayerRankSet(const AString & a_PlayerUUID)
+bool cRankManager::IsPlayerRankSet(const cUUID & a_PlayerUUID)
{
ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
+ AString StrUUID = a_PlayerUUID.ToShortString();
+
try
{
SQLite::Statement stmt(m_DB, "SELECT * FROM PlayerRank WHERE PlayerUUID = ?");
- stmt.bind(1, a_PlayerUUID);
+ stmt.bind(1, StrUUID);
if (stmt.executeStep())
{
// The player UUID was found, they have a rank
@@ -1965,7 +1977,7 @@ bool cRankManager::IsPlayerRankSet(const AString & a_PlayerUUID)
}
catch (const SQLite::Exception & ex)
{
- LOGWARNING("%s: Failed to query DB for player UUID %s: %s", __FUNCTION__, a_PlayerUUID.c_str(), ex.what());
+ LOGWARNING("%s: Failed to query DB for player UUID %s: %s", __FUNCTION__, StrUUID.c_str(), ex.what());
}
return false;
}
@@ -2068,7 +2080,7 @@ bool cRankManager::IsRestrictionInGroup(const AString & a_Restriction, const ASt
-void cRankManager::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID)
+void cRankManager::NotifyNameUUID(const AString & a_PlayerName, const cUUID & a_UUID)
{
ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
@@ -2077,7 +2089,7 @@ void cRankManager::NotifyNameUUID(const AString & a_PlayerName, const AString &
{
SQLite::Statement stmt(m_DB, "UPDATE PlayerRank SET PlayerName = ? WHERE PlayerUUID = ?");
stmt.bind(1, a_PlayerName);
- stmt.bind(2, a_UUID);
+ stmt.bind(2, a_UUID.ToShortString());
stmt.exec();
}
catch (const SQLite::Exception & ex)
@@ -2161,16 +2173,18 @@ void cRankManager::ClearPlayerRanks(void)
-bool cRankManager::UpdatePlayerName(const AString & a_PlayerUUID, const AString & a_NewPlayerName)
+bool cRankManager::UpdatePlayerName(const cUUID & a_PlayerUUID, const AString & a_NewPlayerName)
{
ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
+ AString StrUUID = a_PlayerUUID.ToShortString();
+
try
{
SQLite::Statement stmt(m_DB, "UPDATE PlayerRank SET PlayerName = ? WHERE PlayerUUID = ?");
stmt.bind(1, a_NewPlayerName);
- stmt.bind(2, a_PlayerUUID);
+ stmt.bind(2, StrUUID);
if (stmt.exec() > 0)
{
// The player name was changed, returns true
@@ -2179,7 +2193,7 @@ bool cRankManager::UpdatePlayerName(const AString & a_PlayerUUID, const AString
}
catch (const SQLite::Exception & ex)
{
- LOGWARNING("%s: Failed to update player name from UUID %s: %s", __FUNCTION__, a_PlayerUUID.c_str(), ex.what());
+ LOGWARNING("%s: Failed to update player name from UUID %s: %s", __FUNCTION__, StrUUID.c_str(), ex.what());
}
return false;
}
diff --git a/src/RankManager.h b/src/RankManager.h
index 73d94a5a7..abe860b1c 100644
--- a/src/RankManager.h
+++ b/src/RankManager.h
@@ -14,7 +14,7 @@
-
+class cUUID;
class cMojangAPI;
@@ -58,22 +58,22 @@ public:
/** Returns the name of the rank that the specified player has assigned to them.
If the player has no rank assigned, returns an empty string (NOT the default rank). */
- AString GetPlayerRankName(const AString & a_PlayerUUID);
+ AString GetPlayerRankName(const cUUID & a_PlayerUUID);
/** Returns the last name that the specified player has.
An empty string is returned if the player isn't in the database. */
- AString GetPlayerName(const AString & a_PlayerUUID);
+ AString GetPlayerName(const cUUID & a_PlayerUUID);
/** Returns the names of Groups that the specified player has assigned to them. */
- AStringVector GetPlayerGroups(const AString & a_PlayerUUID);
+ AStringVector GetPlayerGroups(const cUUID & a_PlayerUUID);
/** Returns the permissions that the specified player has assigned to them.
If the player has no rank assigned to them, returns the default rank's permissions. */
- AStringVector GetPlayerPermissions(const AString & a_PlayerUUID);
+ AStringVector GetPlayerPermissions(const cUUID & a_PlayerUUID);
/** Returns the restrictions that the specified player has assigned to them.
If the player has no rank assigned to them, returns the default rank's restrictions. */
- AStringVector GetPlayerRestrictions(const AString & a_PlayerUUID);
+ AStringVector GetPlayerRestrictions(const cUUID & a_PlayerUUID);
/** Returns the names of groups that the specified rank has assigned to it.
Returns an empty vector if the rank doesn't exist. */
@@ -95,8 +95,8 @@ public:
Returns an empty vector if the rank doesn't exist. Any non-existent groups are ignored. */
AStringVector GetRankRestrictions(const AString & a_RankName);
- /** Returns the short uuids of all defined players. The returned players are ordered by their name (NOT their UUIDs). */
- AStringVector GetAllPlayerUUIDs(void);
+ /** Returns the uuids of all defined players. The returned players are ordered by their name (NOT their UUIDs). */
+ std::vector<cUUID> GetAllPlayerUUIDs(void);
/** Returns the names of all defined ranks. */
AStringVector GetAllRanks(void);
@@ -116,7 +116,7 @@ public:
/** Returns the message visuals (prefix, postfix, color) for the specified player.
Returns true if the visuals were read from the DB, false if not (player not found etc). */
bool GetPlayerMsgVisuals(
- const AString & a_PlayerUUID,
+ const cUUID & a_PlayerUUID,
AString & a_MsgPrefix,
AString & a_MsgSuffix,
AString & a_MsgNameColorCode
@@ -199,13 +199,13 @@ public:
Note that this doesn't change the cPlayer if the player is already connected, you need to update all the
cPlayer instances manually.
The PlayerName is provided for reference, so that GetRankPlayerNames() can work. */
- void SetPlayerRank(const AString & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName);
+ void SetPlayerRank(const cUUID & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName);
/** Removes the player's rank assignment. The player is left without a rank.
Note that this doesn't change the cPlayer instances for the already connected players, you need to update
all the instances manually.
No action if the player has no rank assigned to them already. */
- void RemovePlayerRank(const AString & a_PlayerUUID);
+ void RemovePlayerRank(const cUUID & a_PlayerUUID);
/** Sets the message visuals of an existing rank. No action if the rank name is not found. */
void SetRankVisuals(
@@ -231,7 +231,7 @@ public:
bool GroupExists(const AString & a_GroupName);
/** Returns true iff the specified player has a rank assigned to them in the DB. */
- bool IsPlayerRankSet(const AString & a_PlayerUUID);
+ bool IsPlayerRankSet(const cUUID & a_PlayerUUID);
/** Returns true iff the specified rank contains the specified group. */
bool IsGroupInRank(const AString & a_GroupName, const AString & a_RankName);
@@ -243,7 +243,7 @@ public:
bool IsRestrictionInGroup(const AString & a_Restriction, const AString & a_GroupName);
/** Called by cMojangAPI whenever the playername-uuid pairing is discovered. Updates the DB. */
- void NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID);
+ void NotifyNameUUID(const AString & a_PlayerName, const cUUID & a_UUID);
/** Sets the specified rank as the default rank.
Returns true on success, false on failure (rank not found). */
@@ -257,7 +257,7 @@ public:
void ClearPlayerRanks(void);
/** Updates the playername that is saved with this uuid. Returns false if a error occurred */
- bool UpdatePlayerName(const AString & a_PlayerUUID, const AString & a_NewPlayerName);
+ bool UpdatePlayerName(const cUUID & a_PlayerUUID, const AString & a_NewPlayerName);
protected:
diff --git a/src/Root.cpp b/src/Root.cpp
index a487310a8..3e30d8a07 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -676,7 +676,7 @@ void cRoot::KickUser(int a_ClientID, const AString & a_Reason)
-void cRoot::AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties)
+void cRoot::AuthenticateUser(int a_ClientID, const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties)
{
m_Server->AuthenticateUser(a_ClientID, a_Name, a_UUID, a_Properties);
}
@@ -820,7 +820,7 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac
-bool cRoot::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback)
+bool cRoot::DoWithPlayerByUUID(const cUUID & a_PlayerUUID, cPlayerListCallback & a_Callback)
{
for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
{
diff --git a/src/Root.h b/src/Root.h
index 23eeb8c80..44567018d 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -25,6 +25,7 @@ class cCommandOutputCallback;
class cCompositeChat;
class cSettingsRepositoryInterface;
class cDeadlockDetect;
+class cUUID;
typedef cItemCallback<cPlayer> cPlayerListCallback;
typedef cItemCallback<cWorld> cWorldListCallback;
@@ -123,7 +124,7 @@ public:
void KickUser(int a_ClientID, const AString & a_Reason);
/** Called by cAuthenticator to auth the specified user */
- void AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties);
+ void AuthenticateUser(int a_ClientID, const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties);
/** Executes commands queued in the command queue */
void TickCommands(void);
@@ -141,7 +142,7 @@ public:
bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
/** Finds the player over his uuid and calls the callback */
- bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+ bool DoWithPlayerByUUID(const cUUID & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
/** Finds the player using it's complete username and calls the callback */
bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback);
diff --git a/src/Server.cpp b/src/Server.cpp
index a8703be86..70d594f2d 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -622,7 +622,7 @@ void cServer::KickUser(int a_ClientID, const AString & a_Reason)
-void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties)
+void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties)
{
cCSLock Lock(m_CSClients);
diff --git a/src/Server.h b/src/Server.h
index 2ac530c1e..633f6de70 100644
--- a/src/Server.h
+++ b/src/Server.h
@@ -39,6 +39,7 @@ typedef std::list<cClientHandlePtr> cClientHandlePtrs;
typedef std::list<cClientHandle *> cClientHandles;
class cCommandOutputCallback;
class cSettingsRepositoryInterface;
+class cUUID;
namespace Json
@@ -101,7 +102,7 @@ public:
void KickUser(int a_ClientID, const AString & a_Reason);
/** Authenticates the specified user, called by cAuthenticator */
- void AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties);
+ void AuthenticateUser(int a_ClientID, const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties);
const AString & GetServerID(void) const { return m_ServerID; } // tolua_export
diff --git a/src/UUID.cpp b/src/UUID.cpp
new file mode 100644
index 000000000..e150b3603
--- /dev/null
+++ b/src/UUID.cpp
@@ -0,0 +1,283 @@
+// UUID.h
+
+// Defines the cUUID class representing a Universally Unique Identifier
+
+#include "Globals.h"
+#include "UUID.h"
+
+#include "polarssl/md5.h"
+
+
+/** UUID normalised in textual form. */
+struct sShortUUID
+{
+ char Data[32]{};
+ bool IsValid = false;
+};
+
+/** Returns the given UUID in shortened form with IsValid indicating success.
+Doesn't check digits are hexadecimal but does check dashes if long form. */
+static sShortUUID ShortenUUID(const AString & a_StringUUID)
+{
+ sShortUUID UUID;
+ switch (a_StringUUID.size())
+ {
+ case 32:
+ {
+ // Already a short UUID
+ std::memcpy(UUID.Data, a_StringUUID.data(), 32);
+ UUID.IsValid = true;
+ break;
+ }
+ case 36:
+ {
+ // Long UUID, confirm dashed
+ if (
+ (a_StringUUID[ 8] != '-') ||
+ (a_StringUUID[13] != '-') ||
+ (a_StringUUID[18] != '-') ||
+ (a_StringUUID[23] != '-')
+ )
+ {
+ break;
+ }
+
+ // Copy everying but the dashes from the string
+ std::memcpy(UUID.Data, a_StringUUID.data(), 8);
+ std::memcpy(UUID.Data + 8, a_StringUUID.data() + 9, 4);
+ std::memcpy(UUID.Data + 12, a_StringUUID.data() + 14, 4);
+ std::memcpy(UUID.Data + 16, a_StringUUID.data() + 19, 4);
+ std::memcpy(UUID.Data + 20, a_StringUUID.data() + 24, 12);
+ UUID.IsValid = true;
+ }
+ default: break;
+ }
+ return UUID;
+}
+
+
+
+
+
+/** Returns the integer value of the hex digit or 0xff if invalid. */
+static Byte FromHexDigit(char a_Hex)
+{
+ if (('0' <= a_Hex) && (a_Hex <= '9'))
+ {
+ return static_cast<Byte>(a_Hex - '0');
+ }
+ if (('a' <= a_Hex) && (a_Hex <= 'f'))
+ {
+ return static_cast<Byte>(a_Hex - 'a');
+ }
+ if (('A' <= a_Hex) && (a_Hex <= 'F'))
+ {
+ return static_cast<Byte>(a_Hex - 'A');
+ }
+ return 0xff;
+}
+
+
+
+
+
+/** From a number in the range [0, 16), returns the corresponding hex digit in lowercase. */
+static char ToHexDigit(UInt8 a_Nibble)
+{
+ ASSERT((a_Nibble & 0xf0) == 0);
+ return static_cast<char>(
+ (a_Nibble < 10) ?
+ ('0' + a_Nibble) :
+ ('a' + (a_Nibble - 10))
+ );
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cUUID:
+
+bool cUUID::FromString(const AString & a_StringUUID)
+{
+ sShortUUID Norm = ShortenUUID(a_StringUUID);
+ if (!Norm.IsValid)
+ {
+ return false;
+ }
+
+ std::array<Byte, 16> ParsedUUID{{0}};
+ for (size_t i = 0; i != m_UUID.size(); ++i)
+ {
+ Byte HighNibble = FromHexDigit(Norm.Data[2 * i ]);
+ Byte LowNibble = FromHexDigit(Norm.Data[2 * i + 1]);
+ if ((HighNibble > 0x0f) || (LowNibble > 0x0f))
+ {
+ // Invalid hex digit
+ return false;
+ }
+
+ ParsedUUID[i] = static_cast<Byte>((HighNibble << 4) | LowNibble);
+ }
+
+ // Parsed successfully
+ m_UUID = ParsedUUID;
+ return true;
+}
+
+
+
+
+
+AString cUUID::ToShortString() const
+{
+ AString ShortString(32, '\0');
+ for (size_t i = 0; i != m_UUID.size(); ++i)
+ {
+ Byte HighNibble = (m_UUID[i] >> 4) & 0x0f;
+ Byte LowNibble = m_UUID[i] & 0x0f;
+
+ ShortString[2 * i ] = ToHexDigit(HighNibble);
+ ShortString[2 * i + 1] = ToHexDigit(LowNibble);
+ }
+ return ShortString;
+}
+
+
+
+
+
+AString cUUID::ToLongString() const
+{
+ AString LongString = ToShortString();
+ LongString.reserve(36);
+
+ // Convert to long form by inserting the dashes
+ auto First = LongString.begin();
+ LongString.insert(First + 8, '-');
+ LongString.insert(First + 13, '-');
+ LongString.insert(First + 18, '-');
+ LongString.insert(First + 23, '-');
+
+ return LongString;
+}
+
+
+
+
+
+UInt8 cUUID::Version() const
+{
+ return static_cast<UInt8>((m_UUID[6] >> 4) & 0x0f);
+}
+
+
+
+
+
+UInt8 cUUID::Variant() const
+{
+ const Byte VariantBits = static_cast<Byte>((m_UUID[9] >> 5) & 0x07);
+
+ /* Variant bits format:
+ bits | variant | Description
+ -----|---------|----------------------
+ 0xx | 0 | Obsolete
+ 10x | 1 | Standard UUID
+ 110 | 2 | Microsoft Legacy GUID
+ 111 | 3 | Reserved
+ */
+
+ if ((VariantBits & 0x04) == 0)
+ {
+ return 0;
+ }
+ else if ((VariantBits & 0x02) == 0)
+ {
+ return 1;
+ }
+ else if ((VariantBits & 0x01) == 0)
+ {
+ return 2;
+ }
+ else
+ {
+ return 3;
+ }
+}
+
+
+
+
+
+std::array<Byte, 16> cUUID::ToRaw() const
+{
+ std::array<Byte, 16> Raw(m_UUID);
+ if (Variant() == 2)
+ {
+ // Convert to microsoft mixed-endian format
+ // First 3 components are host-endian, last 2 are network
+ auto First = reinterpret_cast<UInt32 *>(Raw.data());
+ *First = ntohl(*First);
+
+ auto Second = reinterpret_cast<UInt16 *>(&Raw[4]);
+ *Second = ntohs(*Second);
+
+ auto Third = Second + 1;
+ *Third = ntohs(*Third);
+ }
+
+ return Raw;
+}
+
+
+
+
+
+void cUUID::FromRaw(const std::array<Byte, 16> & a_Raw)
+{
+ m_UUID = a_Raw;
+ if (Variant() != 2)
+ {
+ // Standard big-endian formats
+ return;
+ }
+
+ // Convert from microsoft mixed-endian format
+ // First 3 components are host-endian, last 2 are network
+ auto First = reinterpret_cast<UInt32 *>(m_UUID.data());
+ *First = htonl(*First);
+
+ auto Second = reinterpret_cast<UInt16 *>(&m_UUID[4]);
+ *Second = htons(*Second);
+
+ auto Third = Second + 1;
+ *Third = htons(*Third);
+}
+
+
+
+
+
+cUUID cUUID::GenerateVersion3(const AString & a_Name)
+{
+ cUUID UUID;
+ // Generate an md5 checksum, and use it as base for the ID:
+ const Byte * ByteString = reinterpret_cast<const Byte *>(a_Name.data());
+ md5(ByteString, a_Name.length(), UUID.m_UUID.data());
+
+ // Insert version number
+ UUID.m_UUID[6] = (UUID.m_UUID[6] & 0x0f) | 0x30;
+
+ /* Insert variant number
+ Note that by using 1000 instead of 10xx we are losing 2 bits
+ but this is needed for compatibility with the old string uuid generator */
+ UUID.m_UUID[8] = (UUID.m_UUID[8] & 0x0f) | 0x80;
+
+ return UUID;
+}
+
+
+
+
diff --git a/src/UUID.h b/src/UUID.h
new file mode 100644
index 000000000..12fef3e39
--- /dev/null
+++ b/src/UUID.h
@@ -0,0 +1,100 @@
+// UUID.h
+
+// Declares the cUUID class representing a Universally Unique Identifier
+
+#pragma once
+
+
+// tolua_begin
+
+class cUUID
+{
+public:
+ /** Default constructed "nil" UUID */
+ cUUID():
+ m_UUID()
+ {
+ }
+
+ /** Lexicographically compare bytes with another UUID.
+ Returns:
+ 0 when equal to a_Other,
+ < 0 when less than a_Other,
+ > 0 when greater than a_Other */
+ int Compare(const cUUID & a_Other) const
+ {
+ return std::memcmp(m_UUID.data(), a_Other.m_UUID.data(), m_UUID.size());
+ }
+
+ /** Returns true if this contains the "nil" UUID with all bits set to 0 */
+ bool IsNil() const
+ {
+ return (m_UUID == std::array<Byte, 16>{{0}});
+ }
+
+ /** Tries to interpret the string as a short or long form UUID and assign from it.
+ On error, returns false and does not set the value. */
+ bool FromString(const AString & a_StringUUID);
+
+ /** Converts the UUID to a short form string (i.e without dashes). */
+ AString ToShortString() const;
+
+ /** Converts the UUID to a long form string (i.e. with dashes). */
+ AString ToLongString() const;
+
+ /** Returns the version number of the UUID. */
+ UInt8 Version() const;
+
+ /** Returns the variant number of the UUID. */
+ UInt8 Variant() const;
+
+ /** Generates a version 3, variant 1 UUID based on the md5 hash of a_Name. */
+ static cUUID GenerateVersion3(const AString & a_Name);
+
+ // tolua_end
+
+ /** Converts UUID to raw memory representation, respecting UUID variant. */
+ std::array<Byte, 16> ToRaw() const;
+
+ /** Assigns from raw memory representation, respecting UUID variant. */
+ void FromRaw(const std::array<Byte, 16> & a_Raw);
+
+private:
+ /** Binary UUID stored big-endian. */
+ std::array<Byte, 16> m_UUID;
+}; // tolua_export
+
+
+// Comparison operators:
+
+inline bool operator == (const cUUID & a_Lhs, const cUUID & a_Rhs)
+{
+ return (a_Lhs.Compare(a_Rhs) == 0);
+}
+
+inline bool operator != (const cUUID & a_Lhs, const cUUID & a_Rhs)
+{
+ return (a_Lhs.Compare(a_Rhs) != 0);
+}
+
+inline bool operator < (const cUUID & a_Lhs, const cUUID & a_Rhs)
+{
+ return (a_Lhs.Compare(a_Rhs) < 0);
+}
+
+inline bool operator <= (const cUUID & a_Lhs, const cUUID & a_Rhs)
+{
+ return (a_Lhs.Compare(a_Rhs) <= 0);
+}
+
+inline bool operator > (const cUUID & a_Lhs, const cUUID & a_Rhs)
+{
+ return (a_Lhs.Compare(a_Rhs) > 0);
+}
+
+inline bool operator >= (const cUUID & a_Lhs, const cUUID & a_Rhs)
+{
+ return (a_Lhs.Compare(a_Rhs) >= 0);
+}
+
+
diff --git a/src/World.cpp b/src/World.cpp
index 8d8ba90a5..04ca1709e 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -10,6 +10,7 @@
#include "SetChunkData.h"
#include "DeadlockDetect.h"
#include "LineBlockTracer.h"
+#include "UUID.h"
// Serializers
#include "WorldStorage/ScoreboardSerializer.h"
@@ -3241,7 +3242,7 @@ bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCa
-bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback)
+bool cWorld::DoWithPlayerByUUID(const cUUID & a_PlayerUUID, cPlayerListCallback & a_Callback)
{
return DoWithPlayerByUUID(a_PlayerUUID, std::bind(&cPlayerListCallback::Item, &a_Callback, std::placeholders::_1));
}
@@ -3250,7 +3251,7 @@ bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallbac
-bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cLambdaPlayerCallback a_Callback)
+bool cWorld::DoWithPlayerByUUID(const cUUID & a_PlayerUUID, cLambdaPlayerCallback a_Callback)
{
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
diff --git a/src/World.h b/src/World.h
index ed3bf9919..cbaf9cb95 100644
--- a/src/World.h
+++ b/src/World.h
@@ -50,6 +50,7 @@ class cCompositeChat;
class cSetChunkData;
class cBroadcaster;
class cDeadlockDetect;
+class cUUID;
typedef std::list< cPlayer * > cPlayerList;
typedef std::list< std::pair< std::unique_ptr<cPlayer>, cWorld * > > cAwaitingPlayerList;
@@ -287,8 +288,8 @@ public:
cPlayer * FindClosestPlayer(Vector3d a_Pos, float a_SightLimit, bool a_CheckLineOfSight = true);
/** Finds the player over his uuid and calls the callback */
- bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
- bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cLambdaPlayerCallback a_Callback); // Lambda version
+ bool DoWithPlayerByUUID(const cUUID & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+ bool DoWithPlayerByUUID(const cUUID & a_PlayerUUID, cLambdaPlayerCallback a_Callback); // Lambda version
void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player
@@ -548,7 +549,7 @@ public:
bool DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback & a_Callback); // Exported in ManualBindings.cpp
/** Calls the callback for the bed at the specified coords; returns false if there's no bed at those coords, true if found */
- bool DoWithBedAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBedCallback & a_Callback); // Exported in ManualBindings.cpp
+ virtual bool DoWithBedAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBedCallback & a_Callback) override; // Exported in ManualBindings.cpp
/** Calls the callback for the brewingstand at the specified coords; returns false if there's no brewingstand at those coords, true if found */
bool DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback); // Lua-acessible
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index 8c60aac0d..1e8543648 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -7,6 +7,7 @@
#include "EnchantmentSerializer.h"
#include "../ItemGrid.h"
#include "../StringCompression.h"
+#include "../UUID.h"
#include "FastNBT.h"
#include "../BlockEntities/BeaconEntity.h"
@@ -67,11 +68,11 @@ void cNBTChunkSerializer::Finish(void)
m_Writer.EndList();
}
- // If light not valid, reset it to all zeroes:
+ // If light not valid, reset it to defaults:
if (!m_IsLightValid)
{
- memset(m_BlockLight, 0, sizeof(m_BlockLight));
- memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight));
+ m_Data.FillBlockLight(0x00);
+ m_Data.FillSkyLight(0x0f);
}
// Check if "Entity" and "TileEntities" lists exists. MCEdit requires this.
@@ -382,7 +383,7 @@ void cNBTChunkSerializer::AddMobHeadEntity(cMobHeadEntity * a_MobHead)
// The new Block Entity format for a Mob Head. See: https://minecraft.gamepedia.com/Head#Block_entity
m_Writer.BeginCompound("Owner");
- m_Writer.AddString("Id", a_MobHead->GetOwnerUUID());
+ m_Writer.AddString("Id", a_MobHead->GetOwnerUUID().ToShortString());
m_Writer.AddString("Name", a_MobHead->GetOwnerName());
m_Writer.BeginCompound("Properties");
m_Writer.BeginList("textures", TAG_Compound);
@@ -679,9 +680,9 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
{
m_Writer.AddString("Owner", Wolf->GetOwnerName());
}
- if (!Wolf->GetOwnerUUID().empty())
+ if (!Wolf->GetOwnerUUID().IsNil())
{
- m_Writer.AddString("OwnerUUID", Wolf->GetOwnerUUID());
+ m_Writer.AddString("OwnerUUID", Wolf->GetOwnerUUID().ToShortString());
}
m_Writer.AddByte("Sitting", Wolf->IsSitting() ? 1 : 0);
m_Writer.AddByte("Angry", Wolf->IsAngry() ? 1 : 0);
@@ -709,9 +710,9 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
{
m_Writer.AddString("Owner", Ocelot->GetOwnerName());
}
- if (!Ocelot->GetOwnerUUID().empty())
+ if (!Ocelot->GetOwnerUUID().IsNil())
{
- m_Writer.AddString("OwnerUUID", Ocelot->GetOwnerUUID());
+ m_Writer.AddString("OwnerUUID", Ocelot->GetOwnerUUID().ToShortString());
}
m_Writer.AddByte("Sitting", Ocelot->IsSitting() ? 1 : 0);
m_Writer.AddInt ("CatType", Ocelot->GetOcelotType());
diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h
index 3637ea655..50d98a518 100644
--- a/src/WorldStorage/NBTChunkSerializer.h
+++ b/src/WorldStorage/NBTChunkSerializer.h
@@ -1,4 +1,4 @@
-
+
// NBTChunkSerializer.h
// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil
@@ -55,7 +55,7 @@ class cPainting;
class cNBTChunkSerializer :
- public cChunkDataSeparateCollector
+ public cChunkDataCopyCollector
{
public:
cChunkDef::BiomeMap m_Biomes;
@@ -69,15 +69,12 @@ public:
/** Close NBT tags that we've opened */
void Finish(void);
- bool IsLightValid(void) const {return m_IsLightValid; }
+ bool IsLightValid(void) const { return m_IsLightValid; }
protected:
- /* From cChunkDataSeparateCollector we inherit:
- - m_BlockTypes[]
- - m_BlockMetas[]
- - m_BlockLight[]
- - m_BlockSkyLight[] */
+ /* From cChunkDataCopyCollector we inherit:
+ - cChunkData m_Data */
cFastNBTWriter & m_Writer;
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 7aa3eb0cd..a3251481f 100755
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -515,23 +515,25 @@ bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_
// Save blockdata:
a_Writer.BeginList("Sections", TAG_Compound);
- size_t SliceSizeBlock = cChunkDef::Width * cChunkDef::Width * 16;
- size_t SliceSizeNibble = SliceSizeBlock / 2;
- const char * BlockTypes = reinterpret_cast<const char *>(Serializer.m_BlockTypes);
- const char * BlockMetas = reinterpret_cast<const char *>(Serializer.m_BlockMetas);
- #ifdef DEBUG_SKYLIGHT
- const char * BlockLight = reinterpret_cast<const char *>(Serializer.m_BlockSkyLight);
- #else
- const char * BlockLight = reinterpret_cast<const char *>(Serializer.m_BlockLight);
- #endif
- const char * BlockSkyLight = reinterpret_cast<const char *>(Serializer.m_BlockSkyLight);
- for (int Y = 0; Y < 16; Y++)
+ for (size_t Y = 0; Y != cChunkData::NumSections; ++Y)
{
+ auto Section = Serializer.m_Data.GetSection(Y);
+ if (Section == nullptr)
+ {
+ continue;
+ }
+
a_Writer.BeginCompound("");
- a_Writer.AddByteArray("Blocks", BlockTypes + static_cast<unsigned int>(Y) * SliceSizeBlock, SliceSizeBlock);
- a_Writer.AddByteArray("Data", BlockMetas + static_cast<unsigned int>(Y) * SliceSizeNibble, SliceSizeNibble);
- a_Writer.AddByteArray("SkyLight", BlockSkyLight + static_cast<unsigned int>(Y) * SliceSizeNibble, SliceSizeNibble);
- a_Writer.AddByteArray("BlockLight", BlockLight + static_cast<unsigned int>(Y) * SliceSizeNibble, SliceSizeNibble);
+ a_Writer.AddByteArray("Blocks", reinterpret_cast<const char *>(Section->m_BlockTypes), ARRAYCOUNT(Section->m_BlockTypes));
+ a_Writer.AddByteArray("Data", reinterpret_cast<const char *>(Section->m_BlockMetas), ARRAYCOUNT(Section->m_BlockMetas));
+
+ #ifdef DEBUG_SKYLIGHT
+ a_Writer.AddByteArray("BlockLight", reinterpret_cast<const char *>(Section->m_BlockSkyLight), ARRAYCOUNT(Section->m_BlockSkyLight));
+ #else
+ a_Writer.AddByteArray("BlockLight", reinterpret_cast<const char *>(Section->m_BlockLight), ARRAYCOUNT(Section->m_BlockLight));
+ #endif
+
+ a_Writer.AddByteArray("SkyLight", reinterpret_cast<const char *>(Section->m_BlockSkyLight), ARRAYCOUNT(Section->m_BlockSkyLight));
a_Writer.AddByte("Y", static_cast<unsigned char>(Y));
a_Writer.EndCompound();
}
@@ -1398,12 +1400,13 @@ cBlockEntity * cWSSAnvil::LoadMobHeadFromNBT(const cParsedNBT & a_NBT, int a_Tag
int ownerLine = a_NBT.FindChildByName(a_TagIdx, "Owner");
if (ownerLine >= 0)
{
- AString OwnerName, OwnerUUID, OwnerTexture, OwnerTextureSignature;
+ AString OwnerName, OwnerTexture, OwnerTextureSignature;
+ cUUID OwnerUUID;
currentLine = a_NBT.FindChildByName(ownerLine, "Id");
if (currentLine >= 0)
{
- OwnerUUID = a_NBT.GetString(currentLine);
+ OwnerUUID.FromString(a_NBT.GetString(currentLine));
}
currentLine = a_NBT.FindChildByName(ownerLine, "Name");
@@ -2526,7 +2529,7 @@ void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a
}
auto OwnerInfo = LoadEntityOwner(a_NBT, a_TagIdx);
- if (!OwnerInfo.first.empty() && !OwnerInfo.second.empty())
+ if (!OwnerInfo.first.empty() && !OwnerInfo.second.IsNil())
{
Monster->SetOwner(OwnerInfo.first, OwnerInfo.second);
Monster->SetIsTame(true);
@@ -2927,7 +2930,7 @@ void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N
}
auto OwnerInfo = LoadEntityOwner(a_NBT, a_TagIdx);
- if (!OwnerInfo.first.empty() && !OwnerInfo.second.empty())
+ if (!OwnerInfo.first.empty() && !OwnerInfo.second.IsNil())
{
Monster->SetOwner(OwnerInfo.first, OwnerInfo.second);
Monster->SetIsTame(true);
@@ -3064,43 +3067,39 @@ void cWSSAnvil::LoadPigZombieFromNBT(cEntityList & a_Entities, const cParsedNBT
-std::pair<AString, AString> cWSSAnvil::LoadEntityOwner(const cParsedNBT & a_NBT, int a_TagIdx)
+std::pair<AString, cUUID> cWSSAnvil::LoadEntityOwner(const cParsedNBT & a_NBT, int a_TagIdx)
{
// Load the owner information. OwnerUUID or Owner may be specified, possibly both:
- AString OwnerUUID, OwnerName;
+ AString OwnerName;
+ cUUID OwnerUUID;
int OwnerUUIDIdx = a_NBT.FindChildByName(a_TagIdx, "OwnerUUID");
if (OwnerUUIDIdx > 0)
{
- OwnerUUID = a_NBT.GetString(OwnerUUIDIdx);
+ OwnerUUID.FromString(a_NBT.GetString(OwnerUUIDIdx));
}
int OwnerIdx = a_NBT.FindChildByName(a_TagIdx, "Owner");
if (OwnerIdx > 0)
{
OwnerName = a_NBT.GetString(OwnerIdx);
}
- if (OwnerName.empty() && OwnerUUID.empty())
+ if (OwnerName.empty() && OwnerUUID.IsNil())
{
// There is no owner, bail out:
- return std::pair<AString, AString>();
+ return {};
}
// Convert name to UUID, if needed:
- if (OwnerUUID.empty())
+ if (OwnerUUID.IsNil())
{
// This entity has only playername stored (pre-1.7.6), look up the UUID
// The lookup is blocking, but we're running in a separate thread, so it's ok
OwnerUUID = cRoot::Get()->GetMojangAPI().GetUUIDFromPlayerName(OwnerName);
- if (OwnerUUID.empty())
+ if (OwnerUUID.IsNil())
{
// Not a known player, un-tame the entity by bailing out
- return std::pair<AString, AString>();
+ return {};
}
}
- else
- {
- // Normalize the UUID:
- OwnerUUID = cMojangAPI::MakeUUIDShort(OwnerUUID);
- }
// Convert UUID to name, if needed:
if (OwnerName.empty())
@@ -3110,11 +3109,11 @@ std::pair<AString, AString> cWSSAnvil::LoadEntityOwner(const cParsedNBT & a_NBT,
if (OwnerName.empty())
{
// Not a known player, un-tame the entity by bailing out
- return std::pair<AString, AString>();
+ return {};
}
}
- return std::make_pair(OwnerName, OwnerUUID);
+ return { OwnerName, OwnerUUID };
}
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index a53d8d8c4..0f32d1a2e 100755
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -20,6 +20,7 @@ class cItemGrid;
class cMonster;
class cProjectileEntity;
class cHangingEntity;
+class cUUID;
@@ -230,8 +231,8 @@ protected:
void LoadPigZombieFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
/** Loads the owner name and UUID from the entity at the specified NBT tag.
- Returns a pair of {name, uuid}. If the entity is not owned, both are empty strings. */
- std::pair<AString, AString> LoadEntityOwner(const cParsedNBT & a_NBT, int a_TagIdx);
+ Returns a pair of {name, uuid}. If the entity is not owned, name is an empty string and uuid is nil. */
+ std::pair<AString, cUUID> LoadEntityOwner(const cParsedNBT & a_NBT, int a_TagIdx);
/** Loads entity common data from the NBT compound; returns true if successful */
bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx);
diff --git a/tests/ByteBuffer/CMakeLists.txt b/tests/ByteBuffer/CMakeLists.txt
index 12c2f6e3c..06832848e 100644
--- a/tests/ByteBuffer/CMakeLists.txt
+++ b/tests/ByteBuffer/CMakeLists.txt
@@ -19,6 +19,7 @@ set (SHARED_HDRS
set (SRCS
ByteBufferTest.cpp
+ Stubs.cpp
)
source_group("Shared" FILES ${SHARED_SRCS} ${SHARED_HDRS})
diff --git a/tests/ByteBuffer/Stubs.cpp b/tests/ByteBuffer/Stubs.cpp
new file mode 100644
index 000000000..c3271ff31
--- /dev/null
+++ b/tests/ByteBuffer/Stubs.cpp
@@ -0,0 +1,16 @@
+
+// Stubs.cpp
+
+// Implements stubs of various Cuberite methods that are needed for linking but not for runtime
+// This is required so that we don't bring in the entire Cuberite via dependencies
+
+#include "Globals.h"
+#include "UUID.h"
+
+
+
+
+void cUUID::FromRaw(const std::array<Byte, 16> &){}
+
+
+
diff --git a/tests/Generating/LuaState_Declaration.inc b/tests/Generating/LuaState_Declaration.inc
index 4019b26c6..8b342e1b1 100644
--- a/tests/Generating/LuaState_Declaration.inc
+++ b/tests/Generating/LuaState_Declaration.inc
@@ -2,3 +2,8 @@
// LuaState_Declaration.inc
// Dummy include file needed for LuaState to compile successfully
+
+
+bool GetStackValue(int, cUUID *&);
+
+
diff --git a/tests/Generating/LuaState_Typedefs.inc b/tests/Generating/LuaState_Typedefs.inc
index 5eba7c6f8..e79633556 100644
--- a/tests/Generating/LuaState_Typedefs.inc
+++ b/tests/Generating/LuaState_Typedefs.inc
@@ -14,6 +14,7 @@ class cPluginLua;
class cBoundingBox;
template <typename T> class cItemCallback;
class cEntity;
+class cUUID;
diff --git a/tests/Generating/Stubs.cpp b/tests/Generating/Stubs.cpp
index d363576a5..396fa15da 100644
--- a/tests/Generating/Stubs.cpp
+++ b/tests/Generating/Stubs.cpp
@@ -6,9 +6,11 @@
#include "Globals.h"
#include "BlockInfo.h"
+#include "UUID.h"
#include "Bindings.h"
#include "Bindings/DeprecatedBindings.h"
#include "Bindings/LuaJson.h"
+#include "Bindings/LuaState.h"
#include "Bindings/ManualBindings.h"
#include "BlockEntities/BlockEntity.h"
#include "Blocks/BlockHandler.h"
@@ -332,3 +334,21 @@ cBlockEntity * cBlockEntity::Clone(int a_BlockX, int a_BlockY, int a_BlockZ)
+
+bool cLuaState::GetStackValue(int, cUUID *&)
+{
+ return false;
+}
+
+
+
+
+
+bool cUUID::FromString(const AString&)
+{
+ return false;
+}
+
+
+
+
diff --git a/tests/LuaThreadStress/LuaState_Declaration.inc b/tests/LuaThreadStress/LuaState_Declaration.inc
index 4019b26c6..8b342e1b1 100644
--- a/tests/LuaThreadStress/LuaState_Declaration.inc
+++ b/tests/LuaThreadStress/LuaState_Declaration.inc
@@ -2,3 +2,8 @@
// LuaState_Declaration.inc
// Dummy include file needed for LuaState to compile successfully
+
+
+bool GetStackValue(int, cUUID *&);
+
+
diff --git a/tests/LuaThreadStress/LuaState_Typedefs.inc b/tests/LuaThreadStress/LuaState_Typedefs.inc
index 5eba7c6f8..e79633556 100644
--- a/tests/LuaThreadStress/LuaState_Typedefs.inc
+++ b/tests/LuaThreadStress/LuaState_Typedefs.inc
@@ -14,6 +14,7 @@ class cPluginLua;
class cBoundingBox;
template <typename T> class cItemCallback;
class cEntity;
+class cUUID;
diff --git a/tests/LuaThreadStress/Stubs.cpp b/tests/LuaThreadStress/Stubs.cpp
index 709c7efe5..98fbf5b74 100644
--- a/tests/LuaThreadStress/Stubs.cpp
+++ b/tests/LuaThreadStress/Stubs.cpp
@@ -7,13 +7,14 @@
#include "Globals.h"
#include "BlockInfo.h"
#include "Bindings.h"
+#include "DeadlockDetect.h"
+#include "UUID.h"
#include "Bindings/DeprecatedBindings.h"
#include "Bindings/LuaJson.h"
#include "Bindings/ManualBindings.h"
#include "BlockEntities/BlockEntity.h"
#include "Blocks/BlockHandler.h"
#include "Generating/ChunkDesc.h"
-#include "DeadlockDetect.h"
@@ -333,3 +334,21 @@ cBlockEntity * cBlockEntity::Clone(int a_BlockX, int a_BlockY, int a_BlockZ)
+
+bool cLuaState::GetStackValue(int, cUUID *&)
+{
+ return false;
+}
+
+
+
+
+
+bool cUUID::FromString(const AString &)
+{
+ return true;
+}
+
+
+
+