From 9206d05ecde0f31a581cedf76f08197a8b850270 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sat, 21 Dec 2013 09:56:55 +0000 Subject: [PATCH 001/118] Delete clean.bat --- clean.bat | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 clean.bat diff --git a/clean.bat b/clean.bat deleted file mode 100644 index 08e4cc610..000000000 --- a/clean.bat +++ /dev/null @@ -1,24 +0,0 @@ -del *.ncb -del *.ilk -del *.lib -del *.exp -del *.map -del *.pdb -del log.txt -del *.bsc -del applog.txt -del *.suo /AH -del *.user -del debug\*.* /Q -del release\*.* /Q -rd release /Q -rd debug /Q - -cd MCServer -del MCServer.exe -del MCServer_debug.exe -cd .. - -cd VC2010 -call clean.bat -cd .. \ No newline at end of file From 7f5d5a861bb3ee487a0d75ad22ee1d440ab9aa69 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sat, 21 Dec 2013 09:58:04 +0000 Subject: [PATCH 002/118] Updated the stats.cmd file. --- stats.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stats.cmd b/stats.cmd index ad49b15aa..967298aab 100644 --- a/stats.cmd +++ b/stats.cmd @@ -1 +1 @@ -cloc --by-file-by-lang --exclude-dir=source/SQLite,source/LuaExpat,doxy,tolua..-1.0.93,jsoncpp.*,lua-5.1.4,squirrel_3.* --exclude-list-file=cloc-exclude.txt --xml --xsl=1 --report-file=cloc.xml --ignored=cloc-ignored.txt . \ No newline at end of file +cloc --by-file-by-lang --exclude-dir=lib --exclude-list-file=cloc-exclude.txt --xml --xsl=1 --report-file=cloc.xml --ignored=cloc-ignored.txt . From 547c269b75e9bc50cc835379b3f43b9c1604ab3b Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sat, 21 Dec 2013 10:01:05 +0000 Subject: [PATCH 003/118] Update cloc-exclude.txt --- cloc-exclude.txt | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/cloc-exclude.txt b/cloc-exclude.txt index 03c9531a8..c01f3e9c0 100644 --- a/cloc-exclude.txt +++ b/cloc-exclude.txt @@ -1,27 +1,13 @@ -CryptoPP -expat -jsoncpp-src-0.5.0 -lua-5.1.4 -MemDumpAnalysis/expat -source/LuaExpat -source/SQLite -squirrel_3_0_1_stable -VC2008/CryptoPP.vcproj -zlib-1.2.5 -zlib-1.2.7 -source/Bindings.cpp -source/Bindings.h -source/LeakFinder.cpp -source/LeakFinder.h -source/StackWalker.cpp -source/StackWalker.h -BiomeVisualiser/BiomeVisualiser.vcproj -MemDumpAnalysis/MemDumpAnalysis.vcproj -ProtoProxy/ProtoProxy.vcproj -Tests/NoiseTest/NoiseTest.vcproj -Tools/BlockZapper/BlockZapper.vcproj -Tools/ToLuaDoxy/ToLuaDoxy.vcproj +lib +Tools +Tests +src/Bindings.cpp +src/Bindings.h +src/LeakFinder.cpp +src/LeakFinder.h +src/StackWalker.cpp +src/StackWalker.h VC2008 +VC2013 Install -log.xml -build \ No newline at end of file +build From 809b4d7aad12c8fa2c5b117683e9288478bbe38d Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sat, 21 Dec 2013 11:04:21 +0000 Subject: [PATCH 004/118] Marked stats.cmd as executable so it can be run on linux --- stats.cmd | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 stats.cmd diff --git a/stats.cmd b/stats.cmd old mode 100644 new mode 100755 From d777c7efd9fa63f56291f1d77e446e17b33b0707 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sat, 21 Dec 2013 12:59:41 +0000 Subject: [PATCH 005/118] Moved some files. --- {Tests => tests}/ConsoleColors/ConsoleColors.cpp | 0 {Tests => tests}/NoiseTest/GNUmakefile | 0 {Tests => tests}/NoiseTest/NoiseTest.cpp | 0 {Tests => tests}/NoiseTest/NoiseTest.sln | 0 {Tests => tests}/NoiseTest/NoiseTest.vcproj | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {Tests => tests}/ConsoleColors/ConsoleColors.cpp (100%) rename {Tests => tests}/NoiseTest/GNUmakefile (100%) rename {Tests => tests}/NoiseTest/NoiseTest.cpp (100%) rename {Tests => tests}/NoiseTest/NoiseTest.sln (100%) rename {Tests => tests}/NoiseTest/NoiseTest.vcproj (100%) diff --git a/Tests/ConsoleColors/ConsoleColors.cpp b/tests/ConsoleColors/ConsoleColors.cpp similarity index 100% rename from Tests/ConsoleColors/ConsoleColors.cpp rename to tests/ConsoleColors/ConsoleColors.cpp diff --git a/Tests/NoiseTest/GNUmakefile b/tests/NoiseTest/GNUmakefile similarity index 100% rename from Tests/NoiseTest/GNUmakefile rename to tests/NoiseTest/GNUmakefile diff --git a/Tests/NoiseTest/NoiseTest.cpp b/tests/NoiseTest/NoiseTest.cpp similarity index 100% rename from Tests/NoiseTest/NoiseTest.cpp rename to tests/NoiseTest/NoiseTest.cpp diff --git a/Tests/NoiseTest/NoiseTest.sln b/tests/NoiseTest/NoiseTest.sln similarity index 100% rename from Tests/NoiseTest/NoiseTest.sln rename to tests/NoiseTest/NoiseTest.sln diff --git a/Tests/NoiseTest/NoiseTest.vcproj b/tests/NoiseTest/NoiseTest.vcproj similarity index 100% rename from Tests/NoiseTest/NoiseTest.vcproj rename to tests/NoiseTest/NoiseTest.vcproj From 294e629600f85af2d07c1815f24389468549c3e4 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sat, 21 Dec 2013 14:14:30 +0000 Subject: [PATCH 006/118] Update COMPILING --- COMPILING | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/COMPILING b/COMPILING index 708e4413e..5a9f9b6c2 100644 --- a/COMPILING +++ b/COMPILING @@ -1,8 +1,18 @@ COMPILING ========= -To compile MCServer on *nix, you need a GNUmake-compatible make that reads GNUmakefile. -Run "make" to build a debug version (slow, but gives more info on crash) -Run "make release=1" to build a release version (fast, less info on crash) -Add addm32=1 to compile in 32-bit mode on 64-bit systems. -Add `-j 4` to use 4 threads and speed up compilation on multi-core devices. +To compile MCServer on *nix, you need CMake and make, as well as a C compiler, C++ compiler and linker. + +Release Mode +------------ + +Release mode is preferred for almost all cases, it has much better speed and less console spam. However, if you are developing MCServer actively, debug mode might be better. + + cmake . && make + +Debug Mode +---------- + +Debug mode is useful if you want more debugging information about MCServer as it's running or if you want to use a debugger like GDB to debug issues and crashes. + + cmake . -DCMAKE_BUILD_TYPE=DEBUG && make From b89720f4e1b174e2b26c0345c38d5735f9ce355c Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sat, 21 Dec 2013 14:16:33 +0000 Subject: [PATCH 007/118] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 467036950..e67267870 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ After you've cloned the repository, you probably want to pull down the submodule Compilation instructions are available in the COMPILING file. -Linux builds can be downloaded from [the Berboe CI server](http://ci.berboe.co.uk) and windows from xoft's [nightly build service](http://mc-server.xoft.cz). +Linux builds can be downloaded from [Bearbin's CI Service](http://ci.bearbin.net) and Windows builds from xoft's [nightly build service](http://mc-server.xoft.cz). After you've extracted the files, simply run the MCServer executable. From 553633dc721e3e42336a32bcd0cd446756cec7f8 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sat, 21 Dec 2013 14:38:37 +0000 Subject: [PATCH 008/118] Added info about 32 bit mode. --- COMPILING | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/COMPILING b/COMPILING index 5a9f9b6c2..3ae79c830 100644 --- a/COMPILING +++ b/COMPILING @@ -16,3 +16,12 @@ Debug Mode Debug mode is useful if you want more debugging information about MCServer as it's running or if you want to use a debugger like GDB to debug issues and crashes. cmake . -DCMAKE_BUILD_TYPE=DEBUG && make + +32 Bit Mode +----------- + +This is useful if you want to compile MCServer to use on another 32-bit machine. It can be used with debug or release mode. To use 32 bit mode, simply add: + + -DFORCE-32=1 + +to your cmake command and 32 bit will be forced. From d8b4bf8a7027026ff4d347d94defa73aa0a9020f Mon Sep 17 00:00:00 2001 From: worktycho Date: Sat, 21 Dec 2013 16:00:47 +0000 Subject: [PATCH 009/118] Update COMPILING fixed acouple of issues --- COMPILING | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/COMPILING b/COMPILING index 3ae79c830..e10acee98 100644 --- a/COMPILING +++ b/COMPILING @@ -8,7 +8,7 @@ Release Mode Release mode is preferred for almost all cases, it has much better speed and less console spam. However, if you are developing MCServer actively, debug mode might be better. - cmake . && make + cmake . -DCMAKE_BUILD_TYPE=RELEASE && make Debug Mode ---------- @@ -22,6 +22,6 @@ Debug mode is useful if you want more debugging information about MCServer as it This is useful if you want to compile MCServer to use on another 32-bit machine. It can be used with debug or release mode. To use 32 bit mode, simply add: - -DFORCE-32=1 + -DFORCE_32=1 to your cmake command and 32 bit will be forced. From 0d14229ffda2cf8e41ec0afc28bc82d4830c8b44 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Sat, 21 Dec 2013 16:23:15 +0000 Subject: [PATCH 010/118] Fixed a file name. --- COMPILING => COMPILING.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename COMPILING => COMPILING.md (100%) diff --git a/COMPILING b/COMPILING.md similarity index 100% rename from COMPILING rename to COMPILING.md From bd6574230a0acbc538ce7328fc191130d3d8eac1 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 21 Dec 2013 17:31:05 +0100 Subject: [PATCH 011/118] Floater now dissapears when the player doesn't have an fishing rod equipped. --- src/Entities/Player.cpp | 29 +++++++++++++++++++++++++++++ src/Entities/Player.h | 3 +++ 2 files changed, 32 insertions(+) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 8f30cd4cc..948a259ff 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -240,6 +240,11 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) HandleFood(); } + if (m_IsFishing) + { + HandleFloater(); + } + // Send Player List (Once per m_LastPlayerListTime/1000 ms) cTimer t1; if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) @@ -1781,6 +1786,30 @@ void cPlayer::HandleFood(void) +void cPlayer::HandleFloater() +{ + if (GetEquippedItem().m_ItemType == E_ITEM_FISHING_ROD) + { + return; + } + class cFloaterCallback : + public cEntityCallback + { + public: + virtual bool Item(cEntity * a_Entity) override + { + a_Entity->Destroy(true); + return true; + } + } Callback; + m_World->DoWithEntityByID(m_FloaterID, Callback); + SetIsFishing(false); +} + + + + + void cPlayer::ApplyFoodExhaustionFromMovement() { if (IsGameModeCreative()) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index c0ad9eeac..66f1c07a7 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -466,6 +466,9 @@ protected: /// Called in each tick to handle food-related processing void HandleFood(void); + + /// Called in each tick if the player is fishing to make sure the floater dissapears when the player doesn't have a fishing rod as equipped item. + void HandleFloater(void); /// Called in each tick to handle air-related processing i.e. drowning void HandleAir(); From 7565026659c7e5c6f0083a721c011c91d7c78899 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sat, 21 Dec 2013 19:28:00 +0100 Subject: [PATCH 012/118] Further updates for the win nightbuild script. The FTP variables needed are checked at the beginning. The tag file is stored in a per-month subfolder. The bindings are correctly regenerated. --- Nightbuild2008.cmd | 50 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Nightbuild2008.cmd b/Nightbuild2008.cmd index cbd23692b..f3f1ca4cb 100644 --- a/Nightbuild2008.cmd +++ b/Nightbuild2008.cmd @@ -26,6 +26,23 @@ if %vc%a == a set vc="vcbuild.exe" +:: Check that the required environment vars are available: +if "a%ftppass%" == "a" ( + echo You need to set FTP password in the ftppass environment variable to upload the files + goto haderror +) +if "a%ftpuser%" == "a" ( + echo You need to set FTP username in the ftpuser environment variable to upload the files + goto haderror +) +if "a%ftpsite%" == "a" ( + echo You need to set FTP server in the ftpsite environment variable to upload the files + goto haderror +) + + + + :: Get the date and time into vars: For /f "tokens=2-4 delims=/. " %%a in ('date /t') do ( set MYYEAR=%%c @@ -34,11 +51,6 @@ For /f "tokens=2-4 delims=/. " %%a in ('date /t') do ( ) For /f "tokens=1-2 delims=/:" %%a in ('time /t') do (set MYTIME=%%a_%%b) -echo MYYEAR = %MYYEAR% -echo MYMONTH = %MYMONTH% -echo MYDAY = %MYDAY% -echo MYTIME = %MYTIME% - echo Performing nightbuild of MC-Server @@ -48,9 +60,6 @@ echo Performing nightbuild of MC-Server set DONOTPAUSE=y :: Update the sources to the latest revision: -del src\Bindings\Bindings.cpp -del src\Bindings\Bindings.h -git checkout -- src\Bindings\Bindings.* git pull if errorlevel 1 goto haderror @@ -72,7 +81,9 @@ if errorlevel 1 goto haderror :: Test if the version is already present, using a "tagfile" that we create upon successful build -set TAGFILE=Install\built_%COMMITID%.tag +set TAGFOLDER=Install\%MYYEAR%_%MYMONTH%\ +set TAGFILE=%TAGFOLDER%built_%COMMITID%.tag +echo Tag file: %TAGFILE% if exist %TAGFILE% ( echo Latest version already present, bailing out goto end @@ -81,13 +92,13 @@ if exist %TAGFILE% ( :: Update the Bindings: -del src\Bindings.cpp -del src\Bindings.h echo Updating Lua bindings +del src\Bindings\Bindings.cpp +del src\Bindings\Bindings.h set ALLTOLUA_WAIT=N -cd src +cd src\Bindings call AllToLua.bat -cd .. +cd ..\.. @@ -126,18 +137,6 @@ if errorlevel 1 goto haderror :: upload to the FTP: :upload -if "a%ftppass%" == "a" ( - echo You need to set FTP password in the ftppass environment variable to upload the files - goto end -) -if "a%ftpuser%" == "a" ( - echo You need to set FTP username in the ftpuser environment variable to upload the files - goto end -) -if "a%ftpsite%" == "a" ( - echo You need to set FTP server in the ftpsite environment variable to upload the files - goto end -) ncftpput -p %ftppass% -u %ftpuser% -T temp_ %ftpsite% / Install\MCServer_Win_%FILESUFFIX%.7z if errorlevel 1 goto haderror ncftpput -p %ftppass% -u %ftpuser% -T temp_ %ftpsite% /PDBs Install\PDBs_%FILESUFFIX%.7z @@ -148,6 +147,7 @@ echo Upload finished. :: Create the tagfile so that we know that this CommitID has been built already +mkdir %TAGFOLDER% touch %TAGFILE% From abd1ca4fef90864530ea4203aaaaaa17f7cdf5f7 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sat, 21 Dec 2013 19:30:16 +0100 Subject: [PATCH 013/118] Bindings regen script updated. The Bindings.cpp / .h files are no longer stored in the git repo. --- src/Bindings/AllToLua.bat | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Bindings/AllToLua.bat b/src/Bindings/AllToLua.bat index f7867fadb..b2a192880 100644 --- a/src/Bindings/AllToLua.bat +++ b/src/Bindings/AllToLua.bat @@ -1,17 +1,8 @@ :: AllToLua.bat - :: This scripts updates the automatically-generates Lua bindings in Bindings.cpp / Bindings.h - - - - - -:: If there was a Git conflict, resolve it by resetting to HEAD; we're regenerating the files from scratch anyway -git checkout --ours Bindings.cpp -git add -u Bindings.cpp -git checkout --ours Bindings.h -git add -u Bindings.h +:: When called without any parameters, it will pause for a keypress at the end +:: Call with any parameter to disable the wait (for buildserver use) @@ -23,5 +14,9 @@ git add -u Bindings.h - +: Wait for keypress, if no param given: if %ALLTOLUA_WAIT%N == N pause + + + + From 7eaf7342d46b163149adbcf54996750ffed699f3 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sat, 21 Dec 2013 19:34:03 +0100 Subject: [PATCH 014/118] Fixed a typo in .gitignore. The VC IntelliSense DB was getting through again. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 79ae3545d..7eb0a218a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ Symbols cloc-ignored.txt cloc.xml cloc.xsl - *.ncb +*.ncb *.user *.suo /EveryNight.cmd From f244dbfb129355ee83876941c775ea61fb5f0eaf Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 21 Dec 2013 19:58:52 +0100 Subject: [PATCH 015/118] You are now able to get different items from fishing. --- src/Items/ItemFishingRod.h | 110 ++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h index 87021fbd2..80ff6cf1c 100644 --- a/src/Items/ItemFishingRod.h +++ b/src/Items/ItemFishingRod.h @@ -65,11 +65,117 @@ public: if (Callbacks.CanPickup()) { cItems Drops; - Drops.Add(cItem(E_ITEM_RAW_FISH)); + int ItemCategory(a_World->GetTickRandomNumber(99)); + if (ItemCategory <= 4) // Treasures 5% + { + int Treasure(a_World->GetTickRandomNumber(5)); + switch (Treasure) + { + case 0: + { + Drops.Add(cItem(E_ITEM_BOW)); // TODO: Enchantments + break; + } + case 1: + { + Drops.Add(cItem(E_ITEM_BOOK)); // TODO: Enchanted book + break; + } + case 2: + { + Drops.Add(cItem(E_ITEM_FISHING_ROD, 1, a_World->GetTickRandomNumber(50))); // Fishing rod with durability. TODO: Enchantments on it + break; + } + case 3: + { + Drops.Add(cItem(E_ITEM_NAME_TAG)); + break; + } + case 4: + { + Drops.Add(cItem(E_ITEM_SADDLE)); + break; + } + case 5: + { + Drops.Add(cItem(E_BLOCK_LILY_PAD)); + break; + } + } + } + else if (ItemCategory <= 14) // Junk 10% + { + int Junk(a_World->GetTickRandomNumber(70)); + if (Junk <= 1) + { + Drops.Add(cItem(E_ITEM_DYE, 10, 0)); + } + else if (Junk <= 4) + { + Drops.Add(cItem(E_ITEM_BOW, 1, a_World->GetTickRandomNumber(64))); + } + else if (Junk <= 9) + { + Drops.Add(cItem(E_ITEM_STICK)); + } + else if (Junk <= 14) + { + Drops.Add(cItem(E_ITEM_STRING)); + } + else if (Junk <= 22) + { + Drops.Add(cItem(E_ITEM_BOWL)); + } + else if (Junk <= 30) + { + Drops.Add(cItem(E_ITEM_LEATHER)); + } + else if (Junk <= 38) + { + Drops.Add(cItem(E_ITEM_LEATHER_BOOTS)); + } + else if (Junk <= 46) + { + Drops.Add(cItem(E_ITEM_ROTTEN_FLESH)); + } + else if (Junk <= 54) + { + Drops.Add(cItem(E_ITEM_POTIONS)); + } + else if (Junk <= 62) + { + Drops.Add(cItem(E_ITEM_BONE)); + } + else if (Junk <= 70) + { + Drops.Add(cItem(E_BLOCK_TRIPWIRE_HOOK)); + } + } + else // Fish + { + int FishType(a_World->GetTickRandomNumber(99)); + if (FishType <= 1) // Clownfish has a 2% chance of spawning + { + Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_CLOWNFISH)); + } + else if (FishType <= 12) // Pufferfish has a 13% chance of spawning + { + Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_CLOWNFISH)); + } + else if (FishType <= 24) + { + Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_SALMON)); + } + else + { + Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_FISH)); + } + } + + Vector3d FloaterPos(Callbacks.GetPos()); Vector3d FlyDirection(a_Player->GetPosition() - FloaterPos); a_World->SpawnItemPickups(Drops, FloaterPos.x, FloaterPos.y, FloaterPos.z, FlyDirection.x, FlyDirection.Length() / (FlyDirection.y * 2), FlyDirection.z); - // TODO: More types of pickups. } } else From 76ff71810a4746eeeedc81365e925a3861af07f9 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 21 Dec 2013 20:42:43 +0100 Subject: [PATCH 016/118] Once again better Y direction for the items given when fishing. --- src/Items/ItemFishingRod.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h index 80ff6cf1c..e941251b2 100644 --- a/src/Items/ItemFishingRod.h +++ b/src/Items/ItemFishingRod.h @@ -174,8 +174,8 @@ public: Vector3d FloaterPos(Callbacks.GetPos()); - Vector3d FlyDirection(a_Player->GetPosition() - FloaterPos); - a_World->SpawnItemPickups(Drops, FloaterPos.x, FloaterPos.y, FloaterPos.z, FlyDirection.x, FlyDirection.Length() / (FlyDirection.y * 2), FlyDirection.z); + Vector3d FlyDirection(a_Player->GetEyePosition() - FloaterPos); + a_World->SpawnItemPickups(Drops, FloaterPos.x, FloaterPos.y, FloaterPos.z, FlyDirection.x, FlyDirection.y + 1, FlyDirection.z); } } else From c67621679159b3ed17b614c4066b334fd78497be Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sat, 21 Dec 2013 22:20:54 +0100 Subject: [PATCH 017/118] Using regular assignments. --- src/Items/ItemFishingRod.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h index e941251b2..80fca2d7f 100644 --- a/src/Items/ItemFishingRod.h +++ b/src/Items/ItemFishingRod.h @@ -65,10 +65,10 @@ public: if (Callbacks.CanPickup()) { cItems Drops; - int ItemCategory(a_World->GetTickRandomNumber(99)); + int ItemCategory = a_World->GetTickRandomNumber(99); if (ItemCategory <= 4) // Treasures 5% { - int Treasure(a_World->GetTickRandomNumber(5)); + int Treasure = a_World->GetTickRandomNumber(5); switch (Treasure) { case 0: @@ -105,7 +105,7 @@ public: } else if (ItemCategory <= 14) // Junk 10% { - int Junk(a_World->GetTickRandomNumber(70)); + int Junk = a_World->GetTickRandomNumber(70); if (Junk <= 1) { Drops.Add(cItem(E_ITEM_DYE, 10, 0)); @@ -153,7 +153,7 @@ public: } else // Fish { - int FishType(a_World->GetTickRandomNumber(99)); + int FishType = a_World->GetTickRandomNumber(99); if (FishType <= 1) // Clownfish has a 2% chance of spawning { Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_CLOWNFISH)); @@ -173,8 +173,8 @@ public: } - Vector3d FloaterPos(Callbacks.GetPos()); - Vector3d FlyDirection(a_Player->GetEyePosition() - FloaterPos); + Vector3d FloaterPos = Callbacks.GetPos(); + Vector3d FlyDirection = a_Player->GetEyePosition() - FloaterPos; a_World->SpawnItemPickups(Drops, FloaterPos.x, FloaterPos.y, FloaterPos.z, FlyDirection.x, FlyDirection.y + 1, FlyDirection.z); } } From 5b2ac381b63c199ac5150a5d058cde9d30ca9182 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sun, 22 Dec 2013 00:30:21 +0000 Subject: [PATCH 018/118] Fixed them again, confound it --- src/Simulator/RedstoneSimulator.cpp | 30 ++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp index e53c7c172..549c709f6 100644 --- a/src/Simulator/RedstoneSimulator.cpp +++ b/src/Simulator/RedstoneSimulator.cpp @@ -206,10 +206,6 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c itr = m_RepeatersDelayList.erase(itr); continue; } - else if (itr->a_ElapsedTicks < itr->a_DelayTicks) - { - itr->a_ElapsedTicks++; - } itr++; } @@ -659,8 +655,14 @@ void cRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int return; } } - - // Tick incrementing handled in SimChunk + else + { + // Apparently, incrementing ticks only works reliably here, and not in SimChunk; + // With a world with lots of redstone, the repeaters simply do not delay + // I am confounded to say why. Perhaps optimisation failure. + LOGD("Incremented a repeater @ %i %i %i | Elapsed ticks: %i | Target delay: %i", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks); + itr->a_ElapsedTicks++; + } } } @@ -897,7 +899,16 @@ void cRedstoneSimulator::HandleNoteBlock(int a_BlockX, int a_BlockY, int a_Block void cRedstoneSimulator::HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ) { - if (m_World.GetBlockSkyLight(a_BlockX, a_BlockY + 1, a_BlockZ) > 10) + for (int Y = a_BlockY + 1; Y < 10; Y++) + { + if (!g_BlockTransparent[m_World.GetBlock(a_BlockX, Y, a_BlockZ)]) + { + return; + } + } + + LOG("%i", m_World.GetSkyDarkness()); + if (m_World.GetSkyDarkness() > 10) { SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DAYLIGHT_SENSOR); } @@ -1318,7 +1329,7 @@ void cRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, in } // Already in here (normal to allow repeater to continue on powering and updating blocks in front) - just update info and quit - itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; // See below for description + itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + (ShouldPowerOn ? 1 : 0)) * 2; // See below for description itr->a_ElapsedTicks = 0; itr->ShouldPowerOn = ShouldPowerOn; return; @@ -1331,7 +1342,8 @@ void cRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, in // Gets the top two bits (delay time), shifts them into the lower two bits, and adds one (meta 0 = 1 tick; 1 = 2 etc.) // * 2 because apparently, MCS ticks are way faster than vanilla ticks, so repeater aren't noticeably delayed - RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; + // We don't +1 when powering off because everything seems to already delay a tick when powering off, why? No idea :P + RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + (ShouldPowerOn ? 1 : 0)) * 2; RC.a_ElapsedTicks = 0; From acb5a36a6aa3be861e84b7abc542b6223c55cddd Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sun, 22 Dec 2013 00:33:22 +0000 Subject: [PATCH 019/118] Daylight sensors --- src/Simulator/RedstoneSimulator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp index 549c709f6..10b750aeb 100644 --- a/src/Simulator/RedstoneSimulator.cpp +++ b/src/Simulator/RedstoneSimulator.cpp @@ -907,8 +907,7 @@ void cRedstoneSimulator::HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_ } } - LOG("%i", m_World.GetSkyDarkness()); - if (m_World.GetSkyDarkness() > 10) + if (m_World.GetSkyDarkness() < 8) { SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DAYLIGHT_SENSOR); } From e9d1352f6e07d120253ea27ef84129ecc892c540 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 14:45:25 +0100 Subject: [PATCH 020/118] Implented BroadcastParticleEffect --- src/Chunk.cpp | 16 ++++++++++++++++ src/Chunk.h | 1 + src/ChunkMap.cpp | 19 +++++++++++++++++++ src/ChunkMap.h | 1 + src/ClientHandle.cpp | 9 +++++++++ src/ClientHandle.h | 1 + src/Protocol/Protocol.h | 1 + src/Protocol/Protocol125.cpp | 9 +++++++++ src/Protocol/Protocol125.h | 1 + src/Protocol/Protocol15x.cpp | 21 +++++++++++++++++++++ src/Protocol/Protocol15x.h | 5 +++-- src/Protocol/Protocol17x.cpp | 18 ++++++++++++++++++ src/Protocol/Protocol17x.h | 1 + src/Protocol/ProtocolRecognizer.cpp | 10 ++++++++++ src/Protocol/ProtocolRecognizer.h | 1 + src/World.cpp | 9 +++++++++ src/World.h | 1 + 17 files changed, 122 insertions(+), 2 deletions(-) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 3c44b91d0..c446db9a6 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -2745,6 +2745,22 @@ void cChunk::BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animation +void cChunk::BroadcastParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmmount); + } // for itr - LoadedByClient[] +} + + + + + void cChunk::BroadcastRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude) { for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) diff --git a/src/Chunk.h b/src/Chunk.h index 1d762c0ca..05a96d419 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -277,6 +277,7 @@ public: void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastEntityAnimation (const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL); void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = NULL); void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index c67d8e2e8..37f8df766 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -605,6 +605,25 @@ void cChunkMap::BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animat +void cChunkMap::BroadcastParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + + cChunkDef::BlockToChunk(a_SrcX, a_SrcZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmmount, a_Exclude); +} + + + + + void cChunkMap::BroadcastRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); diff --git a/src/ChunkMap.h b/src/ChunkMap.h index dcc6abdc3..e688d1f93 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -74,6 +74,7 @@ public: void BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); void BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL); void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = NULL); void BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 65b376d38..99df47bfb 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1872,6 +1872,15 @@ void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cI +void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) +{ + m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmmount); +} + + + + + void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup) { m_Protocol->SendPickupSpawn(a_Pickup); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 6f82d5d46..26d5e74b7 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -113,6 +113,7 @@ public: void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item); void SendPickupSpawn (const cPickup & a_Pickup); void SendEntityAnimation (const cEntity & a_Entity, char a_Animation); + void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount); void SendPlayerAbilities (void); void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline); void SendPlayerMaxSpeed (void); ///< Informs the client of the maximum player speed (1.6.1+) diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index d90ece2b0..fdbffb3e9 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -81,6 +81,7 @@ public: virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0; virtual void SendPlayerAbilities (void) = 0; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) = 0; + virtual void SendParticleEffect (const AString & a_SoundName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) = 0; virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0; virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+) virtual void SendPlayerMoveLook (void) = 0; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index 48f23801c..e49dd43ff 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -608,6 +608,15 @@ void cProtocol125::SendEntityAnimation(const cEntity & a_Entity, char a_Animatio +void cProtocol125::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) +{ + // Not supported by this protocol version +} + + + + + void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) { cCSLock Lock(m_CSPacket); diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index ebbcd762a..0b32137d8 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -54,6 +54,7 @@ public: virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; virtual void SendKeepAlive (int a_PingID) override; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; diff --git a/src/Protocol/Protocol15x.cpp b/src/Protocol/Protocol15x.cpp index c337d26e7..7e2aa9490 100644 --- a/src/Protocol/Protocol15x.cpp +++ b/src/Protocol/Protocol15x.cpp @@ -36,6 +36,7 @@ Implements the 1.5.x protocol classes: enum { PACKET_WINDOW_OPEN = 0x64, + PACKET_PARTICLE_EFFECT = 0x3F, } ; @@ -76,6 +77,26 @@ void cProtocol150::SendWindowOpen(const cWindow & a_Window) +void cProtocol150::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_PARTICLE_EFFECT); + WriteString(a_ParticleName); + WriteFloat(a_SrcX); + WriteFloat(a_SrcY); + WriteFloat(a_SrcZ); + WriteFloat(a_OffsetX); + WriteFloat(a_OffsetY); + WriteFloat(a_OffsetZ); + WriteFloat(a_ParticleData); + WriteInt(a_ParticleAmmount); + Flush(); +} + + + + + int cProtocol150::ParseWindowClick(void) { HANDLE_PACKET_READ(ReadChar, char, WindowID); diff --git a/src/Protocol/Protocol15x.h b/src/Protocol/Protocol15x.h index e554fe130..0074b3a83 100644 --- a/src/Protocol/Protocol15x.h +++ b/src/Protocol/Protocol15x.h @@ -28,8 +28,9 @@ class cProtocol150 : public: cProtocol150(cClientHandle * a_Client); - virtual void SendWindowOpen(const cWindow & a_Window) override; - + virtual void SendWindowOpen (const cWindow & a_Window) override; + virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; + virtual int ParseWindowClick(void); } ; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 161e81936..bbbd5e973 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -521,6 +521,24 @@ void cProtocol172::SendEntityAnimation(const cEntity & a_Entity, char a_Animatio +void cProtocol172::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) +{ + cPacketizer Pkt(*this, 0x2A); + Pkt.WriteString(a_ParticleName); + Pkt.WriteFloat(a_SrcX); + Pkt.WriteFloat(a_SrcY); + Pkt.WriteFloat(a_SrcZ); + Pkt.WriteFloat(a_OffsetX); + Pkt.WriteFloat(a_OffsetY); + Pkt.WriteFloat(a_OffsetZ); + Pkt.WriteFloat(a_ParticleData); + Pkt.WriteInt(a_ParticleAmmount); +} + + + + + void cProtocol172::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) { cPacketizer Pkt(*this, 0x38); // Playerlist Item packet diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 4a91f0e56..cc0eda1e7 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -66,6 +66,7 @@ public: virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; + virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; virtual void SendPlayerMaxSpeed (void) override; virtual void SendPlayerMoveLook (void) override; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 30b48a92f..1cae4a750 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -386,6 +386,16 @@ void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_W +void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmmount); +} + + + + + void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup) { ASSERT(m_Protocol != NULL); diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 0d69e9406..fbcf59f3b 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -89,6 +89,7 @@ public: virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; virtual void SendKeepAlive (int a_PingID) override; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; diff --git a/src/World.cpp b/src/World.cpp index 976af671d..28c46c27e 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1878,6 +1878,15 @@ void cWorld::BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animation +void cWorld::BroadcastParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmmount, a_Exclude); +} + + + + + void cWorld::BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSPlayers); diff --git a/src/World.h b/src/World.h index 1effc5d5d..4f9bf67e7 100644 --- a/src/World.h +++ b/src/World.h @@ -162,6 +162,7 @@ public: void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastEntityAnimation (const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL); void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL); void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = NULL); void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // tolua_export a_Src coords are Block * 8 From 7a299f1ba62217c6d214f6cfbd266a41938ad577 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 14:48:22 +0100 Subject: [PATCH 021/118] Fishing now uses a countdown instead of a random number each tick. --- src/Entities/Floater.cpp | 37 ++++++++++++++++++++++++++----------- src/Entities/Floater.h | 4 +++- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index ac7a82f91..6409fecf5 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -9,11 +9,12 @@ -cFloater::cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID) : +cFloater::cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID, int a_CountDownTime) : cEntity(etFloater, a_X, a_Y, a_Z, 0.98, 0.98), m_PickupCountDown(0), m_PlayerID(a_PlayerID), - m_CanPickupItem(false) + m_CanPickupItem(false), + m_CountDownTime(a_CountDownTime) { SetSpeed(a_Speed); } @@ -36,17 +37,31 @@ void cFloater::Tick(float a_Dt, cChunk & a_Chunk) HandlePhysics(a_Dt, a_Chunk); if (IsBlockWater(m_World->GetBlock((int) GetPosX(), (int) GetPosY(), (int) GetPosZ())) && m_World->GetBlockMeta((int) GetPosX(), (int) GetPosY(), (int) GetPosZ()) == 0) { - if ((!m_CanPickupItem) && (m_World->GetTickRandomNumber(100) == 0)) + if (!m_CanPickupItem) { - SetPosY(GetPosY() - 1); - m_CanPickupItem = true; - m_PickupCountDown = 20; - LOGD("Floater %i can be picked up", GetUniqueID()); - } - else - { - SetSpeedY(0.7); + if (m_CountDownTime <= 0) + { + m_World->BroadcastSoundEffect("random.splash", (int) floor(GetPosX() * 8), (int) floor(GetPosY() * 8), (int) floor(GetPosZ() * 8), 1, 1); + SetPosY(GetPosY() - 1); + m_CanPickupItem = true; + m_PickupCountDown = 20; + m_CountDownTime = 100 + m_World->GetTickRandomNumber(800); + LOGD("Floater %i can be picked up", GetUniqueID()); + } + else if (m_CountDownTime == 20) // Calculate the position where the particles should spawn and start producing them. + { + LOGD("Started producing particles for floater %i", GetUniqueID()); + m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8))); + m_World->BroadcastParticleEffect("splash", m_ParticlePos.x, m_ParticlePos.y, m_ParticlePos.z, 0, 0, 0, 0, 15); + } + else if (m_CountDownTime < 20) + { + m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6); + m_World->BroadcastParticleEffect("splash", m_ParticlePos.x, m_ParticlePos.y, m_ParticlePos.z, 0, 0, 0, 0, 15); + } + m_CountDownTime--; } + SetSpeedY(0.7); } SetSpeedX(GetSpeedX() * 0.95); SetSpeedZ(GetSpeedZ() * 0.95); diff --git a/src/Entities/Floater.h b/src/Entities/Floater.h index 9bc5039f8..4db94986c 100644 --- a/src/Entities/Floater.h +++ b/src/Entities/Floater.h @@ -14,7 +14,7 @@ class cFloater : public: - cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID); + cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID, int a_CountDownTime); virtual void SpawnOn(cClientHandle & a_Client) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; @@ -23,7 +23,9 @@ public: protected: Vector3d m_Speed; + Vector3d m_ParticlePos; int m_PickupCountDown; int m_PlayerID; + int m_CountDownTime; bool m_CanPickupItem; } ; \ No newline at end of file From 9de847c9b1be8209a96cb6b77d7166157b947a99 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 14:51:19 +0100 Subject: [PATCH 022/118] Countdown time is between 100 and 900 ticks. --- src/Items/ItemFishingRod.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h index 80fca2d7f..0cb9fc654 100644 --- a/src/Items/ItemFishingRod.h +++ b/src/Items/ItemFishingRod.h @@ -180,7 +180,7 @@ public: } else { - cFloater * Floater = new cFloater(a_Player->GetPosX(), a_Player->GetStance(), a_Player->GetPosZ(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID()); + cFloater * Floater = new cFloater(a_Player->GetPosX(), a_Player->GetStance(), a_Player->GetPosZ(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID(), 100 + a_World->GetTickRandomNumber(800)); // TODO: for each lure level do 100 ticks less. Floater->Initialize(a_World); a_Player->SetIsFishing(true, Floater->GetUniqueID()); } From caccf72b46e8e40fa17c2b55a40ed1b6dc6557f2 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 14:54:07 +0100 Subject: [PATCH 023/118] Fixed compiler warnings. --- src/Entities/Floater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index 6409fecf5..d908167df 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -52,12 +52,12 @@ void cFloater::Tick(float a_Dt, cChunk & a_Chunk) { LOGD("Started producing particles for floater %i", GetUniqueID()); m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8))); - m_World->BroadcastParticleEffect("splash", m_ParticlePos.x, m_ParticlePos.y, m_ParticlePos.z, 0, 0, 0, 0, 15); + m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15); } else if (m_CountDownTime < 20) { m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6); - m_World->BroadcastParticleEffect("splash", m_ParticlePos.x, m_ParticlePos.y, m_ParticlePos.z, 0, 0, 0, 0, 15); + m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15); } m_CountDownTime--; } From 8d51c22b368409ef1bfcc4eff5c504209c5f24f7 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 15:22:50 +0100 Subject: [PATCH 024/118] Fishing underground is slower and fishing while raining is faster. --- src/Entities/Floater.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index d908167df..5e3338968 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -59,7 +59,22 @@ void cFloater::Tick(float a_Dt, cChunk & a_Chunk) m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6); m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15); } + m_CountDownTime--; + if (m_World->GetHeight((int) GetPosX(), (int) GetPosZ()) == (int) GetPosY()) + { + if (m_World->IsWeatherWet() && m_World->GetTickRandomNumber(3) == 0) // 25% chance of an extra countdown when being rained on. + { + m_CountDownTime--; + } + } + else // if the floater is underground it has a 50% chance of not decreasing the countdown. + { + if (m_World->GetTickRandomNumber(1) == 0) + { + m_CountDownTime++; + } + } } SetSpeedY(0.7); } From 5f18abaa271a7e4a396b444de65d387ade3a8204 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 15:30:47 +0100 Subject: [PATCH 025/118] Fixed compiler warning. --- src/ChunkMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 37f8df766..86fbceff7 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -610,7 +610,7 @@ void cChunkMap::BroadcastParticleEffect(const AString & a_ParticleName, float a_ cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_SrcX, a_SrcZ, ChunkX, ChunkZ); + cChunkDef::BlockToChunk((int) a_SrcX, (int) a_SrcZ, ChunkX, ChunkZ); cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); if (Chunk == NULL) { From dfa81829fecf4e3bcc3316ebd69deb7d2c205332 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 22 Dec 2013 15:33:43 +0100 Subject: [PATCH 026/118] Fixed signedness warning in cItemGrid. --- src/ItemGrid.cpp | 6 +++--- src/ItemGrid.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp index e9b86173e..d2e6b1c69 100644 --- a/src/ItemGrid.cpp +++ b/src/ItemGrid.cpp @@ -580,11 +580,11 @@ bool cItemGrid::DamageItem(int a_X, int a_Y, short a_Amount) -void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed) +void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, size_t a_CountLootProbabs, int a_NumSlots, int a_Seed) { // Calculate the total weight: int TotalProbab = 1; - for (int i = 0; i < a_CountLootProbabs; i++) + for (size_t i = 0; i < a_CountLootProbabs; i++) { TotalProbab += a_LootProbabs[i].m_Weight; } @@ -597,7 +597,7 @@ void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, i int LootRnd = Rnd % TotalProbab; Rnd >>= 8; cItem CurrentLoot = cItem(E_ITEM_BOOK, 1, 0); // TODO: enchantment - for (int j = 0; j < a_CountLootProbabs; j++) + for (size_t j = 0; j < a_CountLootProbabs; j++) { LootRnd -= a_LootProbabs[i].m_Weight; if (LootRnd < 0) diff --git a/src/ItemGrid.h b/src/ItemGrid.h index a4af523cf..b344e3daf 100644 --- a/src/ItemGrid.h +++ b/src/ItemGrid.h @@ -157,7 +157,7 @@ public: A total of a_NumSlots are taken by the loot. Cannot export to Lua due to raw array a_LootProbabs. TODO: Make this exportable / export through ManualBindings.cpp with a Lua table as LootProbabs */ - void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed); + void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, size_t a_CountLootProbabs, int a_NumSlots, int a_Seed); /// Adds a callback that gets called whenever a slot changes. Must not be called from within the listener callback! void AddListener(cListener & a_Listener); From 318dc76fc9b71a87309078823d1cd457e852157e Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 16:03:11 +0100 Subject: [PATCH 027/118] Made the lure enchantment work. --- src/Items/ItemFishingRod.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h index 0cb9fc654..a427b1676 100644 --- a/src/Items/ItemFishingRod.h +++ b/src/Items/ItemFishingRod.h @@ -180,7 +180,7 @@ public: } else { - cFloater * Floater = new cFloater(a_Player->GetPosX(), a_Player->GetStance(), a_Player->GetPosZ(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID(), 100 + a_World->GetTickRandomNumber(800)); // TODO: for each lure level do 100 ticks less. + cFloater * Floater = new cFloater(a_Player->GetPosX(), a_Player->GetStance(), a_Player->GetPosZ(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID(), 100 + a_World->GetTickRandomNumber(800) - (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLure) * 100)); Floater->Initialize(a_World); a_Player->SetIsFishing(true, Floater->GetUniqueID()); } From 05c670dc50093e5bc678c30766f383a3a0dbb4d3 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 18:37:56 +0100 Subject: [PATCH 028/118] Fixed cacus breaking when next to any non-air block. --- src/Blocks/BlockCactus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blocks/BlockCactus.h b/src/Blocks/BlockCactus.h index f77df5e42..88be25dc0 100644 --- a/src/Blocks/BlockCactus.h +++ b/src/Blocks/BlockCactus.h @@ -54,7 +54,7 @@ public: NIBBLETYPE BlockMeta; if ( a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && - (BlockType != E_BLOCK_AIR) + (g_BlockIsSolid[BlockType]) ) { return false; From 488d7ddd7dc641d198acb9371e841aae93794a76 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 20:15:09 +0100 Subject: [PATCH 029/118] EnderDragons and SnowGolems are able to spawn. --- src/Mobs/Monster.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index e5d21b2f2..563eec7bb 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -739,6 +739,7 @@ cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) case mtChicken: toReturn = new cChicken(); break; case mtCow: toReturn = new cCow(); break; case mtCreeper: toReturn = new cCreeper(); break; + case mtEnderDragon: toReturn = new cEnderDragon(); break; case mtEnderman: toReturn = new cEnderman(); break; case mtGhast: toReturn = new cGhast(); break; case mtMooshroom: toReturn = new cMooshroom(); break; @@ -746,6 +747,7 @@ cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) case mtPig: toReturn = new cPig(); break; case mtSheep: toReturn = new cSheep (Random.NextInt(15)); break; // Colour parameter case mtSilverfish: toReturn = new cSilverfish(); break; + case mtSnowGolem: toReturn = new cSnowGolem(); break; case mtSpider: toReturn = new cSpider(); break; case mtSquid: toReturn = new cSquid(); break; case mtWitch: toReturn = new cWitch(); break; From 41157247360733b6cde3c04c12ccd35427551356 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 20:15:56 +0100 Subject: [PATCH 030/118] You can spawn snow golems with snow blocks and pumpkins. --- src/Blocks/BlockPumpkin.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 76abc6818..cb78400cb 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -14,6 +14,21 @@ public: { } + virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override + { + if + ( + a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_SNOW_BLOCK && + a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_SNOW_BLOCK + ) + { + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtSnowGolem); + } + } + virtual bool GetPlacementBlockTypeMeta( cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, From 5a9b26d3226c86bef212b080ed2ffcd2129567ba Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 20:40:07 +0100 Subject: [PATCH 031/118] Snow golems die in hot biomes and leave a snow trail. --- src/Mobs/SnowGolem.cpp | 17 +++++++++++++++++ src/Mobs/SnowGolem.h | 1 + 2 files changed, 18 insertions(+) diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp index 9e199f87e..e16b526ad 100644 --- a/src/Mobs/SnowGolem.cpp +++ b/src/Mobs/SnowGolem.cpp @@ -2,6 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "SnowGolem.h" +#include "../World.h" @@ -24,3 +25,19 @@ void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) + +void cSnowGolem::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + if (IsBiomeNoDownfall((EMCSBiome) m_World->GetBiomeAt((int) floor(GetPosX()), (int) floor(GetPosZ())) )) + { + TakeDamage(*this); + } + else + { + if (g_BlockIsSolid[m_World->GetBlock((int) floor(GetPosX()), (int) floor(GetPosY()) - 1, (int) floor(GetPosZ()))]) + { + m_World->SetBlock((int) floor(GetPosX()), (int) floor(GetPosY()), (int) floor(GetPosZ()), E_BLOCK_SNOW, 0); + } + } +} diff --git a/src/Mobs/SnowGolem.h b/src/Mobs/SnowGolem.h index d1344adfd..ff5e90da8 100644 --- a/src/Mobs/SnowGolem.h +++ b/src/Mobs/SnowGolem.h @@ -17,6 +17,7 @@ public: CLASS_PROTODEF(cSnowGolem); + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; } ; From 945ba36d1b7b1b90028148aa5637f9842858af12 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sun, 22 Dec 2013 20:03:09 +0000 Subject: [PATCH 032/118] Implemented fall particles --- src/Entities/Player.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 8f30cd4cc..aedd3b77e 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -247,6 +247,9 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) m_World->SendPlayerList(this); m_LastPlayerListTime = t1.GetNowTime(); } + + if (IsFlying()) + m_LastGroundHeight = (float)GetPosY(); } @@ -447,10 +450,19 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) if (m_LastJumpHeight > m_LastGroundHeight) Damage++; m_LastJumpHeight = (float)GetPosY(); - if ((Damage > 0) && (!IsGameModeCreative())) + if (Damage > 0) { - TakeDamage(dtFalling, NULL, Damage, Damage, 0); - } + if (!IsGameModeCreative()) + { + TakeDamage(dtFalling, NULL, Damage, Damage, 0); + } + + GetWorld()->BroadcastSoundParticleEffect( + 2006, + (int)GetPosX(), (int)GetPosY() - 1, (int)GetPosZ(), + Damage // Used as particle effect speed modifier + ); + } m_LastGroundHeight = (float)GetPosY(); } @@ -974,6 +986,9 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) m_GameMode = a_GameMode; m_ClientHandle->SendGameMode(a_GameMode); + + SetFlying(false); + SetCanFly(false); } From 43e819630910e80f4420f15baad3d5322b54b0b3 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sun, 22 Dec 2013 20:03:58 +0000 Subject: [PATCH 033/118] Added many event catchers * Window close * CTRL-C * etc. --- src/Root.cpp | 34 ++++++++++++++++++++-------- src/main.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/Root.cpp b/src/Root.cpp index fffd8fb47..798f965be 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -22,6 +22,7 @@ #include "inifile/iniFile.h" #ifdef _WIN32 + #include "conio.h" #include #elif defined(__linux__) #include @@ -29,6 +30,8 @@ #include #endif +extern bool g_TERMINATE_EVENT_RAISED; + @@ -76,7 +79,7 @@ void cRoot::InputThread(void * a_Params) cLogCommandOutputCallback Output; - while (!(self.m_bStop || self.m_bRestart) && std::cin.good()) + while (!self.m_bStop && !self.m_bRestart && !g_TERMINATE_EVENT_RAISED && std::cin.good()) { AString Command; std::getline(std::cin, Command); @@ -85,10 +88,10 @@ void cRoot::InputThread(void * a_Params) self.ExecuteConsoleCommand(TrimString(Command), Output); } } - - if (!(self.m_bStop || self.m_bRestart)) + + if (g_TERMINATE_EVENT_RAISED || !std::cin.good()) { - // We have come here because the std::cin has received an EOF and the server is still running; stop the server: + // We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running; stop the server: self.m_bStop = true; } } @@ -99,6 +102,12 @@ void cRoot::InputThread(void * a_Params) void cRoot::Start(void) { + #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 + cDeadlockDetect dd; delete m_Log; m_Log = new cMCLogger(); @@ -192,12 +201,20 @@ void cRoot::Start(void) finishmseconds -= mseconds; LOG("Startup complete, took %i ms!", finishmseconds); + #ifdef _WIN32 + EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button + #endif - while (!m_bStop && !m_bRestart) // These are modified by external threads + while (!m_bStop && !m_bRestart && !g_TERMINATE_EVENT_RAISED) // These are modified by external threads { cSleep::MilliSleep(1000); } + if (g_TERMINATE_EVENT_RAISED) + { + m_bStop = true; + } + #if !defined(ANDROID_NDK) delete m_InputThread; m_InputThread = NULL; #endif @@ -222,7 +239,7 @@ void cRoot::Start(void) delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; delete m_CraftingRecipes; m_CraftingRecipes = NULL; LOGD("Forgetting groups..."); - delete m_GroupManager; m_GroupManager = 0; + delete m_GroupManager; m_GroupManager = NULL; LOGD("Unloading worlds..."); UnloadWorlds(); @@ -233,12 +250,11 @@ void cRoot::Start(void) cBlockHandler::Deinit(); LOG("Cleaning up..."); - //delete HeartBeat; HeartBeat = 0; - delete m_Server; m_Server = 0; + delete m_Server; m_Server = NULL; LOG("Shutdown successful!"); } - delete m_Log; m_Log = 0; + delete m_Log; m_Log = NULL; } diff --git a/src/main.cpp b/src/main.cpp index 1f6aad24f..84339b8d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,10 @@ #include #endif // _MSC_VER +// Here, we have some ALL CAPS variables, to give the impression that this is deeeep, gritty programming :P +bool g_TERMINATE_EVENT_RAISED = false; // If something has told the server to stop; checked periodically in cRoot +bool g_SERVER_TERMINATED = false; // Set to true when the server terminates, so our CTRL handler can then tell Windows to close the console + @@ -33,14 +37,21 @@ - -void ShowCrashReport(int) +void NonCtrlHandler(int a_Signal) { - std::signal(SIGSEGV, SIG_DFL); + LOGD("Terminate event raised from std::signal"); + g_TERMINATE_EVENT_RAISED = true; - printf("\n\nMCServer has crashed!\n"); - - exit(-1); + switch (a_Signal) + { + case SIGSEGV: + { + std::signal(SIGSEGV, SIG_DFL); + LOGWARN("Segmentation fault; MCServer has crashed :("); + exit(EXIT_FAILURE); + } + default: break; + } } @@ -111,13 +122,33 @@ LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_Except +#ifdef _WIN32 +// Handle CTRL events in windows, including console window close +BOOL CtrlHandler(DWORD fdwCtrlType) +{ + g_TERMINATE_EVENT_RAISED = true; + LOGD("Terminate event raised from the Windows CtrlHandler"); + + if (fdwCtrlType == CTRL_CLOSE_EVENT) + { + while (!g_SERVER_TERMINATED) { cSleep::MilliSleep(100); } // Delay as much as possible to try to get the server to shut down cleanly + } + + return TRUE; +} +#endif + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // main: int main( int argc, char **argv ) { - (void)argc; - (void)argv; + UNUSED(argc); + UNUSED(argv); #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) InitLeakFinder(); @@ -149,6 +180,13 @@ int main( int argc, char **argv ) } #endif // _WIN32 && !_WIN64 // End of dump-file magic + + #ifdef _WIN32 + if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE)) + { + LOGERROR("Could not install the Windows CTRL handler!"); + } + #endif #if defined(_DEBUG) && defined(_MSC_VER) _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); @@ -160,7 +198,9 @@ int main( int argc, char **argv ) #endif // _DEBUG && _MSC_VER #ifndef _DEBUG - std::signal(SIGSEGV, ShowCrashReport); + std::signal(SIGSEGV, NonCtrlHandler); + std::signal(SIGTERM, NonCtrlHandler); + std::signal(SIGINT, NonCtrlHandler); #endif // DEBUG: test the dumpfile creation: @@ -188,8 +228,10 @@ int main( int argc, char **argv ) #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) DeinitLeakFinder(); #endif - - return 0; + + g_SERVER_TERMINATED = true; + + return EXIT_SUCCESS; } From b02a81678c22b00955bc3b2b87ceed55027bb35a Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sun, 22 Dec 2013 20:04:17 +0000 Subject: [PATCH 034/118] Implemented knockback and critical hit --- src/Entities/Entity.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 8fcdcc82f..0f5ed7262 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -13,6 +13,7 @@ #include "../Bindings/PluginManager.h" #include "../Tracer.h" #include "Minecart.h" +#include "Player.h" @@ -239,10 +240,14 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R TDI.Attacker = a_Attacker; TDI.RawDamage = a_RawDamage; TDI.FinalDamage = a_FinalDamage; - Vector3d Heading; - Heading.x = sin(GetRotation()); - Heading.y = 0.4; // TODO: adjust the amount of "up" knockback when testing - Heading.z = cos(GetRotation()); + + Vector3d Heading(0, 0, 0); + if (a_Attacker != NULL) + { + Heading = a_Attacker->GetLookVector() * (a_Attacker->IsSprinting() ? 10 : 8); + } + Heading.y += 3; + TDI.Knockback = Heading * a_KnockbackAmount; DoTakeDamage(TDI); } @@ -297,6 +302,16 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) return; } + if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer())) + { + // IsOnGround() only is true if the player is moving downwards + if (!((cPlayer *)a_TDI.Attacker)->IsOnGround()) // TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain) + { + a_TDI.FinalDamage + 2; + m_World->BroadcastEntityAnimation(*this, 4); // Critical hit + } + } + m_Health -= (short)a_TDI.FinalDamage; // TODO: Apply damage to armor @@ -306,6 +321,8 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) m_Health = 0; } + AddSpeed(a_TDI.Knockback * 3); + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT); if (m_Health <= 0) From 48b388aa97fa5e3819f91ab6a77dfdc4af5b1a0a Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sun, 22 Dec 2013 20:06:15 +0000 Subject: [PATCH 035/118] A fix for daylight sensors --- src/Simulator/RedstoneSimulator.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp index 10b750aeb..c26663aa1 100644 --- a/src/Simulator/RedstoneSimulator.cpp +++ b/src/Simulator/RedstoneSimulator.cpp @@ -899,7 +899,7 @@ void cRedstoneSimulator::HandleNoteBlock(int a_BlockX, int a_BlockY, int a_Block void cRedstoneSimulator::HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ) { - for (int Y = a_BlockY + 1; Y < 10; Y++) + for (int Y = a_BlockY + 1; Y < cChunkDef::Height; Y++) { if (!g_BlockTransparent[m_World.GetBlock(a_BlockX, Y, a_BlockZ)]) { @@ -907,7 +907,8 @@ void cRedstoneSimulator::HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_ } } - if (m_World.GetSkyDarkness() < 8) + NIBBLETYPE SkyLight = m_World.GetBlockSkyLight(a_BlockX, a_BlockY + 1, a_BlockZ) - m_World.GetSkyDarkness(); + if (SkyLight > 8) { SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DAYLIGHT_SENSOR); } From 5af74fe77c78a0b084d2b8e528610adfdf538be3 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Sun, 22 Dec 2013 21:12:34 +0100 Subject: [PATCH 036/118] Fixed bug where snowgolems could replace non-solid blocks to snow blocks. --- src/Mobs/SnowGolem.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp index e16b526ad..06021cca5 100644 --- a/src/Mobs/SnowGolem.cpp +++ b/src/Mobs/SnowGolem.cpp @@ -35,7 +35,9 @@ void cSnowGolem::Tick(float a_Dt, cChunk & a_Chunk) } else { - if (g_BlockIsSolid[m_World->GetBlock((int) floor(GetPosX()), (int) floor(GetPosY()) - 1, (int) floor(GetPosZ()))]) + BLOCKTYPE BlockBelow = m_World->GetBlock((int) floor(GetPosX()), (int) floor(GetPosY()) - 1, (int) floor(GetPosZ())); + BLOCKTYPE Block = m_World->GetBlock((int) floor(GetPosX()), (int) floor(GetPosY()), (int) floor(GetPosZ())); + if (Block == E_BLOCK_AIR && g_BlockIsSolid[BlockBelow]) { m_World->SetBlock((int) floor(GetPosX()), (int) floor(GetPosY()), (int) floor(GetPosZ()), E_BLOCK_SNOW, 0); } From 11fbfb458d3da8dcba4fdc2d65783a90b660e62d Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sun, 22 Dec 2013 23:20:43 +0000 Subject: [PATCH 037/118] Daylight sensor sensing enhancement --- src/Simulator/RedstoneSimulator.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp index c26663aa1..3e267525f 100644 --- a/src/Simulator/RedstoneSimulator.cpp +++ b/src/Simulator/RedstoneSimulator.cpp @@ -899,18 +899,20 @@ void cRedstoneSimulator::HandleNoteBlock(int a_BlockX, int a_BlockY, int a_Block void cRedstoneSimulator::HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ) { - for (int Y = a_BlockY + 1; Y < cChunkDef::Height; Y++) - { - if (!g_BlockTransparent[m_World.GetBlock(a_BlockX, Y, a_BlockZ)]) - { - return; - } - } + int a_ChunkX, a_ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, a_ChunkX, a_ChunkZ); - NIBBLETYPE SkyLight = m_World.GetBlockSkyLight(a_BlockX, a_BlockY + 1, a_BlockZ) - m_World.GetSkyDarkness(); - if (SkyLight > 8) + if (!m_World.IsChunkLighted(a_ChunkX, a_ChunkZ)) { - SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DAYLIGHT_SENSOR); + m_World.QueueLightChunk(a_ChunkX, a_ChunkZ); + } + else + { + NIBBLETYPE SkyLight = m_World.GetBlockSkyLight(a_BlockX, a_BlockY + 1, a_BlockZ) - m_World.GetSkyDarkness(); + if (SkyLight > 8) + { + SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DAYLIGHT_SENSOR); + } } } From b95ee2d230ee25f02ac86589da2e8b9f723981bf Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Mon, 23 Dec 2013 10:26:45 +0100 Subject: [PATCH 038/118] You can now spawn iron golems. You can spawn them like in vanilla. --- src/Blocks/BlockPumpkin.h | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index cb78400cb..20a6fef5c 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -22,11 +22,28 @@ public: a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_SNOW_BLOCK ) { - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_World->SetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->SetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtSnowGolem); } + if + ( + a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) || (a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) || (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && + a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK + ) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); + } } virtual bool GetPlacementBlockTypeMeta( From c6946fb8c40685747ee576b77fe03bf721e2c83d Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Mon, 23 Dec 2013 10:32:49 +0100 Subject: [PATCH 039/118] Some fixes (SEE DESC) -Added missing mobs (wither, enderdragon and iron golem) to type to string list. -Wither and iron golem can now spawn --- src/Mobs/Monster.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 563eec7bb..f05d7362a 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -40,8 +40,10 @@ static const struct {cMonster::mtCow, "cow"}, {cMonster::mtCreeper, "creeper"}, {cMonster::mtEnderman, "enderman"}, + {cMonster::mtEnderDragon, "enderdragon"}, {cMonster::mtGhast, "ghast"}, {cMonster::mtHorse, "horse"}, + {cMonster::mtIronGolem, "irongolem"}, {cMonster::mtMagmaCube, "magmacube"}, {cMonster::mtMooshroom, "mooshroom"}, {cMonster::mtOcelot, "ocelot"}, @@ -49,11 +51,13 @@ static const struct {cMonster::mtSheep, "sheep"}, {cMonster::mtSilverfish, "silverfish"}, {cMonster::mtSkeleton, "skeleton"}, + {cMonster::mtSnowGolem, "snowgolem"}, {cMonster::mtSlime, "slime"}, {cMonster::mtSpider, "spider"}, {cMonster::mtSquid, "squid"}, {cMonster::mtVillager, "villager"}, {cMonster::mtWitch, "witch"}, + {cMonster::mtWither, "wither"}, {cMonster::mtWolf, "wolf"}, {cMonster::mtZombie, "zombie"}, {cMonster::mtZombiePigman, "zombiepigman"}, @@ -742,6 +746,7 @@ cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) case mtEnderDragon: toReturn = new cEnderDragon(); break; case mtEnderman: toReturn = new cEnderman(); break; case mtGhast: toReturn = new cGhast(); break; + case mtIronGolem: toReturn = new cIronGolem(); break; case mtMooshroom: toReturn = new cMooshroom(); break; case mtOcelot: toReturn = new cOcelot(); break; case mtPig: toReturn = new cPig(); break; @@ -751,6 +756,7 @@ cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) case mtSpider: toReturn = new cSpider(); break; case mtSquid: toReturn = new cSquid(); break; case mtWitch: toReturn = new cWitch(); break; + case mtWither: toReturn = new cWither(); break; case mtWolf: toReturn = new cWolf(); break; case mtZombie: toReturn = new cZombie(false); break; // TODO: Infected zombie parameter case mtZombiePigman: toReturn = new cZombiePigman(); break; From 003043f8abcda9c3117a4824d21bbaf45bc5b72a Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 23 Dec 2013 09:41:34 +0000 Subject: [PATCH 040/118] Added some comments in main.cpp --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 84339b8d6..a4ee09952 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -129,7 +129,7 @@ BOOL CtrlHandler(DWORD fdwCtrlType) g_TERMINATE_EVENT_RAISED = true; LOGD("Terminate event raised from the Windows CtrlHandler"); - if (fdwCtrlType == CTRL_CLOSE_EVENT) + if (fdwCtrlType == CTRL_CLOSE_EVENT) // Console windows closed via 'x' button, Windows will try to close immediately, therefore... { while (!g_SERVER_TERMINATED) { cSleep::MilliSleep(100); } // Delay as much as possible to try to get the server to shut down cleanly } From d8221854d2f529e8165d5153c767bb29a54646a8 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 23 Dec 2013 09:41:45 +0000 Subject: [PATCH 041/118] Fixed a comment in Entity.cpp --- src/Entities/Entity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 0f5ed7262..d82a89ab2 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -304,7 +304,7 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer())) { - // IsOnGround() only is true if the player is moving downwards + // IsOnGround() only is false if the player is moving downwards if (!((cPlayer *)a_TDI.Attacker)->IsOnGround()) // TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain) { a_TDI.FinalDamage + 2; From 9c796bf6b42b54ce5efe294c03ffd28366917805 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 23 Dec 2013 09:51:41 +0000 Subject: [PATCH 042/118] Clarified some code in Player.cpp --- src/Entities/Player.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index aedd3b77e..0c133d3fa 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -987,8 +987,11 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) m_GameMode = a_GameMode; m_ClientHandle->SendGameMode(a_GameMode); - SetFlying(false); - SetCanFly(false); + if (a_GameMode != gmCreative) + { + SetFlying(false); + SetCanFly(false); + } } From 99ade2fa12bd67058926dfa7d6ca6c11d4fadb99 Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Mon, 23 Dec 2013 12:00:08 +0100 Subject: [PATCH 043/118] Improved iron golem detection algorithm - You need to make it like in vanilla - Only used blocks are removed --- src/Blocks/BlockPumpkin.h | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 20a6fef5c..7aecad1e0 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -30,8 +30,8 @@ public: if ( a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) || (a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) || (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && + a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK && + a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK && a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK ) { @@ -39,11 +39,24 @@ public: a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); + } + else if + ( + a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && + a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && + a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && + a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK + ) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); - } + } } virtual bool GetPlacementBlockTypeMeta( From 76444470a482a9d05c7c3eb95d0f9ac118dda53e Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 23 Dec 2013 15:22:05 +0000 Subject: [PATCH 044/118] Provides an enhancement to daylight sensors --- src/Simulator/RedstoneSimulator.cpp | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp index 3e267525f..638b977d4 100644 --- a/src/Simulator/RedstoneSimulator.cpp +++ b/src/Simulator/RedstoneSimulator.cpp @@ -112,10 +112,35 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c LOGD("cRedstoneSimulator: Erased block %s from powered blocks list due to present/past metadata mismatch", ItemToFullString(itr->a_SourceBlock).c_str()); itr = m_PoweredBlocks.erase(itr); } + else if (SourceBlockType == E_BLOCK_DAYLIGHT_SENSOR) + { + if (!a_Chunk->IsLightValid()) + { + m_World.QueueLightChunk(a_ChunkX, a_ChunkZ); + itr++; + continue; + } + else + { + NIBBLETYPE SkyLight; + a_Chunk->UnboundedRelGetBlockSkyLight(RelX, itr->a_SourcePos.y + 1, RelZ, SkyLight); + + if (a_Chunk->GetTimeAlteredLight(SkyLight) <= 8) // Could use SkyLight - m_World.GetSkyDarkness(); + { + LOGD("cRedstoneSimulator: Erased daylight sensor from powered blocks list due to insufficient light level"); + itr = m_PoweredBlocks.erase(itr); + } + else + { + itr++; + continue; + } + } + } else if ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (DestBlockType == E_BLOCK_REDSTONE_WIRE)) { // It is simply not allowed that a wire powers another wire, presuming that data here is sane and a dest and source are beside each other - LOGD("cRedstoneSimulator: Erased redstone wire from powered blocks list because it's source was also wire"); + LOGD("cRedstoneSimulator: Erased redstone wire from powered blocks list because its source was also wire"); itr = m_PoweredBlocks.erase(itr); } else @@ -283,7 +308,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c } } - ++dataitr; + dataitr++; } } From 1bd1696cebd6157261f32c3e8bdecf5914312c22 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Mon, 23 Dec 2013 16:56:21 +0100 Subject: [PATCH 045/118] Just a quick update for the files you need to compile for Android. --- Android/jni/Android.mk | 28 +++++++++++++--------------- Android/jni/app-android.cpp | 12 +++++++----- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Android/jni/Android.mk b/Android/jni/Android.mk index 23488e359..3542e588b 100644 --- a/Android/jni/Android.mk +++ b/Android/jni/Android.mk @@ -5,7 +5,7 @@ LOCAL_MODULE := mcserver -LOCAL_SRC_FILES := $(shell find ../CryptoPP ../lua-5.1.4 ../jsoncpp-src-0.5.0 ../zlib-1.2.7 ../source ../squirrel_3_0_1_stable ../tolua++-1.0.93 ../iniFile ../WebServer ../expat '(' -name '*.cpp' -o -name '*.c' ')') +LOCAL_SRC_FILES := $(shell find ../CryptoPP ../lua ../jsoncpp ../zlib ../src ../tolua++ ../iniFile ../expat ../md5 ../sqlite ../luaexpat '(' -name '*.cpp' -o -name '*.c' ')') LOCAL_SRC_FILES := $(filter-out %SquirrelFunctions.cpp %SquirrelBindings.cpp %cPlugin_Squirrel.cpp %cSquirrelCommandBinder.cpp %minigzip.c %lua.c %tolua.c %toluabind.c %LeakFinder.cpp %StackWalker.cpp %example.c,$(LOCAL_SRC_FILES)) LOCAL_SRC_FILES := $(patsubst %.cpp,../%.cpp,$(LOCAL_SRC_FILES)) LOCAL_SRC_FILES := $(patsubst %.c,../%.c,$(LOCAL_SRC_FILES)) @@ -20,23 +20,21 @@ LOCAL_CFLAGS := -DANDROID_NDK \ LOCAL_STATIC_LIBRARIES := cpufeatures -LOCAL_C_INCLUDES := ../source \ - ../source/md5 \ - ../WebServer \ - ../source/packets \ - ../source/items \ - ../source/blocks \ - ../tolua++-1.0.93/src/lib \ - ../lua-5.1.4/src \ +LOCAL_C_INCLUDES := ../src \ + ../src/packets \ + ../src/items \ + ../src/blocks \ + ../tolua++/src/lib \ + ../lua/src \ ../zlib-1.2.7 \ ../iniFile \ - ../tolua++-1.0.93/include \ - ../jsoncpp-src-0.5.0/include \ - ../jsoncpp-src-0.5.0/src/lib_json \ - ../squirrel_3_0_1_stable/include \ - ../squirrel_3_0_1_stable \ - ../squirrel_3_0_1_stable/sqrat \ + ../tolua++/include \ + ../jsoncpp/include \ + ../jsoncpp/src/lib_json \ ../expat/ \ + ../md5/ \ + ../sqlite/ \ + ../luaexpat/ \ .. \ diff --git a/Android/jni/app-android.cpp b/Android/jni/app-android.cpp index 197df29c4..40009c77c 100644 --- a/Android/jni/app-android.cpp +++ b/Android/jni/app-android.cpp @@ -11,8 +11,9 @@ #include #include "OSSupport/CriticalSection.h" -#include "OSSupport/MakeDir.h" +#include "OSSupport/File.h" #include "ToJava.h" +#include "inifile/iniFile.h" #include "Root.h" #include "WebAdmin.h" @@ -84,7 +85,7 @@ extern "C" void Java_com_mcserver_MCServerActivity_NativeOnCreate( JNIEnv* env, //__android_log_print(ANDROID_LOG_ERROR,"MCServer", "%s", "Logging from C++!"); g_CriticalSection.Unlock(); - mkdir("/sdcard/mcserver", S_IRWXU | S_IRWXG | S_IRWXO); + cFile::CreateFolder("/sdcard/mcserver"); pRoot = new cRoot(); pRoot->Start(); @@ -105,7 +106,7 @@ extern "C" void Java_com_mcserver_MCServerActivity_NativeCleanUp( JNIEnv* env, __android_log_print(ANDROID_LOG_ERROR,"MCServer", "pRoot: %p", pRoot); if( pRoot != NULL ) { - pRoot->ExecuteConsoleCommand("stop"); + pRoot->QueueExecuteConsoleCommand("stop"); } } @@ -122,9 +123,10 @@ extern "C" jboolean Java_com_mcserver_MCServerActivity_NativeIsServerRunning( JN extern "C" jint Java_com_mcserver_MCServerActivity_NativeGetWebAdminPort( JNIEnv* env, jobject thiz ) { - if( pRoot != NULL && pRoot->GetWebAdmin() != NULL ) + cIniFile IniFile; + if (IniFile.ReadFile("/sdcard/mcserver/webadmin.ini")) { - return pRoot->GetWebAdmin()->GetPort(); + return IniFile.GetValueI("WebAdmin", "Port"); } return 0; } \ No newline at end of file From 1014c737a4f16ce374fa834822ba173c00cae678 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 23 Dec 2013 21:18:01 +0000 Subject: [PATCH 046/118] Improved player fall particle positions --- src/Entities/Player.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 0c133d3fa..26572f39b 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -457,11 +457,8 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) TakeDamage(dtFalling, NULL, Damage, Damage, 0); } - GetWorld()->BroadcastSoundParticleEffect( - 2006, - (int)GetPosX(), (int)GetPosY() - 1, (int)GetPosZ(), - Damage // Used as particle effect speed modifier - ); + // Apparently, Mojang changed player positions to always be rounded up. Normally, it doesn't affect much, but we need effect positions to be precise, so ceil() + GetWorld()->BroadcastSoundParticleEffect(2006, (int)floor(GetPosX()), (int)GetPosY() - 1, (int)floor(GetPosZ()), Damage /* Used as particle effect speed modifier */); } m_LastGroundHeight = (float)GetPosY(); From 5bbe72bc905b8729ac3bb7a47dcf886eec759d85 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Mon, 23 Dec 2013 22:36:22 +0100 Subject: [PATCH 047/118] Replaced esCreeper with esMonster. Any monster will be able to explode, and also this fixes #425. --- src/Bindings/PluginLua.cpp | 2 +- src/BlockID.h | 7 +++++-- src/World.h | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 0d17c9ce2..0e5a66cd6 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -386,7 +386,7 @@ bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_Can { case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res); break; - case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res); break; + case esMonster: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cMonster *)a_SourceData, cLuaState::Return, res); break; case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res); break; case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res); break; case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; diff --git a/src/BlockID.h b/src/BlockID.h index 9742e9745..288719ccf 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -833,14 +833,17 @@ enum eExplosionSource { esOther, esPrimedTNT, - esCreeper, + esMonster, esBed, esEnderCrystal, esGhastFireball, esWitherSkullBlack, esWitherSkullBlue, esWitherBirth, - esPlugin + esPlugin, + + // Obsolete constants, kept for compatibility, will be removed after some time: + esCreeper = esMonster, } ; // tolua_end diff --git a/src/World.h b/src/World.h index 4f9bf67e7..c067252d9 100644 --- a/src/World.h +++ b/src/World.h @@ -420,7 +420,7 @@ public: a_SourceData exact type depends on the a_Source: | esOther | void * | | esPrimedTNT | cTNTEntity * | - | esCreeper | cCreeper * | + | esMonster | cMonster * | | esBed | cVector3i * | | esEnderCrystal | Vector3i * | | esGhastFireball | cGhastFireball * | From e593cef109ec847a1f20bfd6f4d4bb25a93bc165 Mon Sep 17 00:00:00 2001 From: Sam Mauldin Date: Mon, 23 Dec 2013 20:58:39 -0600 Subject: [PATCH 048/118] Store plugin in a local variable to fix error on disable --- MCServer/Plugins/MagicCarpet/plugin.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/MCServer/Plugins/MagicCarpet/plugin.lua b/MCServer/Plugins/MagicCarpet/plugin.lua index 81eb02a9c..b05816e48 100644 --- a/MCServer/Plugins/MagicCarpet/plugin.lua +++ b/MCServer/Plugins/MagicCarpet/plugin.lua @@ -1,4 +1,5 @@ local Carpets = {} +local PLUGIN function Initialize( Plugin ) Plugin:SetName( "MagicCarpet" ) @@ -9,7 +10,9 @@ function Initialize( Plugin ) local PluginManager = cPluginManager:Get() PluginManager:BindCommand("/mc", "magiccarpet", HandleCarpetCommand, " - Spawns a magical carpet"); - + + PLUGIN = Plugin + LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() ) return true end @@ -75,4 +78,4 @@ function OnPlayerMoving(Player) end Carpet:moveTo( cLocation:new( Player:GetPosX(), Player:GetPosY(), Player:GetPosZ() ) ) end -end \ No newline at end of file +end From 002dd1fdd6dc2091b580ccec8adc52a3e72f1646 Mon Sep 17 00:00:00 2001 From: Sam Mauldin Date: Mon, 23 Dec 2013 21:04:34 -0600 Subject: [PATCH 049/118] Add myself to CONTRIBUTORS to comply with the Apache License --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 99110ac90..dbcc93c7e 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -17,5 +17,6 @@ Sofapriester mborland SamJBarney worktycho +Sxw1212 Please add yourself to this list if you contribute to MCServer. From be79d12966dac2b5e3ec4ce711b8d4e2372cef7d Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 24 Dec 2013 10:14:33 +0000 Subject: [PATCH 050/118] Update chat colours @madmaxoft, is this right? fixes #467 --- src/ChatColor.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ChatColor.cpp b/src/ChatColor.cpp index 2b223ee76..dca274d76 100644 --- a/src/ChatColor.cpp +++ b/src/ChatColor.cpp @@ -1,4 +1,3 @@ - #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "ChatColor.h" @@ -29,9 +28,9 @@ const std::string cChatColor::Underlined = cChatColor::Color + "n"; const std::string cChatColor::Italic = cChatColor::Color + "o"; const std::string cChatColor::Plain = cChatColor::Color + "r"; -const std::string cChatColor::MakeColor( char a_Color ) +const std::string cChatColor::MakeColor( std::string a_Color ) { - return cChatColor::Color + a_Color; + return a_Color; } From 085008ff8c20e553933a840280244f0462fb16b1 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 24 Dec 2013 10:17:44 +0000 Subject: [PATCH 051/118] Update as per xoft's instructions. --- src/ChatColor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ChatColor.cpp b/src/ChatColor.cpp index dca274d76..f42fa2f4d 100644 --- a/src/ChatColor.cpp +++ b/src/ChatColor.cpp @@ -28,7 +28,7 @@ const std::string cChatColor::Underlined = cChatColor::Color + "n"; const std::string cChatColor::Italic = cChatColor::Color + "o"; const std::string cChatColor::Plain = cChatColor::Color + "r"; -const std::string cChatColor::MakeColor( std::string a_Color ) +const std::string cChatColor::MakeColor( const AString & a_Color ) { return a_Color; } From b397dc438b3f6b908b688e2699f5b9293db66f8a Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 24 Dec 2013 10:24:08 +0000 Subject: [PATCH 052/118] Updated MakeColour's defenition in the header. --- src/ChatColor.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ChatColor.h b/src/ChatColor.h index 85b10f400..e2a9044f3 100644 --- a/src/ChatColor.h +++ b/src/ChatColor.h @@ -29,15 +29,15 @@ public: static const std::string Yellow; static const std::string White; - // Styles ( source: http://wiki.vg/Chat ) - static const std::string Random; - static const std::string Bold; - static const std::string Strikethrough; - static const std::string Underlined; - static const std::string Italic; - static const std::string Plain; + // Styles ( source: http://wiki.vg/Chat ) + static const std::string Random; + static const std::string Bold; + static const std::string Strikethrough; + static const std::string Underlined; + static const std::string Italic; + static const std::string Plain; - static const std::string MakeColor( char a_Color ); + static const std::string MakeColor( const AString & a_Color ); }; // tolua_end From 8674c6800fc904606c6f53408aaae95474b0d7b0 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 24 Dec 2013 10:29:18 +0000 Subject: [PATCH 053/118] updated another file... --- src/GroupManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp index 1ffe3812f..3d454603b 100644 --- a/src/GroupManager.cpp +++ b/src/GroupManager.cpp @@ -77,13 +77,13 @@ cGroupManager::cGroupManager() LOGD("Loading group: %s", KeyName.c_str() ); Group->SetName( KeyName ); - char Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; + AString Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; if( Color != '-' ) - Group->SetColor( cChatColor::MakeColor(Color) ); + Group->SetColor( cChatColor::Color + Color ); else Group->SetColor( cChatColor::White ); - std::string Commands = IniFile.GetValue( KeyName, "Commands", "" ); + AString Commands = IniFile.GetValue( KeyName, "Commands", "" ); if( Commands.size() > 0 ) { AStringVector Split = StringSplit( Commands, "," ); @@ -93,7 +93,7 @@ cGroupManager::cGroupManager() } } - std::string Permissions = IniFile.GetValue( KeyName, "Permissions", "" ); + AString Permissions = IniFile.GetValue( KeyName, "Permissions", "" ); if( Permissions.size() > 0 ) { AStringVector Split = StringSplit( Permissions, "," ); From af6389ea7ab79663d687e22a18d21b3db86cd71c Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 24 Dec 2013 10:30:36 +0000 Subject: [PATCH 054/118] finally removed them all? --- src/Entities/Player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 948a259ff..b923a094e 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1294,7 +1294,7 @@ AString cPlayer::GetColor(void) const { if ( m_Color != '-' ) { - return cChatColor::MakeColor( m_Color ); + return cChatColor::Color + m_Color; } if ( m_Groups.size() < 1 ) From 8c91f7062af78f5b88bf96a35c1e67ecf7280dcf Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 24 Dec 2013 10:36:06 +0000 Subject: [PATCH 055/118] fixed compile? --- src/GroupManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp index 3d454603b..792acc2c3 100644 --- a/src/GroupManager.cpp +++ b/src/GroupManager.cpp @@ -77,7 +77,7 @@ cGroupManager::cGroupManager() LOGD("Loading group: %s", KeyName.c_str() ); Group->SetName( KeyName ); - AString Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; + char Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; if( Color != '-' ) Group->SetColor( cChatColor::Color + Color ); else From 84bfffcc158f79678c59c99dacc48628d0409123 Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Tue, 24 Dec 2013 12:23:20 +0100 Subject: [PATCH 056/118] Fixed a possible crash --- src/Blocks/BlockPumpkin.h | 81 ++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 7aecad1e0..7fca69d08 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -16,47 +16,50 @@ public: virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override { - if - ( - a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_SNOW_BLOCK && - a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_SNOW_BLOCK - ) + if (a_BlockY > 1) { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtSnowGolem); + if + ( + a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_SNOW_BLOCK && + a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_SNOW_BLOCK + ) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtSnowGolem); + } + if + ( + a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && + a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK && + a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK && + a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK + ) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); + } + else if + ( + a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && + a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && + a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && + a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK + ) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); + } } - if - ( - a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK - ) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); - } - else if - ( - a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK - ) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); - } } virtual bool GetPlacementBlockTypeMeta( From 5e245773738ebe31a91f93e60194d62e26c48f9e Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Tue, 24 Dec 2013 12:35:40 +0100 Subject: [PATCH 057/118] Using parenthesis --- src/Blocks/BlockPumpkin.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 7fca69d08..79b3f3a54 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -31,10 +31,10 @@ public: } if ( - a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) && + (a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK) ) { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); @@ -46,10 +46,10 @@ public: } else if ( - a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK && - a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && + (a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && + (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && + (a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK) ) { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); From 155f1751cef82d6f2bbf40190c978d1920d5bcf0 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Tue, 24 Dec 2013 13:48:15 +0100 Subject: [PATCH 058/118] Added the list_item.xml --- Android/res/layout/list_item.xml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Android/res/layout/list_item.xml diff --git a/Android/res/layout/list_item.xml b/Android/res/layout/list_item.xml new file mode 100644 index 000000000..e77da47db --- /dev/null +++ b/Android/res/layout/list_item.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file From 8c6337ff202d81b65cb45b81c60b5d780853e6e7 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 24 Dec 2013 13:25:03 +0000 Subject: [PATCH 059/118] Removed documentation for cChatColour:MakeColor() --- MCServer/Plugins/APIDump/APIDesc.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 5bc4a5f39..d69dd14cd 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -1,4 +1,3 @@ - -- APIDesc.lua -- Contains the API objects' descriptions @@ -308,18 +307,15 @@ g_APIDesc = A wrapper class for constants representing colors or effects. ]], - Functions = - { - MakeColor = { Params = "ColorCodeConstant", Return = "string", Notes = "Creates the complete color-code-sequence from the color or effect constant" }, - }, + Functions = {}, Constants = { Black = { Notes = "" }, Blue = { Notes = "" }, Bold = { Notes = "" }, - Color = { Notes = "The first character of the color-code-sequence, §" }, + Color = { Notes = "The first character of the color-code-sequence, �" }, DarkPurple = { Notes = "" }, - Delimiter = { Notes = "The first character of the color-code-sequence, §" }, + Delimiter = { Notes = "The first character of the color-code-sequence, �" }, Gold = { Notes = "" }, Gray = { Notes = "" }, Green = { Notes = "" }, From e1d3b201bd7a22f2924182f0243bb51433080c79 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 24 Dec 2013 13:25:58 +0000 Subject: [PATCH 060/118] Finally removed the function. --- src/ChatColor.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ChatColor.cpp b/src/ChatColor.cpp index f42fa2f4d..72a0a6928 100644 --- a/src/ChatColor.cpp +++ b/src/ChatColor.cpp @@ -28,11 +28,5 @@ const std::string cChatColor::Underlined = cChatColor::Color + "n"; const std::string cChatColor::Italic = cChatColor::Color + "o"; const std::string cChatColor::Plain = cChatColor::Color + "r"; -const std::string cChatColor::MakeColor( const AString & a_Color ) -{ - return a_Color; -} - - From 200f37c41f20baddf63e9ad6c3098c144a00d0d7 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Tue, 24 Dec 2013 13:26:54 +0000 Subject: [PATCH 061/118] removed the function def also --- src/ChatColor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ChatColor.h b/src/ChatColor.h index e2a9044f3..643c4d5d8 100644 --- a/src/ChatColor.h +++ b/src/ChatColor.h @@ -37,7 +37,6 @@ public: static const std::string Italic; static const std::string Plain; - static const std::string MakeColor( const AString & a_Color ); }; // tolua_end From b42487744871ab4587edabe2a3b87c9d26ac030f Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Tue, 24 Dec 2013 14:44:24 +0100 Subject: [PATCH 062/118] Fixed getting configure button going to port 0. --- Android/jni/app-android.cpp | 7 +++---- src/WebAdmin.cpp | 6 +++--- src/WebAdmin.h | 6 ++++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Android/jni/app-android.cpp b/Android/jni/app-android.cpp index 40009c77c..8f4346ac3 100644 --- a/Android/jni/app-android.cpp +++ b/Android/jni/app-android.cpp @@ -123,10 +123,9 @@ extern "C" jboolean Java_com_mcserver_MCServerActivity_NativeIsServerRunning( JN extern "C" jint Java_com_mcserver_MCServerActivity_NativeGetWebAdminPort( JNIEnv* env, jobject thiz ) { - cIniFile IniFile; - if (IniFile.ReadFile("/sdcard/mcserver/webadmin.ini")) + if( pRoot != NULL && pRoot->GetWebAdmin() != NULL ) { - return IniFile.GetValueI("WebAdmin", "Port"); + return atoi(pRoot->GetWebAdmin()->GetIPv4Ports().c_str()); } return 0; -} \ No newline at end of file +} diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index 462702893..a1f0842aa 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -100,10 +100,10 @@ bool cWebAdmin::Init(void) LOGD("Initialising WebAdmin..."); - AString PortsIPv4 = m_IniFile.GetValueSet("WebAdmin", "Port", "8080"); - AString PortsIPv6 = m_IniFile.GetValueSet("WebAdmin", "PortsIPv6", ""); + m_PortsIPv4 = m_IniFile.GetValueSet("WebAdmin", "Port", "8080"); + m_PortsIPv6 = m_IniFile.GetValueSet("WebAdmin", "PortsIPv6", ""); - if (!m_HTTPServer.Initialize(PortsIPv4, PortsIPv6)) + if (!m_HTTPServer.Initialize(m_PortsIPv4, m_PortsIPv6)) { return false; } diff --git a/src/WebAdmin.h b/src/WebAdmin.h index c629d44ff..0907e7bc3 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -132,6 +132,9 @@ public: /// Escapes text passed into it, so it can be embedded into html. static AString GetHTMLEscapedString(const AString & a_Input); + AString GetIPv4Ports(void) const { return m_PortsIPv4; } + AString GetIPv6Ports(void) const { return m_PortsIPv6; } + // tolua_end /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) @@ -180,6 +183,9 @@ protected: PluginList m_Plugins; + AString m_PortsIPv4; + AString m_PortsIPv6; + /// The Lua template script to provide templates: cLuaState m_TemplateScript; From 6ad0da5ce0dd01a94344ea7371f517b8307e73e9 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Tue, 24 Dec 2013 14:52:19 +0100 Subject: [PATCH 063/118] Removed unneeded include. --- Android/jni/app-android.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Android/jni/app-android.cpp b/Android/jni/app-android.cpp index 8f4346ac3..386f1b90e 100644 --- a/Android/jni/app-android.cpp +++ b/Android/jni/app-android.cpp @@ -13,7 +13,6 @@ #include "OSSupport/CriticalSection.h" #include "OSSupport/File.h" #include "ToJava.h" -#include "inifile/iniFile.h" #include "Root.h" #include "WebAdmin.h" From 89f87f66a5b64619cacf86461c1a0f26563e34c5 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 24 Dec 2013 15:02:35 +0000 Subject: [PATCH 064/118] Improved knockback animation * Critical hits now actually increase damage * Looking down at an entity and hitting them still produces upwards motion (as per Vanilla experience) * Reduced force to be more realistic --- src/Entities/Entity.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index d82a89ab2..8a74c9da4 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -246,7 +246,7 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R { Heading = a_Attacker->GetLookVector() * (a_Attacker->IsSprinting() ? 10 : 8); } - Heading.y += 3; + Heading.y = 2; TDI.Knockback = Heading * a_KnockbackAmount; DoTakeDamage(TDI); @@ -307,7 +307,7 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) // IsOnGround() only is false if the player is moving downwards if (!((cPlayer *)a_TDI.Attacker)->IsOnGround()) // TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain) { - a_TDI.FinalDamage + 2; + a_TDI.FinalDamage += 2; m_World->BroadcastEntityAnimation(*this, 4); // Critical hit } } @@ -321,7 +321,7 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) m_Health = 0; } - AddSpeed(a_TDI.Knockback * 3); + AddSpeed(a_TDI.Knockback * 2); m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT); From c6314e89357434d49fc185e94bccb0fe4b1bc3d7 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 24 Dec 2013 15:02:55 +0000 Subject: [PATCH 065/118] Implemented stone and wooden pressure plates --- src/Simulator/RedstoneSimulator.cpp | 102 +++++++++++++++++++++++++++- src/Simulator/RedstoneSimulator.h | 6 +- 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp index 638b977d4..b78e6582d 100644 --- a/src/Simulator/RedstoneSimulator.cpp +++ b/src/Simulator/RedstoneSimulator.cpp @@ -8,6 +8,7 @@ #include "../Blocks/BlockTorch.h" #include "../Blocks/BlockDoor.h" #include "../Piston.h" +#include "../Tracer.h" @@ -106,7 +107,8 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (SourceBlockMeta == 0)) || ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(SourceBlockMeta)) || ((SourceBlockType == E_BLOCK_DETECTOR_RAIL) && (SourceBlockMeta & 0x08) == 0x08) || - (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(SourceBlockMeta))) + (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(SourceBlockMeta))) || + (((SourceBlockType == E_BLOCK_STONE_PRESSURE_PLATE) || (SourceBlockType == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (SourceBlockMeta == 0)) ) { LOGD("cRedstoneSimulator: Erased block %s from powered blocks list due to present/past metadata mismatch", ItemToFullString(itr->a_SourceBlock).c_str()); @@ -306,6 +308,12 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c HandleRail(a_X, dataitr->y, a_Z, BlockType); break; } + case E_BLOCK_WOODEN_PRESSURE_PLATE: + case E_BLOCK_STONE_PRESSURE_PLATE: + { + HandlePressurePlate(a_X, dataitr->y, a_Z, BlockType); + break; + } } dataitr++; @@ -945,6 +953,98 @@ void cRedstoneSimulator::HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_ +void cRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType) +{ + switch (a_MyType) + { + case E_BLOCK_STONE_PRESSURE_PLATE: + { + // MCS feature - stone pressure plates can only be triggered by players :D + cPlayer * a_Player = m_World.FindClosestPlayer(Vector3f(a_BlockX + 0.5f, (float)a_BlockY, a_BlockZ + 0.5f), 0.5f); + + if (a_Player != NULL) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x1); + SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_STONE_PRESSURE_PLATE); + } + else + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0); + } + break; + } + case E_BLOCK_WOODEN_PRESSURE_PLATE: + { + class cWoodenPressurePlateCallback : + public cEntityCallback + { + public: + cWoodenPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + m_X(a_BlockX), + m_Y(a_BlockY), + m_Z(a_BlockZ), + m_World(a_World), + m_Entity(NULL) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + cTracer LineOfSight(m_World); + + Vector3f EntityPos = a_Entity->GetPosition(); + Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); + float Distance = (EntityPos - BlockPos).Length(); + + if (Distance < 0.5) + { + if (!LineOfSight.Trace(BlockPos, (EntityPos - BlockPos), (int)(EntityPos - BlockPos).Length())) + { + m_Entity = a_Entity; + return true; // Break out, we only need to know for wooden plates that at least one entity is on top + } + } + return false; + } + + bool FoundEntity(void) const + { + return m_Entity != NULL; + } + + protected: + cEntity * m_Entity; + cWorld * m_World; + + int m_X; + int m_Y; + int m_Z; + } ; + + cWoodenPressurePlateCallback WoodenPressurePlateCallback(a_BlockX, a_BlockY, a_BlockZ, &m_World); + m_World.ForEachEntity(WoodenPressurePlateCallback); + + if (WoodenPressurePlateCallback.FoundEntity()) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x1); + SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WOODEN_PRESSURE_PLATE); + } + else + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0); + } + break; + } + default: + LOGD("Unimplemented pressure plate type %s in cRedstoneSimulator", ItemToFullString(a_MyType).c_str()); + break; + } +} + + + + + bool cRedstoneSimulator::AreCoordsDirectlyPowered(int a_BlockX, int a_BlockY, int a_BlockZ) { for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list diff --git a/src/Simulator/RedstoneSimulator.h b/src/Simulator/RedstoneSimulator.h index 309135497..60c86a3c5 100644 --- a/src/Simulator/RedstoneSimulator.h +++ b/src/Simulator/RedstoneSimulator.h @@ -89,6 +89,10 @@ private: void HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ); /// Handles buttons void HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); + /// Handles daylight sensors + void HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ); + /// Handles pressure plates + void HandlePressurePlate(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType); /* ==================== */ /* ====== CARRIERS ====== */ @@ -115,8 +119,6 @@ private: void HandleTrapdoor(int a_BlockX, int a_BlockY, int a_BlockZ); /// Handles noteblocks void HandleNoteBlock(int a_BlockX, int a_BlockY, int a_BlockZ); - /// Handles noteblocks - void HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ); /* ===================== */ /* ====== Helper functions ====== */ From ae6cb01e5666a8d4cedaf221f2e784f862784482 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 24 Dec 2013 15:16:30 +0000 Subject: [PATCH 066/118] Fixed comments in main.cpp and Player.cpp --- src/Entities/Player.cpp | 2 +- src/main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 26572f39b..6d199e130 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -457,7 +457,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) TakeDamage(dtFalling, NULL, Damage, Damage, 0); } - // Apparently, Mojang changed player positions to always be rounded up. Normally, it doesn't affect much, but we need effect positions to be precise, so ceil() + // Mojang uses floor() to get X and Z positions, instead of just casting it to an (int) GetWorld()->BroadcastSoundParticleEffect(2006, (int)floor(GetPosX()), (int)GetPosY() - 1, (int)floor(GetPosZ()), Damage /* Used as particle effect speed modifier */); } diff --git a/src/main.cpp b/src/main.cpp index a4ee09952..81c6b41e4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -129,7 +129,7 @@ BOOL CtrlHandler(DWORD fdwCtrlType) g_TERMINATE_EVENT_RAISED = true; LOGD("Terminate event raised from the Windows CtrlHandler"); - if (fdwCtrlType == CTRL_CLOSE_EVENT) // Console windows closed via 'x' button, Windows will try to close immediately, therefore... + if (fdwCtrlType == CTRL_CLOSE_EVENT) // Console window closed via 'x' button, Windows will try to close immediately, therefore... { while (!g_SERVER_TERMINATED) { cSleep::MilliSleep(100); } // Delay as much as possible to try to get the server to shut down cleanly } From c06388aa4ab7d5f41922f669b1960762188545b6 Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Tue, 24 Dec 2013 18:07:33 +0100 Subject: [PATCH 067/118] Storing Y -1 and -2 blocks in a variable --- src/Blocks/BlockPumpkin.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 79b3f3a54..78a38fd02 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -16,12 +16,14 @@ public: virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override { - if (a_BlockY > 1) + if (a_BlockY > 1) // Make sure server won't check for inexistent blocks (below y=0) { + int BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + int BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);// We don't need to check this blocks more than 1 time if ( - a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_SNOW_BLOCK && - a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_SNOW_BLOCK + (BlockY1 == E_BLOCK_SNOW_BLOCK) && + (BlockY2 == E_BLOCK_SNOW_BLOCK) ) { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); @@ -31,10 +33,10 @@ public: } if ( - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && + (BlockY1 == E_BLOCK_IRON_BLOCK) && (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) && - (a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK) + (BlockY2 == E_BLOCK_IRON_BLOCK) ) { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); From 85fc1478fa8ec847fa467db29d70d37714972ba9 Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Tue, 24 Dec 2013 18:37:09 +0100 Subject: [PATCH 068/118] Changed a little how detection works and added comments. --- src/Blocks/BlockPumpkin.h | 59 ++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 78a38fd02..8e8c7cdce 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -16,13 +16,13 @@ public: virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override { - if (a_BlockY > 1) // Make sure server won't check for inexistent blocks (below y=0) + if (a_BlockY > 1) //Make sure server won't check for inexistent blocks (below y=0). { int BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - int BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);// We don't need to check this blocks more than 1 time + int BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);//We don't need to check this 2 blocks more than 1 time. if ( - (BlockY1 == E_BLOCK_SNOW_BLOCK) && + (BlockY1 == E_BLOCK_SNOW_BLOCK) && //If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. (BlockY2 == E_BLOCK_SNOW_BLOCK) ) { @@ -31,35 +31,38 @@ public: a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtSnowGolem); } - if + else if ( - (BlockY1 == E_BLOCK_IRON_BLOCK) && - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) && + (BlockY1 == E_BLOCK_IRON_BLOCK) && //If the first two blocks below the pumpkin are iron blocks, spawn an iron golem. (BlockY2 == E_BLOCK_IRON_BLOCK) ) { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); - } - else if - ( - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && - (a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && - (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && - (a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) == E_BLOCK_IRON_BLOCK) - ) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); + if + ( + (a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && //Check the first possible locations for arms. + (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) + ) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); //Set all blocks used for creation to air. + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); //Spawn an iron golem. + } + else if + ( + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && //Check the other possible locations. + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) + ) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); + } } } } From 59f8068cf427879f3ac303c254231e4339eb780f Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Tue, 24 Dec 2013 18:41:49 +0100 Subject: [PATCH 069/118] Fixed comments --- src/Blocks/BlockPumpkin.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 8e8c7cdce..7a1f7da4d 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -16,13 +16,13 @@ public: virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override { - if (a_BlockY > 1) //Make sure server won't check for inexistent blocks (below y=0). + if (a_BlockY > 1) // Make sure server won't check for inexistent blocks (below y=0). { int BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - int BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);//We don't need to check this 2 blocks more than 1 time. + int BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); // We don't need to check this 2 blocks more than 1 time. if ( - (BlockY1 == E_BLOCK_SNOW_BLOCK) && //If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. + (BlockY1 == E_BLOCK_SNOW_BLOCK) && // If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. (BlockY2 == E_BLOCK_SNOW_BLOCK) ) { @@ -33,13 +33,13 @@ public: } else if ( - (BlockY1 == E_BLOCK_IRON_BLOCK) && //If the first two blocks below the pumpkin are iron blocks, spawn an iron golem. + (BlockY1 == E_BLOCK_IRON_BLOCK) && // If the first two blocks below the pumpkin are iron blocks, spawn an iron golem. (BlockY2 == E_BLOCK_IRON_BLOCK) ) { if ( - (a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && //Check the first possible locations for arms. + (a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && // Check the first possible locations for arms. (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) ) { @@ -47,12 +47,12 @@ public: a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); //Set all blocks used for creation to air. - a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); //Spawn an iron golem. + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Set all blocks used for creation to air. + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); // Spawn an iron golem. } else if ( - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && //Check the other possible locations. + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && // Check the other possible locations. (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) ) { From 0b49f896eb5607a17371fb8c76432db838453781 Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Tue, 24 Dec 2013 18:50:54 +0100 Subject: [PATCH 070/118] Added myself to contributors --- CONTRIBUTORS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 99110ac90..bd6b204a4 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -17,5 +17,7 @@ Sofapriester mborland SamJBarney worktycho +Sxw1212 +tonibm19 Please add yourself to this list if you contribute to MCServer. From 276f7dae5c6c83bd0abe18c9b73efd6918b00dde Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Tue, 24 Dec 2013 19:08:22 +0100 Subject: [PATCH 071/118] Using BLOCKTYPE instead of int --- src/Blocks/BlockPumpkin.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 7a1f7da4d..1ddc1ed1f 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -18,8 +18,8 @@ public: { if (a_BlockY > 1) // Make sure server won't check for inexistent blocks (below y=0). { - int BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - int BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); // We don't need to check this 2 blocks more than 1 time. + BLOCKTYPE BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + BLOCKTYPE BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); // We don't need to check this 2 blocks more than 1 time. if ( (BlockY1 == E_BLOCK_SNOW_BLOCK) && // If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. From e3f89400d8c4488aad905e4c5a163543bb3603ff Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Tue, 24 Dec 2013 19:13:18 +0100 Subject: [PATCH 072/118] Implemented xoft suggestion --- src/Blocks/BlockPumpkin.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 1ddc1ed1f..3fbe76f8c 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -22,9 +22,10 @@ public: BLOCKTYPE BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); // We don't need to check this 2 blocks more than 1 time. if ( - (BlockY1 == E_BLOCK_SNOW_BLOCK) && // If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. - (BlockY2 == E_BLOCK_SNOW_BLOCK) + return; ) + (BlockY1 == E_BLOCK_SNOW_BLOCK) && // If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. + (BlockY2 == E_BLOCK_SNOW_BLOCK) { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); @@ -33,15 +34,17 @@ public: } else if ( + return; + ) (BlockY1 == E_BLOCK_IRON_BLOCK) && // If the first two blocks below the pumpkin are iron blocks, spawn an iron golem. (BlockY2 == E_BLOCK_IRON_BLOCK) - ) { if ( - (a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && // Check the first possible locations for arms. - (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) + return; ) + (a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && // Check the first possible locations for arms. + (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); @@ -52,9 +55,10 @@ public: } else if ( - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && // Check the other possible locations. - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) + return; ) + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && // Check the other possible locations. + (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); From 96c94a79a75c10532ab8fcebd209554c626c074b Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Tue, 24 Dec 2013 19:25:34 +0100 Subject: [PATCH 073/118] Fixed compilation --- src/Blocks/BlockPumpkin.h | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 3fbe76f8c..5c2d2f66c 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -13,52 +13,32 @@ public: : cBlockHandler(a_BlockType) { } - + virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override { if (a_BlockY > 1) // Make sure server won't check for inexistent blocks (below y=0). { - BLOCKTYPE BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - BLOCKTYPE BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); // We don't need to check this 2 blocks more than 1 time. - if - ( - return; - ) - (BlockY1 == E_BLOCK_SNOW_BLOCK) && // If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. - (BlockY2 == E_BLOCK_SNOW_BLOCK) + int BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + int BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);// We don't need to check this 2 blocks more than 1 time. + if ((BlockY1 == E_BLOCK_SNOW_BLOCK) && (BlockY2 == E_BLOCK_SNOW_BLOCK)) //If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtSnowGolem); } - else if - ( - return; - ) - (BlockY1 == E_BLOCK_IRON_BLOCK) && // If the first two blocks below the pumpkin are iron blocks, spawn an iron golem. - (BlockY2 == E_BLOCK_IRON_BLOCK) + else if ((BlockY1 == E_BLOCK_IRON_BLOCK) && (BlockY2 == E_BLOCK_IRON_BLOCK)) //If the first two blocks below the pumpkin are iron blocks, spawn an iron golem. { - if - ( - return; - ) - (a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && // Check the first possible locations for arms. - (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) + if ((a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK)) //Check the first possible locations for arms. { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Set all blocks used for creation to air. - a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); // Spawn an iron golem. + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); //Set all blocks used for creation to air. + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); //Spawn an iron golem. } - else if - ( - return; - ) - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && // Check the other possible locations. - (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) + else if((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK)) //Check the other possible locations. { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); From 258bb4588051c410e302d8f51bf8c1ad68123352 Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Tue, 24 Dec 2013 19:31:32 +0100 Subject: [PATCH 074/118] BLOCKTYPE... --- src/Blocks/BlockPumpkin.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 5c2d2f66c..d7b8513a7 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -18,8 +18,8 @@ public: { if (a_BlockY > 1) // Make sure server won't check for inexistent blocks (below y=0). { - int BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - int BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);// We don't need to check this 2 blocks more than 1 time. + BLOCKTYPE BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + BLOCKTYPE BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);// We don't need to check this 2 blocks more than 1 time. if ((BlockY1 == E_BLOCK_SNOW_BLOCK) && (BlockY2 == E_BLOCK_SNOW_BLOCK)) //If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); From 508e505aced6f80a0ed1f3d89495add658b11956 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Tue, 24 Dec 2013 23:47:04 +0000 Subject: [PATCH 075/118] Used IsGamemodeCreative() function --- src/Entities/Player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 6d199e130..a4b580b00 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -984,7 +984,7 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) m_GameMode = a_GameMode; m_ClientHandle->SendGameMode(a_GameMode); - if (a_GameMode != gmCreative) + if (!IsGameModeCreative()) { SetFlying(false); SetCanFly(false); From 40c2826a21d71dd8829a29f97fdfb36b68eaba14 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Wed, 25 Dec 2013 14:05:37 +0000 Subject: [PATCH 076/118] Braced up some code --- src/Entities/Player.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index a4b580b00..41c1542d3 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -249,7 +249,9 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) } if (IsFlying()) + { m_LastGroundHeight = (float)GetPosY(); + } } From 1a02d94ed2ff0b7d948319c48905408dde9f9b35 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Wed, 25 Dec 2013 14:12:48 +0000 Subject: [PATCH 077/118] Fixed rails not updating with diagonals --- src/Blocks/BlockRail.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h index 24a101652..55cadfa48 100644 --- a/src/Blocks/BlockRail.h +++ b/src/Blocks/BlockRail.h @@ -43,6 +43,40 @@ public: } + virtual void OnPlaced(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override + { + super::OnPlaced(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + + // Alert diagonal rails + OnNeighborChanged(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ); + OnNeighborChanged(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ); + OnNeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1); + OnNeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1); + + OnNeighborChanged(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ); + OnNeighborChanged(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ); + OnNeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1); + OnNeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1); + } + + + virtual void OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + super::OnDestroyed(a_World, a_BlockX, a_BlockY, a_BlockZ); + + // Alert diagonal rails + OnNeighborChanged(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ); + OnNeighborChanged(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ); + OnNeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1); + OnNeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1); + + OnNeighborChanged(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ); + OnNeighborChanged(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ); + OnNeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1); + OnNeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1); + } + + virtual void OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override { NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); From 6b6b6e74989dc1c8008ae8fef8491847e405331b Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Wed, 25 Dec 2013 16:59:26 +0100 Subject: [PATCH 078/118] Implemented xoft suggestion (partly) I only get it working in first condition. It seems it's not compatible with else if... --- src/Blocks/BlockPumpkin.h | 54 +++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index d7b8513a7..744f0ecde 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -16,40 +16,44 @@ public: virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override { - if (a_BlockY > 1) // Make sure server won't check for inexistent blocks (below y=0). + virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override + { + if (!a_BlockY > 1) // Make sure server won't check for inexistent blocks (below y=0). { - BLOCKTYPE BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - BLOCKTYPE BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);// We don't need to check this 2 blocks more than 1 time. - if ((BlockY1 == E_BLOCK_SNOW_BLOCK) && (BlockY2 == E_BLOCK_SNOW_BLOCK)) //If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. + return; + } + BLOCKTYPE BlockY1 = a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + BLOCKTYPE BlockY2 = a_World->GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); // We don't need to check this 2 blocks more than 1 time. + if ((BlockY1 == E_BLOCK_SNOW_BLOCK) && (BlockY2 == E_BLOCK_SNOW_BLOCK)) //If the first two blocks below the pumpkin are snow blocks, spawn a snow golem. + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtSnowGolem); + } + else if ((BlockY1 == E_BLOCK_IRON_BLOCK) && (BlockY2 == E_BLOCK_IRON_BLOCK)) //If the first two blocks below the pumpkin are iron blocks, spawn an iron golem. + { + if ((a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK)) //Check the first possible locations for arms. { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtSnowGolem); + a_World->FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); //Set all blocks used for creation to air. + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); //Spawn an iron golem. } - else if ((BlockY1 == E_BLOCK_IRON_BLOCK) && (BlockY2 == E_BLOCK_IRON_BLOCK)) //If the first two blocks below the pumpkin are iron blocks, spawn an iron golem. + else if((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK)) //Check the other possible locations. { - if ((a_World->GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && (a_World->GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK)) //Check the first possible locations for arms. - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); //Set all blocks used for creation to air. - a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); //Spawn an iron golem. - } - else if((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && (a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK)) //Check the other possible locations. - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); - a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); - } + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); + a_World->FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtIronGolem); } } } + } virtual bool GetPlacementBlockTypeMeta( cWorld * a_World, cPlayer * a_Player, From 548273c8f7de2eaee34a5ed61cfefb72baf77971 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Wed, 25 Dec 2013 16:07:52 +0000 Subject: [PATCH 079/118] Preliminary mobile entity saving * Fixes #252 * Alleviates #380 + Adds mob saving * Fixed some debug !ASSERTs --- src/Mobs/Monster.cpp | 3 +- src/WorldStorage/NBTChunkSerializer.cpp | 138 +++++- src/WorldStorage/WSSAnvil.cpp | 628 +++++++++++++++++++++++- src/WorldStorage/WSSAnvil.h | 34 +- 4 files changed, 790 insertions(+), 13 deletions(-) diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 563eec7bb..5a8ef0eb2 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -642,9 +642,10 @@ cMonster::eFamily cMonster::FamilyFromType(eType a_Type) case mtEnderman: return mfHostile; case mtGhast: return mfHostile; case mtHorse: return mfPassive; + case mtIronGolem: return mfPassive; case mtMagmaCube: return mfHostile; case mtMooshroom: return mfHostile; - case mtOcelot: return mfHostile; + case mtOcelot: return mfPassive; case mtPig: return mfPassive; case mtSheep: return mfPassive; case mtSilverfish: return mfHostile; diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 5c87c2679..e5043de1f 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -5,6 +5,10 @@ #include "Globals.h" #include "NBTChunkSerializer.h" #include "../BlockID.h" +#include "../ItemGrid.h" +#include "../StringCompression.h" +#include "FastNBT.h" + #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DispenserEntity.h" #include "../BlockEntities/DropperEntity.h" @@ -13,17 +17,27 @@ #include "../BlockEntities/JukeboxEntity.h" #include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/SignEntity.h" -#include "../ItemGrid.h" -#include "../StringCompression.h" + #include "../Entities/Entity.h" -#include "FastNBT.h" #include "../Entities/FallingBlock.h" #include "../Entities/Boat.h" #include "../Entities/Minecart.h" -#include "../Mobs/Monster.h" #include "../Entities/Pickup.h" #include "../Entities/ProjectileEntity.h" +#include "../Mobs/Monster.h" +#include "../Mobs/Bat.h" +#include "../Mobs/Creeper.h" +#include "../Mobs/Enderman.h" +#include "../Mobs/Horse.h" +#include "../Mobs/Magmacube.h" +#include "../Mobs/Sheep.h" +#include "../Mobs/Slime.h" +#include "../Mobs/Skeleton.h" +#include "../Mobs/Villager.h" +#include "../Mobs/Wolf.h" +#include "../Mobs/Zombie.h" + @@ -322,7 +336,120 @@ void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart) void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) { - // TODO + const char * EntityClass = NULL; + switch (a_Monster->GetMobType()) + { + case cMonster::mtBat: EntityClass = "Bat"; break; + case cMonster::mtBlaze: EntityClass = "Blaze"; break; + case cMonster::mtCaveSpider: EntityClass = "CaveSpider"; break; + case cMonster::mtChicken: EntityClass = "Chicken"; break; + case cMonster::mtCow: EntityClass = "Cow"; break; + case cMonster::mtCreeper: EntityClass = "Creeper"; break; + case cMonster::mtEnderDragon: EntityClass = "EnderDragon"; break; + case cMonster::mtEnderman: EntityClass = "Enderman"; break; + case cMonster::mtGhast: EntityClass = "Ghast"; break; + case cMonster::mtGiant: EntityClass = "Giant"; break; + case cMonster::mtHorse: EntityClass = "Horse"; break; + case cMonster::mtIronGolem: EntityClass = "VillagerGolem"; break; + case cMonster::mtMagmaCube: EntityClass = "LavaSlime"; break; + case cMonster::mtMooshroom: EntityClass = "MushroomCow"; break; + case cMonster::mtOcelot: EntityClass = "Ozelot"; break; + case cMonster::mtPig: EntityClass = "Pig"; break; + case cMonster::mtSheep: EntityClass = "Sheep"; break; + case cMonster::mtSilverfish: EntityClass = "Silverfish"; break; + case cMonster::mtSkeleton: EntityClass = "Skeleton"; break; + case cMonster::mtSlime: EntityClass = "Slime"; break; + case cMonster::mtSnowGolem: EntityClass = "SnowMan"; break; + case cMonster::mtSpider: EntityClass = "Spider"; break; + case cMonster::mtSquid: EntityClass = "Squid"; break; + case cMonster::mtVillager: EntityClass = "Villager"; break; + case cMonster::mtWitch: EntityClass = "Witch"; break; + case cMonster::mtWither: EntityClass = "Wither"; break; + case cMonster::mtWolf: EntityClass = "Wolf"; break; + case cMonster::mtZombie: EntityClass = "Zombie"; break; + case cMonster::mtZombiePigman: EntityClass = "PigZombie"; break; + default: + { + ASSERT(!"Unhandled monster type"); + return; + } + } // switch (payload) + + m_Writer.BeginCompound(""); + AddBasicEntity(a_Monster, EntityClass); + switch (a_Monster->GetMobType()) + { + case cMonster::mtBat: + { + m_Writer.AddByte("BatFlags", ((const cBat *)a_Monster)->IsHanging()); + break; + } + case cMonster::mtCreeper: + { + m_Writer.AddByte("powered", ((const cCreeper *)a_Monster)->IsCharged()); + m_Writer.AddByte("ignited", ((const cCreeper *)a_Monster)->IsBlowing()); + break; + } + case cMonster::mtEnderman: + { + m_Writer.AddShort("carried", (Int16)((const cEnderman *)a_Monster)->GetCarriedBlock()); + m_Writer.AddShort("carriedData", (Int16)((const cEnderman *)a_Monster)->GetCarriedMeta()); + break; + } + case cMonster::mtHorse: + { + const cHorse & Horse = *((const cHorse *)a_Monster); + m_Writer.AddByte("ChestedHorse", Horse.IsChested()); + m_Writer.AddByte("EatingHaystack", Horse.IsEating()); + m_Writer.AddByte("Tame", Horse.IsTame()); + m_Writer.AddInt ("Type", Horse.GetHorseType()); + m_Writer.AddInt ("Color", Horse.GetHorseColor()); + m_Writer.AddInt ("Style", Horse.GetHorseStyle()); + m_Writer.AddInt ("ArmorType", Horse.GetHorseArmour()); + m_Writer.AddByte("Saddle", Horse.IsSaddled()); + break; + } + case cMonster::mtMagmaCube: + { + m_Writer.AddByte("Size", ((const cMagmaCube *)a_Monster)->GetSize()); + break; + } + case cMonster::mtSheep: + { + m_Writer.AddByte("Sheared", ((const cSheep *)a_Monster)->IsSheared()); + m_Writer.AddByte("Color", ((const cSheep *)a_Monster)->GetFurColor()); + break; + } + case cMonster::mtSlime: + { + m_Writer.AddInt("Size", ((const cSlime *)a_Monster)->GetSize()); + break; + } + case cMonster::mtSkeleton: + { + m_Writer.AddByte("SkeletonType", (((const cSkeleton *)a_Monster)->IsWither() ? 1 : 0)); + break; + } + case cMonster::mtVillager: + { + m_Writer.AddInt("Profession", ((const cVillager *)a_Monster)->GetVilType()); + break; + } + case cMonster::mtWolf: + { + // TODO: + // _X: CopyPasta error: m_Writer.AddInt("Profession", ((const cVillager *)a_Monster)->GetVilType()); + break; + } + case cMonster::mtZombie: + { + m_Writer.AddByte("IsVillager", (((const cZombie *)a_Monster)->IsVillagerZombie() ? 1 : 0)); + m_Writer.AddByte("IsBaby", (((const cZombie *)a_Monster)->IsBaby() ? 1 : 0)); + m_Writer.AddByte("IsConverting", (((const cZombie *)a_Monster)->IsConverting() ? 1 : 0)); + break; + } + } + m_Writer.EndCompound(); } @@ -479,6 +606,7 @@ void cNBTChunkSerializer::Entity(cEntity * a_Entity) case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break; case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break; case cEntity::etProjectile: AddProjectileEntity ((cProjectileEntity *)a_Entity); break; + case cEntity::etExpOrb: /* TODO */ break; case cEntity::etPlayer: return; // Players aren't saved into the world default: { diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index dd06f19fa..8605930b6 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -6,9 +6,14 @@ #include "Globals.h" #include "WSSAnvil.h" #include "NBTChunkSerializer.h" -#include "../World.h" +#include "FastNBT.h" #include "zlib/zlib.h" +#include "../World.h" #include "../BlockID.h" +#include "../Item.h" +#include "../ItemGrid.h" +#include "../StringCompression.h" + #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DispenserEntity.h" #include "../BlockEntities/DropperEntity.h" @@ -17,11 +22,11 @@ #include "../BlockEntities/JukeboxEntity.h" #include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/SignEntity.h" -#include "../Item.h" -#include "../ItemGrid.h" -#include "../StringCompression.h" -#include "FastNBT.h" + + #include "../Mobs/Monster.h" +#include "../Mobs/IncludeAllMonsters.h" + #include "../Entities/Boat.h" #include "../Entities/FallingBlock.h" #include "../Entities/Minecart.h" @@ -984,6 +989,122 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a { LoadThrownEnderpearlFromNBT(a_Entities, a_NBT, a_EntityTagIdx); } + else if (strncmp(a_IDTag, "Bat", a_IDTagLength) == 0) + { + LoadBatFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Blaze", a_IDTagLength) == 0) + { + LoadBlazeFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "CaveSpider", a_IDTagLength) == 0) + { + LoadCaveSpiderFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Chicken", a_IDTagLength) == 0) + { + LoadChickenFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Cow", a_IDTagLength) == 0) + { + LoadCowFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Creeper", a_IDTagLength) == 0) + { + LoadCreeperFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "EnderDragon", a_IDTagLength) == 0) + { + LoadEnderDragonFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Enderman", a_IDTagLength) == 0) + { + LoadEndermanFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Ghast", a_IDTagLength) == 0) + { + LoadGhastFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Giant", a_IDTagLength) == 0) + { + LoadGiantFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Horse", a_IDTagLength) == 0) + { + LoadHorseFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "VillagerGolem", a_IDTagLength) == 0) + { + LoadIronGolemFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "LavaSlime", a_IDTagLength) == 0) + { + LoadMagmaCubeFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "MushroomCow", a_IDTagLength) == 0) + { + LoadMooshroomFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Ozelot", a_IDTagLength) == 0) + { + LoadOcelotFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Pig", a_IDTagLength) == 0) + { + LoadPigFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Sheep", a_IDTagLength) == 0) + { + LoadSheepFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Silverfish", a_IDTagLength) == 0) + { + LoadSilverfishFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Skeleton", a_IDTagLength) == 0) + { + LoadSkeletonFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Slime", a_IDTagLength) == 0) + { + LoadSlimeFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "SnowMan", a_IDTagLength) == 0) + { + LoadSnowGolemFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Spider", a_IDTagLength) == 0) + { + LoadSpiderFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Squid", a_IDTagLength) == 0) + { + LoadSquidFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Villager", a_IDTagLength) == 0) + { + LoadVillagerFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Witch", a_IDTagLength) == 0) + { + LoadWitchFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Wither", a_IDTagLength) == 0) + { + LoadWitherFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Wolf", a_IDTagLength) == 0) + { + LoadWolfFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Zombie", a_IDTagLength) == 0) + { + LoadZombieFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "PigZombie", a_IDTagLength) == 0) + { + LoadPigZombieFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } // TODO: other entities } @@ -1007,7 +1128,20 @@ void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - // TODO + int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "TileID"); + int MetaIdx = a_NBT.FindChildByName(a_TagIdx, "Data"); + + if ((TypeIdx < 0) || (MetaIdx < 0)) { return; } + + int Type = a_NBT.GetInt(TypeIdx); + NIBBLETYPE Meta = (NIBBLETYPE)a_NBT.GetByte(MetaIdx); + + std::auto_ptr FallingBlock(new cFallingBlock(Vector3i(0, 0, 0), Type, Meta)); + if (!LoadEntityBaseFromNBT(*FallingBlock.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(FallingBlock.release()); } @@ -1254,6 +1388,488 @@ void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cPar +void cWSSAnvil::LoadBatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cBat()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadBlazeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cBlaze()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadCaveSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cCavespider()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadChickenFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cChicken()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadCowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cCow()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadCreeperFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cCreeper()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadEnderDragonFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cEnderDragon()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadEndermanFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cEnderman()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadGhastFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cGhast()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cGiant()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Type"); + int ColorIdx = a_NBT.FindChildByName(a_TagIdx, "Color"); + int StyleIdx = a_NBT.FindChildByName(a_TagIdx, "Style"); + + if ((TypeIdx < 0) || (ColorIdx < 0) || (StyleIdx < 0)) { return; } + + int Type = a_NBT.GetInt(TypeIdx); + int Color = a_NBT.GetInt(ColorIdx); + int Style = a_NBT.GetInt(StyleIdx); + + std::auto_ptr Monster(new cHorse(Type, Color, Style, 1)); + + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadIronGolemFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cIronGolem()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadMagmaCubeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int SizeIdx = a_NBT.FindChildByName(a_TagIdx, "Size"); + + if (SizeIdx < 0) { return; } + + int Size = a_NBT.GetInt(SizeIdx); + + std::auto_ptr Monster(new cMagmaCube(Size)); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadMooshroomFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cMooshroom()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cOcelot()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cPig()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int ColorIdx = a_NBT.FindChildByName(a_TagIdx, "Color"); + + if (ColorIdx < 0) { return; } + + int Color = (int)a_NBT.GetByte(ColorIdx); + + std::auto_ptr Monster(new cSheep(Color)); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cSilverfish()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadSkeletonFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "SkeletonType"); + + if (TypeIdx < 0) { return; } + + bool Type = ((a_NBT.GetByte(TypeIdx) == 1) ? true : false); + + std::auto_ptr Monster(new cSkeleton(Type)); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadSlimeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int SizeIdx = a_NBT.FindChildByName(a_TagIdx, "Size"); + + if (SizeIdx < 0) { return; } + + int Size = a_NBT.GetInt(SizeIdx); + + std::auto_ptr Monster(new cSlime(Size)); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadSnowGolemFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cSnowGolem()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cSpider()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadSquidFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cSquid()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Profession"); + + if (TypeIdx < 0) { return; } + + int Type = a_NBT.GetInt(TypeIdx); + + std::auto_ptr Monster(new cVillager(cVillager::eVillagerType(Type))); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadWitchFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cWitch()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cWither()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cWolf()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int IsVillagerIdx = a_NBT.FindChildByName(a_TagIdx, "IsVillager"); + + if (IsVillagerIdx < 0) { return; } + + bool IsVillagerZombie = ((a_NBT.GetByte(IsVillagerIdx) == 1) ? true : false); + + std::auto_ptr Monster(new cZombie(IsVillagerZombie)); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + +void cWSSAnvil::LoadPigZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr Monster(new cZombiePigman()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx) { double Pos[3]; diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 7685d2236..0a7406267 100644 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -142,18 +142,50 @@ protected: void LoadBoatFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadFallingBlockFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMinecartFFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMinecartTFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMinecartHFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadArrowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadSnowballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadEggFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadFireballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadFireChargeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + + void LoadBatFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadBlazeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadCaveSpiderFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadChickenFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadCowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadCreeperFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadEnderDragonFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadEndermanFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadGhastFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadGiantFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadHorseFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadIronGolemFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMagmaCubeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMooshroomFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadOcelotFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadPigFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSheepFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSilverfishFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSkeletonFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSlimeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSnowGolemFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSpiderFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSquidFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadVillagerFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadWitchFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadWitherFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadWolfFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadZombieFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadPigZombieFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); /// Loads entity common data from the NBT compound; returns true if successful bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx); From 17a84111ceb1c1d28bc420eeae9262bc10b869b9 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Dec 2013 17:26:17 +0100 Subject: [PATCH 080/118] You are now able to sweep mobs to your position using fishing rods. --- src/Entities/Floater.cpp | 143 ++++++++++++++++++++++++++++++++++--- src/Entities/Floater.h | 19 +++-- src/Items/ItemFishingRod.h | 90 ++++++++++++++++------- 3 files changed, 210 insertions(+), 42 deletions(-) diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index 5e3338968..ab0595149 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -1,6 +1,8 @@ #include "Globals.h" +#include "../BoundingBox.h" +#include "../Chunk.h" #include "Floater.h" #include "Player.h" #include "../ClientHandle.h" @@ -9,12 +11,103 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFloaterEntityCollisionCallback +class cFloaterEntityCollisionCallback : + public cEntityCallback +{ +public: + cFloaterEntityCollisionCallback(cFloater * a_Floater, const Vector3d & a_Pos, const Vector3d & a_NextPos) : + m_Floater(a_Floater), + m_Pos(a_Pos), + m_NextPos(a_NextPos), + m_MinCoeff(1), + m_HitEntity(NULL) + { + } + virtual bool Item(cEntity * a_Entity) override + { + if (!a_Entity->IsMob()) // Floaters can only pull mobs not other entities. + { + return false; + } + + cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); + + double LineCoeff; + char Face; + EntBox.Expand(m_Floater->GetWidth() / 2, m_Floater->GetHeight() / 2, m_Floater->GetWidth() / 2); + if (!EntBox.CalcLineIntersection(m_Pos, m_NextPos, LineCoeff, Face)) + { + // No intersection whatsoever + return false; + } + + if (LineCoeff < m_MinCoeff) + { + // The entity is closer than anything we've stored so far, replace it as the potential victim + m_MinCoeff = LineCoeff; + m_HitEntity = a_Entity; + } + + // Don't break the enumeration, we want all the entities + return false; + } + + /// Returns the nearest entity that was hit, after the enumeration has been completed + cEntity * GetHitEntity(void) const { return m_HitEntity; } + + /// Returns true if the callback has encountered a true hit + bool HasHit(void) const { return (m_MinCoeff < 1); } + +protected: + cFloater * m_Floater; + const Vector3d & m_Pos; + const Vector3d & m_NextPos; + double m_MinCoeff; // The coefficient of the nearest hit on the Pos line + + // Although it's bad(tm) to store entity ptrs from a callback, we can afford it here, because the entire callback + // is processed inside the tick thread, so the entities won't be removed in between the calls and the final processing + cEntity * m_HitEntity; // The nearest hit entity +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFloaterCheckEntityExist +class cFloaterCheckEntityExist : + public cEntityCallback +{ +public: + cFloaterCheckEntityExist(void) : + m_EntityExists(false) + { + } + + bool Item(cEntity * a_Entity) override + { + m_EntityExists = true; + return false; + } + + bool DoesExist(void) const { return m_EntityExists; } +protected: + bool m_EntityExists; +} ; + + + + + cFloater::cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID, int a_CountDownTime) : - cEntity(etFloater, a_X, a_Y, a_Z, 0.98, 0.98), + cEntity(etFloater, a_X, a_Y, a_Z, 0.2, 0.2), m_PickupCountDown(0), m_PlayerID(a_PlayerID), m_CanPickupItem(false), - m_CountDownTime(a_CountDownTime) + m_CountDownTime(a_CountDownTime), + m_AttachedMobID(-1) { SetSpeed(a_Speed); } @@ -37,7 +130,7 @@ void cFloater::Tick(float a_Dt, cChunk & a_Chunk) HandlePhysics(a_Dt, a_Chunk); if (IsBlockWater(m_World->GetBlock((int) GetPosX(), (int) GetPosY(), (int) GetPosZ())) && m_World->GetBlockMeta((int) GetPosX(), (int) GetPosY(), (int) GetPosZ()) == 0) { - if (!m_CanPickupItem) + if (!m_CanPickupItem && m_AttachedMobID == -1) // Check if you can't already pickup a fish and if the floater isn't attached to a mob. { if (m_CountDownTime <= 0) { @@ -78,9 +171,8 @@ void cFloater::Tick(float a_Dt, cChunk & a_Chunk) } SetSpeedY(0.7); } - SetSpeedX(GetSpeedX() * 0.95); - SetSpeedZ(GetSpeedZ() * 0.95); - if (CanPickup()) + + if (CanPickup()) // Make sure the floater "loses its fish" { m_PickupCountDown--; if (m_PickupCountDown == 0) @@ -89,9 +181,38 @@ void cFloater::Tick(float a_Dt, cChunk & a_Chunk) LOGD("The fish is gone. Floater %i can not pick an item up.", GetUniqueID()); } } + + if (GetSpeed().Length() > 4 && m_AttachedMobID == -1) + { + cFloaterEntityCollisionCallback Callback(this, GetPosition(), GetPosition() + GetSpeed() / 20); + + a_Chunk.ForEachEntity(Callback); + if (Callback.HasHit()) + { + AttachTo(Callback.GetHitEntity()); + Callback.GetHitEntity()->TakeDamage(*this); // TODO: the player attacked the mob not the floater. + m_AttachedMobID = Callback.GetHitEntity()->GetUniqueID(); + } + } + + cFloaterCheckEntityExist EntityCallback; + m_World->DoWithEntityByID(m_PlayerID, EntityCallback); + if (!EntityCallback.DoesExist()) // The owner doesn't exist anymore. Destroy the floater entity. + { + Destroy(true); + } + + if (m_AttachedMobID != -1) + { + m_World->DoWithEntityByID(m_AttachedMobID, EntityCallback); // The mob the floater was attached to doesn't exist anymore. + if (!EntityCallback.DoesExist()) + { + m_AttachedMobID = -1; + } + } + + SetSpeedX(GetSpeedX() * 0.95); + SetSpeedZ(GetSpeedZ() * 0.95); + BroadcastMovementUpdate(); -} - - - - +} \ No newline at end of file diff --git a/src/Entities/Floater.h b/src/Entities/Floater.h index 4db94986c..4bbe3f352 100644 --- a/src/Entities/Floater.h +++ b/src/Entities/Floater.h @@ -19,13 +19,22 @@ public: virtual void SpawnOn(cClientHandle & a_Client) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - bool CanPickup(void) const { return m_CanPickupItem; } + bool CanPickup(void) const { return m_CanPickupItem; } + int GetOwnerID(void) const { return m_PlayerID; } + int GetAttachedMobID(void) const { return m_AttachedMobID; } protected: - Vector3d m_Speed; + // Position Vector3d m_ParticlePos; - int m_PickupCountDown; - int m_PlayerID; - int m_CountDownTime; + + // Bool needed to check if you can get a fish. bool m_CanPickupItem; + + // Countdown times + int m_PickupCountDown; + int m_CountDownTime; + + // Entity IDs + int m_PlayerID; + int m_AttachedMobID; } ; \ No newline at end of file diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h index a427b1676..941ce3b71 100644 --- a/src/Items/ItemFishingRod.h +++ b/src/Items/ItemFishingRod.h @@ -15,6 +15,61 @@ +///////////////////////////////////////////////////////////////////////////////////// +// cFloaterCallback +class cFloaterCallback : + public cEntityCallback +{ +public: + cFloaterCallback(void) : + m_CanPickup(false), + m_AttachedMobID(-1) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + m_CanPickup = ((cFloater *)a_Entity)->CanPickup(); + m_Pos = Vector3d(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ()); + m_AttachedMobID = ((cFloater *)a_Entity)->GetAttachedMobID(); + a_Entity->Destroy(true); + return true; + } + + bool CanPickup(void) const { return m_CanPickup; } + bool IsAttached(void) const { return (m_AttachedMobID != -1); } + int GetAttachedMobID(void) const { return m_AttachedMobID; } + Vector3d GetPos(void) const { return m_Pos; } + +protected: + bool m_CanPickup; + int m_AttachedMobID; + Vector3d m_Pos; +} ; + +//////////////////////////////////////////////////////////////////////////// +// cSweepEntityCallback +class cSweepEntityCallback : + public cEntityCallback +{ +public: + cSweepEntityCallback(Vector3d a_PlayerPos) : + m_PlayerPos(a_PlayerPos) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + Vector3d Speed = m_PlayerPos - a_Entity->GetPosition(); + a_Entity->AddSpeed(Speed); + return true; + } + +protected: + Vector3d m_PlayerPos; +} ; + + class cItemFishingRodHandler : public cItemHandler @@ -36,33 +91,16 @@ public: if (a_Player->IsFishing()) { - class cFloaterCallback : - public cEntityCallback - { - public: - cFloaterCallback(void) : - m_CanPickup(false) - { - } - - bool CanPickup(void) const { return m_CanPickup; } - Vector3d GetPos(void) const { return m_Pos; } - - virtual bool Item(cEntity * a_Entity) override - { - m_CanPickup = ((cFloater *)a_Entity)->CanPickup(); - m_Pos = Vector3d(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ()); - a_Entity->Destroy(true); - return true; - } - protected: - bool m_CanPickup; - Vector3d m_Pos; - } Callbacks; - a_World->DoWithEntityByID(a_Player->GetFloaterID(), Callbacks); + cFloaterCallback FloaterInfo; + a_World->DoWithEntityByID(a_Player->GetFloaterID(), FloaterInfo); a_Player->SetIsFishing(false); - if (Callbacks.CanPickup()) + if (FloaterInfo.IsAttached()) + { + cSweepEntityCallback SweepEntity(a_Player->GetPosition()); + a_World->DoWithEntityByID(FloaterInfo.GetAttachedMobID(), SweepEntity); + } + else if (FloaterInfo.CanPickup()) { cItems Drops; int ItemCategory = a_World->GetTickRandomNumber(99); @@ -173,7 +211,7 @@ public: } - Vector3d FloaterPos = Callbacks.GetPos(); + Vector3d FloaterPos = FloaterInfo.GetPos(); Vector3d FlyDirection = a_Player->GetEyePosition() - FloaterPos; a_World->SpawnItemPickups(Drops, FloaterPos.x, FloaterPos.y, FloaterPos.z, FlyDirection.x, FlyDirection.y + 1, FlyDirection.z); } From 5eacf327b7a1fc84845e64ac8ffbc75ed5a4a77a Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Dec 2013 18:14:00 +0100 Subject: [PATCH 081/118] Fixed Parentheses. --- src/Entities/Floater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index ab0595149..dfe77f059 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -130,7 +130,7 @@ void cFloater::Tick(float a_Dt, cChunk & a_Chunk) HandlePhysics(a_Dt, a_Chunk); if (IsBlockWater(m_World->GetBlock((int) GetPosX(), (int) GetPosY(), (int) GetPosZ())) && m_World->GetBlockMeta((int) GetPosX(), (int) GetPosY(), (int) GetPosZ()) == 0) { - if (!m_CanPickupItem && m_AttachedMobID == -1) // Check if you can't already pickup a fish and if the floater isn't attached to a mob. + if ((!m_CanPickupItem) && (m_AttachedMobID == -1)) // Check if you can't already pickup a fish and if the floater isn't attached to a mob. { if (m_CountDownTime <= 0) { @@ -182,7 +182,7 @@ void cFloater::Tick(float a_Dt, cChunk & a_Chunk) } } - if (GetSpeed().Length() > 4 && m_AttachedMobID == -1) + if ((GetSpeed().Length() > 4) && (m_AttachedMobID == -1)) { cFloaterEntityCollisionCallback Callback(this, GetPosition(), GetPosition() + GetSpeed() / 20); From 4cee3d8fe354f1f99da04ca269f409af84e3e4f3 Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Wed, 25 Dec 2013 19:32:45 +0100 Subject: [PATCH 082/118] (Hopefully) fixed if conditions --- src/Blocks/BlockPumpkin.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 744f0ecde..008fb4fed 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -16,9 +16,7 @@ public: virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override { - virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override - { - if (!a_BlockY > 1) // Make sure server won't check for inexistent blocks (below y=0). + if (a_BlockY < 2) // Make sure server won't check for inexistent blocks (below y=0). { return; } @@ -53,7 +51,6 @@ public: } } } - } virtual bool GetPlacementBlockTypeMeta( cWorld * a_World, cPlayer * a_Player, From c2befd977727b8091d04db900ede2038e3f755d0 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 25 Dec 2013 19:38:52 +0100 Subject: [PATCH 083/118] Fixed block area height check. The block area couldn't be read up to chunk height. --- src/BlockArea.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp index a5309f995..03ac13207 100644 --- a/src/BlockArea.cpp +++ b/src/BlockArea.cpp @@ -301,10 +301,10 @@ bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_ LOGWARNING("%s: MaxBlockY less than zero, adjusting to zero", __FUNCTION__); a_MaxBlockY = 0; } - else if (a_MaxBlockY >= cChunkDef::Height) + else if (a_MaxBlockY > cChunkDef::Height) { LOGWARNING("%s: MaxBlockY more than chunk height, adjusting to chunk height", __FUNCTION__); - a_MaxBlockY = cChunkDef::Height - 1; + a_MaxBlockY = cChunkDef::Height; } // Allocate the needed memory: From 59b80c84fa3eecfa30641b1e42c10a6081cdd241 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Dec 2013 22:08:41 +0100 Subject: [PATCH 084/118] This generates gravel in Extreme Hills M biomes. --- src/Generating/DistortedHeightmap.cpp | 22 ++++++++++++++++++++-- src/Generating/DistortedHeightmap.h | 1 - 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp index 342a4483f..a2d1c084a 100644 --- a/src/Generating/DistortedHeightmap.cpp +++ b/src/Generating/DistortedHeightmap.cpp @@ -101,6 +101,13 @@ static cDistortedHeightmap::sBlockInfo tbMycelium[] = {E_BLOCK_DIRT, 0}, } ; +static cDistortedHeightmap::sBlockInfo tbGravel[] = +{ + {E_BLOCK_GRAVEL, 0}, + {E_BLOCK_DIRT, 0}, + {E_BLOCK_DIRT, 0}, + {E_BLOCK_DIRT, 0}, +} ; @@ -146,6 +153,7 @@ static cPattern patDirt (tbDirt, ARRAYCOUNT(tbDirt)); static cPattern patPodzol (tbPodzol, ARRAYCOUNT(tbPodzol)); static cPattern patGrassLess(tbGrassLess, ARRAYCOUNT(tbGrassLess)); static cPattern patMycelium (tbMycelium, ARRAYCOUNT(tbMycelium)); +static cPattern patGravel (tbGravel, ARRAYCOUNT(tbGravel)); static cPattern patOFSand (tbOFSand, ARRAYCOUNT(tbOFSand)); static cPattern patOFClay (tbOFClay, ARRAYCOUNT(tbOFClay)); @@ -675,7 +683,6 @@ void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, in case biSavanna: case biSavannaPlateau: case biSunflowerPlains: - case biExtremeHillsM: case biFlowerForest: case biTaigaM: case biSwamplandM: @@ -686,7 +693,6 @@ void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, in case biBirchForestHillsM: case biRoofedForestM: case biColdTaigaM: - case biExtremeHillsPlusM: case biSavannaM: case biSavannaPlateauM: { @@ -737,6 +743,18 @@ void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, in FillColumnMesa(a_ChunkDesc, a_RelX, a_RelZ); return; } + + case biExtremeHillsPlusM: + case biExtremeHillsM: + { + // Select the pattern to use - gravel or grass: + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX; + NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ; + NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY); + const sBlockInfo * Pattern = (Val < -0.1) ? patGravel.Get() : patGrass.Get(); + FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern); + return; + } default: ASSERT(!"Unhandled biome"); return; diff --git a/src/Generating/DistortedHeightmap.h b/src/Generating/DistortedHeightmap.h index e6b3c9d3f..8139a8b89 100644 --- a/src/Generating/DistortedHeightmap.h +++ b/src/Generating/DistortedHeightmap.h @@ -125,7 +125,6 @@ protected: /// Returns the pattern to use for an ocean floor in the specified column const sBlockInfo * ChooseOceanFloorPattern(int a_RelX, int a_RelZ); - // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; virtual void InitializeHeightGen(cIniFile & a_IniFile) override; From 2f59a93f2a09e7ae2dee93aa9925acb2d49841e1 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Dec 2013 22:10:27 +0100 Subject: [PATCH 085/118] Re-added empty line. --- src/Generating/DistortedHeightmap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Generating/DistortedHeightmap.h b/src/Generating/DistortedHeightmap.h index 8139a8b89..e6b3c9d3f 100644 --- a/src/Generating/DistortedHeightmap.h +++ b/src/Generating/DistortedHeightmap.h @@ -125,6 +125,7 @@ protected: /// Returns the pattern to use for an ocean floor in the specified column const sBlockInfo * ChooseOceanFloorPattern(int a_RelX, int a_RelZ); + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; virtual void InitializeHeightGen(cIniFile & a_IniFile) override; From caf3b6d70cc1735f511925660ca3ea24ff2ae1ac Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Dec 2013 22:43:26 +0100 Subject: [PATCH 086/118] Normal extreme hills (plus) now generate a stone/grass pattern. --- src/Generating/DistortedHeightmap.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp index a2d1c084a..4bb01c36f 100644 --- a/src/Generating/DistortedHeightmap.cpp +++ b/src/Generating/DistortedHeightmap.cpp @@ -109,6 +109,13 @@ static cDistortedHeightmap::sBlockInfo tbGravel[] = {E_BLOCK_DIRT, 0}, } ; +static cDistortedHeightmap::sBlockInfo tbStone[] = +{ + {E_BLOCK_STONE, 0}, + {E_BLOCK_STONE, 0}, + {E_BLOCK_STONE, 0}, + {E_BLOCK_STONE, 0}, +} ; @@ -154,6 +161,7 @@ static cPattern patPodzol (tbPodzol, ARRAYCOUNT(tbPodzol)); static cPattern patGrassLess(tbGrassLess, ARRAYCOUNT(tbGrassLess)); static cPattern patMycelium (tbMycelium, ARRAYCOUNT(tbMycelium)); static cPattern patGravel (tbGravel, ARRAYCOUNT(tbGravel)); +static cPattern patStone (tbStone, ARRAYCOUNT(tbStone)); static cPattern patOFSand (tbOFSand, ARRAYCOUNT(tbOFSand)); static cPattern patOFClay (tbOFClay, ARRAYCOUNT(tbOFClay)); @@ -656,7 +664,6 @@ void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, in { case biOcean: case biPlains: - case biExtremeHills: case biForest: case biTaiga: case biSwampland: @@ -679,7 +686,6 @@ void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, in case biRoofedForest: case biColdTaiga: case biColdTaigaHills: - case biExtremeHillsPlus: case biSavanna: case biSavannaPlateau: case biSunflowerPlains: @@ -744,6 +750,18 @@ void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, in return; } + case biExtremeHillsPlus: + case biExtremeHills: + { + // Select the pattern to use - gravel or grass: + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX; + NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ; + NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY); + const sBlockInfo * Pattern = (Val < -0.1) ? patStone.Get() : patGrass.Get(); + FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern); + return; + } + case biExtremeHillsPlusM: case biExtremeHillsM: { From e282eb73c89c2feb567de767c0f9f34896fe8f93 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Dec 2013 23:05:22 +0100 Subject: [PATCH 087/118] Changed tbGravel. Vanilla has 3 layers of gravel and then stone. --- src/Generating/DistortedHeightmap.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp index 4bb01c36f..b6f3866e4 100644 --- a/src/Generating/DistortedHeightmap.cpp +++ b/src/Generating/DistortedHeightmap.cpp @@ -104,9 +104,9 @@ static cDistortedHeightmap::sBlockInfo tbMycelium[] = static cDistortedHeightmap::sBlockInfo tbGravel[] = { {E_BLOCK_GRAVEL, 0}, - {E_BLOCK_DIRT, 0}, - {E_BLOCK_DIRT, 0}, - {E_BLOCK_DIRT, 0}, + {E_BLOCK_GRAVEL, 0}, + {E_BLOCK_GRAVEL, 0}, + {E_BLOCK_STONE, 0}, } ; static cDistortedHeightmap::sBlockInfo tbStone[] = From b767fd784c1ccc563f3e11cefa0eb825732cd284 Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Wed, 25 Dec 2013 23:18:33 +0100 Subject: [PATCH 088/118] Extreme Hills M biomes now generate gravel, stone and grass patterns. --- src/Generating/DistortedHeightmap.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp index b6f3866e4..0a4a0940a 100644 --- a/src/Generating/DistortedHeightmap.cpp +++ b/src/Generating/DistortedHeightmap.cpp @@ -111,7 +111,7 @@ static cDistortedHeightmap::sBlockInfo tbGravel[] = static cDistortedHeightmap::sBlockInfo tbStone[] = { - {E_BLOCK_STONE, 0}, + {E_BLOCK_STONE, 0}, {E_BLOCK_STONE, 0}, {E_BLOCK_STONE, 0}, {E_BLOCK_STONE, 0}, @@ -769,7 +769,15 @@ void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, in NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX; NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ; NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY); - const sBlockInfo * Pattern = (Val < -0.1) ? patGravel.Get() : patGrass.Get(); + const sBlockInfo * Pattern; + if (Val <= 0.0) + { + Pattern = (Val < -0.3) ? patGravel.Get() : patGrass.Get(); + } + else + { + Pattern = (Val < 0.3) ? patStone.Get() : patGrass.Get(); + } FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern); return; } From d0572e73a06a4f2a6f461a4a1d0b0ae3ff74af3d Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Thu, 26 Dec 2013 00:12:06 +0000 Subject: [PATCH 089/118] Moved increment operator to back of variables --- src/Simulator/RedstoneSimulator.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp index b78e6582d..f65908729 100644 --- a/src/Simulator/RedstoneSimulator.cpp +++ b/src/Simulator/RedstoneSimulator.cpp @@ -119,7 +119,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c if (!a_Chunk->IsLightValid()) { m_World.QueueLightChunk(a_ChunkX, a_ChunkZ); - itr++; + ++itr; continue; } else @@ -134,7 +134,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c } else { - itr++; + ++itr; continue; } } @@ -147,7 +147,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c } else { - itr++; + ++itr; } } @@ -192,7 +192,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c } else { - itr++; + ++itr; } } @@ -213,7 +213,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c } else { - itr++; + ++itr; } } @@ -234,7 +234,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c continue; } - itr++; + ++itr; } for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(), end = ChunkData.end(); dataitr != end;) @@ -316,7 +316,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c } } - dataitr++; + ++dataitr; } } @@ -630,7 +630,7 @@ void cRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int QueueRepeaterPowerChange(a_BlockX, a_BlockY, a_BlockZ, a_Meta, 0, false); } - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); itr++) + for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { @@ -1446,7 +1446,7 @@ void cRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_BlockX, int a void cRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, short a_ElapsedTicks, bool ShouldPowerOn) { - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); itr++) + for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) { if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { From 5689c001319149a200740969b16688d271b6c749 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 26 Dec 2013 10:56:05 +0100 Subject: [PATCH 090/118] cBlockArea: Fixed writing full-height areas. --- src/BlockArea.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp index 03ac13207..1148908c6 100644 --- a/src/BlockArea.cpp +++ b/src/BlockArea.cpp @@ -349,10 +349,10 @@ bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__); a_MinBlockY = 0; } - else if (a_MinBlockY >= cChunkDef::Height - m_SizeY) + else if (a_MinBlockY > cChunkDef::Height - m_SizeY) { LOGWARNING("%s: MinBlockY + m_SizeY more than chunk height, adjusting to chunk height", __FUNCTION__); - a_MinBlockY = cChunkDef::Height - m_SizeY - 1; + a_MinBlockY = cChunkDef::Height - m_SizeY; } return a_World->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); From 6884d4235ea81f64d79ee24c7aae7c91694ed47a Mon Sep 17 00:00:00 2001 From: STRWarrior Date: Thu, 26 Dec 2013 14:37:48 +0100 Subject: [PATCH 091/118] Simplefied Extreme Hills M pattern select. --- src/Generating/DistortedHeightmap.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp index 0a4a0940a..15e352e30 100644 --- a/src/Generating/DistortedHeightmap.cpp +++ b/src/Generating/DistortedHeightmap.cpp @@ -753,7 +753,7 @@ void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, in case biExtremeHillsPlus: case biExtremeHills: { - // Select the pattern to use - gravel or grass: + // Select the pattern to use - stone or grass: NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX; NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ; NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY); @@ -765,19 +765,11 @@ void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, in case biExtremeHillsPlusM: case biExtremeHillsM: { - // Select the pattern to use - gravel or grass: + // Select the pattern to use - gravel, stone or grass: NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX; NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ; NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY); - const sBlockInfo * Pattern; - if (Val <= 0.0) - { - Pattern = (Val < -0.3) ? patGravel.Get() : patGrass.Get(); - } - else - { - Pattern = (Val < 0.3) ? patStone.Get() : patGrass.Get(); - } + const sBlockInfo * Pattern = (Val < -0.9) ? patStone.Get() : ((Val > 0) ? patGravel.Get() : patGrass.Get()); FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern); return; } From fb46ad5c75f8536db6f47f3ac4f099ca6604695f Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 26 Dec 2013 14:53:03 +0100 Subject: [PATCH 092/118] Added cmake output folders (VC2008) to gitignore. --- .gitignore | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.gitignore b/.gitignore index 7eb0a218a..4367fea56 100644 --- a/.gitignore +++ b/.gitignore @@ -57,7 +57,44 @@ MCServer.dir/ #win32 cmake stuff *.vcxproj +*.vcproj *.vcxproj.filters *.opensdf *.sdf *.sln + +#cmake output folders +ZERO_CHECK.dir/ +lib/cryptopp/Debug/ +lib/cryptopp/Release/ +lib/cryptopp/cryptopp.dir/ +lib/expat/Debug/ +lib/expat/Release/ +lib/expat/expat.dir/ +lib/inifile/Debug/ +lib/inifile/Release/ +lib/inifile/inifile.dir/ +lib/jsoncpp/Debug/ +lib/jsoncpp/Release/ +lib/jsoncpp/jsoncpp.dir/ +lib/lua/Debug/ +lib/lua/Release/ +lib/lua/lua.dir/ +lib/luaexpat/Debug/ +lib/luaexpat/Release/ +lib/luaexpat/luaexpat.dir/ +lib/md5/Debug/ +lib/md5/Release/ +lib/md5/md5.dir/ +lib/sqlite/Debug/ +lib/sqlite/Release/ +lib/sqlite/sqlite.dir/ +lib/tolua++/Debug/ +lib/tolua++/Release/ +lib/tolua++/tolua.dir/ +lib/tolua++/tolualib.dir/ +lib/zlib/Debug/ +lib/zlib/Release/ +lib/zlib/zlib.dir/ +src/Debug/ +src/Release/ From 2560fb40c60268b1fd4f48e82301cc6aed3f90f1 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 26 Dec 2013 14:54:46 +0100 Subject: [PATCH 093/118] Added proper precompiled headers for MSVC. --- src/CMakeLists.txt | 95 +++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 00c3059b5..107ca60dd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,72 +12,71 @@ include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/jsoncpp/include") set(FOLDERS OSSupport HTTPServer Bindings Items Blocks Protocol Generating) set(FOLDERS ${FOLDERS} WorldStorage Mobs Entities Simulator UI BlockEntities) -if(NOT WIN32) -foreach(folder ${FOLDERS}) - add_subdirectory(${folder}) -endforeach(folder) -file(GLOB SOURCE - "*.cpp" -) -else() +if (NOT WIN32) + foreach(folder ${FOLDERS}) + add_subdirectory(${folder}) + endforeach(folder) -function(includefolder PATH) - FILE(GLOB FOLDER_FILES - "${PATH}/*.cpp" - "${PATH}/*.h" + file(GLOB SOURCE + "*.cpp" ) - source_group("${PATH}" FILES ${FOLDER_FILES}) -endfunction(includefolder) + list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/StackWalker.cpp" "${PROJECT_SOURCE_DIR}/LeakFinder.cpp") -foreach(folder ${FOLDERS}) - includefolder(${folder}) -endforeach(folder) +else () -file(GLOB_RECURSE SOURCE - "*.cpp" - "*.h" -) + function(includefolder PATH) + FILE(GLOB FOLDER_FILES + "${PATH}/*.cpp" + "${PATH}/*.h" + ) + source_group("${PATH}" FILES ${FOLDER_FILES}) + endfunction(includefolder) -include_directories("${PROJECT_SOURCE_DIR}") + foreach(folder ${FOLDERS}) + includefolder(${folder}) + endforeach(folder) -source_group("" FILES ${SOURCE}) + file(GLOB_RECURSE SOURCE + "*.cpp" + "*.h" + ) -#precompiledheaders + include_directories("${PROJECT_SOURCE_DIR}") -file(GLOB_RECURSE HEADERS - "*.h" -) - -foreach(header ${HEADERS}) - set(FLAGS "/Yu ${header} /Yc ${header}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}") - set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_PROFILE} ${FLAGS}") - set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_PROFILE} ${FLAGS}") -endforeach() + source_group("" FILES ${SOURCE}) + # Precompiled headers (1st part) + SET(PrecompiledBinary "$(IntDir)/Globals.pch") + SET_SOURCE_FILES_PROPERTIES( + Globals.cpp PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\" /Fp\"${PrecompiledBinary}\"" + ) endif() -list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/StackWalker.cpp" "${PROJECT_SOURCE_DIR}/LeakFinder.cpp") -if(UNIX) +if (UNIX) set(EXECUTABLE ../MCServer/MCServer) -else() +else () set(EXECUTABLE MCServer) -endif() +endif () + add_executable(${EXECUTABLE} ${SOURCE}) -if(NOT WIN32) -target_link_libraries(${EXECUTABLE} OSSupport HTTPServer Bindings Items Blocks) -target_link_libraries(${EXECUTABLE} Protocol Generating WorldStorage) -target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities) -endif() + +if (WIN32) + SET_TARGET_PROPERTIES( + ${EXECUTABLE} PROPERTIES COMPILE_FLAGS "/Yu\"Globals.h\" /FI\"${PrecompiledBinary}\" /Fp\"${PrecompiledBinary}\"" + OBJECT_DEPENDS "${PrecompiledBinary}" + ) +endif () + + +if (NOT WIN32) + target_link_libraries(${EXECUTABLE} OSSupport HTTPServer Bindings Items Blocks) + target_link_libraries(${EXECUTABLE} Protocol Generating WorldStorage) + target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities) +endif () target_link_libraries(${EXECUTABLE} md5 luaexpat iniFile jsoncpp cryptopp zlib lua) From f1142af455eea674c07ab426fe5ef7d4302b16c9 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Thu, 26 Dec 2013 14:55:19 +0000 Subject: [PATCH 094/118] Server now handles death messages --- src/Entities/Player.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 0fa8254ce..67d5a47ef 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -820,6 +820,22 @@ void cPlayer::KilledBy(cEntity * a_Killer) m_Inventory.Clear(); m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); SaveToDisk(); // Save it, yeah the world is a tough place ! + + if (a_Killer == NULL) + { + GetWorld()->BroadcastChat(Printf("%s[DEATH] %s%s was killed by environmental damage", cChatColor::Red.c_str(), cChatColor::White.c_str(), GetName().c_str())); + } + else if (a_Killer->IsPlayer()) + { + GetWorld()->BroadcastChat(Printf("%s[DEATH] %s%s was killed by %s", cChatColor::Red.c_str(), cChatColor::White.c_str(), GetName().c_str(), ((cPlayer *)a_Killer)->GetName().c_str())); + } + else + { + AString KillerClass = a_Killer->GetClass(); + KillerClass.erase(KillerClass.begin()); // Erase the 'c' of the class (e.g. "cWitch" -> "Witch") + + GetWorld()->BroadcastChat(Printf("%s[DEATH] %s%s was killed by a %s", cChatColor::Red.c_str(), cChatColor::White.c_str(), GetName().c_str(), KillerClass.c_str())); + } } From d41f724a4034724f0c1da72cad15bd0a274ec62d Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Thu, 26 Dec 2013 15:11:48 +0000 Subject: [PATCH 095/118] Writing a plugin APIDump article (#382) --- MCServer/Plugins/APIDump/WebWorldThreads.html | 100 +++---- .../APIDump/Writing-a-MCServer-plugin.html | 253 ++++++++++++++++++ MCServer/Plugins/APIDump/main.css | 6 + 3 files changed, 310 insertions(+), 49 deletions(-) create mode 100644 MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html diff --git a/MCServer/Plugins/APIDump/WebWorldThreads.html b/MCServer/Plugins/APIDump/WebWorldThreads.html index 2f117ab7c..6caf40e6d 100644 --- a/MCServer/Plugins/APIDump/WebWorldThreads.html +++ b/MCServer/Plugins/APIDump/WebWorldThreads.html @@ -8,62 +8,64 @@ -

Webserver vs World threads

-

- This article will explain the threading issues that arise between the webserver and world threads are of concern to plugin authors.

-

- Generally, plugins that provide webadmin pages should be quite careful about their interactions. Most operations on MCServer objects requires synchronization, that MCServer provides automatically and transparently to plugins - when a block is written, the chunkmap is locked, or when an entity is being manipulated, the entity list is locked. Each plugin also has a mutex lock, so that only one thread at a time may be executing plugin code.

-

- This locking can be a source of deadlocks for plugins that are not written carefully.

+
+

Webserver vs World threads

+

+ This article will explain the threading issues that arise between the webserver and world threads are of concern to plugin authors.

+

+ Generally, plugins that provide webadmin pages should be quite careful about their interactions. Most operations on MCServer objects requires synchronization, that MCServer provides automatically and transparently to plugins - when a block is written, the chunkmap is locked, or when an entity is being manipulated, the entity list is locked. Each plugin also has a mutex lock, so that only one thread at a time may be executing plugin code.

+

+ This locking can be a source of deadlocks for plugins that are not written carefully.

-

Example scenario

-

Consider the following example. A plugin provides a webadmin page that allows the admin to kick players off the server. When the admin presses the "Kick" button, the plugin calls cWorld:DoWithPlayer() with a callback to kick the player. Everything seems to be working fine now.

-

- A new feature is developed in the plugin, now the plugin adds a new in-game command so that the admins can kick players while they're playing the game. The plugin registers a command callback with cPluginManager.AddCommand(). Now there are problems bound to happen.

-

- Suppose that two admins are in, one is using the webadmin and the other is in-game. Both try to kick a player at the same time. The webadmin locks the plugin, so that it can execute the plugin code, but right at this moment the OS switches threads. The world thread locks the world so that it can access the list of in-game commands, receives the in-game command, it tries to lock the plugin. The plugin is already locked, so the world thread is put on hold. After a while, the webadmin thread is woken up again and continues processing. It tries to lock the world so that it can traverse the playerlist, but the lock is already held by the world thread. Now both threads are holding one lock each and trying to grab the other lock, and are therefore deadlocked.

+

Example scenario

+

Consider the following example. A plugin provides a webadmin page that allows the admin to kick players off the server. When the admin presses the "Kick" button, the plugin calls cWorld:DoWithPlayer() with a callback to kick the player. Everything seems to be working fine now.

+

+ A new feature is developed in the plugin, now the plugin adds a new in-game command so that the admins can kick players while they're playing the game. The plugin registers a command callback with cPluginManager.AddCommand(). Now there are problems bound to happen.

+

+ Suppose that two admins are in, one is using the webadmin and the other is in-game. Both try to kick a player at the same time. The webadmin locks the plugin, so that it can execute the plugin code, but right at this moment the OS switches threads. The world thread locks the world so that it can access the list of in-game commands, receives the in-game command, it tries to lock the plugin. The plugin is already locked, so the world thread is put on hold. After a while, the webadmin thread is woken up again and continues processing. It tries to lock the world so that it can traverse the playerlist, but the lock is already held by the world thread. Now both threads are holding one lock each and trying to grab the other lock, and are therefore deadlocked.

-

How to avoid the deadlock

-

- There are two main ways to avoid such a deadlock. The first approach is using tasks: Everytime you need to execute a task inside a world, instead of executing it, queue it, using cWorld:QueueTask(). This handy utility can will call the given function inside the world's TickThread, thus eliminating the deadlock, because now there's only one thread. However, this approach will not let you get data back. You cannot query the player list, or the entities, or anything - because when the task runs, the webadmin page has already been served to the browser.

-

- To accommodate this, you'll need to use the second approach - preparing and caching data in the tick thread, possibly using callbacks. This means that the plugin will have global variables that will store the data, and update those variables when the data changes; then the webserver thread will only read those variables, instead of calling the world functions. For example, if a webpage was to display the list of currently connected players, the plugin should maintain a global variable, g_WorldPlayers, which would be a table of worlds, each item being a list of currently connected players. The webadmin handler would read this variable and create the page from it; the plugin would use HOOK_PLAYER_JOINED and HOOK_DISCONNECT to update the variable.

+

How to avoid the deadlock

+

+ There are two main ways to avoid such a deadlock. The first approach is using tasks: Everytime you need to execute a task inside a world, instead of executing it, queue it, using cWorld:QueueTask(). This handy utility can will call the given function inside the world's TickThread, thus eliminating the deadlock, because now there's only one thread. However, this approach will not let you get data back. You cannot query the player list, or the entities, or anything - because when the task runs, the webadmin page has already been served to the browser.

+

+ To accommodate this, you'll need to use the second approach - preparing and caching data in the tick thread, possibly using callbacks. This means that the plugin will have global variables that will store the data, and update those variables when the data changes; then the webserver thread will only read those variables, instead of calling the world functions. For example, if a webpage was to display the list of currently connected players, the plugin should maintain a global variable, g_WorldPlayers, which would be a table of worlds, each item being a list of currently connected players. The webadmin handler would read this variable and create the page from it; the plugin would use HOOK_PLAYER_JOINED and HOOK_DISCONNECT to update the variable.

-

What to avoid

-

- Now that we know what the danger is and how to avoid it, how do we know if our code is susceptible?

-

- The general rule of thumb is to avoid calling any functions that read or write lists of things in the webserver thread. This means most ForEach() and DoWith() functions. Only cRoot:ForEachWorld() is safe - because the list of worlds is not expected to change, so it is not guarded by a mutex. Getting and setting world's blocks is, naturally, unsafe, as is calling other plugins, or creating entities.

+

What to avoid

+

+ Now that we know what the danger is and how to avoid it, how do we know if our code is susceptible?

+

+ The general rule of thumb is to avoid calling any functions that read or write lists of things in the webserver thread. This means most ForEach() and DoWith() functions. Only cRoot:ForEachWorld() is safe - because the list of worlds is not expected to change, so it is not guarded by a mutex. Getting and setting world's blocks is, naturally, unsafe, as is calling other plugins, or creating entities.

-

Example

- The Core has the facility to kick players using the web interface. It used the following code for the kicking (inside the webadmin handler): -
-		local KickPlayerName = Request.Params["players-kick"]
-		local FoundPlayerCallback = function(Player)
-		  if (Player:GetName() == KickPlayerName) then
-			Player:GetClientHandle():Kick("You were kicked from the game!")
-		  end
-		end
-		cRoot:Get():FindAndDoWithPlayer(KickPlayerName, FoundPlayerCallback)
-		
- The cRoot:FindAndDoWithPlayer() is unsafe and could have caused a deadlock. The new solution is queue a task; but since we don't know in which world the player is, we need to queue the task to all worlds: -
-		cRoot:Get():ForEachWorld(    -- For each world...
-		  function(World)
-			World:QueueTask(         -- ... queue a task...
-			  function(a_World)
-				a_World:DoWithPlayer(KickPlayerName,  -- ... to walk the playerlist...
-				  function (a_Player)
-					a_Player:GetClientHandle():Kick("You were kicked from the game!")  -- ... and kick the player
+			

Example

+ The Core has the facility to kick players using the web interface. It used the following code for the kicking (inside the webadmin handler): +
+			local KickPlayerName = Request.Params["players-kick"]
+			local FoundPlayerCallback = function(Player)
+			  if (Player:GetName() == KickPlayerName) then
+				Player:GetClientHandle():Kick("You were kicked from the game!")
+			  end
+			end
+			cRoot:Get():FindAndDoWithPlayer(KickPlayerName, FoundPlayerCallback)
+			
+ The cRoot:FindAndDoWithPlayer() is unsafe and could have caused a deadlock. The new solution is queue a task; but since we don't know in which world the player is, we need to queue the task to all worlds: +
+			cRoot:Get():ForEachWorld(    -- For each world...
+			  function(World)
+				World:QueueTask(         -- ... queue a task...
+				  function(a_World)
+					a_World:DoWithPlayer(KickPlayerName,  -- ... to walk the playerlist...
+					  function (a_Player)
+						a_Player:GetClientHandle():Kick("You were kicked from the game!")  -- ... and kick the player
+					  end
+					)
 				  end
 				)
 			  end
 			)
-		  end
-		)
-		
- +
+ +
\ No newline at end of file diff --git a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html new file mode 100644 index 000000000..3ab997dcd --- /dev/null +++ b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html @@ -0,0 +1,253 @@ + + + + + + MCS Plugin Tutorial + + + + + + +
+

Writing a MCServer plugin

+

+ This article will explain how to write a basic plugin. It details basic requirements + for a plugin, explains how to register a hook and bind a command, and gives plugin + standards details. +

+

+ Let us begin. In order to begin development, we must firstly obtain a compiled copy + of MCServer, and make sure that the Core plugin is within the Plugins folder, and activated. + Core handles much of the MCServer end-user experience and is a necessary component of + plugin development, as necessary plugin components depend on sone of its functions. +

+

+ Next, we must obtain a copy of CoreMessaging.lua. This can be found + here. + This is used to provide messaging support that is compliant with MCServer standards. +

+

Creating the basic template

+

+ Plugins are written in Lua. Therefore, create a new Lua file. You can create as many files as you wish, with + any filename - MCServer bungs them all together at runtime, however, let us create a file called main.lua for now. + Format it like so: +

+
+			local PLUGIN
+			
+			function Initialize( Plugin )
+				Plugin:SetName( "DerpyPlugin" )
+				Plugin:SetVersion( 1 )
+				
+				PLUGIN = Plugin
+
+				-- Hooks
+		
+				local PluginManager = cPluginManager:Get()
+				-- Command bindings
+
+				LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() )
+				return true
+			end
+			
+			function OnDisable()
+				LOG(PLUGIN:GetName() .. " is shutting down...")
+			end
+			
+

+ Now for an explanation of the basics. +

    +
  • function Initialize is called on plugin startup. It is the place where the plugin is set up.
  • +
  • Plugin:SetName sets the name of the plugin.
  • +
  • Plugin:SetVersion sets the revision number of the plugin. This must be an integer.
  • +
  • LOG logs to console a message, in this case, it prints that the plugin was initialised.
  • +
  • The PLUGIN variable just stores this plugin's object, so GetName() can be called in OnDisable (as no Plugin parameter is passed there, contrary to Initialize).
  • +
  • function OnDisable is called when the plugin is disabled, commonly when the server is shutting down. Perform cleanup and logging here.
  • +
+ Be sure to return true for this function, else MCS thinks you plugin had failed to initialise and prints a stacktrace with an error message. +

+ +

Registering hooks

+

+ Hooks are things that MCServer calls when an internal event occurs. For example, a hook is fired when a player places a block, moves, + logs on, eats, and many other things. For a full list, see the API documentation. +

+

+ A hook can be either informative or overridable. In any case, returning false will not trigger a response, but returning true will cancel + the hook and prevent it from being propagated further to other plugins. An overridable hook simply means that there is visible behaviour + to a hook's cancellation, such as a chest being prevented from being opened. There are some exceptions to this where only changing the value the + hook passes has an effect, and not the actual return value, an example being the HOOK_KILLING hook. See the API docs for details. +

+

+ To register a hook, insert the following code template into the "-- Hooks" area in the previous code example. +

+
+				cPluginManager.AddHook(cPluginManager.HOOK_NAME_HERE, FunctionNameToBeCalled)
+			
+

+ What does this code do? +

    +
  • cPluginManager.AddHook registers the hook. The hook name is the second parameter. See the previous API documentation link for a list of all hooks.
  • +
+ What about the third parameter, you ask? Well, it is the name of the function that MCServer calls when the hook fires. It is in this + function that you should handle or cancel the hook. +

+

+ So in total, this is a working representation of what we have so far covered. +

+
+			function Initialize( Plugin )
+				Plugin:SetName( "DerpyPlugin" )
+				Plugin:SetVersion( 1 )
+
+				cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
+		
+				local PluginManager = cPluginManager:Get()
+				-- Command bindings
+
+				LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() )
+				return true
+			end
+			
+			function OnPlayerMoving(Player) -- See API docs for parameters of all hooks
+				return true -- Prohibit player movement, see docs for whether a hook is cancellable
+			end
+			
+

+ So, that code stops the player from moving. Not particularly helpful, but yes :P. Note that ALL documentation is available + on the main API docs page, so if ever in doubt, go there. +

+

Binding a command

+

Format

+

+ So now we know how to hook into MCServer, how do we bind a command, such as /explode, for a player to type? That is more complicated. + We firstly add this template to the "-- Command bindings" section of the initial example: +

+
+				-- ADD THIS IF COMMAND DOES NOT REQUIRE A PARAMETER (/explode)
+				PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " - Description of command")
+				
+				-- ADD THIS IF COMMAND DOES REQUIRE A PARAMETER (/explode Notch)
+				PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ Description of command and parameter(s)")
+			
+

+ What does it do, and why are there two? +

    +
  • PluginManager:BindCommand binds a command. It takes the command name (with a slash), the permission a player needs to execute the command, the function + to call when the command is executed, and a description of the command.
  • +
+ The command name is pretty self explanatory. The permission node is basically just a string that the player's group needs to have, so you can have anything in there, + though we recommend a style such as "derpyplugin.explode". The function to call is like the ones with Hooks, but with some fixed parameters which we will come on to later, + and the description is a description of the command which is shown when "/help" is typed. +

+

+ So why are there two? Standards. A plugin that accepts a parameter MUST use a format for the description of " ~ Description of command and parms" + whereas a command that doesn't accept parameters MUST use " - Description of command" instead. Be sure to put a space before the tildes or dashes. + Additionally, try to keep the description brief and on one line on the client. +

+

Parameters

+

+ What parameters are in the function MCServer calls when the command is executed? A 'Split' array and a 'Player' object. +

+

The Split Array

+

+ The Split array is an array of all text submitted to the server, including the actual command. MCServer automatically splits the text into the array, + so plugin authors do not need to worry about that. An example of a Split array passed for the command, "/derp zubby explode" would be:

+    /derp (Split[1])
+    zubby (Split[2])
+    explode (Split[3])
+
+    The total amount of parameters passed were: 3 (#Split) +

+

The Player Object and sending them messages

+

+ The Player object is basically a pointer to the player that has executed the command. You can do things with them, but most common is sending + a message. Again, see the API documentation for fuller details. But, you ask, how do we send a message to the client? +

+

+ Remember that copy of CoreMessaging.lua that we downloaded earlier? Make sure that file is in your plugin folder, along with the main.lua file you are typing + your code in. Since MCS brings all the files together on JIT compile, we don't need to worry about requiring any files or such. Simply follow the below examples: +

+
+				-- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it)
+				-- Use: Informational message, such as instructions for usage of a command
+				SendMessage(Player, "Usage: /explode [player]")
+				
+				-- Format: §green[INFO] §white%text% (green [INFO] etc.)
+				-- Use: Success message, like when a command executes successfully
+				SendMessageSuccess(Player, "Notch was blown up!")
+				
+				-- Format: §rose[INFO] §white%text% (rose coloured [INFO] etc.)
+				-- Use: Failure message, like when a command was entered correctly but failed to run, such as when the destination player wasn't found in a /tp command
+				SendMessageFailure(Player, "Player Salted was not found")
+			
+

+ Those are the basics. If you want to output text to the player for a reason other than the three listed above, and you want to colour the text, simply concatenate + "cChatColor.*colorhere*" with your desired text, concatenate being "..". See the API docs for more details of all colours, as well as details on logging to console with + LOG("Text"). +

+

Final example and conclusion

+

+ So, a working example that checks the validity of a command, and blows up a player, and also refuses pickup collection to players with >100ms ping. +

+
+			function Initialize( Plugin )
+				Plugin:SetName( "DerpyPluginThatBlowsPeopleUp" )
+				Plugin:SetVersion( 9001 )
+		
+				local PluginManager = cPluginManager:Get()
+				PluginManager:BindCommand("/explode", "derpyplugin.explode", Explode, " ~ Explode a player");
+
+				cPluginManager.AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup)
+
+				LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() )
+				return true
+			end
+			
+			function Explode(Split, Player)
+				if #Split ~= 2
+					SendMessage(Player, "Usage: /explode [playername]") -- There was more or less than one argument (excluding the /explode bit)
+				else
+					local ExplodePlayer = function(Explodee) -- Create a callback ExplodePlayer with parameter Explodee, which MCS calls for every player on the server
+						if (Explodee:GetName() == Split[2] then -- If the player we are currently at is the one we specified as the parameter...
+							Player:GetWorld():DoExplosionAt(Explodee:GetPosX(), Explodee:GetPosY(), Explodee:GetPosZ(), false, esPlugin) -- Explode 'em; see API docs for further details of this function
+							SendMessageSuccess(Player, Split[2] .. " was successfully exploded") -- Success!
+							return true -- Break out
+						end
+					end
+					
+					cRoot:Get():FindAndDoWithPlayer(Split[2], ExplodePlayer) -- Tells MCS to loop through all players and call the callback above with the Player object it has found
+					
+					SendMessageFailure(Player, Split[2] .. " was not found") -- We have not broken out so far, therefore, the player must not exist, send failure
+				end
+				
+				return true -- Concluding return
+			end
+			
+			function OnCollectingPickup(Player, Pickup) -- Again, see the API docs for parameters of all hooks. In this case, it is a Player and Pickup object
+				if (Player:GetClientHandle():GetPing() > 100) then -- Get ping of player, in milliseconds
+					return true -- Discriminate against high latency - you don't get drops :D
+				else
+					return false -- You do get the drops! Yay~
+				end
+			end
+			
+

+ Make sure to read the comments for a description of what everything does. Also be sure to return true for all command handlers, unless you want MCS to print out an "Unknown command" message + when the command gets executed :P. Make sure to follow standards - use CoreMessaging.lua functions for messaging, dashes for no parameter commands and tildes for vice versa, + and finally, the API documentation is your friend! +

+

+ Happy coding ;) +

+ + +
+
+
This tutorial was brought you by Aperture Science, in conjunction with McDonalds Enterprises.
+ + diff --git a/MCServer/Plugins/APIDump/main.css b/MCServer/Plugins/APIDump/main.css index 5cc603a3f..797079873 100644 --- a/MCServer/Plugins/APIDump/main.css +++ b/MCServer/Plugins/APIDump/main.css @@ -49,6 +49,12 @@ header font-family: Segoe UI Light, Helvetica; } +footer +{ + text-align: center; + font-family: Segoe UI Light, Helvetica; +} + #content { padding: 0px 25px 25px 25px; From 1cf6502be23ff78e07a96b906738c97805120ca0 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 11:51:08 +0100 Subject: [PATCH 096/118] Made cmake compilation possible on Windows. --- CMakeLists.txt | 14 +++++++++----- lib/expat/CMakeLists.txt | 9 +++++++-- lib/lua/CMakeLists.txt | 23 +++++++++++++++++------ lib/sqlite/CMakeLists.txt | 15 +++++++++++++-- src/Bindings/LuaState.cpp | 4 ++-- src/CMakeLists.txt | 20 +++++++++++++++----- src/LeakFinder.cpp | 18 +++++++----------- src/StackWalker.cpp | 6 +++--- 8 files changed, 73 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17eee02bb..fa3658583 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_definitions(-DCRYPTOPP_DISABLE_ASM) endif() -add_definitions(-DLUA_USE_DLOPEN) +add_definitions(-DLUA_BUILD_AS_DLL) +add_definitions(-DXML_STATIC) add_subdirectory(lib/inifile/) add_subdirectory(lib/jsoncpp/) @@ -106,10 +107,13 @@ else() add_flags("/Wall") endif() set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE_BAK}") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic") -set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -rdynamic") -set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -rdynamic") -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_PROFILE} -rdynamic") + +if (NOT WIN32) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic") + set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -rdynamic") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -rdynamic") + set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_PROFILE} -rdynamic") +endif() add_subdirectory (src) diff --git a/lib/expat/CMakeLists.txt b/lib/expat/CMakeLists.txt index df9e8d57c..667804b9a 100644 --- a/lib/expat/CMakeLists.txt +++ b/lib/expat/CMakeLists.txt @@ -2,10 +2,15 @@ cmake_minimum_required (VERSION 2.6) project (expat) -include_directories ("${PROJECT_SOURCE_DIR}/../../src/") - file(GLOB SOURCE "*.c" ) +# add headers to MSVC project files: +if (WIN32) + file(GLOB HEADERS "*.h") + set(SOURCE ${SOURCE} ${HEADERS}) + source_group("Sources" FILES ${SOURCE}) +endif() + add_library(expat ${SOURCE}) diff --git a/lib/lua/CMakeLists.txt b/lib/lua/CMakeLists.txt index 4787b6aa6..81d019f00 100644 --- a/lib/lua/CMakeLists.txt +++ b/lib/lua/CMakeLists.txt @@ -8,12 +8,23 @@ file(GLOB SOURCE "src/*.c" ) -if(${STATIC_LUA}) -add_library(lua ${SOURCE}) -else() -add_library(lua SHARED ${SOURCE}) +list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/src/lua.c" "${PROJECT_SOURCE_DIR}/src/luac.c") + +# add headers to MSVC project files: +if (WIN32) + file(GLOB HEADERS "src/*.h") + list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/src/lua.h" "${PROJECT_SOURCE_DIR}/src/luac.h") + set(SOURCE ${SOURCE} ${HEADERS}) + source_group("Sources" FILES ${SOURCE}) endif() -if(UNIX) -target_link_libraries(lua m dl) +# Lua needs to be linked dynamically on Windows and statically on *nix, so that LuaRocks work +if (WIN32) + add_library(lua SHARED ${SOURCE}) +else() + add_library(lua ${SOURCE}) +endif() + +if (UNIX) + target_link_libraries(m dl) endif() diff --git a/lib/sqlite/CMakeLists.txt b/lib/sqlite/CMakeLists.txt index 8596e2d9c..07e5a22cb 100644 --- a/lib/sqlite/CMakeLists.txt +++ b/lib/sqlite/CMakeLists.txt @@ -2,13 +2,24 @@ cmake_minimum_required (VERSION 2.6) project (sqlite) -include_directories ("${PROJECT_SOURCE_DIR}/../../src/") include_directories ("${PROJECT_SOURCE_DIR}/../") file(GLOB SOURCE "*.c" ) + +# add headers to MSVC project files: +if (WIN32) + file(GLOB HEADERS "src/*.h") + list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/src/lua.h" "${PROJECT_SOURCE_DIR}/src/luac.h") + set(SOURCE ${SOURCE} ${HEADERS}) + source_group("Sources" FILES ${SOURCE}) +endif() + + add_library(sqlite ${SOURCE}) -target_link_libraries(sqlite dl) +if (UNIX) + target_link_libraries(sqlite dl) +endif() diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index cfa3f70ca..64a818a60 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -18,7 +18,7 @@ extern "C" // fwd: SQLite/lsqlite3.c extern "C" { - LUALIB_API int luaopen_lsqlite3(lua_State * L); + int luaopen_lsqlite3(lua_State * L); } // fwd: LuaExpat/lxplib.c: @@ -309,7 +309,7 @@ void cLuaState::Push(const AStringVector & a_Vector) { ASSERT(IsValid()); - lua_createtable(m_LuaState, a_Vector.size(), 0); + lua_createtable(m_LuaState, (int)a_Vector.size(), 0); int newTable = lua_gettop(m_LuaState); int index = 1; for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 107ca60dd..88e469b74 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,9 +49,15 @@ else () source_group("" FILES ${SOURCE}) # Precompiled headers (1st part) - SET(PrecompiledBinary "$(IntDir)/Globals.pch") SET_SOURCE_FILES_PROPERTIES( - Globals.cpp PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\" /Fp\"${PrecompiledBinary}\"" + Globals.cpp PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\"" + ) + # CMake cannot "remove" the precompiled header flags, so we use a dummy precompiled header compatible with just this one file: + SET_SOURCE_FILES_PROPERTIES( + Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS "/Yc\"string.h\" /Fp\"$(IntDir)/Bindings.pch\"" + ) + SET_SOURCE_FILES_PROPERTIES( + "StackWalker.cpp LeakFinder.h" PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\"" ) endif() @@ -66,10 +72,11 @@ endif () add_executable(${EXECUTABLE} ${SOURCE}) +# Precompiled headers (2nd part) if (WIN32) SET_TARGET_PROPERTIES( - ${EXECUTABLE} PROPERTIES COMPILE_FLAGS "/Yu\"Globals.h\" /FI\"${PrecompiledBinary}\" /Fp\"${PrecompiledBinary}\"" - OBJECT_DEPENDS "${PrecompiledBinary}" + ${EXECUTABLE} PROPERTIES COMPILE_FLAGS "/Yu\"Globals.h\"" + OBJECT_DEPENDS "$(IntDir)/$(TargetName.pch)" ) endif () @@ -79,4 +86,7 @@ if (NOT WIN32) target_link_libraries(${EXECUTABLE} Protocol Generating WorldStorage) target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities) endif () -target_link_libraries(${EXECUTABLE} md5 luaexpat iniFile jsoncpp cryptopp zlib lua) +if (WIN32) + target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib) +endif() +target_link_libraries(${EXECUTABLE} md5 luaexpat iniFile jsoncpp cryptopp zlib lua sqlite) diff --git a/src/LeakFinder.cpp b/src/LeakFinder.cpp index 0f84adb2b..9d7f185ba 100644 --- a/src/LeakFinder.cpp +++ b/src/LeakFinder.cpp @@ -95,15 +95,11 @@ * **********************************************************************/ -#include -#include // Needed if compiled with "WIN32_LEAN_AND_MEAN" +#include "Globals.h" + #include +#include // Needed if compiled with "WIN32_LEAN_AND_MEAN" #include -#include - -#include -#include - #include "LeakFinder.h" @@ -463,11 +459,11 @@ public: pHashEntry->nDataSize = nDataSize; pHashEntry->Next = NULL; #ifdef _M_IX86 - pHashEntry->pCallstackOffset = (LPVOID) min(context.Ebp, context.Esp); + pHashEntry->pCallstackOffset = (LPVOID) std::min(context.Ebp, context.Esp); #elif _M_X64 - pHashEntry->pCallstackOffset = (LPVOID) min(context.Rdi, context.Rsp); + pHashEntry->pCallstackOffset = (LPVOID) std::min(context.Rdi, context.Rsp); #elif _M_IA64 - pHashEntry->pCallstackOffset = (LPVOID) min(context.IntSp, context.RsBSP); + pHashEntry->pCallstackOffset = (LPVOID) std::min(context.IntSp, context.RsBSP); #else #error "Platform not supported!" #endif @@ -490,7 +486,7 @@ public: if (pHashEntry->nMaxStackSize > 0) { SIZE_T len = ((SIZE_T) pHashEntry->pStackBaseAddr + pHashEntry->nMaxStackSize) - (SIZE_T)pHashEntry->pCallstackOffset; - bytesToRead = min(len, MAX_CALLSTACK_LEN_BUF); + bytesToRead = std::min(len, (SIZE_T)MAX_CALLSTACK_LEN_BUF); } // Now read the callstack: if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) pHashEntry->pCallstackOffset, &(pHashEntry->pcCallstackAddr), bytesToRead, &(pHashEntry->nCallstackLen)) == 0) diff --git a/src/StackWalker.cpp b/src/StackWalker.cpp index bf18b9fc9..b4f8ed8c7 100644 --- a/src/StackWalker.cpp +++ b/src/StackWalker.cpp @@ -73,10 +73,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **********************************************************************/ -#include + +#include "Globals.h" + #include -#include -#include #pragma comment(lib, "version.lib") // for "VerQueryValue" #pragma warning(disable:4826) From 66f9fcbb693a929ff35788fede05980cb8bf6cdb Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 11:56:39 +0100 Subject: [PATCH 097/118] Fixed unix Lua dependencies. --- lib/lua/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lua/CMakeLists.txt b/lib/lua/CMakeLists.txt index 81d019f00..63ac18e90 100644 --- a/lib/lua/CMakeLists.txt +++ b/lib/lua/CMakeLists.txt @@ -26,5 +26,5 @@ else() endif() if (UNIX) - target_link_libraries(m dl) + target_link_libraries(lua m dl) endif() From f5c013071eeec698d36971403b19ed39bfa02ffc Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 12:01:45 +0100 Subject: [PATCH 098/118] Lua is built statically on *nix. --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa3658583..0df702b29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,12 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_definitions(-DCRYPTOPP_DISABLE_ASM) endif() -add_definitions(-DLUA_BUILD_AS_DLL) +# Under Windows, we need Lua as DLL; on *nix we need it linked statically: +if (WIN32) + add_definitions(-DLUA_BUILD_AS_DLL) +endif() + +# The Expat library is linked in statically, make the source files aware of that: add_definitions(-DXML_STATIC) add_subdirectory(lib/inifile/) From 1a3a291bde12ad438bd95ea5df3c7b647df550d9 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 14:32:40 +0100 Subject: [PATCH 099/118] APIDump: Fixed bad for-loop. How did I not see this before? --- MCServer/Plugins/APIDump/main_APIDump.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua index e5c8b9c30..06dd451d7 100644 --- a/MCServer/Plugins/APIDump/main_APIDump.lua +++ b/MCServer/Plugins/APIDump/main_APIDump.lua @@ -232,11 +232,11 @@ end function DumpAPIHtml() LOG("Dumping all available functions and constants to API subfolder..."); - LOG("Moving static files.."); + LOG("Copying static files.."); cFile:CreateFolder("API/Static"); local localFolder = g_Plugin:GetLocalFolder(); - for k, v in cFile:GetFolderContents(localFolder .. "/Static") do - cFile:Copy(localFolder .. "/Static/" .. v, "API/Static/" .. v); + for idx, fnam in ipairs(cFile:GetFolderContents(localFolder .. "/Static")) do + cFile:Copy(localFolder .. "/Static/" .. fnam, "API/Static/" .. fnam); end LOG("Creating API tables..."); From f0ca18d72ab37521b2352d06232a4e1a0f5c38f4 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 14:33:15 +0100 Subject: [PATCH 100/118] APIDump: Added the WritingPlugin article to the exported pages. --- MCServer/Plugins/APIDump/APIDesc.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index d69dd14cd..f08f83698 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -2760,6 +2760,7 @@ end ExtraPages = { -- No sorting is provided for these, they will be output in the same order as defined here + { FileName = "Writing-a-MCServer-plugin.html", Title = "Writing a MCServer plugin" }, { FileName = "WebWorldThreads.html", Title = "Webserver vs World threads" }, } } ; From 270d79d47be1396b2ab03c2447544c64997b28db Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 14:46:07 +0100 Subject: [PATCH 101/118] APIDump: Fixed the WritingPlugin article. The code is no longer weirdly indented in the browser, and links are relative to the API docs root. --- .../APIDump/Writing-a-MCServer-plugin.html | 193 +++++++++--------- 1 file changed, 101 insertions(+), 92 deletions(-) diff --git a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html index 3ab997dcd..8d74051a6 100644 --- a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html +++ b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html @@ -2,7 +2,6 @@ - MCS Plugin Tutorial @@ -35,26 +34,26 @@ Format it like so:

-			local PLUGIN
-			
-			function Initialize( Plugin )
-				Plugin:SetName( "DerpyPlugin" )
-				Plugin:SetVersion( 1 )
-				
-				PLUGIN = Plugin
+local PLUGIN
 
-				-- Hooks
-		
-				local PluginManager = cPluginManager:Get()
-				-- Command bindings
+function Initialize(Plugin)
+	Plugin:SetName("DerpyPlugin")
+	Plugin:SetVersion(1)
+	
+	PLUGIN = Plugin
 
-				LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() )
-				return true
-			end
-			
-			function OnDisable()
-				LOG(PLUGIN:GetName() .. " is shutting down...")
-			end
+	-- Hooks
+
+	local PluginManager = cPluginManager:Get()
+	-- Command bindings
+
+	LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
+	return true
+end
+
+function OnDisable()
+	LOG(PLUGIN:GetName() .. " is shutting down...")
+end
 			

Now for an explanation of the basics. @@ -72,7 +71,7 @@

Registering hooks

Hooks are things that MCServer calls when an internal event occurs. For example, a hook is fired when a player places a block, moves, - logs on, eats, and many other things. For a full list, see the API documentation. + logs on, eats, and many other things. For a full list, see the API documentation.

A hook can be either informative or overridable. In any case, returning false will not trigger a response, but returning true will cancel @@ -84,7 +83,7 @@ To register a hook, insert the following code template into the "-- Hooks" area in the previous code example.

-				cPluginManager.AddHook(cPluginManager.HOOK_NAME_HERE, FunctionNameToBeCalled)
+cPluginManager.AddHook(cPluginManager.HOOK_NAME_HERE, FunctionNameToBeCalled)
 			

What does this code do? @@ -98,22 +97,22 @@ So in total, this is a working representation of what we have so far covered.

-			function Initialize( Plugin )
-				Plugin:SetName( "DerpyPlugin" )
-				Plugin:SetVersion( 1 )
+function Initialize(Plugin)
+	Plugin:SetName("DerpyPlugin")
+	Plugin:SetVersion(1)
 
-				cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
-		
-				local PluginManager = cPluginManager:Get()
-				-- Command bindings
+	cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
 
-				LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() )
-				return true
-			end
-			
-			function OnPlayerMoving(Player) -- See API docs for parameters of all hooks
-				return true -- Prohibit player movement, see docs for whether a hook is cancellable
-			end
+	local PluginManager = cPluginManager:Get()
+	-- Command bindings
+
+	LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
+	return true
+end
+
+function OnPlayerMoving(Player) -- See API docs for parameters of all hooks
+	return true -- Prohibit player movement, see docs for whether a hook is cancellable
+end
 			

So, that code stops the player from moving. Not particularly helpful, but yes :P. Note that ALL documentation is available @@ -126,11 +125,11 @@ We firstly add this template to the "-- Command bindings" section of the initial example:

-				-- ADD THIS IF COMMAND DOES NOT REQUIRE A PARAMETER (/explode)
-				PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " - Description of command")
-				
-				-- ADD THIS IF COMMAND DOES REQUIRE A PARAMETER (/explode Notch)
-				PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ Description of command and parameter(s)")
+-- ADD THIS IF COMMAND DOES NOT REQUIRE A PARAMETER (/explode)
+PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " - Description of command")
+
+-- ADD THIS IF COMMAND DOES REQUIRE A PARAMETER (/explode Notch)
+PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ Description of command and parameter(s)")
 			

What does it do, and why are there two? @@ -171,17 +170,17 @@ your code in. Since MCS brings all the files together on JIT compile, we don't need to worry about requiring any files or such. Simply follow the below examples:

-				-- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it)
-				-- Use: Informational message, such as instructions for usage of a command
-				SendMessage(Player, "Usage: /explode [player]")
-				
-				-- Format: §green[INFO] §white%text% (green [INFO] etc.)
-				-- Use: Success message, like when a command executes successfully
-				SendMessageSuccess(Player, "Notch was blown up!")
-				
-				-- Format: §rose[INFO] §white%text% (rose coloured [INFO] etc.)
-				-- Use: Failure message, like when a command was entered correctly but failed to run, such as when the destination player wasn't found in a /tp command
-				SendMessageFailure(Player, "Player Salted was not found")
+-- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it)
+-- Use: Informational message, such as instructions for usage of a command
+SendMessage(Player, "Usage: /explode [player]")
+
+-- Format: §green[INFO] §white%text% (green [INFO] etc.)
+-- Use: Success message, like when a command executes successfully
+SendMessageSuccess(Player, "Notch was blown up!")
+
+-- Format: §rose[INFO] §white%text% (rose coloured [INFO] etc.)
+-- Use: Failure message, like when a command was entered correctly but failed to run, such as when the destination player wasn't found in a /tp command
+SendMessageFailure(Player, "Player Salted was not found")
 			

Those are the basics. If you want to output text to the player for a reason other than the three listed above, and you want to colour the text, simply concatenate @@ -193,51 +192,63 @@ So, a working example that checks the validity of a command, and blows up a player, and also refuses pickup collection to players with >100ms ping.

-			function Initialize( Plugin )
-				Plugin:SetName( "DerpyPluginThatBlowsPeopleUp" )
-				Plugin:SetVersion( 9001 )
-		
-				local PluginManager = cPluginManager:Get()
-				PluginManager:BindCommand("/explode", "derpyplugin.explode", Explode, " ~ Explode a player");
+function Initialize(Plugin)
+	Plugin:SetName("DerpyPluginThatBlowsPeopleUp")
+	Plugin:SetVersion(9001)
 
-				cPluginManager.AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup)
+	local PluginManager = cPluginManager:Get()
+	PluginManager:BindCommand("/explode", "derpyplugin.explode", Explode, " ~ Explode a player");
 
-				LOG( "Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() )
-				return true
-			end
-			
-			function Explode(Split, Player)
-				if #Split ~= 2
-					SendMessage(Player, "Usage: /explode [playername]") -- There was more or less than one argument (excluding the /explode bit)
-				else
-					local ExplodePlayer = function(Explodee) -- Create a callback ExplodePlayer with parameter Explodee, which MCS calls for every player on the server
-						if (Explodee:GetName() == Split[2] then -- If the player we are currently at is the one we specified as the parameter...
-							Player:GetWorld():DoExplosionAt(Explodee:GetPosX(), Explodee:GetPosY(), Explodee:GetPosZ(), false, esPlugin) -- Explode 'em; see API docs for further details of this function
-							SendMessageSuccess(Player, Split[2] .. " was successfully exploded") -- Success!
-							return true -- Break out
-						end
-					end
-					
-					cRoot:Get():FindAndDoWithPlayer(Split[2], ExplodePlayer) -- Tells MCS to loop through all players and call the callback above with the Player object it has found
-					
-					SendMessageFailure(Player, Split[2] .. " was not found") -- We have not broken out so far, therefore, the player must not exist, send failure
-				end
-				
-				return true -- Concluding return
-			end
-			
-			function OnCollectingPickup(Player, Pickup) -- Again, see the API docs for parameters of all hooks. In this case, it is a Player and Pickup object
-				if (Player:GetClientHandle():GetPing() > 100) then -- Get ping of player, in milliseconds
-					return true -- Discriminate against high latency - you don't get drops :D
-				else
-					return false -- You do get the drops! Yay~
-				end
-			end
+	cPluginManager.AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup)
+
+	LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
+	return true
+end
+
+function Explode(Split, Player)
+	if (#Split ~= 2) then
+		-- There was more or less than one argument (excluding the "/explode" bit)
+		-- Send the proper usage to the player and exit
+		SendMessage(Player, "Usage: /explode [playername]")
+		return true
+	end
+
+	-- Create a callback ExplodePlayer with parameter Explodee, which MCS calls for every player on the server
+	local HasExploded = false
+	local ExplodePlayer = function(Explodee)
+		-- If the player we are currently at is the one we specified as the parameter
+		if (Explodee:GetName() == Split[2]) then
+			-- Create an explosion at the same position as they are; see API docs for further details of this function
+			Player:GetWorld():DoExplosionAt(Explodee:GetPosX(), Explodee:GetPosY(), Explodee:GetPosZ(), false, esPlugin)
+			SendMessageSuccess(Player, Split[2] .. " was successfully exploded")
+			HasExploded = true;
+			return true -- Signalize to MCS that we do not need to call this callback for any more players
+		end
+	end
+	
+	-- Tell MCS to loop through all players and call the callback above with the Player object it has found
+	cRoot:Get():FindAndDoWithPlayer(Split[2], ExplodePlayer)
+	
+	if not(HasExploded) then
+		-- We have not broken out so far, therefore, the player must not exist, send failure
+		SendMessageFailure(Player, Split[2] .. " was not found")
+	end
+	
+	return true
+end
+
+function OnCollectingPickup(Player, Pickup) -- Again, see the API docs for parameters of all hooks. In this case, it is a Player and Pickup object
+	if (Player:GetClientHandle():GetPing() > 100) then -- Get ping of player, in milliseconds
+		return true -- Discriminate against high latency - you don't get drops :D
+	else
+		return false -- You do get the drops! Yay~
+	end
+end
 			

Make sure to read the comments for a description of what everything does. Also be sure to return true for all command handlers, unless you want MCS to print out an "Unknown command" message when the command gets executed :P. Make sure to follow standards - use CoreMessaging.lua functions for messaging, dashes for no parameter commands and tildes for vice versa, - and finally, the API documentation is your friend! + and finally, the API documentation is your friend!

Happy coding ;) @@ -247,7 +258,5 @@ prettyPrint(); -


-
This tutorial was brought you by Aperture Science, in conjunction with McDonalds Enterprises.
From daeb37b27b9732bd34e02566f708f961bb6003a5 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 15:01:49 +0100 Subject: [PATCH 102/118] APIDump: Extra pages renamed to articles and moved to front. --- MCServer/Plugins/APIDump/main_APIDump.lua | 63 ++++++++++++++--------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua index 06dd451d7..c36285099 100644 --- a/MCServer/Plugins/APIDump/main_APIDump.lua +++ b/MCServer/Plugins/APIDump/main_APIDump.lua @@ -229,6 +229,32 @@ end +local function WriteArticles(f) + f:write([[ +

Articles

+

The following articles provide various extra information on plugin development

+
    + ]]); + for i, extra in ipairs(g_APIDesc.ExtraPages) do + local SrcFileName = g_PluginFolder .. "/" .. extra.FileName; + if (cFile:Exists(SrcFileName)) then + local DstFileName = "API/" .. extra.FileName; + if (cFile:Exists(DstFileName)) then + cFile:Delete(DstFileName); + end + cFile:Copy(SrcFileName, DstFileName); + f:write("
  • " .. extra.Title .. "
  • \n"); + else + f:write("
  • " .. extra.Title .. " (file is missing)
  • \n"); + end + end + f:write("

"); +end + + + + + function DumpAPIHtml() LOG("Dumping all available functions and constants to API subfolder..."); @@ -308,12 +334,17 @@ function DumpAPIHtml()

The API reference is divided into the following sections:


+ ]]); + + WriteArticles(f); + + f:write([[

Class index

The following classes are available in the MCServer Lua scripting language:

    @@ -325,6 +356,9 @@ function DumpAPIHtml() f:write([[


+ ]]); + + f:write([[

Hooks

A plugin can register to be called whenever an "interesting event" occurs. It does so by calling @@ -352,30 +386,11 @@ function DumpAPIHtml() WriteHtmlHook(hook); end end - f:write([[ - + f:write([[ +


-

Extra pages

- -

The following pages provide various extra information

- -
    -]]); - for i, extra in ipairs(g_APIDesc.ExtraPages) do - local SrcFileName = g_PluginFolder .. "/" .. extra.FileName; - if (cFile:Exists(SrcFileName)) then - local DstFileName = "API/" .. extra.FileName; - if (cFile:Exists(DstFileName)) then - cFile:Delete(DstFileName); - end - cFile:Copy(SrcFileName, DstFileName); - f:write("
  • " .. extra.Title .. "
  • \n"); - else - f:write("
  • " .. extra.Title .. " (file is missing)
  • \n"); - end - end - f:write("
"); - + ]]); + -- Copy the static files to the output folder (overwrite any existing): cFile:Copy(g_Plugin:GetLocalFolder() .. "/main.css", "API/main.css"); cFile:Copy(g_Plugin:GetLocalFolder() .. "/prettify.js", "API/prettify.js"); From 0c071c593787c1faa86349497101db42ffc92490 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 15:08:27 +0100 Subject: [PATCH 103/118] APIDump: Split the huge DumpAPIHtml function into smaller ones. --- MCServer/Plugins/APIDump/main_APIDump.lua | 108 ++++++++++++---------- 1 file changed, 61 insertions(+), 47 deletions(-) diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua index c36285099..67d6d1036 100644 --- a/MCServer/Plugins/APIDump/main_APIDump.lua +++ b/MCServer/Plugins/APIDump/main_APIDump.lua @@ -255,6 +255,65 @@ end +local function WriteClasses(f, a_API) + f:write([[ +

Class index

+

The following classes are available in the MCServer Lua scripting language: +

    + ]]); + for i, cls in ipairs(a_API) do + f:write("
  • ", cls.Name, "
  • \n"); + WriteHtmlClass(cls, a_API); + end + f:write([[ +

+
+ ]]); +end + + + + + +local function WriteHooks(f, a_Hooks, a_UndocumentedHooks) + f:write([[ +

Hooks

+

+ A plugin can register to be called whenever an "interesting event" occurs. It does so by calling + cPluginManager's AddHook() function and implementing a callback + function to handle the event.

+

+ A plugin can decide whether it will let the event pass through to the rest of the plugins, or hide it + from them. This is determined by the return value from the hook callback function. If the function + returns false or no value, the event is propagated further. If the function returns true, the processing + is stopped, no other plugin receives the notification (and possibly MCServer disables the default + behavior for the event). See each hook's details to see the exact behavior.

+ + + + + + ]]); + for i, hook in ipairs(a_Hooks) do + if (hook.DefaultFnName == nil) then + -- The hook is not documented yet + f:write(" \n \n \n \n"); + table.insert(a_UndocumentedHooks, hook.Name); + else + f:write(" \n \n \n \n"); + WriteHtmlHook(hook); + end + end + f:write([[ +
Hook nameCalled when
" .. hook.Name .. "(No documentation yet)
" .. hook.Name .. "" .. LinkifyString(hook.CalledWhen, hook.Name) .. "
+
+ ]]); +end + + + + + function DumpAPIHtml() LOG("Dumping all available functions and constants to API subfolder..."); @@ -343,53 +402,8 @@ function DumpAPIHtml() ]]); WriteArticles(f); - - f:write([[ -

Class index

-

The following classes are available in the MCServer Lua scripting language: -

    - ]]); - for i, cls in ipairs(API) do - f:write("
  • ", cls.Name, "
  • \n"); - WriteHtmlClass(cls, API); - end - f:write([[ -

-
- ]]); - - f:write([[ -

Hooks

-

- A plugin can register to be called whenever an "interesting event" occurs. It does so by calling - cPluginManager's AddHook() function and implementing a callback - function to handle the event.

-

- A plugin can decide whether it will let the event pass through to the rest of the plugins, or hide it - from them. This is determined by the return value from the hook callback function. If the function - returns false or no value, the event is propagated further. If the function returns true, the processing - is stopped, no other plugin receives the notification (and possibly MCServer disables the default - behavior for the event). See each hook's details to see the exact behavior.

- - - - - - ]]); - for i, hook in ipairs(Hooks) do - if (hook.DefaultFnName == nil) then - -- The hook is not documented yet - f:write(" \n \n \n \n"); - table.insert(UndocumentedHooks, hook.Name); - else - f:write(" \n \n \n \n"); - WriteHtmlHook(hook); - end - end - f:write([[ -
Hook nameCalled when
" .. hook.Name .. "(No documentation yet)
" .. hook.Name .. "" .. LinkifyString(hook.CalledWhen, hook.Name) .. "
-
- ]]); + WriteClasses(f, API); + WriteHooks(f, Hooks, UndocumentedHooks); -- Copy the static files to the output folder (overwrite any existing): cFile:Copy(g_Plugin:GetLocalFolder() .. "/main.css", "API/main.css"); From b99255820b00547ef49045117d7346a30999870c Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 15:26:41 +0100 Subject: [PATCH 104/118] APIDump: Added a quick-nav menu to class descriptions. First part of #403. --- MCServer/Plugins/APIDump/main_APIDump.lua | 28 ++++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua index 67d6d1036..42cd5bad4 100644 --- a/MCServer/Plugins/APIDump/main_APIDump.lua +++ b/MCServer/Plugins/APIDump/main_APIDump.lua @@ -255,7 +255,7 @@ end -local function WriteClasses(f, a_API) +local function WriteClasses(f, a_API, a_ClassMenu) f:write([[

Class index

The following classes are available in the MCServer Lua scripting language: @@ -263,7 +263,7 @@ local function WriteClasses(f, a_API) ]]); for i, cls in ipairs(a_API) do f:write("

  • ", cls.Name, "
  • \n"); - WriteHtmlClass(cls, a_API); + WriteHtmlClass(cls, a_API, a_ClassMenu); end f:write([[

    @@ -378,7 +378,19 @@ function DumpAPIHtml() LOGINFO("Cannot output HTML API: " .. err); return; end + + -- Create a class menu that will be inserted into each class file for faster navigation (#403) + local ClassMenuTab = {}; + for idx, cls in ipairs(API) do + table.insert(ClassMenuTab, ""); + table.insert(ClassMenuTab, cls.Name); + table.insert(ClassMenuTab, "
    "); + end + local ClassMenu = table.concat(ClassMenuTab, ""); + -- Write the HTML file: f:write([[ @@ -402,7 +414,7 @@ function DumpAPIHtml() ]]); WriteArticles(f); - WriteClasses(f, API); + WriteClasses(f, API, ClassMenu); WriteHooks(f, Hooks, UndocumentedHooks); -- Copy the static files to the output folder (overwrite any existing): @@ -860,7 +872,7 @@ end -function WriteHtmlClass(a_ClassAPI, a_AllAPI) +function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu) local cf, err = io.open("API/" .. a_ClassAPI.Name .. ".html", "w"); if (cf == nil) then return; @@ -975,7 +987,11 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI)

    ]], a_ClassAPI.Name, [[


    -

    Contents

    +
    Quick navigation: + ]]); + cf:write(a_ClassMenu); + cf:write([[ +

    Contents

      ]]); @@ -1073,7 +1089,7 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI) end end - cf:write([[]]); + cf:write([[
    ]]); cf:close(); end From 9a9888a8cc0be28af5217de38f1154db97fa649a Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 15:36:28 +0100 Subject: [PATCH 105/118] APIDump: Added a quick-nav menu to hook descriptions. Second part of #403. --- MCServer/Plugins/APIDump/main_APIDump.lua | 29 +++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua index 42cd5bad4..fef827f3e 100644 --- a/MCServer/Plugins/APIDump/main_APIDump.lua +++ b/MCServer/Plugins/APIDump/main_APIDump.lua @@ -275,7 +275,7 @@ end -local function WriteHooks(f, a_Hooks, a_UndocumentedHooks) +local function WriteHooks(f, a_Hooks, a_UndocumentedHooks, a_HookNav) f:write([[

    Hooks

    @@ -301,7 +301,7 @@ local function WriteHooks(f, a_Hooks, a_UndocumentedHooks) table.insert(a_UndocumentedHooks, hook.Name); else f:write(" \n " .. hook.Name .. "\n " .. LinkifyString(hook.CalledWhen, hook.Name) .. "\n \n"); - WriteHtmlHook(hook); + WriteHtmlHook(hook, a_HookNav); end end f:write([[ @@ -379,7 +379,7 @@ function DumpAPIHtml() return; end - -- Create a class menu that will be inserted into each class file for faster navigation (#403) + -- Create a class navigation menu that will be inserted into each class file for faster navigation (#403) local ClassMenuTab = {}; for idx, cls in ipairs(API) do table.insert(ClassMenuTab, ""); + table.insert(HookNavTab, (hook.Name:gsub("^HOOK_", ""))); -- remove the "HOOK_" part of the name + table.insert(HookNavTab, "
    "); + end + local HookNav = table.concat(HookNavTab, ""); + -- Write the HTML file: f:write([[ @@ -415,7 +426,7 @@ function DumpAPIHtml() WriteArticles(f); WriteClasses(f, API, ClassMenu); - WriteHooks(f, Hooks, UndocumentedHooks); + WriteHooks(f, Hooks, UndocumentedHooks, HookNav); -- Copy the static files to the output folder (overwrite any existing): cFile:Copy(g_Plugin:GetLocalFolder() .. "/main.css", "API/main.css"); @@ -1097,7 +1108,7 @@ end -function WriteHtmlHook(a_Hook) +function WriteHtmlHook(a_Hook, a_HookNav) local fnam = "API/" .. a_Hook.DefaultFnName .. ".html"; local f, error = io.open(fnam, "w"); if (f == nil) then @@ -1120,7 +1131,11 @@ function WriteHtmlHook(a_Hook)

    ]], a_Hook.Name, [[


    -

    +
    Quick navigation: + ]]); + f:write(a_HookNav); + f:write([[ +

    ]]); f:write(LinkifyString(a_Hook.Desc, HookName)); f:write("

    \n

    Callback function

    \n

    The default name for the callback function is "); @@ -1150,7 +1165,7 @@ function WriteHtmlHook(a_Hook) f:write("

    ", (example.Desc or "missing Desc"), "

    \n"); f:write("
    ", (example.Code or "missing Code"), "\n
    \n\n"); end - f:write([[]]); + f:write([[
    ]]); f:close(); end From e4c6e853eaa231f0c95235b02612ad35edf73dce Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 15:48:01 +0100 Subject: [PATCH 106/118] APIDump: Added quick links to index to each class and hook file. Third part of #403. --- MCServer/Plugins/APIDump/main_APIDump.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua index fef827f3e..ff837ec4e 100644 --- a/MCServer/Plugins/APIDump/main_APIDump.lua +++ b/MCServer/Plugins/APIDump/main_APIDump.lua @@ -998,7 +998,13 @@ function WriteHtmlClass(a_ClassAPI, a_AllAPI, a_ClassMenu)

    ]], a_ClassAPI.Name, [[


    -
    Quick navigation: +
    + Index:
    + Articles
    + Classes
    + Hooks
    +
    + Quick navigation:
    ]]); cf:write(a_ClassMenu); cf:write([[ @@ -1131,7 +1137,13 @@ function WriteHtmlHook(a_Hook, a_HookNav)

    ]], a_Hook.Name, [[


    -
    Quick navigation: +
    + Index:
    + Articles
    + Classes
    + Hooks
    +
    + Quick navigation:
    ]]); f:write(a_HookNav); f:write([[ From 9689896f9c113b4ee2e3d2ca29659cc8b7215686 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 20:48:11 +0100 Subject: [PATCH 107/118] APIDump: Articles eplicitly declare being UTF-8. This fixes display in firefox. --- MCServer/Plugins/APIDump/WebWorldThreads.html | 1 + MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html | 1 + 2 files changed, 2 insertions(+) diff --git a/MCServer/Plugins/APIDump/WebWorldThreads.html b/MCServer/Plugins/APIDump/WebWorldThreads.html index 6caf40e6d..fc80a6178 100644 --- a/MCServer/Plugins/APIDump/WebWorldThreads.html +++ b/MCServer/Plugins/APIDump/WebWorldThreads.html @@ -6,6 +6,7 @@ +
    diff --git a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html index 8d74051a6..50e39d533 100644 --- a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html +++ b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html @@ -7,6 +7,7 @@ +
    From 537d1c4e421d6d79cd5c56fd4d97220ae2a3d0e5 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 27 Dec 2013 20:56:01 +0100 Subject: [PATCH 108/118] APIDump: Added the SettingUpDecoda article. This implements #383. --- MCServer/Plugins/APIDump/APIDesc.lua | 1 + MCServer/Plugins/APIDump/SettingUpDecoda.html | 49 ++++++++++++++++++ .../APIDump/Static/decoda_debug_settings.png | Bin 0 -> 6178 bytes .../Plugins/APIDump/Static/decoda_logo.png | Bin 0 -> 2413 bytes .../APIDump/Static/decoda_workspace.png | Bin 0 -> 70644 bytes 5 files changed, 50 insertions(+) create mode 100644 MCServer/Plugins/APIDump/SettingUpDecoda.html create mode 100644 MCServer/Plugins/APIDump/Static/decoda_debug_settings.png create mode 100644 MCServer/Plugins/APIDump/Static/decoda_logo.png create mode 100644 MCServer/Plugins/APIDump/Static/decoda_workspace.png diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index f08f83698..d388d15dd 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -2761,6 +2761,7 @@ end { -- No sorting is provided for these, they will be output in the same order as defined here { FileName = "Writing-a-MCServer-plugin.html", Title = "Writing a MCServer plugin" }, + { FileName = "SettingUpDecoda.html", Title = "Setting up the Decoda Lua IDE" }, { FileName = "WebWorldThreads.html", Title = "Webserver vs World threads" }, } } ; diff --git a/MCServer/Plugins/APIDump/SettingUpDecoda.html b/MCServer/Plugins/APIDump/SettingUpDecoda.html new file mode 100644 index 000000000..4ad827efe --- /dev/null +++ b/MCServer/Plugins/APIDump/SettingUpDecoda.html @@ -0,0 +1,49 @@ + + + + + MCServer - Setting up Decoda + + + + + + + +
    +

    Setting up the Decoda IDE

    +

    + This article will explain how to set up Decoda, an IDE for writing Lua code, so that you can develop MCServer plugins with the comfort of an IDE.

    + +

    About Decoda

    + +

    To quickly introduce Decoda, it is an IDE for writing Lua code. It has the basic features expected of an IDE - you can group files into project, you can edit multiple files in a tabbed editor, the code is syntax-highlighted. Code completion, symbol browsing, and more. It also features a Lua debugger that allows you to debug your Lua code within any application that embeds the Lua runtime or uses Lua as a dynamic-link library (DLL). Although it is written using the multiplatform WxWidgets toolkit, it hasn't yet been ported to any platform other than 32-bit Windows. This unfortunately means that Linux users will not be able to use it. It can be used on 64-bit Windows, but the debugger only works for 32-bit programs.

    +

    Here's a screenshot of a default Decoda window with the debugger stepping through the code (scaled down):
    +

    +

    As you can see, you can set breakpoints in the code, inspect variables' values, view both the Lua and native (C++) call-stacks. Decoda also breaks program execution when a faulty Lua script is executed, providing a detailed error message and pointing you directly to the faulting code. It is even possible to attach a C++ debugger to a process that is being debugged by Decoda, this way you can trap both C++ and Lua errors.

    +

    Decoda is open-source, the sources are on GitHub: https://github.com/unknownworlds/decoda. You can download a compiled binary from the creators' site, http://unknownworlds.com/decoda/. + +

    Project management

    +

    To begin using Decoda, you need to create a project, or load an existing one. Decoda projects have a .deproj extension, and are simply a list of Lua files that are to be opened. You can create a project through menu Project -> New Project. Save your project first, so that Decoda knows what relative paths to use for the files. Then either add existing Lua files or create new one, through menu Project -> Add Add New File / Add Existing File.

    +

    Next you need to set up the executable that Decoda will run when debugging your files. Select menu Project -> Settings. A new dialog will open:
    +

    +

    In the debugging section, fill in the full path to MCServer.exe, or click the triple-dot button to browse for the file. Note that the Working directory will be automatically filled in for you with the folder where the executable is (until the last backslash). This is how it's supposed to work, don't change it; if it for some reason doesn't update, copy and paste the folder name from the Command edit box. All done, you can close this dialog now.

    + +

    Debugging

    +

    You are now ready to debug your code. Before doing that, though, don't forget to save your project file. If you haven't done so already, enable your plugin in the settings.ini file. If you want the program to break at a certain line, it is best to set the breakpoint before starting the program. Set the cursor on the line and hit F9 (or use menu Debug -> Toggle Breakpoint) to toggle a breakpoint on that line. Finally, hit F5, or select menu Debug -> Start to launch MCServer under the debugger. The MCServer window comes up and loads your plugin. If Decoda displays the Project Settings dialog instead, you haven't set up the executable to run, see the Project management section for instructions.

    +

    At this point you will see that Decoda starts adding new items to your project. All the files for all plugins are added temporarily. Don't worry, they are only temporary, they will be removed again once the debugging session finishes. You can tell the temporary files from the regular files by their icon, it's faded out. Decoda handles all the files that MCServer loads, so you can actually debug any of those faded files, too.

    +

    If there's an error in the code, the Decoda window will flash and a dialog box will come up, describing the error and taking you to the line where it occured. Note that the execution is paused in the thread executing the plugin, so until you select Debug -> Continue from the menu (F5), MCServer won't be fully running. You can fix the error and issue a "reload" command in MCServer console to reload the plugin code anew (MCServer doesn't detect changes in plugin code automatically).

    +

    If the execution hits a breakpoint, the Decoda window will flash and a yellow arrow is displayed next to the line. You can step through the code using F10 and F11, just like in MSVS. You can also use the Watch window to inspect variable values, or simply hover your mouse over a variable to display its value in the tooltip.

    + +

    Limitations

    +

    So far everything seems wonderful. Unfortunately, it doesn't always go as easy. There are several limits to what Decoda can do:

    +
      +
    • When the program encounters a logical bug (using a nil value, calling a non-existent function etc.), Decoda will break, but usually the Watch window and the tooltips are showing nonsense values. You shouldn't trust them in such a case, if you kep running into such a problem regularly, put console logging functions (LOG) in the code to print out any info you need prior to the failure.
    • +
    • Sometimes breakpoints just don't work. This is especially true if there are multiple plugins that have files of the same name. Breakpoints will never work after changing the code and reloading the plugin in MCServer; you need to stop the server and start again to reenable breakpoints.
    • +
    • Most weirdly, sometimes Decoda reports an error, but instead of opening the current version of the file, opens up another window with old contents of the file. Watch for this, because you could overwrite your new code if you saved this file over your newer file. Fortunately enough, Decoda will always ask you for a filename before saving such a file.
    • +
    • Decoda stores the project in two files. The .deproj file has the file list, and should be put into version control systems. The .deuser file has the settings (debugged application etc.) and is per-user specific. This file shouldn't go to version control systems, because each user may have different paths for the debuggee.
    • +
    • Unfortunately for us Windows users, the Decoda project file uses Unix-lineends (CR only). This makes it problematic when checking the file into a version control system, since those usually expect windows (CRLF) lineends; I personally convert the lineends each time I edit the project file using TED Notepad.
    • +
    +
    + + diff --git a/MCServer/Plugins/APIDump/Static/decoda_debug_settings.png b/MCServer/Plugins/APIDump/Static/decoda_debug_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..462d517b43a2369bf1f1e2aeada7ed429bbb1d01 GIT binary patch literal 6178 zcmeHLXHZn#mTekZK2TAh5d;yCLz4u_k|as!OQuoDlF=q70m&e!AVPB+RFI^!5+q4R zKtKdUl3$Z^7LX(eB1xuw^QLNkOx1gT=GV-vx^-)>bNAVGcGX^IuN`BkuXU21ogRTe zoP@CII0OO-ArMs7e^FBqH@RF+%7@Aar=^A{>OQwbF=%e7>Zu|SZxfCm*rE^!YJ{Qg zH4O^>7jRKiRf~#@8tCmlIy#~t|Ns1d2L6v^;QVLnNXk*^Jh5gz2n0j(pO>o5BiDff zvioY7`WkyU`TE;>J0c8i@$SB&`Wn}`{Uk-DMa9L$w!B{;5a?(~UG-YP&DHcrFTV+# z(BQ0JI10OK{@3X}nlW_L>e%eBaD+lfc*V(L<%!(J9s1yj;)bv~qljV_Zpc~(ORY(z zL;i=2rr7OmpKnsZ>TKHRp@N>tqeCrN+t(rem(Iuoz!;;3D#A)^INSU0M~0nektx1k z`{DiDkxPOl@K#{qkd^{)F9bHm3>QXbz3k9}_h(izGZnP7ok67)Vw1K(>WUo<$duva8 z7MgGeM&Y$!8b-4%fl?X)>3y@L6%*A8_1Jb})f3W)ktOwr+Giw_ZX+?6CFv&D-MS*| z<_}wbWC;g1YHa`tfY(emvR?e%nN2?y;_m}$3}hao@ zTk@ZLs6tz>r~CIMeq_exHCEAoS6uU8aR_;XfG*Inp|DJvTIM{E10ov%eOSZ_gP0RG zlx(wc;-f(#RU8t~W%CvifJC@aII#64vB2ub#OMhiF1{SrgzJl9E(>`UH3~v&NFC$t zIKZS?nTA{d_;Borm>Odn9XM@C#P-wN=@S3t`e>buf=ePt!!FJ9VH+_s!7|mzzxgl< z@QU{b|kTk#u|ndE{ZuEC%SHuqYts0>+|Niz5>1sk60c#MnzR_1TDP&4+_ia0Tj zKx^V$!;bJg2=975KQ?=qN~)QkU93lf7_onS<}H2l)WVjpvf`cmPaAUL9&NTez^&JI zbC*be&om2)t0=~clb_#fzJJ`%5Gjwk&doF|`Da3G$}HD&tj>&38E8P`&BN$S{|e(}Q_BoxZfTbBBKoB{ix+r15#-3r`r++%*(x2zN?cUc4Q znKSx21GuT36-^vZgmcpDf zoMv|L95GjUpDFCx7}E!df?QcfF*+~3Ap+aB=WmdV7ZglhQbjR6qMX^!%nAdi7 zK%oC2RI)47L$Km^gEqyLVj&*(+u=xgoTme5HpnYke> zVYb_$CU-GJZB(@6+jslgxdy4I3li70FLoDSD63f*;#6yA&<%pY+lRFRg`4Z{x#8$| zc-Mo1cxx`^mG^96Sw2?VcRJX3EN4j51Uv&iGrnZ7?n>dgyh^%%(41;1FkJGRTI zRM=GV#WZ3ng(%DzIzPh1og{Vrdc;IAE?+)?omNN%4!6^Hiv@bCp;J>3lVN9`ULq-P z{M0!`2pKC^AqY!V>_^<=hg8IKWHjK{e#)NT>xq3K`T0!>0Rb=Pc*4esTMiR~l@-^g zekF}AVAZU`Yqy@IRHy!qw1@^i>24!kd@}79!yMAUNiCcZi!cMo%iE85Z1NDQNvXbQ zpwvNT?*XS;_HGmFJJc0|svhgaK`Nq~di}hDa?B>&&@V%d1^M?fqrq=(p9#1C?XJ-d z1f-FgbuD>jM=`?je0Yn6fIaXJmn(W?Ta4(v*hzMiHZCsWimOvcLGNf2{2a{Ja+zi$ z|3q|X_tAqVgeD9gi0cEoPa5y@*g4pjxXHR>gH+UVK%vj9dpI+khWC&Z^P2u!Z>WuHlMLh7E7pjXA5zhEA z0>4&1xWSDt)2n;YL?~~DB>YybdoFxp!9cx)^4Zz%FIRZy;7=DIY9e6WdcJjfIsLer z_lrVxp&#*cgA%NV%f1#$8y{C`YIv7WMl%1!cCb8wij_T2tk%p47(8#%BKI!_^V+^E zi_$Q2`*6TEG1T*TRAnA(?j`M9u=5*wp;U|%&Y-$$<2Eg>|lPuV5!fvxSFrVU{?`V z>bYV1;6W$kj4xvHa7>&V6F>|drR3UNP2M$qu<$jdZmmX=Z*^hvNT>8AM@84rfaQ-D ze9V%s&Q4yP)Unnw-)`z^H6InHd;0CJ%Erc`<1?=MV!-;Bg!rYgFy@Y8@DQLAT_PQW$fWoOr=Ei*zVtqub#Ny$-}yU(1hYxU)^+NN z!!I?6J-Fgy$6zV1a@mB`A*$*agt@LBtbl>&DL)z0s5~~|UK044Mkt^1Gm*a{C60-B zCLn427_>{o%R68A^-HIQJm>7;lNPX`pk>{X4$$?EgYFj5 zK6O`v=K@9vr0EfGye@rs;2ogr)Xb;}1U13twBmwM*{AFKXntMC6xB_aN$Ez=gPMB? z+gAm|iB!D7>&s#2MtwViuBDjcZ2<$yVmpk%YigVRKF1{>oBL^4q-T4I4CpQ{mVf=0 zn1psGM6(f7af*JwNw3NJ1F}$R=XC%5j^V=8a^hJGM3k!=@q}>Fcz#J;7~>OAF%6TP z$@)$K04C7p+!nT=a+*oK=-0>LZj+EwU8?+`!0@HY`e1ExT+AJF0TtLhSIO=FZLd~!6e4|5j$Ow7yXU55`s0ioF9pg6Qlz4Ss6~{HqM`?+Ga>6ZwAmWVynm9 zUTP#cJm;nCqxs$Llsz|eCBQaHi9lkk8)|05;!fRUdvOZjX)gT~I|ldNW7S-whS-}P zU5SeT+f&hud;i;RSFK>r(S^9I?IC+v{v{iiwR&`|nz#bMy9RD8>OYF?YzwNlfvrNftu2=9p$Mzi&Fhhkx%bZDgB(;MkPMv zscj)mDw@XO83aoam`= zj`ReQ#JH);ABDpFZz_~6eVld9RLNxp)PWf^PV_D1m9k#D8WCm)!}?t!Cyp{N_kd+6 z;xWhMF5t9g%<91is?Wb>hrY#R&ur*yMR~2uN;)rIuaCJ_b zyj*d+?DxiiB^3f{Ef!r|!EUarQ8Pi#`rVb(!jQ|5Sj4MJGDpertqCGB{)l~#_$?7Dl%-?dj7M2`+gzLWMkp(S5$ zpDaXdr9r+6<@N8C5?8uzQY{ot^vo7Pf^NJN$&Qu2RjC0x{Z#T^0d#p^J1eo|&Izsf zbimV}Hhcz39p3-+mIz3xlA{+O$sjH{KRD`|kCO$!JFWA!>B9u)Zr3;d@UwmTobZv) zr*@3D_Dw>Y4!06Louh6p_Ht$)@-)PdI__wDFDqeoGeRYA4p^~#5vO~FRe}ab-3LF3 z*tW3;#oD={EBrsLJ5#NfmIBw0_@vqVqWPXq4x$ig?B55I~k zR$zz5YB)p=xEYilm+$^?TPaE+P)sDLX(^horjh2q<9M{i z;IaGd43gCnmi*J8o*Tal*Oioiad>I`>sE#ps=xyaOX>a{8#I28BmLf^WpM85lloK+8a=aDg57%%ShFn^ze0yG^YtfwQ?82t)WGJiuHL!}kqNb%?28kcW3bol)Q-*sSC zF!lj&rJSOCXI}|3-Rkca1E!PSE798!#5-Irx7YCR@intUALQowMbf!a%6~k)JK629 z$#1ZweM^az-^-Ls;P7Xp5fOyLO=M+O}5h7qVrVVA?0a2 zpj076pAr?yyyD*x8GugUYu{@uxo?e@Ap=mgq>Z4V)RDu+CL+jsX8imDWL(Q78vxUw zeVmN}-g$?b*5F?$nZAnsUW4uiVL`Kmwb)k;4dV_^z_uP!zQ5?H75ulOvHN~8ju5UW zD&ffkh4b!TI#l8Fe7sfS)w9#bD6qk3(jS-rDtLISuK#!NedE-IpDU#vd0v+HE3`eg zDn4{-VJ#dB_LsD9%pAkw@^%9A{jb6{NhOIZ($Ldd6_}LqP++w~8X9O@K0jE+5qT4y zbAH|krrWP5jjlc`v&b_T?b88EXF!AS=nK{B<7#;_HpxH-Cs*0LXrCd)FwDv>?q@rtzOk{amhX^!^%%k*vX}Ta!u& zTAGi(hYPx#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy8gxZibW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE-^4L^m3s900_lN zL_t(oN0nE3P}Juc{^1dY2rQsA$E4{@#%AgtwquWUTH9$lnQ3d3%kp*?7^O#`t*6fWfxHllV#r7 z{k^~Md!Oe$zmFII+86n3h`EAU+6?Wsc87oA&kQX_n?)I;=3n?h%onkz34!V49jyZI zYgdu=?PgT(=ta*JKZZTGxh?z|bd93xvJX`s*tn(z?`c)o$UW29RnC9jqRj-|K3k8= zZ+F9E8d6{*f!hcqHYkLn}hIm4IyA1LC84*+yR`8gD72R#((D2ka*SS5_uqj zH?=Yp{Kf=lYe)eHdPgu|8it#IZX*Oc|r_*-;IcaZXect_C(E9fidOQ^ zwOeqz1?VXaz~3{B>P@{!C~$;;ro{j^0rO`*bQO8wF@#XQ)`EX)*C$0HNt>7u@4(SWAOWl;Pz{Bi+Vvsy zUJanPoKhLU2=^E6E{TR6CZM-y5Z!qJm`^a|$NcC#OwY3ke$a#VOiH>N<~%P>E^foV znN7%uZ%`yu1U_Kl+3V;v^KGvoSPg;n0obnvV5^`A@Z|ukmx8cf@Bs$kx$i7j1lFwh zJHP_(&L#K}f*c)scy7>=ps2UvSM3sAg&n);+rQ39Q&~mL%uQC zt`n%5K$U)2IqhuaeppH=7bzF~@LcAUk-5TlixgYo*2@Uwv)Hl-PEd2EALjHReAz%K z3-Dx+?>0A#J4oUq7xrsS$Vo6FD`A{~v|ZUo3;TRXf%cRI&~wSh_VJ9=*pnydwmFfN`eG`<7e+;^>_b_tuO2W_5p1>2T<{I zD-OpPag+pv!~v4fHffbG9&^E_C-aM5n9ql$`>YpTgGh2e0gy>k$d5y8kU|K_ihZpo8N6==s!g44^)Rma1m7y)Dg>y~X)&#{lr zLgyl&Ahr|viMMb%kpy&YI6@MdoLc=XK2rfJ_LK{4>{-)s4{Z6PupAmw2I_Wq!@UW( z@wa~92=Mg6#Y(?z<$p1h#-X(hXx-pcI>UM!JR3=13$SwWYNdPSsn<|H_clJ$b)hJ! z3uj0ok0g#Jw1o*cY6ECv8VrX?AV-8%RucC78&5ogIvsFWGvWk8=>PIW0hYdYLxF>N zj}#@g1Hm@||6j*-b2apGf0@pX^SW-FBZ-2<4iYdSH?cij1MR0gFc7pk(}9+KE_ic+ z7n9d0qbDCWAwS-#SYJwVAt$bS0&FGF|63q^=?SG*#e*g{z2R>stU`$7ZW{e~Nn>z4We&AQ1vznfX<8 zEx3bGuIoq{LtU~H4JiY-mehxeq+XJ+l7J;#3$%BNcGV=zTGE2%jDG01@Vdofdt7^3 z8O@ldM=^6J1j=+CoYt{w$B)}j;)~$?60m1pxbju?KMvfQKLo>k51NzR=qEX2$}nnE zT&N_0G7^w9oMjXWNgz6kcW8Q)ueYFTqYbu5@z7v4$E6UspmV`VuO^OK4aNn$bv(+5 z#DpWF2s{RuQhcxwSl<05c7xJ;NVIC4KnV#*3ZxFA1h$hv?hlQq+H8a4jR~M9XM7D~ z3)y)Ijmprgv70A>iSF5lCN3cd6AM%nuF-gu6!%XR0jYr~0r~OqygHQp(Tv`=CVLt& zbSlAsKg6z|6opC_)|o{8Bwb|vb0mgGcvE^n4G88W0=s6`;NYSr9MC%Pe%$G((Iqp# z&ng~)l^wwQztXRcdmRHzi6MCeL3;SYoL@)Re{c4W;Nv@yS{7k-O5c55aF#`Q9|2M9 znOldnc@4--u%Ln!UdZ>4=b!bVyCeKoJimEi&PRP_dHmpqWmlU2v}fA#pU($T@yf&GOSEl0ROc flAh@R?|uJ&?Z52+XC8u?00000NkvXXu0mjfIYe<4fC58`Ff>SmG)PNI zhjf1j>+yO2@B90%Ymkd!*k_-$*Sgoe?zN5~>Z)@1xKy}kXlVEf^7l2-&@gYIpPFA3VO*-RTC`4l zTz9#6d3bj1)6vi_EEMibX}RdHo#9lNwmzE1H^88y7>+E zCdW2>K^#rUbJS?K$QD3IYTGE9gk5CXsZ+j1`#9S4NeQE68OIZ+C)%AWQk)+KCpX4d zHr$6eC->Kv72QVGqdSK3-fI^;au@p0b2M!(aUg8INu_=E{L%W}^oIlSwC}|cUWPc{ zv}hRrdZ{aY1xXwj#LGiOUr7+6q5tcZOK&^n`ps&zWB~^%wSsXI+XwAmFC837h@_(L zzu$y0MfQTyj!Y#&@G&LP{`KO=oahMBhEy-(IqU70OpctcTp2m-xMFkRJGuMl;kEw% zSiVaTy#RUGS?B$27$WAfcK(osW=zNtHaFOj86k0cI2U}oZY~01aOUd#S&*UZ#=kD5 zOKa#Bo0OVXp6zUv-}$y5RCrNRMKFC7@{quWy5?YhWuJt=b8V1i|3&o?H}_d(+Qq1d z4Qi*in`LTiqUs46{>^{w-S8ET`lmvV$g#O;paav)sVmX*U|9YK+jX%S`M7fz}~AbLe0u}du)+hQ=>UH_<5c8%)v#I^F?dxrrYVEJBmF3+{Cn}Xu!Ngw>ZJ>6Pq3Tn4YovRm? ztEB0Lp+p@xXXZ?+`q6>DdxTQuCCaar6O|OuP|`TK?f=*qaSH(I(AVL!6-Zv6zip3rjqQ$CsqgzPl&y@D}W6oAH^=RJq!iWQs z*eMrSy5$9WeTi|q9#gEMB3(sfuwVb}h!sNJyYv2PJ;T%5 z5!&pxD~zpr-UeTc6P)gc*PWm5cx`!$Z&n((A3H|WteBGP9e)R39AD!;yzo5!Hhq}v z5AhgI{U|;cvkEda&*1k4H2Ky?r5ZmFKUckrcr+ zdkd~EGko8I0CSzBPr4&TowjUxPRDJIW-g9m1*i5#BkFbs49@2aF5oE_rWYgPtE&=+ z(IVdSG^ZB^JA)sm_q#GTy%x{n#pm?ty!L$WRLy?ISXxP_rB6~rvSXu*Jvp;g!o1}e zSd^iuC(xeAUYHhO1_r5$nBe?nk$QTPggE4qQO#`lblKTd=rHnhbiB@Z8EeyH?0a{Q z==Yyn3KeH)!!jM~dm7?R8rx+Yrj{u3~8~k(mNKT=sUog~&g);FmUJMrEXZ1D`KG zcHTU$c_?ud;k`E=@3nKL%`*f0Nfp2B@nnRDJamv(Sy+EX#p-BYpA z)Z_IM)-00d>YY#0Xt`49bC+D7`~bodDVC{|PaU|*)Z67KTRj^!G0-OINBIw07JbE( zh7#4?6Y%=pkW=VYoAL2LN~33TSB&6nP2${$TU=C&6s-~Z!0#d1ri$@m z`@t=yCo5#O9)#RwkQiuoY`$zHzfPgf77kwbwci+R@mi7`8g+sby6sgryQemNPM)^s zOLHD}v42g?y>(T5wWe-+P@-mTGUBWpuyRj9@JXxxBThzwYZz9d)s-bl^+{! zB*0#Zg6GU1+4!GdiTNuc*)WC01j{76)2kokxC8<`Q4c=X#XZmMzmk3ZlOW3vopDn#5{~t*c4P*PnD&8Xg zoYH|1^Y?N5&Yeqef_j{P%=jY%x-ov85)IwMWySwjensW z7$hPMlO}h&DNZ!90Sbrawh-r1mOJz2ZOkUO;r){;6fxMLQhhpy-(;G&G<5aMEQ-Ox zr-d&fIu|ZtD`W9Bn0MmXIg82iUc@TBE|aQzh*aA6q&Ky)A4ECVlVp7NnX-#XVr-uA z%l8G@^kV-Q;JTb-9ypo|h7&hmU5TxEB|~Y-n4*A}b`aFwlOM@N0lu!PJ<@#DQs45J zUd_|@IQICJnW;2nsgLfD73)QbatA>#m>N*xga!+a%A<}jgV6E9`P8cD^#eZ3rpCFT z0QdavF4<&-D+!c#Jl$U{EI#$Rk%R0Y9oL(7p)R;J92B&NJ_nNf>1~gvJBx$K@#HZ6 zv0wr&zz^DFPdW1WWiRYpy8WJI>&Gg`5Sde`Gt2Bhm9eKY31q50)sv(SDox^dyJ-(+ zT10TfFohNjmC0_h8#$&c@f^IYd26iJulAHz|8Iy!6qAFv=*< zeBAS56T3mPw;mB5b!D1oR;?pvMUsb2NrysrkgxKq4gn+i0_0;JH&la;^Xd@DQQv%J zAsMXePO>cZhXo<-Vw^3(;o8l-Zx@LRv*}gZjdE|T;L;hJVIUf#b?A8bqF;CAA-Ajq z--Y5V<4VC>&yaR3S?GU!%=b0UZa#RIH-q`O2W$3@x=On&G%8<~@F7*JjRpHh^mvFi=Edwu(G<%oF+#^R} zh+#}<)cyDuUMYhLEAR?meTdd|va=#-#eXcA078#LCdAog4q1k0$?h9e(4)!~G#|OwpPY=gRGbJJsdUeWj@H+nYCeWq zOPQ(j>CEsxZO9CADCd0>hWaPoXqgfHAhc`$#K%tn$3S0#cTdkX{mrp}sO}9!gJIpr z`p4}r<6aU1L?sl*NyG8)I0M$%k)llpb5(!!ctKmFj@j5yt&T5e^4IB#ppz@Ub!-;l z_67?O>JVb^zfM3RG<^T&)xPw;tu)>f!J5yY+eq+K5I!bZfTSkmMFY)Wd+?F>L3X`% zY<>jMhyVD1Dg%jxta)#wmgH}mE~#8E ztG=)Yx192<9B-T^-WORQh?R}GlNfdeE6%1i&$`1D&3Oa;n^JyFdx{^jV!LLuy?B+EE5=e-{FuFw0Q0 zHJg?Fz#kU(SVv56LMS|rC+Olk&x(86S1_;4DmIPDTzUHN@s%3NLmolQggT~irX~0j zn_7XX#Y3+4%4fPIb44V7oI?&n7ixEQcN2Cq&~&X7Z_r-O^YzmQ2Bcu&{6@>53jKyG z+=op;0YNi6$wV*3;CIz~K_6>2-O7*X?mLsr7OG&t3RxT5 zb+(#Xi0;1@MKQUkyK&Mj8tst&GMM&*jsx~L}rN<^M2u@!K?-^CNTg2KjrkW zLQ1QQjnOzSxy*1fxbV-(h!Fz@dsA4~VMQ8q)U%zsm3y%!-bNXNV=pp#UeRCCGLc%u zSsa-E_VC^{%;a2KuGm#ODTv3R|Hpuvu;wxt)~dJ%*6(m%iUP1_!oJCU;z4@C1vRk4 z-q=Ztao1ZSV+OtlH-6&C%;P#WxmpqZ*pCN4FN%_z#$UBmk zHJu^^mPrV(_;D0L&`nsNCPe74tLAkcL|!kQ zg9;2eqph^59ECNhMqrk$f}TjY@clpzAQTT$*Jl7AV&w|+;9?!z(^^9gpc$>#U zfvxV#?eRy4gPiWkN_kiE4?fO6*#i9z8A%1&O8uy?(1{PCABCdy3vFAAeCz260hBm9 z*tjkj|NYZ3%a_HL)bYeqj?FI9o=2a8?&a}!F~zPCEaTq*M_VDmqSl2)t*dZ7suhmE zEsJ2OpA8qvbgD5X%1|gH!aNQ4ro*LTRJ9rit~uDeAt>lId|S9hKyYi~^SrsMIaigQ zh7LqWIw8*@q5Z2~crDqC46n_Ej!^^&D7N1FB}waKs~gEbj`|csUzagP3Svlf6;lY^ zsK)qWN(?Z?ZKznIfZ)o6>QO08no)-yS!ISEjyS|6v4Q0VzI-+epRN2Qw8_vaqO{a4 zW^`_!`9O$shM>66OTv-t@2DywMm%)l!h>ODJSXH?f-jsA1iZN^A$Nn*#G%p9Tj`$_%_H@VTtvyRl?pPqoYn&s&j-=q7!1lEm=MC50&s@5(I zSQIysJ2Z@2d-R2Ruh|qB-$zndpt)@vJjk{i&)>F zaV@s|_Kq(iS+i%t$jMc^mNZhgFv-aIMLr0b;QtO^gIf*Uf9$%zzpC#Iy2}(%y8!x2ajV5!0_HF9YDPH-FuV}um z^gbsD!d$g5pL}p~vh4*y&DR0z!}y*ZFo==;RhrB0zWeR5kJWBHX9QlW2d9|J@KCNw z-9oV0o<4N&FA~m4vK8^@>k&KzYZ%WOnGfDLKUQ-Fgp1Mdqjlc{)0~>DL?(>`r{#6? z3v42hh)1C^V1j{3b!I4U-pADp{og=}PG}J$S)q&$E&l3z{3*$xnqWo7;AGkRWWoC& zzHVz%eCum_krj)Ym}184R^LFGRn@mS?(>5>c~m1xUuw>Ez6vClYi)cR&EXH~2QeDL z#q=x!_TY3Pn5F~|5V>Gh^a8Ew8TfcxfJ==Z8P`5X8e+Qe@DtxH-H|r)z&`?Q1ENtmi95DG&v(D4omSr^?{ZHE5-17a-X`th_V9)_) z+Qh-v_5*JhK{10F{kxVm1kZ^z8=gLO@VxH-ho_mzNs?Gr%3nblJJkdvozSZ9I%<+$ z9K*kPjVu@(TY2xfGICDDfObnk=R~0*n<8FA;p6&;h5EItwf?=JV@qPs-Knpp94R09 zU2f(6=GTi!lDxy;I1Mew5dEH)r zvICFPPf&O9;`J%D7n6>s`?oCPYs|rbb)PE@9ymv?H9 zdc;pGz2=s)z!t^ydW987pM$6OHwdDTh#ZcVK9HY1HkKr-XaaWoGvXMpHYV$w__?Lh zLYJ}wo_i3(O^|Sk;d-X=Yhw({aOq>-)sDozD*PJqFATP%SeLcD)-9KsV z192fDKKRn!hNI};@vl66@X&L!>BFH6xrloNMml(RvQNarb4c8DQ(#x$P&P9X>oUV! zrH>5z5bb+y!=EY_Pkl`u6Hb+c*|g(z^vqz#Qtje9&4n^C2r^l=-f=Kxcl6=O@)V11 zw#!Z&4XuXM2^(by+{{td`W6F=#tLa*=UvU7<@ZsJfpxxebtN+ogJ`6m*q;P~r|l9s zyt+9{cb||yV%K7%b_|ZjFVkJZ?JL^z%&`k7f_4o)g;KTlQc&(^=0UdjReTlZ?qWt> zz1*LfJngFw_Rji}kglRP>W&1MaFic$q?zG(sQ#%*S#e6c&S@jwXM%$Ph@jbN@Cx=+ znLd%YRFnsM6Q@r_-SZgKDz0#&ncUsUO@CpUlO%e+)jQI96)kPXdY_*L5!5E8F6%=| z=xOl3y;@u=%+%;Ittq3)Hh+FUS1C<%DG>EO4y)xX1BS+U2{yh_I7K>OkFPPZw|YSg z&iL9ZZeJ`+f|>axI{$eWk<+rmKDqN}V`gq?K_+Q(!eqffY` z>T^~5K-!+EEEA0RLEYoH@soLVh@!^8Axhdvi#|lNzG!v2X~-1c&jFRu*!T{}^DzbT zF$+2)&)E+d@Ge=;uBxOr{>MDz(j<%&8v}%?Y*)%=$AFU7zR?Tn(BY}(NU2VMhK0m| zj_lua=i+73Lb7Rf=im6{a)l$+TRzDKb=bpGKyNrnTo3NV7h|}zj^-S!HQSC(R}eqQXm)pL#N?O_ z!yDvh=ZcJD7=QPUezZP$+bDl6-qYBZ&c8z=x&UNfyb@cWO-8Y^UE+IpBJi;NmFfn! zlPOZNvZnZvBJEF!-GtZ&`Ng9EUjQ%>aMixataK#`hxgLZKP*0+#0TkIj(U_mkos!`}n7|2?n^HOK zq&I$NyIvu5;w#w_OK>lwRgVD1gLN4~AEbpxiNEWgi^`B}Szfn(D|Jo)MTeqeJOheQ z8MQ~OG{5;A!W2W6E-IF?f&jPpF19W3Yt9~6(Vr%ZAtna%Gv7VjPXn|*dvF%)FdngV z+&}w%N5~U`Tj!)vs-Ny5K;15B$c{PTvUPJ9oPB4tLG{nae6bUKM-GltfX7z7;wvmyp1>98^sg7|5HN^0O{#_7%YV*Nf7as=z2<=&C- zD1;(cK{ZWdADjcNME0psCsY388%KxVqfBZb!5H7-e0E-Rr#*3PatlmNP_O+j)hlP)if>vaW$rA2>8&(LMv8BLy+8f{~*5j3R=xsbk7?{Q>kxv>Z3?S-* z6gOoq;W|l#bi+AKC6C{3R{!|XjgaNs@qF*>XPXYY>_IezX{ zl2OP0!S8*7d=zxU(!u->PWTm|c4l+M1e1=_sh+_Gc+ZS7D(geW2QPs-^HiOV=L14i zi-jj>KYoQaxw+(aCSQVQfrV^-Z>*nGVt!-R)7MdXorzEuPWu{zb&#iqtng3c31cv8 zNCg7dP-aPY-|&v?LU~`{a;itZMqHEux!{7_zzKgDcWg29r8rQ?m`1 z6|{O&ujH-f$&F~=)~#BK&Rf7}Qi{lae>bFQ$F0@DqjinSl3ciZVhKKR?}105)+Ci| zLSAMQ_JPom4Cp|LiCei1C9Tt7s6>+AzMZ=(D8keUTXq;)Sfo!^R?!ev@+hv=DB14% zPVvsC5B7m|<4oNM)g#=By>oTlZB`qF>%6iw-}WN_@MP;5*$!i^k?S0T!k}EjGM80tVRkT z^DtReI&6O}Z3eE-uf&p@SMyKx9H|y!ZmLweDwj&I5lFv{I?4a`fGGhMn5R)e!2X)D zorB{s*NE04>z5zI6L8*WvMVca#{AjQms2OC=2=Xkb1b)+pmr7uMyQ(|usg5RUY`?Z z5-@mBIeBu`PEauS>P>|z8M5rnSL#(L3*nHzXlT=cxV&FOoioB@H3UdWEVTLDmW8B5 zck{r~mUsQP+tv4C{O_>Q!A0V>1!Kfy6&Uzg-eD%BRK8<|*r;ZFicQG3$AR;?;+l-r zB|tvS(tUht$5pJKOdj~N?UXD7Sc~-=1&UtO7u54W6;kmH{KW~jetj9&sD|FTfcM5i zV&qm%zj1FN0b8|22p+)!HdYDo*JWl5Wk?Y%_w|zT5s+-Rx-xHjf2;Sh2kBJ1wCEoJ+VzqZ6e>>%wo^aSApQ!_9AVFUDkujo#%@ zIvx6^m<^K>ReER@)N0CJIwsw4cu!;dY17fp=vu?-q40~D%60X`il>J^D}|*L02;36 zfln0%1xsAwLT~qF$HBWz;d|jGmgd?t!YcNcWPDdV*OMSM)-h0oT-)+WID82S_P`Nd zM~C)xMp~#tSZy988I|a_5opNUO;KcM#wIfOZ1~vx=rj0Tg0pddGP2_A^CWC;VDSV| zzB%qu+ju5%qR8408h55a7)-uCxl^0ky5BZd&-4nUQ8A$8w`Ffw@+FHme=DN_Z?@`8 zfCQBxowhrUb@n<)r`#ZQ=xsW~`#w5`OMFv~`U!8+FPj~Rm*F2A-I9#`hgmJ2=nKo< zWWyc1ECO;+p{R^*@lbNju`&nid_QCYACy-xH6-Y#U;5N*VJF;gE?NaurWHkML?aRI zX%;np0>|s?(1Tl9cX2NZQnj0$2Fgkh#!X}@oKIy;l?taZBDYB9N%@Nq$F@N7WSXGG z-664=bqOuZjd1fsSVWV{pKLLO1HhO1Drl&H9xB!0E8r$RZnI|TIMY@kgei}faE<^U zyY;Ni9#ZK3)_8VQfKa)rSBT9;|v)G1sF6vllPB{>NwuolJKMkK!%s1L(Vz<9qn z40c+F8jZga3*M+bpsm4qe$-Y>NhkXuhEbiD7y)J;9pL%Xlrlua$X0*fyoXB+QsCGn zA=}**s3CriX2EKI17|QCa|tdC!{W#JligqYupyq?)zDiDonZ)`aow(QeLY2p^-N=x z0x!Psa=#qO@yX*~pIv_1TtasqKamm87g(bUAsngu=p6Co{Xl*pie-S zAp4W>xEtI30oyD)%#{a?LMXpvx^ueFE%TAlK9mfoF0LUTn zoXbSjDzPP}o4bD>7R>t6FCc#rD5eE z?dhg{pq>Anph&zvil&uEW>O`uFxek8_M#eFTZ`?n4+0wfLkJhraW!Ua?;hq*=lJVR zH3#h;sl5Z=)4wa2(y0>Sz&lGgX?QpPo*vnKS=p+5Gl*6?~x$fFLfOfrP#2Lpc==(y&%x1duH!$;>fJxLcFT=HCS#3=m8n~ zEZCIhKBVHFz4^w3quxN`RGdLqhC(K~Zod3O`8!31Mi$g{Es!S;j!exmCH1emmlGyC z-XrflW?_VuEpciqOgQH*JKx2iY?ncwn+qvGQ)3DW5CLeIhogaR_{)QN*n z6ln^u<5L#YoW_Os=C{ah#$^(1m}ldfc9`*K8(o=~@#|rHc9?Q(2(w2RyCos7Ls_$8 z!^&5sZC$gzb>!!Pt#4RrHNPM+kMfkEh%*oE2#nnxl@fNKeXLwhs;IHg3zMb5XLSs8 zul2K%8~GfGBpe1e!W)Ier#9=E$mw?j=QWwnGC>sWrn7U z^k$3Zc6Wq*`&IVLPB0u$()D2e$Ks^r(m0A6rlzqpEO}@k;)i5Hoa*)w0dk|?Cg{j$ z$P-yeumky=am6Z;$l2How(=^Q*sCHi*${P!RuurSP_wUYx!<~apo3MQ(W}_JO>&cw z-D141nqBl`K|H2-HxK#9`qQb;~sc$$=3 zPs^nN(=fpiDN}p(QQ0ur5`yRz`W&#mCEM2KD*%V2-=OH(*ca!-S;NhGvl7#hk+(ja zmqCcIbY*bK7TM=a(i`kO3~}$q5-Eq&I#Kmm#CbKDinRz?C@nr(VO0UcwL-UHdVcvO zD;|Z7*XguxC_QwuPeIQwwxdgmB7UL+^O=v;oDAS94PWbT4#FH=Wzkt5^ntwrGR9C5 zs?kxNf{Zl9=l@FjFSH>=y)4dr53FUGu3{N%r~>(gKNp;ki(Q8s%uWWTMs8o&o3uWO z9so6Zs>>8YK3x$N3<%}ZN3RfHl)tL{nr~M)!WobEwK%P|eh>#U-v@f~^yi}EkSu~) zcbxc8O#bi=PzIZxnof+Sj2h)9@SJ;0Z@*9f$dhkq(uSObpQQ$1@WMX+vbdIt#yB(5 zZ~UghYJ-XTge^OuBpkKG7E(o5BKsMvZ^0P;;tz9PjFy=0aGr1xS#d>8>%o&B&x{Uc zfvxKt1Sb#6j>L1wV)?}G%|8_IKBX3KZ;J@VqtbQ-ib*uwevvDkw+-0h=_rivY`*_;DP z1PjCs`r;J03vxdSX?{1weP}6BiYyVvi|is*4=>hlkVdb>lZWIN3=ELoEPEg|{=I{3 zOkSh6-24c$hEs7Egx6bVk?~rxS)M`*0*g2a=8{q(Tjs#D4Jz$j5h)PY`@9UV<$PWy zDwg};CJ(t;_$Z#qFlhC)I0}!z3h0jM1+m3u%mT;@a3$6iT+~6xFxVn&FS5}^1VMw-3))Jd!PZ9G)0oUL?N%hD2Q~q}o z@?dFo7eLm=sv660x!;)d+>~-783gM2Zpk1z655|j_7?IG{bn|Xr2p_B^*=m_G%WY& zcObekV9oO)xAFg|`b!bNc+dl>x_XtmP&u2_w}SVjba%h8JLqdR1H3H3Ax(1UvQ+u; z8vOsIH~9M^RqY%}{7Imrk2=dU55Yc8epvQU^uj&rV7PBDa3|wo|@A^ zM8xowo??I)j4YsZtieSq>MlG)#>e+Kp>|F@jqm=5__}n4xNqB*E^}iA_}{tFI52YX zdTk*lw4uMOzotk=1>n)w3&5xS_p9jvISQJ-Z_W5?*2y>O2N#Al-xArsr@B#$3pjKf ze2zsxUaPm<+x3x)b(I{HsL(xZ&^0@zi$6f|n!rb0+`b(3P<^S+dts`!w}zWKVA z;N;iXB(RU@Ous4&Kyj45%--QmD{!8B&Qf(x3SVBFwERc?$FPL!8kZUGB~2%Di8-9B z@MJT?OyxrQ+1Ohmfs@UoUg2>;c}nsFeH2gsOv^2))|0B^=YdnN-x7BnfNhJjy4`=H z%`OnnHZ1t6Amy>=Jb!yglbqVX31VN&rGy+xB!1scpyVjaHAy5BbwH>{;p@Xz4K#g2 zX#~XQfJ&z4=~Y+mw^?T!{_o(gI&X9YVw;vEFWfp|(#vnd!C_{>Wb6Oe_2q$5B^S8} zYhHKW$iA5-Fn#TV=k($ZXyalOA{~YNQhS1Swo+a%zcV2)08-U0y7s}2MoUgE+12UW zXp#gs@r27|6*=U0Cy_Rozv5G0yja2j0YD`a8GesOURSJV*Il(ZLmtc{klMmoe;STJ(aAJ^e1 zwu0cfvFKY1>p3%p zeUDYJE`7BUTAEv`(9Zlr_1O0CJUUigptdP)cX|^LMBmE6E zh)W}1)`R$*Qzu$4K*T;iaX{}gkg8bvf!m)t^XP}fQ2^<3o<=xk5&=>#lA?Q5o#+>e zb}<3BTkqDacZ^2$0T@)qKw&VT|G5GKdQ6#xb&c`J6sUati^I}}J3;>&#Ony^)t6UC z$%BD$B3SdRi^!i6RR*Z-;bbFVc`e4uUNxQGf$?3%T)N!!t|JCCAgp-3^~1@24bIfJ zVJi1$H1EL^$p2LW@|H-bl-*mxU4n<8xVoInQa z1oWm5;P)xlDE~kK_0Rk81C+vB3K@R;)H+ZDF;w~Y-Z9yDy_D7Et#3{5YZ^earKN>X zc){n;Fwl=R*e9+&{)4@ufT6bNv(l%>o&F2(Ne_e4Z|6GTEy3?u7cBSzR<`J7F(UY7 zW#w0wqRI#Ts1wfWS)2Jj_cY_#vZr>ZpE9$+Hwr|Ron>%VK{Ir9YhOorzp&p=@VQZ0 zSb4Fbo_lY0A*zBt+u@7iOA+?@q~x6sCOoj_e3)#}*vKo9dg9;5jLrw_4$CGa_V5Txv$a6(O~iK#mbn!jq-OVgp{ zRJNi12IZ#}49LgL@0xUJ`wv3PkWc#XerAsFOKlqn=SvW*{cj-nM(pM-q$sJQEcFE6 z+Zjv+`{gF`RAu$S+tSDuXo2a@*dVC3&}4yd11Inr30;Q_D5H_+=5C?1s*t-5X7Ur^ zH46_NjyKBpUQ?TI)|{d+R>Q!ybJOxr8L0)e0{8Mk}BuyAYDT6pz<5yS^?k zWM@u&&df~@D^A1TLh(RtBsUjEK-kdq%{y6_pzqtv2&jThE4U-*GdP7VRGD&R&)yn4 zBpQ3peHlv}l2eUI7V_BT2Rgme{w#aIgpJkK>rc3J?F^UobWBh_c-FDmR;U#mV5fdNo+f^IzoK|kBKUd}yiwS9y=MqS9+wa1 zQsEJ|erBgCJ`%=aZGtDlZ@9{Qx?5mf$)RecE%ZRnS=Q;tsr%CZIP#&kb`@U(@kLfkg(Neucjjn>SS6(-cBda-#E@IXAs2?=|YR?tR**epz_uHHIYN5L^(1f1) zMl3+Dt1U~xF3E+s)0A}Ic@U&FtLTpF{)v9gdU$UwGO~m`?LOU4zZn98-1s__s65NX zDh$>?$ z@ch!_(jdasi+RaNN7)aQl~OEvG`u)hnZ2UH?6KKGEUixLX@xJF{fjz?bVR&MiF7e9 zdr%LVfUeD88@eM_3NqUJj&45)wn3vXFQvpIFgWzsYpNSdOQ|XUcdk1k5VMr+4ve{{WjR&I zEk()N(Ph1(-F6#kWOHI+ujb{F7BN8gN1X)7X`pWf0yB;8afpBA-fbiCT~9Cvw{x!dwTM+12-p^-uca(zTA_Bw_z2L1{hF6lkb z(T#fOcsTwdl#2Jd;mu|21oTV0S`h=%Z9b%IG^FVLHJ~L2O3DIS;viq_3gX6#BtQnab5bNm(@B>{ZYDyz_!%Ag+= zIL5(qb9LUrPuB%u0a{R%+D0BVE-5;3E@8!q;)AxXX z9jf%D>K;p3Re4{)Pr|JL-kJqox@eAv&H)qZ8_t-qHu(n1fJD^`3SB<3xcloGyYhx( zfP~18Wdig8{v%iTGgUKd_6T~|Wra?f6W3a@4`uZUdVJQDhFsp2Qs%Gj7cI{nAUbKs zgHQzQmuy)uQV1&bQM57+_hVmUGcfg2kTya4jBXV-qUzqF^?iuNX*Mc6sWB(@Dv*JV zSH!QM{K{R1i@p?{8-hL-2umW8FX>lsaxkzUL6X6NB;#+%x*b z&7ofO)JvG&IXD^t*3mcOq<<%wu+OU7Up4+gn--uLvVm%Q>9{st2Z@YJWED=+2;xEl42%+&d56%>GaiwCYyE{b*`}A0AI?! zwbOi=B`(38^dg@VtOON8jT{3l%a{LeLatc|p2ePelA%|~6S%1u-WQ6LpDF=m1R-gu zfP_Xv)7n8dL9&9GO*vbL?7{^RnNvEX_K)0E&o3183AcXO0^s0tN~w3?+3IY*1KW@o zQstp#VcpPzS0OivK*RfF3fiPP4MjRi6wPELQ!3Lk^B@Lc>teNUF3u%-NMO&qPJddf737lBZm?o3u zZKy53wF~tI*i-8YkLtrU(&>7})2B?< zeyi?fakbO{33>MD^VrjxW2Bl>9%3NY4Ey$wP}52=$F9kwj#FhmDkGtjMg4}3t36>b zJ1o$Vc~z_i;L2~o@1My2znL`+*)}O7dy4UL3xiMs%%}4o<;;B zbCc&DbeKhxnJmGh#=@UV($ew>qWlVE8RMC=f$SzmCV{M{uACLmbs6%*iI~vQKtet_ zKdHp1nz{yh^Ois^n#h(Hx4oHA+Lob5s~w@1+q2P`k_4sa$_1)pI6`T01EHiUdEjT4Ezr*{MQ7Y z3MyZY{P>O%)f+djeMx&*`ePnPSuYyp=jHpxi#%dVRLSP0Um4G~-j`381O;#P)I#Vt zL6HWc9N<8R>3rJKjp@2sFO%`JF9N_Yr}wt$NQGbzZq`K2=NiPCzP!k5m`&%r;$_*N zkUY?6_^V*F2nPyM{*U@nY7co3K$oS*cW7@Y(-KTj0EM*=3D4e|bD6R0L5Xizs6pSh zEdZ2F5@N{lN4SE~0f|=p7`5fMBjYQTwu~&l%n9ceg-Z9|D9sDT)0TE) z*2DV>xzvA6oEs*~O|gpW3+ya+hkE{x%D8n&cyjk}XNbHi^OkDV>@ajoguG@o`a!|k zx3jC#?U@1!--nUY%pWLeCbqAw?Zr1sOOH3kFIAn*SZ=;SnOdH=5L^lqr>=K@mIDA0 zG`VE1cU}OMNYEkBq|O0P?0{Z~pqzqWf%Y}@@4#OarBAKh>7-N{u|tXdkcOb}WqLeK z^2=rXD()ryp5t!1)M$Q{!TkTN3>G=?3D6|gdHa%EfQ6HP6;uIvjsR3;K>CQZOxO8k zeBfazW)0p$fVquC;vpv8WKXA^dxqCf?Ef@|hGBuFqOK2P9`r~;uJhkX2lR}q_MD`@ zXRt$op_xmDjrhMR(>Ftau;vm@I< z&^r&q!r3wNayQeq4t4uQW)Ym1MN^BL?8}_P34#APn1KcD)0U`DN+h;;2yqD{1P9xy zT@DOu+e!C2>fQ1k~RWcI=TJL5@0>VbJ#K!@O(h!_E5 zZqu?7dUCz(>h-zBj}L1`xM=*2E}b8f8K}nRWol~wPX^-f0~m@O>!bV0`Bm-}?ulP+ zshnGSynKG$SsDV{#Q0my3xT7LfE)KoH$7MKwpp)XiuxW6fHrQw&oXOU|A-s*>byxy z>hP{M6_SfiMb0Gh)*sbP$A%&VxL={}mlgAL zzU?g|TLn=m$`5e+9z7|P7k|??q8JFSHX+A$=3Usm0+W?n{5j4BtmRL9>Gl6@8bqog zean>|NJz%goBq*&np)GD1LTDr{-xd@Gp}gA!VJI;IFXRZTzMy)vj8ly8kNF;q|zHQ z4p-@cMmx555WUyTi{=QePgqWsIPXu~upHlRO*rK;HCAPPaTs@jxg&^tjeq-(AAnjw znMm|8@n0aByA^`f)sH|fQS3%UdZM3Us{GOo(Fxm#ekzhng5b9NlY_#F=`T(aUy(l1YH+eN@Z^_S5py@T7@U3O$4v~n-mw4kbfe49hz(SLOca|GT88|$ zYVPwgZrxKNZtaRkc#C35d$<$ep~Fc3O9^yB*68-HoW-vS#OYOoLc`6Goa2d0%rlup zdD3{QA|ce`PHrPtmpxlWRfA!sb zWtjn|#C#7_s3$2^%Pl$G2U1qkACEi&PrUcz)U&|lo8ppnC{FIX0R&I+dgMO=n!U4v zt8YVe@wZ0u(IF}u5ny++b6mK;J97xnOEUpAbQCBJm^LadS5ICOlFvv=c zr!J7}ma{&F9HO>PWlmD>*H(!Xj~!0otr>?Tk1%S%%?1@h^(3?pZ2D9gf0{S}=TiN^1LSDO9Ax5GnYRuy z2o>l|%Vu&Opk;nxpDq!M=h;uA zCh)6JNN8Zpk5-Co&z}7=T=(GHZrmMB#fx+kOur8GdgA!i=~&7x)AOg zA<{ql3n)SWVtHYK?|7^BkwsRmsWQ&f?vYcNA3Ww0-mx<3+X9V6Uru$ynvBN42Lm8s z%C8ufu7PxHS3R&(NeJwbjNsMp(i&Ii1K>rhbiK51PKH3=oH|(QaPYMK=5uW&p8gy# z2cEG7I`wYAxn%E<0R!eYers(*3>>6)TYq$@Wu4;_Dv^_q%HJzh?SIz1+o|Go|Z3Aj+bES;GJ+N35!c&7OOAgX9v~b49f1 zI8nP%r}sBU@z%e+$VTOFCU#GfGEQ*JbDvsT^M7vvgW)y^MYG-Z(>^^}dtbUImF?N> zM-#^XV?fp-0O;9M0W`m&cYu^bEChGV!O+ojZ)vPU#*!Rr|KVM|vGEd|J|lz~=%D-m zmQ$Tz|5Z*6)Eo>*hXroI)hLq(K(Nl3C+}=;a_S}D+xc?bjvq}1bl~0eEwzU(Cn$%H z`^NpTD-Mw8{ZmmHl9yyMK>{P&=Q*GbGCmQF0lG@FVgJ!p@@$#%Af8esoEHAtVYdou zcTbwDB3kT$E>_5bfY|`!S7+!df1VKJAp)jCwY~$h*w;tnP>o^+4dt2tA93FuPIdps z-Jop3u@aHlu}2Y^**kKOW6zR(BBGK}R#x_230cWFMnz=Ltc%Q;5y7l!P@6Y@FdcVd^OL+uPToztfaUUb5%K-o#ji%b!RhxZM=fGE*8;470 zD}RX0po%#t(q_S*6~xnfF63$Q@Cx>=ohXFQbcuT(%w{9)xvej+40xXbH9;Qsmw5;U zxUUg3cdHmZVUIDJ@TGy@&j;|{H;SZDybGF2j)8~`CPZkuzXXyq0np!WTjlj9?$Uw= zqCh0g`xVg~RV9Bm_cHCvc!*fSYjHB=&;Ged-s<=?^dKvf}?@|v^Oi) zSVf{ey;gHI*P!-AK#k2D950|f?B`sdhZMNsU*J(qQENW zMHwHOBE6A-=q?9zC;`UF#+h?;p z-SbH}>;^$6fUPC{0-;qJ?u^ZT^5e`mo7aW;JwE5grA}ek9{7_C4yMnl+U|UPK^3sq zcgm}Mq>xtSVxYQDeV~xaOWz*BX9N09Z|=0P=wL8c+uk%^-WGC2LeP?x zZq7q2*H+0wG3HrEAGx||MkBU3Mvt=iR`7QZ|y zw%&_D5nLD2+qo10EpJYl_4Ozz>uOAPx6A!k{d3W-${Hzl)oQvEKv0lh>}AZQMEe7F zPAVwj@&#>8B)+x33KvV3txS=b2WmTF>8jFNlhmg0vzZ>$-L>#WgGa z+ozD5+7PiAkaoGbXHY)zU2xFiNnXB1f*j?DOyk}CcCD-dA^N;IN@~L_05%(dr#Y3U zxxFG#Vpzb$PN$<$g;k9Tf;qxgDafLgi};+|;_>lncdNFpzYY=%jkz{v5*~nio`dpI zvsOZ8H++t7;A~lz=;`L6FwG{impk!8aX0X?z%q$NZR8(|7-boTRWCB|%)`532CB+> z2=7wiLc_4f_pW!lzjEilGBdHdc-7{~j-swVyGLQm5ko0HNkwe4HuyJ94Zw4)6Vjnx zDYP;m!+Uq^taTu_qEKwaNiWwd{I}+74z6zrf*0H0QpR#@MyV$g2Re?rhbc6KFq5p{ zor-(ILyaz9U;SakBR=}oxY5`x>2eEWM}wg5SLv9 zoW@xjVs7#Cy0!Ku5UCYGvHC4%;C?G$&A`F0mWiR8m;Iz!W$Ui<2;Yw?yToZwZwuc)p#9gBfPg3#aDq!$cLWiY&R@wOV+D&CFh3>t((uN_(?zseq51gTu}1td=7Y zOB-jo<{NKk>{so8pa*nVU&__O!F~sSXuZO9eM;9&nmpa-@iZfA9u>s+*`mwdx{BU- zfEVd`*RFHOFkNAsbMq(gsh9nM*x0(pziA-6%;QWYBNb<&oRc{os$4o2N1DE!x!e!N zoD-0S77E~Nh(wA8qI49$sS`yE?UbhpuSq$>{oDu;w0gKp8occ9GK8DQe~c`o`|y4V zxi2C8HOrrY+IL(*El%lV2&-SbX~P%U;VVo+DIIZZ494qqJup9mk(yXdiMr&|d;#nc zYm5_+%oO{C2Mamq(*`Pq=14oINWZm$akI|*spm_trpkZW#N7--y4X&v z>O|dbYnCXJS`P*Xrl#n3ytV|a)DC*mjOH`6vS?$tGEprltHUhyrSJ)x{>SByA|Jag|y<_ggZIlCROnH&q`XwG) zd7sxP=OaO$swG*rM8;f*h1?tY{J5U2@{Qy|CpRV8C$aF++lKiKyZyHv7Hekl(36cX zw90}gxKtDF*z8W6P{5zTvpPWt-P^3ZM|Rio>LgqRJRjB22Q533^6A+HblpG0sM3$6 zU2Pwwv9$L=-S1s0vo50OCYDl-wAZQ{pvfx)@MNJV2fstq)zUCjBdckYkM2_ezA(c9 z_q<$-cg%2x2GE|0G@fO#OmV!YpWD$q5-3zdAyFrmHcKx2{o2*1ghV+FcTn;pFHPvf zj$Vd$e3&qsr|3PZY??iO|5|U|4TUyQ;nKb`TgSW1mKO>I7{)kH)Y9*JFAJP&<^(_74YgoX$$5$1@ zP6Z#P-T|t4^W_VUbX(>p+2~IOU!U|FUffWkc|!dh06-kYjTtUR|AD-hYWO6kuM5M{t=vBT5#u)iU5dIS z#HM^@pkZ9vEcG`;Fa<{|5cK9gYa{{`KN~)Yvw(OC5%W|yMs~dEupf-K2{dL}UkyeS=maru6GlQVTg42H~xOklfab|FVU?K(OgTTRviSQmg z$NrY1AoK3=hRsPuTqG}v&LPwcWbhb471(IBW+oLRKP#Pmst!{RF+U28THt5E#SL^i zaRHauONyZXHwF$2 zr6>5vkfTfHCN2Ws@>dz=n(SGfeY#3R2@dY1EOaaXwCVWT=I|(~(aHQaN4e?O5lNM- zH=rmN<=Jmbs^jzDtM+>CwQ@68Af6l7nq|U<=cirJ)YF4u0U`uS|AXW)FHGSx|Ikr5 ztvbE~nC8R~7XVdI5wv=oyk#qi@ScY$^T&%^O59?R4~^L&CisafVY7m(ebl24u;@QA zq~9X-2fjmO^OSdS$}B^55fHT%dH)>u0aQky0=;ip@UTUWIXt&z&G^tr4q(p`;ar#U z)Nj_7ZQjl2_0eHVGxzWQR~$nbMCbU>j&~VGy-p0WPoS6!Qgymb-2HWZZ|CDaHN?!_ zHZaET=V`ND%W6jF)>uECgheiBf{h{9n^4Mw`+R~K;!1}>T9No08NulBhRdnQoeyu*_0`T}eH`+%-*(O2DAbhDST0^GA`&Vn;Qxjb(RJ z@T@zl(!k&+&3Q`9_HE)|ecIr#`yl~Z!Vpt=_GLdf|0er6bMljEC1 zdRGebQa>6;Qep?@bpF_wyyT<}>JShuXkl%ivy9OtYPU3=;edvtuc} zGym0NUAWx9VXJ=@NlZ&Ek8r(3;%W3TjcSP!A!YJ{bm?)Ak7r{>KzEDz2u2JL)plR@ z5DNWp3XD$>SDVWvDPU8w^W4YctA*vth1Cu=i!F2Ko++r(in+y(oP4$XHXKMp^4ZY2 z?o6-bRP73gf|mom^0H1J=R!c`Y33O1odZPs2-ehNsD!8VL2^+%f^Fs1ACa8bFWBwf zer)CR`1{n=iGH7LhQRCq^w?Jsm+m07IDm9k+kN?lxA|iMo+6L1`Q&lm6D5MMzDg|g z>hoSrviJ}gJzfq@?QTKV#0$-G_l#&lFJ6O@0%8Z;GGaw4G-h;8w2&^wIGaGXEBPFR z-GH@BKV49s&h=-{*e_TGd6i5))hrH8O}^E>hU<1cA{ljQ8DGq6=bS$K7h-$#8el>5a&KeXZX-qAzHn4A;B}dp-HWwFVe}B8 z0edlBkPu5sBZZP!swrg$4JfwZfMUB7)No@*dh$YByGn2xt-r+F0m0Uam2`jQ&;sEv zK$GhQ+H}aQ;U9##9H#3oQ+dr90@h__WPsq zRHZ!a+Xlli61Z>WZ~^PN8%ef#V4xQUT8|LC2c3oaK0gVyh4~!Ey#-H}2#m%p z;5*aT<_Ry1LE4VDq}M8nioj{ff@k8 zT+Ul>3Z<01b=@2Yj5ShitAL`D zAp&PpvW{hUH0(j?+OEA$R}ZWLd9!5!-UKo z#*{b%s4KTKT`ULmY3|{MBzzd34>mp)sf2UZMAM@pj0M->Nj|f!QGY#`fnZ%F(4?3n zLo12>9|!5UMn}kUSBU~fYu$FE!x`ed`1(htZn{BCt@3!s-x|qQOCdgVChOM;52VA} z1joZad1J$;cu-?6Df?s&IK6@3JxQ$ue3TVhT~R+__WQiP?IOU~x*BV&voEw)1i*_Ard_D_+ijbpx*4`2Xy_u=IXpsk>&UXUrO1iHh;y*c_AbtTCC6;9Pa3;3r!-Io0HbA=BCc6z?-+oJZlico1 zc(XZ@Gwz~O#g*dnE1pj5jzhzO1w3N6S!GJa_m-Rg4q|q|M%hJsNkkO84AS4*%DbkuQY6nMDz-ER?P~(f3x}e8TFmE z1X`;q>+^lzKhOk#_6YM8KC$XntefJi+paHs#h>#eo<-PUYsT7*{zLUiwct#+iS^rK zZo*z_C$?L}-8x~pBs2abj+ir7C++)c_eDORItwko*R3U4r)rOsga+TIhFazh76`ak zu>xrDpXLDknE`W|B)-7clLFfvqSboT2+BD!z(UnEIcHiMHpW_g<-DQ5t?nlk*RMhI z6*`|9ot#xsZE4@1Q2PcQNATZ|qbr87x39q7g4@yQgo|!Z7q9hWVCDGHH-Sd|IM)c?%V|1l64G{4-9%=tXTFQIm5RLp z&5_{j{Lh`e;s+Fi6p|}L0o7<0P9sp37qdH+jRBqKiV7Gvg}?id-4<|R+tY|B)9Ft1 ztVn*})o(Yrslx}qk=z;K%1W2;aG%NB^sh`_?s>d$YFO(BGwNqi6+9n`O-iimjk9dV zqw5RM{(CmBT4P7fH3ceGs%Xt#k!Iz`C`7?7h=AnpFwv_pBjh9XC?)HwNIpmyggK%| z$H#Sqz1^io-(GpN7NVfK^$r#9*Va=LvS1>;{Q-E0uip9SJVnRlP}HZ=GrL=_XM8R-mTJAfaYxoVIEVtqaYDXXiE z5F&G$w!W9zbA?svrqiUiu7I_Ggi6X2z`#uvk_uXkG94=G4>q40OjQsjkyYtwf%|7a zj5#lKHNNY{6Lybt1Xa&)YxO^IwF>pPJjm~BQpE6Y12w*ybYxBYMF5q^(H_9y8MvPs z(D5%@EUTIwzz4aaAIT$RpX4JTcC>jO-+0r)dv@I4B(BzR?7aS{=WBaNtqM+~vAr98*kKi=uxj=^Nl4UYt zWP5L^!a}>9+60jsJy@4~YJEtBD_; z*p3GV7XpsJSxAvfJQy)VryW(_P{1aMD9F6#-b2pc8{{2{@(D0>@-jxFy@W+i*V*JI zxQ{8tYk7yx`(4+UaY&yi!f|Q~w^V4hRtqNZKAY^?z}isfuL2DQtsZi!dWYP*_oKWpioj;v*r4^p-TK1#OQS< zndg|);ktCqXARd+IsURAVxZV6P#Q7wtm?tlSYRl#uOH+3H{x6cH!?I}p%%yB9E0~t zot%<`6Z@*QAYP|F1X4R8^GY zZ9Fwr*6GObtIlpZc79-LOn>PkSumtYGpQ+%d&<`f9dgpKEe97ltV6-l>iLY^()c^8 z8qrw!GuO**-}(EbdaS^>-vTB~vGGekfOc2)|g@Q@$su0Zmyk(`BYeIZp2Tx#J{!v&(Nd2946s$sM`U?&J`dpzv<-BF* z)kdK_CIFxL_Q2Z4c%0d`PhL({fy3gO$>&eM?`_K!bBvvMH=6cr)M&9h@LIJJul7wOvbOsU>?Auz z9r5lOYdr6~H>77}dym9=un#`LBSW90Azase50KkR$XI*~oP7k(cMWRBx83cb!9Dff zOc`%3BldAz{RI^(x(dI{i5njkUneN}Dj*s|TQ-o~roy_rprLtWu`3SCT!2 zJx9R+qmCRE;r;&krt;D{u!2i#+=*p*0XLH~?`R`@*iF2Mo5qdME4mro?Q0!9@yL($ z<;#;lTvjOtzsHEVWGU4(q9^1T3Gnu5$^~4bA?(F`K9a^Eocd|zK_)6rK4lB#Q#-x5 zDnu{+uF5SikDp7QaqjI8YuX&g1jYm4U>S;9RQ`jRI&+WT5gv9;#N|{#!dCHLFOcS>Hk}k$k z;MfxQJLCfxvQm{npYo06ex1MUHrT)t0@A*2$ltg`Y?w{INs1^|&>?J10v+&x94STS z@Pdd-L=4mY@OrAH$R1(}77%m@IES%yc(uST^WXR30}%?+BTxRfh>&*2J!@h@z_{$F4y zgXb^9OPhT-0?#eh>*%?^56x|-P;SW;=VuIR`aOcTuVT<-Z2d#Kts_{!&n)Y4WMDm~ zyH?kEGOEFjOD5PIVr#!P2ln*CYZKb%hh*_7pS6jWcsf2z_eqAw^A2B=+$8-R%zpqw zPZ$}?EIDljEG$?JrO&bip|pXle5TUMcnHsdO1DIi0dM8!?Re3g6q7T^zK@6!OUN zgJ=AP3;=u)H$m@ju!orYYd>QQGR^^dg z#S1%*PIdUvOPy>Y|8_Py$|+vi8u z5Ikid46Jgy6JShPo)D@I&hOJI+j89JBRMYJ21x2?pN@4I+FRgEkmO$g{}Ki}1v16| zv9$qXzXH)HUG~dWl0=@xrJUMGP%XeIx_Vjb#qdcOm-d7=Rydg#Id!kF`lX+rf&>Ig zrq=vVZ86Tm>>TU4XjW~N?}+Y`uP160ALg-@j|D#Ig9uAp!z@;d@t(f{o<>#>xB;Ho zg|h#qfS1bPqD`!d*zW0pH(Bc8A-g!?J;W~VrqL|e-?bQ}K!xOBwX=EhKZiF!C6y9( zMw!lIhU?3=?-HYMp3Kpwk~jwaxQmU7{Dp8__O{^Tbm&hz7dHUtLz~AeTi7h#Rifg~ zF9V*YhB^3h^*)UIMHlv9uR)ih>2k+Ua{rN|G2q@oE-A%DuFGVXU|4R@SCb5PTgKVf z$3ib14|4x=_Mdh1n|!g)zIlOS{AHqqfld&R1$9wmb7$iBy=^K@)sOysLT{Y^Yr#`E zyMBxVxY=Mz(2ngdZZ@|dM0}r%9hUxcmvhVB7xn_8?0}JW6TWar2BQL0L5xM8h0Jd{ z+rSy6?!0VFJ+5drKFm;(xOY%I|C6P+XCH(h^-OXX?X|oKhBe3=nk@V9ud@Po4MX1o z8yWk55UriokqcruI0ktx+S;!co=XpELk5JtOXLy65Tb~=qcG@GdjnADC$}i?HUH}h zToO^HdV}A~t~>jA8OkRCvu`{mSac|BX@MV%@v7qFC~V*$w?y^3_QZc4M*eO`Xv9O+ z(|ku-C9E0N^iU{>n?d{3s-9w7&7Fhxx8`Zh(f_e=I_HKHTb}vJe4exP!Ub4A=+Ut$$AP>U8L-1DLYinze09RV&Vf{&Mk4Yh5 zb9esU@Un-x7n$~Z3KWB?4en{%HFMbH<#@Y)B)q3fn1~R4s zxQ@ikH`Z<|97lHg(5-oaBooZ;3C~X?{!_r}(LZ(a12Tw$GQ;O^lUHY#8-1|19Vy<* zy|*^4laE$HIy5HPaH;EW#I`P8&cBY{2+QcHQab5ks~B)gd^>IV$KLX?5(206+7{%A zJjp_MZ$N5RHJ0i!Wgs4^4xv^fFCa?u{*z3&Kd!*7cH|n)X_%V+5?kDxVZ^;huO6k~ zG)IWQ71#wN@_2C>`kp!j+)Zp#&DE9z+D$w)lf;LjJV(f0)F!X{>a+xssYpzvKym%Q-EPtGX{N zoK8xt)eB=y(Vx&*uwf#>**{RfkbQd_DD-ys$EbKnR+-&=SISOQa7T&DZ2#Mg_w5`B z!sX6JJ6)W12Ixg|?uwLTJsdrTAk5WGO;pypRhm}}mY6L5J zg9WKd_#`{mqi+|G@b9rA5z0=s_UxB0X})VN;}vG0qVDFI#S`bpCoHDnb`H57+p%fH z%j|xifzAq*R#YQDDwegPzVu}vc+1QwVDWsIBuIOE03GW+fPrgs)gh7bKL`&cVA^-K zjDky&g%Th{6!V~++`(Kt4WGdV0tTW5{Y&2(x*FBuhgG}o?BwQ^SMQMt z%5fR)*!VmvvnnZ~P@gR^RyzNpl<2b;*mV&o0d6)dNxSttxIdA4p#jhr;@q6QIchS8 zX1ph}9DfdlpBUggB7I%bhy-dr*pM2-d|j|~TC23_m%H`V+LXb8_)ymgxftrn`}eXoih->S{q0uzPvo$RyD#tE9=Le-q1r$OWz8Vf z#I)SN zG=6F*eD+Qp#SrW^(Nfi;ipNVP1sYG!jGrXdvpr^W(C9qKgxWTMZJWgYM)s7^9O*6M zg)sBfNg|(H2R#i&w~A@mI_$JVbT^j`9bOkMdfLP}WADD+D!MroXXzy2lrKimjc+Y5 z@P0w*Lyr%pRh+5$m0SFoku^k)Ern$!wNbbJLxkfYcRo!RWgw=|j?vvrH4%i!n8A5xpZMRCW2EJ1G>oyw?N2euGt($fxPgAil%Cvu zOZrC;Eqy~DUs(MIU7e_A#lebr!E+hQRZi}9j?37W6zhDp@uH0*xe~ex=VC11Z(Y6p zpi$wH+ky~(dC{nRZvuGoG}?%=dT)MIr|#?@q;m>Ec`JhT^Ta8<%AN)KDU%(r;RHt( z=6#-6(tzn5k|l+?lZtP?HXEw=*Dxn4T)ZsYYoYV)^ELd=Z1xaAX1d~Qi#mK({O;_P zuD#uTq`tlml!3kMjp|~R^>0bv`K@Q-6}@%3?!DUM!+>8KvDe#a=l=RpvcTy^XvoUQ z`U;-TpUeVp2h)%pNpW0p(&QiwJNA%$$6kJO#FEV(=Vw3G-gv@L#JP@yi?^1Kl3^)^ zVWd81i7JUXZCc+1Bckdr~TnZr19x^w9MO{`zNjDbOaxZ-t~}_D9l^@|oxV z?Ibwkf9i9pKQ1(@0d*WVfAmPmYgO~AnooNXmBi;oX|@v$EiVpVuFIobA8RS+tKD^| zd|KHx$_1a2s^9W5r*dF%D#vPUW=uKi7*yrWUT&K`|DG|+#LKK9&Xnz?$!?APr0N}Q z#D{k?<^392N<0Q)6F4vH>pi(xI|w8YnL~#7g4K>}j$+&`r^_4PZYuE_1#EAIZx+>~ z8861%96Dlc0ZkF13CHI!L>%Y;Rt^IF^o#*nHW~t%TCxMeDNFm?wr&nH7wT_a>-<{4 zHu`c+J&Wvo>Nm3rZ}lfWov|h4oDtMhZo)%$bKUykg10(9n>2CSB6-79Wi5oiV7bve z<1CRc(JnY20=?ifsCAfq_Og(fF-mNXj^`0wC ziu$lBebV!f-+vG2e7Rhds@gG=m419H5EPb-i#Nh1yKpG9X~Pn9GgGW(+E|}H%*tB# zc3_HoMvbF*E`bo{sq=y)x4yz4s~2XuC~E22X!w*2C#)5s!Z1SeMV7vJTtTf%_rQ7; z;1E)-iE+5NGk6)NuRlDU(yvdYai;ZRD%^WTzNCQZIO+QeEZSbH=V+v2(&O+~l?HDw zNn4#8j|_Mm5aEw&5*~))T-z$zuH-QaplKnmjPdri*1alRcy7RczvnY4%S<&RA=xLC z>fX_+d%7^kugO*BP-+{CW996FA9%OW;Soh&^h@>N4D!{# zDhYlwv+HUbb(Yb0)khRQTi**;Vn^sj5PF&3j2-XBd1$rt9#I$-?g$-8<=C+5L<7xdadpS_yZ-NL-|9|e0G?I1>M zGM=7O<93#fFMi4xMPcOJ;M4grTILfV4kYx8v@t;wG6r5SliDI4P{l2Dht$3E7S{1F zRN)@ddzG0d_jL2BeaF^|yIwf&?SxvAas!@h#>CsITVq3~3%K@I#{V#=1DkT-#4a|>1j_} z1u)%C#xR%eRYV83efRQMXPo6Mp+*P=OJYEL879=As5W`gN#d-_PB*Q{Q*XSYnYK>{I%lW)nL7kXtYReAPHbn1Auu)@e*XEb^E^%pzy2m>>COUQk)Zn zl`vAbsTRUtsDND1tNe?bZ`tbas4onVfuH&V`FGi;d-Z3~$nW_HfSv@po`j``%!kJ{ zur0vL@I~gYoUQ3c{(^_dfbCSoNpR$+=gI4d4SLEfYGaW9nkMSMa;Jgr9vph6RW<4G zrT)Ucji7(vxlfY@gHc|0=_4u)9$50QTvmmU+`4pk+WG@e3E<2N)l$N0gSQ6uk{f^o zxb<4D;CHV{@=qsw9IB)Y-p&-}sDDV_=U*fzc-0&-J2Rnj2l3qg#IU*HjJ*-La#!Xm zs*ETYaiPF~m$T2ZR~uqxC~R)!1}1&I;_y|kJ@p65`}0c|4!LeZ)U^+jUHztt-H)hQ z;$V;8M<1Oc2WiF;>A`oAdBK^g7c0ONW^v(vY{{I4Ya?mGmSfD(HI2AmlW$jAqKOI% zBKv~89h|X+b#y{^Xx)%^Kd?5OQCuoris!eZUc@Fio{&MRmZo7dT7&;1rQeb(gCwHm zf()KL%fSq%hvC!utjl$MZdO}uris>Q$F4oHqY()lGs@GZCJW%M})3`fu?mKi6RfgvEWcyj$FTP zBzH#V=(|{%0`+lDh4%IV9s4wo11kZrfz z3G#TsMN3WTf8UXyjCY)D=Mj_+Xvz0*3bJd&yp5hHz_o3;&I@M4&LEYt?!y4#NpSTXp(tQE?c-O#V1T6@Cu5H9ObPWpF6Gw^?~qC!+)COE0uGu9H|IKn^`W4bH`tc|mT^Zx$6H^BU7=>Q@x z3@bQB?@g9VKBuqg(kYc?NZmc70wrENnQ9`R%P#ZgfO`Wjk#vOf-a)Bi-L*!S% zsWA&(&AJmf5?D81q}(ag9J@V9L#1afSUdTmIb*gWu`AjlE)NAHPnyAy9;zq9KanebgO*|_t(1Zj z5Eyp?JfM3K;#kN|hYL?&K2s6eqJFv?vLw+1D4!>91SF$bKEQps9nu-S8*1X!35>(< z2u}5=T&W9t=(#YOwmVk!Te})4?#*JjW&wNsNFp1%%Wg? zob+;PgxwXHRWldnOzze?R3@`Pxcx9~T+6j*aI7FEcv3v^OlyO}nG74#;z_40%`+?cwS_V^Nk_*Ha?{vb z0@xH?GjdsC=Jy0>Dv7g&ChH|j_%7x5!fc;P1W#>9DV-Y}qY22NZWD#Lrx$@CNGhXn z>dz&$7O^R*495{@p3KU_L7d*5wr^z^VW)qJUp#^}`AB7k+eVB(E)@64l+?*@9AUZe zMj$a3WujURLp`NXiwrAEHW2+#hg;M9H8#5bnI*`hF!QZb!V-JX6If;j#ZiPT5?^oS zVx1-`q+sTjdN1K$9!4;!Dmz23~xKqdyFusgMdrg;c+@L zuw4QF4kPn9orU@Cm(nt8Vjen1yok=q0-YxuchDNWPelyNc8$LbBNSs{PFH2aYadRxnt>UU5WrF4ex3h`r7VO8DDK|=fOBpA=1NT@roEx) zHKwsCv!1f)@QlVfM#k8&V?ST@hS&J^MG#hET0)6x+;84%J{SYTSS7W;w_L*&d5}6v zQ8Oph6@-dHq{v6X5;2FZjwXcp0j-#QfK{V7bt_rL9kswkcLub>+A0BQ4OXxc1jgn&VF78>0Y0{o5l; z{aLB9>$O3-5YR!m$*O#~i=zVSs0hYpAh0$j&-=wpJQc|NPwv*VDtW0IZNJ42i_crx z4Rp_tMSW8?$L!L4svi;_{$@eY%Hkkfk9PR)qQ@Jn<+pju!l1VZV?HfPaLGiC?brTwCE>_Q?MYNQQrHN3E60#ci} zwh-W<6OtfSMK864K-T0>lnPi_z1}gRu?d$Kt=*IAIlRgjr|dgJr`r8wBg%e!s>5G) z;Tt|F^QVR_I2T?yxgbqxo;rIvebxsUQb{0Yrh^UKPWl|~5d~Dw1_wL#DV*;4vwuXn z7*5)~Qmrw~sl*lwUH$C|(@%RON@NKCX}BI06RIyP|6fwC+*y}D~o8~}1weiG8?)TK@MpTL2St0j?~ypR1u``}hGa06Ka zdRav-J~Y;XI&ai$d2pnNfaU2IiYM^HHEoXC92)fF7N^3qSO3ZW0lr9WZaz^*3q^4@ znKq6y8|_Tyv&f8+s(rW&BIG_g?ECCUBs&}~?FuY80N{@{nVp(4m-qw_}0^|1aTtu1qgCR$6 zL*s^7zX?LEaz55#gTFL}j>AA>SU2>X+vhhA;|Jq$;GVx0HE?1mBGT#+ey{?pSy5%*Xl1+@PMngy%G=|ps1 z!?zo%;vgVzYex3V{;VMjkjE^5Mn6*M{B7<`yVLano=t_@-36gi#O|DM@rzwDio4$17ayUxzNx4bxfC9+(|IfFzLzc{4gcLjN9i z=dx@B1j*l*|t8J&V;0LMUrsL^sx%D%JOX6x_e@oEa(gF+7Y1a6qg-inQT7_RR!p+W4LBMblg`DL4=lz5Q&-X zf02jExfUkHvdkD(O+UFhSFAqJf=~^bW$oybhm;N=B7yK7oYRJPZR7rf1PP@F@UobH z=n=y(UOGKXYovbttq%D?56>bcH^n;Luy3so4puCQ$loL{zBO2$bV08gM54S8&oofN z+!nXjwVsDRfDT#}rS!tH2c`dSm5I?dqT{QTP@(pHdIFuMOi~_Bkpgt&hF}% zuV_YQ=F0DvG>9661Ac4ZJW+R6UVTOiieUnN65Pj400RHm$8WIqCjws{2hiI}e50Rv zSgznJ23b}s@#V~5{o&gS^R!L{dt1t5NZ!)6Am0Nb;u{^%uRz(L|L_^>*CEho7lqz+ z0B^tM3axz5G{`~U(-62+wR9-Z&gLC@yy3ruyia=YaVB#g6c3R5N%6C2WpiI;v#f@o z#K&`ZF78idg&V9dp|SqNWP@ZR$9|KMq*erQ$m3%2lun7oaL)TJb)}}6{$+D{iRR(L z;WvH;09WkUfD35sMo+$LfgOYTu?O2cJqUP>3W$y$0o)b!Rd-6xoio4vqjs=qUA~tb zG|RxeBP40Mm%b<0!g)b*lq(a%RcoOA$=)+k-{-0s-{1sMon0Vc<@c!0I6P&RUaJ!- zu}hK3-lGr-VLj{LJ93g46xrLwYLCGt^UFPus<3~R2bXxDF8t{8Z>?U{3?895_&39% z`}a!QaOT$HCfpmNq#j|e!zig+gW0L}O&$yGLro|UP&@`n$B<>N?C-QC$Amdk4#m%6 zI}xJ{E+3v5v)6KnWem>Zn`Lk&hpVJE0$qwaGNqi%y2P5L^92v-VdbR-57%EAn4SHh z%;^x{IVf|y!PgNVu0o&jdG&MnKaaIHl^7w@e#r8f@&71vBV1BH3*A&tc;*O@d;(vt z4OPG0!)FWvl7OIlwgBxltggBb%Gv$+&qC*cVrrU|#~`=P?}Gn&Q@&h*mJm5`TJ3HR z^*@m(CvebSoNf~W3PZ(r)>6J%DPf!?p5)2F4_^HvSZIiWs)Yo>m4DP``#p~W@h?8_ zo_LIRE-tlQvciQ9DU%LW$hwQ<~82*+>td1%2by9b5Xbx>d z&O&6u<9P^g7K&vLsV80_^Z|0tkXb(S;MiXfsM3CW#EV=is1(GEKdG-&b4x7wUhCfZ z+B5j_$GolVCaHzmQHuTM8N`7@rWeOX_)Ec$gF<C<Lf7OjAD13r&`R#*m-@* zCbnBQd0XYqikis*c3zF2GSfNgBC2&P?(f9r3Ov7DbktUig=?rJLjMkq3(GlI2K#oI z@7m07w274!WmOjxDI)c+4meGimg-K-65WDq{vI$u?-*b&Uj6v1j5;5G3JpEl3 zJ%+NY*b_0_=Kr*PSzI!QLi&E3Vhl2`L^UI*s$UnX2iw>Yx9%ahuhSNuO73G2<#$1aLb1R%7 zNeiI$m#tU16n+iQ(h)Cr*ttx+c-eR7Y*>e#0Rxhxt=E zb?NZ)y>Q2%uIQAOIC2e&Xu*k!GQPF_R!39;Vr^!E$To;QZ;1mU_F7CpESx=*>vePf zG{7&4Z}57$yhnSLriaxV}-Wm~$E$3p$wQcH@mmh-L+4qqurqGg(>* zP(DN7$A+G^=Dp6kr(GSd8GIGN*2f%?(P+UoZzANs*wmfR`l&AEz_{Zx2lQej=gHre zTP0Y{bn$p(o?ZtcxFO#jD0P#2Z}WBZyw$z(PML43Na_V&yvZ7`yW+lgKg#|o@Wft_LHjkn%@Jg1q6Cj&WX4C5>CW&}!Mh4&5E zW6MAHKF~F$?CT0W|k3WH)mZjfLf;+DA(|>5Y}$xYV+pE)_}Cb$dDJK zF=s`MDcf7^%Em67=Qqd)j!q}(M89<|x&zL$$)onvyOT9F_Gm_m6Dg`jNwx2aCNpzt zkd-ecD>0g#Q|D!W2;b4vtgIoKGMDp_-hS%8v?+P#2LZOz3`6d>(UhU%WnM|sib_%z z!Li{>b@+sJvq~b26ImbY*0~6GJ`-D05c>BvplkYUJC7vghRda~Bs`Ltfx_zbK*TrhKJRh$Sqk*80vw z=Dnu`hTD(1bs93jW@IZiRYU;<*u+^Qy~MiRuy>7QwiMa6(TgK$`l#*;qaNzp@<q->#(IP~2|Esn zA<@85+2uTNg8T?rydXrZeEHq5d!K@a%`ocwahH6g=VI?FN~WK=@a(EwI9jUzIYN@5 zHlEQd?~xqF0cu9@@`oFhcM5Oe$}ZYbGI?m0S^ z`9$n{ugkDO3S3{GZlv5yFTZ03PuulwlvbN>x<$v38SK-B_fB$6B4%8QMjjp{5TG?U#JspFF!*H*u9eS*!{0FjfLPdLJ8E^lz@#6o5 z-pvdK6Tt;HC>zCW32Z|&I=&3_RV~{BO^xl{)SyWAk-L*8xzG!{`L8D;0noT+?H_su z8=Xq|88cN9(r|g?b4$%RBT?3hUBF$Dc zlJ1`|vExy)_^@nKrTL2*ybKyFpf)~_|MU}&nY^8^3nJh3r~C4(+?dVG!9Z1sku6bUo=p7i|5g6@W~`Lc57&kdnZ~L!t9G7mugXv)n^hk8KV-BOoqZ;G$Mq=)x(N{pnf~-{B7igH8=J6ieU!l?PYj=9B-(L;i-Zy5od1w$kyXMa^R&A`i})UNa&()mT~Qj+IV15b*$Ye@N-!eLW7qyGXowS z0^~fyKI&%=?iB|g^i6HKVORJ(oC9O!>b*~vzbl;N6Y@yz0z;IEYr!(Nixi5hFh)^h zBC%4;By>Shl#X(f`c39|Nj9XH@y{K8@EA<%e??qW%;W7=fmr*r@&ySPc*P`9K7vj3 z;=2-O4kH7OU}YYx@<(xE6Z6Y<+lHOTyQg-NOO^-kR@dx`?j!NPfOtM{tBdd2D+2 z`XD@oeeY9|d2<+duvpo?eaQcam_1gV2U!RK6v*Zm6iD)CInw>Rnfspwm;sw&DRPp@ zE*Ks~>SnOSgTr~C8#J^~s};@VqBBCXkFMyk*3cb-Bf8K)rjs9i?PAKvTQKbd&ca)f z6R<-VT-gsoxc)6ZHcplMP1gWqKnrZ}moMn-OL)M4J5GSt38*TufjPOtPhxG)2}_;M zOW<`rg6wpAT_bjQ0jD5z1|rjH|F$ctdsHp3n2jNI6jz8Ac+OU;?cq!f3E&ZSlFbYs zb>mk5M6sXriTMqHBI-ZswSEsz@0~5pVIJYvUt}wngSdtu*IE?@p*iA9bo3jzVSY6W zzw<0gh$^&c*9i~(X0on}^rq=?U zX?YEg33IaS-+lb{<$?A0lz`Bx_Z1zm#-*7Arzlm#R6=TNQzip7^df6P$1z|UnjQCl z9V+)vkD{m21S_p95tG%-n6d_4G2hOx|L2Z#Aan;xW3;6rHc0!ULnEf`r~q>M4< z9ZLliT=RJMo&|ZD*FGZ<_tu{LYRMYUx-6z>;rw+vQa2ZMnRgm%Q>0F^AlcsWA`35z zFiq2a5gVH>Q9{B zp~&a|kaiY8QNC}#7X(Qq7Z8Kar9(iZr8}2e8ld`o2jzxTvsQd>5%pup}y1SZCiS#aG#8P;RCB zi?kd_{*Nkf$HsqE9Fd&@wX^=P7rFOM7&y`Zba5hh+SOCa-cz?`rY|hRqno&GCPjWG zJk*Ke3Y2}3%i-f%DKZ<_$*ZsiycekhbUH549CrN{&@RS)oOszN8GVs2Dx54(yxzan zV*oFimq>v4pKw2Y$WLG~vcg#Tmv3(m|Ie?Am2u&x4L67W#zX(P+A-p+AFn8ZX zJfsv`I{eIO{SR{Tq8ViZefVc9s-AakRl;4S=(DoW@HspmcHs_h*N1TT#L{^~LMn0F z>k?D#I3Dw70i>37j0Y- zdMRn)-9AJdtRw9{xdU>Duv%pyn!Kx)2Hdqlk03T-`W2qbL3)4AB@QnlXAkC-1w`Mr z5ZGR|3~qquP&F07z?_cn8QSIK-qXZ6U$u<3*U0E^vrFt2IPub5Hp^yTpM@gt69F=l z55S>5wpa}mEW46xEXewXuQxjIG9RuhgyHt-anx{o*5N2qw>N7+!qY}J7n0@Z@~1N@ zA~|qIUPiVal@~%O(!FgR_~RRY`rP$gdmsINrLbw42j)-iv`_0_BaL0aiu+r*llh;X zsk0TMAiEtTU)O^>LV2@Gbb+C4o{3CAd6x2~RCb?D07phGUaopgDa-g)I2fHMKC z0NIHCl_f;oIU^Be{);8Zk@w#i`vIgDH>mf{UH#|T+g0>Hc>tx8`dXk3=r8;;@c<;r z{6zoN%ni`UN({@b0_Jb_8S6tNgS-EpbMB<{cLu71tbl>45ytE%t-1 z{nL!amJoy+1}<74E_zIK?7o#7XI{_(m)<|kygNgp+hk0U{AU%Ak@h=(JJ?spJKgLT zz;U46Q$PIcibbDzvm~)rY9T3sRCyWxN-{6=WTzA0w&u5b9u1(dL=zsbA`;_In+S?o z+dG9g;~AXGp=ssXmz5@`*f*-D=L9aZiM?OY21hT-;{%m)e;P}9 z#JPf&Dy;Leql(!4P-+_7j4_i@PnXvE`)_PeZ?msryX6Sgo+pUKo zG^LoP46jS0k}|bKo#)b`Uh7FO(MCLNezcK7m$LiH;CMqiXnY2+4Q*`onG9mWyDapD zasxouEz6wZsE1oyitmfxY$zs-UUu#(S-q@EBi>P_6tg@l^|dAik)ZV5LwszZvA&<*|e14no?i%G?mNr z){hC0Q4^0j#z)+mo<20V+ zzV$?l@M#VetyodR8GJ&p&+zW4G9eqqefe0p;}urOHLHNk z!=Rhq{NQ&H2)4P0d~w?VGSPMlXt!kb44cip%Iijnywgv~9R|c_V!A{v#E~ryO=l#K z`C4U9stG}Odj&f-rFcvCQ>+{J9Z*X$$HO09(pNhm%~4QQoXNNihiEuZQL)c@HNG)4 zD9IK-KurET3y*DBs-H+mI2~UG9Q0_(;&E7nG#&!2%d8{O)NE~o>yP`!FT+VMMO`83 z)bg>VYvOY_F_JI&a(U-m7)Ze{`d0bsaIunWGvcflzs6?WOo+C}s-z6m~0lQvh zBR5(fNga(zEj@n)E(kc7pu>>wjaP$>a zd8f%t>p#3oy#LyVc5w)(ADZIjXqQNn660yP+u1D+# zir%G_#OODtmVd(c&2Fp{;kehA?}1~L>SerDy1}@^;~0GZ#q-o2CIxG;*$yZ9GvRXn-pRuHmU9#2)vhKm#Hy?+t0-B>WKss)8 z5?ZE1CA61Qk!y+D>f}5wOXauIl?v3$qPduoc7a12l~C z?(@(hCofzG>vD9Syd=yb*!Iu2x`a?T?W4hty82e5PsmlZ7xYcUaN4ar9RyM5z&q$C zP4XjoK;%o`F=+9DKhKm$XXrDg>2JI}Gg}&tS0!Nh0IyfP?}=PB$ji*dyvf6o$3;P7 zY+|y=E_=Y>D_9qU^Czv=C$`SLfh0}~BA1iEFJJHn344SBTpq-^+qe`*)*t~?@Ir=C z0XJPXI5IA3k3E^tgv}_c!X;6X5m?EA*5)Z$#D7yz%!id?moR=Rs`#nyuuJ${D1m3j z3p0U?24fRfwZL4p>e$2Q<#5*T5TI)5>Qi++;#cJNr#CW$d;^A#Ff@4<=9TO^K$2vu z64tc+L@(|16*u0p&R-cPy4G^NW+X= z*zn+z%xdp@vN)S(x`umgUOPZAOP9|Tnc}kaR?-moSXP-_RM}5>vJ)yOax=OUUun2l zF|(8FkG>tELWwd14eM^eMtrU^0<~Ht0S_X@6v^?OzuGS@VcG0v`emKAJP7goo!`HJ zx>R%sgi&Z=Y7ZIiSwz8XJ3{FYpR%TV<&hwlue|1o@;i{a`dL-3{Nl zqQVHroR~~>s#|c1sp)K##yo+_Ihk$rbR|E(kd4ub9JPM~?R`Gj3$ZFVmB5ZA<@2K-cXf|xTC1gAM=>cW)ufi83=o3s=MMNm7 zU(wlpRQ^>#UVZeeK9{%tt;>c)y57dsUJBM?W*LzowyR&6lz>E;6EIVBr_$OH9S!r1 zQHT;v59PT#ZAU+15D(A@lH{>u6RDL=12&``kh=q4QYpe$c;{Ro6)?bUpP}591;nTS zI<;|u@942&JD{5O7_SV5JGksxG>K-EBNzIK7Kq6yuoC48zK&iU_E(MY$i_b&X z_~B-zLsp}a0fpIE zL9?yPxjGgj2{Xdfr-3wX(ken+ZE+I>7-}lXiD1TEDb*~HOMBXc`2hJg*5yF@(}Tz1 z5xjJ;tifNR5-u+|ep@O zROB8u8)+aQwwwdqk%4gu*mnP1qg|_8VzBd<4sQux4t$T>UWe-%SRF`s0iMxwWazw}DjU3NJ zSp$B~I6j*h2h0U~3$rZ#&{zEhDG4)tC9~;kA{nl2a8hobf(*PUBL5qps6q?<`EC2U z@~@``fLDA_eTP>}dH(3HC=I|2{F!@UW=XgiCQi2l+D-TH{3LGFlIjR`ej#|Znq!4N z`T9FBRA!NzV=&mG$BcF6(!}?51WdIKWiy}Su-k))QV zv{~5}L9<29?uCC*-YwYCC195lj9sk22;y7`itgo@O`yZ8Cs$c;{1r^8S7Aa@Px$Uf z%0+^0Aw>f@Ne`p8lPmFlugUx^V}3jg{_Bbv>~IhNL+uog4S#QS#-JtcYp zKoR_osVAPR2Ly0BOL1y3M>a|1-i~M~m*|jmZ*M8|{Q)6Z%mb|M($yi*%KT&%ltf|P z+HFTcCQ_J!(RJQ5M9@?LIrm!2$V!Ya*q>@M!Y&}ZTQHoQiK1d%)D5RMz|8(NXtenE zfn*J+2EH4PxT0*i-^UalLvUOT!ly|ANu~&C`Gua?zw#Qg`T}NRUo{Ag*QT$bZ(5)0 zImo2X!|$2--iK7^-utNNMAKBwrxQNhpUy_wvm2ou_q`6i>2p(5#Ekji5~PhFibJ1< zHM_GXXPAs_z!K*NwxT9R5XN12a1o-NiPs%D0!f}qSc9{3R z12Yj$w9lVlvtgsTcnakH`Nzc}7x4FYRfBE@o#4LqrmmFFm4q;J(gX`UW38cn4CLD4 zn}6cS{#kOr5{+aUUn@Dp(|)Lc1bf8UvDFk$C(bY^bDs8uAT=<*sq4k^=wa6SDel+v zDZ6gFg%n$mS=m%mKfKG?UC-aahTDk$S)Qp@E3KJ4U}*+`YdR5f@DbHxH{qM?Kd#2= zEM+eXD=ON*fZNH|E`@s~NW!_yea*$xk4n@4vHk)YLZ)QPN7t?Ro}_+vZ`KI`5OQa>K@~pUDbOx)6OW)uHe15`81bFmwzY34rxGE^OxXw!(MCD z82c{v>HWIpzpyr)Q#tx6iluZDr2Ol~5k0B`;CN<)b@_sfKibZ|`oN+K^n?zN)tIPEWkug5 zJr+(oky%a)6zLHkh#6ns+X3AU%v+hDmEG-X)Keg~=NVE^q6fC3;Yy)-ekDGbB9EN( zy7LQ14b_IFPpjTm&8%!jsFrZ6{J7a%ZmcIg*CVe#m@^#1UOQZeY z?!OAw6T&eyPRQT9bMV%n!r#tp#&EX`u->Tp6y-Z}TX||bO4~yrBi=GFthTC@bQbog za5ZP*zBH5uNDO%~e+hje6KLB$bH3aM&}MkR-OmRS7c2hfyARlkJdft>2L>z@-nz_F zKc-^3-zoSy*?i84zoWt>IQpjuYhy^z+*h_B5$9P;N*7ResH3{d{m!-En8=#2aEloA zU1i4m!SUw19j>9{OA2KM;{+ zH7*bN2`|cRQ$)UQ5&vyXF(+lNQe2q_hPOQ4 z2z&)QQ9cMf1&%|ti?WqY#2A%EfhV|sAB9ZuLwj^+)q6VO7lfsZYI$Jm2^JKlf*4KDqN zZ3Iwv1coq}h`iCpFi(O9RWA`>tVfFr`pwOz+{hTur{`X&agW9Qo5=!*% zV_0NXgC0r|Q-R_dkbD1@W!^n}pYFMZT%?`RYSO221nNSN-$cV*{^M|FvX288tX59l zN5_+t@pm+X%TNpLvxIwwmP)@$0aL2G8~xrqpS3i?K)S$t1p_aH zKEz;rO`Yw=_Pv5k*hQ$t0=dVl7vLhQVh)8u&lG3I=TyiCSi&7hx- zm!8r>*ubw~{j2rGoyXH2boHf8WvlCOLowW&C>T}anff#A0Dwe~FW|UigA|0G=_W4R z=4t9*G2dj*oV9Ey)T=w+x;8M)9f*1(DEv-)SCQqoKA3@V0j}%^5P88Wp!5IJyP9*R z>80y=A*#l3Y1)%|wVMbIjKYb)6<%3Z4JEZ+UU0q3qoIuDxjXQiS7qYSbi_L~3g4;#CVO>L#PX%x!qyAccK=6b!B_Jeo*zX;%FvA^s83&R z3<~kwy{WF#R$)h&_J)19Y#|J@<)5i_s5bs;2n_HET(hVx7%x1g+&!<-{82N`@( zdB#}qmm~qaPwa7>jn!1<4L|1k@`A;{D=hAS&mYI&%gIj`Rs>i%Zl4|~F&0ESv}THy zKgy8^Xryw@{!^ysP(3fM zg)-{zl40@)+byTw8()_H&^A@(G0?cWS9yB(VZ6rqm)3EMT31zC7fTMeI@k_}8kBT6 zTQ~#*3&ZF=0QZ`Wjsd7tNfDbglZTB<$aw(=+A#|AU{E4sv7*dVGwL?Z?Sb1(u5Zkp zv%>lZan9p+Javh5y2%XWpBc}Z;zKljp5my&YgjLtzk(P1kKLg!stXj~tzx2qfi+E6 zQT!5<<{m^ZE>?#3?gF9TQ;%QJ{(M=_(oC>@|G?WZCC01hUoQt9;^Y7%p5#4PHTAO? zD`1rxco@jN@%DA4yMyKkh^qPI!9UAPAur8}>2@l{Z&vd$8R*|_CF|b$-adX9M_w*i z)NMBAr?`nE#{17;Rv7{_!KIqy)tN=J(+L%j-tQ=g#j3 zekSz1KK!vF|Aw`@W9UC$GH|u|rs|{LJp)aQKDZg)t+)v;hPB~ZP!~%o!IK|!_aKiy z^4kLW>AgTv?A7(XVo6&HDGV)mP&@y1;CBGNwUS`PE~Gp0L{)l=gl=sBrvWzYC=D4U z4@vxINiQG*CgZG2o3wyYo8t!T2ZcC-S-BUktq6??P`dMSWWEA_#3!qDjCR;TzcwyMn%1>4;IB~KU6wWOMky9tq_e2Yrw)9xsLs~2>75w`s zHk-MuF8UZfR3AXOZOfzkkQm4gT@to*`bZwc9llyAloVNS_c&QJXI1$RFo z@X!DUWMo{uFj{B${9_J%oIj6!KLRiWJ!K6@r5yBeLw_Si5^c&s>^f3rKaOym39sC) z+E9_gfOn5n2a>ngt^7hC`sU!qgX*T|ZMk~w##zhR;?2?`>MBvj`XFD-Se@L$ z&IRX4{E;eC3D~S{CF476ErVn4V*&H^7nPK4_$F?}x1Y?0v{QbYJO zF;CA>ZJemnhpJZtQQ3zBtrr>f)$KOJi?42K77(>atX8A})UE60Y_avtaQ4;E?Y#E^ zqG|Yc-gQ`{3HX=q**w+N&ma5n9gdeT9f9n9+3WmU6s!kYLtySqwt^dWou%U3lijb) z*Ft+aoV0cN1H~`l^Qb|9Q-16q+Wf}s(ky7izKtq)s_G9%ZE{xpn^N$#+&e~w5ZQ(t zUs(3rPoqR~+mW%bc(+$mqrVBz7w*rO5*VKns;CLw0Gzr?j>HL2d-=D@+&O%zSW-&M z+cc9fhHJ`L36BY!P&-)TI`qH`qN3lx2QhlT27(w8mGo{!63n~pG5BBqa6GRa_BL~GhP*jNQY7nc-sg5hf?kN26 z+s|>7XNaA}hb^ZghIb`{>fN;K=uWvd=C4=+aG(hp#$mKjdzFLrAF% z@zf^V#&XOk2e&h_9lg(Atos4aZz==IOS7`aP<;*=HeB`1$cuOQdG76An9V`XJEdSb zCPTlAH3-ywGED4x$)ojZzRKPye_7<<{jjX)5z*N5#MGA17d<;5f8>)^|H-kjQO?`t zu-4lhqq8&774nt?vGG0CjQr)#syCfcMxIE#786&*^0fbfDr)5;V*CAY(^hfkL-nq! zTx*&j^g`{!gCXIASMQHjzt%6n8-1MjD>_w-AoMG{3+~cS1%f5GgwvaC_s7q%g;NyG zo2^77YD$;4BT*Sz3Ymgr54v>l@Pvb~2Tm0ErN|a~$B`qgQRKK0x={L)P~M-Goik>+ z6lTC=l^#KN{Fp~=N|VxAXrC+fBj$HH7)W?fqr@xTRYN-+WhumcA~j%?w|oh~kgc@t z?X^`3&a}@B!Pc+-33mq@d~=euVR{C?+%8kz0WApJZVI6OWGO0!0e;3*myw+QgKtES zON=i`$bH*VGB4L1y*BY$<4(s%TM_5+Q8`x(2k(bmZ?6UXRE<>S07V)?wTt?=fa+#* zv_O{N`76(xlj-qa(kkQ+SBIUeLeB*^y<3RgsXM6jXK_tmgmahWGe$IwxHcg!a07+8*@ zYX3J5a=LEK*JGyfRdkAtsU{l@lYle5!>_dIGRQM`Ow+@$AY{j0{o;O?RD*^D9;Ljx z#7xu<4pqwqynh%K<-yMW>}rftW2t&n95&^^4waE72XG&q*cv!=Nc2Hj5f@~`)(GAO(Bi$1<%toaml`?7J(OWaIxgzi?*sU_ z;yaYot`kLDF2-WOn(EGG?=A^G$W14)Ma#*giOxLD;gNOtWeulI4sTgpgApWz81eBi zYY7mdAz*SZm4HP$@xETH*&Au`&)|4hm2&X}D%0`?U6hs`s{m=$hv3t_Nya*|B+*efVZs_I3tUbJ@Hx z>eW?dbiw3*6Pe>bSnof}UO$%@eU+e&E0VwWfwnsH_7Ron?%Bu+8FIs685hM^01lot zZ+=tbZU1vx5NW|QFZf^(bKkVvlRsdFgu_gi00;D*T7kWdOeQ`iMtPDnn>T*x>wORy zP1)Vq94gfKC?;bj5tcgn82h9T!>R}zzm&_HB(Go{adB!|1nUus{w;Sr`1X^*bOn%e z#v_0dg}$N3ygSwTcdUhrA&;>YvS}mluWYPH__E`o&xNtbju4gqw&v+=R;%mc*jU-^ zzNX)*rPx`$G0^=*EgP5noIs3EGB>Wf4z6by9GkA=$q|3x8#f&g)PBykJElm?GZ;kB zW(mTslYQ;|I}l4QaO4{0CSdNismJLlM0bhO?b((cWw?1K&?yCM#3eC z)-k434Pfb#Ok`wVP81CLt>=}9`EHC6%SV|TUw|r%FDDdRj-<s$$Q7{wDPbv}S<PaBCS5NV3$pPN{ZW~o~!UJT6hX0iDs zUlr9vAaa#frt1AmIQul(_=dC9udC!15hjW{juk`gES{&SUhHvRZnxf!x3;W)O=-Qs z9QNO|pHAXOn^Qc-bLIK??$CR9o?9Z>)AK~q-H@#mW z4I{748tNGLOZfbcLlqa!6H8z|r!@p(dp5}S8L{(ki!H119@j|M)=(qPrlZE3rrW_q zWOs?r`p4@ll-E|h|3(Muv?#2_y#E_L>+NcVq!@6~IrksyuGH;bT|dNbo%OSF8Su!Q zB_K#pP=4kGeQpYg(LUY|NJz%A zMnen(8ypZD>Jr0RBMO)|>JutrOiYSVhI9Tqg9pCz#DWVQRV>I3q=$ag^f#Ky_FBFe1H6~GrI_nn^g z+EykD=6O~|!_K&Ceu(SBksb&W&z$B&^Ns%Lh+uvT#{f>HEjvJjs4oQvUqSK4D3C{b zUIKRep{1Fy^Y2%HOs87W|K?(`dF8-w`*J~$=idsCG zL+v!Rp1(iP(}!P;-wwSWzGxTon%ZbSvb#N8yq*k^|C9#ML#Du@@RO;Je&;0C$_6eLF_$r>w)t z_kgwTSKZXE{0j?gAcNSDG5M~AeZB!MHsjjO6hb?XZozAp&RAtxz}h+FRdHixVYPm9 z-b9A1o5`&0*@}Du^FEv9^zA>m>CHjyPK%((Th59q&WnzX4jYYqCR2R2rZ_K`#r#h< zuESDpHrG;Gcf4DFgZ+;_4_}lGU&Ryn%nLMcrww0rht15^|I;pCVT{&CZrXVoYwj|!To>~B zV7>66sIpHjIg0DCWBW3+>mUKY^>M$Aq>V+-m4|t7 z!Ue3g_)*pQQI?O{1-Y2G^|qf3K0CdU(H{hoBZhMcnqzt3cSG)g=vnt?;clC^MRc`$ z(3kI=whnm}->7gr``O~wpD|uW#@14Sotq>=iWi{<>CExH{3&S}<5ImZ;a_#VuMIe* zAp4PWIDfm;T#wLzfi>tZQP|;-)S`^!)aTnA6U~+EJ{_?IKJDSI6-DrjI_Z zF3zYaf`y|%gNoMuba2J{Vd2H|Zh`BgdcO@U;K5qF0``z;6fy^O<%k;P^tpiCLb2jgei>(yoTqG`D#aDPvPPhI(arATrj6$^WG)R%6Gv|7Hf9Qop zpz0pVT0LDDc5xn@{7QUh8b3X2pc3m;;M0MO{E=1cxl^N+FFD#}2=V?u}8y7s40x&#Ci1 z^+nLKM6!lHaO)Cea-aBH^X8?YZvMepZ{)l&Wsqh6=fD@fR&3`8zZDTM@;_l0Fj9Qt zn_E#kE{~@mL&c0|2@v3Oei!dl`27H)aRznKkE&Y}_PY_gEit}`S-e@RxZbvFS-POQ zxU4|!psv9F*RuZG!$V@IwZ+5v)<2n?K{LcV0)#E?ZdcFV0)tEQ>&TW27~&$-Zcg);z`WDo z$uv}Y_R0aie>!Vjv@CP^`*egRC6ri`tgrOr)fU=*#)9`rQOfn6WeQ?%`1V}&cGb@K z{1Z81GmyL`VBr=ShPtU=c-L!yI=Y4+mSV_n>T|9pA^sN--`yOa9pA-Ec1QnPyakuT zSwj$zaLCbXT{G-aDHC3LPa5tFUHDSHTtDrWAc#u97^aYf(C-nw!$pe`ON{pLo(6H( zbJdzjd||!{kYDm)a^@pI&_u0^G8=EnI`~t` zM?Hc03T!3RH?J)(Uc(NWZtQYAR*r9%!2atS#{Q#EZkJKb+rziJs`3ukUB-w@i2p9r z!o|nirDw*+&0=1?1T9z410d;%HKYEgUUDD{sdQqx)}H`5m#(f9&e7;WdK%*+naOv_ z+(XLx`OaM}L(%k}X7*~Y26!LGD=q5^Y3VtSwJ{qpRB*JVYiw%td=#25heY$@k8rkB4oWJl#jdJEFmJTxN~$({Q8B$s?k<1>tTcv$)=B#^4L z5pI1CmjFg2(@3Mu=VF@Nyu1m=^p^EK0QzWbOs+w8pn3FO zz_A>D_{+o4uh+Z6_skpS1uPP+dzWWMzCUIRzHEy57E2v?ZQNz*48FNQZHpDUWJWGT zMlMrvqUKDiCiA^&rZ-hkM`zNUjoh{7cg0Wf7*wp6V2LY2Bb|@QUBzy3MY<)f+4NCr=2-LbWJJs}@X( zPTc*>;I!|y=5kB3arqv<(fHiWRrm?Vn(+^9hm#o#;|qq(u2;S>2rVqi=MB+j#0fIS z$M5Li`6}I;?uuteI|`*elfwWur(z>N%q+95-B8nuhERUUA)Qc`q!Y}fCluT#z1rIz zmEg`17^jZyWC_iysT_?mkoVYtEx-!h^r%4#bNeY0}Rpn1+) zE&4?LHOo(bLMF@e8+Obiy4HP?O|UzPwTnaZT*6rrPP*QNZ;KZQCW9-yydRS6)3=ny@}B`c;_yqH>DMyG7tKjIOE z69el<7dD~rd_kpdH-d3`7Nwqf?{{zp>>JBH1-r}G5v9qlm6z{TFdlA3{$yW~n{Oh? z7&w7TqIefB6Zb*&L;AiPzI~&8)0wp1^5(h~)Wu8m#Kz0GQUgvdy=%m{6B9{nI;G&L zSG|m#hgF1(j2mi$wL?7J+NoyaX05C1mxk=S_C!1N7Tx)eTMYQamGaxMQVu^lFBiXm zxF=sSYAjDF3rT%Y@r3kM*gEN{(<#+0-_jE~XAKD$=aoo|6u{_6R_VA&GRDBlw% zUe=PEpB1aeLE`v)fOk|W?$3mp;HNqC!bKE}?rwgT?_N0aJ~9m`12`0&oy>*8Xc;gy zI9`09I41`1J!PFl8vTluL}CCO6lXRD40$$MAjx}R8_zHvEUbsVRK{p|Ck)zba^%oV zr`xW;K3H$OE1+})$8qR45C>hf<@v-BMjnycx2Qp^&fox5p-5B0nlen3CD#(ou- zUt1Jm*tT1E&TA$Er37R=A`gj3dG7&QJqkUDwOJc3{e6&uX+-~6x=3#*(%o-+?E?1# z*KEx1;i}oWHc@{!)bLrda+mfOtRVh^r;KD>UyLBwp_7fl-A}&GKctinDfslhIxbw1 zS%p%kVqd`b<8j@s>4UGysuHXe#(5lAY7@N%A*{2av~f&ap~j_zrx!GnGdM zn+3%pUVYu=jVQFFGP%beXup|O+@9pVW`&;zgG+UNwpr)nE8tO~8-76vc?-qC?J|9nT**jSn08P(v9Pxi&Xy1 z*%Hba4{RAp6d4niJ8!ac;w3o}-%2wUclK^uJds3igkwC354#>}u;oN8^BvNp6q|<) zNt{xkcRUX=GwG;kr;%l0t83?uze!@N87f13Dp!@Dd!IatUsbs%g;kotXC*lGAie%8 zJ8lJo-Z8DO9Cb@G9u$qhCQ6(xLB+?&jY#S|r^xh;Wsb(9d7m_A8=)udY;3W;Y2;yS zl5WfW18b4Pwvy%v8>5ClO^>>Y4ZAFah=;*w;b2CW^=}2>Uzm$dBi0L3iwOmd3~g>7 zKPc`V_uf_0`v&T|Qx;?fkO)2yJhPzSrAEoqf1+GTp3J!aB|#YlVpFF__D#p$G)wl`o_8MU2#M#A#x@Imn>`? zWaLLL<|&5rkvad_^rRdVEBcvT)mB-Jl9EH@ahT0nPj1vc~ zob0gny51WCM>*NPegbqGyXk+!a6tlVV5+FKP8`4&D>tUrh)9;+v>~CHb<3PGxt=1T zuc}2josAd2dAU?1MVe~?Vu7oR32ie|^To))e=2nxDQ-r?M6D58>1^FkE1hQ0+Lr(M zdNwvv)DaTg69mg0yIw2&^C8OL5GqOr zw6GasxhzB8j`#6Qcu5~d7Ce2mHZ(^`(0t$l6M5dYaW?YP=Hzl|LEee<>zdT()F!J}ub<+E%dqG9H zM#k6fj-S%dUfFAz77t@KlG&4n1c$7O;-zeVHP;4=Y}wP*ic@9c(1dC*(a|w1rKLY0 zv=OQ=ryL|NKNC{4X(RoFLDurZ zmU>i;3=uk&0;FfjrTIL!F&+v$e(lS)(4|i6uG~moGJ`Ll(46OtiS{$@F$#;6#40ra z@x%(p(PNY_0YVhtbz`Bo)sH*9`_pGzWnN`W4wq#klObi}Gf6tPlBFJ{cAK^JE4|XS zI^Y*1$OB(5OC9Yq0&pH1)`1MQWCiF_P*-7Vbyj0V?}*;>d%eAKFX(C$9SLt-$m%lK7u`5IQ8Z0 z){jo>EOqcNjR6fME(2=B@GMB(6JRq;fOa1+nIdSF^h_s7YbJIgXS`@^jhWGbcZF_t zQ7Va9?r6RoT;CgrP|aZ^v%mbVCEsE3s>WDTn)AWO->j66$`9K8*z!jbhhZN``**nm zCz}D^CTsQTR6c%Ni}T{`_;$h1w9AY&7R6o&O72hgG|sRzDZbYH0dAs`yMSLghc@F{ z_p*(E%wwNTXv^mU0W=m{GqW7$oXcV`1tcw$w)mRVWrf^oysp3GZ9s}Ig37D~f%97P zGVUbZ&hjeF2CUI=sOP-2m^p92@D%w_U9l>5v#s}RWhr72C(*I9*z--uGFWc0EK zz-S#Lt2x@vxqJ<6_N`rNL#q`iyo8uYnPY^Ie?!z_GZNPtxSK~L6O&AV3rtxY-CZ;V zABW?s`~;_K0(_x&~a~ zosE|2RMB_whWA|qA#)#0Khh`BgIRCWqh2)6cFN$QDWvK$H^SbOWc1_CeC%5_A)gcf zJ=_;9^@o0e=JMsng3yX9-*T^}gP%jds%NMabv6@!V?1x6RkU?wzqt@cTcq#m($rP6 zGHu@So%nX*Ph@mYJ2s*GbO|tM*)slIrS7fhl8W~{$IJDuxt>R1+v0cME#8<#z|eA~ zU^xESssiCqIa|A2_M}T*qdmT`FXI#968U}8H~WSN7mRW4=g)u`BV+v_=F7#zjByc% z7jd1#SRT=#X9+WuhK9~g9QQ4l#hmKz+pXI$rj$83(%@xuuJ$Sv$Si+OdiZCKnfGy; zmgoQ~$z15PEVBgXxFc0+xGSr#RnW@Pl$GK8;@x0Fv`~;*O4@hqw)}-+Pb29peT*zhYJWOappUf`hceHCf#;=@RVN+(LHkZ@SYZp&%N^r&VixDR|Qa^+Iwi zxw_w5_RD&U<3t1my4AfktVrEMTu6Ifkh@E}Sg9Cc0!0v+?i7A(EAFzRqcipcpACc< zQtIq_*57;j%y&^4VmK-{yD^Es8To#1M#u(Y2d91if*AcQANFS>bQOoKrI0=+BjHU& zp1U;6XUUKvTlNZWe;10h>xSvu&6G2bWhsh(D#lj1NPH}}+OR<1o{CP}#I?lEh*rSb z+o5p_$qVKH>#+QgYqQ3u)j+|$wUKB$zqmr{eJ)rlHO^WGLk64dM7bY|3n*!uujTx( zRpY#%pYX5dhUWBm-}|v6qBNVBQli!{LrwR1hw``S#FmgW;X}9B;VN9Y7Ue8U$R<;9#YM;7kY&j&jF=JzHBP<7k_j*j3}>y z1a(P=SgR5&D!forLmo2|oMq&|?{iXvSEHeArS)R($1It?a5)Pyusq3l6;aIHs<`=8 zm(qHD!}-4VBnA3{#H|YU#|nT#Hew)#nGoc<1>jU7g^X3hM#=5%$k7@*SaE|6$JZe` zDF)VlrsQfEoW_OaN(Ayv^a{Ienb4=*fj_B&$(&Uu&C?p%2BSvK{vyK{J2$IPy)+b{XZDuYnotKZyIbI%2E@!2-Jc?oUPFB1Dq z9y3AC9}FYxQ@V6;KX{Zfiox|lD#th2#92^*`MnZwf{0vj{9!M+{Z|JS0r-mJ+7=i= zM(}bslbQ4j&O4JJ@ui;UsOy|C)Ng{=>nX=^vo4WVWK<#88nK*(I;R3C7x-%{J%b0_ z(iE4eu zOf#kWal`Im`^Ymqd@EUPg^{IqD*=&gK@nw>@PX_gwFF~QU=YkMQQZx!V|)t|Tm*OG zRWl&G0Kg)0jL9oDh>ZDp;~#z%t#Tl&2yTaut*gwghOY;vs4j@ST9%(Jp2xVugSv!@ zH(z}QFI%&!kUi*d?#xI4k57SuD#4-adEIjFYRVr*E^&f+S?*)dxmHy>9u2r-x@I-7 zgOtf$N)uYiqp@T#;qMX!Nh=|bC31}+@(Ow`<}sbpl>f{xFrd9eODn`Ing$v}ekEpC zibj%SLHnWkmbky>o~fN(?+%Z3fT%os6*n16&suKwK!DQU)Nxgu5 zv`jV9t&Y5WNY-?g9xwGHGVegDKWrEJ@)sTInHr>IJR8M7CW=bT+dA0 z9?r+{g+c(%#Ws+tC0xvBnzmcx#POn;K!JoBUddCgnxMFeU&JL>$MqO@)1#a4Dv})o z6)qyRb<=18NX0Bhk0CmRZv-VPOLJ7CyyTe`7FRv-Yu>kIEQLi=Iah^TPMugbTP>Kn zck#-E%m7jOnFO33@K>5%sfMAHAJmiThKbnUO8381b-YbM9sQ54-a0DE?t32=q)~e4 zX2_vSLc)Qeq-y|a>5vvsT5{-g7>Di}Bt*KTrAtskx=|1TMR?vDKhO91t@ZN9EY@(} zoOAZs=j?s<+55U)t^SZJ=9Is@D^&82?-gYnQS06P*2h9dD#l)o1KRJTJIG(x&VtrV z<^9ccKBvrr_#M<#klq0wBFM6zH(ESKR!i0yM+N{JF* zP`NwgfI>5udElK6U4)+iyBR`}oPCeVX#mQ;*E&3`{oFhFDXxDdM0s9dSv~11-*Vz1 z6Volb>z#0QN?~1daK*!0PF-$CYFnWuxXvbm%2%ZOdkwy3!1T z!j1FCY6yBVZVt$4#jmiz$^OVmWs;DRkrlZWX_{u!vDAouQE?#cplI$ljYKPeoWXZr z*mr?(ljDq#CPD&$X*w7vyGwDC>?h&sJw8XL!o|04yWvGnoFEK0x5fyijgwIy%W6Nj zNQP~6OK<$VUe^b@>-S;lGg2fUz8tNQ8;e|^GXCReN zPWZLH&}`eMIWc2BXdr}x)jU~>`JufiV8j(6IAsI&xRNmhl6o&IgS2DRf z9Mj9J5mYKYZBJ22w@{E^+X^Bp|BkTDvW-|Q6pJGg1-05XU6)h<*^R&zm4iyYghh{{ z?U+S3@ckRZip97}czFDj>Um#O4xUy%PQ?a?YLXb2Y*f2bj1+j zjAhC8{fV?8tr`!^@E>1*_5^ToMV0p3d22aXGKFY0eKZ(bv%fM8uO*@^l*m(cp*)1^6R_9u8y*GSt*7Y)`ml7LDI;9V7Pdnt%^|#g_NnF@ zUua+}ZHuhtGyw3sxl&P1(prd3U!S0A4OpSw0i>WNisIr~3oH6XZPf*~s%Pff3ssV! zr-5{X?=&pf9O2#cv}B<3xJ(r-ONVDu!jxl1JmEv<{u^tbhVTnF## zfJ1Z_9`_uSQ4w-?b#WhohC^!fqYwQ`hUqoq-a{q(eiF&aPx43Fmcdvbe2BcNDPk>y zd}e!^Mm-5S@--i)*hQRQITZ*nvYD7Wb^6R+0(RciVh!hsYC-S%*SDHh0e=bvU6Qv9 zOp>_fg(Rg_U)fVN@1<^oBfkoN_%+R#xEJM$nEsLNL0qY{mo0Xt0u-ok192+_K9^gx zLA_Nd8%-Ocg1^Bj+OM2Pzz`K&cAeF zmF^|^@%U(J)K8GzhSz7#h<3t^O4U$(<3o-*RSSR{mT@AA?d9=Y8vp+OW3EA^^-_XO zG#?#T-3hG>=&=WiMFr3Hc61yU+Au!{3-XB@l-ZU!NX)ISJAi8VIT7x>glV~;zi9kK zQOjG!4?&aSAd3OPI-W5GHWNd$2)jf|Aao$p8oY7G#_}5uPE5GaPi+QV?Np$kTK8@N zU+U{i~G-NZ@R~Ah%j`v9h=IA)@1B+)mtzUgx=KWKe`)D=Oi0cO>HSlGI)TF~}e zJjMvj_p!!kf19@6(+(+hN%Jr(O)JgrMgylfZ#O_2_ylZ@Y0kBWboU7B?ous_@+$uv zxXL*7j9=Dyimaal&F?oPeB~1mHz38~1>#n>bS%K4au3HiwJUAN%Y1A90hB-0Q{@&2 zwSNtI|4H?ichH7T2Y;iH3Jj|Gt!P@xOe;bVk!Xt@oL+;a`mL3n7i_{qXOQL`v-p&^ z#3t7VPYwe(71umkk}-St?VWpfCo}T#%MY6a0-}3)rO7fl%x2J*(?B2Q7G65D68){H z{lx*$xd5sstC)8Hf>J^HYN1O-3h@w71ZlGOYio(E-I}yED>7>vwXzBflIt~VyD!tn zrtjsV563{|{&$S0ZtYpdHuMPnMYy=&Q{4ngFXiKLq?QRB4MY#Uo(pAp|5h#6n`l_g z_#0pP`Gq~>FQEPpAd`iU0M{3Gb@LVgA*SK}r3esx?IWAb;E@AP&}>`!NAS%AD#bn1 zD1EW&a;LXC4*`46Qlh>GivH;pVHd`HS5xJ@fR+p`e9YSw6U<{v8fBoZEV8j-_agVY z^zf5Wd1FA)0N2G+?!SfV0OFYtCWqZ68AG*FT?X% zg65L?Ktm-RwKCFQ0b*-*D+Z9E*9e#@_k<`hA+KOsWdzPR7O5FU1!mTP@p8TEer#>E z{bhCgh_^Ugkqc}>ercvM>qp3795^pgLz342;D$|jur;K6Hu0D9SX^kIx7|Ro+}_qX z0B-zvJ%Ln0NN>Kz5GTvDA@Kfln-qOX~mdB-0o76h3>Hzz}RZT>cj}owR>P={Lk#sjIGoJ0R8?rs6?b%7& z>Qi(J)IJv`PpjrxS%h}WMN~CTYId}-bKZ&*01*JL;ROI-SbwJ_)vb$1?^eMkQaXQ+ zx6R%szM@NZuF7lPiEsi-ygt?7AQmod!QkeM5MGW^WCrEvTj4eU6|fT3tkR`Q+He7= z#z@320@vB$8*}U}vOOQctp$YPuBA3uci7r}Egx3_dO77VX;MM{#Z(=LW1+MO?HR7z z+^4FiT~X4IFI@22x1w~G!~|bBt;)(dghPY(_v#+=b-Wi3oEp44v{XM>?*Bf@*cl^dqrKyfQCfNni#A?8AEH#Lf5*-xbh2ceE zoQLx9_|?|1cVtd=4Q90xMhB)w)G*@dBRo$B%9-f#*>RP}M+ zZ03F!XHn7iP5~=JTk^${R(i=&qF#**&C#*EK_#pNp)-i}qs+%-^pm6Kcq3cQQDo+x z;2yb*93F1U!WHz7S-(hcU10^GCOshrghN}QcoF{tw5j9p9SP9Sg;v`irr3a`Yo7!e z_imKMU}*ECUBt;~*-XSu#43vC4WcB1U4;R$+-G0g&*wUgZ}^B6EXv6LnV0)59y=;b zl*YJe8z%SJ0BiA}5ltuJf}eHmEN+L4GJ;#=TcW`n4lM563`S@BU^%vJV`jeRh&jig zj{Y9zgO~uu;UXO};fM*7C$l0Qw@hWe#d6SddJDEu)zi2c#|+0hJGNeDb-{Tv6hln! znH;os)E`tz@Dx~eIvA=KHa=1Aj&R2gv16k*h)fq)f? zSN4^trQv{MOG=?r8Cr3JuoTHpqilS3+E`D3eyIS?3)(31ljG6k_YSLKq=9`BkO{L^ zSuUXVjp}T$4MVM&7t5Ff;$aobheRL5cWOu;5CtCV=1RQeRc1c&QX3q1|1IX4^Nv1l z>kZjJ(y(x2?agr&I;o-SHvCYStNzTp_PoS)?uD-RQg;#;gQ%`WzvOx5ZK$19e?SMC zJd{(CAguW)*8x!#KP=99v32Ku)=XQ#WI(<*U-`kL3H;J~T?`JGN%L8W6r*Bvd8U%H zl!e}51>+O@m$)C_&?EmYm9NT!ny4lW&-mwIoQO^9&m7YHZ8|}xv-}g|iOq(p1iue& zgG|N!GaydS(1M*vSs#+@QP5S~>c+=}j(!>tiu^T8-sy&@Ddu}s1GqY#Y0lsX0_=W; zCtt`ouF$T|^$#{Cd**VVjt2=HvMwIjDhw}jyv(`yRs3aiN$$6|n1OV}hrXD!T#i`H z4o;}W8m|sg(^kwj^zk_1C?rxk(*DzXK{`9`v2^^X!FPxt`;>nyBL}VX0jVPu7_;$^q&Lkc zyYqv$;?m7!SLr6Br_H|Jc!Rn|=~oW0ZgTIL_3aQy$gaBT;-ePkgo*oy=Eslv z%d#gu(p!*&(UO~|Ble5Wfceh6H^{_A)FWd$2 za74~{J6l0RyM%z3)BE;=D%j2O6hi#OG+?s<{Pd)l^@R6lYXq)~*HTs^pW~CM4?tQ@ z#I;vB%Jng34qIhjzr_O74D2xRIiRaD6-k0eEr;J*SQX2~rI&(Ul-}{_$UY3VXA{5? z1k60~n0O0>Vgel|j9%_GG($d|;r&tn>royt?;~rcTc>A05q@u(WBKDZD>%4l!MvI7 zfg;OfgSzxAg$*kRe+dXm^09QBl->~BcL)y=j2O+ddMFK=a34-LrZA*{JB3n&NPHlV z=to#Sey)4iYE#en-W;s0~7Y*%72`UYScZtH_{vkkMh31VzPby5-zoM-m9=;l`Qs`ys+#XV_1s`qA2x3K}gjFHYY zvq36YdyM*eiu&RC$yD+E&V9S1x|qHYX8MUGp}<%=;|E05e|+{-JwdK_Yxb_7;r}ZS zqR%(Q3)p%7EimlHi@ECE$*aNMP*j= zw=ZB^E=v<&VnOd+q6dKv3?w+0=*c9M>d8@;RA3$h2m~*lTm6Cu^(p+yy;G?s3s1M{}uQVt? z*>VF5L)uqggh&^q@L9 zv)=y22EgOCMm5%1iUfq7>0$Z8Sg7;{QgAeXj7OM{Dt6PqL=WrikLws8qR!S{k}T}3 zm!Ix!0H~KyXZ@`y`AJ~|`1=~UMuPv)Cw~uS9`=9f+o=2eQ!yQrE86@y-Id`$oAbV9 ziou^va}jacR@>?22mcJbaX`0PPvPiE8w~a+2Vqa5<9r?s|G|ZnS`Kka20rfpHH@%G z?|e-2DGoo;sKXbZXn5@Ih;t;CTk(cB*0tg`;oUx_Y0YB!Y@xAP+`AH5fzpc1s<1Bz zLI+uGt@Lw(vdhJ3Wb)?%$aWFSP7A0su#DYgwt<-2$);XgBNjpSNXkkwq;ituNTlSy zu}PQJZ_mjhOE;OX>{H`M2o)y!W@$?M{1$>6$K)eRq6z}NLoMc7%wcOagS`%h1_PG# zpp7}wCy+cDmV=V)d8nqrMSfX8)s|Ww7{JdObV7wW4^$9S|2#@oQq@W zV0+4_JQqGLGE?{_k!Dl8mMzR&ovw%_lNsh6U^YyXNYiL~Xi>LmeMHec!X3fLQlR?6 zE5x3yRQ=_yLHo1=IGu2I7a`+6yNOWM7J*wmdHlCx!3!>nRq5|GU6PNc9ypHcqy7{h|0zzkUPp4!J?LArXf9iL6d~sq zgOFekk5d%UrQW_Txw%o>QkR#Y>J#pkOiA!&!RH6^VP=0{^(*aNR(OVxuConOV2h)V z3eiczH^Sw0jed4?nY1Sh6}EpcCI#v#WQL7H*0+avH;PbcKU>ZO zP-Pih3MV^aGrxv6-+TF{71!+j4(WgO4)kLa%<;Y?;02$`9294dbT|s>3!{;buYhFq zWxUd!F`Yjalqsku{J<%uwfrh~J;rqdnz>Yl8B+gI`1?~r$&$L6n8Ugk&)@-Q!&ust zY61s?zU&&{Rl4CfIl&#cf6!XZRpiM{a^7;-xt^16yH}~Jt)?pG zwMN{xILJ?Yr8hP-n#1wIQ`#{u<+aWI$MJCuzYJ$MPtVAsD@R!K16=B~1wjTz{InU4 z^5IAy-LX}vuJ>c@;_AG6!zbzw-g8Hm>J*((-XD%DrRy=OPLbGK)a2BWIB5ru8rHF# zGnZ@48mWlMJ`Y>kB#XXkN%Em|J9TOnLyIH*``-n;E|-drq78oDbD$ZMwo&+au;1Xp z0aM^EXqdRfc{{xDF1Ss<+Y-kK<`NF0aQ>=|~ z{@A{ldQxctSZ;(@XqRX8`I+pipI@?C_-in)wkdGwaj~WPg{bHG1`>$;fnv;Zcf|dKs1(a!!YJ;{Tp<9ZL!D~wo+<$~?Amkidh72TIbL&pVz zVzSuFZT${E*Lc)gnpsaf+n|t<_kk0sAM(kJPJ~#(-|GPY4o%qO!k%wLkD`1EG*Xy) z4?toe;Lb5opJomf3;wC~V>wZoP~bNA=C6UnP52+lu?mN9Z@Zk?6Wvq}U~ATKLfvUY z_7uY|BL#hN57b(~}?>G=<5G>xlB-`za(hwjhNcgZ4>HjtS&2 z98(?^&A&k&OQU|>{();Am;u_r9r+k1bNVb%^aBY-(-*;KLS+fxY#x`wjmtDkojv?e zK#1|Prt9y0fGhIWKx?MnN{OmY^RvS)LfwD$4?|x2FVR4{=0_H5t*S+e-b{;}!k|+w zE&6iIRFE$YypFxTlqC_w_xF~O`c?+-Cr6{|0lL3})|W35-S-PH0K3H#obLeCyuu7^ zrrn4&XE8neyp&&0lOK@9RBL=<|&WCvc zhq*~m{NlHCXFu0;Z5~p)imx13=DRG8q#G|2_dT5>y^*5BbZAI{Nr2PlsD%sAuYDZX z-`Qf3e~BoBxx&e}k3`M`G6A8ptb;1R@$&Glp`qV!t~` z_5p3ydWA{BPga4yC9y@vZ#BTcI|?mZMbufDsN83G!rB{m?;6Tlr><<-t{&9QhZC_3 zUNTENIfR*`)Dte|wNHwEE;GUb?1;e}SyC-m_i11zwAuQu0A__h46bhJt(IU${vX9k`j?Y9!O(=?5)bGUd5 zvIMCZ_blJ;e(pNH%icN_u|=I!u-tG~8m;j4m@b^m>Nyw5gw3&!;5dPf1^;A%5VFqO zMBpE*m;NXYUXnEiKi)Rbn>4j%;AEHF_4w%tnx%@|nF{!sAIpNPzN<}?HU24Rz6pB0 zpW-j&cK^8*fjicyjN+45BwlUqF>_g*y7`c_TH=L&e9W6V@?h2)O{F!%J>jT-m>uMqJo5JpT!WRBT#E3`4uzKi?H}^RU1kaI z4X?|#m%N|?ol1O}r#mo+D#C-$9D35S0x$b>klkAbr8f8$%f1F1mh}=zf+vB4qW?eO zXKUsjdtTo7SJt!T>69P!XHs6@h}~2<)(^$B1zMYT3V!0oOuM7RBY_@_680(idq^}{d$kM8g#()(dQl`u6=lwJ14WjWsK0G;{?K5EgJ7@nraCSXlL z?|rfQU2CykWnhELKp|BODc#AK0tpewfe5W~8clLX7bXrLFCM$4J&StyxtYrQU9fql zv8J(eZL24LRbgpkBZ=@A79ydSA{HkRiU#x&`^x>=^+@mq(>dhQ zGqkHFrFJ-^Uq8w_^X5l642SHJD%^Uv^LbpYb`;1>E#DG;kMjuL&RdOBq>_VP=<4bX z>3_rM9Ln$Y%cW~w*3zb!fYs9nXwAK=&?f1ouaaaBp>DQN+Z?DI>sxy(>4X|Puf-1} z+y#Rw%U0xG$;Og^%ol6F^3`lZzgHLcSjyF&kw5R&*K zJ)B#34|zg|GveDX?9=mUVq!wqZxXqvMUc?&ys0sk^!cIA-b?OXs$XE1qzS{+tvizI zPeJMZMhLD55kN7>xB<ZAuGa$#ISNk%=zWrbIEo-^S)Gw13GO*nkd^Mgp zhEjyb1_`*Msxl~^CIi|?MLraLT=*H4CsX}*Vp%Hzwo5k{S#xCfd`LfM&eU!Wp=YM- zL{cXkumT=qub*Fc*vhMJaJq!%GRQ6min$3(%!*V(^Y4al^}lAKpX;Lpp64D%$#aBt zzb1_Ra*=oKoHXy(|J}m_Cl_WdxSKcU({ZF3R^^L_k;VbU!HVT|#oA5MKPYWB1-e-E zoKgb5y@U1dM>7bkweWIJ={-sz=h1)PDt;xwG4_T1Nwwi8|L@sj9qZP_^Bao}byD(p zoj$bDi`8@8k4#j_^0$i@m|fsQoK1M*56bZJw+30fj)=wDDfj<1_7#F-H+;e$*r?2} zoq)O2V2r!MdHnAm10YzY-g~3$)Z8lYNzniP6QC?`f}ykx)6>*zgjLr~5bx5b72p1y z(m$?(&)WZeMwnd97v}u?%>rp?QfBtaesYzq9NwiM`8Ps()4QZ=;Ny-}5lEAwQu1 u&Gu~%_X++0KlCB(|E~eyw{Q-e8?1w?-_F@F);xEBA9a|vG6rfL@&5s@%1EOC literal 0 HcmV?d00001 From 06dbc783fe7408c58ebcdeccc87ddcde878f22f2 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sat, 28 Dec 2013 09:44:14 +0100 Subject: [PATCH 109/118] Fixed the Windows script for uploading Lua API to an FTP server. The new script handles subfolders, too. --- .gitignore | 1 + MakeLuaAPI.cmd | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4367fea56..977122047 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ cloc.xsl *.user *.suo /EveryNight.cmd +/UploadLuaAPI.cmd # IDE Stuff ## Sublime Text diff --git a/MakeLuaAPI.cmd b/MakeLuaAPI.cmd index 80bb206d4..6ea44c9fb 100644 --- a/MakeLuaAPI.cmd +++ b/MakeLuaAPI.cmd @@ -38,7 +38,7 @@ cd .. :: Upload the API to the web: -ncftpput -p %ftppass% -u %ftpuser% -T temp_ %ftpsite% /LuaAPI MCServer/API/*.* +ncftpput -p %ftppass% -u %ftpuser% -T temp_ -R %ftpsite% /LuaAPI MCServer/API/*.* if errorlevel 1 goto haderror echo Upload finished. From 725b997a2817bd999079e5e6678f5497373cbbac Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sat, 28 Dec 2013 21:59:50 +0100 Subject: [PATCH 110/118] Fixed a (valid) warning in RCONServer. --- src/OSSupport/SocketThreads.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/OSSupport/SocketThreads.h b/src/OSSupport/SocketThreads.h index ecbac3aeb..858729c49 100644 --- a/src/OSSupport/SocketThreads.h +++ b/src/OSSupport/SocketThreads.h @@ -61,6 +61,9 @@ public: class cCallback { public: + // Force a virtual destructor in all subclasses: + virtual ~cCallback() {} + /// Called when data is received from the remote party virtual void DataReceived(const char * a_Data, int a_Size) = 0; From b93b4c4825503e3c03c44091b415cc7186b24455 Mon Sep 17 00:00:00 2001 From: Mike Hunsinger Date: Sat, 28 Dec 2013 23:49:51 -0700 Subject: [PATCH 111/118] Added function to create Tall Birch tree in BirchTreeForest biomes --- src/Generating/Trees.cpp | 49 +++++++++++++++++++++++++++++++++++++--- src/Generating/Trees.h | 3 +++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp index fbed57cb6..7e8a3c75f 100644 --- a/src/Generating/Trees.cpp +++ b/src/Generating/Trees.cpp @@ -216,7 +216,14 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); break; } - + + case biBirchForestM: + case biBirchForestHillsM: + { + GetTallBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + break; + } + case biRoofedForest: case biColdTaiga: case biColdTaigaHills: @@ -237,8 +244,6 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No case biIcePlainsSpikes: case biJungleM: case biJungleEdgeM: - case biBirchForestM: - case biBirchForestHillsM: case biRoofedForestM: case biColdTaigaM: case biMegaSpruceTaiga: @@ -377,6 +382,44 @@ void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Nois +void GetTallBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + int Height = 9 + (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) % 3); + + // Prealloc, so that we don't realloc too often later: + a_LogBlocks.reserve(Height); + a_OtherBlocks.reserve(80); + + // The entire trunk, out of logs: + for (int i = Height - 1; i >= 0; --i) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_BIRCH)); + } + int h = a_BlockY + Height; + + // Top layer - just the Plus: + PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH)); // There's no log at this layer + h--; + + // Second layer - log, Plus and maybe Corners: + PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + h--; + + // Third and fourth layers - BigO2 and maybe 2*Corners: + for (int Row = 0; Row < 2; Row++) + { + PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x3fffffff + Row * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + h--; + } // for Row - 2* +} + + + + + void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) { // Half chance for a spruce, half for a pine: diff --git a/src/Generating/Trees.h b/src/Generating/Trees.h index f5148ad6f..514158eb7 100644 --- a/src/Generating/Trees.h +++ b/src/Generating/Trees.h @@ -63,6 +63,9 @@ void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a /// Generates an image of a random birch tree void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); +/// Generates an image of a random large birch tree +void GetTallBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks,sSetBlockVector & a_OtherBlocks); + /// Generates an image of a random conifer tree void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); From 248ba1ea9f6826234535c2a777b3834fbe264e0d Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 29 Dec 2013 12:51:58 +0100 Subject: [PATCH 112/118] Added HOOK_PLUGINS_LOADED. This fixes #482. --- MCServer/Plugins/Debuggers/Debuggers.lua | 9 ++++++++ src/Bindings/LuaState.h | 19 +++++++++++++++++ src/Bindings/Plugin.h | 1 + src/Bindings/PluginLua.cpp | 18 ++++++++++++++++ src/Bindings/PluginLua.h | 1 + src/Bindings/PluginManager.cpp | 26 +++++++++++++++++++++--- src/Bindings/PluginManager.h | 2 ++ 7 files changed, 73 insertions(+), 3 deletions(-) diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index c9a610f71..8f2fa3682 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -27,6 +27,7 @@ function Initialize(Plugin) cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity); cPluginManager.AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick); cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); + cPluginManager.AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); PM = cRoot:Get():GetPluginManager(); PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities"); @@ -524,6 +525,14 @@ end +function OnPluginsLoaded() + LOG("All plugins loaded"); +end + + + + + function OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc) -- Get the topmost block coord: local Height = a_ChunkDesc:GetHeight(0, 0); diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 15b0cdeff..40bb67e69 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -240,6 +240,25 @@ public: return CallFunction(0); } + /// Call any 0-param 1-return Lua function in a single line: + template< + typename FnT, typename RetT1 + > + bool Call(FnT a_FnName, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + /// Call any 1-param 1-return Lua function in a single line: template< typename FnT, typename ArgT1, typename RetT1 diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 9a3c2383e..ee0f8a062 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -82,6 +82,7 @@ public: virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; + virtual bool OnPluginsLoaded (void) = 0; virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 0e5a66cd6..69e83fb0a 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -910,6 +910,24 @@ bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_Block +bool cPluginLua::OnPluginsLoaded(void) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + bool ret = false; + m_LuaState.Call((int)(**itr), cLuaState::Return, ret); + res = res || ret; + } + return res; +} + + + + + bool cPluginLua::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) { cCSLock Lock(m_CriticalSection); diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index e1e274c72..1b257285e 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -79,6 +79,7 @@ public: virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + virtual bool OnPluginsLoaded (void) override; virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 832dc4249..ffffe1a23 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -118,7 +118,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) int KeyNum = a_SettingsIni.FindKey("Plugins"); // If it does, how many plugins are there? - unsigned int NumPlugins = ((KeyNum != -1) ? (a_SettingsIni.GetNumValues(KeyNum)) : 0); + int NumPlugins = ((KeyNum != -1) ? (a_SettingsIni.GetNumValues(KeyNum)) : 0); if (KeyNum == -1) { @@ -126,7 +126,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) } else if (NumPlugins > 0) { - for(unsigned int i = 0; i < NumPlugins; i++) + for (int i = 0; i < NumPlugins; i++) { AString ValueName = a_SettingsIni.GetValueName(KeyNum, i); if (ValueName.compare("Plugin") == 0) @@ -136,7 +136,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) { if (m_Plugins.find(PluginFile) != m_Plugins.end()) { - LoadPlugin( PluginFile ); + LoadPlugin(PluginFile); } } } @@ -155,6 +155,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) { LOG("-- Loaded 1 Plugin --"); } + CallHookPluginsLoaded(); } @@ -987,6 +988,25 @@ bool cPluginManager::CallHookPlayerUsingItem(cPlayer & a_Player, int a_BlockX, i +bool cPluginManager::CallHookPluginsLoaded(void) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLUGINS_LOADED); + if (Plugins == m_Hooks.end()) + { + return false; + } + bool res = false; + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + res = !(*itr)->OnPluginsLoaded() || res; + } + return res; +} + + + + + bool cPluginManager::CallHookPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) { HookMap::iterator Plugins = m_Hooks.find(HOOK_POST_CRAFTING); diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 04d6470c7..5abb8be84 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -94,6 +94,7 @@ public: // tolua_export HOOK_PLAYER_USED_ITEM, HOOK_PLAYER_USING_BLOCK, HOOK_PLAYER_USING_ITEM, + HOOK_PLUGINS_LOADED, HOOK_POST_CRAFTING, HOOK_PRE_CRAFTING, HOOK_SPAWNED_ENTITY, @@ -181,6 +182,7 @@ public: // tolua_export bool CallHookPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); bool CallHookPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); bool CallHookPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); + bool CallHookPluginsLoaded (void); bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity); From b84cd0b3c573d584d2dd73b9e3866cd990b9fd49 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 29 Dec 2013 13:15:46 +0100 Subject: [PATCH 113/118] APIDump: Documented OnPluginsLoaded. --- .../Plugins/APIDump/Hooks/OnPluginsLoaded.lua | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua diff --git a/MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua b/MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua new file mode 100644 index 000000000..d36cdf5c5 --- /dev/null +++ b/MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua @@ -0,0 +1,79 @@ +return +{ + HOOK_PLUGINS_LOADED = + { + CalledWhen = "All the enabled plugins have been loaded", + DefaultFnName = "OnPluginsLoaded", -- also used as pagename + Desc = [[ + This callback gets called when the server finishes loading and initializing plugins. This is the + perfect occasion for a plugin to query other plugins and possibly start communicating with them using + the {{cPluginManager}}:Call() function. + ]], + Params = {}, + Returns = [[ + The return value is ignored, all registered callbacks are called. + ]], + CodeExamples = + { + { + Title = "CoreMessaging", + Desc = [[ + This example shows how to implement the CoreMessaging functionality - messages to players will be + sent through the Core plugin, formatted by that plugin. As a fallback for when the Core plugin is + not present, the messages are sent directly by this code, unformatted. + ]], + Code = [[ +-- These are the fallback functions used when the Core is not present: +local function SendMessageFallback(a_Player, a_Message) + a_Player:SendMessage(a_Message); +end + +local function SendMessageSuccessFallback(a_Player, a_Message) + a_Player:SendMessage(a_Message); +end + +local function SendMessageFailureFallback(a_Player, a_Message) + a_Player:SendMessage(a_Message); +end + +-- These three "variables" will hold the actual functions to call. +-- By default they are initialized to the Fallback variants, but will be redirected to Core when all plugins load +SendMessage = SendMessageFallback; +SendMessageSuccess = SendMessageSuccessFallback; +SendMessageFailure = SendMessageFailureFallback; + +-- The callback tries to connect to the Core, if successful, overwrites the three functions with Core ones +local function OnPluginsLoaded() + local CorePlugin = cPluginManager:Get():GetPlugin("Core"); + if (CorePlugin == nil) then + -- The Core is not loaded, keep the Fallback functions + return; + end + + -- Overwrite the three functions with Core functionality: + SendMessage = function(a_Player, a_Message) + CorePlugin:Call("SendMessage", a_Player, a_Message); + end + SendMessageSuccess = function(a_Player, a_Message) + CorePlugin:Call("SendMessageSuccess", a_Player, a_Message); + end + SendMessageFailure = function(a_Player, a_Message) + CorePlugin:Call("SendMessageFailure", a_Player, a_Message); + end +end + +-- Global scope, register the callback: +cPluginManager.AddHook(cPluginManager.HOOK_PLUGINS_LOADED, CoreMessagingPluginsLoaded); + + +-- Usage, anywhere else in the plugin: +SendMessageFailure(a_Player, "Cannot teleport to player, the destination player " .. PlayerName .. " was not found"); + ]], + }, + } , -- CodeExamples + }, -- HOOK_PLUGINS_LOADED +} + + + + From 8406a37ac262bdf135b35f8a8f6f9170553c645e Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 29 Dec 2013 13:23:30 +0100 Subject: [PATCH 114/118] APIDump: The PRE html tag has tab width set to 2 spaces. --- MCServer/Plugins/APIDump/main.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MCServer/Plugins/APIDump/main.css b/MCServer/Plugins/APIDump/main.css index 797079873..aa26bd186 100644 --- a/MCServer/Plugins/APIDump/main.css +++ b/MCServer/Plugins/APIDump/main.css @@ -30,6 +30,11 @@ pre { border: 1px solid #ccc; background-color: #eee; + -moz-tab-size: 2; + -o-tab-size: 2; + -webkit-tab-size: 2; + -ms-tab-size: 2; + tab-size: 2; } body From 61af77a5c5c99ec82decc24ff8fca899f959e48a Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 29 Dec 2013 13:24:38 +0100 Subject: [PATCH 115/118] APIDump: Static files overwrite their destination. --- MCServer/Plugins/APIDump/main_APIDump.lua | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua index ff837ec4e..b3a95eb22 100644 --- a/MCServer/Plugins/APIDump/main_APIDump.lua +++ b/MCServer/Plugins/APIDump/main_APIDump.lua @@ -321,6 +321,7 @@ function DumpAPIHtml() cFile:CreateFolder("API/Static"); local localFolder = g_Plugin:GetLocalFolder(); for idx, fnam in ipairs(cFile:GetFolderContents(localFolder .. "/Static")) do + cFile:Delete("API/Static/" .. fnam); cFile:Copy(localFolder .. "/Static/" .. fnam, "API/Static/" .. fnam); end @@ -428,11 +429,18 @@ function DumpAPIHtml() WriteClasses(f, API, ClassMenu); WriteHooks(f, Hooks, UndocumentedHooks, HookNav); - -- Copy the static files to the output folder (overwrite any existing): - cFile:Copy(g_Plugin:GetLocalFolder() .. "/main.css", "API/main.css"); - cFile:Copy(g_Plugin:GetLocalFolder() .. "/prettify.js", "API/prettify.js"); - cFile:Copy(g_Plugin:GetLocalFolder() .. "/prettify.css", "API/prettify.css"); - cFile:Copy(g_Plugin:GetLocalFolder() .. "/lang-lua.js", "API/lang-lua.js"); + -- Copy the static files to the output folder: + local StaticFiles = + { + "main.css", + "prettify.js", + "prettify.css", + "lang-lua.js", + }; + for idx, fnam in ipairs(StaticFiles) do + cFile:Delete("API/" .. fnam); + cFile:Copy(g_Plugin:GetLocalFolder() .. "/" .. fnam, "API/" .. fnam); + end -- List the documentation problems: LOG("Listing leftovers..."); From 61254d035643de23013b8995a7385ac79a673cbd Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 29 Dec 2013 14:48:58 +0100 Subject: [PATCH 116/118] APIDump: Fixed a factual error in OnPluginsLoaded description. --- MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua b/MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua index d36cdf5c5..0d5b7271e 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnPluginsLoaded.lua @@ -6,8 +6,8 @@ return DefaultFnName = "OnPluginsLoaded", -- also used as pagename Desc = [[ This callback gets called when the server finishes loading and initializing plugins. This is the - perfect occasion for a plugin to query other plugins and possibly start communicating with them using - the {{cPluginManager}}:Call() function. + perfect occasion for a plugin to query other plugins through {{cPluginManager}}:GetPlugin() and + possibly start communicating with them using the {{cPlugin}}:Call() function. ]], Params = {}, Returns = [[ From e5f9c97e3d27fcae2825e795a82a95270c4a1460 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Mon, 30 Dec 2013 11:22:52 +0000 Subject: [PATCH 117/118] Create GETTING-STARTED.md --- GETTING-STARTED.md | 119 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 GETTING-STARTED.md diff --git a/GETTING-STARTED.md b/GETTING-STARTED.md new file mode 100644 index 000000000..8b6f932ab --- /dev/null +++ b/GETTING-STARTED.md @@ -0,0 +1,119 @@ +Hello! Thanks for wanting to work on this project :smile:, and I hope that this file will help you somewhat in getting all set up and running. I'll go through the basics of getting the projet environment set up, the code organization and style, and general development practices. I'll also show you some good issues to start off working on to get yourself familiarised with the code. + +Minecraft Basics +---------------- + +If you don't play Minecraft or don't have a great knowledge of the basic systems, you should get to know them. The [Minecraft Wiki](http://minecraft.gamepedia.com/Minecraft_Wiki) is quite useful for this task, although some youtubers are also fairly good at teaching the basics and just playing is quite good too. + +I'd say that the important topics are: + +* Differnt types of blocks and how they act. +* Mobs, what they do and how. +* Redstone, pistons, and automation. +* Farming +* Fighting, health and the hunger system. + +Useful Resources +---------------- + + * [Minecraft Wiki](http://minecraft.gamepedia.com/Minecraft_Wiki) + * [Minecraft Protocol Wiki](http://wiki.vg) + +Setting up a Dev Environment +============================ + +Requirements +------------ + +**Linux/BSD/Solaris/OSX:** + +You'll need the basic C++ build tools: + + * gcc (or clang or another C compiler) + * g++ (or clang++ or another C++ compiler) + * make + +You'll also need CMake to generate the makefile to build from. + +**Windows:** + +If you use Windows, your best bet is the MSVC2008 (available as a free download in the Express edition from MS) or MSVS2013 (ditto), solution files for both are currently in the repo. + +Setting up the Repo +------------------- + +Next, you'll need to set up the repo. You can make a fork and work on that then PR in, or I can set you up with membership for the repo so you can work on branches here (still use PRs though, they're great tools and for the first few you'll definitely need some changes). If you want membership to the repo, just create an issue and I can set you up. + +Once you've cloned, you need to pull down the submodules: + + git submodule init + git submodule update + +After that they should come down automatically when you pull but it's not bad to refresh every once in a while. + +Repo Arrangement +--------------------------- + +The MCServer repo has recently been rearranged for better code separation and other things, but basically it's split into a few areas: + + * `src` + This holds all of the MCServer source code, and is where most development takes place. + It's split into logical areas for blocks, the protocol handling and other things. + * `lib` + This holds all the 3rd party libraries for MCServer. You basically don't need to touch these, and we're thinking of switching them into submodules soon. + * `MCServer` + This folder isn't greatly named, but it contains the default plugins and environment to actually run the server. You'll find the executable (named `MCServer`) here and in the `plugins` subdir the default plugins. The config files are also stored here. Config files with `.example.ini` on the end are generated by the server or source control and should be left alone, instead you should copy them to a file without the `example` in the name which will be prioritised over the generated ones. + +Code Styles +------------------ + +Mainly follow the code styles in [CONTRIBUTING.md](https://github.com/mc-server/MCServer/blob/master/CONTRIBUTING.md), which is definitely an important read. + + +How to Build +------------------ + +**Linux/BSD/Solaris/OSX:** + +Follow the instructions in [COMPILING.md](https://github.com/mc-server/MCServer/blob/master/COMPILING.md). You probably want to build in debug mode (when you're developing) for console alerts and debugging capability, even though it's much slower for everyday use. + +Basically, the process is: + + cmake . -DCMAKE_BUILD_TYPE=DEBUG && make + +**Windows:** + +You need to first execute the `src/Bindings/AllToLua.bat` script file, then just open the solution file in your MSVC of choice and build. + +How to Run +---------- + +The server can be run (on *nix) by a simple `./MCServer` in the `MCServer` directory. On first run it will generate the world and start a server on the default port (configurable in `settings.ini`) so you can connect in minecraft via `localhost`. + +Where to Get Started +------------------------------- + +There are a few fairly easy issues for you to get started with, as well as some more difficult but interesting ones. + +**Easy**: + + * #288 + * #385 + * #402 + * #388 + * #380 + * Clean up some of the compiler warnings. (Check [Travis CI](http://travis-ci.org/mc-server/MCServer) for a list of them.) With clang, there are over 10000 lines of warnings to clean up. + +**More Difficult**: + + * #17 + * #418 + * #398 + +You may also want to write some plugins. They are written in lua, with excellent API documentation available via [APIDump](http://mc-server.xoft.cz/LuaAPI). The [Core](https://github.com/mc-server/Core) plugin should also help quite a bit here. + +Special Things +--------------------- + + * MCServer uses ToLUA for the Lua API, and you'll really have to ask @madmaxoft for how to export stuff and @worktycho for how to add stuff to the auto generated bindings (he just re-worked it with CMake). + * Ask questions as much as you like, we're here to help :smiley: From 17b56579ebb1b5d6a79ce095df44a9ef37de223c Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Mon, 30 Dec 2013 11:41:37 +0000 Subject: [PATCH 118/118] Update GETTING-STARTED.md --- GETTING-STARTED.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GETTING-STARTED.md b/GETTING-STARTED.md index 8b6f932ab..66318a629 100644 --- a/GETTING-STARTED.md +++ b/GETTING-STARTED.md @@ -18,6 +18,8 @@ Useful Resources * [Minecraft Wiki](http://minecraft.gamepedia.com/Minecraft_Wiki) * [Minecraft Protocol Wiki](http://wiki.vg) + * [Lua API Documentation](http://mc-server.xoft.cz/LuaAPI) + * [VS2008 Download](http://stackoverflow.com/questions/15318560/visual-c-2008-express-download-link-dead) Setting up a Dev Environment ============================