1
0

Merge branch 'master' into GeneratorArticle

This commit is contained in:
Mattes D 2014-06-01 10:43:37 +02:00
commit 2059944d1e
138 changed files with 20907 additions and 913 deletions

1
.gitignore vendored
View File

@ -48,6 +48,7 @@ world_nether
CMakeFiles/
cmake_install.cmake
CMakeCache.txt
CTestTestfile.cmake
Makefile
*.a

View File

@ -2,8 +2,15 @@ language: cpp
compiler:
- gcc
- clang
before_install:
- if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi
# Build MCServer
script: cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1 && make -j 2 && cd MCServer/ && (echo stop | $MCSERVER_PATH)
script: ./CIbuild.sh
after_success:
- ./uploadCoverage.sh
env:
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer
@ -11,6 +18,11 @@ env:
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE TRAVIS_MCSERVER_FORCE32=1 MCSERVER_PATH=./MCServer
- TRAVIS_MCSERVER_BUILD_TYPE=DEBUG TRAVIS_MCSERVER_FORCE32=1 MCSERVER_PATH=./MCServer_debug
matrix:
include:
- compiler: gcc
env: TRAVIS_MCSERVER_BUILD_TYPE=COVERAGE MCSERVER_PATH=./MCServer
# Notification Settings
notifications:
email:

11
CIbuild.sh Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
cmake . -DBUILD_TOOLS=1 -DSELF_TEST=1;
make -j 2;
make -j 2 test;
cd MCServer/;
if [ "$TRAVIS_MCSERVER_BUILD_TYPE" != "COVERAGE" ]
then echo stop | $MCSERVER_PATH;
fi

View File

@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 2.6)
cmake_minimum_required (VERSION 2.8.2)
# Without this, the MSVC variable isn't defined for MSVC builds ( http://www.cmake.org/pipermail/cmake/2011-November/047130.html )
enable_language(CXX C)
@ -14,6 +14,10 @@ if(DEFINED ENV{TRAVIS_MCSERVER_FORCE32})
set(FORCE32 $ENV{TRAVIS_MCSERVER_FORCE32})
endif()
if(DEFINED ENV{TRAVIS_BUILD_WITH_COVERAGE})
set(BUILD_WITH_COVERAGE $ENV{TRAVIS_BUILD_WITH_COVERAGE})
endif()
# This has to be done before any flags have been set up.
if(${BUILD_TOOLS})
add_subdirectory(Tools/MCADefrag/)
@ -69,3 +73,8 @@ set_exe_flags()
add_subdirectory (src)
if(${SELF_TEST})
enable_testing()
add_subdirectory (tests)
endif()

View File

@ -27,7 +27,7 @@ Code Stuff
- The only exception: a `switch` statement with all `case` statements being a single short statement is allowed to use the short brace-less form.
- These two rules really mean that indent is governed by braces
* Add an empty last line in all source files (GCC and GIT can complain otherwise)
* Use doxy-comments for functions in the header file, format as `/** Description */`
* All new public functions in all classes need documenting comments on what they do and what behavior they follow, use doxy-comments formatted as `/** Description */`. Do not use asterisks on additional lines in multi-line comments.
* Use spaces after the comment markers: `// Comment` instead of `//Comment`

4
MCServer/.gitignore vendored
View File

@ -30,3 +30,7 @@ motd.txt
*.xml
mcserver_api.lua
# Ignore the webadmin certs / privkey, so that no-one commits theirs by accident:
webadmin/httpscert.crt
webadmin/httpskey.pem

View File

@ -39,7 +39,8 @@ pre
body
{
min-width: 800px;
min-width: 400px;
max-width: 1200px;
width: 95%;
margin: 10px auto;
background-color: white;

@ -1 +1 @@
Subproject commit 5c8557d4fdfa580c100510cde07a1a778ea2e244
Subproject commit 3790f78d3f7503ff33a423b8e73e81a275562783

View File

@ -0,0 +1,11 @@
echo This script generates the certificate and private key for the https webadmin
echo Note that the generated certificate is self-signed, and therefore not trusted by browsers
echo Note that this script requires openssl to be installed and in PATH
echo.
echo When OpenSSL asks you for Common Name, you need to enter the fully qualified domain name of the server, that is, e. g. gallery.xoft.cz
echo.
echo If OpenSSL fails with an error, "WARNING: can't open config file: /usr/local/ssl/openssl.cnf", you need to run this script as an administrator
echo.
openssl req -x509 -newkey rsa:2048 -keyout httpskey.pem -out httpscert.crt -days 3650 -nodes
pause

View File

@ -0,0 +1,10 @@
#!/bin/bash
echo "This script generates the certificate and private key for the https webadmin"
echo "Note that the generated certificate is self-signed, and therefore not trusted by browsers"
echo "Note that this script requires openssl to be installed and in PATH"
echo ""
echo "When OpenSSL asks you for Common Name, you need to enter the fully qualified domain name of the server, that is, e. g. gallery.xoft.cz"
echo ""
openssl req -x509 -newkey rsa:2048 -keyout httpskey.pem -out httpscert.crt -days 3650 -nodes

View File

@ -1,38 +1,39 @@
MCServer
MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer.svg)](https://travis-ci.org/mc-server/MCServer) [![Support via Gittip](http://img.shields.io/gittip/mcs_team.svg)](https://www.gittip.com/mcs_team) [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74)
========
**Current Protocol Supported:** Minecraft v1.2 -> v1.7
MCServer is a performant C++ Minecraft server designed for use in memory and cpu-limited places, or just to make regular server perform better.
MCServer is a Minecraft server that is written in C++ and designed to be efficient with memory and CPU, as well as having a flexible Lua Plugin API.
MCServer can run on PCs, Macs, and *nix. This includes android phones and tablets as well as Raspberry Pis.
We currently support the protocol from Minecraft 1.2 all the way up to Minecraft 1.7.9.
Installation
------------
To install MCServer, you can either download the repository and compile it, or download a pre-compiled version.
Normally, you will want to download a pre-compiled version of MCServer from one of the buildservers:
If you've cloned the repository using Git, you need to pull down the submodules (core plugins, some dependencies). This can be achieved with `git submodule init` and then on a regular basis (to keep up to date) `git submodule update`.
* [Linux and Raspberry Pi](http://ci.bearbin.net) (Bearbin's CI Server)
* [Windows](http://mc-server.xoft.cz) (xoft's nightly build service)
If you downloaded a ZIP file of the sources instead, you will need to download PolarSSL, too, from https://github.com/polarssl/polarssl , and unpack it into the `lib/polarssl` folder. You will also need to manually download all the plugins that you want included.
You simply need to download and extract these files before you can use the server.
Compilation instructions are available in the COMPILING file.
Linux builds can be downloaded from [Bearbin's CI Service](http://ci.bearbin.net) and Windows builds from xoft's [nightly build service](http://mc-server.xoft.cz).
After you've extracted the files, simply run the MCServer executable.
If you're a more advanced user, you may want to compile the server yourself for more performance. See the [COMPILING.md](https://github.com/mc-server/MCServer/blob/master/COMPILING.md) file for more details.
Contributing
------------
MCServer is licensed under the Apache license V2, and we welcome anybody to fork and submit a Pull Request back with their changes, and if you want to join as a permanent member we can add you to the team.
Check out the [CONTRIBUTING.md](https://github.com/mc-server/MCServer/blob/master/CONTRIBUTING.md) file for more details.
Other Stuff
-----------
For other stuff, including plugins and discussion, check the [forums](http://forum.mc-server.org) and [wiki](http://wiki.mc-server.org/).
For other stuff, including plugins and discussion, check the [forums](http://forum.mc-server.org) and [Plugin API](http://mc-server.xoft.cz/LuaAPI/).
Earn bitcoins for commits or donate to reward the MCServer developers: [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74)
Travis CI: [![Build Status](https://travis-ci.org/mc-server/MCServer.png?branch=master)](https://travis-ci.org/mc-server/MCServer)
Support Us on Gittip: [![Support via Gittip](http://img.shields.io/gittip/mcs_team.svg)](https://www.gittip.com/mcs_team)
Travis CI: [![Build Status](http://img.shields.io/travis/mc-server/MCServer.svg)](https://travis-ci.org/mc-server/MCServer)

View File

@ -1,32 +1,41 @@
macro (add_flags_lnk FLAGS)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} ${FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_COVERAGE} ${FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "${CMAKE_SHARED_LINKER_FLAGS_COVERAGE} ${FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} ${FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS_COVERAGE "${CMAKE_MODULE_LINKER_FLAGS_COVERAGE} ${FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${FLAGS}")
endmacro()
macro(add_flags_cxx FLAGS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} ${FLAGS}")
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE} ${FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}")
endmacro()
macro(set_flags)
# Add the preprocessor macros used for distinguishing between debug and release builds (CMake does this automatically for MSVC):
if (NOT MSVC)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/lib/cmake-coverage/")
include(CodeCoverage)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -D_DEBUG")
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE} -D_DEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG")
endif()
if(MSVC)
@ -42,9 +51,10 @@ macro(set_flags)
elseif(APPLE)
#on os x clang adds pthread for us but we need to add it for gcc
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
add_flags_cxx("-stdlib=libc++")
add_flags_lnk("-stdlib=libc++")
else()
@ -57,6 +67,7 @@ macro(set_flags)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
endif()
@ -97,10 +108,12 @@ macro(set_lib_flags)
string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "/W3" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
else()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -w")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -w")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -w")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -w")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -w")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -w")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -w")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -w")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -w")
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE} -w")
endif()
# On Unix we use two dynamic loading libraries dl and ltdl.
@ -118,8 +131,8 @@ endmacro()
macro(enable_profile)
# Declare the flags used for profiling builds:
if (MSVC)
set (CXX_PROFILING "")
set (LNK_PROFILING "/PROFILE")
set (CXX_PROFILING "/Zi")
set (LNK_PROFILING "/PROFILE /DEBUG")
else()
set (CXX_PROFILING "-pg")
set (LNK_PROFILING "-pg")
@ -128,7 +141,7 @@ macro(enable_profile)
# Declare the profiling configurations:
SET(CMAKE_CXX_FLAGS_DEBUGPROFILE
"${CMAKE_CXX_FLAGS_DEBUG} ${PCXX_ROFILING}"
"${CMAKE_CXX_FLAGS_DEBUG} ${CXX_PROFILING}"
CACHE STRING "Flags used by the C++ compiler during profile builds."
FORCE )
SET(CMAKE_C_FLAGS_DEBUGPROFILE
@ -171,7 +184,11 @@ macro(enable_profile)
CMAKE_EXE_LINKER_FLAGS_RELEASEPROFILE
CMAKE_SHARED_LINKER_FLAGS_RELEASEPROFILE )
# The configuration types need to be set after their respective c/cxx/linker flags and before the project directive
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile" CACHE STRING "" FORCE)
if(MSVC)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile" CACHE STRING "" FORCE)
else()
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile;Coverage" CACHE STRING "" FORCE)
endif()
endmacro()
macro(set_exe_flags)
@ -180,10 +197,12 @@ macro(set_exe_flags)
# We do not do that for MSVC since MSVC produces an awful lot of warnings for its own STL headers;
# the important warnings are turned on using #pragma in Globals.h
if (NOT MSVC)
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
string(REPLACE "-w" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
string(REPLACE "-w" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE}")
string(REPLACE "-w" "" CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE}")
add_flags_cxx("-Wall -Wextra -Wno-unused-parameter -Wno-error=switch")
# we support non-IEEE 754 fpus so can make no guarentees about error

View File

@ -22,6 +22,15 @@
#define ALIGN_8
#define ALIGN_16
#define FORMATSTRING(formatIndex, va_argsIndex)
// MSVC has its own custom version of zu format
#define SIZE_T_FMT "%Iu"
#define SIZE_T_FMT_PRECISION(x) "%" #x "Iu"
#define SIZE_T_FMT_HEX "%Ix"
#define NORETURN __declspec(noreturn)
#elif defined(__GNUC__)
// TODO: Can GCC explicitly mark classes as abstract (no instances can be created)?
@ -39,6 +48,12 @@
#define stricmp strcasecmp
#define FORMATSTRING(formatIndex,va_argsIndex)
#define SIZE_T_FMT "%zu"
#define SIZE_T_FMT_PRECISION(x) "%" #x "zu"
#define SIZE_T_FMT_HEX "%zx"
#define NORETURN __attribute((__noreturn__))
#else
#error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler"

View File

@ -38,9 +38,9 @@ set(SHARED_SRC
../../src/MCLogger.cpp
../../src/PolarSSL++/AesCfb128Decryptor.cpp
../../src/PolarSSL++/AesCfb128Encryptor.cpp
../../src/PolarSSL++/CryptoKey.cpp
../../src/PolarSSL++/CtrDrbgContext.cpp
../../src/PolarSSL++/EntropyContext.cpp
../../src/PolarSSL++/PublicKey.cpp
../../src/PolarSSL++/RsaPrivateKey.cpp
)
set(SHARED_HDR
@ -50,9 +50,9 @@ set(SHARED_HDR
../../src/MCLogger.h
../../src/PolarSSL++/AesCfb128Decryptor.h
../../src/PolarSSL++/AesCfb128Encryptor.h
../../src/PolarSSL++/CryptoKey.h
../../src/PolarSSL++/CtrDrbgContext.h
../../src/PolarSSL++/EntropyContext.h
../../src/PolarSSL++/PublicKey.h
../../src/PolarSSL++/RsaPrivateKey.h
)
set(SHARED_OSS_SRC

View File

@ -7,7 +7,7 @@
#include "Connection.h"
#include "Server.h"
#include <iostream>
#include "PolarSSL++/PublicKey.h"
#include "PolarSSL++/CryptoKey.h"
#ifdef _WIN32
#include <direct.h> // For _mkdir()
@ -2900,7 +2900,7 @@ void cConnection::SendEncryptionKeyResponse(const AString & a_ServerPublicKey, c
Byte SharedSecret[16];
Byte EncryptedSecret[128];
memset(SharedSecret, 0, sizeof(SharedSecret)); // Use all zeroes for the initial secret
cPublicKey PubKey(a_ServerPublicKey);
cCryptoKey PubKey(a_ServerPublicKey);
int res = PubKey.Encrypt(SharedSecret, sizeof(SharedSecret), EncryptedSecret, sizeof(EncryptedSecret));
if (res < 0)
{

View File

@ -0,0 +1,160 @@
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# USAGE:
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt:
# INCLUDE(CodeCoverage)
#
# 3. Set compiler flags to turn off optimization and enable coverage:
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
#
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
# which runs your test executable and produces a lcov code coverage report:
# Example:
# SETUP_TARGET_FOR_COVERAGE(
# my_coverage_target # Name for custom target.
# test_driver # Name of the test driver executable that runs the tests.
# # NOTE! This should always have a ZERO as exit code
# # otherwise the coverage generation will not complete.
# coverage # Name of output directory.
# )
#
# 4. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
#
# Check prereqs
FIND_PROGRAM( GCOV_PATH gcov )
FIND_PROGRAM( LCOV_PATH lcov )
FIND_PROGRAM( GENHTML_PATH genhtml )
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
IF(NOT GCOV_PATH)
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
ENDIF() # NOT GCOV_PATH
IF(NOT CMAKE_COMPILER_IS_GNUCXX)
# Clang version 3.0.0 and greater now supports gcov as well.
MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.")
IF(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
ENDIF()
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
SET(CMAKE_CXX_FLAGS_COVERAGE
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
SET(CMAKE_C_FLAGS_COVERAGE
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
MARK_AS_ADVANCED(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage"))
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
# Param _targetname The name of new the custom make target
# Param _testrunner The name of the target which runs the tests.
# MUST return ZERO always, even on errors.
# If not, no coverage report will be created!
# Param _outputname lcov output is generated as _outputname.info
# HTML report is generated in _outputname/index.html
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname)
IF(NOT LCOV_PATH)
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
ENDIF() # NOT LCOV_PATH
IF(NOT GENHTML_PATH)
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
ENDIF() # NOT GENHTML_PATH
# Setup target
ADD_CUSTOM_TARGET(${_targetname}
# Cleanup lcov
${LCOV_PATH} --directory . --zerocounters
# Run tests
COMMAND ${_testrunner} ${ARGV3}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info
COMMAND ${LCOV_PATH} --remove ${_outputname}.info 'tests/*' '/usr/*' --output-file ${_outputname}.info.cleaned
COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned
COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show info where to find the report
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
COMMAND ;
COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
)
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
# Param _targetname The name of new the custom make target
# Param _testrunner The name of the target which runs the tests
# Param _outputname cobertura output is generated as _outputname.xml
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname)
IF(NOT PYTHON_EXECUTABLE)
MESSAGE(FATAL_ERROR "Python not found! Aborting...")
ENDIF() # NOT PYTHON_EXECUTABLE
IF(NOT GCOVR_PATH)
MESSAGE(FATAL_ERROR "gcovr not found! Aborting...")
ENDIF() # NOT GCOVR_PATH
ADD_CUSTOM_TARGET(${_targetname}
# Run tests
${_testrunner} ${ARGV3}
# Running gcovr
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running gcovr to produce Cobertura code coverage report."
)
# Show info where to find the report
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
COMMAND ;
COMMENT "Cobertura code coverage report saved in ${_outputname}.xml."
)
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA

View File

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -20,6 +20,12 @@ endif()
# Lua needs to be linked dynamically on Windows and statically on *nix, so that LuaRocks work
if (WIN32)
#for compiliers other than msvc we need to tell lua that its building as a dll
if (NOT MSVC)
add_flags_cxx(-DLUA_BUILD_AS_DLL=1)
endif()
add_library(lua SHARED ${SOURCE})
set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/MCServer)

View File

@ -1,5 +1,11 @@
if(NOT TARGET polarssl)
message("including polarssl")
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl EXCLUDE_FROM_ALL )
if (SELF_TEST)
set(ENABLE_TESTING OFF CACHE BOOL "Disable tests")
set(ENABLE_PROGRAMS OFF CACHE BOOL "Disable programs")
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl)
else()
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl EXCLUDE_FROM_ALL)
endif()
endif()

View File

@ -17,6 +17,10 @@ if (WIN32)
source_group("Sources" FILES ${SOURCE})
endif()
# FreeBSD requires us to define this to get POSIX 2001 standard
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
add_flags_cxx(-D__POSIX_VISIBLE=200112)
endif()
add_library(sqlite ${SOURCE})

View File

@ -76,6 +76,7 @@ $cfile "../CompositeChat.h"
$cfile "../Map.h"
$cfile "../MapManager.h"
$cfile "../Scoreboard.h"
$cfile "../Statistics.h"

View File

@ -2864,8 +2864,8 @@ static int tolua_cCompositeChat_SetMessageType(lua_State * tolua_S)
}
// Set the type:
int MessageType;
L.GetStackValue(1, MessageType);
int MessageType = mtCustom;
L.GetStackValue(2, MessageType);
self->SetMessageType((eMessageType)MessageType);
// Cut away everything from the stack except for the cCompositeChat instance; return that:

View File

@ -10,7 +10,7 @@
#pragma once
#include "StringUtils.h"
// tolua_begin

View File

@ -9,7 +9,7 @@
#include "OSSupport/GZipFile.h"
#include "Blocks/BlockHandler.h"
#include "Cuboid.h"
#include "ChunkData.h"
@ -1835,116 +1835,88 @@ bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes)
void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer)
{
if (m_Area.m_BlockTypes == NULL)
{
// Don't want BlockTypes
return;
}
int SizeY = m_Area.m_Size.y;
int MinY = m_Origin.y;
// SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
// OffX, OffZ are the offsets of the current chunk data from the area origin
// BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
int SizeX = cChunkDef::Width;
int SizeZ = cChunkDef::Width;
int OffX, OffZ;
int BaseX, BaseZ;
OffX = m_CurrentChunkX * cChunkDef::Width - m_Origin.x;
if (OffX < 0)
{
BaseX = -OffX;
SizeX += OffX; // SizeX is decreased, OffX is negative
OffX = 0;
}
else
{
BaseX = 0;
}
OffZ = m_CurrentChunkZ * cChunkDef::Width - m_Origin.z;
if (OffZ < 0)
{
BaseZ = -OffZ;
SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
OffZ = 0;
}
else
{
BaseZ = 0;
}
// If the chunk extends beyond the area in the X or Z axis, cut off the Size:
if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_Origin.x + m_Area.m_Size.x)
{
SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_Origin.x + m_Area.m_Size.x);
}
if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_Origin.z + m_Area.m_Size.z)
{
SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
}
for (int y = 0; y < SizeY; y++)
{
int ChunkY = MinY + y;
int AreaY = y;
for (int z = 0; z < SizeZ; z++)
if (m_Area.m_BlockTypes != NULL)
{
int ChunkZ = BaseZ + z;
int AreaZ = OffZ + z;
for (int x = 0; x < SizeX; x++)
int SizeY = m_Area.m_Size.y;
int MinY = m_Origin.y;
// SizeX, SizeZ are the dimensions of the block data to copy from the current chunk (size of the geometric union)
// OffX, OffZ are the offsets of the current chunk data from the area origin
// BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
int SizeX = cChunkDef::Width;
int SizeZ = cChunkDef::Width;
int OffX, OffZ;
int BaseX, BaseZ;
OffX = m_CurrentChunkX * cChunkDef::Width - m_Origin.x;
if (OffX < 0)
{
int ChunkX = BaseX + x;
int AreaX = OffX + x;
m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetBlock(a_BlockTypes, ChunkX, ChunkY, ChunkZ);
} // for x
} // for z
} // for y
}
BaseX = -OffX;
SizeX += OffX; // SizeX is decreased, OffX is negative
OffX = 0;
}
else
{
BaseX = 0;
}
OffZ = m_CurrentChunkZ * cChunkDef::Width - m_Origin.z;
if (OffZ < 0)
{
BaseZ = -OffZ;
SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
OffZ = 0;
}
else
{
BaseZ = 0;
}
// If the chunk extends beyond the area in the X or Z axis, cut off the Size:
if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_Origin.x + m_Area.m_Size.x)
{
SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_Origin.x + m_Area.m_Size.x);
}
if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_Origin.z + m_Area.m_Size.z)
{
SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
}
void cBlockArea::cChunkReader::BlockMeta(const NIBBLETYPE * a_BlockMetas)
{
if (m_Area.m_BlockMetas == NULL)
{
// Don't want metas
return;
for (int y = 0; y < SizeY; y++)
{
int ChunkY = MinY + y;
int AreaY = y;
for (int z = 0; z < SizeZ; z++)
{
int ChunkZ = BaseZ + z;
int AreaZ = OffZ + z;
for (int x = 0; x < SizeX; x++)
{
int ChunkX = BaseX + x;
int AreaX = OffX + x;
m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlock(ChunkX, ChunkY, ChunkZ);
} // for x
} // for z
} // for y
}
}
CopyNibbles(m_Area.m_BlockMetas, a_BlockMetas);
}
void cBlockArea::cChunkReader::BlockLight(const NIBBLETYPE * a_BlockLight)
{
if (m_Area.m_BlockLight == NULL)
if (m_Area.m_BlockMetas != NULL)
{
// Don't want light
return;
a_BlockBuffer.CopyMetas(m_Area.m_BlockMetas);
}
CopyNibbles(m_Area.m_BlockLight, a_BlockLight);
}
void cBlockArea::cChunkReader::BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight)
{
if (m_Area.m_BlockSkyLight == NULL)
if (m_Area.m_BlockLight != NULL)
{
// Don't want skylight
return;
a_BlockBuffer.CopyBlockLight(m_Area.m_BlockLight);
}
CopyNibbles(m_Area.m_BlockSkyLight, a_BlockSkyLight);
}
if (m_Area.m_BlockSkyLight != NULL)
{
a_BlockBuffer.CopySkyLight(m_Area.m_BlockSkyLight);
}
}
@ -2123,7 +2095,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
return;
} // case msOverwrite
case cBlockArea::msFillAir:
@ -2137,7 +2109,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
return;
} // case msFillAir
case cBlockArea::msImprint:
@ -2151,7 +2123,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
return;
} // case msImprint
case cBlockArea::msLake:
@ -2165,7 +2137,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
return;
} // case msLake
case cBlockArea::msSpongePrint:
@ -2179,7 +2151,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
return;
} // case msSpongePrint
case cBlockArea::msDifference:
@ -2193,7 +2165,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
return;
} // case msDifference
case cBlockArea::msMask:
@ -2207,16 +2179,13 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
return;
} // case msMask
default:
{
LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
ASSERT(!"Unknown block area merge strategy");
break;
}
} // switch (a_Strategy)
LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
ASSERT(!"Unknown block area merge strategy");
return;
}

View File

@ -14,6 +14,7 @@
#include "ForEachChunkProvider.h"
#include "Vector3.h"
#include "ChunkDataCallback.h"
@ -316,11 +317,8 @@ protected:
void CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc);
// cChunkDataCallback overrides:
virtual bool Coords (int a_ChunkX, int a_ChunkZ) override;
virtual void BlockTypes (const BLOCKTYPE * a_BlockTypes) override;
virtual void BlockMeta (const NIBBLETYPE * a_BlockMetas) override;
virtual void BlockLight (const NIBBLETYPE * a_BlockLight) override;
virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override;
virtual bool Coords(int a_ChunkX, int a_ChunkZ) override;
virtual void ChunkData(const cChunkData & a_BlockTypes) override;
} ;
typedef NIBBLETYPE * NIBBLEARRAY;

View File

@ -46,8 +46,30 @@ public:
bool IsWither(void) const { return m_IsWither; }
void Reset(void) { m_IsWither = false; }
} CallbackA, CallbackB;
class cPlayerCallback : public cPlayerListCallback
{
Vector3f m_Pos;
virtual bool Item(cPlayer * a_Player)
{
// TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one
double Dist = (a_Player->GetPosition() - m_Pos).Length();
if (Dist < 50.0)
{
// If player is close, award achievement
a_Player->AwardAchievement(achSpawnWither);
}
return false;
}
public:
cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
} PlayerCallback(Vector3f(a_BlockX, a_BlockY, a_BlockZ));
a_World->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ, CallbackA);
if (!CallbackA.IsWither())
@ -86,6 +108,9 @@ public:
// Spawn the wither:
a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtWither);
// Award Achievement
a_World->ForEachPlayer(PlayerCallback);
return true;
}
@ -113,6 +138,9 @@ public:
// Spawn the wither:
a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtWither);
// Award Achievement
a_World->ForEachPlayer(PlayerCallback);
return true;
}

View File

@ -30,25 +30,25 @@ public:
cByteBuffer(size_t a_BufferSize);
~cByteBuffer();
/// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not
/** Writes the bytes specified to the ringbuffer. Returns true if successful, false if not */
bool Write(const void * a_Bytes, size_t a_Count);
/// Returns the number of bytes that can be successfully written to the ringbuffer
/** Returns the number of bytes that can be successfully written to the ringbuffer */
size_t GetFreeSpace(void) const;
/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
/** Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() */
size_t GetUsedSpace(void) const;
/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
/** Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) */
size_t GetReadableSpace(void) const;
/// Returns the current data start index. For debugging purposes.
/** Returns the current data start index. For debugging purposes. */
size_t GetDataStart(void) const { return m_DataStart; }
/// Returns true if the specified amount of bytes are available for reading
/** Returns true if the specified amount of bytes are available for reading */
bool CanReadBytes(size_t a_Count) const;
/// Returns true if the specified amount of bytes are available for writing
/** Returns true if the specified amount of bytes are available for writing */
bool CanWriteBytes(size_t a_Count) const;
// Read the specified datatype and advance the read pointer; return true if successfully read:
@ -65,7 +65,7 @@ public:
bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8
bool ReadLEInt (int & a_Value);
/// Reads VarInt, assigns it to anything that can be assigned from an UInt32 (unsigned short, char, Byte, double, ...)
/** Reads VarInt, assigns it to anything that can be assigned from an UInt32 (unsigned short, char, Byte, double, ...) */
template <typename T> bool ReadVarInt(T & a_Value)
{
UInt32 v;
@ -91,37 +91,37 @@ public:
bool WriteVarUTF8String (const AString & a_Value); // string length as VarInt, then string as UTF-8
bool WriteLEInt (int a_Value);
/// Reads a_Count bytes into a_Buffer; returns true if successful
/** Reads a_Count bytes into a_Buffer; returns true if successful */
bool ReadBuf(void * a_Buffer, size_t a_Count);
/// Writes a_Count bytes into a_Buffer; returns true if successful
/** Writes a_Count bytes into a_Buffer; returns true if successful */
bool WriteBuf(const void * a_Buffer, size_t a_Count);
/// Reads a_Count bytes into a_String; returns true if successful
/** Reads a_Count bytes into a_String; returns true if successful */
bool ReadString(AString & a_String, size_t a_Count);
/// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String
/** Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String */
bool ReadUTF16String(AString & a_String, size_t a_NumChars);
/// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer
/** Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer */
bool SkipRead(size_t a_Count);
/// Reads all available data into a_Data
/** Reads all available data into a_Data */
void ReadAll(AString & a_Data);
/// Reads the specified number of bytes and writes it into the destinatio bytebuffer. Returns true on success.
/** Reads the specified number of bytes and writes it into the destinatio bytebuffer. Returns true on success. */
bool ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes);
/// Removes the bytes that have been read from the ringbuffer
/** Removes the bytes that have been read from the ringbuffer */
void CommitRead(void);
/// Restarts next reading operation at the start of the ringbuffer
/** Restarts next reading operation at the start of the ringbuffer */
void ResetRead(void);
/// Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication
/** Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication */
void ReadAgain(AString & a_Out);
/// Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs
/** Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs */
void CheckValid(void) const;
protected:
@ -136,7 +136,7 @@ protected:
size_t m_WritePos; // Where the data ends in the ringbuffer
size_t m_ReadPos; // Where the next read will start in the ringbuffer
/// Advances the m_ReadPos by a_Count bytes
/** Advances the m_ReadPos by a_Count bytes */
void AdvanceReadPos(size_t a_Count);
} ;

View File

@ -238,26 +238,12 @@ void cChunk::MarkLoadFailed(void)
void cChunk::GetAllData(cChunkDataCallback & a_Callback)
{
a_Callback.HeightMap (&m_HeightMap);
a_Callback.BiomeData (&m_BiomeMap);
a_Callback.HeightMap(&m_HeightMap);
a_Callback.BiomeData(&m_BiomeMap);
COMPRESSED_BLOCKTYPE Blocks = m_BlockTypes;
Blocks.resize(NumBlocks);
a_Callback.BlockTypes (&Blocks[0]);
a_Callback.LightIsValid(m_IsLightValid);
COMPRESSED_NIBBLETYPE Metas = m_BlockMeta;
Metas.resize(NumBlocks / 2);
a_Callback.BlockMeta (&Metas[0]);
a_Callback.LightIsValid (m_IsLightValid);
COMPRESSED_NIBBLETYPE BlockLights = m_BlockLight;
BlockLights.resize(NumBlocks / 2);
a_Callback.BlockLight (&BlockLights[0]);
COMPRESSED_NIBBLETYPE BlockSkyLights = m_BlockSkyLight;
BlockSkyLights.resize(NumBlocks / 2, 0xff);
a_Callback.BlockSkyLight(&BlockSkyLights[0]);
a_Callback.ChunkData(m_ChunkData);
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
{
@ -296,48 +282,10 @@ void cChunk::SetAllData(
CalculateHeightmap(a_BlockTypes);
}
int IdxWhereNonEmptyStarts = 0;
{ // Blocktype compression
unsigned char Highest = 0;
int X = 0, Z = 0;
m_BlockTypes.clear();
for (int x = 0; x < Width; x++)
{
for (int z = 0; z < Width; z++)
{
unsigned char Height = m_HeightMap[x + z * Width];
if (Height > Highest)
{
Highest = Height;
X = x; Z = z;
}
}
}
IdxWhereNonEmptyStarts = MakeIndexNoCheck(X, Highest + 1, Z);
m_BlockTypes.insert(m_BlockTypes.end(), &a_BlockTypes[0], &a_BlockTypes[IdxWhereNonEmptyStarts]);
}
{ // Blockmeta compression
m_BlockMeta.clear();
m_BlockMeta.insert(m_BlockMeta.end(), &a_BlockMeta[0], &a_BlockMeta[IdxWhereNonEmptyStarts / 2]);
}
if (a_BlockLight != NULL)
{
// Compress blocklight
m_BlockLight.clear();
m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[IdxWhereNonEmptyStarts / 2]);
}
if (a_BlockSkyLight != NULL)
{
// Compress skylight
m_BlockSkyLight.clear();
m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_BlockSkyLight[0], &a_BlockSkyLight[IdxWhereNonEmptyStarts / 2]);
}
m_ChunkData.SetBlockTypes(a_BlockTypes);
m_ChunkData.SetMetas(a_BlockMeta);
m_ChunkData.SetBlockLight(a_BlockLight);
m_ChunkData.SetSkyLight(a_BlockSkyLight);
m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL);
@ -378,15 +326,9 @@ void cChunk::SetLight(
// TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation.
// Postponing until we see how bad it is :)
{ // Compress blocklight
m_BlockLight.clear();
m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[m_BlockTypes.size() / 2]);
}
m_ChunkData.SetBlockLight(a_BlockLight);
{ // Compress skylight
m_BlockSkyLight.clear();
m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_SkyLight[0], &a_SkyLight[m_BlockTypes.size() / 2]);
}
m_ChunkData.SetSkyLight(a_SkyLight);
m_IsLightValid = true;
}
@ -397,8 +339,7 @@ void cChunk::SetLight(
void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes)
{
std::copy(m_BlockTypes.begin(), m_BlockTypes.end(), a_BlockTypes);
std::fill_n(&a_BlockTypes[m_BlockTypes.size()], NumBlocks - m_BlockTypes.size(), E_BLOCK_AIR);
m_ChunkData.CopyBlockTypes(a_BlockTypes);
}
@ -684,8 +625,7 @@ void cChunk::Tick(float a_Dt)
void cChunk::TickBlock(int a_RelX, int a_RelY, int a_RelZ)
{
unsigned Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
cBlockHandler * Handler = BlockHandler(GetBlock(Index));
cBlockHandler * Handler = BlockHandler(GetBlock(a_RelX, a_RelY, a_RelZ));
ASSERT(Handler != NULL); // Happenned on server restart, FS #243
cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap());
cBlockInServerPluginInterface PluginInterface(*this->GetWorld());
@ -810,19 +750,18 @@ void cChunk::CheckBlocks()
{
return;
}
std::vector<unsigned int> ToTickBlocks;
std::vector<Vector3i> ToTickBlocks;
std::swap(m_ToTickBlocks, ToTickBlocks);
cChunkInterface ChunkInterface(m_World->GetChunkMap());
cBlockInServerPluginInterface PluginInterface(*m_World);
for (std::vector<unsigned int>::const_iterator itr = ToTickBlocks.begin(), end = ToTickBlocks.end(); itr != end; ++itr)
for (std::vector<Vector3i>::const_iterator itr = ToTickBlocks.begin(), end = ToTickBlocks.end(); itr != end; ++itr)
{
unsigned int index = (*itr);
Vector3i BlockPos = IndexToCoordinate(index);
Vector3i Pos = (*itr);
cBlockHandler * Handler = BlockHandler(GetBlock(index));
Handler->Check(ChunkInterface, PluginInterface, BlockPos.x, BlockPos.y, BlockPos.z, *this);
cBlockHandler * Handler = BlockHandler(GetBlock(Pos));
Handler->Check(ChunkInterface, PluginInterface, Pos.x, Pos.y, Pos.z, *this);
} // for itr - ToTickBlocks[]
}
@ -865,8 +804,7 @@ void cChunk::TickBlocks(void)
continue; // It's all air up here
}
unsigned int Index = MakeIndexNoCheck(m_BlockTickX, m_BlockTickY, m_BlockTickZ);
cBlockHandler * Handler = BlockHandler(GetBlock(Index));
cBlockHandler * Handler = BlockHandler(GetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ));
ASSERT(Handler != NULL); // Happenned on server restart, FS #243
Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, m_BlockTickX, m_BlockTickY, m_BlockTickZ);
} // for i - tickblocks
@ -1258,9 +1196,8 @@ bool cChunk::UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBB
// The chunk is not available, bail out
return false;
}
int idx = Chunk->MakeIndex(a_RelX, a_RelY, a_RelZ);
a_BlockLight = Chunk->GetBlockLight(idx);
a_SkyLight = Chunk->GetSkyLight(idx);
a_BlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
a_SkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
return true;
}
@ -1465,11 +1402,9 @@ void cChunk::CalculateHeightmap(const BLOCKTYPE * a_BlockTypes)
void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
// Tick this block and its neighbors:
m_ToTickBlocks.push_back(index);
m_ToTickBlocks.push_back(Vector3i(a_RelX, a_RelY, a_RelZ));
QueueTickBlockNeighbors(a_RelX, a_RelY, a_RelZ);
// If there was a block entity, remove it:
@ -1533,7 +1468,7 @@ void cChunk::QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ)
return;
}
m_ToTickBlocks.push_back(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ));
m_ToTickBlocks.push_back(Vector3i(a_RelX, a_RelY, a_RelZ));
}
@ -1571,9 +1506,8 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
ASSERT(IsValid());
const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
const BLOCKTYPE OldBlockType = GetBlock(index);
const BLOCKTYPE OldBlockMeta = GetNibble(m_BlockMeta, index);
const BLOCKTYPE OldBlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
const BLOCKTYPE OldBlockMeta = m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta))
{
return;
@ -1581,11 +1515,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
MarkDirty();
if ((size_t)index >= m_BlockTypes.size())
{
m_BlockTypes.resize(index + 1);
}
m_BlockTypes[index] = a_BlockType;
m_ChunkData.SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType);
// The client doesn't need to distinguish between stationary and nonstationary fluids:
if (
@ -1601,7 +1531,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta));
}
SetNibble(m_BlockMeta, index, a_BlockMeta);
m_ChunkData.SetMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta);
// ONLY recalculate lighting if it's necessary!
if (
@ -1624,7 +1554,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
{
for (int y = a_RelY - 1; y > 0; --y)
{
if (GetBlock(MakeIndexNoCheck(a_RelX, y, a_RelZ)) != E_BLOCK_AIR)
if (GetBlock(a_RelX, y, a_RelZ) != E_BLOCK_AIR)
{
m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)y;
break;
@ -1638,39 +1568,18 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
void cChunk::SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta)
{
if (GetNibble(m_BlockMeta, a_BlockIdx) == a_Meta)
{
return;
}
MarkDirty();
SetNibble(m_BlockMeta, a_BlockIdx, a_Meta);
Vector3i Coords(IndexToCoordinate(a_BlockIdx));
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, Coords.x, Coords.y, Coords.z, GetBlock(a_BlockIdx), a_Meta));
}
void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client)
{
// The coords must be valid, because the upper level already does chunk lookup. No need to check them again.
// There's an debug-time assert in MakeIndexNoCheck anyway
unsigned int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
if (a_Client == NULL)
{
// Queue the block for all clients in the chunk (will be sent in Tick())
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(index), GetMeta(index)));
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ)));
return;
}
Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ);
a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(index), GetMeta(index));
a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ));
// FS #268 - if a BlockEntity digging is cancelled by a plugin, the entire block entity must be re-sent to the client:
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr)
@ -2529,27 +2438,7 @@ BLOCKTYPE cChunk::GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
return 0; // Clip
}
return GetBlock(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ));
}
BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
{
if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks))
{
ASSERT(!"GetBlock(idx) out of bounds!");
return 0;
}
if ((size_t)a_BlockIdx >= m_BlockTypes.size())
{
return E_BLOCK_AIR;
}
return m_BlockTypes[a_BlockIdx];
return m_ChunkData.GetBlock(a_RelX, a_RelY, a_RelZ);
}
@ -2558,9 +2447,8 @@ BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
{
int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
a_BlockType = GetBlock(Idx);
a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, Idx);
a_BlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
a_BlockMeta = m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
}
@ -2569,11 +2457,10 @@ void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_
void cChunk::GetBlockInfo(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight)
{
int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
a_BlockType = GetBlock(Idx);
a_Meta = cChunkDef::GetNibble(m_BlockMeta, Idx);
a_SkyLight = cChunkDef::GetNibble(m_BlockSkyLight, Idx);
a_BlockLight = cChunkDef::GetNibble(m_BlockLight, Idx);
a_BlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
a_Meta = m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
a_SkyLight = m_ChunkData.GetSkyLight(a_RelX, a_RelY, a_RelZ);
a_BlockLight = m_ChunkData.GetBlockLight(a_RelX, a_RelY, a_RelZ);
}

View File

@ -3,6 +3,7 @@
#include "Entities/Entity.h"
#include "ChunkDef.h"
#include "ChunkData.h"
#include "Simulator/FireSimulator.h"
#include "Simulator/SandSimulator.h"
@ -66,6 +67,7 @@ public:
cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects
cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP // Neighbor chunks
);
cChunk(cChunk & other);
~cChunk();
bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated)
@ -154,7 +156,7 @@ public:
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc.
BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const;
BLOCKTYPE GetBlock(int a_BlockIdx) const;
BLOCKTYPE GetBlock(Vector3i a_cords) const { return GetBlock(a_cords.x, a_cords.y, a_cords.z);}
void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);
@ -320,15 +322,23 @@ public:
m_BlockTickZ = a_RelZ;
}
inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const { return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); }
inline NIBBLETYPE GetMeta(int a_BlockIdx) const { return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); }
inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { SetMeta(MakeIndex(a_RelX, a_RelY, a_RelZ), a_Meta); }
void SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta);
inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
{
return m_ChunkData.GetMeta(a_RelX, a_RelY, a_RelZ);
}
inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta)
{
bool hasChanged = m_ChunkData.SetMeta(a_RelX, a_RelY, a_RelZ, a_Meta);
if (hasChanged)
{
MarkDirty();
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(a_RelX, a_RelY, a_RelZ), a_Meta));
}
}
inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); }
inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ, true); }
inline NIBBLETYPE GetBlockLight(int a_Idx) const {return cChunkDef::GetNibble(m_BlockLight, a_Idx); }
inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx, true); }
inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return m_ChunkData.GetBlockLight(a_RelX, a_RelY, a_RelZ); }
inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return m_ChunkData.GetSkyLight(a_RelX, a_RelY, a_RelZ); }
/** Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
@ -403,8 +413,8 @@ private:
bool m_IsSaving; // True if the chunk is being saved
bool m_HasLoadFailed; // True if chunk failed to load and hasn't been generated yet since then
std::vector<unsigned int> m_ToTickBlocks;
sSetBlockVector m_PendingSendBlocks; ///< Blocks that have changed and need to be sent to all clients
std::vector<Vector3i> m_ToTickBlocks;
sSetBlockVector m_PendingSendBlocks; ///< Blocks that have changed and need to be sent to all clients
sSetBlockQueueVector m_SetBlockQueue; ///< Block changes that are queued to a specific tick
@ -420,10 +430,7 @@ private:
cWorld * m_World;
cChunkMap * m_ChunkMap;
COMPRESSED_BLOCKTYPE m_BlockTypes;
COMPRESSED_NIBBLETYPE m_BlockMeta;
COMPRESSED_NIBBLETYPE m_BlockLight;
COMPRESSED_NIBBLETYPE m_BlockSkyLight;
cChunkData m_ChunkData;
cChunkDef::HeightMap m_HeightMap;
cChunkDef::BiomeMap m_BiomeMap;

592
src/ChunkData.cpp Normal file
View File

@ -0,0 +1,592 @@
// ChunkData.cpp
// Implements the cChunkData class that represents the block's type, meta, blocklight and skylight storage for a chunk
#include "Globals.h"
#include "ChunkData.h"
/** Returns true if all a_Array's elements between [0] and [a_NumElements - 1] are equal to a_Value. */
template <typename T> inline bool IsAllValue(const T * a_Array, size_t a_NumElements, T a_Value)
{
for (size_t i = 0; i < a_NumElements; i++)
{
if (a_Array[i] != a_Value)
{
return false;
}
}
return true;
}
cChunkData::cChunkData(void)
#if __cplusplus < 201103L
// auto_ptr style interface for memory management
: m_IsOwner(true)
#endif
{
for (size_t i = 0; i < NumSections; i++)
{
m_Sections[i] = NULL;
}
}
cChunkData::~cChunkData()
{
#if __cplusplus < 201103L
// auto_ptr style interface for memory management
if (!m_IsOwner)
{
return;
}
#endif
for (size_t i = 0; i < NumSections; i++)
{
Free(m_Sections[i]);
m_Sections[i] = NULL;
}
}
#if __cplusplus < 201103L
// auto_ptr style interface for memory management
cChunkData::cChunkData(const cChunkData & a_Other) :
m_IsOwner(true)
{
// Move contents and ownership from a_Other to this, pointer-wise:
for (size_t i = 0; i < NumSections; i++)
{
m_Sections[i] = a_Other.m_Sections[i];
}
a_Other.m_IsOwner = false;
}
cChunkData & cChunkData::operator =(const cChunkData & a_Other)
{
// If assigning to self, no-op
if (&a_Other == this)
{
return *this;
}
// Free any currently held contents:
if (m_IsOwner)
{
for (size_t i = 0; i < NumSections; i++)
{
Free(m_Sections[i]);
m_Sections[i] = NULL;
}
}
// Move contents and ownership from a_Other to this, pointer-wise:
m_IsOwner = true;
for (size_t i = 0; i < NumSections; i++)
{
m_Sections[i] = a_Other.m_Sections[i];
}
a_Other.m_IsOwner = false;
return *this;
}
#else
// unique_ptr style interface for memory management
cChunkData::cChunkData(cChunkData && other)
{
for (size_t i = 0; i < NumSections; i++)
{
m_Sections[i] = other.m_Sections[i];
other.m_Sections[i] = NULL;
}
}
cChunkData & cChunkData::operator =(cChunkData && other)
{
if (&other != this)
{
for (size_t i = 0; i < NumSections; i++)
{
Free(m_Sections[i]);
m_Sections[i] = other.m_Sections[i];
other.m_Sections[i] = NULL;
}
}
return *this;
}
#endif
BLOCKTYPE cChunkData::GetBlock(int a_X, int a_Y, int a_Z) const
{
ASSERT((a_X >= 0) && (a_X < cChunkDef::Width));
ASSERT((a_Y >= 0) && (a_Y < cChunkDef::Height));
ASSERT((a_Z >= 0) && (a_Z < cChunkDef::Width));
int Section = a_Y / SectionHeight;
if (m_Sections[Section] != NULL)
{
int Index = cChunkDef::MakeIndexNoCheck(a_X, a_Y - (Section * SectionHeight), a_Z);
return m_Sections[Section]->m_BlockTypes[Index];
}
else
{
return 0;
}
}
void cChunkData::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block)
{
if (
(a_RelX >= cChunkDef::Width) || (a_RelX < 0) ||
(a_RelY >= cChunkDef::Height) || (a_RelY < 0) ||
(a_RelZ >= cChunkDef::Width) || (a_RelZ < 0)
)
{
ASSERT(!"cChunkData::SetMeta(): index out of range!");
return;
}
int Section = a_RelY / SectionHeight;
if (m_Sections[Section] == NULL)
{
if (a_Block == 0x00)
{
return;
}
m_Sections[Section] = Allocate();
if (m_Sections[Section] == NULL)
{
ASSERT(!"Failed to allocate a new section in Chunkbuffer");
return;
}
ZeroSection(m_Sections[Section]);
}
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
m_Sections[Section]->m_BlockTypes[Index] = a_Block;
}
NIBBLETYPE cChunkData::GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
{
if (
(a_RelX < cChunkDef::Width) && (a_RelX > -1) &&
(a_RelY < cChunkDef::Height) && (a_RelY > -1) &&
(a_RelZ < cChunkDef::Width) && (a_RelZ > -1))
{
int Section = a_RelY / SectionHeight;
if (m_Sections[Section] != NULL)
{
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
return (m_Sections[Section]->m_BlockMetas[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
}
else
{
return 0;
}
}
ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!");
return 0;
}
bool cChunkData::SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Nibble)
{
if (
(a_RelX >= cChunkDef::Width) || (a_RelX < 0) ||
(a_RelY >= cChunkDef::Height) || (a_RelY < 0) ||
(a_RelZ >= cChunkDef::Width) || (a_RelZ < 0)
)
{
ASSERT(!"cChunkData::SetMeta(): index out of range!");
return false;
}
int Section = a_RelY / SectionHeight;
if (m_Sections[Section] == NULL)
{
if ((a_Nibble & 0xf) == 0x00)
{
return false;
}
m_Sections[Section] = Allocate();
if (m_Sections[Section] == NULL)
{
ASSERT(!"Failed to allocate a new section in Chunkbuffer");
return false;
}
ZeroSection(m_Sections[Section]);
}
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
NIBBLETYPE oldval = m_Sections[Section]->m_BlockMetas[Index / 2] >> ((Index & 1) * 4) & 0xf;
m_Sections[Section]->m_BlockMetas[Index / 2] = static_cast<NIBBLETYPE>(
(m_Sections[Section]->m_BlockMetas[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
);
return oldval == a_Nibble;
}
NIBBLETYPE cChunkData::GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const
{
if (
(a_RelX < cChunkDef::Width) && (a_RelX > -1) &&
(a_RelY < cChunkDef::Height) && (a_RelY > -1) &&
(a_RelZ < cChunkDef::Width) && (a_RelZ > -1)
)
{
int Section = a_RelY / SectionHeight;
if (m_Sections[Section] != NULL)
{
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
return (m_Sections[Section]->m_BlockLight[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
}
else
{
return 0;
}
}
ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!");
return 0;
}
NIBBLETYPE cChunkData::GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) const
{
if ((a_RelX < cChunkDef::Width) && (a_RelX > -1) && (a_RelY < cChunkDef::Height) && (a_RelY > -1) && (a_RelZ < cChunkDef::Width) && (a_RelZ > -1))
{
int Section = a_RelY / SectionHeight;
if (m_Sections[Section] != NULL)
{
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY - (Section * SectionHeight), a_RelZ);
return (m_Sections[Section]->m_BlockLight[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
}
else
{
return 0xF;
}
}
ASSERT(!"cChunkData::GetMeta(): coords out of chunk range!");
return 0;
}
cChunkData cChunkData::Copy(void) const
{
cChunkData copy;
for (size_t i = 0; i < NumSections; i++)
{
if (m_Sections[i] != NULL)
{
copy.m_Sections[i] = Allocate();
*copy.m_Sections[i] = *m_Sections[i];
}
}
return copy;
}
void cChunkData::CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx, size_t a_Length) const
{
size_t ToSkip = a_Idx;
for (size_t i = 0; i < NumSections; i++)
{
size_t StartPos = 0;
if (ToSkip > 0)
{
StartPos = std::min(ToSkip, +SectionBlockCount);
ToSkip -= StartPos;
}
if (StartPos < SectionBlockCount)
{
size_t ToCopy = std::min(+SectionBlockCount - StartPos, a_Length);
a_Length -= ToCopy;
if (m_Sections[i] != NULL)
{
BLOCKTYPE * blockbuffer = m_Sections[i]->m_BlockTypes;
memcpy(&a_Dest[(i * SectionBlockCount) + StartPos - a_Idx], blockbuffer + StartPos, sizeof(BLOCKTYPE) * ToCopy);
}
else
{
memset(&a_Dest[(i * SectionBlockCount) - a_Idx], 0, sizeof(BLOCKTYPE) * ToCopy);
}
}
}
}
void cChunkData::CopyMetas(NIBBLETYPE * a_Dest) const
{
for (size_t i = 0; i < NumSections; i++)
{
if (m_Sections[i] != NULL)
{
memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockMetas, sizeof(m_Sections[i]->m_BlockMetas));
}
else
{
memset(&a_Dest[i * SectionBlockCount / 2], 0, sizeof(m_Sections[i]->m_BlockMetas));
}
}
}
void cChunkData::CopyBlockLight(NIBBLETYPE * a_Dest) const
{
for (size_t i = 0; i < NumSections; i++)
{
if (m_Sections[i] != NULL)
{
memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockLight, sizeof(m_Sections[i]->m_BlockLight));
}
else
{
memset(&a_Dest[i * SectionBlockCount / 2], 0, sizeof(m_Sections[i]->m_BlockLight));
}
}
}
void cChunkData::CopySkyLight(NIBBLETYPE * a_Dest) const
{
for (size_t i = 0; i < NumSections; i++)
{
if (m_Sections[i] != NULL)
{
memcpy(&a_Dest[i * SectionBlockCount / 2], &m_Sections[i]->m_BlockSkyLight, sizeof(m_Sections[i]->m_BlockSkyLight));
}
else
{
memset(&a_Dest[i * SectionBlockCount / 2], 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
}
}
}
void cChunkData::SetBlockTypes(const BLOCKTYPE * a_Src)
{
ASSERT(a_Src != NULL);
for (size_t i = 0; i < NumSections; i++)
{
// If the section is already allocated, copy the data into it:
if (m_Sections[i] != NULL)
{
memcpy(m_Sections[i]->m_BlockTypes, &a_Src[i * SectionBlockCount], sizeof(m_Sections[i]->m_BlockTypes));
continue;
}
// The section doesn't exist, find out if it is needed:
if (IsAllValue(a_Src + i * SectionBlockCount, SectionBlockCount, (const BLOCKTYPE)0))
{
// No need for the section, the data is all-air
continue;
}
// Allocate the section and copy the data into it:
m_Sections[i] = Allocate();
memcpy(m_Sections[i]->m_BlockTypes, &a_Src[i * SectionBlockCount], sizeof(m_Sections[i]->m_BlockTypes));
memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas));
memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight));
memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
} // for i - m_Sections[]
}
void cChunkData::SetMetas(const NIBBLETYPE * a_Src)
{
ASSERT(a_Src != NULL);
for (size_t i = 0; i < NumSections; i++)
{
// If the section is already allocated, copy the data into it:
if (m_Sections[i] != NULL)
{
memcpy(m_Sections[i]->m_BlockMetas, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockMetas));
continue;
}
// The section doesn't exist, find out if it is needed:
if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, (NIBBLETYPE)0))
{
// No need for the section, the data is all zeroes
continue;
}
// Allocate the section and copy the data into it:
m_Sections[i] = Allocate();
memcpy(m_Sections[i]->m_BlockMetas, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockMetas));
memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes));
memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight));
memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
} // for i - m_Sections[]
}
void cChunkData::SetBlockLight(const NIBBLETYPE * a_Src)
{
if (a_Src == NULL)
{
return;
}
for (size_t i = 0; i < NumSections; i++)
{
// If the section is already allocated, copy the data into it:
if (m_Sections[i] != NULL)
{
memcpy(m_Sections[i]->m_BlockLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockLight));
continue;
}
// The section doesn't exist, find out if it is needed:
if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, (NIBBLETYPE)0))
{
// No need for the section, the data is all zeroes
continue;
}
// Allocate the section and copy the data into it:
m_Sections[i] = Allocate();
memcpy(m_Sections[i]->m_BlockLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockLight));
memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes));
memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas));
memset(m_Sections[i]->m_BlockSkyLight, 0xff, sizeof(m_Sections[i]->m_BlockSkyLight));
} // for i - m_Sections[]
}
void cChunkData::SetSkyLight(const NIBBLETYPE * a_Src)
{
if (a_Src == NULL)
{
return;
}
for (size_t i = 0; i < NumSections; i++)
{
// If the section is already allocated, copy the data into it:
if (m_Sections[i] != NULL)
{
memcpy(m_Sections[i]->m_BlockSkyLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockSkyLight));
continue;
}
// The section doesn't exist, find out if it is needed:
if (IsAllValue(a_Src + i * SectionBlockCount / 2, SectionBlockCount / 2, (NIBBLETYPE)0xff))
{
// No need for the section, the data is all zeroes
continue;
}
// Allocate the section and copy the data into it:
m_Sections[i] = Allocate();
memcpy(m_Sections[i]->m_BlockSkyLight, &a_Src[i * SectionBlockCount / 2], sizeof(m_Sections[i]->m_BlockSkyLight));
memset(m_Sections[i]->m_BlockTypes, 0x00, sizeof(m_Sections[i]->m_BlockTypes));
memset(m_Sections[i]->m_BlockMetas, 0x00, sizeof(m_Sections[i]->m_BlockMetas));
memset(m_Sections[i]->m_BlockLight, 0x00, sizeof(m_Sections[i]->m_BlockLight));
} // for i - m_Sections[]
}
cChunkData::sChunkSection * cChunkData::Allocate(void)
{
// TODO: Use an allocation pool
return new cChunkData::sChunkSection;
}
void cChunkData::Free(cChunkData::sChunkSection * a_Section)
{
// TODO: Use an allocation pool
delete a_Section;
}
void cChunkData::ZeroSection(cChunkData::sChunkSection * a_Section) const
{
memset(a_Section->m_BlockTypes, 0x00, sizeof(a_Section->m_BlockTypes));
memset(a_Section->m_BlockMetas, 0x00, sizeof(a_Section->m_BlockMetas));
memset(a_Section->m_BlockLight, 0x00, sizeof(a_Section->m_BlockLight));
memset(a_Section->m_BlockSkyLight, 0xff, sizeof(a_Section->m_BlockSkyLight));
}

125
src/ChunkData.h Normal file
View File

@ -0,0 +1,125 @@
// ChunkData.h
// Declares the cChunkData class that represents the block's type, meta, blocklight and skylight storage for a chunk
#pragma once
#include <cstring>
#include "ChunkDef.h"
#if __cplusplus < 201103L
// auto_ptr style interface for memory management
#else
// unique_ptr style interface for memory management
#endif
class cChunkData
{
public:
cChunkData(void);
~cChunkData();
#if __cplusplus < 201103L
// auto_ptr style interface for memory management
cChunkData(const cChunkData & a_Other);
cChunkData & operator =(const cChunkData & a_Other);
#else
// unique_ptr style interface for memory management
cChunkData(cChunkData && a_Other);
cChunkData & operator =(cChunkData && a_ther);
#endif
BLOCKTYPE GetBlock(int a_X, int a_Y, int a_Z) const;
void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block);
NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const;
bool SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Nibble);
NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const;
NIBBLETYPE GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) const;
/** Creates a (deep) copy of self. */
cChunkData Copy(void) const;
/** Copies the blocktype data into the specified flat array.
Optionally, only a part of the data is copied, as specified by the a_Idx and a_Length parameters. */
void CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx = 0, size_t a_Length = cChunkDef::NumBlocks) const;
/** Copies the metadata into the specified flat array. */
void CopyMetas(NIBBLETYPE * a_Dest) const;
/** Copies the block light data into the specified flat array. */
void CopyBlockLight(NIBBLETYPE * a_Dest) const;
/** Copies the skylight data into the specified flat array. */
void CopySkyLight (NIBBLETYPE * a_Dest) const;
/** Copies the blocktype data from the specified flat array into the internal representation.
Allocates sections that are needed for the operation.
Requires that a_Src is a valid pointer. */
void SetBlockTypes(const BLOCKTYPE * a_Src);
/** Copies the metadata from the specified flat array into the internal representation.
Allocates sectios that are needed for the operation.
Requires that a_Src is a valid pointer. */
void SetMetas(const NIBBLETYPE * a_Src);
/** Copies the blocklight data from the specified flat array into the internal representation.
Allocates sectios that are needed for the operation.
Allows a_Src to be NULL, in which case it doesn't do anything. */
void SetBlockLight(const NIBBLETYPE * a_Src);
/** Copies the skylight data from the specified flat array into the internal representation.
Allocates sectios that are needed for the operation.
Allows a_Src to be NULL, in which case it doesn't do anything. */
void SetSkyLight(const NIBBLETYPE * a_Src);
private:
static const size_t SectionHeight = 16;
static const size_t NumSections = (cChunkDef::Height / SectionHeight);
static const size_t SectionBlockCount = SectionHeight * cChunkDef::Width * cChunkDef::Width;
#if __cplusplus < 201103L
// auto_ptr style interface for memory management
mutable bool m_IsOwner;
#endif
struct sChunkSection {
BLOCKTYPE m_BlockTypes [SectionBlockCount];
NIBBLETYPE m_BlockMetas [SectionBlockCount / 2];
NIBBLETYPE m_BlockLight [SectionBlockCount / 2];
NIBBLETYPE m_BlockSkyLight[SectionBlockCount / 2];
};
sChunkSection * m_Sections[NumSections];
/** Allocates a new section. Entry-point to custom allocators. */
static sChunkSection * Allocate(void);
/** Frees the specified section, previously allocated using Allocate().
Note that a_Section may be NULL. */
static void Free(sChunkSection * a_Section);
/** Sets the data in the specified section to their default values. */
void ZeroSection(sChunkSection * a_Section) const;
};

127
src/ChunkDataCallback.h Normal file
View File

@ -0,0 +1,127 @@
// ChunkDataCallback.h
// Declares the cChunkDataCallback interface and several trivial descendants, for reading chunk data
#pragma once
#include "ChunkData.h"
/** Interface class used for getting data out of a chunk using the GetAllData() function.
Implementation must use the pointers immediately and NOT store any of them for later use
The virtual methods are called in the same order as they're declared here.
*/
class cChunkDataCallback abstract
{
public:
virtual ~cChunkDataCallback() {}
/** Called before any other callbacks to inform of the current coords
(only in processes where multiple chunks can be processed, such as cWorld::ForEachChunkInRect()).
If false is returned, the chunk is skipped.
*/
virtual bool Coords(int a_ChunkX, int a_ChunkZ) { UNUSED(a_ChunkX); UNUSED(a_ChunkZ); return true; };
/// Called once to provide heightmap data
virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {UNUSED(a_HeightMap); };
/// Called once to provide biome data
virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) {UNUSED(a_BiomeMap); };
/// Called once to let know if the chunk lighting is valid. Return value is ignored
virtual void LightIsValid(bool a_IsLightValid) {UNUSED(a_IsLightValid); };
/// Called once to export block info
virtual void ChunkData(const cChunkData & a_Buffer) {UNUSED(a_Buffer); };
/// Called for each entity in the chunk
virtual void Entity(cEntity * a_Entity) {UNUSED(a_Entity); };
/// Called for each blockentity in the chunk
virtual void BlockEntity(cBlockEntity * a_Entity) {UNUSED(a_Entity); };
} ;
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a buffer
*/
class cChunkDataCollector :
public cChunkDataCallback
{
public:
cChunkData m_BlockData;
protected:
virtual void ChunkData(const cChunkData & a_BlockData) override
{
m_BlockData = a_BlockData.Copy();
}
};
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer
*/
class cChunkDataArrayCollector :
public cChunkDataCallback
{
public:
// Must be unsigned char instead of BLOCKTYPE or NIBBLETYPE, because it houses both.
unsigned char m_BlockData[cChunkDef::BlockDataSize];
protected:
virtual void ChunkData(const cChunkData & a_ChunkBuffer) override
{
a_ChunkBuffer.CopyBlockTypes(m_BlockData);
a_ChunkBuffer.CopyMetas(m_BlockData + cChunkDef::NumBlocks);
a_ChunkBuffer.CopyBlockLight(m_BlockData + 3 * cChunkDef::NumBlocks / 2);
a_ChunkBuffer.CopySkyLight(m_BlockData + 2 * cChunkDef::NumBlocks);
}
};
/** A simple implementation of the cChunkDataCallback interface that collects all block data into separate buffers */
class cChunkDataSeparateCollector :
public cChunkDataCallback
{
public:
cChunkDef::BlockTypes m_BlockTypes;
cChunkDef::BlockNibbles m_BlockMetas;
cChunkDef::BlockNibbles m_BlockLight;
cChunkDef::BlockNibbles m_BlockSkyLight;
protected:
virtual void ChunkData(const cChunkData & a_ChunkBuffer) override
{
a_ChunkBuffer.CopyBlockTypes(m_BlockTypes);
a_ChunkBuffer.CopyMetas(m_BlockMetas);
a_ChunkBuffer.CopyBlockLight(m_BlockLight);
a_ChunkBuffer.CopySkyLight(m_BlockSkyLight);
}
} ;

View File

@ -330,136 +330,6 @@ private:
/** Interface class used for getting data out of a chunk using the GetAllData() function.
Implementation must use the pointers immediately and NOT store any of them for later use
The virtual methods are called in the same order as they're declared here.
*/
class cChunkDataCallback abstract
{
public:
virtual ~cChunkDataCallback() {}
/** Called before any other callbacks to inform of the current coords
(only in processes where multiple chunks can be processed, such as cWorld::ForEachChunkInRect()).
If false is returned, the chunk is skipped.
*/
virtual bool Coords(int a_ChunkX, int a_ChunkZ) { UNUSED(a_ChunkX); UNUSED(a_ChunkZ); return true; };
/// Called once to provide heightmap data
virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {UNUSED(a_HeightMap); };
/// Called once to provide biome data
virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) {UNUSED(a_BiomeMap); };
/// Called once to export block types
virtual void BlockTypes (const BLOCKTYPE * a_Type) {UNUSED(a_Type); };
/// Called once to export block meta
virtual void BlockMeta (const NIBBLETYPE * a_Meta) {UNUSED(a_Meta); };
/// Called once to let know if the chunk lighting is valid. Return value is used to control if BlockLight() and BlockSkyLight() are called next (if true)
virtual bool LightIsValid(bool a_IsLightValid) {UNUSED(a_IsLightValid); return true; };
/// Called once to export block light
virtual void BlockLight (const NIBBLETYPE * a_BlockLight) {UNUSED(a_BlockLight); };
/// Called once to export sky light
virtual void BlockSkyLight(const NIBBLETYPE * a_SkyLight) {UNUSED(a_SkyLight); };
/// Called for each entity in the chunk
virtual void Entity(cEntity * a_Entity) {UNUSED(a_Entity); };
/// Called for each blockentity in the chunk
virtual void BlockEntity(cBlockEntity * a_Entity) {UNUSED(a_Entity); };
} ;
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer
*/
class cChunkDataCollector :
public cChunkDataCallback
{
public:
// Must be unsigned char instead of BLOCKTYPE or NIBBLETYPE, because it houses both.
unsigned char m_BlockData[cChunkDef::BlockDataSize];
protected:
virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
{
memcpy(m_BlockData, a_BlockTypes, sizeof(cChunkDef::BlockTypes));
}
virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
{
memcpy(m_BlockData + cChunkDef::NumBlocks, a_BlockMeta, cChunkDef::NumBlocks / 2);
}
virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
{
memcpy(m_BlockData + 3 * cChunkDef::NumBlocks / 2, a_BlockLight, cChunkDef::NumBlocks / 2);
}
virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
{
memcpy(m_BlockData + 2 * cChunkDef::NumBlocks, a_BlockSkyLight, cChunkDef::NumBlocks / 2);
}
} ;
/** A simple implementation of the cChunkDataCallback interface that collects all block data into a separate buffers
*/
class cChunkDataSeparateCollector :
public cChunkDataCallback
{
public:
cChunkDef::BlockTypes m_BlockTypes;
cChunkDef::BlockNibbles m_BlockMetas;
cChunkDef::BlockNibbles m_BlockLight;
cChunkDef::BlockNibbles m_BlockSkyLight;
protected:
virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
{
memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes));
}
virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
{
memcpy(m_BlockMetas, a_BlockMeta, sizeof(m_BlockMetas));
}
virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
{
memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
}
virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
{
memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
}
} ;
/** Interface class used for comparing clients of two chunks.
Used primarily for entity moving while both chunks are locked.
*/

View File

@ -219,9 +219,8 @@ bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTY
return false;
}
int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ);
a_BlockType = Chunk->GetBlock(Index);
a_BlockMeta = Chunk->GetMeta(Index);
a_BlockType = Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
a_BlockMeta = Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ);
return true;
}
@ -242,8 +241,7 @@ bool cChunkMap::LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLO
return false;
}
int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ);
a_BlockType = Chunk->GetBlock(Index);
a_BlockType = Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
return true;
}
@ -264,8 +262,7 @@ bool cChunkMap::LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIB
return false;
}
int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ);
a_BlockMeta = Chunk->GetMeta(Index);
a_BlockMeta = Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ);
return true;
}
@ -1484,9 +1481,8 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
res = false;
continue;
}
int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z);
itr->BlockType = Chunk->GetBlock(idx);
itr->BlockMeta = Chunk->GetMeta(idx);
itr->BlockType = Chunk->GetBlock(itr->x, itr->y, itr->z);
itr->BlockMeta = Chunk->GetMeta(itr->x, itr->y, itr->z);
}
return res;
}

View File

@ -5,7 +5,8 @@
#pragma once
#include "ChunkDef.h"
#include "ChunkDataCallback.h"

View File

@ -27,6 +27,7 @@ Note that it may be called by world's BroadcastToChunk() if the client is still
#include "OSSupport/IsThread.h"
#include "ChunkDef.h"
#include "ChunkDataCallback.h"

View File

@ -336,7 +336,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID)
// Send scoreboard data
World->GetScoreBoard().SendTo(*this);
// Delay the first ping until the client "settles down"
// This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
cTimer t1;
@ -815,6 +815,17 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
return;
}
if (
((a_Status == DIG_STATUS_STARTED) || (a_Status == DIG_STATUS_FINISHED)) && // Only do a radius check for block destruction - things like pickup tossing send coordinates that are to be ignored
((Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) ||
(Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) ||
(Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6))
)
{
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
return;
}
cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
if (PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status))
{
@ -878,6 +889,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
case DIG_STATUS_CANCELLED:
{
// Block breaking cancelled by player
FinishDigAnimation();
return;
}
@ -916,7 +928,7 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
// It is a duplicate packet, drop it right away
return;
}
if (
m_Player->IsGameModeCreative() &&
ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType)
@ -925,14 +937,17 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
// Players can't destroy blocks with a Sword in the hand.
return;
}
if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta))
if (
(Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) ||
(Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) ||
(Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6)
)
{
// A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
return;
}
// Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig/aim bug in the client:
m_HasStartedDigging = true;
m_LastDigBlockX = a_BlockX;
@ -1004,24 +1019,24 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
return;
}
m_HasStartedDigging = false;
if (m_BlockDigAnimStage != -1)
FinishDigAnimation();
cWorld * World = m_Player->GetWorld();
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem());
if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta))
{
// End dig animation
m_BlockDigAnimStage = -1;
// It seems that 10 ends block animation
m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 10, this);
// A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
return;
}
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem());
if (a_OldBlock == E_BLOCK_AIR)
{
LOGD("Dug air - what the function?");
return;
}
cWorld * World = m_Player->GetWorld();
ItemHandler->OnBlockDestroyed(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ);
// The ItemHandler is also responsible for spawning the pickups
cChunkInterface ChunkInterface(World->GetChunkMap());
@ -1036,6 +1051,36 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
void cClientHandle::FinishDigAnimation()
{
if (
!m_HasStartedDigging || // Hasn't received the DIG_STARTED packet
(m_LastDigBlockX == -1) ||
(m_LastDigBlockY == -1) ||
(m_LastDigBlockZ == -1)
)
{
return;
}
m_HasStartedDigging = false;
if (m_BlockDigAnimStage != -1)
{
// End dig animation
m_BlockDigAnimStage = -1;
// It seems that 10 ends block animation
m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ, 10, this);
}
m_BlockDigAnimX = -1;
m_BlockDigAnimY = -1;
m_BlockDigAnimZ = -1;
}
void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem)
{
LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s",
@ -1043,7 +1088,23 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
);
cWorld * World = m_Player->GetWorld();
if (
(Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) ||
(Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) ||
(Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6)
)
{
if (a_BlockFace != BLOCK_FACE_NONE)
{
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); // 2 block high things
m_Player->GetInventory().SendEquippedSlot();
}
return;
}
cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
if (PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
{
@ -1057,7 +1118,8 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); //2 block high things
World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); // 2 block high things
m_Player->GetInventory().SendEquippedSlot();
}
return;
}
@ -1246,6 +1308,7 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
// Handler refused the placement, send that information back to the client:
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockY, m_Player);
m_Player->GetInventory().SendEquippedSlot();
return;
}
@ -1255,6 +1318,7 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
// A plugin doesn't agree with placing the block, revert the block on the client:
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
m_Player->GetInventory().SendEquippedSlot();
return;
}
@ -1327,28 +1391,8 @@ void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsO
void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround)
{
if ((m_Player == NULL) || (m_State != csPlaying))
{
// The client hasn't been spawned yet and sends nonsense, we know better
return;
}
/*
// TODO: Invalid stance check
if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65))
{
LOGD("Invalid stance");
SendPlayerMoveLook();
return;
}
*/
m_Player->MoveTo(Vector3d(a_PosX, a_PosY, a_PosZ));
m_Player->SetStance (a_Stance);
m_Player->SetTouchGround(a_IsOnGround);
m_Player->SetHeadYaw (a_Rotation);
m_Player->SetYaw (a_Rotation);
m_Player->SetPitch (a_Pitch);
HandlePlayerLook(a_Rotation, a_Pitch, a_IsOnGround);
HandlePlayerPos(a_PosX, a_PosY, a_PosZ, a_Stance, a_IsOnGround);
}
@ -2437,6 +2481,15 @@ void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTy
void cClientHandle::SendStatistics(const cStatManager & a_Manager)
{
m_Protocol->SendStatistics(a_Manager);
}
void cClientHandle::SendTabCompletionResults(const AStringVector & a_Results)
{
m_Protocol->SendTabCompletionResults(a_Results);
@ -2676,12 +2729,13 @@ void cClientHandle::PacketError(unsigned char a_PacketType)
void cClientHandle::DataReceived(const char * a_Data, size_t a_Size)
bool cClientHandle::DataReceived(const char * a_Data, size_t a_Size)
{
// Data is received from the client, store it in the buffer to be processed by the Tick thread:
m_TimeSinceLastPacket = 0;
cCSLock Lock(m_CSIncomingData);
m_IncomingData.append(a_Data, a_Size);
return false;
}

View File

@ -39,6 +39,7 @@ class cFallingBlock;
class cItemHandler;
class cWorld;
class cCompositeChat;
class cStatManager;
@ -79,9 +80,9 @@ public:
static AString GenerateOfflineUUID(const AString & a_Username); // tolua_export
/** Formats the type of message with the proper color and prefix for sending to the client. **/
AString FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString & a_AdditionalData);
static AString FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString & a_AdditionalData);
AString FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2);
static AString FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2);
void Kick(const AString & a_Reason); // tolua_export
void Authenticate(const AString & a_Name, const AString & a_UUID); // Called by cAuthenticator when the user passes authentication
@ -160,6 +161,7 @@ public:
void SendSpawnMob (const cMonster & a_Mob);
void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch);
void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType = 0);
void SendStatistics (const cStatManager & a_Manager);
void SendTabCompletionResults(const AStringVector & a_Results);
void SendTeleportEntity (const cEntity & a_Entity);
void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ);
@ -374,6 +376,9 @@ private:
/** Handles the DIG_FINISHED dig packet: */
void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta);
/** The clients will receive a finished dig animation */
void FinishDigAnimation();
/** Converts the protocol-formatted channel list (NUL-separated) into a proper string vector. */
AStringVector BreakApartPluginChannels(const AString & a_PluginChannels);
@ -390,7 +395,7 @@ private:
void HandleAnvilItemName(const char * a_Data, size_t a_Length);
// cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client
virtual bool DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client
virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
virtual void SocketClosed (void) override; // The socket has been closed for any reason
}; // tolua_export

View File

@ -189,6 +189,15 @@ void cCompositeChat::AddSuggestCommandPart(const AString & a_Text, const AString
void cCompositeChat::AddShowAchievementPart(const AString & a_PlayerName, const AString & a_Achievement, const AString & a_Style)
{
m_Parts.push_back(new cShowAchievementPart(a_PlayerName, a_Achievement, a_Style));
}
void cCompositeChat::ParseText(const AString & a_ParseText)
{
size_t len = a_ParseText.length();
@ -290,9 +299,10 @@ void cCompositeChat::ParseText(const AString & a_ParseText)
void cCompositeChat::SetMessageType(eMessageType a_MessageType)
void cCompositeChat::SetMessageType(eMessageType a_MessageType, const AString & a_AdditionalMessageTypeData)
{
m_MessageType = a_MessageType;
m_AdditionalMessageTypeData = a_AdditionalMessageTypeData;
}
@ -476,3 +486,16 @@ cCompositeChat::cSuggestCommandPart::cSuggestCommandPart(const AString & a_Text,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCompositeChat::cShowAchievementPart:
cCompositeChat::cShowAchievementPart::cShowAchievementPart(const AString & a_PlayerName, const AString & a_Achievement, const AString & a_Style) :
super(ptShowAchievement, a_Achievement, a_Style),
m_PlayerName(a_PlayerName)
{
}

View File

@ -38,6 +38,7 @@ public:
ptUrl,
ptRunCommand,
ptSuggestCommand,
ptShowAchievement,
} ;
class cBasePart
@ -46,6 +47,7 @@ public:
ePartType m_PartType;
AString m_Text;
AString m_Style;
AString m_AdditionalStyleData;
cBasePart(ePartType a_PartType, const AString & a_Text, const AString & a_Style = "");
@ -106,6 +108,15 @@ public:
public:
cSuggestCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style = "");
} ;
class cShowAchievementPart :
public cBasePart
{
typedef cBasePart super;
public:
AString m_PlayerName;
cShowAchievementPart(const AString & a_PlayerName, const AString & a_Achievement, const AString & a_Style = "");
} ;
typedef std::vector<cBasePart *> cParts;
@ -148,13 +159,20 @@ public:
/** Adds a part that suggests a command (enters it into the chat message area, but doesn't send) when clicked.
The default style is underlined yellow text. */
void AddSuggestCommandPart(const AString & a_Text, const AString & a_SuggestedCommand, const AString & a_Style = "u@b");
/** Adds a part that fully formats a specified achievement using client translatable strings
Takes achievement name and player awarded to. Displays as {player} has earned the achievement {achievement_name}.
*/
void AddShowAchievementPart(const AString & a_PlayerName, const AString & a_Achievement, const AString & a_Style = "");
/** Parses text into various parts, adds those.
Recognizes "http:" and "https:" URLs and @color-codes. */
void ParseText(const AString & a_ParseText);
/** Sets the message type, which is indicated by prefixes added to the message when serializing. */
void SetMessageType(eMessageType a_MessageType);
/** Sets the message type, which is indicated by prefixes added to the message when serializing
Takes optional AdditionalMessageTypeData to set m_AdditionalMessageTypeData. See said variable for more documentation.
*/
void SetMessageType(eMessageType a_MessageType, const AString & a_AdditionalMessageTypeData = "");
/** Adds the "underline" style to each part that is an URL. */
void UnderlineUrls(void);
@ -163,6 +181,9 @@ public:
/** Returns the message type set previously by SetMessageType(). */
eMessageType GetMessageType(void) const { return m_MessageType; }
/** Returns additional data pertaining to message type, for example, the name of a mtPrivateMsg sender */
AString GetAdditionalMessageTypeData(void) const { return m_AdditionalMessageTypeData; }
/** Returns the text from the parts that comprises the human-readable data.
Used for older protocols that don't support composite chat
@ -184,6 +205,9 @@ protected:
/** The message type, as indicated by prefixes. */
eMessageType m_MessageType;
/** Additional data pertaining to message type, for example, the name of a mtPrivateMsg sender */
AString m_AdditionalMessageTypeData;
/** Adds a_AddStyle to a_Style; overwrites the existing style if appropriate.
If the style already contains something that a_AddStyle overrides, it is erased first. */

View File

@ -3,6 +3,7 @@
#include "ChatColor.h"
#include <limits>
#include <cmath>
@ -528,6 +529,15 @@ inline float GetSpecialSignf( float a_Val )
template<class T> inline T Diff(T a_Val1, T a_Val2)
{
return std::abs(a_Val1 - a_Val2);
}
// tolua_begin
enum eMessageType

View File

@ -10,3 +10,5 @@ file(GLOB SOURCE
)
add_library(Entities ${SOURCE})
target_link_libraries(Entities WorldStorage)

View File

@ -312,12 +312,16 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer()))
{
cPlayer * Player = (cPlayer *)a_TDI.Attacker;
// 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)
if (!Player->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
}
Player->GetStatManager().AddValue(statDamageDealt, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5));
}
m_Health -= (short)a_TDI.FinalDamage;
@ -370,6 +374,11 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
if (m_Health <= 0)
{
KilledBy(a_TDI.Attacker);
if (a_TDI.Attacker != NULL)
{
a_TDI.Attacker->Killed(this);
}
}
return true;
}
@ -575,9 +584,16 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
if (m_AttachedTo != NULL)
{
if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5)
Vector3d DeltaPos = m_Pos - m_AttachedTo->GetPosition();
if (DeltaPos.Length() > 0.5)
{
SetPosition(m_AttachedTo->GetPosition());
if (IsPlayer())
{
cPlayer * Player = (cPlayer *)this;
Player->UpdateMovementStats(DeltaPos);
}
}
}
else
@ -601,6 +617,10 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
m_TicksSinceLastVoidDamage = 0;
}
if (IsMob() || IsPlayer() || IsPickup() || IsExpOrb())
{
DetectCacti();
}
if (IsMob() || IsPlayer())
{
// Set swimming state
@ -998,6 +1018,27 @@ void cEntity::TickInVoid(cChunk & a_Chunk)
void cEntity::DetectCacti(void)
{
int X = POSX_TOINT, Y = POSY_TOINT, Z = POSZ_TOINT;
double w = m_Width / 2;
if (
((Y > 0) && (Y < cChunkDef::Height)) &&
((((X + 1) - GetPosX() < w) && (GetWorld()->GetBlock(X + 1, Y, Z) == E_BLOCK_CACTUS)) ||
((GetPosX() - X < w) && (GetWorld()->GetBlock(X - 1, Y, Z) == E_BLOCK_CACTUS)) ||
(((Z + 1) - GetPosZ() < w) && (GetWorld()->GetBlock(X, Y, Z + 1) == E_BLOCK_CACTUS)) ||
((GetPosZ() - Z < w) && (GetWorld()->GetBlock(X, Y, Z - 1) == E_BLOCK_CACTUS)) ||
(((GetPosY() - Y < 1) && (GetWorld()->GetBlock(X, Y, Z) == E_BLOCK_CACTUS))))
)
{
TakeDamage(dtCactusContact, NULL, 1, 0);
}
}
void cEntity::SetSwimState(cChunk & a_Chunk)
{
int RelY = (int)floor(GetPosY() + 0.1);

View File

@ -299,6 +299,9 @@ public:
/// Called when the health drops below zero. a_Killer may be NULL (environmental damage)
virtual void KilledBy(cEntity * a_Killer);
/// Called when the entity kills another entity
virtual void Killed(cEntity * a_Victim) {}
/// Heals the specified amount of HPs
void Heal(int a_HitPoints);
@ -317,6 +320,9 @@ public:
/// Updates the state related to this entity being on fire
virtual void TickBurning(cChunk & a_Chunk);
/** Detects the time for application of cacti damage */
virtual void DetectCacti(void);
/// Handles when the entity is in the void
virtual void TickInVoid(cChunk & a_Chunk);

View File

@ -55,9 +55,8 @@ void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
return;
}
int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx);
NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx);
BLOCKTYPE BlockBelow = a_Chunk.GetBlock(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
NIBBLETYPE BelowMeta = a_Chunk.GetMeta(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta))
{
// Fallen onto a block that breaks this into pickups (e. g. half-slab)

View File

@ -192,6 +192,16 @@ bool cPickup::CollectedBy(cPlayer * a_Dest)
int NumAdded = a_Dest->GetInventory().AddItem(m_Item);
if (NumAdded > 0)
{
// Check achievements
switch (m_Item.m_ItemType)
{
case E_BLOCK_LOG: a_Dest->AwardAchievement(achMineWood); break;
case E_ITEM_LEATHER: a_Dest->AwardAchievement(achKillCow); break;
case E_ITEM_DIAMOND: a_Dest->AwardAchievement(achDiamonds); break;
case E_ITEM_BLAZE_ROD: a_Dest->AwardAchievement(achBlazeRod); break;
default: break;
}
m_Item.m_ItemCount -= NumAdded;
m_World->BroadcastCollectPickup(*this, *a_Dest);
// Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)

View File

@ -16,6 +16,9 @@
#include "../Items/ItemHandler.h"
#include "../Vector3.h"
#include "../WorldStorage/StatSerializer.h"
#include "../CompositeChat.h"
#include "inifile/iniFile.h"
#include "json/json.h"
@ -191,6 +194,8 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
return;
}
}
m_Stats.AddValue(statMinutesPlayed, 1);
if (!a_Chunk.IsValid())
{
@ -372,7 +377,7 @@ short cPlayer::DeltaExperience(short a_Xp_delta)
}
LOGD("Player \"%s\" gained/lost %d experience, total is now: %d",
m_PlayerName.c_str(), a_Xp_delta, m_CurrentXp);
GetName().c_str(), a_Xp_delta, m_CurrentXp);
// Set experience to be updated
m_bDirtyExperience = true;
@ -386,7 +391,7 @@ short cPlayer::DeltaExperience(short a_Xp_delta)
void cPlayer::StartChargingBow(void)
{
LOGD("Player \"%s\" started charging their bow", m_PlayerName.c_str());
LOGD("Player \"%s\" started charging their bow", GetName().c_str());
m_IsChargingBow = true;
m_BowCharge = 0;
}
@ -397,7 +402,7 @@ void cPlayer::StartChargingBow(void)
int cPlayer::FinishChargingBow(void)
{
LOGD("Player \"%s\" finished charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge);
LOGD("Player \"%s\" finished charging their bow at a charge of %d", GetName().c_str(), m_BowCharge);
int res = m_BowCharge;
m_IsChargingBow = false;
m_BowCharge = 0;
@ -410,7 +415,7 @@ int cPlayer::FinishChargingBow(void)
void cPlayer::CancelChargingBow(void)
{
LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge);
LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", GetName().c_str(), m_BowCharge);
m_IsChargingBow = false;
m_BowCharge = 0;
}
@ -451,8 +456,18 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
else
{
float Dist = (float)(m_LastGroundHeight - floor(GetPosY()));
if (Dist >= 2.0) // At least two blocks - TODO: Use m_LastJumpHeight instead of m_LastGroundHeight above
{
// Increment statistic
m_Stats.AddValue(statDistFallen, (StatValue)floor(Dist * 100 + 0.5));
}
int Damage = (int)(Dist - 3.f);
if (m_LastJumpHeight > m_LastGroundHeight) Damage++;
if (m_LastJumpHeight > m_LastGroundHeight)
{
Damage++;
}
m_LastJumpHeight = (float)GetPosY();
if (Damage > 0)
@ -815,7 +830,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer()))
{
cPlayer* Attacker = (cPlayer*) a_TDI.Attacker;
cPlayer * Attacker = (cPlayer *)a_TDI.Attacker;
if ((m_Team != NULL) && (m_Team == Attacker->m_Team))
{
@ -832,6 +847,8 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
// Any kind of damage adds food exhaustion
AddFoodExhaustion(0.3f);
SendHealth();
m_Stats.AddValue(statDamageTaken, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5));
return true;
}
return false;
@ -862,6 +879,8 @@ void cPlayer::KilledBy(cEntity * a_Killer)
Pickups.Add(cItem(E_ITEM_RED_APPLE));
}
m_Stats.AddValue(statItemsDropped, Pickups.Size());
m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
SaveToDisk(); // Save it, yeah the world is a tough place !
@ -871,9 +890,9 @@ void cPlayer::KilledBy(cEntity * a_Killer)
}
else if (a_Killer->IsPlayer())
{
GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), ((cPlayer *)a_Killer)->GetName().c_str()));
cPlayer * Killer = (cPlayer *)a_Killer;
m_World->GetScoreBoard().AddPlayerScore(((cPlayer *)a_Killer)->GetName(), cObjective::otPlayerKillCount, 1);
GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str()));
}
else
{
@ -883,6 +902,8 @@ void cPlayer::KilledBy(cEntity * a_Killer)
GetWorld()->BroadcastChatDeath(Printf("%s was killed by a %s", GetName().c_str(), KillerClass.c_str()));
}
m_Stats.AddValue(statDeaths);
m_World->GetScoreBoard().AddPlayerScore(GetName(), cObjective::otDeathCount, 1);
}
@ -890,6 +911,33 @@ void cPlayer::KilledBy(cEntity * a_Killer)
void cPlayer::Killed(cEntity * a_Victim)
{
cScoreboard & ScoreBoard = m_World->GetScoreBoard();
if (a_Victim->IsPlayer())
{
m_Stats.AddValue(statPlayerKills);
ScoreBoard.AddPlayerScore(GetName(), cObjective::otPlayerKillCount, 1);
}
else if (a_Victim->IsMob())
{
if (((cMonster *)a_Victim)->GetMobFamily() == cMonster::mfHostile)
{
AwardAchievement(achKillMonster);
}
m_Stats.AddValue(statMobKills);
}
ScoreBoard.AddPlayerScore(GetName(), cObjective::otTotalKillCount, 1);
}
void cPlayer::Respawn(void)
{
m_Health = GetMaxHealth();
@ -1108,6 +1156,47 @@ void cPlayer::SetIP(const AString & a_IP)
unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach)
{
eStatistic Prerequisite = cStatInfo::GetPrerequisite(a_Ach);
// Check if the prerequisites are met
if (Prerequisite != statInvalid)
{
if (m_Stats.GetValue(Prerequisite) == 0)
{
return 0;
}
}
StatValue Old = m_Stats.GetValue(a_Ach);
if (Old > 0)
{
return m_Stats.AddValue(a_Ach);
}
else
{
// First time, announce it
cCompositeChat Msg;
Msg.SetMessageType(mtSuccess);
Msg.AddShowAchievementPart(GetName(), cStatInfo::GetName(a_Ach));
m_World->BroadcastChat(Msg);
// Increment the statistic
StatValue New = m_Stats.AddValue(a_Ach);
// Achievement Get!
m_ClientHandle->SendStatistics(m_Stats);
return New;
}
}
void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
{
SetPosition(a_PosX, a_PosY, a_PosZ);
@ -1192,6 +1281,9 @@ void cPlayer::MoveTo( const Vector3d & a_NewPos )
// TODO: should do some checks to see if player is not moving through terrain
// TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
Vector3d DeltaPos = a_NewPos - GetPosition();
UpdateMovementStats(DeltaPos);
SetPosition( a_NewPos );
SetStance(a_NewPos.y + 1.62);
@ -1223,7 +1315,7 @@ void cPlayer::AddToGroup( const AString & a_GroupName )
{
cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName );
m_Groups.push_back( Group );
LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName.c_str() );
LOGD("Added %s to group %s", GetName().c_str(), a_GroupName.c_str() );
ResolveGroups();
ResolvePermissions();
}
@ -1247,13 +1339,13 @@ void cPlayer::RemoveFromGroup( const AString & a_GroupName )
if( bRemoved )
{
LOGD("Removed %s from group %s", m_PlayerName.c_str(), a_GroupName.c_str() );
LOGD("Removed %s from group %s", GetName().c_str(), a_GroupName.c_str() );
ResolveGroups();
ResolvePermissions();
}
else
{
LOGWARN("Tried to remove %s from group %s but was not in that group", m_PlayerName.c_str(), a_GroupName.c_str() );
LOGWARN("Tried to remove %s from group %s but was not in that group", GetName().c_str(), a_GroupName.c_str() );
}
}
@ -1359,7 +1451,7 @@ void cPlayer::ResolveGroups()
if( AllGroups.find( CurrentGroup ) != AllGroups.end() )
{
LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!",
m_PlayerName.c_str(), CurrentGroup->GetName().c_str()
GetName().c_str(), CurrentGroup->GetName().c_str()
);
}
else
@ -1371,7 +1463,7 @@ void cPlayer::ResolveGroups()
{
if( AllGroups.find( *itr ) != AllGroups.end() )
{
LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() );
LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", GetName().c_str(), (*itr)->GetName().c_str() );
continue;
}
ToIterate.push_back( *itr );
@ -1422,10 +1514,7 @@ void cPlayer::TossEquippedItem(char a_Amount)
Drops.push_back(DroppedItem);
}
double vX = 0, vY = 0, vZ = 0;
EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY);
vY = -vY * 2 + 1.f;
m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player
TossItems(Drops);
}
@ -1441,6 +1530,7 @@ void cPlayer::TossHeldItem(char a_Amount)
char OriginalItemAmount = Item.m_ItemCount;
Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount);
Drops.push_back(Item);
if (OriginalItemAmount > a_Amount)
{
Item.m_ItemCount = OriginalItemAmount - a_Amount;
@ -1451,10 +1541,7 @@ void cPlayer::TossHeldItem(char a_Amount)
}
}
double vX = 0, vY = 0, vZ = 0;
EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY);
vY = -vY * 2 + 1.f;
m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player
TossItems(Drops);
}
@ -1466,10 +1553,21 @@ void cPlayer::TossPickup(const cItem & a_Item)
cItems Drops;
Drops.push_back(a_Item);
TossItems(Drops);
}
void cPlayer::TossItems(const cItems & a_Items)
{
m_Stats.AddValue(statItemsDropped, a_Items.Size());
double vX = 0, vY = 0, vZ = 0;
EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY);
vY = -vY * 2 + 1.f;
m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player
m_World->SpawnItemPickups(a_Items, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player
}
@ -1517,19 +1615,19 @@ void cPlayer::LoadPermissionsFromDisk()
cIniFile IniFile;
if (IniFile.ReadFile("users.ini"))
{
AString Groups = IniFile.GetValueSet(m_PlayerName, "Groups", "Default");
AString Groups = IniFile.GetValueSet(GetName(), "Groups", "Default");
AStringVector Split = StringSplitAndTrim(Groups, ",");
for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr)
{
if (!cRoot::Get()->GetGroupManager()->ExistsGroup(*itr))
{
LOGWARNING("The group %s for player %s was not found!", itr->c_str(), m_PlayerName.c_str());
LOGWARNING("The group %s for player %s was not found!", itr->c_str(), GetName().c_str());
}
AddToGroup(*itr);
}
AString Color = IniFile.GetValue(m_PlayerName, "Color", "-");
AString Color = IniFile.GetValue(GetName(), "Color", "-");
if (!Color.empty())
{
m_Color = Color[0];
@ -1538,7 +1636,7 @@ void cPlayer::LoadPermissionsFromDisk()
else
{
cGroupManager::GenerateDefaultUsersIni(IniFile);
IniFile.AddValue("Groups", m_PlayerName, "Default");
IniFile.AddValue("Groups", GetName(), "Default");
AddToGroup("Default");
}
IniFile.WriteFile("users.ini");
@ -1553,14 +1651,14 @@ bool cPlayer::LoadFromDisk()
LoadPermissionsFromDisk();
// Log player permissions, cause it's what the cool kids do
LOGINFO("Player %s has permissions:", m_PlayerName.c_str() );
LOGINFO("Player %s has permissions:", GetName().c_str() );
for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr )
{
if( itr->second ) LOG(" - %s", itr->first.c_str() );
}
AString SourceFile;
Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() );
Printf(SourceFile, "players/%s.json", GetName().c_str() );
cFile f;
if (!f.Open(SourceFile, cFile::fmRead))
@ -1621,9 +1719,14 @@ bool cPlayer::LoadFromDisk()
m_Inventory.LoadFromJson(root["inventory"]);
m_LoadedWorldName = root.get("world", "world").asString();
// Load the player stats.
// We use the default world name (like bukkit) because stats are shared between dimensions/worlds.
cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats);
StatSerializer.Load();
LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"",
m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str()
GetName().c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str()
);
return true;
@ -1679,12 +1782,12 @@ bool cPlayer::SaveToDisk()
std::string JsonData = writer.write(root);
AString SourceFile;
Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() );
Printf(SourceFile, "players/%s.json", GetName().c_str() );
cFile f;
if (!f.Open(SourceFile, cFile::fmWrite))
{
LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str());
LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", GetName().c_str(), SourceFile.c_str());
return false;
}
if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size())
@ -1692,6 +1795,16 @@ bool cPlayer::SaveToDisk()
LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str());
return false;
}
// Save the player stats.
// We use the default world name (like bukkit) because stats are shared between dimensions/worlds.
cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats);
if (!StatSerializer.Save())
{
LOGERROR("Could not save stats for player %s", GetName().c_str());
return false;
}
return true;
}
@ -1706,7 +1819,10 @@ cPlayer::StringList cPlayer::GetResolvedPermissions()
const PermissionMap& ResolvedPermissions = m_ResolvedPermissions;
for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr )
{
if( itr->second ) Permissions.push_back( itr->first );
if (itr->second)
{
Permissions.push_back( itr->first );
}
}
return Permissions;
@ -1845,6 +1961,92 @@ void cPlayer::HandleFloater()
bool cPlayer::IsClimbing(void) const
{
int PosX = POSX_TOINT;
int PosY = POSY_TOINT;
int PosZ = POSZ_TOINT;
if ((PosY < 0) || (PosY >= cChunkDef::Height))
{
return false;
}
BLOCKTYPE Block = m_World->GetBlock(PosX, PosY, PosZ);
switch (Block)
{
case E_BLOCK_LADDER:
case E_BLOCK_VINES:
{
return true;
}
default: return false;
}
}
void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos)
{
StatValue Value = (StatValue)floor(a_DeltaPos.Length() * 100 + 0.5);
if (m_AttachedTo == NULL)
{
if (IsClimbing())
{
if (a_DeltaPos.y > 0.0) // Going up
{
m_Stats.AddValue(statDistClimbed, (StatValue)floor(a_DeltaPos.y * 100 + 0.5));
}
}
else if (IsSubmerged())
{
m_Stats.AddValue(statDistDove, Value);
}
else if (IsSwimming())
{
m_Stats.AddValue(statDistSwum, Value);
}
else if (IsOnGround())
{
m_Stats.AddValue(statDistWalked, Value);
}
else
{
if (Value >= 25) // Ignore small/slow movement
{
m_Stats.AddValue(statDistFlown, Value);
}
}
}
else
{
switch (m_AttachedTo->GetEntityType())
{
case cEntity::etMinecart: m_Stats.AddValue(statDistMinecart, Value); break;
case cEntity::etBoat: m_Stats.AddValue(statDistBoat, Value); break;
case cEntity::etMonster:
{
cMonster * Monster = (cMonster *)m_AttachedTo;
switch (Monster->GetMobType())
{
case cMonster::mtPig: m_Stats.AddValue(statDistPig, Value); break;
case cMonster::mtHorse: m_Stats.AddValue(statDistHorse, Value); break;
default: break;
}
break;
}
default: break;
}
}
}
void cPlayer::ApplyFoodExhaustionFromMovement()
{
if (IsGameModeCreative())

View File

@ -7,6 +7,8 @@
#include "../World.h"
#include "../ClientHandle.h"
#include "../Statistics.h"
@ -125,6 +127,9 @@ public:
inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export
/** Returns whether the player is climbing (ladders, vines e.t.c). */
bool IsClimbing(void) const;
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override;
// tolua_begin
@ -174,6 +179,15 @@ public:
cTeam * UpdateTeam(void);
// tolua_end
/** Return the associated statistic and achievement manager. */
cStatManager & GetStatManager() { return m_Stats; }
/** Awards the player an achievement.
If all prerequisites are met, this method will award the achievement and will broadcast a chat message.
If the achievement has been already awarded to the player, this method will just increment the stat counter.
Returns the _new_ stat value. (0 = Could not award achievement) */
unsigned int AwardAchievement(const eStatistic a_Ach);
void SetIP(const AString & a_IP);
@ -306,6 +320,8 @@ public:
void AbortEating(void);
virtual void KilledBy(cEntity * a_Killer) override;
virtual void Killed(cEntity * a_Victim) override;
void Respawn(void); // tolua_export
@ -375,6 +391,9 @@ public:
/** If true the player can fly even when he's not in creative. */
void SetCanFly(bool a_CanFly);
/** Update movement-related statistics. */
void UpdateMovementStats(const Vector3d & a_DeltaPos);
/** Returns wheter the player can fly or not. */
virtual bool CanFly(void) const { return m_CanFly; }
// tolua_end
@ -487,6 +506,8 @@ protected:
cTeam * m_Team;
cStatManager m_Stats;
void ResolvePermissions(void);
@ -506,6 +527,9 @@ protected:
/** 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);
/** Tosses a list of items. */
void TossItems(const cItems & a_Items);
/** Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) */
void ApplyFoodExhaustionFromMovement();

View File

@ -212,7 +212,7 @@ void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile)
void cBiomeGenList::InitializeBiomes(const AString & a_Biomes)
{
AStringVector Split = StringSplit(a_Biomes, ",");
AStringVector Split = StringSplitAndTrim(a_Biomes, ",");
// Convert each string in the list into biome:
for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr)

View File

@ -25,6 +25,7 @@
#include "Noise3DGenerator.h"
#include "POCPieceGenerator.h"
#include "Ravines.h"
#include "VillageGen.h"
@ -32,6 +33,7 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cTerrainCompositionGen:
cTerrainCompositionGen * cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed)
{
AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
@ -404,6 +406,15 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
{
m_FinishGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen));
}
else if (NoCaseCompare(*itr, "Villages") == 0)
{
int GridSize = a_IniFile.GetValueSetI("Generator", "VillageGridSize", 384);
int MaxDepth = a_IniFile.GetValueSetI("Generator", "VillageMaxDepth", 2);
int MaxSize = a_IniFile.GetValueSetI("Generator", "VillageMaxSize", 128);
int MinDensity = a_IniFile.GetValueSetI("Generator", "VillageMinDensity", 50);
int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80);
m_FinishGens.push_back(new cVillageGen(Seed, GridSize, MaxDepth, MaxSize, MinDensity, MaxDensity, *m_BiomeGen, *m_HeightGen));
}
else if (NoCaseCompare(*itr, "WaterLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);

View File

@ -9,6 +9,34 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cEmptyStructure:
/** A cStructure descendant representing an empty structure.
Used when the generator descended from cGridStructGen doesn't return any structure, to keep at least the
Origin coords so that the structure isn't queried over and over again. */
class cEmptyStructure :
public cGridStructGen::cStructure
{
typedef cGridStructGen::cStructure super;
public:
cEmptyStructure(int a_OriginX, int a_OriginZ) :
super(a_OriginX, a_OriginZ)
{
}
protected:
virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) override
{
// Do nothing
}
} ;
cGridStructGen::cGridStructGen(
int a_Seed,
int a_GridSizeX, int a_GridSizeZ,
@ -22,6 +50,15 @@ cGridStructGen::cGridStructGen(
m_MaxStructureSizeZ(a_MaxStructureSizeZ),
m_MaxCacheSize(a_MaxCacheSize)
{
size_t NumStructuresPerQuery = (size_t)((m_MaxStructureSizeX / m_GridSizeX + 1) * (m_MaxStructureSizeZ / m_GridSizeZ + 1));
if (NumStructuresPerQuery > m_MaxCacheSize)
{
m_MaxCacheSize = NumStructuresPerQuery * 4;
LOGINFO(
"cGridStructGen: The cache size is too small (%u), increasing the cache size to %u to avoid inefficiency.",
(unsigned)a_MaxCacheSize, (unsigned)m_MaxCacheSize
);
}
}
@ -81,7 +118,12 @@ void cGridStructGen::GetStructuresForChunk(int a_ChunkX, int a_ChunkZ, cStructur
} // for itr - a_Structures[]
if (!Found)
{
a_Structures.push_back(CreateStructure(OriginX, OriginZ));
cStructurePtr Structure = CreateStructure(OriginX, OriginZ);
if (Structure.get() == NULL)
{
Structure.reset(new cEmptyStructure(OriginX, OriginZ));
}
a_Structures.push_back(Structure);
}
} // for z
} // for x

View File

@ -39,14 +39,6 @@ class cGridStructGen :
public cFinishGen
{
public:
cGridStructGen(
int a_Seed,
int a_GridSizeX, int a_GridSizeZ,
int a_MaxStructureSizeX, int a_MaxStructureSizeZ,
size_t a_MaxCacheSize
);
protected:
/** Represents a single structure that occupies the grid point. Knows how to draw itself into a chunk. */
class cStructure
{
@ -75,6 +67,14 @@ protected:
typedef std::list<cStructurePtr> cStructurePtrs;
cGridStructGen(
int a_Seed,
int a_GridSizeX, int a_GridSizeZ,
int a_MaxStructureSizeX, int a_MaxStructureSizeZ,
size_t a_MaxCacheSize
);
protected:
/** Seed for generating the semi-random grid. */
int m_Seed;

View File

@ -1283,7 +1283,7 @@ cStructGenMineShafts::cStructGenMineShafts(
int a_Seed, int a_GridSize, int a_MaxSystemSize,
int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase
) :
super(a_Seed, a_GridSize, a_GridSize, 120 + a_MaxSystemSize * 10, 120 + a_MaxSystemSize * 10, 100),
super(a_Seed, a_GridSize, a_GridSize, a_MaxSystemSize, a_MaxSystemSize, 100),
m_Noise(a_Seed),
m_GridSize(a_GridSize),
m_MaxSystemSize(a_MaxSystemSize),

View File

@ -186,6 +186,11 @@ cPOCPieceGenerator::cPOCPieceGenerator(int a_Seed) :
cPOCPieceGenerator::~cPOCPieceGenerator()
{
cPieceGenerator::FreePieces(m_Pieces);
for (cPieces::iterator itr = m_AvailPieces.begin(), end = m_AvailPieces.end(); itr != end; ++itr)
{
delete *itr;
}
m_AvailPieces.clear();
}

View File

@ -286,7 +286,8 @@ cPlacedPiece::cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece
m_Parent(a_Parent),
m_Piece(&a_Piece),
m_Coords(a_Coords),
m_NumCCWRotations(a_NumCCWRotations)
m_NumCCWRotations(a_NumCCWRotations),
m_HasBeenMovedToGround(false)
{
m_Depth = (m_Parent == NULL) ? 0 : (m_Parent->GetDepth() + 1);
m_HitBox = a_Piece.RotateMoveHitBox(a_NumCCWRotations, a_Coords.x, a_Coords.y, a_Coords.z);
@ -297,6 +298,36 @@ cPlacedPiece::cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece
cPiece::cConnector cPlacedPiece::GetRotatedConnector(size_t a_Index) const
{
cPiece::cConnectors Connectors = m_Piece->GetConnectors();
ASSERT(Connectors.size() >= a_Index);
return m_Piece->RotateMoveConnector(Connectors[a_Index], m_NumCCWRotations, m_Coords.x, m_Coords.y, m_Coords.z);
}
cPiece::cConnector cPlacedPiece::GetRotatedConnector(const cPiece::cConnector & a_Connector) const
{
return m_Piece->RotateMoveConnector(a_Connector, m_NumCCWRotations, m_Coords.x, m_Coords.y, m_Coords.z);
}
void cPlacedPiece::MoveToGroundBy(int a_OffsetY)
{
m_Coords.y += a_OffsetY;
m_HasBeenMovedToGround = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPieceGenerator:
@ -331,7 +362,31 @@ cPlacedPiece * cPieceGenerator::PlaceStartingPiece(int a_BlockX, int a_BlockY, i
// Choose a random one of the starting pieces:
cPieces StartingPieces = m_PiecePool.GetStartingPieces();
cPiece * StartingPiece = StartingPieces[rnd % StartingPieces.size()];
int Total = 0;
for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr)
{
Total += m_PiecePool.GetStartingPieceWeight(**itr);
}
cPiece * StartingPiece;
if (Total > 0)
{
int Chosen = rnd % Total;
StartingPiece = StartingPieces.front();
for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr)
{
Chosen -= m_PiecePool.GetStartingPieceWeight(**itr);
if (Chosen <= 0)
{
StartingPiece = *itr;
break;
}
}
}
else
{
// All pieces returned zero weight, but we need one to start. Choose with equal chance:
StartingPiece = StartingPieces[rnd % StartingPieces.size()];
}
rnd = rnd >> 16;
// Choose a random supported rotation:

View File

@ -110,6 +110,7 @@ public:
virtual cPieces GetStartingPieces(void) = 0;
/** Returns the relative weight with which the a_NewPiece is to be selected for placing under a_PlacedPiece through a_ExistingConnector.
a_ExistingConnector is the original connector, before any movement or rotation is applied to it.
This allows the pool to tweak the piece's chances, based on the previous pieces in the tree and the connector used.
The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will never be chosen.
*/
@ -119,6 +120,15 @@ public:
const cPiece & a_NewPiece
) { return 1; }
/** Returns the relative weight with which the a_NewPiece is to be selected for placing as the first piece.
This allows the pool to tweak the piece's chances.
The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will not be chosen.
If all pieces return 0, a random piece is chosen, with all equal chances.
*/
virtual int GetStartingPieceWeight(
const cPiece & a_NewPiece
) { return 1; }
/** Called after a piece is placed, to notify the pool that it has been used.
The pool may adjust the pieces it will return the next time. */
virtual void PiecePlaced(const cPiece & a_Piece) = 0;
@ -138,19 +148,41 @@ class cPlacedPiece
public:
cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece, const Vector3i & a_Coords, int a_NumCCWRotations);
const cPiece & GetPiece (void) const { return *m_Piece; }
const Vector3i & GetCoords (void) const { return m_Coords; }
int GetNumCCWRotations(void) const { return m_NumCCWRotations; }
const cCuboid & GetHitBox (void) const { return m_HitBox; }
int GetDepth (void) const { return m_Depth; }
const cPlacedPiece * GetParent (void) const { return m_Parent; }
const cPiece & GetPiece (void) const { return *m_Piece; }
const Vector3i & GetCoords (void) const { return m_Coords; }
int GetNumCCWRotations (void) const { return m_NumCCWRotations; }
const cCuboid & GetHitBox (void) const { return m_HitBox; }
int GetDepth (void) const { return m_Depth; }
bool HasBeenMovedToGround(void) const { return m_HasBeenMovedToGround; }
/** Returns the coords as a modifiable object. */
Vector3i & GetCoords(void) { return m_Coords; }
/** Returns the connector at the specified index, rotated in the actual placement.
Undefined behavior if a_Index is out of range. */
cPiece::cConnector GetRotatedConnector(size_t a_Index) const;
/** Returns a copy of the specified connector, modified to account for the translation and rotation for
this placement. */
cPiece::cConnector GetRotatedConnector(const cPiece::cConnector & a_Connector) const;
/** Moves the placed piece Y-wise by the specified offset.
Sets m_HasBeenMovedToGround to true, too.
Used eg. by village houses. */
void MoveToGroundBy(int a_OffsetY);
protected:
const cPlacedPiece * m_Parent;
const cPiece * m_Piece;
Vector3i m_Coords;
int m_NumCCWRotations;
cCuboid m_HitBox;
int m_Depth;
cCuboid m_HitBox; // Hitbox of the placed piece, in world coords
int m_Depth; // Depth in the generated piece tree
/** Set to true once the piece has been moved Y-wise.
Used eg. by village houses. */
bool m_HasBeenMovedToGround;
};
typedef std::vector<cPlacedPiece *> cPlacedPieces;

View File

@ -108,6 +108,9 @@ static const cPrefab::sDef g_TestPrefabDef =
// AddWeightIfSame:
1000,
// MoveToGround:
false,
};
static cPrefab g_TestPrefab(g_TestPrefabDef);
@ -127,7 +130,8 @@ cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
m_MergeStrategy(a_Def.m_MergeStrategy),
m_ShouldExtendFloor(a_Def.m_ShouldExtendFloor),
m_DefaultWeight(a_Def.m_DefaultWeight),
m_AddWeightIfSame(a_Def.m_AddWeightIfSame)
m_AddWeightIfSame(a_Def.m_AddWeightIfSame),
m_MoveToGround(a_Def.m_MoveToGround)
{
m_BlockArea[0].Create(m_Size);
CharMap cm;
@ -136,6 +140,34 @@ cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
ParseConnectors(a_Def.m_Connectors);
ParseDepthWeight(a_Def.m_DepthWeight);
AddRotatedBlockAreas();
}
cPrefab::cPrefab(const cBlockArea & a_Image, int a_AllowedRotations) :
m_Size(a_Image.GetSize()),
m_AllowedRotations(a_AllowedRotations),
m_MergeStrategy(cBlockArea::msOverwrite),
m_ShouldExtendFloor(false),
m_DefaultWeight(1),
m_AddWeightIfSame(0),
m_MoveToGround(false)
{
m_HitBox.p1.Set(0, 0, 0);
m_HitBox.p2.Set(m_Size.x - 1, m_Size.y - 1, m_Size.z - 1);
m_BlockArea[0].CopyFrom(a_Image);
AddRotatedBlockAreas();
}
void cPrefab::AddRotatedBlockAreas(void)
{
// 1 CCW rotation:
if ((m_AllowedRotations & 0x01) != 0)
{
@ -164,13 +196,21 @@ cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
void cPrefab::Draw(cChunkDesc & a_Dest, const cPlacedPiece * a_Placement) const
{
Draw(a_Dest, a_Placement->GetCoords(), a_Placement->GetNumCCWRotations());
}
void cPrefab::Draw(cChunkDesc & a_Dest, const Vector3i & a_Placement, int a_NumRotations) const
{
// Draw the basic image:
Vector3i Placement = a_Placement->GetCoords();
Vector3i Placement(a_Placement);
int ChunkStartX = a_Dest.GetChunkX() * cChunkDef::Width;
int ChunkStartZ = a_Dest.GetChunkZ() * cChunkDef::Width;
Placement.Move(-ChunkStartX, 0, -ChunkStartZ);
const cBlockArea & Image = m_BlockArea[a_Placement->GetNumCCWRotations()];
const cBlockArea & Image = m_BlockArea[a_NumRotations];
a_Dest.WriteBlockArea(Image, Placement.x, Placement.y, Placement.z, m_MergeStrategy);
// If requested, draw the floor (from the bottom of the prefab down to the nearest non-air)
@ -257,6 +297,24 @@ int cPrefab::GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cC
void cPrefab::SetDefaultWeight(int a_DefaultWeight)
{
m_DefaultWeight = a_DefaultWeight;
}
void cPrefab::AddConnector(int a_RelX, int a_RelY, int a_RelZ, eBlockFace a_Direction, int a_Type)
{
m_Connectors.push_back(cConnector(a_RelX, a_RelY, a_RelZ, a_Type, a_Direction));
}
void cPrefab::ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef)
{
ASSERT(a_CharMapDef != NULL);

View File

@ -82,19 +82,47 @@ public:
Can be positive or negative.
This is used e. g. to make nether bridges prefer spanning multiple segments or to penalize turrets next to each other. */
int m_AddWeightIfSame;
/** If true, the piece will be moved Y-wise so that its first connector is sitting on the terrain.
This is used e. g. for village houses. */
bool m_MoveToGround;
};
/** Creates a prefab from the provided definition. */
cPrefab(const sDef & a_Def);
/** Creates a prefab based on the given BlockArea and allowed rotations. */
cPrefab(const cBlockArea & a_Image, int a_AllowedRotations);
/** Draws the prefab into the specified chunk, according to the placement stored in the PlacedPiece. */
void Draw(cChunkDesc & a_Dest, const cPlacedPiece * a_Placement) const;
/** Draws the prefab into the specified chunks, according to the specified placement and rotations. */
void Draw(cChunkDesc & a_Dest, const Vector3i & a_Placement, int a_NumRotations) const;
/** Returns true if the prefab has any connector of the specified type. */
bool HasConnectorType(int a_ConnectorType) const;
/** Returns the weight (chance) of this prefab generating as the next piece after the specified placed piece.
PiecePool implementations can use this for their GetPieceWeight() implementations. */
int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector) const;
/** Sets the (unmodified) DefaultWeight property for this piece. */
void SetDefaultWeight(int a_DefaultWeight);
/** Returns the unmodified DefaultWeight property for the piece. */
int GetDefaultWeight(void) const { return m_DefaultWeight; }
/** Sets the AddWeightIfSame member, that is used to modify the weight when the previous piece is the same prefab */
void SetAddWeightIfSame(int a_AddWeightIfSame) { m_AddWeightIfSame = a_AddWeightIfSame; }
/** Adds the specified connector to the list of connectors this piece supports. */
void AddConnector(int a_RelX, int a_RelY, int a_RelZ, eBlockFace a_Direction, int a_Type);
/** Returns whether the prefab should be moved Y-wise to ground before drawing, rather than staying
at the coords governed by the connectors. */
bool ShouldMoveToGround(void) const { return m_MoveToGround; }
protected:
/** Packs complete definition of a single block, for per-letter assignment. */
@ -149,6 +177,10 @@ protected:
Can be positive or negative.
This is used e. g. to make nether bridges prefer spanning multiple segments or to penalize turrets next to each other. */
int m_AddWeightIfSame;
/** If true, the piece will be moved Y-wise so that its first connector is sitting on the terrain.
This is used e. g. for village houses. */
bool m_MoveToGround;
// cPiece overrides:
@ -157,6 +189,10 @@ protected:
virtual cCuboid GetHitBox(void) const override;
virtual bool CanRotateCCW(int a_NumRotations) const override;
/** Based on the m_AllowedRotations, adds rotated cBlockAreas to the m_BlockArea array.
To be called only from this class's constructor! */
void AddRotatedBlockAreas(void);
/** Parses the CharMap in the definition into a CharMap binary data used for translating the definition into BlockArea. */
void ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef);

View File

@ -26,6 +26,34 @@ cPrefabPiecePool::cPrefabPiecePool(
cPrefabPiecePool::~cPrefabPiecePool()
{
Clear();
}
void cPrefabPiecePool::Clear(void)
{
m_PiecesByConnector.clear();
for (cPieces::iterator itr = m_AllPieces.begin(), end = m_AllPieces.end(); itr != end; ++itr)
{
delete *itr;
}
m_AllPieces.clear();
for (cPieces::iterator itr = m_StartingPieces.begin(), end = m_StartingPieces.end(); itr != end; ++itr)
{
delete *itr;
}
m_StartingPieces.clear();
}
void cPrefabPiecePool::AddPieceDefs(const cPrefab::sDef * a_PieceDefs, size_t a_NumPieceDefs)
{
ASSERT(a_PieceDefs != NULL);
@ -101,6 +129,15 @@ int cPrefabPiecePool::GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const c
int cPrefabPiecePool::GetStartingPieceWeight(const cPiece & a_NewPiece)
{
return ((const cPrefab &)a_NewPiece).GetDefaultWeight();
}
void cPrefabPiecePool::PiecePlaced(const cPiece & a_Piece)
{
// Do nothing

View File

@ -34,6 +34,12 @@ public:
const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs
);
/** Destroys the pool, freeing all pieces. */
~cPrefabPiecePool();
/** Removes and frees all pieces from this pool. */
void Clear(void);
/** Adds pieces from the specified definitions into m_AllPieces. Also adds the pieces into
the m_PiecesByConnector map.
May be called multiple times with different PieceDefs, will add all such pieces. */
@ -44,7 +50,6 @@ public:
May be called multiple times with different PieceDefs, will add all such pieces. */
void AddStartingPieceDefs(const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs);
protected:
/** The type used to map a connector type to the list of pieces with that connector */
@ -70,6 +75,7 @@ protected:
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override;
virtual cPieces GetStartingPieces(void) override;
virtual int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece) override;
virtual int GetStartingPieceWeight(const cPiece & a_NewPiece) override;
virtual void PiecePlaced(const cPiece & a_Piece) override;
virtual void Reset(void) override;
} ;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
// AlchemistVillagePrefabs.h
// Declares the prefabs in the group AlchemistVillage
#include "../Prefab.h"
extern const cPrefab::sDef g_AlchemistVillagePrefabs[];
extern const cPrefab::sDef g_AlchemistVillageStartingPrefabs[];
extern const size_t g_AlchemistVillagePrefabsCount;
extern const size_t g_AlchemistVillageStartingPrefabsCount;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
// JapaneseVillagePrefabs.h
// Declares the prefabs in the group JapaneseVillage
#include "../Prefab.h"
extern const cPrefab::sDef g_JapaneseVillagePrefabs[];
extern const cPrefab::sDef g_JapaneseVillageStartingPrefabs[];
extern const size_t g_JapaneseVillagePrefabsCount;
extern const size_t g_JapaneseVillageStartingPrefabsCount;

View File

@ -155,6 +155,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BalconyCorridor
@ -315,6 +318,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BalconyTee2
@ -435,6 +441,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BlazePlatform
@ -605,6 +614,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BlazePlatformOverhang
@ -805,6 +817,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-1000,
// MoveToGround:
false,
}, // BridgeCircleCrossing
@ -1006,6 +1021,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeCrossing
@ -1100,6 +1118,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeCrumble1
@ -1200,6 +1221,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeCrumble2
@ -1379,6 +1403,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
1000,
// MoveToGround:
false,
}, // BridgeDoubleCrumble
@ -1619,6 +1646,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeFunnelDown
@ -1948,6 +1978,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeLevelCrossing
@ -2067,6 +2100,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
1000,
// MoveToGround:
false,
}, // BridgeSegment
@ -2227,6 +2263,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeTee
@ -2328,6 +2367,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // Corridor11
@ -2429,6 +2471,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // Corridor13
@ -2524,6 +2569,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
500,
// MoveToGround:
false,
}, // Corridor5
@ -2663,6 +2711,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // CorridorCorner5
@ -2803,6 +2854,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // CorridorCornerChest5
@ -2928,6 +2982,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-50,
// MoveToGround:
false,
}, // CorridorCrossing
@ -3080,6 +3137,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // CorridorStairs
@ -3181,6 +3241,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // DarkCorridor
@ -3438,6 +3501,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // LavaStaircase
@ -3769,6 +3835,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-1000,
// MoveToGround:
false,
}, // LavaStaircaseBig
@ -4047,6 +4116,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // LavaStairsBridge
@ -4235,6 +4307,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-1000,
// MoveToGround:
false,
}, // MidStaircase
@ -4378,6 +4453,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // StairsToOpen1
@ -4521,6 +4599,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // StairsToOpen2
@ -4638,6 +4719,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // Tee2x4
@ -4767,6 +4851,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // Tee4x4
@ -4863,6 +4950,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-50,
// MoveToGround:
false,
}, // TinyCorridorCorner
@ -4960,6 +5050,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // TinyCorridorCornerChest
@ -5059,6 +5152,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-50,
// MoveToGround:
false,
}, // TinyCorridorCrossing
@ -5174,6 +5270,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-99,
// MoveToGround:
false,
}, // Turret
}; // g_NetherFortPrefabs
@ -5378,6 +5477,9 @@ const cPrefab::sDef g_NetherFortStartingPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // CentralRoom
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
// PlainsVillagePrefabs.h
// Declares the prefabs in the group PlainsVillage
#include "../Prefab.h"
extern const cPrefab::sDef g_PlainsVillagePrefabs[];
extern const cPrefab::sDef g_PlainsVillageStartingPrefabs[];
extern const size_t g_PlainsVillagePrefabsCount;
extern const size_t g_PlainsVillageStartingPrefabsCount;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
// SandFlatRoofVillagePrefabs.h
// Declares the prefabs in the group SandFlatRoofVillage
#include "../Prefab.h"
extern const cPrefab::sDef g_SandFlatRoofVillagePrefabs[];
extern const cPrefab::sDef g_SandFlatRoofVillageStartingPrefabs[];
extern const size_t g_SandFlatRoofVillagePrefabsCount;
extern const size_t g_SandFlatRoofVillageStartingPrefabsCount;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
// SandVillagePrefabs.h
// Declares the prefabs in the group SandVillage
#include "../Prefab.h"
extern const cPrefab::sDef g_SandVillagePrefabs[];
extern const cPrefab::sDef g_SandVillageStartingPrefabs[];
extern const size_t g_SandVillagePrefabsCount;
extern const size_t g_SandVillageStartingPrefabsCount;

View File

@ -596,24 +596,22 @@ void cStructGenDirectOverhangs::GenFinish(cChunkDesc & a_ChunkDesc)
// Interpolate between FloorLo and FloorHi:
for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++)
{
switch (a_ChunkDesc.GetBiome(x, z))
EMCSBiome biome = a_ChunkDesc.GetBiome(x, z);
if ((biome == biExtremeHills) || (biome == biExtremeHillsEdge))
{
case biExtremeHills:
case biExtremeHillsEdge:
int Lo = FloorLo[x + 17 * z] / 256;
int Hi = FloorHi[x + 17 * z] / 256;
for (int y = 0; y < SEGMENT_HEIGHT; y++)
{
int Lo = FloorLo[x + 17 * z] / 256;
int Hi = FloorHi[x + 17 * z] / 256;
for (int y = 0; y < SEGMENT_HEIGHT; y++)
int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT;
if (Val < 0)
{
int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT;
if (Val < 0)
{
a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_AIR);
}
} // for y
break;
}
} // switch (biome)
a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_AIR);
}
} // for y
break;
} // if (biome)
} // for z, x
// Swap the floors:

View File

@ -174,7 +174,7 @@ 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;
return;
}
case biTaiga:
@ -184,14 +184,14 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
{
// Conifers
GetConiferTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
break;
return;
}
case biSwampland:
{
// Swamp trees:
GetSwampTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
break;
return;
}
case biJungle:
@ -207,21 +207,21 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
{
GetJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
}
break;
return;
}
case biBirchForest:
case biBirchForestHills:
{
GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
break;
return;
}
case biBirchForestM:
case biBirchForestHillsM:
{
GetTallBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
break;
return;
}
case biRoofedForest:
@ -257,9 +257,29 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
{
// TODO: These need their special trees
GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
break;
return;
}
case biDesert:
case biDesertHills:
case biRiver:
case biBeach:
case biHell:
case biSky:
case biOcean:
case biFrozenOcean:
case biFrozenRiver:
case biVariant:
case biNumBiomes:
case biNumVariantBiomes:
case biInvalidBiome:
{
// These biomes have no trees, or are non-biome members of the enum.
return;
}
}
ASSERT(!"Invalid biome type!");
}

View File

@ -0,0 +1,430 @@
// VillageGen.cpp
// Implements the cVillageGen class representing the village generator
#include "Globals.h"
#include "VillageGen.h"
#include "Prefabs/AlchemistVillagePrefabs.h"
#include "Prefabs/JapaneseVillagePrefabs.h"
#include "Prefabs/PlainsVillagePrefabs.h"
#include "Prefabs/SandVillagePrefabs.h"
#include "Prefabs/SandFlatRoofVillagePrefabs.h"
#include "PieceGenerator.h"
/*
How village generating works:
By descending from a cGridStructGen, a semi-random grid is generated. A village may be generated for each of
the grid's cells. Each cell checks the biomes in an entire chunk around it, only generating a village if all
biomes are village-friendly. If yes, the entire village structure is built for that cell. If not, the cell
is left village-less.
A village is generated using the regular BFS piece generator. The well piece is used as the starting piece,
the roads and houses are then used as the following pieces. Only the houses are read from the prefabs,
though, the roads are generated by code and their content is ignored. A special subclass of the cPiecePool
class is used, so that the roads connect to each other and to the well only in predefined manners.
The well has connectors of type "2". The houses have connectors of type "-1". The roads have connectors of
both types' opposites, type "-2" at the far ends and type "1" on the long edges. Additionally, there are
type "2" connectors along the long edges of the roads as well, so that the roads create T junctions.
When the village is about to be drawn into a chunk, it queries the heights for each piece intersecting the
chunk. The pieces are shifted so that their pivot points lie on the surface, and the roads are drawn
directly by turning the surface blocks into gravel / sandstone.
The village prefabs are stored in global piecepools (one pool per village type). In order to support
per-village density setting, the cVillage class itself implements the cPiecePool interface, relaying the
calls to the underlying cVillagePiecePool, after processing the density check.
*/
class cVillagePiecePool :
public cPrefabPiecePool
{
typedef cPrefabPiecePool super;
public:
cVillagePiecePool(
const cPrefab::sDef * a_PieceDefs, size_t a_NumPieceDefs,
const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs
) :
super(a_PieceDefs, a_NumPieceDefs, a_StartingPieceDefs, a_NumStartingPieceDefs)
{
// Add the road pieces:
for (int len = 27; len < 60; len += 12)
{
cBlockArea BA;
BA.Create(len, 1, 3, cBlockArea::baTypes | cBlockArea::baMetas);
BA.Fill(cBlockArea::baTypes | cBlockArea::baMetas, E_BLOCK_GRAVEL, 0);
cPrefab * RoadPiece = new cPrefab(BA, 1);
RoadPiece->AddConnector(0, 0, 1, BLOCK_FACE_XM, -2);
RoadPiece->AddConnector(len - 1, 0, 1, BLOCK_FACE_XP, -2);
RoadPiece->SetDefaultWeight(100);
// Add the road connectors:
for (int x = 1; x < len; x += 12)
{
RoadPiece->AddConnector(x, 0, 0, BLOCK_FACE_ZM, 2);
RoadPiece->AddConnector(x, 0, 2, BLOCK_FACE_ZP, 2);
}
// Add the buildings connectors:
for (int x = 7; x < len; x += 12)
{
RoadPiece->AddConnector(x, 0, 0, BLOCK_FACE_ZM, 1);
RoadPiece->AddConnector(x, 0, 2, BLOCK_FACE_ZP, 1);
}
m_AllPieces.push_back(RoadPiece);
m_PiecesByConnector[-2].push_back(RoadPiece);
m_PiecesByConnector[1].push_back(RoadPiece);
m_PiecesByConnector[2].push_back(RoadPiece);
} // for len - roads of varying length
}
// cPrefabPiecePool overrides:
virtual int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece) override
{
// Roads cannot branch T-wise (appending -2 connector to a +2 connector on a 1-high piece):
if ((a_ExistingConnector.m_Type == 2) && (a_PlacedPiece.GetDepth() > 0) && (a_PlacedPiece.GetPiece().GetSize().y == 1))
{
return 0;
}
return ((const cPrefab &)a_NewPiece).GetPieceWeight(a_PlacedPiece, a_ExistingConnector);
}
} ;
class cVillageGen::cVillage :
public cGridStructGen::cStructure,
protected cPiecePool
{
typedef cGridStructGen::cStructure super;
public:
cVillage(
int a_Seed,
int a_OriginX, int a_OriginZ,
int a_MaxRoadDepth,
int a_MaxSize,
int a_Density,
cPiecePool & a_Prefabs,
cTerrainHeightGen & a_HeightGen,
BLOCKTYPE a_RoadBlock
) :
super(a_OriginX, a_OriginZ),
m_Seed(a_Seed),
m_Noise(a_Seed),
m_MaxSize(a_MaxSize),
m_Density(a_Density),
m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, 255, a_OriginZ + a_MaxSize),
m_Prefabs(a_Prefabs),
m_HeightGen(a_HeightGen),
m_RoadBlock(a_RoadBlock)
{
// Generate the pieces for this village; don't care about the Y coord:
cBFSPieceGenerator pg(*this, a_Seed);
pg.PlacePieces(a_OriginX, 0, a_OriginZ, a_MaxRoadDepth + 1, m_Pieces);
if (m_Pieces.empty())
{
return;
}
// If the central piece should be moved to ground, move it, and
// check all of its dependents and move those that are strictly connector-driven based on its new Y coord:
if (((cPrefab &)m_Pieces[0]->GetPiece()).ShouldMoveToGround())
{
int OrigPosY = m_Pieces[0]->GetCoords().y;
PlacePieceOnGround(*m_Pieces[0]);
int NewPosY = m_Pieces[0]->GetCoords().y;
MoveAllDescendants(m_Pieces, 0, NewPosY - OrigPosY);
}
}
~cVillage()
{
cPieceGenerator::FreePieces(m_Pieces);
}
protected:
/** Seed for the random functions */
int m_Seed;
/** The noise used as a pseudo-random generator */
cNoise m_Noise;
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */
int m_MaxSize;
/** The density for this village. Used to refrain from populating all house connectors. Range [0, 100] */
int m_Density;
/** Borders of the vilalge - no item may reach out of this cuboid. */
cCuboid m_Borders;
/** Prefabs to use for buildings */
cPiecePool & m_Prefabs;
/** The underlying height generator, used for placing the structures on top of the terrain. */
cTerrainHeightGen & m_HeightGen;
/** The village pieces, placed by the generator. */
cPlacedPieces m_Pieces;
/** The block to use for the roads. */
BLOCKTYPE m_RoadBlock;
// cGridStructGen::cStructure overrides:
virtual void DrawIntoChunk(cChunkDesc & a_Chunk) override
{
// Iterate over all items
// Each intersecting prefab is placed on ground, then drawn
// Each intersecting road is drawn by replacing top soil blocks with gravel / sandstone blocks
cChunkDef::HeightMap HeightMap; // Heightmap for this chunk, used by roads
m_HeightGen.GenHeightMap(a_Chunk.GetChunkX(), a_Chunk.GetChunkZ(), HeightMap);
for (cPlacedPieces::iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
{
cPrefab & Prefab = (cPrefab &)((*itr)->GetPiece());
if ((*itr)->GetPiece().GetSize().y == 1)
{
// It's a road, special handling (change top terrain blocks to m_RoadBlock)
DrawRoad(a_Chunk, **itr, HeightMap);
continue;
}
if (Prefab.ShouldMoveToGround() && !(*itr)->HasBeenMovedToGround())
{
PlacePieceOnGround(**itr);
}
Prefab.Draw(a_Chunk, *itr);
} // for itr - m_PlacedPieces[]
}
/** Adjusts the Y coord of the given piece so that the piece is on the ground.
Ground level is assumed to be represented by the first connector in the piece. */
void PlacePieceOnGround(cPlacedPiece & a_Piece)
{
cPiece::cConnector FirstConnector = a_Piece.GetRotatedConnector(0);
int ChunkX, ChunkZ;
int BlockX = FirstConnector.m_Pos.x;
int BlockZ = FirstConnector.m_Pos.z;
int BlockY;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cChunkDef::HeightMap HeightMap;
m_HeightGen.GenHeightMap(ChunkX, ChunkZ, HeightMap);
int TerrainHeight = cChunkDef::GetHeight(HeightMap, BlockX, BlockZ);
a_Piece.MoveToGroundBy(TerrainHeight - FirstConnector.m_Pos.y + 1);
}
/** Draws the road into the chunk.
The heightmap is not queried from the heightgen, but is given via parameter, so that it may be queried just
once for all roads in a chunk. */
void DrawRoad(cChunkDesc & a_Chunk, cPlacedPiece & a_Road, cChunkDef::HeightMap & a_HeightMap)
{
cCuboid RoadCoords = a_Road.GetHitBox();
RoadCoords.Sort();
int MinX = std::max(RoadCoords.p1.x - a_Chunk.GetChunkX() * cChunkDef::Width, 0);
int MaxX = std::min(RoadCoords.p2.x - a_Chunk.GetChunkX() * cChunkDef::Width, cChunkDef::Width - 1);
int MinZ = std::max(RoadCoords.p1.z - a_Chunk.GetChunkZ() * cChunkDef::Width, 0);
int MaxZ = std::min(RoadCoords.p2.z - a_Chunk.GetChunkZ() * cChunkDef::Width, cChunkDef::Width - 1);
for (int z = MinZ; z <= MaxZ; z++)
{
for (int x = MinX; x <= MaxX; x++)
{
a_Chunk.SetBlockType(x, cChunkDef::GetHeight(a_HeightMap, x, z), z, m_RoadBlock);
}
}
}
// cPiecePool overrides:
virtual cPieces GetPiecesWithConnector(int a_ConnectorType)
{
return m_Prefabs.GetPiecesWithConnector(a_ConnectorType);
}
virtual cPieces GetStartingPieces(void)
{
return m_Prefabs.GetStartingPieces();
}
virtual int GetPieceWeight(
const cPlacedPiece & a_PlacedPiece,
const cPiece::cConnector & a_ExistingConnector,
const cPiece & a_NewPiece
) override
{
// Check against the density:
if (a_ExistingConnector.m_Type == 1)
{
const Vector3i & Coords = a_PlacedPiece.GetRotatedConnector(a_ExistingConnector).m_Pos;
int rnd = (m_Noise.IntNoise3DInt(Coords.x, Coords.y, Coords.z) / 7) % 100;
if (rnd > m_Density)
{
return 0;
}
}
// Density check passed, relay to m_Prefabs:
return m_Prefabs.GetPieceWeight(a_PlacedPiece, a_ExistingConnector, a_NewPiece);
}
virtual int GetStartingPieceWeight(const cPiece & a_NewPiece) override
{
return m_Prefabs.GetStartingPieceWeight(a_NewPiece);
}
virtual void PiecePlaced(const cPiece & a_Piece) override
{
m_Prefabs.PiecePlaced(a_Piece);
}
virtual void Reset(void) override
{
m_Prefabs.Reset();
}
void MoveAllDescendants(cPlacedPieces & a_PlacedPieces, size_t a_Pivot, int a_HeightDifference)
{
size_t num = a_PlacedPieces.size();
cPlacedPiece * Pivot = a_PlacedPieces[a_Pivot];
for (size_t i = a_Pivot + 1; i < num; i++)
{
if (
(a_PlacedPieces[i]->GetParent() == Pivot) && // It is a direct dependant of the pivot
!((const cPrefab &)a_PlacedPieces[i]->GetPiece()).ShouldMoveToGround() // It attaches strictly by connectors
)
{
a_PlacedPieces[i]->MoveToGroundBy(a_HeightDifference);
MoveAllDescendants(a_PlacedPieces, i, a_HeightDifference);
}
} // for i - a_PlacedPieces[]
}
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cVillageGen:
static cVillagePiecePool g_SandVillage(g_SandVillagePrefabs, g_SandVillagePrefabsCount, g_SandVillageStartingPrefabs, g_SandVillageStartingPrefabsCount);
static cVillagePiecePool g_SandFlatRoofVillage(g_SandFlatRoofVillagePrefabs, g_SandFlatRoofVillagePrefabsCount, g_SandFlatRoofVillageStartingPrefabs, g_SandFlatRoofVillageStartingPrefabsCount);
static cVillagePiecePool g_AlchemistVillage(g_AlchemistVillagePrefabs, g_AlchemistVillagePrefabsCount, g_AlchemistVillageStartingPrefabs, g_AlchemistVillageStartingPrefabsCount);
static cVillagePiecePool g_PlainsVillage(g_PlainsVillagePrefabs, g_PlainsVillagePrefabsCount, g_PlainsVillageStartingPrefabs, g_PlainsVillageStartingPrefabsCount);
static cVillagePiecePool g_JapaneseVillage(g_JapaneseVillagePrefabs, g_JapaneseVillagePrefabsCount, g_JapaneseVillageStartingPrefabs, g_JapaneseVillageStartingPrefabsCount);
static cVillagePiecePool * g_DesertVillagePools[] =
{
&g_SandVillage,
&g_SandFlatRoofVillage,
&g_AlchemistVillage,
} ;
static cVillagePiecePool * g_PlainsVillagePools[] =
{
&g_PlainsVillage,
&g_JapaneseVillage,
} ;
cVillageGen::cVillageGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize, int a_MinDensity, int a_MaxDensity, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen) :
super(a_Seed, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 100),
m_Noise(a_Seed + 1000),
m_MaxDepth(a_MaxDepth),
m_MaxSize(a_MaxSize),
m_MinDensity(a_MinDensity),
m_MaxDensity(a_MaxDensity),
m_BiomeGen(a_BiomeGen),
m_HeightGen(a_HeightGen)
{
}
cGridStructGen::cStructurePtr cVillageGen::CreateStructure(int a_OriginX, int a_OriginZ)
{
// Generate the biomes for the chunk surrounding the origin:
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_OriginX, a_OriginZ, ChunkX, ChunkZ);
cChunkDef::BiomeMap Biomes;
m_BiomeGen.GenBiomes(ChunkX, ChunkZ, Biomes);
// Check if all the biomes are village-friendly:
// If just one is not, no village is created, because it's likely that an unfriendly biome is too close
cVillagePiecePool * VillagePrefabs = NULL;
BLOCKTYPE RoadBlock = E_BLOCK_GRAVEL;
int rnd = m_Noise.IntNoise2DInt(a_OriginX, a_OriginZ) / 11;
cVillagePiecePool * PlainsVillage = g_PlainsVillagePools[rnd % ARRAYCOUNT(g_PlainsVillagePools)];
cVillagePiecePool * DesertVillage = g_DesertVillagePools[rnd % ARRAYCOUNT(g_DesertVillagePools)];
for (size_t i = 0; i < ARRAYCOUNT(Biomes); i++)
{
switch (Biomes[i])
{
case biDesert:
case biDesertM:
{
// These biomes allow sand villages
VillagePrefabs = DesertVillage;
// RoadBlock = E_BLOCK_SANDSTONE;
break;
}
case biPlains:
case biSavanna:
case biSavannaM:
case biSunflowerPlains:
{
// These biomes allow plains-style villages
VillagePrefabs = PlainsVillage;
break;
}
default:
{
// Village-unfriendly biome, bail out with zero structure:
return cStructurePtr();
}
} // switch (Biomes[i])
} // for i - Biomes[]
// Choose density for the village, random between m_MinDensity and m_MaxDensity:
int Density;
if (m_MaxDensity > m_MinDensity)
{
Density = m_MinDensity + rnd % (m_MaxDensity - m_MinDensity);
}
else
{
Density = m_MinDensity;
}
// Create a village based on the chosen prefabs:
if (VillagePrefabs == NULL)
{
return cStructurePtr();
}
return cStructurePtr(new cVillage(m_Seed, a_OriginX, a_OriginZ, m_MaxDepth, m_MaxSize, Density, *VillagePrefabs, m_HeightGen, RoadBlock));
}

View File

@ -0,0 +1,57 @@
// VillageGen.h
// Declares the cVillageGen class representing the village generator
#pragma once
#include "GridStructGen.h"
#include "PrefabPiecePool.h"
class cVillageGen :
public cGridStructGen
{
typedef cGridStructGen super;
public:
cVillageGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize, int a_MinDensity, int a_MaxDensity, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen);
protected:
class cVillage; // fwd: VillageGen.cpp
/** The noise used for generating random numbers */
cNoise m_Noise;
/** Maximum depth of the generator tree*/
int m_MaxDepth;
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */
int m_MaxSize;
/** Minimum density - percentage of allowed house connections. Range [0, 100] */
int m_MinDensity;
/** Maximum density - percentage of allowed house connections. Range [0, 100] */
int m_MaxDensity;
/** The underlying biome generator that defines whether the village is created or not */
cBiomeGen & m_BiomeGen;
/** The underlying height generator, used to position the prefabs crossing chunk borders */
cTerrainHeightGen & m_HeightGen;
// cGridStructGen overrides:
virtual cStructurePtr CreateStructure(int a_OriginX, int a_OriginZ) override;
} ;

View File

@ -225,16 +225,28 @@ template class SizeChecker<UInt16, 2>;
#ifndef TEST_GLOBALS
// Common headers (part 1, without macros):
#include "StringUtils.h"
#include "OSSupport/Sleep.h"
#include "OSSupport/CriticalSection.h"
#include "OSSupport/Semaphore.h"
#include "OSSupport/Event.h"
#include "OSSupport/Thread.h"
#include "OSSupport/File.h"
#include "MCLogger.h"
#else
// Logging functions
void inline LOGERROR(const char* a_Format, ...) FORMATSTRING(1,2);
// Common headers (part 1, without macros):
#include "StringUtils.h"
#include "OSSupport/Sleep.h"
#include "OSSupport/CriticalSection.h"
#include "OSSupport/Semaphore.h"
#include "OSSupport/Event.h"
#include "OSSupport/Thread.h"
#include "OSSupport/File.h"
#include "MCLogger.h"
void inline LOGERROR(const char* a_Format, ...)
{
va_list argList;
va_start(argList, a_Format);
vprintf(a_Format, argList);
va_end(argList);
}
#endif
@ -253,10 +265,44 @@ template class SizeChecker<UInt16, 2>;
#define FAST_FLOOR_DIV( x, div ) (((x) - (((x) < 0) ? ((div) - 1) : 0)) / (div))
// Own version of assert() that writes failed assertions to the log for review
#ifdef _DEBUG
#define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) )
#ifdef TEST_GLOBALS
class cAssertFailure
{
};
#ifdef _WIN32
#if (defined(_MSC_VER) && defined(_DEBUG))
#define DBG_BREAK _CrtDbgBreak()
#else
#define DBG_BREAK
#endif
#define REPORT_ERROR(FMT, ...) \
{ \
AString msg = Printf(FMT, __VA_ARGS__); \
puts(msg.c_str()); \
fflush(stdout); \
OutputDebugStringA(msg.c_str()); \
DBG_BREAK; \
}
#else
#define REPORT_ERROR(FMT, ...) \
{ \
AString msg = Printf(FMT, __VA_ARGS__); \
puts(msg.c_str()); \
fflush(stdout); \
}
#endif
#define ASSERT(x) do { if (!(x)) { throw cAssertFailure();} } while (0)
#define testassert(x) do { if(!(x)) { REPORT_ERROR("Test failure: %s, file %s, line %d\n", #x, __FILE__, __LINE__); exit(1); } } while (0)
#define CheckAsserts(x) do { try {x} catch (cAssertFailure) { break; } REPORT_ERROR("Test failure: assert didn't fire for %s, file %s, line %d\n", #x, __FILE__, __LINE__); exit(1); } while (0)
#else
#define ASSERT(x) ((void)(x))
#ifdef _DEBUG
#define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) )
#else
#define ASSERT(x) ((void)(x))
#endif
#endif
// Pretty much the same as ASSERT() but stays in Release builds

View File

@ -45,8 +45,14 @@ cGroupManager::cGroupManager()
{
LOGD("-- Loading Groups --");
LoadGroups();
CheckUsers();
if (!LoadGroups())
{
LOGWARNING("ERROR: Groups could not load!");
}
if (!CheckUsers())
{
LOGWARNING("ERROR: User file could not be found!");
}
LOGD("-- Groups Successfully Loaded --");
}
@ -70,13 +76,13 @@ void cGroupManager::GenerateDefaultUsersIni(cIniFile & a_IniFile)
void cGroupManager::CheckUsers(void)
bool cGroupManager::CheckUsers()
{
cIniFile IniFile;
if (!IniFile.ReadFile("users.ini"))
{
GenerateDefaultUsersIni(IniFile);
return;
return true;
}
int NumKeys = IniFile.GetNumKeys();
@ -97,13 +103,15 @@ void cGroupManager::CheckUsers(void)
}
} // for itr - Split[]
} // for i - ini file keys
// Always return true for now, just but we can handle writefile fails later.
return true;
}
void cGroupManager::LoadGroups()
bool cGroupManager::LoadGroups()
{
cIniFile IniFile;
if (!IniFile.ReadFile("groups.ini"))
@ -180,6 +188,8 @@ void cGroupManager::LoadGroups()
}
}
}
// Always return true, we can handle writefile fails later.
return true;
}

View File

@ -16,8 +16,8 @@ class cGroupManager
public:
bool ExistsGroup(const AString & a_Name);
cGroup * GetGroup(const AString & a_Name);
void LoadGroups(void);
void CheckUsers(void);
bool LoadGroups();
bool CheckUsers();
/** Writes the default header to the specified ini file, and saves it as "users.ini". */
static void GenerateDefaultUsersIni(cIniFile & a_IniFile);

View File

@ -26,6 +26,7 @@ cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) :
cHTTPConnection::~cHTTPConnection()
{
// LOGD("HTTP: Connection deleting: %p", this);
delete m_CurrentRequest;
}
@ -144,7 +145,7 @@ void cHTTPConnection::Terminate(void)
void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size)
bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size)
{
switch (m_State)
{
@ -162,12 +163,12 @@ void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size)
m_CurrentRequest = NULL;
m_State = wcsInvalid;
m_HTTPServer.CloseConnection(*this);
return;
return true;
}
if (m_CurrentRequest->IsInHeaders())
{
// The request headers are not yet complete
return;
return false;
}
// The request has finished parsing its headers successfully, notify of it:
@ -183,13 +184,12 @@ void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size)
// Process the rest of the incoming data into the request body:
if (a_Size > BytesConsumed)
{
DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed);
return cHTTPConnection::DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed);
}
else
{
DataReceived("", 0); // If the request has zero body length, let it be processed right-away
return cHTTPConnection::DataReceived("", 0); // If the request has zero body length, let it be processed right-away
}
break;
}
case wcsRecvBody:
@ -209,7 +209,7 @@ void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size)
{
m_State = wcsInvalid;
m_HTTPServer.CloseConnection(*this);
return;
return true;
}
delete m_CurrentRequest;
m_CurrentRequest = NULL;
@ -223,6 +223,7 @@ void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size)
break;
}
}
return false;
}

View File

@ -91,9 +91,15 @@ protected:
// cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client
virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
virtual void SocketClosed (void) override; // The socket has been closed for any reason
/** Data is received from the client.
Returns true if the connection has been closed as the result of parsing the data. */
virtual bool DataReceived(const char * a_Data, size_t a_Size) override;
/** Data can be sent to client */
virtual void GetOutgoingData(AString & a_Data) override;
/** The socket has been closed for any reason */
virtual void SocketClosed(void) override;
} ;
typedef std::vector<cHTTPConnection *> cHTTPConnections;

View File

@ -201,7 +201,7 @@ size_t cHTTPRequest::ParseRequestLine(const char * a_Data, size_t a_Size)
return AString::npos;
}
// Check that there's HTTP/version at the end
if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0)
if (strncmp(m_IncomingHeaderData.c_str() + URLEnd + 1, "HTTP/1.", 7) != 0)
{
m_IsValid = false;
return AString::npos;

View File

@ -8,6 +8,7 @@
#include "HTTPMessage.h"
#include "HTTPConnection.h"
#include "HTTPFormParser.h"
#include "SslHTTPConnection.h"
@ -142,6 +143,41 @@ cHTTPServer::~cHTTPServer()
bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6)
{
// Read the HTTPS cert + key:
AString CertFile = cFile::ReadWholeFile("webadmin/httpscert.crt");
AString KeyFile = cFile::ReadWholeFile("webadmin/httpskey.pem");
if (!CertFile.empty() && !KeyFile.empty())
{
m_Cert.reset(new cX509Cert);
int res = m_Cert->Parse(CertFile.data(), CertFile.size());
if (res == 0)
{
m_CertPrivKey.reset(new cCryptoKey);
int res2 = m_CertPrivKey->ParsePrivate(KeyFile.data(), KeyFile.size(), "");
if (res2 != 0)
{
// Reading the private key failed, reset the cert:
LOGWARNING("WebServer: Cannot read HTTPS certificate private key: -0x%x", -res2);
m_Cert.reset();
}
}
else
{
LOGWARNING("WebServer: Cannot read HTTPS certificate: -0x%x", -res);
}
}
// Notify the admin about the HTTPS / HTTP status
if (m_Cert.get() == NULL)
{
LOGWARNING("WebServer: The server is running in unsecure HTTP mode.");
}
else
{
LOGINFO("WebServer: The server is running in secure HTTPS mode.");
}
// Open up requested ports:
bool HasAnyPort;
HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4);
HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort;
@ -195,7 +231,15 @@ void cHTTPServer::Stop(void)
void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket)
{
cHTTPConnection * Connection = new cHTTPConnection(*this);
cHTTPConnection * Connection;
if (m_Cert.get() != NULL)
{
Connection = new cSslHTTPConnection(*this, m_Cert, m_CertPrivKey);
}
else
{
Connection = new cHTTPConnection(*this);
}
m_SocketThreads.AddClient(a_Socket, Connection);
cCSLock Lock(m_CSConnections);
m_Connections.push_back(Connection);

View File

@ -12,6 +12,9 @@
#include "../OSSupport/ListenThread.h"
#include "../OSSupport/SocketThreads.h"
#include "inifile/iniFile.h"
#include "PolarSSL++/RsaPrivateKey.h"
#include "PolarSSL++/CryptoKey.h"
#include "PolarSSL++/X509Cert.h"
@ -66,6 +69,7 @@ public:
protected:
friend class cHTTPConnection;
friend class cSslHTTPConnection;
cListenThread m_ListenThreadIPv4;
cListenThread m_ListenThreadIPv6;
@ -78,6 +82,12 @@ protected:
/// The callbacks to call for various events
cCallbacks * m_Callbacks;
/** The server certificate to use for the SSL connections */
cX509CertPtr m_Cert;
/** The private key for m_Cert. */
cCryptoKeyPtr m_CertPrivKey;
// cListenThread::cCallback overrides:
virtual void OnConnectionAccepted(cSocket & a_Socket) override;

View File

@ -0,0 +1,107 @@
// SslHTTPConnection.cpp
// Implements the cSslHTTPConnection class representing a HTTP connection made over a SSL link
#include "Globals.h"
#include "SslHTTPConnection.h"
#include "HTTPServer.h"
cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey) :
super(a_HTTPServer),
m_Ssl(64000),
m_Cert(a_Cert),
m_PrivateKey(a_PrivateKey)
{
m_Ssl.Initialize(false);
m_Ssl.SetOwnCert(a_Cert, a_PrivateKey);
}
bool cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size)
{
// If there is outgoing data in the queue, notify the server that it should write it out:
if (!m_OutgoingData.empty())
{
m_HTTPServer.NotifyConnectionWrite(*this);
}
// Process the received data:
const char * Data = a_Data;
size_t Size = a_Size;
for (;;)
{
// Try to write as many bytes into Ssl's "incoming" buffer as possible:
size_t BytesWritten = 0;
if (Size > 0)
{
BytesWritten = m_Ssl.WriteIncoming(Data, Size);
Data += BytesWritten;
Size -= BytesWritten;
}
// Try to read as many bytes from SSL's decryption as possible:
char Buffer[32000];
int NumRead = m_Ssl.ReadPlain(Buffer, sizeof(Buffer));
if (NumRead > 0)
{
if (super::DataReceived(Buffer, (size_t)NumRead))
{
// The socket has been closed, and the object is already deleted. Bail out.
return true;
}
}
// If both failed, bail out:
if ((BytesWritten == 0) && (NumRead <= 0))
{
return false;
}
}
}
void cSslHTTPConnection::GetOutgoingData(AString & a_Data)
{
for (;;)
{
// Write as many bytes from our buffer to SSL's encryption as possible:
int NumWritten = 0;
if (!m_OutgoingData.empty())
{
NumWritten = m_Ssl.WritePlain(m_OutgoingData.data(), m_OutgoingData.size());
if (NumWritten > 0)
{
m_OutgoingData.erase(0, (size_t)NumWritten);
}
}
// Read as many bytes from SSL's "outgoing" buffer as possible:
char Buffer[32000];
size_t NumBytes = m_Ssl.ReadOutgoing(Buffer, sizeof(Buffer));
if (NumBytes > 0)
{
a_Data.append(Buffer, NumBytes);
}
// If both failed, bail out:
if ((NumWritten <= 0) && (NumBytes == 0))
{
return;
}
}
}

View File

@ -0,0 +1,45 @@
// SslHTTPConnection.h
// Declared the cSslHTTPConnection class representing a HTTP connection made over a SSL link
#pragma once
#include "HTTPConnection.h"
#include "PolarSSL++/BufferedSslContext.h"
class cSslHTTPConnection :
public cHTTPConnection
{
typedef cHTTPConnection super;
public:
/** Creates a new connection on the specified server.
Sends the specified cert as the server certificate, uses the private key for decryption. */
cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey);
protected:
cBufferedSslContext m_Ssl;
/** The certificate to send to the client */
cX509CertPtr m_Cert;
/** The private key used for the certificate */
cCryptoKeyPtr m_PrivateKey;
// cHTTPConnection overrides:
virtual bool DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client
virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
} ;

View File

@ -243,6 +243,16 @@ void cInventory::SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item)
void cInventory::SendEquippedSlot()
{
int EquippedSlotNum = cInventory::invArmorCount + cInventory::invInventoryCount + GetEquippedSlotNum();
SendSlot(EquippedSlotNum);
}
const cItem & cInventory::GetSlot(int a_SlotNum) const
{
if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots))

View File

@ -56,13 +56,13 @@ public:
// tolua_begin
/// Removes all items from the entire inventory
/** Removes all items from the entire inventory */
void Clear(void);
/// Returns number of items out of a_ItemStack that can fit in the storage
/** Returns number of items out of a_ItemStack that can fit in the storage */
int HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots);
/// Returns how many items of the specified type would fit into the slot range specified
/** Returns how many items of the specified type would fit into the slot range specified */
int HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots);
/** Adds as many items out of a_ItemStack as can fit.
@ -86,33 +86,36 @@ public:
*/
int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst);
/// Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed
/** Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed */
bool RemoveOneEquippedItem(void);
/// Returns the number of items of type a_Item that are stored
/** Returns the number of items of type a_Item that are stored */
int HowManyItems(const cItem & a_Item);
/// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack
/** Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack */
bool HasItems(const cItem & a_ItemStack);
/** Sends the equipped item slot to the client */
void SendEquippedSlot();
/// Returns the cItemGrid object representing the armor slots
/** Returns the cItemGrid object representing the armor slots */
cItemGrid & GetArmorGrid(void) { return m_ArmorSlots; }
/// Returns the cItemGrid object representing the main inventory slots
/** Returns the cItemGrid object representing the main inventory slots */
cItemGrid & GetInventoryGrid(void) { return m_InventorySlots; }
/// Returns the cItemGrid object representing the hotbar slots
/** Returns the cItemGrid object representing the hotbar slots */
cItemGrid & GetHotbarGrid(void) { return m_HotbarSlots; }
/// Returns the player associated with this inventory
/** Returns the player associated with this inventory */
cPlayer & GetOwner(void) { return m_Owner; }
/// Copies the non-empty slots into a_ItemStacks; preserves the original a_Items contents
/** Copies the non-empty slots into a_ItemStacks; preserves the original a_Items contents */
void CopyToItems(cItems & a_Items);
// tolua_end
/// Returns the player associated with this inventory (const version)
/** Returns the player associated with this inventory (const version) */
const cPlayer & GetOwner(void) const { return m_Owner; }
// tolua_begin
@ -136,10 +139,10 @@ public:
*/
int ChangeSlotCount(int a_SlotNum, int a_AddToCount);
/// Adds the specified damage to the specified item; deletes the item and returns true if the item broke.
/** Adds the specified damage to the specified item; deletes the item and returns true if the item broke. */
bool DamageItem(int a_SlotNum, short a_Amount);
/// Adds the specified damage to the currently held item; deletes the item and returns true if the item broke.
/** Adds the specified damage to the currently held item; deletes the item and returns true if the item broke. */
bool DamageEquippedItem(short a_Amount = 1);
const cItem & GetEquippedHelmet (void) const { return m_ArmorSlots.GetSlot(0); }
@ -149,13 +152,13 @@ public:
// tolua_end
/// Sends the slot contents to the owner
/** Sends the slot contents to the owner */
void SendSlot(int a_SlotNum);
/// Update items (e.g. Maps)
/** Update items (e.g. Maps) */
void UpdateItems(void);
/// Converts an armor slot number into the ID for the EntityEquipment packet
/** Converts an armor slot number into the ID for the EntityEquipment packet */
static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum);
void SaveToJson(Json::Value & a_Value);
@ -172,10 +175,10 @@ protected:
cPlayer & m_Owner;
/// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum
/** Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum */
const cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const;
/// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum
/** Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum */
cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum);
// cItemGrid::cListener override:

View File

@ -228,7 +228,7 @@ public:
void Add (const cItem & a_Item) {push_back(a_Item); }
void Delete(int a_Idx);
void Clear (void) {clear(); }
size_t Size (void) {return size(); }
size_t Size (void) const { return size(); }
void Set (int a_Idx, short a_ItemType, char a_ItemCount, short a_ItemDamage);
void Add (short a_ItemType, char a_ItemCount, short a_ItemDamage)

View File

@ -142,6 +142,8 @@ public:
break;
}
}
a_Player->GetStatManager().AddValue(statTreasureFished, 1);
}
else if (ItemCategory <= 14) // Junk 10%
{
@ -190,6 +192,8 @@ public:
{
Drops.Add(cItem(E_BLOCK_TRIPWIRE_HOOK));
}
a_Player->GetStatManager().AddValue(statJunkFished, 1);
}
else // Fish
{
@ -210,6 +214,8 @@ public:
{
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_FISH));
}
a_Player->GetStatManager().AddValue(statFishCaught, 1);
}
if (cRoot::Get()->GetPluginManager()->CallHookPlayerFishing(*a_Player, Drops))

View File

@ -18,20 +18,17 @@
class cReader :
public cChunkDataCallback
{
virtual void BlockTypes(const BLOCKTYPE * a_Type) override
virtual void ChunkData(const cChunkData & a_ChunkBuffer) override
{
// ROW is a block of 16 Blocks, one whole row is copied at a time (hopefully the compiler will optimize that)
// C++ doesn't permit copying arrays, but arrays as a part of a struct is ok :)
typedef struct {BLOCKTYPE m_Row[16]; } ROW;
ROW * InputRows = (ROW *)a_Type;
ROW * OutputRows = (ROW *)m_BlockTypes;
BLOCKTYPE * OutputRows = m_BlockTypes;
int InputIdx = 0;
int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3;
for (int y = 0; y < cChunkDef::Height; y++)
{
for (int z = 0; z < cChunkDef::Width; z++)
{
OutputRows[OutputIdx] = InputRows[InputIdx++];
a_ChunkBuffer.CopyBlockTypes(OutputRows + OutputIdx * 16, InputIdx * 16, 16);
InputIdx++;
OutputIdx += 3;
} // for z
// Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows

View File

@ -24,7 +24,7 @@ void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, doubl
if (a_Distance < it->second.m_Distance)
{
it->second.m_Distance = a_Distance;
it->second.m_Chunk = a_Chunk;
it->second.m_Chunk = &a_Chunk;
}
}
@ -36,7 +36,7 @@ void cMobProximityCounter::convertMaps()
{
for(tMonsterToDistance::const_iterator itr = m_MonsterToDistance.begin(); itr != m_MonsterToDistance.end(); ++itr)
{
m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,itr->second.m_Chunk)));
m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,*itr->second.m_Chunk)));
}
}

View File

@ -16,9 +16,9 @@ protected :
// structs used for later maps (see m_MonsterToDistance and m_DistanceToMonster)
struct sDistanceAndChunk
{
sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(a_Chunk) {}
sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(&a_Chunk) {}
double m_Distance;
cChunk& m_Chunk;
cChunk* m_Chunk;
};
struct sMonsterAndChunk
{

View File

@ -355,6 +355,8 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
InStateEscaping(a_Dt);
break;
}
case ATTACKING: break;
} // switch (m_EMState)
BroadcastMovementUpdate();
@ -464,8 +466,10 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
return false;
}
if((m_SoundHurt != "") && (m_Health > 0))
if (!m_SoundHurt.empty() && (m_Health > 0))
{
m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f);
}
if (a_TDI.Attacker != NULL)
{

View File

@ -2,7 +2,9 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Wither.h"
#include "../World.h"
#include "../Entities/Player.h"
@ -100,3 +102,35 @@ void cWither::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cWither::KilledBy(cEntity * a_Killer)
{
super::KilledBy(a_Killer);
class cPlayerCallback : public cPlayerListCallback
{
Vector3f m_Pos;
virtual bool Item(cPlayer * a_Player)
{
// TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one
double Dist = (a_Player->GetPosition() - m_Pos).Length();
if (Dist < 50.0)
{
// If player is close, award achievement
a_Player->AwardAchievement(achKillWither);
}
return false;
}
public:
cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
} PlayerCallback(GetPosition());
m_World->ForEachPlayer(PlayerCallback);
}

View File

@ -29,6 +29,7 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void KilledBy(cEntity * a_Killer) override;
private:

View File

@ -63,8 +63,10 @@ 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, size_t a_Size) = 0;
/** Called when data is received from the remote party.
SocketThreads does not care about the return value, others can use it for their specific purpose -
for example HTTPServer uses it to signal if the connection was terminated as a result of the data received. */
virtual bool DataReceived(const char * a_Data, size_t a_Size) = 0;
/** Called when data can be sent to remote party
The function is supposed to *set* outgoing data to a_Data (overwrite) */

View File

@ -20,6 +20,37 @@ cBufferedSslContext::cBufferedSslContext(size_t a_BufferSize):
size_t cBufferedSslContext::WriteIncoming(const void * a_Data, size_t a_NumBytes)
{
size_t NumBytes = std::min(m_IncomingData.GetFreeSpace(), a_NumBytes);
if (NumBytes > 0)
{
m_IncomingData.Write(a_Data, NumBytes);
return NumBytes;
}
return 0;
}
size_t cBufferedSslContext::ReadOutgoing(void * a_Data, size_t a_DataMaxSize)
{
size_t NumBytes = std::min(m_OutgoingData.GetReadableSpace(), a_DataMaxSize);
if (NumBytes > 0)
{
m_OutgoingData.ReadBuf(a_Data, NumBytes);
m_OutgoingData.CommitRead();
return NumBytes;
}
return 0;
}
int cBufferedSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes)
{
// Called when PolarSSL wants to read encrypted data from the SSL peer

View File

@ -1,4 +1,3 @@
cmake_minimum_required (VERSION 2.6)
project (MCServer)
@ -11,8 +10,8 @@ set(SOURCES
BufferedSslContext.cpp
CallbackSslContext.cpp
CtrDrbgContext.cpp
CryptoKey.cpp
EntropyContext.cpp
PublicKey.cpp
RsaPrivateKey.cpp
Sha1Checksum.cpp
SslContext.cpp
@ -26,8 +25,8 @@ set(HEADERS
BufferedSslContext.h
CallbackSslContext.h
CtrDrbgContext.h
CryptoKey.h
EntropyContext.h
PublicKey.h
RsaPrivateKey.h
SslContext.h
Sha1Checksum.h

View File

@ -0,0 +1,149 @@
// CryptoKey.cpp
// Implements the cCryptoKey class representing a RSA public key in PolarSSL
#include "Globals.h"
#include "CryptoKey.h"
cCryptoKey::cCryptoKey(void)
{
pk_init(&m_Pk);
m_CtrDrbg.Initialize("rsa_pubkey", 10);
}
cCryptoKey::cCryptoKey(const AString & a_PublicKeyData)
{
pk_init(&m_Pk);
m_CtrDrbg.Initialize("rsa_pubkey", 10);
int res = ParsePublic(a_PublicKeyData.data(), a_PublicKeyData.size());
if (res != 0)
{
LOGWARNING("Failed to parse public key: -0x%x", res);
ASSERT(!"Cannot parse PubKey");
return;
}
}
cCryptoKey::cCryptoKey(const AString & a_PrivateKeyData, const AString & a_Password)
{
pk_init(&m_Pk);
m_CtrDrbg.Initialize("rsa_privkey", 11);
int res = ParsePrivate(a_PrivateKeyData.data(), a_PrivateKeyData.size(), a_Password);
if (res != 0)
{
LOGWARNING("Failed to parse private key: -0x%x", res);
ASSERT(!"Cannot parse PubKey");
return;
}
}
cCryptoKey::~cCryptoKey()
{
pk_free(&m_Pk);
}
int cCryptoKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength)
{
ASSERT(IsValid());
size_t DecryptedLen = a_DecryptedMaxLength;
int res = pk_decrypt(&m_Pk,
a_EncryptedData, a_EncryptedLength,
a_DecryptedData, &DecryptedLen, a_DecryptedMaxLength,
ctr_drbg_random, m_CtrDrbg.GetInternal()
);
if (res != 0)
{
return res;
}
return (int)DecryptedLen;
}
int cCryptoKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength)
{
ASSERT(IsValid());
size_t EncryptedLength = a_EncryptedMaxLength;
int res = pk_encrypt(&m_Pk,
a_PlainData, a_PlainLength, a_EncryptedData, &EncryptedLength, a_EncryptedMaxLength,
ctr_drbg_random, m_CtrDrbg.GetInternal()
);
if (res != 0)
{
return res;
}
return (int)EncryptedLength;
}
int cCryptoKey::ParsePublic(const void * a_Data, size_t a_NumBytes)
{
ASSERT(!IsValid()); // Cannot parse a second key
return pk_parse_public_key(&m_Pk, (const unsigned char *)a_Data, a_NumBytes);
}
int cCryptoKey::ParsePrivate(const void * a_Data, size_t a_NumBytes, const AString & a_Password)
{
ASSERT(!IsValid()); // Cannot parse a second key
if (a_Password.empty())
{
return pk_parse_key(&m_Pk, (const unsigned char *)a_Data, a_NumBytes, NULL, 0);
}
else
{
return pk_parse_key(
&m_Pk,
(const unsigned char *)a_Data, a_NumBytes,
(const unsigned char *)a_Password.c_str(), a_Password.size()
);
}
}
bool cCryptoKey::IsValid(void) const
{
return (pk_get_type(&m_Pk) != POLARSSL_PK_NONE);
}

View File

@ -0,0 +1,76 @@
// CryptoKey.h
// Declares the cCryptoKey class representing a RSA public key in PolarSSL
#pragma once
#include "CtrDrbgContext.h"
#include "polarssl/pk.h"
class cCryptoKey
{
friend class cSslContext;
public:
/** Constructs an empty key instance. Before use, it needs to be filled by ParsePublic() or ParsePrivate() */
cCryptoKey(void);
/** Constructs the public key out of the DER- or PEM-encoded pubkey data */
cCryptoKey(const AString & a_PublicKeyData);
/** Constructs the private key out of the DER- or PEM-encoded privkey data, with the specified password.
If a_Password is empty, no password is assumed. */
cCryptoKey(const AString & a_PrivateKeyData, const AString & a_Password);
~cCryptoKey();
/** Decrypts the data using the stored public key
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
Returns the number of bytes decrypted, or negative number for error. */
int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength);
/** Encrypts the data using the stored public key
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
Returns the number of bytes decrypted, or negative number for error. */
int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength);
/** Parses the specified data into a public key representation.
The key can be DER- or PEM-encoded.
Returns 0 on success, PolarSSL error code on failure. */
int ParsePublic(const void * a_Data, size_t a_NumBytes);
/** Parses the specified data into a private key representation.
If a_Password is empty, no password is assumed.
The key can be DER- or PEM-encoded.
Returns 0 on success, PolarSSL error code on failure. */
int ParsePrivate(const void * a_Data, size_t a_NumBytes, const AString & a_Password);
/** Returns true if the contained key is valid. */
bool IsValid(void) const;
protected:
/** The PolarSSL representation of the key data */
pk_context m_Pk;
/** The random generator used in encryption and decryption */
cCtrDrbgContext m_CtrDrbg;
/** Returns the internal context ptr. Only use in PolarSSL API calls. */
pk_context * GetInternal(void) { return &m_Pk; }
} ;
typedef SharedPtr<cCryptoKey> cCryptoKeyPtr;

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