diff options
78 files changed, 2326 insertions, 519 deletions
diff --git a/.gitmodules b/.gitmodules index 93fac9d1f..20f8ea103 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "lib/libevent"] path = lib/libevent url = https://github.com/mc-server/libevent.git +[submodule "lib/TCLAP"] + path = lib/TCLAP + url = https://github.com/mc-server/TCLAP.git diff --git a/.travis.yml b/.travis.yml index 6dde5b62b..e39cdbc14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,39 +1,27 @@ language: cpp -compiler: clang -before_install: -# - if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi +compiler: +- clang +- gcc - # g++4.8 +before_install: - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo apt-get update -qq install: - # g++4.8 and clang - sudo apt-get install -qq g++-4.8 + - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90 + - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 90 # lua, needed for style checking and possibly later on for bindings generation - sudo apt-get install -qq lua5.1 - - # g++4.8 - - if [ "$CXX" == "g++" ]; then export CXX="g++-4.8"; export CC="gcc-4.8"; fi -# Build MCServer script: ./CIbuild.sh -#after_success: -# - ./uploadCoverage.sh - env: - TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer - TRAVIS_MCSERVER_BUILD_TYPE=DEBUG MCSERVER_PATH=./MCServer_debug -#matrix: -# include: -# - compiler: gcc -# env: TRAVIS_MCSERVER_BUILD_TYPE=COVERAGE MCSERVER_PATH=./MCServer - -# Notification Settings notifications: email: on_success: change diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bb780eba..ae83662c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,9 @@ if(MSVC OR MSVC_IDE) endif() endif() +set(BUILD_TOOLS OFF CACHE BOOL "") +set(SELF_TEST OFF CACHE BOOL "") + # This has to be done before any flags have been set up. if(${BUILD_TOOLS}) message("Building tools") diff --git a/CONTRIBUTORS b/CONTRIBUTORS index a25c6f06b..4a6850a2f 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -2,11 +2,13 @@ Many people have contributed to MCServer, and this list attempts to broadcast at BasedDoge (Donated AlchemistVillage prefabs) bearbin (Alexander Harkness) +beeduck derouinw Diusrex Duralex FakeTruth (founder) Howaner +jasperarmstrong keyboard Lapayo Luksor @@ -16,15 +18,17 @@ Masy98 mborland mgueydan MikeHunsinger +missingchar (mathias-github) mtilden nesco p-mcgowan rs2k SafwatHalaby (Safwat Halaby) SamJBarney +Seadragon91 (Lukas Pioch) Sofapriester SphinxC0re -STR_Warrior +NiLSPACE (formerly STR_Warrior) structinf (xdot) Sxw1212 Taugeshtu @@ -33,7 +37,7 @@ tonibm19 UltraCoderRU WebFreak001 worktycho -xoft +xoft (Mattes Dolak/madmaxoft on GH) Yeeeeezus (Donated AlchemistVillage prefabs) Please add yourself to this list if you contribute to MCServer. diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 2c26ccecc..4af01c0a4 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -29,7 +29,7 @@ g_APIDesc = { ConstantName = { Notes = "Notes about the constant" }, } , - + ConstantGroups = { GroupName1 = -- GroupName1 is used as the HTML anchor name @@ -270,7 +270,7 @@ g_APIDesc = <h3>Special strategies</h3> <p>For each strategy, evaluate the table rows from top downwards, the first match wins.</p> - + <p> <strong>msDifference</strong> - changes all the blocks which are the same to air. Otherwise the source block gets placed. </p> @@ -282,8 +282,8 @@ g_APIDesc = <td> B </td><td> B </td><td> Air </td><td> The blocks are the same so we get air. </td> </tr> </tbody></table> - - + + <p> <strong>msLake</strong> - used for merging areas with lava and water lakes, in the appropriate generator. </p> @@ -363,7 +363,7 @@ g_APIDesc = <td> A </td><td> non-A </td><td> A </td><td> Differing blocks are kept from 'self' </td> </tr> </tbody></table> - + <p> <strong>msSimpleCompare</strong> - the blocks that are the same in both areas are replaced with air, all the differing blocks are replaced with stone. Meta is used in the comparison, too, two blocks of the @@ -535,7 +535,7 @@ g_APIDesc = function OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc) -- Get the topmost block coord: local Height = a_ChunkDesc:GetHeight(0, 0); - + -- Create a sign there: a_ChunkDesc:SetBlockTypeMeta(0, Height + 1, 0, E_BLOCK_SIGN_POST, 0); local BlockEntity = a_ChunkDesc:GetBlockEntity(0, Height + 1, 0); @@ -666,7 +666,7 @@ end</pre> }, }, -- AdditionalInfo }, -- cCompositeChat - + cCraftingGrid = { Desc = [[ @@ -738,7 +738,7 @@ local Hash = cCryptoHash.sha1HexString("DataToHash") </pre></p> <p>Each cryptographic hash has two variants, one returns the hash as a raw binary string, the other returns the hash as a hex-encoded string twice as long as the binary string. ]], - + Functions = { md5 = { Params = "Data", Return = "string", Notes = "(STATIC) Calculates the md5 hash of the data, returns it as a raw (binary) string of 16 characters." }, @@ -747,7 +747,7 @@ local Hash = cCryptoHash.sha1HexString("DataToHash") sha1HexString = { Params = "Data", Return = "string", Notes = "(STATIC) Calculates the sha1 hash of the data, returns it as a hex-encoded string of 40 characters." }, }, }, -- cCryptoHash - + cEnchantments = { Desc = [[ @@ -1038,7 +1038,7 @@ cFile:Delete("/usr/bin/virus.exe"); }, Inherits = "cEntity", }, - + cIniFile = { Desc = [[ @@ -1073,7 +1073,7 @@ ValueName0=SomeOtherValue { constructor = { Params = "", Return = "cIniFile", Notes = "Creates a new empty cIniFile object." }, AddHeaderComment = { Params = "Comment", Return = "", Notes = "Adds a comment to be stored in the file header." }, - AddKeyComment = + AddKeyComment = { { Params = "KeyID, Comment", Return = "", Notes = "Adds a comment to be stored in the file under the specified key" }, { Params = "KeyName, Comment", Return = "", Notes = "Adds a comment to be stored in the file under the specified key" }, @@ -1117,12 +1117,12 @@ ValueName0=SomeOtherValue { Params = "KeyName", Return = "number", Notes = "Returns the number of comments under the specified key" }, }, GetNumKeys = { Params = "", Return = "number", Notes = "Returns the total number of keys. This is the range for the KeyID (0 .. GetNumKeys() - 1)" }, - GetNumValues = + GetNumValues = { { Params = "KeyID", Return = "number", Notes = "Returns the number of values stored under the specified key." }, { Params = "KeyName", Return = "number", Notes = "Returns the number of values stored under the specified key." }, }, - GetValue = + GetValue = { { Params = "KeyName, ValueName", Return = "string", Notes = "Returns the value of the specified name under the specified key. Returns an empty string if the value doesn't exist." }, { Params = "KeyID, ValueID", Return = "string", Notes = "Returns the value of the specified name under the specified key. Returns an empty string if the value doesn't exist." }, @@ -1130,7 +1130,7 @@ ValueName0=SomeOtherValue GetValueB = { Params = "KeyName, ValueName", Return = "bool", Notes = "Returns the value of the specified name under the specified key, as a bool. Returns false if the value doesn't exist." }, GetValueF = { Params = "KeyName, ValueName", Return = "number", Notes = "Returns the value of the specified name under the specified key, as a floating-point number. Returns zero if the value doesn't exist." }, GetValueI = { Params = "KeyName, ValueName", Return = "number", Notes = "Returns the value of the specified name under the specified key, as an integer. Returns zero if the value doesn't exist." }, - GetValueName = + GetValueName = { { Params = "KeyID, ValueID", Return = "string", Notes = "Returns the name of the specified value Inverse for FindValue()." }, { Params = "KeyName, ValueID", Return = "string", Notes = "Returns the name of the specified value Inverse for FindValue()." }, @@ -1141,7 +1141,7 @@ ValueName0=SomeOtherValue GetValueSetI = { Params = "KeyName, ValueName, Default", Return = "number", Notes = "Returns the value of the specified name under the specified key, as an integer. If the value doesn't exist, creates it with the specified default." }, HasValue = { Params = "KeyName, ValueName", Return = "bool", Notes = "Returns true if the specified value is present." }, ReadFile = { Params = "FileName, [AllowExampleFallback]", Return = "bool", Notes = "Reads the values from the specified file. Previous in-memory contents are lost. If the file cannot be opened, and AllowExample is true, another file, \"filename.example.ini\", is loaded and then saved as \"filename.ini\". Returns true if successful, false if not." }, - SetValue = + SetValue = { { Params = "KeyID, ValueID, NewValue", Return = "bool", Notes = "Overwrites the specified value with a new value. If the specified value doesn't exist, returns false (doesn't add)." }, { Params = "KeyName, ValueName, NewValue, [CreateIfNotExists]", Return = "bool", Notes = "Overwrites the specified value with a new value. If CreateIfNotExists is true (default) and the value doesn't exist, it is first created. Returns true if the value was successfully set, false if not (didn't exists, CreateIfNotExists false)." }, @@ -2061,6 +2061,7 @@ a_Player:OpenWindow(Window); GetPrimaryServerVersion = { Params = "", Return = "number", Notes = "Returns the servers primary server version." }, GetProtocolVersionTextFromInt = { Params = "Protocol Version", Return = "string", Notes = "Returns the Minecraft version from the given Protocol. If there is no version found, it returns 'Unknown protocol(Parameter)'" }, GetServer = { Params = "", Return = "{{cServer|cServer}}", Notes = "Returns the cServer object." }, + GetServerUpTime = { Params = "", Return = "number", Notes = "Returns the uptime of the server in seconds." }, GetTotalChunkCount = { Params = "", Return = "number", Notes = "Returns the amount of loaded chunks." }, GetVirtualRAMUsage = { Params = "", Return = "number", Notes = "Returns the amount of virtual RAM that the entire MCServer process is using, in KiB. Negative if the OS doesn't support this query." }, GetWebAdmin = { Params = "", Return = "{{cWebAdmin|cWebAdmin}}", Notes = "Returns the cWebAdmin object." }, @@ -2146,7 +2147,7 @@ end ShouldAuthenticate = { Params = "", Return = "bool", Notes = "Returns true iff the server is set to authenticate players (\"online mode\")." }, }, }, -- cServer - + cStringCompression = { Desc = [[ @@ -2157,7 +2158,7 @@ end local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress") </pre> ]], - + Functions = { CompressStringGZIP = {Params = "string", Return = "string", Notes = "Compress a string using GZIP"}, @@ -2205,7 +2206,7 @@ local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress") }, Inherits = "cEntity", }, - + cWebPlugin = { Desc = "", @@ -2361,7 +2362,7 @@ local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress") GetGeneratorQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks that are queued in the chunk generator." }, GetHeight = { Params = "BlockX, BlockZ", Return = "number", Notes = "Returns the maximum height of the particula block column in the world. If the chunk is not loaded, it waits for it to load / generate. <b>WARNING</b>: Do not use, Use TryGetHeight() instead for a non-waiting version, otherwise you run the risk of a deadlock!" }, GetIniFileName = { Params = "", Return = "string", Notes = "Returns the name of the world.ini file that the world uses to store the information." }, - GetLightingQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks in the lighting thread's queue." }, + GetLightingQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks in the lighting thread's queue." }, GetLinkedEndWorldName = { Params = "", Return = "string", Notes = "Returns the name of the end world this world is linked to." }, GetLinkedNetherWorldName = { Params = "", Return = "string", Notes = "Returns the name of the Netherworld linked to this world." }, GetLinkedOverworldName = { Params = "", Return = "string", Notes = "Returns the name of the world this world is linked to." }, @@ -2514,7 +2515,7 @@ World:ForEachEntity( if not(a_Entity:IsMob()) then return; end - + -- Get the cMonster out of cEntity, now that we know the entity represents one. local Monster = tolua.cast(a_Entity, "cMonster"); if (Monster:GetMobType() == mtSpider) then @@ -2565,7 +2566,7 @@ end } }, }, -- ItemCategory - + lxp = { Desc = [[ @@ -2635,7 +2636,7 @@ local Callbacks = { CharacterData = function(a_Parser, a_String) LOG(string.rep(" ", Depth) .. "* " .. a_String); end - + EndElement = function(a_Parser, a_ElementName) Depth = Depth - 1; LOG(string.rep(" ", Depth) .. "- " .. a_ElementName); @@ -2671,12 +2672,12 @@ Parser:close(); }, }, -- AdditionalInfo }, -- lxp - + sqlite3 = { Desc = [[ ]], - + Functions = { complete = { Params = "string", Return = "bool", Notes = "Returns true if the string sql comprises one or more complete SQL statements and false otherwise." }, @@ -2700,7 +2701,7 @@ myDB:close() version = { Return = "string", Notes = "Returns a string with SQLite version information, in the form 'x.y[.z]'." }, }, }, - + TakeDamageInfo = { Desc = [[ @@ -2994,7 +2995,7 @@ end "WriteHtmlHook", "WriteStats", }, - + IgnoreConstants = { "cChestEntity.__cBlockEntityWindowOwner__", @@ -3003,12 +3004,12 @@ end "cHopperEntity.__cBlockEntityWindowOwner__", "cLuaWindow.__cItemGrid__cListener__", }, - + IgnoreVariables = { "__.*__", -- tolua exports multiple inheritance this way } , - + ExtraPages = { -- No sorting is provided for these, they will be output in the same order as defined here @@ -3020,7 +3021,3 @@ end { FileName = "WebWorldThreads.html", Title = "Webserver vs World threads" }, } } ; - - - - diff --git a/MCServer/Plugins/APIDump/Hooks/OnEntityChangedWorld.lua b/MCServer/Plugins/APIDump/Hooks/OnEntityChangedWorld.lua new file mode 100644 index 000000000..6675fdbe0 --- /dev/null +++ b/MCServer/Plugins/APIDump/Hooks/OnEntityChangedWorld.lua @@ -0,0 +1,28 @@ +return +{ + HOOK_ENTITY_CHANGED_WORLD = + { + CalledWhen = "After a entity has changed the world.", + DefaultFnName = "OnEntityChangedWorld", -- also used as pagename + Desc = [[ + This hook is called after the server has moved the {{cEntity|entity}} to the given world. This is an information-only + callback, the entity is already in the new world.<p> + See also the {{OnEntityChangingWorld|HOOK_ENTITY_CHANGING_WORLD}} hook for a similar hook called before the + entity is moved to the new world. + ]], + Params = + { + { Name = "Entity", Type = "{{cEntity}}", Notes = "The entity that has changed the world" }, + { Name = "World", Type = "{{cWorld}}", Notes = "The world from which the entity has come" }, + }, + Returns = [[ + If the function returns false or no value, the next plugin's callback is called. If the function + returns true, no other callback is called for this event. + ]], + }, -- HOOK_ENTITY_CHANGED_WORLD +} + + + + + diff --git a/MCServer/Plugins/APIDump/Hooks/OnEntityChangingWorld.lua b/MCServer/Plugins/APIDump/Hooks/OnEntityChangingWorld.lua new file mode 100644 index 000000000..521f829c7 --- /dev/null +++ b/MCServer/Plugins/APIDump/Hooks/OnEntityChangingWorld.lua @@ -0,0 +1,29 @@ +return +{ + HOOK_ENTITY_CHANGING_WORLD = + { + CalledWhen = "Before a entity is changing the world.", + DefaultFnName = "OnEntityChangingWorld", -- also used as pagename + Desc = [[ + This hook is called before the server moves the {{cEntity|entity}} to the given world. Plugins may + refuse the changing of the entity to the new world.<p> + See also the {{OnEntityChangedWorld|HOOK_ENTITY_CHANGED_WORLD}} hook for a similar hook is called after the + entity has been moved to the world. + ]], + Params = + { + { Name = "Entity", Type = "{{cEntity}}", Notes = "The entity that wants to change the world" }, + { Name = "World", Type = "{{cWorld}}", Notes = "The world to which the entity wants to change" }, + }, + Returns = [[ + If the function returns false or no value, the next plugin's callback is called. If the function + returns true, no other callback is called for this event and the change of the entity to the world is + cancelled. + ]], + }, -- HOOK_ENTITY_CHANGING_WORLD +} + + + + + diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core -Subproject ea0ab964d568630fd4f2b52954186f2851a769e +Subproject 5171b43807ff699a6b239ad4969520730b3748a diff --git a/MCServer/crafting.txt b/MCServer/crafting.txt index e7d11ab92..daac9e098 100644 --- a/MCServer/crafting.txt +++ b/MCServer/crafting.txt @@ -322,7 +322,7 @@ MushroomStew = Bowl, * | BrownMushroom, * | RedMushroom, * Bread = Wheat, 1:1, 2:1, 3:1 Sugar = Sugarcane, * Cake = MilkBucket, 1:1, 2:1, 3:1 | Sugar, 1:2, 3:2 | Egg, 2:2 | Wheat, 1:3, 2:3, 3:3 -Cookie = Wheat, *, * | CocoaBeans, * +Cookie, 8 = Wheat, *, * | CocoaBeans, * GoldenApple = RedApple, 2:2 | GoldIngot, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 EnchantedGoldenApple = RedApple, 2:2 | GoldBlock, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 Melon = MelonSlice, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 diff --git a/MCServer/items.ini b/MCServer/items.ini index daf366654..5eb04619c 100644 --- a/MCServer/items.ini +++ b/MCServer/items.ini @@ -815,7 +815,7 @@ rawrabbit=411 cookedrabbit=412 rabbitstew=413 rabbitsoup=413 -rabbitsfood=414 +rabbitsfoot=414 rabbithide=415 armorstand=416 ironhorsearmor=417 @@ -22,7 +22,11 @@ For Windows, you just need to download a file and extract it: - [Windows 32 bit](http://builds.cuberite.org/job/MCServer%20Windows%20x86/lastSuccessfulBuild/artifact/Install/MCServer.zip) - [Windows 64 bit](http://builds.cuberite.org/job/MCServer%20Windows%20x64/lastSuccessfulBuild/artifact/Install/MCServer.zip) -For other operating systems you need to download and compile yourself. There is also an archive of binary builds on the buildserver: http://builds.cuberite.org +For other operating systems you need to download and compile yourself. This can be done either manually, or with this automatic script: + + bash -c "$(wget -O - https://raw.githubusercontent.com/mc-server/MCServer/master/compile.sh)" + +There is also an archive of binary builds on the buildserver: http://builds.cuberite.org Compiling the server yourself has other benefits: you may get better performance performance (1.5-3x as fast) and it supports more operating systems. See the [COMPILING.md](https://github.com/mc-server/MCServer/blob/master/COMPILING.md) file for more details. @@ -36,9 +40,7 @@ Check out the [CONTRIBUTING.md](https://github.com/mc-server/MCServer/blob/maste Other Stuff ----------- -For other stuff, including plugins and discussion, check the [forums](http://forum.mc-server.org) and [Plugin API](http://mc-server.xoft.cz/LuaAPI/). - -Earn bitcoins for commits or donate to reward the MCServer developers: [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74) +For other stuff, including plugins and discussion, check out the [forums](http://forum.mc-server.org) and [Plugin API](http://mc-server.xoft.cz/LuaAPI/). Support Us on Gratipay: [![Support via Gratipay](http://img.shields.io/gittip/cuberite_team.svg)](https://www.gratipay.com/cuberite_team) diff --git a/SetFlags.cmake b/SetFlags.cmake index 0d1bd99b3..92046b688 100644 --- a/SetFlags.cmake +++ b/SetFlags.cmake @@ -240,6 +240,11 @@ macro(set_exe_flags) # we support non-IEEE 754 fpus so can make no guarentees about error add_flags_cxx("-ffast-math") + if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + # backtrace() and friends are in libexecinfo + add_flags_lnk("-lexecinfo") + endif() + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") get_clang_version() if ("${CLANG_VERSION}" VERSION_LESS 3.0) @@ -263,6 +268,10 @@ macro(set_exe_flags) # flags introduced in 3.2 add_flags_cxx("-Wno-documentation") endif() + if ("${CLANG_VERSION}" VERSION_GREATER 3.5) + # Use this flag to ignore error for a reserved macro problem in sqlite 3 + add_flags_cxx("-Wno-reserved-id-macro") + endif() endif() endif() diff --git a/compile.sh b/compile.sh new file mode 100755 index 000000000..329d79d81 --- /dev/null +++ b/compile.sh @@ -0,0 +1,199 @@ +#|| goto :windows_detected + +# Do we already have a repo? +if [[ -d .git && -f easyinstall.sh && -f MakeLuaAPI.cmd ]]; then # A good enough indicator that we're in the MCServer git repo. +cd ../ +echo "MCServer repository detected. This should make the process faster, especially if you compiled before." +fi + +# Error functions. +function error +{ + echo + echo "-----------------" + echo "Script aborted, reason:" + echo $1 + exit -1 +} + +function missingDepsExit +{ + echo + echo "Please install the dependencies, then come back." + echo + exit -2 +} + + +# Echo: Greetings. +echo +echo "Hello, this script will download and compile Cuberite/MCServer." +echo "On subsequent runs, it will update your MCServer." +echo "The compilation and download will occur in the current directory." +echo "If you're updating, you should run <Path to MCServer>/MCServer/compile.sh" +echo "Compiling from source takes time, but it usually generates better executables." +echo "If you prefer ready-to-use binaries or if you want more info, please visit:" +echo "http://cuberite.org/" +echo "http://mc-server.org/" + +MISSING_PROGRAMS="" + +# Compiler check. +GCC_EXISTS=0 +CLANG_EXISTS=0 +g++ --help > /dev/null 2> /dev/null && GCC_EXISTS=1 +clang --help > /dev/null 2> /dev/null && CLANG_EXISTS=1 +if [[ $GCC_EXISTS == 0 && $CLANG_EXISTS == 0 ]]; then +MISSING_PROGRAMS="gcc g++" +fi + +# Depdendency check. +while read program; do +$program --help > /dev/null 2> /dev/null || MISSING_PROGRAMS="$MISSING_PROGRAMS $program" +done <<"EOF" +git +make +cmake +EOF +if [[ $MISSING_PROGRAMS != "" ]]; then + echo + echo "-----------------" + echo "You have missing compilation dependencies:" + echo $MISSING_PROGRAMS + echo + + # apt-get guide. + apt-get --help > /dev/null 2> /dev/null && \ + echo "You can install the missing depndencies via:" && \ + echo -n "sudo apt-get install " && echo $MISSING_PROGRAMS && missingDepsExit + + # yum guide. + yum --help > /dev/null 2> /dev/null && \ + echo "You can install the missing depndencies via:" && \ + echo -n "sudo yum install " && echo $MISSING_PROGRAMS && missingDepsExit + + # rpm guide. + rpm --help > /dev/null 2> /dev/null && \ + echo "You can install the missing depndencies via:" && \ + echo -n "sudo rpm -i " && echo $MISSING_PROGRAMS && missingDepsExit + + # pacman guide. + pacman --help > /dev/null 2> /dev/null && \ + echo "You can install the missing depndencies via:" && \ + echo -n "sudo pacman -S " && echo $MISSING_PROGRAMS && missingDepsExit + + missingDepsExit +fi + +exit +# Echo: Branch choice. +echo +echo "You can choose between 2 branches:" +echo "* (S)Stable: (Coming soon) Choose the stable branch if you want the most reliable server." +echo " As of now, Stable is not yet available, please use testing instead." +echo +echo "* (T)Testing: The testing branch is less stable," +echo " but using it and finding and reporting bugs helps us a lot!" +echo +echo "* (D)Dev: The least stable of the three. (Master branch)" +echo " Choose the development branch if you are feeling adventurous and" +echo " want to try new, bleeding edge features." +echo + + +# Input: Branch choice. +echo -n "Choose the branch (s/t/d): " +read BRANCH +if [[ ($BRANCH == "s") || ($BRANCH == "S" ) ]]; then + #BRANCH="stable" + error "We don't have a stable branch yet, please use testing, sorry." +elif [[ ($BRANCH == "t") || ($BRANCH == "T" ) ]]; then + BRANCH="testing" +elif [[ ($BRANCH == "d") || ($BRANCH == "D" ) ]]; then + BRANCH="master" +else + error "Unrecognized user input." +fi + +# Echo: Compile mode choice. +echo +echo "Choose compile mode:" +echo "* (N)Normal: Compiles normally." +echo +echo "* (D)Debug: Compiles in debug mode. Makes your console and crashes much more verbose." +echo " But it costs performance." +echo +echo "Note that the script will connect to the internet in order to fetch code after this step." +echo "It will then compile your program." +echo + +# Input: Compile mode choice. +echo -n "Choose compile mode: (n/d): " +read BUILDTYPE +if [[ ($BUILDTYPE == "d") || ($BUILDTYPE == "D") ]]; then + BUILDTYPE="Debug" +elif [[ ($BUILDTYPE == "n") || ($BUILDTYPE == "N") ]]; then + BUILDTYPE="Release" +else + error "Unrecognized user input." +fi + + +# Echo: Downloading began. +echo +echo " --- Downloading MCServer's source code from the $BRANCH branch..." + + +# Git: Clone. +if [ ! -d MCServer ]; then + echo " --- Looks like your first run, cloning the whole code..." + git clone https://github.com/mc-server/MCServer.git +fi + + +# Git: Fetch. +pushd MCServer +echo " --- Updating the $BRANCH branch..." +git fetch origin $BRANCH || error "git fetch failed" +git checkout $BRANCH || error "git checkout failed" +git merge origin/$BRANCH || error "git merge failed" + + +# Git: Submodules. +echo " --- Updating submodules..." +git submodule init +git submodule update + + +# Cmake. +echo " --- Running cmake..." +popd +if [ ! -d build-mcserver ]; then mkdir build-mcserver; fi +pushd build-mcserver +cmake ../MCServer/ -DCMAKE_BUILD_TYPE=$BUILDTYPE || error "cmake failed" + + +# Make. +echo " --- Compiling..." +make -j`nproc` || error "Compiling failed" +echo + + +# Echo: Compilation complete. +popd +pushd MCServer/MCServer +echo +echo "-----------------" +echo "Compilation done!" +echo +echo "Cuberite awaits you at:" +echo "`pwd`/MCServer" +echo +echo "Enjoy :)" +popd +exit 0 + +:windows_detected +echo "This script is not available for Windows yet, sorry." +echo "You can still download the Windows binaries from: http://mc-server.org" + diff --git a/lib/TCLAP b/lib/TCLAP new file mode 160000 +Subproject 12cee38782897cfe60a1611615c200c45cd99ea diff --git a/lib/sqlite/CMakeLists.txt b/lib/sqlite/CMakeLists.txt index 993dac146..b9471c1f2 100644 --- a/lib/sqlite/CMakeLists.txt +++ b/lib/sqlite/CMakeLists.txt @@ -25,7 +25,7 @@ endif() # FreeBSD requires us to define this to get POSIX 2001 standard if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") - add_flags_cxx(-D__POSIX_VISIBLE=200112) + add_flags_cxx("-D_XOPEN_SOURCE=600") endif() add_library(sqlite ${SOURCE}) diff --git a/lib/tolua++/CMakeLists.txt b/lib/tolua++/CMakeLists.txt index 12054323b..02883397e 100644 --- a/lib/tolua++/CMakeLists.txt +++ b/lib/tolua++/CMakeLists.txt @@ -53,4 +53,8 @@ if(UNIX) target_link_libraries(tolua m ${DYNAMIC_LOADER}) endif() +if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + add_flags_lnk(-L/usr/local/lib) +endif() + target_link_libraries(tolua tolualib lua) diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp index 3ee5ca498..5becbba92 100644 --- a/src/Bindings/ManualBindings_World.cpp +++ b/src/Bindings/ManualBindings_World.cpp @@ -530,7 +530,7 @@ static int tolua_cWorld_TryGetHeight(lua_State * tolua_S) // Call the implementation: int Height = 0; bool res = self->TryGetHeight(BlockX, BlockZ, Height); - L.Push(res ? 1 : 0); + L.Push(res); if (res) { L.Push(Height); diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index d0c2bcefa..1330bca0d 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -56,6 +56,8 @@ public: virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0; virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0; + virtual bool OnEntityChangingWorld (cEntity & a_Entity, cWorld & a_World) = 0; + virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) = 0; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) = 0; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 76d3557a4..234bf579b 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -534,6 +534,54 @@ bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_E +bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGING_WORLD]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Entity, &a_World, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGED_WORLD]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Entity, &a_World, res); + if (res) + { + return true; + } + } + return false; +} + + + + + bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) { cCSLock Lock(m_CriticalSection); @@ -1884,6 +1932,8 @@ const char * cPluginLua::GetHookFnName(int a_HookType) case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect"; case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation"; case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect"; + case cPluginManager::HOOK_ENTITY_CHANGING_WORLD: return "OnEntityChangingWorld"; + case cPluginManager::HOOK_ENTITY_CHANGED_WORLD: return "OnEntityChangedWorld"; case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport"; case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand"; case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake"; diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index 524c249b0..a763cdfdf 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -115,6 +115,8 @@ public: virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override; + virtual bool OnEntityChangingWorld (cEntity & a_Entity, cWorld & a_World) override; + virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) override; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) override; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; @@ -177,7 +179,7 @@ public: bool CanAddOldStyleHook(int a_HookType); // cWebPlugin overrides - virtual const AString GetWebTitle(void) const {return GetName(); } + virtual const AString GetWebTitle(void) const override {return GetName(); } virtual AString HandleWebRequest(const HTTPRequest & a_Request) override; /** Adds a new web tab to webadmin. diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index db2493955..5b6bec728 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -118,7 +118,7 @@ void cPluginManager::ReloadPluginsNow(void) -void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) +void cPluginManager::ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings) { LOG("-- Loading Plugins --"); @@ -130,7 +130,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) RefreshPluginList(); // Load the plugins: - AStringVector ToLoad = GetFoldersToLoad(a_SettingsIni); + AStringVector ToLoad = GetFoldersToLoad(a_Settings); for (auto & pluginFolder: ToLoad) { LoadPlugin(pluginFolder); @@ -157,16 +157,16 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) -void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni) +void cPluginManager::InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings) { - a_SettingsIni.AddKeyName("Plugins"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=Debuggers"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump"); - a_SettingsIni.AddValue("Plugins", "Plugin", "Core"); - a_SettingsIni.AddValue("Plugins", "Plugin", "TransAPI"); - a_SettingsIni.AddValue("Plugins", "Plugin", "ChatLog"); + a_Settings.AddKeyName("Plugins"); + a_Settings.AddKeyComment("Plugins", " Plugin=Debuggers"); + a_Settings.AddKeyComment("Plugins", " Plugin=HookNotify"); + a_Settings.AddKeyComment("Plugins", " Plugin=ChunkWorx"); + a_Settings.AddKeyComment("Plugins", " Plugin=APIDump"); + a_Settings.AddValue("Plugins", "Plugin", "Core"); + a_Settings.AddValue("Plugins", "Plugin", "TransAPI"); + a_Settings.AddValue("Plugins", "Plugin", "ChatLog"); } @@ -525,6 +525,42 @@ bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d & +bool cPluginManager::CallHookEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) +{ + FIND_HOOK(HOOK_ENTITY_CHANGING_WORLD); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnEntityChangingWorld(a_Entity, a_World)) + { + return true; + } + } + return false; +} + + + + +bool cPluginManager::CallHookEntityChangedWorld(cEntity & a_Entity, cWorld & a_World) +{ + FIND_HOOK(HOOK_ENTITY_CHANGED_WORLD); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnEntityChangedWorld(a_Entity, a_World)) + { + return true; + } + } + return false; +} + + + + bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result) { FIND_HOOK(HOOK_EXECUTE_COMMAND); @@ -1896,25 +1932,23 @@ size_t cPluginManager::GetNumLoadedPlugins(void) const -AStringVector cPluginManager::GetFoldersToLoad(cIniFile & a_SettingsIni) +AStringVector cPluginManager::GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings) { // Check if the Plugins section exists. - int KeyNum = a_SettingsIni.FindKey("Plugins"); - if (KeyNum == -1) + if (!a_Settings.KeyExists("Plugins")) { - InsertDefaultPlugins(a_SettingsIni); - KeyNum = a_SettingsIni.FindKey("Plugins"); + InsertDefaultPlugins(a_Settings); } // Get the list of plugins to load: AStringVector res; - int NumPlugins = a_SettingsIni.GetNumValues(KeyNum); - for (int i = 0; i < NumPlugins; i++) + auto Values = a_Settings.GetValues("Plugins"); + for (auto NameValue : Values) { - AString ValueName = a_SettingsIni.GetValueName(KeyNum, i); + AString ValueName = NameValue.first; if (ValueName.compare("Plugin") == 0) { - AString PluginFile = a_SettingsIni.GetValue(KeyNum, i); + AString PluginFile = NameValue.second; if (!PluginFile.empty()) { res.push_back(PluginFile); diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index d8c886b62..6bcef87bf 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -24,6 +24,7 @@ class cPlayer; class cPlugin; class cProjectileEntity; class cWorld; +class cSettingsRepositoryInterface; struct TakeDamageInfo; typedef SharedPtr<cPlugin> cPluginPtr; @@ -85,6 +86,8 @@ public: HOOK_DISCONNECT, HOOK_PLAYER_ANIMATION, HOOK_ENTITY_ADD_EFFECT, + HOOK_ENTITY_CHANGING_WORLD, + HOOK_ENTITY_CHANGED_WORLD, HOOK_EXECUTE_COMMAND, HOOK_EXPLODED, HOOK_EXPLODING, @@ -200,6 +203,8 @@ public: bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason); bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier); bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition); + bool CallHookEntityChangingWorld (cEntity & a_Entity, cWorld & a_World); + bool CallHookEntityChangedWorld (cEntity & a_Entity, cWorld & a_World); bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result); // If a_Player == nullptr, it is a console cmd bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); @@ -364,20 +369,20 @@ private: /** Reloads all plugins, defaulting to settings.ini for settings location */ void ReloadPluginsNow(void); - /** Reloads all plugins with a cIniFile object expected to be initialised to settings.ini */ - void ReloadPluginsNow(cIniFile & a_SettingsIni); + /** Reloads all plugins with a settings repo expected to be initialised to settings.ini */ + void ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings); /** Unloads all plugins */ void UnloadPluginsNow(void); - /** Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini */ - void InsertDefaultPlugins(cIniFile & a_SettingsIni); + /** Handles writing default plugins if 'Plugins' key not found using a settings repo expected to be intialised to settings.ini */ + void InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings); /** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns crExecuted if the command is executed. */ CommandResult HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions); /** Returns the folders that are specified in the settings ini to load plugins from. */ - AStringVector GetFoldersToLoad(cIniFile & a_SettingsIni); + AStringVector GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings); } ; // tolua_export diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h index 740dbca51..30a09bc02 100644 --- a/src/BlockEntities/BlockEntityWithItems.h +++ b/src/BlockEntities/BlockEntityWithItems.h @@ -76,7 +76,7 @@ protected: cItemGrid m_Contents; // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) + virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) override { UNUSED(a_SlotNum); ASSERT(a_Grid == &m_Contents); diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h index d3f85e9d2..f350ef4c4 100644 --- a/src/BlockEntities/NoteEntity.h +++ b/src/BlockEntities/NoteEntity.h @@ -52,7 +52,7 @@ public: virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle &) override {} - virtual void SetRedstonePower(bool a_Value) + virtual void SetRedstonePower(bool a_Value) override { if (a_Value) { diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp index d13c8d657..a4e375cf0 100644 --- a/src/Blocks/BlockDoor.cpp +++ b/src/Blocks/BlockDoor.cpp @@ -50,10 +50,24 @@ void cBlockDoorHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterfac UNUSED(a_CursorY); UNUSED(a_CursorZ); - if (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) + switch (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ)) { - ChangeDoor(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); - a_Player->GetWorld()->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle()); + default: + { + ASSERT(!"Unhandled door block type"); + } + case E_BLOCK_ACACIA_DOOR: + case E_BLOCK_BIRCH_DOOR: + case E_BLOCK_DARK_OAK_DOOR: + case E_BLOCK_JUNGLE_DOOR: + case E_BLOCK_SPRUCE_DOOR: + case E_BLOCK_IRON_DOOR: + case E_BLOCK_WOODEN_DOOR: + { + ChangeDoor(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + a_Player->GetWorld()->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle()); + break; + } } } diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h index 8e44c94ac..4d4610fd8 100644 --- a/src/Blocks/BlockLeaves.h +++ b/src/Blocks/BlockLeaves.h @@ -40,29 +40,41 @@ public: { cFastRandom rand; - // Old leaves - 3 bits contain display; new leaves - 1st bit, shifted left two for saplings to understand - if (rand.NextInt(20) == 0) + // There is a chance to drop a sapling that varies depending on the type of leaf broken. + // TODO: Take into account fortune for sapling drops. + int chance; + if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE)) + { + // Jungle leaves have a 2.5% chance of dropping a sapling. + chance = rand.NextInt(40); + } + else + { + // Other leaves have a 5% chance of dropping a sapling. + chance = rand.NextInt(20); + } + if (chance == 0) { a_Pickups.push_back( cItem( E_BLOCK_SAPLING, 1, - (m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (2 << (a_BlockMeta & 0x01)) + (m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (4 + (a_BlockMeta & 0x01)) ) ); } - - // 1 % chance of dropping an apple, if the leaves' type is Apple Leaves + + // 0.5 % chance of dropping an apple, if the leaves' type is Apple Leaves if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE)) { - if (rand.NextInt(101) == 0) + if (rand.NextInt(200) == 0) { a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); } } } - - + + virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override { NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h index d63df94cf..3abc572f3 100644 --- a/src/Blocks/BlockTorch.h +++ b/src/Blocks/BlockTorch.h @@ -103,6 +103,11 @@ public: case E_BLOCK_STAINED_GLASS: case E_BLOCK_FENCE: case E_BLOCK_NETHER_BRICK_FENCE: + case E_BLOCK_SPRUCE_FENCE: + case E_BLOCK_BIRCH_FENCE: + case E_BLOCK_JUNGLE_FENCE: + case E_BLOCK_DARK_OAK_FENCE: + case E_BLOCK_ACACIA_FENCE: case E_BLOCK_COBBLESTONE_WALL: { // Torches can only be placed on top of these blocks diff --git a/src/Blocks/BlockVine.h b/src/Blocks/BlockVine.h index 00d7a69b8..3d06a8223 100644 --- a/src/Blocks/BlockVine.h +++ b/src/Blocks/BlockVine.h @@ -177,7 +177,7 @@ public: } - virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) + virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override { UNUSED(a_ChunkInterface); UNUSED(a_WorldInterface); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2e367bcf5..1b721100d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,11 +48,13 @@ SET (SRCS Logger.cpp Map.cpp MapManager.cpp + MemorySettingsRepository.cpp MobCensus.cpp MobFamilyCollecter.cpp MobProximityCounter.cpp MobSpawner.cpp MonsterConfig.cpp + OverridesSettingsRepository.cpp ProbabDistrib.cpp RankManager.cpp RCONServer.cpp @@ -116,11 +118,13 @@ SET (HDRS Map.h MapManager.h Matrix4.h + MemorySettingsRepository.h MobCensus.h MobFamilyCollecter.h MobProximityCounter.h MobSpawner.h MonsterConfig.h + OverridesSettingsRepository.h ProbabDistrib.h RankManager.h RCONServer.h @@ -128,6 +132,7 @@ SET (HDRS Scoreboard.h Server.h SetChunkData.h + SettingsRepositoryInterface.h Statistics.h StringCompression.h StringUtils.h @@ -142,6 +147,7 @@ SET (HDRS include_directories(".") include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite") include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/SQLiteCpp/include") +include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/TCLAP/include") configure_file("BuildInfo.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.h") @@ -326,4 +332,9 @@ endif () if (WIN32) target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib) endif() + +if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + add_flags_lnk(-L/usr/local/lib) +endif() + target_link_libraries(${EXECUTABLE} luaexpat jsoncpp mbedtls zlib sqlite lua SQLiteCpp event_core event_extra) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index c8ccc1cf5..765ccdee2 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -2329,6 +2329,15 @@ void cClientHandle::SendHealth(void) +void cClientHandle::SendHideTitle(void) +{ + m_Protocol->SendHideTitle(); +} + + + + + void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) { m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); @@ -2532,6 +2541,15 @@ void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effec +void cClientHandle::SendResetTitle() +{ + m_Protocol->SendResetTitle(); +} + + + + + void cClientHandle::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) { m_Protocol->SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks); @@ -2586,6 +2604,42 @@ void cClientHandle::SendDisplayObjective(const AString & a_Objective, cScoreboar +void cClientHandle::SendSetSubTitle(const cCompositeChat & a_SubTitle) +{ + m_Protocol->SendSetSubTitle(a_SubTitle); +} + + + + + +void cClientHandle::SendSetRawSubTitle(const AString & a_SubTitle) +{ + m_Protocol->SendSetRawSubTitle(a_SubTitle); +} + + + + + +void cClientHandle::SendSetTitle(const cCompositeChat & a_Title) +{ + m_Protocol->SendSetTitle(a_Title); +} + + + + + +void cClientHandle::SendSetRawTitle(const AString & a_Title) +{ + m_Protocol->SendSetRawTitle(a_Title); +} + + + + + void cClientHandle::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) { m_Protocol->SendSoundEffect(a_SoundName, a_X, a_Y, a_Z, a_Volume, a_Pitch); @@ -2676,6 +2730,15 @@ void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +void cClientHandle::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) +{ + m_Protocol->SendTitleTimes(a_FadeInTicks, a_DisplayTicks, a_FadeOutTicks); +} + + + + + void cClientHandle::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) { m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay, a_DoDaylightCycle); @@ -2894,13 +2957,20 @@ void cClientHandle::PacketError(UInt32 a_PacketType) void cClientHandle::SocketClosed(void) { // The socket has been closed for any reason - + if (!m_Username.empty()) // Ignore client pings { LOGD("Client %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str()); cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected"); } - + if (m_State < csDestroying) + { + cWorld * World = m_Player->GetWorld(); + if (World != nullptr) + { + World->RemovePlayer(m_Player, true); // Must be called before cPlayer::Destroy() as otherwise cChunk tries to delete the player, and then we do it again + } + } Destroy(); } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 61f29b57c..bcfa55825 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -172,6 +172,7 @@ public: // tolua_export void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion); void SendGameMode (eGameMode a_GameMode); void SendHealth (void); + void SendHideTitle (void); void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item); void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale); void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale); @@ -192,9 +193,14 @@ public: // tolua_export void SendPlayerSpawn (const cPlayer & a_Player); void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID); + void SendResetTitle (void); void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks = false); void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode); void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode); + void SendSetSubTitle (const cCompositeChat & a_SubTitle); + void SendSetRawSubTitle (const AString & a_SubTitle); + void SendSetTitle (const cCompositeChat & a_Title); + void SendSetRawTitle (const AString & a_Title); void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch); // tolua_export void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data); void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock); @@ -205,6 +211,7 @@ public: // tolua_export void SendTabCompletionResults (const AStringVector & a_Results); void SendTeleportEntity (const cEntity & a_Entity); void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); + void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks); void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle); // tolua_export void SendUnloadChunk (int a_ChunkX, int a_ChunkZ); void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity); diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 4a909a1fd..dca44488b 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1403,14 +1403,25 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) return false; } + // Ask the plugins if the entity is allowed to changing the world + if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World)) + { + // A Plugin doesn't allow the entity to changing the world + return false; + } + // Remove all links to the old world SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal GetWorld()->BroadcastDestroyEntity(*this); // Queue add to new world a_World->AddEntity(this); + cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD SetWorld(a_World); + // Entity changed the world, call the hook + cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld); + return true; } diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h index d1736e9b9..05eaf16e9 100644 --- a/src/Entities/Minecart.h +++ b/src/Entities/Minecart.h @@ -135,7 +135,7 @@ protected: virtual void Destroyed() override; // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) + virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) override { UNUSED(a_SlotNum); ASSERT(a_Grid == &m_Contents); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 607a663de..e3e3fac4f 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -94,7 +94,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : SetMaxHealth(MAX_HEALTH); m_Health = MAX_HEALTH; - + m_LastPlayerListTime = std::chrono::steady_clock::now(); m_PlayerName = a_PlayerName; @@ -106,7 +106,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : SetPosY(World->GetSpawnY()); SetPosZ(World->GetSpawnZ()); SetBedPos(Vector3i(static_cast<int>(World->GetSpawnX()), static_cast<int>(World->GetSpawnY()), static_cast<int>(World->GetSpawnZ()))); - + LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() ); @@ -128,7 +128,14 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : m_IsFlying = true; } } - + + if (m_GameMode == gmSpectator) // If player is reconnecting to the server in spectator mode + { + m_CanFly = true; + m_IsFlying = true; + m_bVisible = false; + } + cRoot::Get()->GetServer()->PlayerCreated(this); } @@ -145,17 +152,17 @@ cPlayer::~cPlayer(void) } LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), this, GetUniqueID()); - + // Notify the server that the player is being destroyed cRoot::Get()->GetServer()->PlayerDestroying(this); SaveToDisk(); m_ClientHandle = nullptr; - + delete m_InventoryWindow; m_InventoryWindow = nullptr; - + LOGD("Player %p deleted", this); } @@ -201,7 +208,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) m_ClientHandle = nullptr; return; } - + if (!m_ClientHandle->IsPlaying()) { // We're not yet in the game, ignore everything @@ -210,21 +217,21 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } m_Stats.AddValue(statMinutesPlayed, 1); - + if (!a_Chunk.IsValid()) { // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83) return; } - + super::Tick(a_Dt, a_Chunk); - + // Handle charging the bow: if (m_IsChargingBow) { m_BowCharge += 1; } - + // Handle updating experience if (m_bDirtyExperience) { @@ -236,7 +243,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Apply food exhaustion from movement: ApplyFoodExhaustionFromMovement(); - + if (cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this, m_LastPos, GetPosition())) { CanMove = false; @@ -257,10 +264,10 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { FinishEating(); } - + HandleFood(); } - + if (m_IsFishing) { HandleFloater(); @@ -460,7 +467,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) { return; } - + m_bTouchGround = a_bTouchGround; if (!m_bTouchGround) @@ -509,7 +516,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) { // cPlayer makes sure damage isn't applied in creative, no need to check here TakeDamage(dtFalling, nullptr, Damage, Damage, 0); - + // Fall particles GetWorld()->BroadcastSoundParticleEffect(2006, POSX_TOINT, static_cast<int>(GetPosY()) - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */); } @@ -541,7 +548,7 @@ void cPlayer::SetFoodLevel(int a_FoodLevel) m_FoodSaturationLevel = 5.0; return; } - + m_FoodLevel = FoodLevel; SendHealth(); } @@ -609,7 +616,7 @@ void cPlayer::StartEating(void) { // Set the timer: m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS; - + // Send the packets: m_World->BroadcastEntityAnimation(*this, 3); m_World->BroadcastEntityMetadata(*this); @@ -623,7 +630,7 @@ void cPlayer::FinishEating(void) { // Reset the timer: m_EatingFinishTick = -1; - + // Send the packets: m_ClientHandle->SendEntityStatus(*this, esPlayerEatingAccepted); m_World->BroadcastEntityMetadata(*this); @@ -757,7 +764,7 @@ void cPlayer::SetSprintingMaxSpeed(double a_Speed) void cPlayer::SetFlyingMaxSpeed(double a_Speed) { m_FlyingMaxSpeed = a_Speed; - + // Update the flying speed, always: m_ClientHandle->SendPlayerAbilities(); } @@ -769,7 +776,7 @@ void cPlayer::SetFlyingMaxSpeed(double a_Speed) void cPlayer::SetCrouch(bool a_IsCrouched) { // Set the crouch status, broadcast to all visible players - + if (a_IsCrouched == m_IsCrouched) { // No change @@ -790,7 +797,7 @@ void cPlayer::SetSprint(bool a_IsSprinting) // No change return; } - + m_IsSprinting = a_IsSprinting; m_ClientHandle->SendPlayerMaxSpeed(); } @@ -876,7 +883,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) } } } - + if (super::DoTakeDamage(a_TDI)) { // Any kind of damage adds food exhaustion @@ -1005,7 +1012,7 @@ void cPlayer::Respawn(void) m_Health = GetMaxHealth(); SetInvulnerableTicks(20); - + // Reset food level: m_FoodLevel = MAX_FOOD_LEVEL; m_FoodSaturationLevel = 5.0; @@ -1017,7 +1024,7 @@ void cPlayer::Respawn(void) // ToDo: send score to client? How? m_ClientHandle->SendRespawn(GetWorld()->GetDimension(), true); - + // Extinguish the fire: StopBurning(); @@ -1151,7 +1158,7 @@ void cPlayer::CloseWindow(bool a_CanRefuse) m_CurrentWindow = m_InventoryWindow; return; } - + if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse) { // Close accepted, go back to inventory window (the default): @@ -1189,21 +1196,17 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); return; } - + if (m_GameMode == a_GameMode) { // Gamemode already set return; } - + m_GameMode = a_GameMode; m_ClientHandle->SendGameMode(a_GameMode); - if (!(IsGameModeCreative() || IsGameModeSpectator())) - { - SetFlying(false); - SetCanFly(false); - } + SetCapabilities(); m_World->BroadcastPlayerListUpdateGameMode(*this); } @@ -1215,6 +1218,30 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) void cPlayer::LoginSetGameMode( eGameMode a_GameMode) { m_GameMode = a_GameMode; + + SetCapabilities(); +} + + + + + +void cPlayer::SetCapabilities() +{ + if (!IsGameModeCreative() || IsGameModeSpectator()) + { + SetFlying(false); + SetCanFly(false); + } + + if (IsGameModeSpectator()) + { + SetVisible(false); + } + else + { + SetVisible(true); + } } @@ -1306,12 +1333,12 @@ void cPlayer::SendRotation(double a_YawDegrees, double a_PitchDegrees) Vector3d cPlayer::GetThrowStartPos(void) const { Vector3d res = GetEyePosition(); - + // Adjust the position to be just outside the player's bounding box: res.x += 0.16 * cos(GetPitch()); res.y += -0.1; res.z += 0.16 * sin(GetPitch()); - + return res; } @@ -1323,9 +1350,9 @@ Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const { Vector3d res = GetLookVector(); res.Normalize(); - + // TODO: Add a slight random change (+-0.0075 in each direction) - + return res * a_SpeedCoeff; } @@ -1370,13 +1397,13 @@ void cPlayer::MoveTo( const Vector3d & a_NewPos) } return; } - + // TODO: should do some checks to see if player is not moving through terrain // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too Vector3d DeltaPos = a_NewPos - GetPosition(); UpdateMovementStats(DeltaPos); - + SetPosition( a_NewPos); SetStance(a_NewPos.y + 1.62); } @@ -1411,7 +1438,7 @@ bool cPlayer::HasPermission(const AString & a_Permission) // Empty permission request is always granted return true; } - + AStringVector Split = StringSplit(a_Permission, "."); // Iterate over all restrictions; if any matches, then return failure: @@ -1583,7 +1610,7 @@ void cPlayer::TossItems(const cItems & a_Items) { return; } - + m_Stats.AddValue(statItemsDropped, (StatValue)a_Items.Size()); double vX = 0, vY = 0, vZ = 0; @@ -1605,7 +1632,14 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) // Don't move to same world return false; } - + + // Ask the plugins if the player is allowed to changing the world + if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World)) + { + // A Plugin doesn't allow the player to changing the world + return false; + } + // Send the respawn packet: if (a_ShouldSendRespawn && (m_ClientHandle != nullptr)) { @@ -1621,6 +1655,7 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) // Queue adding player to the new world, including all the necessary adjustments to the object a_World->AddPlayer(this); + cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value // Update the view distance. @@ -1634,7 +1669,10 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) // Broadcast the player into the new world. a_World->BroadcastSpawnEntity(*this); - + + // Player changed the world, call the hook + cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld); + return true; } @@ -1651,7 +1689,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World) { return true; } - + // Load from the offline UUID file, if allowed: AString OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName()); const char * OfflineUsage = " (unused)"; @@ -1663,7 +1701,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World) return true; } } - + // Load from the old-style name-based file, if allowed: if (cRoot::Get()->GetServer()->ShouldLoadNamedPlayerData()) { @@ -1678,7 +1716,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World) return true; } } - + // 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 @@ -1755,7 +1793,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) { m_CanFly = true; } - + m_Inventory.LoadFromJson(root["inventory"]); cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents); @@ -1774,11 +1812,11 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) // We use the default world name (like bukkit) because stats are shared between dimensions / worlds. cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); StatSerializer.Load(); - + LOGD("Player %s was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"", GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), a_World->GetName().c_str() ); - + return true; } @@ -2134,7 +2172,7 @@ void cPlayer::ApplyFoodExhaustionFromMovement() { return; } - + // Calculate the distance travelled, update the last pos: Vector3d Movement(GetPosition() - m_LastPos); Movement.y = 0; // Only take XZ movement into account @@ -2337,7 +2375,7 @@ AString cPlayer::GetUUIDFileName(const AString & a_UUID) { AString UUID = cMojangAPI::MakeUUIDDashed(a_UUID); ASSERT(UUID.length() == 36); - + AString res("players/"); res.append(UUID, 0, 2); res.push_back('/'); @@ -2345,7 +2383,3 @@ AString cPlayer::GetUUIDFileName(const AString & a_UUID) res.append(".json"); return res; } - - - - diff --git a/src/Entities/Player.h b/src/Entities/Player.h index a0cd9b1d6..a84fdd0c7 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -26,42 +26,42 @@ class cPlayer : public cPawn { typedef cPawn super; - + public: static const int MAX_HEALTH; - + static const int MAX_FOOD_LEVEL; - + /** Number of ticks it takes to eat an item */ static const int EATING_TICKS; - + // tolua_end - + CLASS_PROTODEF(cPlayer) - + cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName); - + virtual ~cPlayer(); virtual void SpawnOn(cClientHandle & a_Client) override; - + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &) override { UNUSED(a_Dt); } /** Returns the curently equipped weapon; empty item if none */ virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } - + /** Returns the currently equipped helmet; empty item if none */ virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); } - + /** Returns the currently equipped chestplate; empty item if none */ virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); } /** Returns the currently equipped leggings; empty item if none */ virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); } - + /** Returns the currently equipped boots; empty item if none */ virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); } @@ -104,16 +104,16 @@ public: static int CalcLevelFromXp(int a_CurrentXp); // tolua_end - + /** Starts charging the equipped bow */ void StartChargingBow(void); - + /** Finishes charging the current bow. Returns the number of ticks for which the bow has been charged */ int FinishChargingBow(void); - + /** Cancels the current bow charging */ void CancelChargingBow(void); - + /** Returns true if the player is currently charging the bow */ bool IsChargingBow(void) const { return m_IsChargingBow; } @@ -128,7 +128,7 @@ public: /** Gets the contents of the player's associated enderchest */ cItemGrid & GetEnderChestContents(void) { return m_EnderChestContents; } - + inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export /** Returns whether the player is climbing (ladders, vines etc.) */ @@ -137,43 +137,49 @@ public: virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override; // tolua_begin - + /** Sends the "look" packet to the player, forcing them to set their rotation to the specified values. a_YawDegrees is clipped to range [-180, +180), a_PitchDegrees is clipped to range [-180, +180) but the client only uses [-90, +90] */ void SendRotation(double a_YawDegrees, double a_PitchDegrees); - + /** Returns the position where projectiles thrown by this player should start, player eye position + adjustment */ Vector3d GetThrowStartPos(void) const; - + /** Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff. */ Vector3d GetThrowSpeed(double a_SpeedCoeff) const; - + /** Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable */ eGameMode GetGameMode(void) const { return m_GameMode; } - + /** Returns the current effective gamemode (inherited gamemode is resolved before returning) */ eGameMode GetEffectiveGameMode(void) const { return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; } - + /** Sets the gamemode for the player. The gamemode may be gmNotSet, in that case the player inherits the world's gamemode. Updates the gamemode on the client (sends the packet) */ void SetGameMode(eGameMode a_GameMode); + // Sets the current gamemode, doesn't check validity, doesn't send update packets to client + void LoginSetGameMode(eGameMode a_GameMode); + + // Updates player's capabilities - flying, visibility, etc. from their gamemode. + void SetCapabilities(); + /** Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world */ bool IsGameModeCreative(void) const; - + /** Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world */ bool IsGameModeSurvival(void) const; - + /** Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world */ bool IsGameModeAdventure(void) const; - + /** Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current world */ bool IsGameModeSpectator(void) const; - + AString GetIP(void) const { return m_IP; } // tolua_export /** Returns the associated team, nullptr if none */ @@ -195,11 +201,8 @@ public: If the achievement has been already awarded to the player, this method will just increment the stat counter. Returns the _new_ stat value. (0 = Could not award achievement) */ unsigned int AwardAchievement(const eStatistic a_Ach); - + void SetIP(const AString & a_IP); - - // Sets the current gamemode, doesn't check validity, doesn't send update packets to client - void LoginSetGameMode(eGameMode a_GameMode); /** Forces the player to move in the given direction. @deprecated Use SetSpeed instead. */ @@ -210,15 +213,15 @@ public: cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export const cWindow * GetWindow(void) const { return m_CurrentWindow; } - + /** Opens the specified window; closes the current one first using CloseWindow() */ void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp - + // tolua_begin - + /** Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true */ void CloseWindow(bool a_CanRefuse = true); - + /** Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow */ void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true); @@ -243,7 +246,7 @@ public: const AString & GetName(void) const { return m_PlayerName; } void SetName(const AString & a_Name) { m_PlayerName = a_Name; } - + // tolua_end bool HasPermission(const AString & a_Permission); // tolua_export @@ -260,7 +263,7 @@ public: const AStringVector & GetRestrictions(void) const { return m_Restrictions; } // Exported in ManualBindings.cpp // tolua_begin - + /** Returns the full color code to use for this player, based on their rank. The returned value either is empty, or includes the cChatColor::Delimiter. */ AString GetColor(void) const; @@ -279,15 +282,15 @@ public: /** Heals the player by the specified amount of HPs (positive only); sends health update */ virtual void Heal(int a_Health) override; - + int GetFoodLevel (void) const { return m_FoodLevel; } double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; } int GetFoodTickTimer (void) const { return m_FoodTickTimer; } double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; } - + /** Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore */ bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); } - + void SetFoodLevel (int a_FoodLevel); void SetFoodSaturationLevel (double a_FoodSaturationLevel); void SetFoodTickTimer (int a_FoodTickTimer); @@ -298,10 +301,10 @@ public: /** Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values. */ void AddFoodExhaustion(double a_Exhaustion); - + /** Returns true if the player is currently in the process of eating the currently equipped item */ bool IsEating(void) const { return (m_EatingFinishTick >= 0); } - + /** Returns true if the player is currently flying. */ bool IsFlying(void) const { return m_IsFlying; } @@ -329,16 +332,16 @@ public: GetWorld()->BroadcastEntityAnimation(*this, 2); } } - + /** Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet */ void StartEating(void); - + /** Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets */ void FinishEating(void); - + /** Aborts the current eating operation */ void AbortEating(void); - + virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual void Killed(cEntity * a_Victim) override; @@ -356,69 +359,69 @@ public: bool SaveToDisk(void); typedef cWorld * cWorldPtr; - + /** Loads the player data from the disk file Sets a_World to the world where the player will spawn, based on the stored world name or the default world by calling LoadFromFile() Returns true on success, false on failure */ bool LoadFromDisk(cWorldPtr & a_World); - + /** Loads the player data from the specified file Sets a_World to the world where the player will spawn, based on the stored world name or the default world Returns true on success, false on failure */ bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World); - + const AString & GetLoadedWorldName() { return m_LoadedWorldName; } void UseEquippedItem(int a_Amount = 1); - + void SendHealth(void); void SendExperience(void); - + /** In UI windows, get the item that the player is dragging */ cItem & GetDraggingItem(void) {return m_DraggingItem; } - + // In UI windows, when inventory-painting: /** Clears the list of slots that are being inventory-painted. To be used by cWindow only */ void ClearInventoryPaintSlots(void); - + /** Adds a slot to the list for inventory painting. To be used by cWindow only */ void AddInventoryPaintSlot(int a_SlotNum); - + /** Returns the list of slots currently stored for inventory painting. To be used by cWindow only */ const cSlotNums & GetInventoryPaintSlots(void) const; - + // tolua_begin - + /** Returns the current relative maximum speed (takes current sprinting / flying state into account) */ double GetMaxSpeed(void) const; - + /** Gets the normal relative maximum speed */ double GetNormalMaxSpeed(void) const { return m_NormalMaxSpeed; } - + /** Gets the sprinting relative maximum speed */ double GetSprintingMaxSpeed(void) const { return m_SprintingMaxSpeed; } - + /** Gets the flying relative maximum speed */ double GetFlyingMaxSpeed(void) const { return m_FlyingMaxSpeed; } - + /** Sets the normal relative maximum speed. Sends the update to player, if needed. */ void SetNormalMaxSpeed(double a_Speed); - + /** Sets the sprinting relative maximum speed. Sends the update to player, if needed. */ void SetSprintingMaxSpeed(double a_Speed); - + /** Sets the flying relative maximum speed. Sends the update to player, if needed. */ void SetFlyingMaxSpeed(double a_Speed); - + /** Sets the crouch status, broadcasts to all visible players */ void SetCrouch(bool a_IsCrouched); - + /** Starts or stops sprinting, sends the max speed update to the client, if needed */ void SetSprint(bool a_IsSprinting); - + /** Flags the player as flying */ void SetFlying(bool a_IsFlying); @@ -442,17 +445,17 @@ public: /** Sets the player's bed (home) position */ void SetBedPos(const Vector3i & a_Pos) { m_LastBedPos = a_Pos; } - + // tolua_end /** Update movement-related statistics. */ void UpdateMovementStats(const Vector3d & a_DeltaPos); - + // 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; } @@ -483,16 +486,16 @@ public: bool PlaceBlocks(const sSetBlockVector & a_Blocks); // cEntity overrides: - virtual bool IsCrouched (void) const { return m_IsCrouched; } - virtual bool IsSprinting(void) const { return m_IsSprinting; } - virtual bool IsRclking (void) const { return IsEating() || IsChargingBow(); } + virtual bool IsCrouched (void) const override { return m_IsCrouched; } + virtual bool IsSprinting(void) const override { return m_IsSprinting; } + virtual bool IsRclking (void) const override { return IsEating() || IsChargingBow(); } - virtual void Detach(void); + virtual void Detach(void) override; /** Called by cClientHandle when the client is being destroyed. The player removes its m_ClientHandle ownership so that the ClientHandle gets deleted. */ void RemoveClientHandle(void); - + protected: typedef std::vector<std::vector<AString> > AStringVectorVector; @@ -535,16 +538,16 @@ protected: // Food-related variables: /** Represents the food bar, one point equals half a "drumstick" */ int m_FoodLevel; - + /** "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel */ double m_FoodSaturationLevel; - + /** Count-up to the healing or damaging action, based on m_FoodLevel */ int m_FoodTickTimer; - + /** A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little */ double m_FoodExhaustionLevel; - + float m_LastJumpHeight; float m_LastGroundHeight; bool m_bTouchGround; @@ -564,31 +567,31 @@ protected: eGameMode m_GameMode; AString m_IP; - + /** The item being dragged by the cursor while in a UI window */ cItem m_DraggingItem; std::chrono::steady_clock::time_point m_LastPlayerListTime; cClientHandlePtr m_ClientHandle; - + cSlotNums m_InventoryPaintSlots; - + /** Max speed, relative to the game default. 1 means regular speed, 2 means twice as fast, 0.5 means half-speed. Default value is 1. */ double m_NormalMaxSpeed; - + /** Max speed, relative to the game default max speed, when sprinting. 1 means regular speed, 2 means twice as fast, 0.5 means half-speed. Default value is 1.3. */ double m_SprintingMaxSpeed; - + /** Max speed, relative to the game default flying max speed, when flying. 1 means regular speed, 2 means twice as fast, 0.5 means half-speed. Default value is 1. */ double m_FlyingMaxSpeed; - + bool m_IsCrouched; bool m_IsSprinting; bool m_IsFlying; @@ -629,7 +632,7 @@ protected: Will not apply food penalties if found to be true; will set to false after processing */ 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; @@ -642,14 +645,14 @@ protected: void ResolvePermissions(void); void ResolveGroups(void); - virtual void Destroyed(void); + virtual void Destroyed(void) override; /** Filters out damage for creative mode / friendly fire */ virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; /** Stops players from burning in creative mode */ virtual void TickBurning(cChunk & a_Chunk) override; - + /** Called in each tick to handle food-related processing */ void HandleFood(void); @@ -666,7 +669,3 @@ protected: This can be used both for online and offline UUIDs. */ AString GetUUIDFileName(const AString & a_UUID); } ; // tolua_export - - - - diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index 00b34bddd..c988224e6 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -1259,7 +1259,7 @@ bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX } if ( (BlockUnderFeet != E_BLOCK_GRASS) && - ((AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig)) + ((AnimalToSpawn == mtWolf) || (AnimalToSpawn == mtRabbit) || (AnimalToSpawn == mtCow) || (AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig)) ) { return false; @@ -1274,6 +1274,7 @@ bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5); cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn); + NewMob->SetHealth(NewMob->GetMaxHealth()); NewMob->SetPosition(AnimalX, AnimalY, AnimalZ); a_ChunkDesc.GetEntities().push_back(NewMob); LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ); diff --git a/src/Generating/TwoHeights.cpp b/src/Generating/TwoHeights.cpp index e75c301de..06c474458 100644 --- a/src/Generating/TwoHeights.cpp +++ b/src/Generating/TwoHeights.cpp @@ -69,7 +69,7 @@ public: } - virtual void InitializeShapeGen(cIniFile & a_IniFile) + virtual void InitializeShapeGen(cIniFile & a_IniFile) override { m_FrequencyX = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyX", 40)); m_FrequencyY = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyY", 40)); diff --git a/src/Generating/VillageGen.cpp b/src/Generating/VillageGen.cpp index a3f8caa46..27146e757 100644 --- a/src/Generating/VillageGen.cpp +++ b/src/Generating/VillageGen.cpp @@ -259,13 +259,13 @@ protected: // cPiecePool overrides: - virtual cPieces GetPiecesWithConnector(int a_ConnectorType) + virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override { return m_Prefabs.GetPiecesWithConnector(a_ConnectorType); } - virtual cPieces GetStartingPieces(void) + virtual cPieces GetStartingPieces(void) override { return m_Prefabs.GetStartingPieces(); } diff --git a/src/Globals.h b/src/Globals.h index 1f354ae77..27d944fcc 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -433,10 +433,14 @@ typename std::enable_if<std::is_arithmetic<T>::value, C>::type CeilC(T a_Value) //temporary replacement for std::make_unique until we get c++14 -template <class T, class... Args> -std::unique_ptr<T> make_unique(Args&&... args) + +namespace cpp14 { - return std::unique_ptr<T>(new T(args...)); + template <class T, class... Args> + std::unique_ptr<T> make_unique(Args&&... args) + { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); + } } // a tick is 50 ms diff --git a/src/IniFile.cpp b/src/IniFile.cpp index 99c0a5f0f..cd98cce57 100644 --- a/src/IniFile.cpp +++ b/src/IniFile.cpp @@ -49,6 +49,9 @@ cIniFile::cIniFile(void) : bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect) { + + m_Filename = a_FileName; + // Normally you would use ifstream, but the SGI CC compiler has // a few bugs with ifstream. So ... fstream used. fstream f; @@ -56,7 +59,8 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect) AString keyname, valuename, value; AString::size_type pLeft, pRight; bool IsFromExampleRedirect = false; - + + f.open((FILE_IO_PREFIX + a_FileName).c_str(), ios::in); if (f.fail()) { @@ -650,7 +654,7 @@ void cIniFile::Clear(void) -bool cIniFile::HasValue(const AString & a_KeyName, const AString & a_ValueName) +bool cIniFile::HasValue(const AString & a_KeyName, const AString & a_ValueName) const { // Find the key: int keyID = FindKey(a_KeyName); @@ -889,8 +893,36 @@ void cIniFile::RemoveBom(AString & a_line) const +bool cIniFile::KeyExists(AString a_keyname) const +{ + return FindKey(a_keyname) != noID; +} + + + + + +std::vector<std::pair<AString, AString>> cIniFile::GetValues(AString a_keyName) +{ + std::vector<std::pair<AString, AString>> ret; + int keyID = FindKey(a_keyName); + if (keyID == noID) + { + return ret; + } + for (size_t valueID = 0; valueID < keys[keyID].names.size(); ++valueID) + { + ret.emplace_back(keys[keyID].names[valueID], keys[keyID].values[valueID]); + } + return ret; +} + + + + + AStringVector ReadUpgradeIniPorts( - cIniFile & a_IniFile, + cSettingsRepositoryInterface & a_Settings, const AString & a_KeyName, const AString & a_PortsValueName, const AString & a_OldIPv4ValueName, @@ -899,23 +931,34 @@ AStringVector ReadUpgradeIniPorts( ) { // Read the regular value, but don't use the default (in order to detect missing value for upgrade): - AStringVector Ports = StringSplitAndTrim(a_IniFile.GetValue(a_KeyName, a_PortsValueName), ";,"); + + AStringVector Ports; + + for (auto pair : a_Settings.GetValues(a_KeyName)) + { + if (pair.first != a_PortsValueName) + { + continue; + } + AStringVector temp = StringSplitAndTrim(pair.second, ";,"); + Ports.insert(Ports.end(), temp.begin(), temp.end()); + } if (Ports.empty()) { // Historically there were two separate entries for IPv4 and IPv6, merge them and migrate: - AString Ports4 = a_IniFile.GetValue(a_KeyName, a_OldIPv4ValueName, a_DefaultValue); - AString Ports6 = a_IniFile.GetValue(a_KeyName, a_OldIPv6ValueName); + AString Ports4 = a_Settings.GetValue(a_KeyName, a_OldIPv4ValueName, a_DefaultValue); + AString Ports6 = a_Settings.GetValue(a_KeyName, a_OldIPv6ValueName); Ports = MergeStringVectors(StringSplitAndTrim(Ports4, ";,"), StringSplitAndTrim(Ports6, ";,")); - a_IniFile.DeleteValue(a_KeyName, a_OldIPv4ValueName); - a_IniFile.DeleteValue(a_KeyName, a_OldIPv6ValueName); + a_Settings.DeleteValue(a_KeyName, a_OldIPv4ValueName); + a_Settings.DeleteValue(a_KeyName, a_OldIPv6ValueName); // If those weren't present or were empty, use the default:" if (Ports.empty()) { Ports = StringSplitAndTrim(a_DefaultValue, ";,"); } - a_IniFile.SetValue(a_KeyName, a_PortsValueName, StringsConcat(Ports, ',')); + a_Settings.SetValue(a_KeyName, a_PortsValueName, StringsConcat(Ports, ',')); } return Ports; @@ -923,4 +966,3 @@ AStringVector ReadUpgradeIniPorts( - diff --git a/src/IniFile.h b/src/IniFile.h index 71fea3a00..2a4113fb4 100644 --- a/src/IniFile.h +++ b/src/IniFile.h @@ -18,7 +18,7 @@ #pragma once - +#include "SettingsRepositoryInterface.h" #define MAX_KEYNAME 128 #define MAX_VALUENAME 128 @@ -30,10 +30,12 @@ // tolua_begin -class cIniFile +class cIniFile : public cSettingsRepositoryInterface { private: bool m_IsCaseInsensitive; + + AString m_Filename; struct key { @@ -53,15 +55,19 @@ private: void RemoveBom(AString & a_line) const; public: - - enum errors - { - noID = -1, - }; /// Creates a new instance with no data cIniFile(void); +// tolua_end + virtual ~cIniFile() = default; + + virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override; + + virtual bool KeyExists(const AString a_keyName) const override; + +// tolua_begin + // Sets whether or not keynames and valuenames should be case sensitive. // The default is case insensitive. void CaseSensitive (void) { m_IsCaseInsensitive = false; } @@ -77,11 +83,13 @@ public: /// Writes data stored in class to the specified ini file bool WriteFile(const AString & a_FileName) const; + virtual bool Flush() override { return WriteFile(m_Filename); } + /// Deletes all stored ini data (but doesn't touch the file) void Clear(void); /** Returns true iff the specified value exists. */ - bool HasValue(const AString & a_KeyName, const AString & a_ValueName); + bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override; /// Returns index of specified key, or noID if not found int FindKey(const AString & keyname) const; @@ -93,7 +101,7 @@ public: int GetNumKeys(void) const { return (int)keys.size(); } /// Add a key name - int AddKeyName(const AString & keyname); + int AddKeyName(const AString & keyname) override; // Returns key names by index. AString GetKeyName(const int keyID) const; @@ -109,7 +117,7 @@ public: // Gets value of [keyname] valuename =. // Overloaded to return string, int, and double. // Returns defValue if key / value not found. - AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const; + AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override; AString GetValue (const int keyID, const int valueID, const AString & defValue = "") const; double GetValueF(const AString & keyname, const AString & valuename, const double defValue = 0) const; int GetValueI(const AString & keyname, const AString & valuename, const int defValue = 0) const; @@ -119,18 +127,18 @@ public: } // Gets the value; if not found, write the default to the INI file - AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = ""); + AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override; double GetValueSetF(const AString & keyname, const AString & valuename, const double defValue = 0.0); - int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0); - Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0); - bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) + int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override; + Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override; + bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override { return (GetValueSetI(keyname, valuename, defValue ? 1 : 0) != 0); } // Adds a new value to the specified key. // If a value of the same name already exists, creates another one (non-standard INI file) - void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value); + void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override; void AddValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value); void AddValueB(const AString & a_KeyName, const AString & a_ValueName, const bool a_Value) { @@ -143,8 +151,8 @@ public: // Returns true if value set, false otherwise. // Overloaded to accept string, int, and double. bool SetValue (const int keyID, const int valueID, const AString & value); - bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true); - bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true); + bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override; + bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override; bool SetValueI(const AString & a_Keyname, const AString & a_ValueName, const Int64 a_Value, const bool a_CreateIfNotExists = true); bool SetValueB(const AString & a_KeyName, const AString & a_ValueName, const bool a_Value, const bool a_CreateIfNotExists = true) { @@ -155,7 +163,7 @@ public: // Deletes specified value. // Returns true if value existed and deleted, false otherwise. bool DeleteValueByID(const int keyID, const int valueID); - bool DeleteValue(const AString & keyname, const AString & valuename); + bool DeleteValue(const AString & keyname, const AString & valuename) override; // Deletes specified key and all values contained within. // Returns true if key existed and deleted, false otherwise. @@ -196,15 +204,15 @@ public: bool AddKeyComment(const int keyID, const AString & comment); /// Add a key comment - bool AddKeyComment(const AString & keyname, const AString & comment); + bool AddKeyComment(const AString & keyname, const AString & comment) override; /// Return a key comment AString GetKeyComment(const int keyID, const int commentID) const; - AString GetKeyComment(const AString & keyname, const int commentID) const; + AString GetKeyComment(const AString & keyname, const int commentID) const override; // Delete a key comment. bool DeleteKeyComment(const int keyID, const int commentID); - bool DeleteKeyComment(const AString & keyname, const int commentID); + bool DeleteKeyComment(const AString & keyname, const int commentID) override; // Delete all comments for a key. bool DeleteKeyComments(const int keyID); @@ -222,7 +230,7 @@ Reads the list of ports from a_PortsValueName. If that value doesn't exist or is in a_OldIPv4ValueName and a_OldIPv6ValueName; in this case the old values are removed from the INI file. If there is none of the three values or they are all empty, the default is used and stored in the Ports value. */ AStringVector ReadUpgradeIniPorts( - cIniFile & a_IniFile, + cSettingsRepositoryInterface & a_Settings, const AString & a_KeyName, const AString & a_PortsValueName, const AString & a_OldIPv4ValueName, diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h index 47429551a..0689670f9 100644 --- a/src/Items/ItemBucket.h +++ b/src/Items/ItemBucket.h @@ -80,6 +80,13 @@ public: return false; } + // Check to see if destination block is too far away + // Reach Distance Multiplayer = 5 Blocks + if ((BlockPos.x - a_Player->GetPosX() > 5) || (BlockPos.z - a_Player->GetPosZ() > 5)) + { + return false; + } + // Remove water / lava block (unless plugins disagree) if (!a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, E_BLOCK_AIR, 0)) { @@ -126,6 +133,13 @@ public: { return false; } + + // Check to see if destination block is too far away + // Reach Distance Multiplayer = 5 Blocks + if ((BlockPos.x - a_Player->GetPosX() > 5) || (BlockPos.z - a_Player->GetPosZ() > 5)) + { + return false; + } if (a_Player->GetGameMode() != gmCreative) { diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h index dabbdbba1..3da93e2f1 100644 --- a/src/Items/ItemSign.h +++ b/src/Items/ItemSign.h @@ -25,7 +25,7 @@ public: cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ - ) + ) override { // If the regular placement doesn't work, do no further processing: if (!super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) diff --git a/src/MemorySettingsRepository.cpp b/src/MemorySettingsRepository.cpp new file mode 100644 index 000000000..21b4c769c --- /dev/null +++ b/src/MemorySettingsRepository.cpp @@ -0,0 +1,312 @@ + +#include "Globals.h" + +#include "MemorySettingsRepository.h" + + + + +bool cMemorySettingsRepository::KeyExists(const AString keyname) const +{ + return m_Map.count(keyname) != 0; +} + + + + + +bool cMemorySettingsRepository::HasValue(const AString & a_KeyName, const AString & a_ValueName) const +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + return false; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + return false; + } + return true; +} + + + + +int cMemorySettingsRepository::AddKeyName(const AString & a_keyname) +{ + m_Map.emplace(a_keyname, std::unordered_multimap<AString, sValue>{}); + return 0; +} + + + + + +bool cMemorySettingsRepository::AddKeyComment(const AString & keyname, const AString & comment) +{ + return false; +} + + + + + +AString cMemorySettingsRepository::GetKeyComment(const AString & keyname, const int commentID) const +{ + return ""; +} + + + + + +bool cMemorySettingsRepository::DeleteKeyComment(const AString & keyname, const int commentID) +{ + return false; +} + + + + + + +void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) +{ + if (m_Writable) + { + m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value)); + } +} + + + + +void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, Int64 a_Value) +{ + if (m_Writable) + { + m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value)); + } +} + + + + + +void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, bool a_Value) +{ + if (m_Writable) + { + m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value)); + } +} + + + + + +std::vector<std::pair<AString, AString>> cMemorySettingsRepository::GetValues(AString a_keyName) +{ + std::vector<std::pair<AString, AString>> ret; + for (auto pair : m_Map[a_keyName]) + { + ret.emplace_back(pair.first, pair.second.getStringValue()); + } + return ret; +} + + + + + +AString cMemorySettingsRepository::GetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) const +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + return defValue; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + return defValue; + } + return iter->second.getStringValue(); +} + + + + + +AString cMemorySettingsRepository::GetValueSet (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + return iter->second.getStringValue(); +} + + + + + +int cMemorySettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const int defValue) +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + AddValue(a_KeyName, a_ValueName, static_cast<Int64>(defValue)); + return defValue; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + AddValue(a_KeyName, a_ValueName, static_cast<Int64>(defValue)); + return defValue; + } + return static_cast<int>(iter->second.getIntValue()); +} + + + + + +Int64 cMemorySettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const Int64 defValue) +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + return iter->second.getIntValue(); +} + + + + +bool cMemorySettingsRepository::GetValueSetB(const AString & a_KeyName, const AString & a_ValueName, const bool defValue) +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + return iter->second.getBoolValue(); +} + + + + + +bool cMemorySettingsRepository::SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists) +{ + if (!m_Writable) + { + return false; + } + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + if (a_CreateIfNotExists) + { + AddValue(a_KeyName, a_ValueName, a_Value); + } + return a_CreateIfNotExists; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + if (a_CreateIfNotExists) + { + AddValue(a_KeyName, a_ValueName, a_Value); + } + return a_CreateIfNotExists; + } + iter->second = sValue(a_Value); + return true; +} + + + + +bool cMemorySettingsRepository::SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists) +{ + if (!m_Writable) + { + return false; + } + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + if (a_CreateIfNotExists) + { + AddValue(a_KeyName, a_ValueName, static_cast<Int64>(a_Value)); + } + return a_CreateIfNotExists; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + if (a_CreateIfNotExists) + { + AddValue(a_KeyName, a_ValueName, static_cast<Int64>(a_Value)); + } + return a_CreateIfNotExists; + } + iter->second = sValue(static_cast<Int64>(a_Value)); + return true; +} + + + + + +bool cMemorySettingsRepository::DeleteValue(const AString & a_KeyName, const AString & a_ValueName) +{ + if (!m_Writable) + { + return false; + } + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + return false; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + return false; + } + outerIter->second.erase(iter); + return true; +} + +bool cMemorySettingsRepository::Flush() +{ + return true; +} + diff --git a/src/MemorySettingsRepository.h b/src/MemorySettingsRepository.h new file mode 100644 index 000000000..335bc4513 --- /dev/null +++ b/src/MemorySettingsRepository.h @@ -0,0 +1,80 @@ + +#pragma once + +#include "SettingsRepositoryInterface.h" + +#include <unordered_map> + +class cMemorySettingsRepository : public cSettingsRepositoryInterface +{ +public: + + virtual bool KeyExists(const AString keyname) const override; + + virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override; + + virtual int AddKeyName(const AString & keyname) override; + + virtual bool AddKeyComment(const AString & keyname, const AString & comment) override; + + virtual AString GetKeyComment(const AString & keyname, const int commentID) const override; + + virtual bool DeleteKeyComment(const AString & keyname, const int commentID) override; + + virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override; + void AddValue (const AString & a_KeyName, const AString & a_ValueName, const Int64 a_Value); + void AddValue (const AString & a_KeyName, const AString & a_ValueName, const bool a_Value); + + virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override; + + virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override; + + + virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override; + virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override; + virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override; + virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override; + + virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override; + virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override; + + virtual bool DeleteValue(const AString & keyname, const AString & valuename) override; + + virtual bool Flush() override; + + void SetReadOnly() + { + m_Writable = false; + } + +private: + + bool m_Writable = true; + + struct sValue + { + sValue(AString value) : m_Type(eType::String), m_stringValue (value) {} + sValue(Int64 value) : m_Type(eType::Int64), m_intValue(value) {} + sValue(bool value) : m_Type(eType::Bool), m_boolValue(value) {} + + AString getStringValue() const { ASSERT(m_Type == eType::String); return m_stringValue; } + Int64 getIntValue() const { ASSERT(m_Type == eType::Int64); return m_intValue; } + bool getBoolValue() const { ASSERT(m_Type == eType::Bool); return m_boolValue; } + private: + enum class eType + { + String, + Int64, + Bool + } m_Type; + AString m_stringValue; + union + { + Int64 m_intValue; + bool m_boolValue; + }; + }; + + std::unordered_map<AString, std::unordered_multimap<AString, sValue>> m_Map{}; +}; + diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 055ff47d2..648599999 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -76,9 +76,11 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } cTracer LineOfSight(GetWorld()); - Vector3d AttackDirection(m_Target->GetPosition() - GetPosition()); + Vector3d MyHeadPosition = GetPosition() + Vector3d(0, GetHeight(), 0); + Vector3d AttackDirection(m_Target->GetPosition() + Vector3d(0, m_Target->GetHeight(), 0) - MyHeadPosition); - if (ReachedFinalDestination() && !LineOfSight.Trace(GetPosition(), AttackDirection, (int)AttackDirection.Length())) + + if (ReachedFinalDestination() && !LineOfSight.Trace(MyHeadPosition, AttackDirection, static_cast<int>(AttackDirection.Length()))) { // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) Attack(a_Dt); diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h index be283705e..27168ebae 100644 --- a/src/Mobs/Horse.h +++ b/src/Mobs/Horse.h @@ -26,7 +26,7 @@ public: bool IsEating (void) const {return m_bIsEating; } bool IsRearing (void) const {return m_bIsRearing; } bool IsMthOpen (void) const {return m_bIsMouthOpen; } - bool IsTame (void) const {return m_bIsTame; } + bool IsTame (void) const override {return m_bIsTame; } int GetHorseType (void) const {return m_Type; } int GetHorseColor (void) const {return m_Color; } int GetHorseStyle (void) const {return m_Style; } diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index a29d67d15..1da4124ed 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -169,7 +169,7 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk) m_NoPathToTarget = false; m_NoMoreWayPoints = false; m_PathFinderDestination = m_FinalDestination; - m_Path = new cPath(a_Chunk, GetPosition().Floor(), m_PathFinderDestination.Floor(), 20); + m_Path = new cPath(a_Chunk, GetPosition(), m_PathFinderDestination, 20, GetWidth(), GetHeight()); } switch (m_Path->Step(a_Chunk)) @@ -177,13 +177,14 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk) case ePathFinderStatus::NEARBY_FOUND: { m_NoPathToTarget = true; - m_Path->AcceptNearbyPath(); + m_PathFinderDestination = m_Path->AcceptNearbyPath(); break; } case ePathFinderStatus::PATH_NOT_FOUND: { - StopMovingToPosition(); // Give up pathfinding to that destination. + StopMovingToPosition(); // Try to calculate a path again. + // Note that the next time may succeed, e.g. if a player breaks a barrier. break; } case ePathFinderStatus::CALCULATING: @@ -202,7 +203,7 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk) { if ((m_Path->IsFirstPoint() || ReachedNextWaypoint())) { - m_NextWayPointPosition = Vector3d(0.5, 0, 0.5) + m_Path->GetNextPoint(); + m_NextWayPointPosition = m_Path->GetNextPoint(); m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition. } } @@ -519,7 +520,7 @@ void cMonster::SetPitchAndYawFromDestination() double HeadRotation, HeadPitch; Distance.Normalize(); VectorToEuler(Distance.x, Distance.y, Distance.z, HeadRotation, HeadPitch); - if (abs(BodyRotation - HeadRotation) < 120) + if (std::abs(BodyRotation - HeadRotation) < 120) { SetHeadYaw(HeadRotation); SetPitch(-HeadPitch); diff --git a/src/Mobs/Path.cpp b/src/Mobs/Path.cpp index ba8046a2b..6f3d43305 100644 --- a/src/Mobs/Path.cpp +++ b/src/Mobs/Path.cpp @@ -6,20 +6,15 @@ #include "Path.h" #include "../Chunk.h" +#define JUMP_G_COST 20 + #define DISTANCE_MANHATTAN 0 // 1: More speed, a bit less accuracy 0: Max accuracy, less speed. #define HEURISTICS_ONLY 0 // 1: Much more speed, much less accurate. #define CALCULATIONS_PER_STEP 10 // Higher means more CPU load but faster path calculations. // The only version which guarantees the shortest path is 0, 0. -enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST}; -struct cPathCell -{ - Vector3i m_Location; // Location of the cell in the world. - int m_F, m_G, m_H; // F, G, H as defined in regular A*. - eCellStatus m_Status; // Which list is the cell in? Either non, open, or closed. - cPathCell * m_Parent; // Cell's parent, as defined in regular A*. - bool m_IsSolid; // Is the cell an air or a solid? Partial solids are currently considered solids. -}; + + @@ -37,12 +32,11 @@ bool compareHeuristics::operator()(cPathCell * & a_Cell1, cPathCell * & a_Cell2) /* cPath implementation */ cPath::cPath( cChunk & a_Chunk, - const Vector3i & a_StartingPoint, const Vector3i & a_EndingPoint, int a_MaxSteps, + const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps, double a_BoundingBoxWidth, double a_BoundingBoxHeight, int a_MaxUp, int a_MaxDown ) : - m_Destination(a_EndingPoint.Floor()), - m_Source(a_StartingPoint.Floor()), + m_CurrentPoint(0), // GetNextPoint increments this to 1, but that's fine, since the first cell is always a_StartingPoint m_Chunk(&a_Chunk), m_BadChunkFound(false) @@ -50,6 +44,21 @@ cPath::cPath( // TODO: if src not walkable OR dest not walkable, then abort. // Borrow a new "isWalkable" from ProcessIfWalkable, make ProcessIfWalkable also call isWalkable + a_BoundingBoxWidth = 1; // Until we improve physics, if ever. + + m_BoundingBoxWidth = ceil(a_BoundingBoxWidth); + m_BoundingBoxHeight = ceil(a_BoundingBoxHeight); + m_HalfWidth = a_BoundingBoxWidth / 2; + + int HalfWidthInt = a_BoundingBoxWidth / 2; + m_Source.x = floor(a_StartingPoint.x - HalfWidthInt); + m_Source.y = floor(a_StartingPoint.y); + m_Source.z = floor(a_StartingPoint.z - HalfWidthInt); + + m_Destination.x = floor(a_EndingPoint.x - HalfWidthInt); + m_Destination.y = floor(a_EndingPoint.y); + m_Destination.z = floor(a_EndingPoint.z - HalfWidthInt); + if (GetCell(m_Source)->m_IsSolid || GetCell(m_Destination)->m_IsSolid) { m_Status = ePathFinderStatus::PATH_NOT_FOUND; @@ -154,7 +163,7 @@ bool cPath::IsSolid(const Vector3i & a_Location) } if (BlockType == E_BLOCK_STATIONARY_WATER) { - GetCell(a_Location + Vector3i(0, -1, 0))->m_IsSolid = true; // Mobs will always think that the fence is 2 blocks high and therefore won't jump over. + GetCell(a_Location + Vector3i(0, -1, 0))->m_IsSolid = true; } return cBlockInfo::IsSolid(BlockType); @@ -185,28 +194,57 @@ bool cPath::Step_Internal() // Calculation not finished yet. // Check if we have a new NearestPoint. - if (CurrentCell->m_H < m_NearestPointToTarget->m_H) + // TODO I don't like this that much, there should be a smarter way. + if ((m_Destination - CurrentCell->m_Location).Length() < 5) + { + if (m_Rand.NextInt(4) == 0) + { + m_NearestPointToTarget = CurrentCell; + } + } + else if (CurrentCell->m_H < m_NearestPointToTarget->m_H) { m_NearestPointToTarget = CurrentCell; } - // process a currentCell by inspecting all neighbors. - // Check North, South, East, West on all 3 different heights. - int i; - for (i = -1; i <= 1; ++i) + + // Check North, South, East, West on our height. + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 0, 0), CurrentCell, 10); + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 0, 0), CurrentCell, 10); + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 0, 1), CurrentCell, 10); + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 0, -1), CurrentCell, 10); + + // Check diagonals on XY plane. + for (int x = -1; x <= 1; x += 2) { - ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, i, 0), CurrentCell, 10); - ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, i, 0), CurrentCell, 10); - ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, i, 1), CurrentCell, 10); - ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, i, -1), CurrentCell, 10); + if (GetCell(CurrentCell->m_Location + Vector3i(x, 0, 0))->m_IsSolid) // If there's a solid our east / west. + { + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, 1, 0), CurrentCell, JUMP_G_COST); // Check east / west-up. + } + else + { + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, -1, 0), CurrentCell, 14); // Else check east / west-down. + } + } + + // Check diagonals on the YZ plane. + for (int z = -1; z <= 1; z += 2) + { + if (GetCell(CurrentCell->m_Location + Vector3i(0, 0, z))->m_IsSolid) // If there's a solid our east / west. + { + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, z), CurrentCell, JUMP_G_COST); // Check east / west-up. + } + else + { + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, -1, z), CurrentCell, 14); // Else check east / west-down. + } } - // Check diagonals on mob's height only. - int x, z; - for (x = -1; x <= 1; x += 2) + // Check diagonals on the XZ plane. (Normal diagonals, this plane is special because of gravity, etc) + for (int x = -1; x <= 1; x += 2) { - for (z = -1; z <= 1; z += 2) + for (int z = -1; z <= 1; z += 2) { // This condition prevents diagonal corner cutting. if (!GetCell(CurrentCell->m_Location + Vector3i(x, 0, 0))->m_IsSolid && !GetCell(CurrentCell->m_Location + Vector3i(0, 0, z))->m_IsSolid) @@ -319,7 +357,53 @@ si::setBlock((Ret)->m_Location.x, (Ret)->m_Location.y, (Ret)->m_Location.z, debu void cPath::ProcessIfWalkable(const Vector3i & a_Location, cPathCell * a_Parent, int a_Cost) { cPathCell * cell = GetCell(a_Location); - if (!cell->m_IsSolid && GetCell(a_Location + Vector3i(0, -1, 0))->m_IsSolid && !GetCell(a_Location + Vector3i(0, 1, 0))->m_IsSolid) + int x, y, z; + + // Make sure we fit in the position. + for (y = 0; y < m_BoundingBoxHeight; ++y) + { + for (x = 0; x < m_BoundingBoxWidth; ++x) + { + for (z = 0; z < m_BoundingBoxWidth; ++z) + { + if (GetCell(a_Location + Vector3i(x, y, z))->m_IsSolid) + { + return; + } + } + } + } + + /*y =-1; + for (x = 0; x < m_BoundingBoxWidth; ++x) + { + for (z = 0; z < m_BoundingBoxWidth; ++z) + { + if (!GetCell(a_Location + Vector3i(x, y, z))->m_IsSolid) + { + return; + } + } + } + ProcessCell(cell, a_Parent, a_Cost);*/ + + // Make sure there's at least 1 piece of solid below us. + + bool GroundFlag = false; + y =-1; + for (x = 0; x < m_BoundingBoxWidth; ++x) + { + for (z = 0; z < m_BoundingBoxWidth; ++z) + { + if (GetCell(a_Location + Vector3i(x, y, z))->m_IsSolid) + { + GroundFlag = true; + break; + } + } + } + + if (GroundFlag) { ProcessCell(cell, a_Parent, a_Cost); } @@ -388,23 +472,20 @@ void cPath::ProcessCell(cPathCell * a_Cell, cPathCell * a_Caller, int a_GDelta) cPathCell * cPath::GetCell(const Vector3i & a_Location) { // Create the cell in the hash table if it's not already there. - cPathCell * Cell; if (m_Map.count(a_Location) == 0) // Case 1: Cell is not on any list. We've never checked this cell before. { - Cell = new cPathCell(); - Cell->m_Location = a_Location; - m_Map[a_Location] = UniquePtr<cPathCell>(Cell); - Cell->m_IsSolid = IsSolid(a_Location); - Cell->m_Status = eCellStatus::NOLIST; + m_Map[a_Location].m_Location = a_Location; + m_Map[a_Location].m_IsSolid = IsSolid(a_Location); + m_Map[a_Location].m_Status = eCellStatus::NOLIST; #ifdef COMPILING_PATHFIND_DEBUGGER #ifdef COMPILING_PATHFIND_DEBUGGER_MARK_UNCHECKED si::setBlock(a_Location.x, a_Location.y, a_Location.z, debug_unchecked, Cell->m_IsSolid ? NORMAL : MINI); #endif #endif - return Cell; + return &m_Map[a_Location]; } else { - return m_Map[a_Location].get(); + return &m_Map[a_Location]; } } diff --git a/src/Mobs/Path.h b/src/Mobs/Path.h index 7a4182f17..3b9c0400e 100644 --- a/src/Mobs/Path.h +++ b/src/Mobs/Path.h @@ -1,16 +1,13 @@ #pragma once -/* Wanna use the pathfinder? Put this in your header file: - -// Fwd: cPath +/* +// Needed Fwds: cPath enum class ePathFinderStatus; class cPath; - -Put this in your .cpp: -#include "...Path.h" */ +#include "../FastRandom.h" #ifdef COMPILING_PATHFIND_DEBUGGER /* Note: the COMPILING_PATHFIND_DEBUGGER flag is used by Native / WiseOldMan95 to debug this class outside of MCServer. This preprocessor flag is never set when compiling MCServer. */ @@ -24,13 +21,30 @@ class cChunk; /* Various little structs and classes */ enum class ePathFinderStatus {CALCULATING, PATH_FOUND, PATH_NOT_FOUND, NEARBY_FOUND}; -struct cPathCell; // Defined inside Path.cpp +enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST}; +struct cPathCell +{ + Vector3i m_Location; // Location of the cell in the world. + int m_F, m_G, m_H; // F, G, H as defined in regular A*. + eCellStatus m_Status; // Which list is the cell in? Either non, open, or closed. + cPathCell * m_Parent; // Cell's parent, as defined in regular A*. + bool m_IsSolid; // Is the cell an air or a solid? Partial solids are currently considered solids. +}; + + + + + class compareHeuristics { public: bool operator()(cPathCell * & a_V1, cPathCell * & a_V2); }; + + + + class cPath { public: @@ -54,8 +68,8 @@ public: @param a_MaxSteps The maximum steps before giving up. */ cPath( cChunk & a_Chunk, - const Vector3i & a_StartingPoint, const Vector3i & a_EndingPoint, int a_MaxSteps, - double a_BoundingBoxWidth = 1, double a_BoundingBoxHeight = 2, + const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps, + double a_BoundingBoxWidth, double a_BoundingBoxHeight, int a_MaxUp = 1, int a_MaxDown = 1 ); @@ -75,10 +89,11 @@ public: /* Point retrieval functions, inlined for performance. */ /** Returns the next point in the path. */ - inline Vector3i GetNextPoint() + inline Vector3d GetNextPoint() { ASSERT(m_Status == ePathFinderStatus::PATH_FOUND); - return m_PathPoints[m_PathPoints.size() - 1 - (++m_CurrentPoint)]; + Vector3i Point = m_PathPoints[m_PathPoints.size() - 1 - (++m_CurrentPoint)]; + return Vector3d(Point.x + m_HalfWidth, Point.y, Point.z + m_HalfWidth); } /** Checks whether this is the last point or not. Never call getnextPoint when this is true. */ inline bool IsLastPoint() @@ -92,11 +107,12 @@ public: return (m_CurrentPoint == 0); } /** Get the point at a_index. Remark: Internally, the indexes are reversed. */ - inline Vector3i GetPoint(size_t a_index) + inline Vector3d GetPoint(size_t a_index) { ASSERT(m_Status == ePathFinderStatus::PATH_FOUND); ASSERT(a_index < m_PathPoints.size()); - return m_PathPoints[m_PathPoints.size() - 1 - a_index]; + Vector3i Point = m_PathPoints[m_PathPoints.size() - 1 - a_index]; + return Vector3d(Point.x + m_HalfWidth, Point.y, Point.z + m_HalfWidth); } /** Returns the total number of points this path has. */ inline int GetPointCount() @@ -144,11 +160,15 @@ private: /* Pathfinding fields */ std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics> m_OpenList; - std::unordered_map<Vector3i, UniquePtr<cPathCell>, VectorHasher> m_Map; + std::unordered_map<Vector3i, cPathCell, VectorHasher> m_Map; Vector3i m_Destination; Vector3i m_Source; + int m_BoundingBoxWidth; + int m_BoundingBoxHeight; + double m_HalfWidth; int m_StepsLeft; cPathCell * m_NearestPointToTarget; + cFastRandom m_Rand; /* Control fields */ ePathFinderStatus m_Status; diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h index 73ffb55c2..5de83acd8 100644 --- a/src/Mobs/Wolf.h +++ b/src/Mobs/Wolf.h @@ -25,8 +25,8 @@ public: virtual void Attack(std::chrono::milliseconds a_Dt) override; // Get functions - bool IsSitting (void) const { return m_IsSitting; } - bool IsTame (void) const { return m_IsTame; } + 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; } diff --git a/src/OSSupport/Errors.cpp b/src/OSSupport/Errors.cpp index a5361e1a6..407799495 100644 --- a/src/OSSupport/Errors.cpp +++ b/src/OSSupport/Errors.cpp @@ -22,7 +22,7 @@ AString GetOSErrorString( int a_ErrNo) // According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r(): - #if !defined(__APPLE__) && ( _GNU_SOURCE) && !defined(ANDROID_NDK) // GNU version of strerror_r() + #if !defined(__APPLE__) && defined( _GNU_SOURCE) && !defined(ANDROID_NDK) // GNU version of strerror_r() char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer)); if (res != nullptr) diff --git a/src/OverridesSettingsRepository.cpp b/src/OverridesSettingsRepository.cpp new file mode 100644 index 000000000..e63f2c44c --- /dev/null +++ b/src/OverridesSettingsRepository.cpp @@ -0,0 +1,273 @@ + +#include "Globals.h" +#include "OverridesSettingsRepository.h" + +cOverridesSettingsRepository::cOverridesSettingsRepository(std::unique_ptr<cSettingsRepositoryInterface> a_Main, std::unique_ptr<cSettingsRepositoryInterface> a_Overrides) : + m_Main(std::move(a_Main)), + m_Overrides(std::move(a_Overrides)) +{ +} + + + + + +bool cOverridesSettingsRepository::KeyExists(const AString a_keyName) const +{ + return m_Overrides->KeyExists(a_keyName) || m_Main->KeyExists(a_keyName); +} + + + + +bool cOverridesSettingsRepository::HasValue(const AString & a_KeyName, const AString & a_ValueName) const +{ + return m_Overrides->HasValue(a_KeyName, a_ValueName) || m_Main->HasValue(a_KeyName, a_ValueName); +} + + + + + +int cOverridesSettingsRepository::AddKeyName(const AString & a_keyname) +{ + + if (m_Overrides->KeyExists(a_keyname)) + { + m_Overrides->AddKeyName(a_keyname); + return 0; + } + + return m_Main->AddKeyName(a_keyname); +} + + + + + +bool cOverridesSettingsRepository::AddKeyComment(const AString & a_keyname, const AString & a_comment) +{ + if (m_Overrides->KeyExists(a_keyname)) + { + return m_Overrides->AddKeyComment(a_keyname, a_comment); + } + + return m_Main->AddKeyComment(a_keyname, a_comment); +} + + + + + +AString cOverridesSettingsRepository::GetKeyComment(const AString & a_keyname, const int a_commentID) const +{ + + if (m_Overrides->KeyExists(a_keyname)) + { + return m_Overrides->GetKeyComment(a_keyname, a_commentID); + } + + return m_Main->GetKeyComment(a_keyname, a_commentID); +} + + + + + +bool cOverridesSettingsRepository::DeleteKeyComment(const AString & a_keyname, const int a_commentID) +{ + if (m_Overrides->KeyExists(a_keyname)) + { + return m_Overrides->DeleteKeyComment(a_keyname, a_commentID); + } + + return m_Main->DeleteKeyComment(a_keyname, a_commentID); +} + + + + + +void cOverridesSettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + m_Overrides->AddValue(a_KeyName, a_ValueName, a_Value); + } + else + { + m_Main->AddValue(a_KeyName, a_ValueName, a_Value); + } +} + + + + + +std::vector<std::pair<AString, AString>> cOverridesSettingsRepository::GetValues(AString a_keyName) +{ + auto overrides = m_Overrides->GetValues(a_keyName); + auto main = m_Main->GetValues(a_keyName); + std::sort(overrides.begin(), overrides.end(), [](std::pair<AString, AString> a, std::pair<AString, AString> b) -> bool { return a < b ;}); + std::sort(main.begin(), main.end(), [](std::pair<AString, AString> a, std::pair<AString, AString> b) -> bool { return a < b ;}); + + std::vector<std::pair<AString, AString>> ret; + + + size_t overridesIndex = 0; + for (auto pair : main) + { + if (overridesIndex >= overrides.size()) + { + ret.push_back(pair); + continue; + } + if (pair.first == overrides[overridesIndex].first) + { + continue; + } + while (pair.first > overrides[overridesIndex].first) + { + ret.push_back(overrides[overridesIndex]); + overridesIndex++; + } + ret.push_back(pair); + } + return ret; +} + + + + + +AString cOverridesSettingsRepository::GetValue(const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) const +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->GetValue(a_KeyName, a_ValueName, defValue); + } + else + { + return m_Main->GetValue(a_KeyName, a_ValueName, defValue); + } +} + + + + + +AString cOverridesSettingsRepository::GetValueSet (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->GetValueSet(a_KeyName, a_ValueName, defValue); + } + else + { + return m_Main->GetValueSet(a_KeyName, a_ValueName, defValue); + } +} + + + + + +int cOverridesSettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const int defValue) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->GetValueSetI(a_KeyName, a_ValueName, defValue); + } + else + { + return m_Main->GetValueSetI(a_KeyName, a_ValueName, defValue); + } +} + + + + + +Int64 cOverridesSettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const Int64 defValue) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->GetValueSetI(a_KeyName, a_ValueName, defValue); + } + else + { + return m_Main->GetValueSetI(a_KeyName, a_ValueName, defValue); + } +} + + + + + +bool cOverridesSettingsRepository::GetValueSetB(const AString & a_KeyName, const AString & a_ValueName, const bool defValue) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->GetValueSetB(a_KeyName, a_ValueName, defValue); + } + else + { + return m_Main->GetValueSetB(a_KeyName, a_ValueName, defValue); + } +} + + + + + +bool cOverridesSettingsRepository::SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->SetValue(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists); + } + else + { + return m_Main->SetValue(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists); + } +} + + + + + +bool cOverridesSettingsRepository::SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->SetValueI(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists); + } + else + { + return m_Main->SetValueI(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists); + } +} + + + + + +bool cOverridesSettingsRepository::DeleteValue(const AString & a_KeyName, const AString & a_ValueName) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->DeleteValue(a_KeyName, a_ValueName); + } + else + { + return m_Main->DeleteValue(a_KeyName, a_ValueName); + } +} + + + +bool cOverridesSettingsRepository::Flush() +{ + return m_Overrides->Flush() && m_Main->Flush(); +} + diff --git a/src/OverridesSettingsRepository.h b/src/OverridesSettingsRepository.h new file mode 100644 index 000000000..04a53997f --- /dev/null +++ b/src/OverridesSettingsRepository.h @@ -0,0 +1,52 @@ + +#pragma once + +#include "SettingsRepositoryInterface.h" + +#include <unordered_map> + +class cOverridesSettingsRepository : public cSettingsRepositoryInterface +{ + +public: + cOverridesSettingsRepository(std::unique_ptr<cSettingsRepositoryInterface> a_Main, std::unique_ptr<cSettingsRepositoryInterface> a_Overrides); + + virtual ~cOverridesSettingsRepository() = default; + + virtual bool KeyExists(const AString keyname) const override; + + virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override; + + virtual int AddKeyName(const AString & keyname) override; + + virtual bool AddKeyComment(const AString & keyname, const AString & comment) override; + + virtual AString GetKeyComment(const AString & keyname, const int commentID) const override; + + virtual bool DeleteKeyComment(const AString & keyname, const int commentID) override; + + virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override; + + virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override; + + virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override; + + virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override; + virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override; + virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override; + virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override; + + virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override; + virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override; + + virtual bool DeleteValue(const AString & keyname, const AString & valuename) override; + + virtual bool Flush() override; + +private: + + std::unique_ptr<cSettingsRepositoryInterface> m_Main; + std::unique_ptr<cSettingsRepositoryInterface> m_Overrides; + +}; + diff --git a/src/PolarSSL++/BlockingSslClientSocket.cpp b/src/PolarSSL++/BlockingSslClientSocket.cpp index 821125b31..f5ad2f08c 100644 --- a/src/PolarSSL++/BlockingSslClientSocket.cpp +++ b/src/PolarSSL++/BlockingSslClientSocket.cpp @@ -54,19 +54,19 @@ class cBlockingSslClientSocketLinkCallbacks: } - virtual void OnReceivedData(const char * a_Data, size_t a_Length) + virtual void OnReceivedData(const char * a_Data, size_t a_Length) override { m_Socket.OnReceivedData(a_Data, a_Length); } - virtual void OnRemoteClosed(void) + virtual void OnRemoteClosed(void) override { m_Socket.OnDisconnected(); } - virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override { m_Socket.OnDisconnected(); } diff --git a/src/PolarSSL++/BlockingSslClientSocket.h b/src/PolarSSL++/BlockingSslClientSocket.h index 319e82bf2..462ee95a7 100644 --- a/src/PolarSSL++/BlockingSslClientSocket.h +++ b/src/PolarSSL++/BlockingSslClientSocket.h @@ -21,6 +21,11 @@ class cBlockingSslClientSocket : { public: cBlockingSslClientSocket(void); + + ~cBlockingSslClientSocket(void) + { + Disconnect(); + } /** Connects to the specified server and performs SSL handshake. Returns true if successful, false on failure. Sets internal error text on failure. */ diff --git a/src/Protocol/Authenticator.cpp b/src/Protocol/Authenticator.cpp index c9e4296a2..bfbe5028d 100644 --- a/src/Protocol/Authenticator.cpp +++ b/src/Protocol/Authenticator.cpp @@ -19,6 +19,10 @@ #define DEFAULT_AUTH_SERVER "sessionserver.mojang.com" #define DEFAULT_AUTH_ADDRESS "/session/minecraft/hasJoined?username=%USERNAME%&serverId=%SERVERID%" + + + + cAuthenticator::cAuthenticator(void) : super("cAuthenticator"), m_Server(DEFAULT_AUTH_SERVER), @@ -40,11 +44,11 @@ cAuthenticator::~cAuthenticator() -void cAuthenticator::ReadINI(cIniFile & IniFile) +void cAuthenticator::ReadSettings(cSettingsRepositoryInterface & a_Settings) { - m_Server = IniFile.GetValueSet ("Authentication", "Server", DEFAULT_AUTH_SERVER); - m_Address = IniFile.GetValueSet ("Authentication", "Address", DEFAULT_AUTH_ADDRESS); - m_ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true); + m_Server = a_Settings.GetValueSet ("Authentication", "Server", DEFAULT_AUTH_SERVER); + m_Address = a_Settings.GetValueSet ("Authentication", "Address", DEFAULT_AUTH_ADDRESS); + m_ShouldAuthenticate = a_Settings.GetValueSetB("Authentication", "Authenticate", true); } @@ -69,9 +73,9 @@ void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, co -void cAuthenticator::Start(cIniFile & IniFile) +void cAuthenticator::Start(cSettingsRepositoryInterface & a_Settings) { - ReadINI(IniFile); + ReadSettings(a_Settings); m_ShouldTerminate = false; super::Start(); } @@ -267,3 +271,7 @@ bool cAuthenticator::GetPlayerProperties(const AString & a_UUID, Json::Value & a return true; } */ + + + + diff --git a/src/Protocol/Authenticator.h b/src/Protocol/Authenticator.h index 853eff535..02b349256 100644 --- a/src/Protocol/Authenticator.h +++ b/src/Protocol/Authenticator.h @@ -14,7 +14,7 @@ #include "../OSSupport/IsThread.h" - +class cSettingsRepositoryInterface; @@ -40,13 +40,13 @@ public: ~cAuthenticator(); /** (Re-)read server and address from INI: */ - void ReadINI(cIniFile & IniFile); + void ReadSettings(cSettingsRepositoryInterface & a_Settings); /** Queues a request for authenticating a user. If the auth fails, the user will be kicked */ void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash); /** Starts the authenticator thread. The thread may be started and stopped repeatedly */ - void Start(cIniFile & IniFile); + void Start(cSettingsRepositoryInterface & a_Settings); /** Stops the authenticator thread. The thread may be started and stopped repeatedly */ void Stop(void); diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 0d1441500..110590359 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -38,12 +38,36 @@ const int MAX_PER_QUERY = 100; -/** This is the data of the root certs for Starfield Technologies, the CA that signed sessionserver.mojang.com's cert: -Downloaded from http://certs.starfieldtech.com/repository/ */ -static const AString & StarfieldCACert(void) +/** Returns the CA certificates that should be trusted for Mojang-related connections. */ +static const AString & GetCACerts(void) { static const AString Cert( - // G2 cert + // Equifax root CA cert + // Currently used for signing *.mojang.com's cert + // Exported from Mozilla Firefox's built-in CA repository + "-----BEGIN CERTIFICATE-----\n" + "MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV\n" + "UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy\n" + "dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1\n" + "MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx\n" + "dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B\n" + "AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f\n" + "BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A\n" + "cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC\n" + "AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ\n" + "MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm\n" + "aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw\n" + "ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj\n" + "IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF\n" + "MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA\n" + "A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y\n" + "7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh\n" + "1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4\n" + "-----END CERTIFICATE-----\n\n" + + // Starfield G2 cert + // This is the data of the root certs for Starfield Technologies, the CA that used to sign sessionserver.mojang.com's cert + // Downloaded from http://certs.starfieldtech.com/repository/ "-----BEGIN CERTIFICATE-----\n" "MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx\n" "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n" @@ -67,7 +91,8 @@ static const AString & StarfieldCACert(void) "pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1\n" "mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n" "-----END CERTIFICATE-----\n\n" - // Original (G1) cert: + + // Starfield original (G1) cert: "-----BEGIN CERTIFICATE-----\n" "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n" "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n" @@ -226,12 +251,12 @@ cMojangAPI::~cMojangAPI() -void cMojangAPI::Start(cIniFile & a_SettingsIni, bool a_ShouldAuth) +void cMojangAPI::Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth) { - m_NameToUUIDServer = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); - m_NameToUUIDAddress = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); - m_UUIDToProfileServer = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER); - m_UUIDToProfileAddress = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS); + m_NameToUUIDServer = a_Settings.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); + m_NameToUUIDAddress = a_Settings.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); + m_UUIDToProfileServer = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER); + m_UUIDToProfileAddress = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS); LoadCachesFromDisk(); if (a_ShouldAuth) { @@ -390,7 +415,7 @@ bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_R { // Connect the socket: cBlockingSslClientSocket Socket; - Socket.SetTrustedRootCertsFromString(StarfieldCACert(), a_ServerName); + Socket.SetTrustedRootCertsFromString(GetCACerts(), a_ServerName); if (!Socket.Connect(a_ServerName, 443)) { LOGWARNING("%s: Can't connect to %s: %s", __FUNCTION__, a_ServerName.c_str(), Socket.GetLastErrorText().c_str()); @@ -434,7 +459,6 @@ bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_R a_Response.append((const char *)buf, (size_t)ret); } - Socket.Disconnect(); return true; } diff --git a/src/Protocol/MojangAPI.h b/src/Protocol/MojangAPI.h index 0dc2617b6..bea950740 100644 --- a/src/Protocol/MojangAPI.h +++ b/src/Protocol/MojangAPI.h @@ -25,7 +25,7 @@ namespace Json - +class cSettingsRepositoryInterface; // tolua_begin class cMojangAPI @@ -38,7 +38,7 @@ public: /** Initializes the API; reads the settings from the specified ini file. Loads cached results from disk. */ - void Start(cIniFile & a_SettingsIni, bool a_ShouldAuth); + void Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth); /** Connects to the specified server using SSL, sends the given request and receives the response. Checks Mojang certificates using the hard-coded Starfield root CA certificate. diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 3bca7551b..7be72014a 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -88,6 +88,7 @@ public: virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0; virtual void SendGameMode (eGameMode a_GameMode) = 0; virtual void SendHealth (void) = 0; + virtual void SendHideTitle (void) = 0; virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0; virtual void SendKeepAlive (int a_PingID) = 0; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0; @@ -112,12 +113,17 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0; + virtual void SendResetTitle (void) = 0; virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) = 0; virtual void SendExperience (void) = 0; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0; virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0; virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) = 0; virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) = 0; + virtual void SendSetSubTitle (const cCompositeChat & a_SubTitle) = 0; + virtual void SendSetRawSubTitle (const AString & a_SubTitle) = 0; + virtual void SendSetTitle (const cCompositeChat & a_Title) = 0; + virtual void SendSetRawTitle (const AString & a_Title) = 0; virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) = 0; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0; @@ -128,6 +134,7 @@ public: virtual void SendTabCompletionResults (const AStringVector & a_Results) = 0; virtual void SendTeleportEntity (const cEntity & a_Entity) = 0; virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; + virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) = 0; virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) = 0; virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0; virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) = 0; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 799c021f3..0bd219fb1 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -555,6 +555,15 @@ void cProtocol172::SendHealth(void) +void cProtocol172::SendHideTitle(void) +{ + // Not implemented in this protocol version +} + + + + + void cProtocol172::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) { ASSERT(m_State == 3); // In game mode? @@ -995,6 +1004,15 @@ void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect +void cProtocol172::SendResetTitle(void) +{ + // Not implemented in this protocol version +} + + + + + void cProtocol172::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) { if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks) @@ -1094,6 +1112,42 @@ void cProtocol172::SendDisplayObjective(const AString & a_Objective, cScoreboard +void cProtocol172::SendSetSubTitle(const cCompositeChat & a_SubTitle) +{ + // Not implemented in this protocol version +} + + + + + +void cProtocol172::SendSetRawSubTitle(const AString & a_SubTitle) +{ + // Not implemented in this protocol version +} + + + + + +void cProtocol172::SendSetTitle(const cCompositeChat & a_Title) +{ + // Not implemented in this protocol version +} + + + + + +void cProtocol172::SendSetRawTitle(const AString & a_Title) +{ + // Not implemented in this protocol version +} + + + + + void cProtocol172::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) { ASSERT(m_State == 3); // In game mode? @@ -1296,6 +1350,15 @@ void cProtocol172::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +void cProtocol172::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) +{ + // Not implemented in this protocol version +} + + + + + void cProtocol172::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) { ASSERT(m_State == 3); // In game mode? diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 773c39f87..ead2935b0 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -90,6 +90,7 @@ public: virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; virtual void SendGameMode (eGameMode a_GameMode) override; virtual void SendHealth (void) override; + virtual void SendHideTitle (void) override; virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; virtual void SendKeepAlive (int a_PingID) override; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; @@ -113,9 +114,14 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; + virtual void SendResetTitle (void) override; virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override; virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; + virtual void SendSetSubTitle (const cCompositeChat & a_SubTitle) override; + virtual void SendSetRawSubTitle (const AString & a_SubTitle) override; + virtual void SendSetTitle (const cCompositeChat & a_Title) override; + virtual void SendSetRawTitle (const AString & a_Title) override; virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; @@ -126,6 +132,7 @@ public: virtual void SendTabCompletionResults (const AStringVector & a_Results) override; virtual void SendTeleportEntity (const cEntity & a_Entity) override; virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override; virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override; virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override; diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 4612af4a5..d06022ce0 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -542,6 +542,18 @@ void cProtocol180::SendHealth(void) +void cProtocol180::SendHideTitle(void) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x45); // Title packet + Pkt.WriteVarInt32(3); // Hide title +} + + + + + void cProtocol180::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) { ASSERT(m_State == 3); // In game mode? @@ -1065,6 +1077,18 @@ void cProtocol180::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect +void cProtocol180::SendResetTitle(void) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x45); // Title packet + Pkt.WriteVarInt32(4); // Reset title +} + + + + + void cProtocol180::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) { if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks) @@ -1167,6 +1191,52 @@ void cProtocol180::SendDisplayObjective(const AString & a_Objective, cScoreboard +void cProtocol180::SendSetSubTitle(const cCompositeChat & a_SubTitle) +{ + SendSetRawSubTitle(a_SubTitle.CreateJsonString(false)); +} + + + + + +void cProtocol180::SendSetRawSubTitle(const AString & a_SubTitle) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x45); // Title packet + Pkt.WriteVarInt32(1); // Set subtitle + + Pkt.WriteString(a_SubTitle); +} + + + + + +void cProtocol180::SendSetTitle(const cCompositeChat & a_Title) +{ + SendSetRawTitle(a_Title.CreateJsonString(false)); +} + + + + + +void cProtocol180::SendSetRawTitle(const AString & a_Title) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x45); // Title packet + Pkt.WriteVarInt32(0); // Set title + + Pkt.WriteString(a_Title); +} + + + + + void cProtocol180::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) { ASSERT(m_State == 3); // In game mode? @@ -1374,6 +1444,22 @@ void cProtocol180::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +void cProtocol180::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x45); // Title packet + Pkt.WriteVarInt32(2); // Set title display times + + Pkt.WriteBEInt32(a_FadeInTicks); + Pkt.WriteBEInt32(a_DisplayTicks); + Pkt.WriteBEInt32(a_FadeOutTicks); +} + + + + + void cProtocol180::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) { ASSERT(m_State == 3); // In game mode? diff --git a/src/Protocol/Protocol18x.h b/src/Protocol/Protocol18x.h index 21024d702..6143e8b4e 100644 --- a/src/Protocol/Protocol18x.h +++ b/src/Protocol/Protocol18x.h @@ -85,6 +85,7 @@ public: virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; virtual void SendGameMode (eGameMode a_GameMode) override; virtual void SendHealth (void) override; + virtual void SendHideTitle (void) override; virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; virtual void SendKeepAlive (int a_PingID) override; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; @@ -109,6 +110,7 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; + virtual void SendResetTitle (void) override; virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override; virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; virtual void SendExperience (void) override; @@ -116,6 +118,10 @@ public: virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override; + virtual void SendSetSubTitle (const cCompositeChat & a_SubTitle) override; + virtual void SendSetRawSubTitle (const AString & a_SubTitle) override; + virtual void SendSetTitle (const cCompositeChat & a_Title) override; + virtual void SendSetRawTitle (const AString & a_Title) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; virtual void SendSpawnMob (const cMonster & a_Mob) override; @@ -125,6 +131,7 @@ public: virtual void SendTabCompletionResults (const AStringVector & a_Results) override; virtual void SendTeleportEntity (const cEntity & a_Entity) override; virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override; virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override; virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index e7f7a4526..477f2d71e 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -350,6 +350,16 @@ void cProtocolRecognizer::SendHealth(void) +void cProtocolRecognizer::SendHideTitle(void) +{ + ASSERT(m_Protocol != nullptr); + m_Protocol->SendHideTitle(); +} + + + + + void cProtocolRecognizer::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) { ASSERT(m_Protocol != nullptr); @@ -599,6 +609,16 @@ void cProtocolRecognizer::SendRemoveEntityEffect(const cEntity & a_Entity, int a +void cProtocolRecognizer::SendResetTitle(void) +{ + ASSERT(m_Protocol != nullptr); + m_Protocol->SendResetTitle(); +} + + + + + void cProtocolRecognizer::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) { ASSERT(m_Protocol != nullptr); @@ -659,6 +679,46 @@ void cProtocolRecognizer::SendDisplayObjective(const AString & a_Objective, cSco +void cProtocolRecognizer::SendSetSubTitle(const cCompositeChat & a_SubTitle) +{ + ASSERT(m_Protocol != nullptr); + m_Protocol->SendSetSubTitle(a_SubTitle); +} + + + + + +void cProtocolRecognizer::SendSetRawSubTitle(const AString & a_SubTitle) +{ + ASSERT(m_Protocol != nullptr); + m_Protocol->SendSetRawSubTitle(a_SubTitle); +} + + + + + +void cProtocolRecognizer::SendSetTitle(const cCompositeChat & a_Title) +{ + ASSERT(m_Protocol != nullptr); + m_Protocol->SendSetTitle(a_Title); +} + + + + + +void cProtocolRecognizer::SendSetRawTitle(const AString & a_Title) +{ + ASSERT(m_Protocol != nullptr); + m_Protocol->SendSetRawTitle(a_Title); +} + + + + + void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) { ASSERT(m_Protocol != nullptr); @@ -759,6 +819,16 @@ void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_Bloc +void cProtocolRecognizer::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) +{ + ASSERT(m_Protocol != nullptr); + m_Protocol->SendTitleTimes(a_FadeInTicks, a_DisplayTicks, a_FadeOutTicks); +} + + + + + void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) { ASSERT(m_Protocol != nullptr); diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 6c2185d6d..956b5dcc0 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -73,6 +73,7 @@ public: virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; virtual void SendGameMode (eGameMode a_GameMode) override; virtual void SendHealth (void) override; + virtual void SendHideTitle (void) override; virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; virtual void SendKeepAlive (int a_PingID) override; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; @@ -97,12 +98,17 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; + virtual void SendResetTitle (void) override; virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override; virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override; + virtual void SendSetSubTitle (const cCompositeChat & a_SubTitle) override; + virtual void SendSetRawSubTitle (const AString & a_SubTitle) override; + virtual void SendSetTitle (const cCompositeChat & a_Title) override; + virtual void SendSetRawTitle (const AString & a_Title) override; virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; @@ -113,6 +119,7 @@ public: virtual void SendTabCompletionResults (const AStringVector & a_Results) override; virtual void SendTeleportEntity (const cEntity & a_Entity) override; virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override; virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override; virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override; diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index 685bd92f5..c5dc9b69b 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -134,15 +134,15 @@ cRCONServer::~cRCONServer() -void cRCONServer::Initialize(cIniFile & a_IniFile) +void cRCONServer::Initialize(cSettingsRepositoryInterface & a_Settings) { - if (!a_IniFile.GetValueSetB("RCON", "Enabled", false)) + if (!a_Settings.GetValueSetB("RCON", "Enabled", false)) { return; } // Read the password, don't allow an empty one: - m_Password = a_IniFile.GetValueSet("RCON", "Password", ""); + m_Password = a_Settings.GetValueSet("RCON", "Password", ""); if (m_Password.empty()) { LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled."); @@ -150,7 +150,7 @@ void cRCONServer::Initialize(cIniFile & a_IniFile) } // Read the listening ports for RCON from config: - AStringVector Ports = ReadUpgradeIniPorts(a_IniFile, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575"); + AStringVector Ports = ReadUpgradeIniPorts(a_Settings, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575"); // Start listening on each specified port: for (auto port: Ports) diff --git a/src/RCONServer.h b/src/RCONServer.h index 352fa7b50..ecd936eeb 100644 --- a/src/RCONServer.h +++ b/src/RCONServer.h @@ -17,7 +17,7 @@ // fwd: class cServer; -class cIniFile; +class cSettingsRepositoryInterface; @@ -29,7 +29,7 @@ public: cRCONServer(cServer & a_Server); virtual ~cRCONServer(); - void Initialize(cIniFile & a_IniFile); + void Initialize(cSettingsRepositoryInterface & a_Settings); protected: friend class cRCONCommandOutput; @@ -61,7 +61,7 @@ protected: // cTCPLink::cCallbacks overrides: - virtual void OnLinkCreated(cTCPLinkPtr a_Link); + virtual void OnLinkCreated(cTCPLinkPtr a_Link) override; virtual void OnReceivedData(const char * a_Data, size_t a_Length) override; virtual void OnRemoteClosed(void) override; virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; diff --git a/src/Root.cpp b/src/Root.cpp index 349608e8d..b28e7c894 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -19,6 +19,8 @@ #include "LoggerListeners.h" #include "BuildInfo.h" #include "IniFile.h" +#include "SettingsRepositoryInterface.h" +#include "OverridesSettingsRepository.h" #ifdef _WIN32 #include <conio.h> @@ -70,7 +72,7 @@ cRoot::~cRoot() void cRoot::InputThread(cRoot & a_Params) { cLogCommandOutputCallback Output; - + while (!cRoot::m_ShouldStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good()) { AString Command; @@ -96,19 +98,19 @@ void cRoot::InputThread(cRoot & a_Params) -void cRoot::Start(void) +void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo) { #ifdef _WIN32 HWND hwnd = GetConsoleWindow(); HMENU hmenu = GetSystemMenu(hwnd, FALSE); EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling #endif - + cLogger::cListener * consoleLogListener = MakeConsoleListener(); cLogger::cListener * fileLogListener = new cFileListener(); cLogger::GetInstance().AttachListener(consoleLogListener); cLogger::GetInstance().AttachListener(fileLogListener); - + LOG("--- Started Log ---\n"); #ifdef BUILD_ID @@ -130,22 +132,24 @@ void cRoot::Start(void) m_Server = new cServer(); LOG("Reading server config..."); - cIniFile IniFile; - if (!IniFile.ReadFile("settings.ini")) + + auto IniFile = cpp14::make_unique<cIniFile>(); + if (!IniFile->ReadFile("settings.ini")) { LOGWARN("Regenerating settings.ini, all settings will be reset"); - IniFile.AddHeaderComment(" This is the main server configuration"); - IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); - IniFile.AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help"); + IniFile->AddHeaderComment(" This is the main server configuration"); + IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); + IniFile->AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help"); } + auto settingsRepo = cpp14::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(overridesRepo)); LOG("Starting server..."); m_MojangAPI = new cMojangAPI; - bool ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true); - m_MojangAPI->Start(IniFile, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init - if (!m_Server->InitServer(IniFile, ShouldAuthenticate)) + bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true); + m_MojangAPI->Start(*settingsRepo, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init + if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate)) { - IniFile.WriteFile("settings.ini"); + settingsRepo->Flush(); LOGERROR("Failure starting server, aborting..."); return; } @@ -158,31 +162,31 @@ void cRoot::Start(void) m_RankManager->Initialize(*m_MojangAPI); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); - + LOGD("Loading worlds..."); - LoadWorlds(IniFile); + LoadWorlds(*settingsRepo); LOGD("Loading plugin manager..."); m_PluginManager = new cPluginManager(); - m_PluginManager->ReloadPluginsNow(IniFile); - + m_PluginManager->ReloadPluginsNow(*settingsRepo); + LOGD("Loading MonsterConfig..."); m_MonsterConfig = new cMonsterConfig; // This sets stuff in motion LOGD("Starting Authenticator..."); - m_Authenticator.Start(IniFile); - + m_Authenticator.Start(*settingsRepo); + LOGD("Starting worlds..."); StartWorlds(); - - if (IniFile.GetValueSetB("DeadlockDetect", "Enabled", true)) + + if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true)) { LOGD("Starting deadlock detector..."); - dd.Start(IniFile.GetValueSetI("DeadlockDetect", "IntervalSec", 20)); + dd.Start(settingsRepo->GetValueSetI("DeadlockDetect", "IntervalSec", 20)); } - - IniFile.WriteFile("settings.ini"); + + settingsRepo->Flush(); LOGD("Finalising startup..."); if (m_Server->Start()) @@ -203,6 +207,10 @@ void cRoot::Start(void) #endif LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count())); + + // Save the current time + m_StartTime = std::chrono::steady_clock::now(); + #ifdef _WIN32 EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button #endif @@ -249,7 +257,7 @@ void cRoot::Start(void) LOGD("Unloading worlds..."); UnloadWorlds(); - + LOGD("Stopping plugin manager..."); delete m_PluginManager; m_PluginManager = nullptr; @@ -260,9 +268,9 @@ void cRoot::Start(void) LOG("Shutdown successful!"); } - + LOG("--- Stopped Log ---"); - + cLogger::GetInstance().DetachListener(consoleLogListener); delete consoleLogListener; cLogger::GetInstance().DetachListener(fileLogListener); @@ -282,30 +290,29 @@ void cRoot::LoadGlobalSettings() -void cRoot::LoadWorlds(cIniFile & IniFile) +void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings) { // First get the default world - AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world"); + AString DefaultWorldName = a_Settings.GetValueSet("Worlds", "DefaultWorld", "world"); m_pDefaultWorld = new cWorld(DefaultWorldName.c_str()); m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld; // Then load the other worlds - int KeyNum = IniFile.FindKey("Worlds"); - int NumWorlds = IniFile.GetNumValues(KeyNum); - if (NumWorlds <= 0) + auto Worlds = a_Settings.GetValues("Worlds"); + if (Worlds.size() <= 0) { return; } - + bool FoundAdditionalWorlds = false; - for (int i = 0; i < NumWorlds; i++) + for (auto WorldNameValue : Worlds) { - AString ValueName = IniFile.GetValueName(KeyNum, i); + AString ValueName = WorldNameValue.first; if (ValueName.compare("World") != 0) { continue; } - AString WorldName = IniFile.GetValue(KeyNum, i); + AString WorldName = WorldNameValue.second; if (WorldName.empty()) { continue; @@ -317,10 +324,10 @@ void cRoot::LoadWorlds(cIniFile & IniFile) if (!FoundAdditionalWorlds) { - if (IniFile.GetKeyComment("Worlds", 0) != " World=secondworld") + if (a_Settings.GetKeyComment("Worlds", 0) != " World=secondworld") { - IniFile.DeleteKeyComment("Worlds", 0); - IniFile.AddKeyComment("Worlds", " World=secondworld"); + a_Settings.DeleteKeyComment("Worlds", 0); + a_Settings.AddKeyComment("Worlds", " World=secondworld"); } } } @@ -847,7 +854,3 @@ int cRoot::GetFurnaceFuelBurnTime(const cItem & a_Fuel) cFurnaceRecipe * FR = Get()->GetFurnaceRecipe(); return FR->GetBurnTime(a_Fuel); } - - - - diff --git a/src/Root.h b/src/Root.h index e0b6cf26c..ab820427f 100644 --- a/src/Root.h +++ b/src/Root.h @@ -24,6 +24,7 @@ class cWorld; class cPlayer; class cCommandOutputCallback; class cCompositeChat; +class cSettingsRepositoryInterface; typedef cItemCallback<cPlayer> cPlayerListCallback; typedef cItemCallback<cWorld> cWorldListCallback; @@ -53,7 +54,7 @@ public: cRoot(void); ~cRoot(); - void Start(void); + void Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo); // tolua_begin cServer * GetServer(void) { return m_Server; } @@ -69,22 +70,31 @@ public: a_OverworldName should be set for non-overworld dimensions if one wishes that world to link back to an overworld via portals */ cWorld * CreateAndInitializeWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = ""); + + /** Returns the up time of the server in seconds */ + int GetServerUpTime(void) + { + return static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - m_StartTime).count()); + } // tolua_end - + /// Calls the callback for each world; returns true if the callback didn't abort (return true) bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings << - + /// Writes chunkstats, for each world and totals, to the output callback void LogChunkStats(cCommandOutputCallback & a_Output); - + cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; } cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature - + /// Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel static int GetFurnaceFuelBurnTime(const cItem & a_Fuel); // tolua_export - + + /** The current time where the startup of the server has been completed */ + std::chrono::steady_clock::time_point m_StartTime; + cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } @@ -97,32 +107,32 @@ public: "stop" and "restart" commands have special handling. */ void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); - + /** Queues a console command for execution through the cServer class. The command will be executed in the tick thread The command's output will be sent to console "stop" and "restart" commands have special handling. */ void QueueExecuteConsoleCommand(const AString & a_Cmd); // tolua_export - + /// Executes a console command through the cServer class; does special handling for "stop" and "restart". void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); - + /// Kicks the user, no matter in what world they are. Used from cAuthenticator void KickUser(int a_ClientID, const AString & a_Reason); - + /// Called by cAuthenticator to auth the specified user void AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties); - + /// Executes commands queued in the command queue void TickCommands(void); /// Returns the number of chunks loaded int GetTotalChunkCount(void); // tolua_export - + /// Saves all chunks in all worlds void SaveAllChunks(void); // tolua_export - + /// Calls the callback for each player in all worlds bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << @@ -131,12 +141,12 @@ public: /** Finds the player over his uuid and calls the callback */ bool DoWithPlayerByUUID(const AString & 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); - + // tolua_begin - + /// Sends a chat message to all connected clients (in all worlds) void BroadcastChat (const AString & a_Message, eMessageType a_ChatPrefix = mtCustom); void BroadcastChat (const cCompositeChat & a_Message); @@ -148,18 +158,18 @@ public: void BroadcastChatLeave (const AString & a_Message) { BroadcastChat(a_Message, mtLeave); } void BroadcastChatSuccess(const AString & a_Message) { BroadcastChat(a_Message, mtSuccess); } void BroadcastChatWarning(const AString & a_Message) { BroadcastChat(a_Message, mtWarning); } - + /// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); - + /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error static int GetVirtualRAMUsage(void); - + /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error static int GetPhysicalRAMUsage(void); - + // tolua_end - + private: class cCommand { @@ -169,17 +179,17 @@ private: m_Output(a_Output) { } - + AString m_Command; cCommandOutputCallback * m_Output; } ; - + typedef std::map<AString, cWorld *> WorldMap; typedef std::vector<cCommand> cCommandQueue; cWorld * m_pDefaultWorld; WorldMap m_WorldsByName; - + cCriticalSection m_CSPendingCommands; cCommandQueue m_PendingCommands; @@ -204,25 +214,21 @@ private: void LoadGlobalSettings(); /// Loads the worlds from settings.ini, creates the worldmap - void LoadWorlds(cIniFile & IniFile); - + void LoadWorlds(cSettingsRepositoryInterface & a_Settings); + /// Starts each world's life void StartWorlds(void); - + /// Stops each world's threads, so that it's safe to unload them void StopWorlds(void); - + /// Unloads all worlds from memory void UnloadWorlds(void); - + /// Does the actual work of executing a command void DoExecuteConsoleCommand(const AString & a_Cmd); - + static cRoot * s_Root; static void InputThread(cRoot & a_Params); }; // tolua_export - - - - diff --git a/src/Server.cpp b/src/Server.cpp index 996de2695..fd3188b18 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -72,7 +72,7 @@ class cServerListenCallbacks: virtual void OnAccepted(cTCPLink & a_Link) override {} - virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override { LOGWARNING("Cannot listen on port %d: %d (%s).", m_Port, a_ErrorCode, a_ErrorMsg.c_str()); } @@ -185,12 +185,12 @@ void cServer::PlayerDestroying(const cPlayer * a_Player) -bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth) +bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth) { - m_Description = a_SettingsIni.GetValueSet("Server", "Description", "MCServer - in C++!"); - m_MaxPlayers = a_SettingsIni.GetValueSetI("Server", "MaxPlayers", 100); - m_bIsHardcore = a_SettingsIni.GetValueSetB("Server", "HardcoreEnabled", false); - m_bAllowMultiLogin = a_SettingsIni.GetValueSetB("Server", "AllowMultiLogin", false); + m_Description = a_Settings.GetValueSet("Server", "Description", "MCServer - in C++!"); + m_MaxPlayers = a_Settings.GetValueSetI("Server", "MaxPlayers", 100); + m_bIsHardcore = a_Settings.GetValueSetB("Server", "HardcoreEnabled", false); + m_bAllowMultiLogin = a_Settings.GetValueSetB("Server", "AllowMultiLogin", false); m_PlayerCount = 0; m_PlayerCountDiff = 0; @@ -205,9 +205,9 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth) LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS); LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS); - m_Ports = ReadUpgradeIniPorts(a_SettingsIni, "Server", "Ports", "Port", "PortsIPv6", "25565"); + m_Ports = ReadUpgradeIniPorts(a_Settings, "Server", "Ports", "Port", "PortsIPv6", "25565"); - m_RCONServer.Initialize(a_SettingsIni); + m_RCONServer.Initialize(a_Settings); m_bIsConnected = true; @@ -226,16 +226,16 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth) } // Check if both BungeeCord and online mode are on, if so, warn the admin: - m_ShouldAllowBungeeCord = a_SettingsIni.GetValueSetB("Authentication", "AllowBungeeCord", false); + m_ShouldAllowBungeeCord = a_Settings.GetValueSetB("Authentication", "AllowBungeeCord", false); if (m_ShouldAllowBungeeCord && m_ShouldAuthenticate) { LOGWARNING("WARNING: BungeeCord is allowed and server set to online mode. This is unsafe and will not work properly. Disable either authentication or BungeeCord in settings.ini."); } - m_ShouldLoadOfflinePlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false); - m_ShouldLoadNamedPlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadNamedPlayerData", true); + m_ShouldLoadOfflinePlayerData = a_Settings.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false); + m_ShouldLoadNamedPlayerData = a_Settings.GetValueSetB("PlayerData", "LoadNamedPlayerData", true); - m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE); + m_ClientViewDistance = a_Settings.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE); if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE) { m_ClientViewDistance = cClientHandle::MIN_VIEW_DISTANCE; diff --git a/src/Server.h b/src/Server.h index 1f30295b7..4d0bc1c18 100644 --- a/src/Server.h +++ b/src/Server.h @@ -38,8 +38,8 @@ class cClientHandle; typedef SharedPtr<cClientHandle> cClientHandlePtr; typedef std::list<cClientHandlePtr> cClientHandlePtrs; typedef std::list<cClientHandle *> cClientHandles; -class cIniFile; class cCommandOutputCallback; +class cSettingsRepositoryInterface; namespace Json @@ -58,7 +58,7 @@ public: // tolua_end virtual ~cServer() {} - bool InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth); + bool InitServer(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth); // tolua_begin diff --git a/src/SettingsRepositoryInterface.h b/src/SettingsRepositoryInterface.h new file mode 100644 index 000000000..443b90ff5 --- /dev/null +++ b/src/SettingsRepositoryInterface.h @@ -0,0 +1,61 @@ + +#pragma once + +class cSettingsRepositoryInterface +{ +public: + + enum errors + { + noID = -1, + }; + + virtual ~cSettingsRepositoryInterface() = default; + + /** Returns true iff the specified key exists */ + virtual bool KeyExists(const AString keyname) const = 0; + + /** Returns true iff the specified value exists. */ + virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const = 0; + + /** Add a key name. Return value is not required to mean anything **/ + virtual int AddKeyName(const AString & keyname) = 0; + + /** Add a key comment, will always fail if the repository does not support comments **/ + virtual bool AddKeyComment(const AString & keyname, const AString & comment) = 0; + + /** Return a key comment, returns "" for repositories that do not return comments **/ + virtual AString GetKeyComment(const AString & keyname, const int commentID) const = 0; + + /** Delete a key comment, will always fail if the repository does not support comments **/ + virtual bool DeleteKeyComment(const AString & keyname, const int commentID) = 0; + + /** Adds a new value to the specified key. + If a value of the same name already exists, creates another one **/ + virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) = 0; + + /** returns a vector containing a name, value pair for each value under the key **/ + virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) = 0; + + /** Get the value at the specified key and value, returns defValue on failure **/ + virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const = 0; + + /** Gets the value; if not found, write the default to the repository **/ + virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") = 0; + virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) = 0; + virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) = 0; + virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) = 0; + + /** Overwrites the value of the key, value pair + Specify the optional parameter as false if you do not want the value created if it doesn't exist. + Returns true if value set, false otherwise. **/ + virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) = 0; + virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) = 0; + + /** Deletes the specified key, value pair **/ + virtual bool DeleteValue(const AString & keyname, const AString & valuename) = 0; + + + /** Writes the changes to the backing store, if the repository has one **/ + virtual bool Flush() = 0; +}; diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index f6a9972d6..664c6502c 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -239,7 +239,7 @@ public: // cSlotAreaTemporary overrides: virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; - virtual void DblClicked (cPlayer & a_Player, int a_SlotNum); + virtual void DblClicked (cPlayer & a_Player, int a_SlotNum) override; virtual void OnPlayerRemoved(cPlayer & a_Player) override; virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; diff --git a/src/World.cpp b/src/World.cpp index ced459da8..3c39de317 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -150,7 +150,7 @@ protected: int m_LastReportChunkCount; // cChunkCoordCallback override: - virtual void Call(int a_ChunkX, int a_ChunkZ) + virtual void Call(int a_ChunkX, int a_ChunkZ) override { // Check if this was the last chunk: m_NumPrepared += 1; @@ -362,6 +362,7 @@ cWorld::~cWorld() void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) { BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ); + BroadcastSoundEffect("ambient.weather.thunder", a_BlockX, a_BlockY, a_BlockZ, 50, 1); } @@ -621,18 +622,18 @@ void cWorld::Start(void) InitialiseAndLoadMobSpawningValues(IniFile); SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay())); - m_ChunkMap = make_unique<cChunkMap>(this); + m_ChunkMap = cpp14::make_unique<cChunkMap>(this); // preallocate some memory for ticking blocks so we don't need to allocate that often m_BlockTickQueue.reserve(1000); m_BlockTickQueueCopy.reserve(1000); // Simulators: - m_SimulatorManager = make_unique<cSimulatorManager>(*this); + m_SimulatorManager = cpp14::make_unique<cSimulatorManager>(*this); m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER); m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); - m_SandSimulator = make_unique<cSandSimulator>(*this, IniFile); - m_FireSimulator = make_unique<cFireSimulator>(*this, IniFile); + m_SandSimulator = cpp14::make_unique<cSandSimulator>(*this, IniFile); + m_FireSimulator = cpp14::make_unique<cFireSimulator>(*this, IniFile); m_RedstoneSimulator = InitializeRedstoneSimulator(IniFile); // Water, Lava and Redstone simulators get registered in their initialize function. @@ -2680,7 +2681,7 @@ void cWorld::UnloadUnusedChunks(void) void cWorld::QueueUnloadUnusedChunks(void) { - QueueTask(make_unique<cWorld::cTaskUnloadUnusedChunks>()); + QueueTask(cpp14::make_unique<cWorld::cTaskUnloadUnusedChunks>()); } diff --git a/src/World.h b/src/World.h index 6c0548ae6..2ecdd519c 100644 --- a/src/World.h +++ b/src/World.h @@ -203,10 +203,10 @@ public: bool VillagersShouldHarvestCrops(void) const { return m_VillagersShouldHarvestCrops; } - virtual eDimension GetDimension(void) const { return m_Dimension; } + virtual eDimension GetDimension(void) const override { return m_Dimension; } /** Returns the world height at the specified coords; waits for the chunk to get loaded / generated */ - virtual int GetHeight(int a_BlockX, int a_BlockZ); + virtual int GetHeight(int a_BlockX, int a_BlockZ) override; // tolua_end @@ -253,7 +253,7 @@ public: void BroadcastScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode); void BroadcastScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode); void BroadcastDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display); - void BroadcastSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = nullptr); // tolua_export + void BroadcastSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = nullptr) override; // tolua_export void BroadcastSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = nullptr); // tolua_export void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr); void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr); @@ -788,7 +788,7 @@ public: bool IsWeatherWet(void) const { return !IsWeatherSunny(); } /** Returns true if it is raining, stormy or snowing at the specified location. This takes into account biomes. */ - virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) + virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) override { return (IsWeatherWet() && !IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ))); } diff --git a/src/main.cpp b/src/main.cpp index 1c34b8f61..5cd057278 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Root.h" +#include "tclap/CmdLine.h" #include <exception> #include <csignal> @@ -14,7 +15,7 @@ #include "OSSupport/NetworkSingleton.h" #include "BuildInfo.h" - +#include "MemorySettingsRepository.h" @@ -206,7 +207,7 @@ BOOL CtrlHandler(DWORD fdwCtrlType) //////////////////////////////////////////////////////////////////////////////// // universalMain - Main startup logic for both standard running and as a service -void universalMain() +void universalMain(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo) { #ifdef _WIN32 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE)) @@ -226,7 +227,7 @@ void universalMain() #endif { cRoot Root; - Root.Start(); + Root.Start(std::move(overridesRepo)); } #if !defined(ANDROID_NDK) catch (std::exception & e) @@ -257,7 +258,7 @@ DWORD WINAPI serviceWorkerThread(LPVOID lpParam) UNREFERENCED_PARAMETER(lpParam); // Do the normal startup - universalMain(); + universalMain(cpp14::make_unique<cMemorySettingsRepository>()); return ERROR_SUCCESS; } @@ -363,14 +364,79 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[]) +std::unique_ptr<cMemorySettingsRepository> parseArguments(int argc, char **argv) +{ + try + { + TCLAP::CmdLine cmd("MCServer"); + + TCLAP::ValueArg<int> slotsArg("s", "max-players", "Maximum number of slots for the server to use, overrides setting in setting.ini", false, -1, "number", cmd); + + TCLAP::MultiArg<int> portsArg("p", "port", "The port number the server should listen to", false, "port", cmd); + + TCLAP::SwitchArg commLogArg("", "log-comm", "Log server client communications to file", cmd); + + TCLAP::SwitchArg commLogInArg("", "log-comm-in", "Log inbound server client communications to file", cmd); + + TCLAP::SwitchArg commLogOutArg("", "log-comm-out", "Log outbound server client communications to file", cmd); + + TCLAP::SwitchArg noBufArg("", "no-output-buffering", "Disable output buffering", cmd); + + cmd.parse(argc, argv); + + auto repo = cpp14::make_unique<cMemorySettingsRepository>(); + + if (slotsArg.isSet()) + { + + int slots = slotsArg.getValue(); + + repo->AddValue("Server", "MaxPlayers", static_cast<Int64>(slots)); + + } + + if (portsArg.isSet()) + { + std::vector<int> ports = portsArg.getValue(); + for (auto port : ports) + { + repo->AddValue("Server", "Port", static_cast<Int64>(port)); + } + } + + if (commLogArg.getValue()) + { + g_ShouldLogCommIn = true; + g_ShouldLogCommOut = true; + } + else + { + g_ShouldLogCommIn = commLogInArg.getValue(); + g_ShouldLogCommOut = commLogOutArg.getValue(); + } + + if (noBufArg.getValue()) + { + setvbuf(stdout, nullptr, _IONBF, 0); + } + + repo->SetReadOnly(); + + return repo; + } + catch (TCLAP::ArgException &e) + { + printf("error reading command line %s for arg %s", e.error().c_str(), e.argId().c_str()); + return cpp14::make_unique<cMemorySettingsRepository>(); + } +} + //////////////////////////////////////////////////////////////////////////////// // main: -int main( int argc, char **argv) +int main(int argc, char **argv) { - UNUSED(argc); - UNUSED(argv); #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) InitLeakFinder(); @@ -425,39 +491,13 @@ int main( int argc, char **argv) // DEBUG: test the dumpfile creation: // *((int *)0) = 0; + auto argsRepo = parseArguments(argc, argv); + // Check if comm logging is to be enabled: for (int i = 0; i < argc; i++) { AString Arg(argv[i]); - if ( - (NoCaseCompare(Arg, "/commlog") == 0) || - (NoCaseCompare(Arg, "/logcomm") == 0) - ) - { - g_ShouldLogCommIn = true; - g_ShouldLogCommOut = true; - } - else if ( - (NoCaseCompare(Arg, "/commlogin") == 0) || - (NoCaseCompare(Arg, "/comminlog") == 0) || - (NoCaseCompare(Arg, "/logcommin") == 0) - ) - { - g_ShouldLogCommIn = true; - } - else if ( - (NoCaseCompare(Arg, "/commlogout") == 0) || - (NoCaseCompare(Arg, "/commoutlog") == 0) || - (NoCaseCompare(Arg, "/logcommout") == 0) - ) - { - g_ShouldLogCommOut = true; - } - else if (NoCaseCompare(Arg, "nooutbuf") == 0) - { - setvbuf(stdout, nullptr, _IONBF, 0); - } - else if (NoCaseCompare(Arg, "/service") == 0) + if (NoCaseCompare(Arg, "/service") == 0) { cRoot::m_RunAsService = true; } @@ -483,7 +523,7 @@ int main( int argc, char **argv) #endif { // Not running as a service, do normal startup - universalMain(); + universalMain(std::move(argsRepo)); } #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) |