├── src ├── mod.cpp ├── network │ ├── posix │ │ ├── channel_base.cpp │ │ ├── send_buffer.cpp │ │ ├── command.cpp │ │ ├── follow_up.cpp │ │ ├── worker.cpp │ │ ├── strerror_r.cpp │ │ ├── misc.cpp │ │ ├── worker_channel.cpp │ │ └── error.cpp │ ├── windows │ │ ├── completion_command.cpp │ │ ├── make_socket.cpp │ │ ├── initializer.cpp │ │ ├── send_command.cpp │ │ ├── receive_command.cpp │ │ ├── connect_command.cpp │ │ ├── iocp.cpp │ │ ├── accept_data.cpp │ │ ├── error.cpp │ │ ├── reference_manager.cpp │ │ └── accept_command.cpp │ ├── connection.cpp │ └── linux │ │ ├── notification.cpp │ │ └── notifier.cpp ├── world │ ├── generator.cpp │ ├── begin.cpp │ ├── key.cpp │ ├── populators.cpp │ ├── get_info.cpp │ ├── interest.cpp │ ├── events.cpp │ ├── populator.cpp │ ├── load.cpp │ ├── get_column.cpp │ ├── has_skylight.cpp │ ├── block_id.cpp │ ├── generators.cpp │ ├── column_id.cpp │ ├── set_seed.cpp │ ├── save.cpp │ ├── world.cpp │ ├── add_client.cpp │ └── maintenance.cpp ├── mysql_data_provider │ ├── blob.cpp │ ├── get_buffer.cpp │ ├── prepared_statement.cpp │ └── factory.cpp ├── command │ └── command.cpp ├── player │ ├── player.cpp │ ├── get_player.cpp │ ├── update_position.cpp │ └── event_handlers.cpp ├── cli │ ├── error.cpp │ ├── cli_provider.cpp │ ├── date_time.cpp │ └── args.cpp ├── format.cpp ├── concurrency_manager.cpp ├── hardware_concurrency.cpp ├── permissions │ ├── permissions_table_entry.cpp │ └── info.cpp ├── handshake │ └── main.cpp ├── entity_id │ └── main.cpp ├── new_delete.cpp ├── recursive_mutex.cpp ├── serializer.cpp ├── basic_chat │ └── main.cpp ├── data_provider.cpp ├── blacklist │ └── info.cpp ├── info │ ├── mcpp.cpp │ ├── dp.cpp │ ├── pool.cpp │ ├── mods.cpp │ └── handler.cpp ├── brand │ ├── info.cpp │ └── main.cpp ├── whitelist │ └── info.cpp ├── chat_login │ └── main.cpp ├── base_64.cpp ├── packet_router.cpp ├── save │ └── info.cpp ├── ban │ └── info.cpp ├── client_list_iterator.cpp ├── url.cpp ├── kick │ └── main.cpp ├── client_list.cpp ├── chat │ └── chat_misc.cpp ├── multi_scope_guard.cpp └── time │ └── info.cpp ├── include ├── fma.hpp ├── typedefs.hpp ├── base_64.hpp ├── hardware_concurrency.hpp ├── format.hpp ├── chat_provider.hpp ├── url.hpp ├── command_interpreter.hpp ├── seed_sequence.hpp ├── brand │ └── brand.hpp ├── entity_id │ └── entity_id.hpp ├── safeint.hpp ├── traits.hpp ├── sha1.hpp ├── uniform_int_distribution.hpp ├── singleton.hpp ├── recursive_mutex.hpp ├── info │ └── info.hpp ├── synchronized_random.hpp ├── blacklist │ └── blacklist.hpp ├── auth │ └── auth.hpp ├── mod.hpp ├── packet_router.hpp ├── save │ └── save.hpp ├── rsa_key.hpp ├── whitelist │ └── whitelist.hpp ├── ban │ └── ban.hpp ├── aes_128_cfb_8.hpp ├── plugin_message │ └── plugin_message.hpp └── compression.hpp ├── instructions.txt ├── .gitattributes ├── schema.sql ├── LICENSE.txt └── README.md /src/mod.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | Module::Module () noexcept { } 8 | 9 | 10 | Module::~Module () noexcept { } 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /include/fma.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | #ifndef FP_FAST_FMA 13 | #ifdef fma 14 | #undef fma 15 | #endif 16 | #define fma(a,b,c) (((a)*(b))+(c)) 17 | #endif 18 | -------------------------------------------------------------------------------- /src/network/posix/channel_base.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | ChannelBase::~ChannelBase () noexcept { } 11 | 12 | 13 | } 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/world/generator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | void World::generate (ColumnContainer & column) { 8 | 9 | get_generator(column.ID().Dimension)(column); 10 | 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/network/posix/send_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | using namespace MCPP::NetworkImpl; 5 | 6 | 7 | namespace MCPP { 8 | 9 | 10 | Connection::SendBuffer::SendBuffer (Vector buffer) noexcept : Buffer(std::move(buffer)), Sent(0) { } 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/world/begin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | WorldHandle World::Begin (BlockWriteStrategy write, BlockAccessStrategy access) { 8 | 9 | return WorldHandle( 10 | this, 11 | write, 12 | access 13 | ); 14 | 15 | } 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/mysql_data_provider/blob.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace MySQL { 8 | 9 | 10 | Blob::Blob (const void * ptr, Word len) : Pointer(ptr), Length(safe_cast(len)) { } 11 | 12 | 13 | } 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/command/command.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | Vector Command::AutoComplete (const CommandEvent &) { 8 | 9 | return Vector(); 10 | 11 | } 12 | 13 | 14 | bool Command::Check (const CommandEvent &) { 15 | 16 | return true; 17 | 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /include/typedefs.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | namespace MCPP { 13 | 14 | 15 | /** 16 | * The type of the callback which shall 17 | * be invoked to write to the log. 18 | */ 19 | typedef std::function LogType; 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/network/posix/command.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | using namespace MCPP::NetworkImpl; 5 | 6 | 7 | namespace MCPP { 8 | 9 | 10 | ConnectionHandler::Command::Command (CommandType type, FDType fd, SmartPointer channel) noexcept 11 | : Type(type), 12 | FD(fd), 13 | Impl(std::move(channel)) 14 | { } 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/player/player.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | Player::~Player () noexcept { 8 | 9 | // Drop all columns so that they do 10 | // not spuriously remain loaded 11 | for (auto & column : Columns) World::Get().Remove( 12 | Conn, 13 | column, 14 | true 15 | ); 16 | 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/cli/error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace MCPP { 6 | 7 | 8 | namespace CLIImpl { 9 | 10 | 11 | void Raise () { 12 | 13 | throw std::system_error( 14 | std::error_code( 15 | GetLastError(), 16 | std::system_category() 17 | ) 18 | ); 19 | 20 | } 21 | 22 | 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/network/posix/follow_up.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | FollowUp::FollowUp () noexcept 11 | : Remove(false), 12 | Sent(0), 13 | Received(0), 14 | Incoming(0), 15 | Outgoing(0), 16 | Accepted(0), 17 | Disconnected(0) 18 | { } 19 | 20 | 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /instructions.txt: -------------------------------------------------------------------------------- 1 | MSYS: 2 | 3 | http://sourceforge.net/projects/mingwbuilds/files/external-binary-packages/ 4 | 5 | Instructions for building OpenSSL: 6 | 7 | http://stackoverflow.com/questions/9379363/how-to-build-openssl-with-mingw-in-windows 8 | 9 | OpenSSL: 10 | 11 | http://www.openssl.org/source/ 12 | 13 | Compiling ZLIB: 14 | 15 | http://kemovitra.blogspot.ca/2009/06/mingw-compiling-zlib.html#.UaaYmkDvv-t -------------------------------------------------------------------------------- /src/format.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | String FormatVersion (Word major, Word minor, Word patch) { 8 | 9 | String retr(major); 10 | 11 | if (!((minor==0) && (patch==0))) { 12 | 13 | retr << "." << String(minor); 14 | 15 | if (patch!=0) retr << "." << String(patch); 16 | 17 | } 18 | 19 | return retr; 20 | 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/player/get_player.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | SmartPointer Players::get (const SmartPointer & client) noexcept { 8 | 9 | return players_lock.Read([&] () { 10 | 11 | auto iter=players.find(client); 12 | 13 | return (iter==players.end()) ? SmartPointer() : iter->second; 14 | 15 | }); 16 | 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/network/posix/worker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | using namespace MCPP::NetworkImpl; 5 | 6 | 7 | namespace MCPP { 8 | 9 | 10 | ConnectionHandler::Worker::Worker () { 11 | 12 | Control.Attach(N); 13 | 14 | Count=0; 15 | 16 | } 17 | 18 | 19 | void ConnectionHandler::Worker::Update (FDType fd) { 20 | 21 | Control.Send(Command(CommandType::Update,fd)); 22 | 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/network/windows/completion_command.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace MCPP { 6 | 7 | 8 | namespace NetworkImpl { 9 | 10 | 11 | CompletionCommand::CompletionCommand (CommandType type) noexcept : Type(type) { 12 | 13 | // Zero out overlapped structure as 14 | // WinAPI requires 15 | std::memset(&overlapped,0,sizeof(overlapped)); 16 | 17 | } 18 | 19 | 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/network/windows/make_socket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | SOCKET MakeSocket (bool is_v6) { 11 | 12 | SOCKET retr=socket( 13 | is_v6 ? AF_INET6 : AF_INET, 14 | SOCK_STREAM, 15 | IPPROTO_TCP 16 | ); 17 | 18 | if (retr==INVALID_SOCKET) RaiseWSA(); 19 | 20 | return retr; 21 | 22 | } 23 | 24 | 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /include/base_64.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | namespace Base64 { 13 | 14 | 15 | /** 16 | * Encodes a buffer of bytes as 17 | * a base 64 string. 18 | * 19 | * \param [in] buffer 20 | * A buffer of bytes. 21 | * 22 | * \return 23 | * The base 64 encoding of the 24 | * string. 25 | */ 26 | String Encode (const Vector & buffer); 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/network/windows/initializer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | Initializer::Initializer () { 11 | 12 | WSADATA temp; // Ignored 13 | int result=WSAStartup(MAKEWORD(2,2),&temp); 14 | if (result!=0) Raise(result); 15 | 16 | } 17 | 18 | 19 | Initializer::~Initializer () noexcept { 20 | 21 | WSACleanup(); 22 | 23 | } 24 | 25 | 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/world/key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | static const String key_template("column_{0}_{1}_{2}"); 8 | 9 | 10 | String World::key (ColumnID id) const { 11 | 12 | return String::Format( 13 | key_template, 14 | id.X, 15 | id.Z, 16 | id.Dimension 17 | ); 18 | 19 | } 20 | 21 | 22 | String World::key (const ColumnContainer & column) const { 23 | 24 | return key(column.ID()); 25 | 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/network/posix/strerror_r.cpp: -------------------------------------------------------------------------------- 1 | // Apparently setting -std=gnu++11 sets 2 | // _GNU_SOURCE on the command line 3 | #ifdef _GNU_SOURCE 4 | #undef _GNU_SOURCE 5 | #endif 6 | #define _POSIX_C_SOURCE 200112L 7 | #include 8 | 9 | 10 | namespace MCPP { 11 | 12 | 13 | namespace NetworkImpl { 14 | 15 | 16 | int strerror_r (int errnum, char * buf, size_t buflen) noexcept { 17 | 18 | return ::strerror_r(errnum,buf,buflen); 19 | 20 | } 21 | 22 | 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/concurrency_manager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | ConcurrencyManager::ConcurrencyManager ( 8 | ThreadPool & pool, 9 | Word max, 10 | std::function panic 11 | ) noexcept : pool(pool), max(max), running(0), panic(std::move(panic)) { 12 | 13 | // Sanity check 14 | if (this->max==0) this->max=1; 15 | 16 | } 17 | 18 | 19 | Word ConcurrencyManager::Maximum () const noexcept { 20 | 21 | return max; 22 | 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /include/hardware_concurrency.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | namespace MCPP { 13 | 14 | 15 | /** 16 | * Determines the number of simultaneous 17 | * threads of executing the hardware this 18 | * program is running on may execute. 19 | * 20 | * \return 21 | * The number of concurrent threads 22 | * that the hardware of this computer 23 | * may execute. 24 | */ 25 | Word HardwareConcurrency () noexcept; 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/hardware_concurrency.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | #ifdef ENVIRONMENT_WINDOWS 5 | #include 6 | #else 7 | #include 8 | #endif 9 | 10 | 11 | namespace MCPP { 12 | 13 | 14 | Word HardwareConcurrency () noexcept { 15 | 16 | #ifdef ENVIRONMENT_WINDOWS 17 | 18 | SYSTEM_INFO sysinfo; 19 | GetSystemInfo(&sysinfo); 20 | return sysinfo.dwNumberOfProcessors; 21 | 22 | #else 23 | 24 | return sysconf(_SC_NPROCESSORS_ONLN); 25 | 26 | #endif 27 | 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /include/format.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | namespace MCPP { 13 | 14 | 15 | /** 16 | * Formats a triplet of version numbers 17 | * into a descriptive string. 18 | * 19 | * \param [in] major 20 | * The major version number. 21 | * \param [in] minor 22 | * The minor version number. 23 | * \param [in] patch 24 | * The patch number. 25 | * 26 | * \return 27 | * A formatted string. 28 | */ 29 | String FormatVersion (Word major, Word minor, Word patch); 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/network/connection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | using namespace MCPP::NetworkImpl; 6 | 7 | 8 | namespace MCPP { 9 | 10 | 11 | IPAddress Connection::IP () const noexcept { 12 | 13 | return remote_ip; 14 | 15 | } 16 | 17 | 18 | UInt16 Connection::Port () const noexcept { 19 | 20 | return remote_port; 21 | 22 | } 23 | 24 | 25 | Word Connection::Sent () const noexcept { 26 | 27 | return sent; 28 | 29 | } 30 | 31 | 32 | Word Connection::Received () const noexcept { 33 | 34 | return received; 35 | 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /include/chat_provider.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | 13 | namespace MCPP { 14 | 15 | 16 | /** 17 | * Provides the ability for a front-end 18 | * to send messages through chat. 19 | */ 20 | class ChatProvider { 21 | 22 | 23 | public: 24 | 25 | 26 | /** 27 | * Sends a message through chat. 28 | * 29 | * \param [in] message 30 | * The message to send through 31 | * chat. 32 | */ 33 | virtual void Send (const String & message) = 0; 34 | 35 | 36 | }; 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/permissions/permissions_table_entry.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | using namespace MCPP::PermissionsImpl; 5 | 6 | 7 | namespace MCPP { 8 | 9 | 10 | PermissionsTableEntry::PermissionsTableEntry () noexcept : Full(false) { } 11 | 12 | 13 | PermissionsTableEntry::PermissionsTableEntry (String name, const PermissionsSet & set) 14 | : Name(std::move(name)), 15 | Full(set.Individual.Full()), 16 | Difference(set.Individual.Count()), 17 | Groups(set.Groups.size()) 18 | { 19 | 20 | for (auto & s : set.Individual) Difference.Add(s); 21 | 22 | for (auto & s : set.Groups) Groups.Add(s); 23 | 24 | } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/world/populators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | void World::Add (const Populator * populator, SByte dimension, Word priority) { 8 | 9 | // Ignore null populators 10 | if (populator==nullptr) return; 11 | 12 | // Get the vector of populators associated 13 | // with this dimension 14 | auto & vec=populators[dimension]; 15 | 16 | // Find insertion point 17 | Word i=0; 18 | for ( 19 | ; 20 | (ivec[i].Item<1>()); 22 | ++i 23 | ); 24 | 25 | // Perform insertion 26 | vec.Emplace( 27 | i, 28 | populator, 29 | priority 30 | ); 31 | 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/world/get_info.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | WorldInfo World::GetInfo () const noexcept { 8 | 9 | Word num=lock.Execute([&] () { return world.size(); }); 10 | 11 | return WorldInfo{ 12 | Word(maintenances), 13 | UInt64(maintenance_time), 14 | Word(loaded), 15 | UInt64(load_time), 16 | Word(unloaded), 17 | Word(saved), 18 | UInt64(save_time), 19 | Word(generated), 20 | UInt64(generate_time), 21 | Word(populated), 22 | UInt64(populate_time), 23 | num, 24 | num*( 25 | sizeof(ColumnContainer::Blocks)+ 26 | sizeof(ColumnContainer::Biomes)+ 27 | sizeof(ColumnContainer::Populated) 28 | ) 29 | }; 30 | 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /include/url.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #include 7 | 8 | 9 | namespace MCPP { 10 | 11 | 12 | /** 13 | * Contains tools for dealing with 14 | * URLs. 15 | */ 16 | namespace URL { 17 | 18 | 19 | /** 20 | * URL encodes a given string. 21 | * 22 | * \param [in] encode 23 | * The string to be encoded. 24 | * 25 | * \return 26 | * The encoded string. 27 | */ 28 | String Encode (const String & encode); 29 | /** 30 | * URL decodes a given string. 31 | * 32 | * \param [in] decode 33 | * The string to be decoded. 34 | * 35 | * \return 36 | * The decoded string. 37 | */ 38 | String Decode (const String & decode); 39 | 40 | 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /schema.sql: -------------------------------------------------------------------------------- 1 | DROP DATABASE IF EXISTS `mcpp`; 2 | 3 | CREATE DATABASE `mcpp` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'; 4 | 5 | USE `mcpp`; 6 | 7 | CREATE TABLE `settings` ( 8 | `setting` varchar(191) PRIMARY KEY, 9 | `value` text 10 | ); 11 | 12 | CREATE TABLE `log` ( 13 | `when` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 14 | `type` varchar(255) NOT NULL, 15 | `text` text NOT NULL 16 | ); 17 | 18 | CREATE TABLE `chat_log` ( 19 | `when` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 20 | `from` text NOT NULL, 21 | `to` text, 22 | `message` text NOT NULL, 23 | `notes` text 24 | ); 25 | 26 | CREATE TABLE `keyvaluestore` ( 27 | `key` varchar(191) NOT NULL, 28 | `value` text NOT NULL, 29 | INDEX(`key`) 30 | ); 31 | 32 | CREATE TABLE `binary` ( 33 | `key` varchar(191) PRIMARY KEY, 34 | `value` mediumblob NOT NULL 35 | ); 36 | -------------------------------------------------------------------------------- /src/network/linux/notification.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | bool Notification::impl (uint32_t flag) const noexcept { 11 | 12 | return (event.events&flag)!=0; 13 | 14 | } 15 | 16 | 17 | FDType Notification::FD () const noexcept { 18 | 19 | return event.data.fd; 20 | 21 | } 22 | 23 | 24 | bool Notification::Readable () const noexcept { 25 | 26 | return impl(EPOLLIN); 27 | 28 | } 29 | 30 | 31 | bool Notification::Writeable () const noexcept { 32 | 33 | return impl(EPOLLOUT); 34 | 35 | } 36 | 37 | 38 | bool Notification::Error () const noexcept { 39 | 40 | return impl(EPOLLERR); 41 | 42 | } 43 | 44 | 45 | bool Notification::End () const noexcept { 46 | 47 | return impl(EPOLLHUP); 48 | 49 | } 50 | 51 | 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /include/command_interpreter.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | namespace MCPP { 13 | 14 | 15 | /** 16 | * Specifies an interface to which 17 | * command interpreters must conform, 18 | * allowing the front-end to issue 19 | * commands and receive the results 20 | * thereof. 21 | */ 22 | class CommandInterpreter { 23 | 24 | 25 | public: 26 | 27 | 28 | /** 29 | * Executes a command and obtains the 30 | * result thereof. 31 | * 32 | * \param [in] command 33 | * The text of the command to 34 | * execute. 35 | * 36 | * \return 37 | * A nullable string which contains 38 | * the result of the command, or which 39 | * is nulled if the command returned 40 | * no result. 41 | */ 42 | virtual Nullable operator () (const String & command) = 0; 43 | 44 | 45 | }; 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/network/windows/send_command.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | SendCommand::SendCommand (Vector buffer) noexcept : CompletionCommand(CommandType::Send), Buffer(std::move(buffer)) { } 11 | 12 | 13 | DWORD SendCommand::Dispatch (SOCKET socket) noexcept { 14 | 15 | // Prepare WSABUF structure 16 | buf.len=static_cast(Buffer.Count()); 17 | buf.buf=reinterpret_cast(Buffer.begin()); 18 | 19 | // Make WSASend call 20 | if (WSASend( 21 | socket, 22 | &buf, 23 | 1, 24 | nullptr, 25 | 0, 26 | reinterpret_cast(this), 27 | nullptr 28 | )==SOCKET_ERROR) { 29 | 30 | // It's only an error if we actually 31 | // failed 32 | auto result=WSAGetLastError(); 33 | if (result!=WSA_IO_PENDING) return result; 34 | 35 | } 36 | 37 | return 0; 38 | 39 | } 40 | 41 | 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/world/interest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | void World::Interested (ColumnID id, bool prepare) { 8 | 9 | // This automatically acquires 10 | // interest 11 | auto column=get_column(id); 12 | 13 | if (prepare) { 14 | 15 | // Column must be prepared -- fully 16 | // populated 17 | 18 | try { 19 | 20 | if (!column->WaitUntil(ColumnState::Populated)) process(*column); 21 | 22 | } catch (...) { 23 | 24 | // Don't leak interest 25 | column->EndInterest(); 26 | 27 | throw; 28 | 29 | } 30 | 31 | } 32 | 33 | } 34 | 35 | 36 | void World::EndInterest (ColumnID id) noexcept { 37 | 38 | lock.Execute([&] () { 39 | 40 | // Releasing interest is only relevant 41 | // if the column-in-question actually 42 | // is loaded 43 | 44 | auto iter=world.find(id); 45 | if (iter!=world.end()) iter->second->EndInterest(); 46 | 47 | }); 48 | 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /include/seed_sequence.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | namespace MCPP { 16 | 17 | 18 | /** 19 | * A seed sequence which provides 20 | * cryptographically secure pseudo-random 21 | * numbers. 22 | */ 23 | class SeedSequence { 24 | 25 | 26 | public: 27 | 28 | 29 | typedef UInt32 result_type; 30 | 31 | 32 | template 33 | void generate (RandomIt begin, RandomIt end) { 34 | 35 | RandomDevice::value_type> gen; 36 | 37 | do *begin=gen(); 38 | while ((++begin)!=end); 39 | 40 | } 41 | 42 | 43 | std::size_t size () const noexcept { 44 | 45 | return 0; 46 | 47 | } 48 | 49 | 50 | template 51 | void param (OutputIt) const noexcept { } 52 | 53 | 54 | }; 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/mysql_data_provider/get_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace MySQL { 8 | 9 | 10 | Vector GetBuffer (MYSQL_BIND & bind, PreparedStatement & stmt, Word column) { 11 | 12 | auto real_len=*bind.length; 13 | 14 | // If there's nothing to fetch, 15 | // just return an empty buffer 16 | if (real_len==0) return Vector(); 17 | 18 | // Determine how long the buffer 19 | // needs to be 20 | auto len=safe_cast(real_len); 21 | 22 | // Create an appropriately-sized 23 | // buffer 24 | Vector retr(len); 25 | 26 | // Wire into the bind 27 | bind.buffer=retr.begin(); 28 | bind.buffer_length=real_len; 29 | 30 | // Fetch 31 | stmt.Fetch(bind,column); 32 | retr.SetCount(len); 33 | 34 | // Reset buffer 35 | bind.buffer=nullptr; 36 | bind.buffer_length=0; 37 | 38 | // Return 39 | return retr; 40 | 41 | } 42 | 43 | 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/world/events.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | namespace MCPP { 7 | 8 | 9 | bool World::can_set (const BlockSetEvent & event) noexcept { 10 | 11 | try { 12 | 13 | return ( 14 | CanSet(event) && 15 | CanReplace[event.From.GetType()](event) && 16 | CanPlace[event.To.GetType()](event) 17 | ); 18 | 19 | } catch (...) { } 20 | 21 | return false; 22 | 23 | } 24 | 25 | 26 | void World::on_set (const BlockSetEvent & event) { 27 | 28 | OnSet(event); 29 | OnReplace[event.From.GetType()](event); 30 | OnPlace[event.To.GetType()](event); 31 | 32 | } 33 | 34 | 35 | template 36 | static inline void cleanup_array (T (& arr) [n]) noexcept { 37 | 38 | for (auto & i : arr) i=T(); 39 | 40 | } 41 | 42 | 43 | void World::cleanup_events () noexcept { 44 | 45 | cleanup_array(OnReplace); 46 | cleanup_array(OnPlace); 47 | cleanup_array(CanReplace); 48 | cleanup_array(CanPlace); 49 | 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/cli/cli_provider.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace MCPP { 6 | 7 | 8 | // Default implementation does nothing 9 | void CLIProvider::operator () (String) { } 10 | 11 | 12 | void CLIProvider::Set (LogType log, ChatLogType chat_log) noexcept { 13 | 14 | lock.Execute([&] () mutable { 15 | 16 | this->log=std::move(log); 17 | this->chat_log=std::move(chat_log); 18 | 19 | }); 20 | 21 | } 22 | 23 | 24 | void CLIProvider::Clear () noexcept { 25 | 26 | lock.Execute([&] () mutable { 27 | 28 | log=LogType(); 29 | chat_log=ChatLogType(); 30 | 31 | }); 32 | 33 | } 34 | 35 | 36 | void CLIProvider::WriteLog (const String & str, Service::LogType type) const { 37 | 38 | lock.Execute([&] () { if (log) log(str,type); }); 39 | 40 | } 41 | 42 | 43 | void CLIProvider::WriteChatLog (const String & from, const Vector & to, const String & msg, const Nullable & notes) const { 44 | 45 | lock.Execute([&] () { if (chat_log) chat_log(from,to,msg,notes); }); 46 | 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/handshake/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | using namespace MCPP; 8 | 9 | 10 | static const String name("Handshake Handler"); 11 | static const Word priority=1; 12 | 13 | 14 | class Handshake : public Module { 15 | 16 | 17 | private: 18 | 19 | 20 | typedef Packets::Handshaking::Serverbound::Handshake handshake; 21 | 22 | 23 | public: 24 | 25 | 26 | virtual const String & Name () const noexcept override { 27 | 28 | return name; 29 | 30 | } 31 | 32 | 33 | virtual Word Priority () const noexcept override { 34 | 35 | return priority; 36 | 37 | } 38 | 39 | 40 | virtual void Install () override { 41 | 42 | Server::Get().Router( 43 | handshake::PacketID, 44 | ProtocolState::Handshaking 45 | )=[] (PacketEvent event) { 46 | 47 | auto & packet=event.Data.Get(); 48 | 49 | event.From->SetState(packet.CurrentState); 50 | 51 | }; 52 | 53 | } 54 | 55 | 56 | }; 57 | 58 | 59 | INSTALL_MODULE(Handshake) 60 | -------------------------------------------------------------------------------- /src/cli/date_time.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace CLIImpl { 8 | 9 | 10 | static const String date_template( 11 | "{0}/{1}/{2} {3}:{4}:{5}.{6}" 12 | ); 13 | 14 | 15 | static inline String format (WORD num, Word len) { 16 | 17 | String str(num); 18 | 19 | while (str.Count() 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | void World::populate (ColumnContainer & column, const WorldHandle * handle) { 8 | 9 | // If there's no handle, create one 10 | Nullable local_handle; 11 | if (handle==nullptr) local_handle.Construct( 12 | Begin( 13 | BlockWriteStrategy::Dirty, 14 | BlockAccessStrategy::Generate 15 | ) 16 | ); 17 | 18 | PopulateEvent event{ 19 | column.ID(), 20 | (handle==nullptr) ? *local_handle : *handle 21 | }; 22 | 23 | // Attempt to retrieve populators 24 | // for this dimension 25 | auto iter=populators.find(event.ID.Dimension); 26 | 27 | if (iter!=populators.end()) { 28 | 29 | event.Handle.BeginPopulate(); 30 | 31 | try { 32 | 33 | for (const auto & populator : iter->second) (*populator.Item<0>())(event); 34 | 35 | } catch (...) { 36 | 37 | // Don't leak population 38 | // count 39 | event.Handle.EndPopulate(); 40 | 41 | throw; 42 | 43 | } 44 | 45 | event.Handle.EndPopulate(); 46 | 47 | } 48 | 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/world/load.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | namespace MCPP { 7 | 8 | 9 | ColumnState World::load (ColumnContainer & column) { 10 | 11 | // Attemt to retrieve data 12 | auto buffer=Server::Get().Data().GetBinary(key(column)); 13 | // If no data was retrieved from 14 | // the backing store, the column 15 | // will have to be generated 16 | if (buffer.IsNull()) return ColumnState::Generating; 17 | 18 | // Decompress 19 | auto decompressed=Inflate( 20 | buffer->begin(), 21 | buffer->end() 22 | ); 23 | 24 | // If the data is an invalid length, 25 | // generate the column 26 | if (decompressed.Count()!=ColumnContainer::Size) return ColumnState::Generating; 27 | 28 | // Copy the decompressed data 29 | std::memcpy( 30 | column.Get(), 31 | decompressed.begin(), 32 | ColumnContainer::Size 33 | ); 34 | 35 | // The column was loaded, but what 36 | // stat was it in? 37 | // 38 | // Return its state based on this 39 | return column.Populated ? ColumnState::Populated : ColumnState::Generated; 40 | 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. 4 | 5 | In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTBILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/entity_id/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | using namespace MCPP; 6 | 7 | 8 | namespace MCPP { 9 | 10 | 11 | static const String name("Entity ID Generator"); 12 | static const Word priority=1; 13 | 14 | 15 | EntityIDGenerator::EntityIDGenerator () noexcept { 16 | 17 | id=0; 18 | 19 | } 20 | 21 | 22 | const String & EntityIDGenerator::Name () const noexcept { 23 | 24 | return name; 25 | 26 | } 27 | 28 | 29 | Word EntityIDGenerator::Priority () const noexcept { 30 | 31 | return priority; 32 | 33 | } 34 | 35 | 36 | void EntityIDGenerator::Install () { } 37 | 38 | 39 | static Singleton singleton; 40 | 41 | 42 | Int32 EntityIDGenerator::Get () noexcept { 43 | 44 | union { 45 | Int32 s; 46 | UInt32 u; 47 | }; 48 | 49 | u=singleton.Get().id++; 50 | 51 | return s; 52 | 53 | } 54 | 55 | 56 | } 57 | 58 | 59 | extern "C" { 60 | 61 | 62 | Module * Load () { 63 | 64 | return &(singleton.Get()); 65 | 66 | } 67 | 68 | 69 | void Unload () { 70 | 71 | singleton.Destroy(); 72 | 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /include/brand/brand.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | namespace MCPP { 17 | 18 | 19 | class Brands : public Module { 20 | 21 | 22 | private: 23 | 24 | 25 | std::unordered_map< 26 | SmartPointer, 27 | Nullable 28 | > brands; 29 | RWLock lock; 30 | 31 | 32 | public: 33 | 34 | 35 | /** 36 | * Retrieves a reference to a valid 37 | * instance of this class. 38 | * 39 | * \return 40 | * A reference to a valid instance 41 | * of this class. 42 | */ 43 | static Brands & Get () noexcept; 44 | 45 | 46 | /** 47 | * \cond 48 | */ 49 | 50 | 51 | virtual const String & Name () const noexcept override; 52 | virtual Word Priority () const noexcept override; 53 | virtual void Install () override; 54 | 55 | 56 | /** 57 | * \endcond 58 | */ 59 | 60 | 61 | Nullable Get (const SmartPointer & client); 62 | 63 | 64 | }; 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /include/entity_id/entity_id.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | namespace MCPP { 15 | 16 | 17 | /** 18 | * Generates unique entity IDs. 19 | */ 20 | class EntityIDGenerator : public Module { 21 | 22 | 23 | private: 24 | 25 | 26 | // Minecraft only supports 32-bit IDs 27 | std::atomic id; 28 | 29 | 30 | public: 31 | 32 | 33 | /** 34 | * Retrieves a signed 32-bit identifier 35 | * which is almost guaranteed to be 36 | * unique across this instance of the 37 | * server. 38 | * 39 | * Thread safe. 40 | * 41 | * \return 42 | * A signed 32-bit identifier. 43 | */ 44 | static Int32 Get () noexcept; 45 | 46 | 47 | /** 48 | * \cond 49 | */ 50 | 51 | 52 | EntityIDGenerator () noexcept; 53 | 54 | 55 | virtual const String & Name () const noexcept override; 56 | virtual Word Priority () const noexcept override; 57 | virtual void Install () override; 58 | 59 | 60 | /** 61 | * \endcond 62 | */ 63 | 64 | 65 | }; 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /include/safeint.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | namespace RLeahyLib { 13 | 14 | 15 | /** 16 | * Casts one type of integer safely to another. 17 | * 18 | * An exception will be thrown if the value 19 | * of \em f is not representable as type \em To. 20 | * 21 | * \tparam To 22 | * The integer type to cast to. 23 | * \tparam From 24 | * The integer type being cast from. Should 25 | * be deduced. 26 | * 27 | * \param [in] f 28 | * The integer to cast. 29 | * 30 | * \return 31 | * \em f cast to type \em To. 32 | */ 33 | template 34 | To safe_cast (From f) { 35 | 36 | return static_cast( 37 | SafeInt(f) 38 | ); 39 | 40 | } 41 | 42 | 43 | /** 44 | * Wraps an integer in a SafeInt wrapper. 45 | * 46 | * \tparam T 47 | * The type of integer to wrap. Should 48 | * be deduced. 49 | * 50 | * \param [in] t 51 | * The integer to wrap. 52 | * 53 | * \return 54 | * \em t wrapped in a SafeInt. 55 | */ 56 | template 57 | SafeInt MakeSafe (T t) noexcept { 58 | 59 | return SafeInt(t); 60 | 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/new_delete.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | void * operator new (std::size_t size) { 6 | 7 | return Memory::RawAllocate(size); 8 | 9 | } 10 | 11 | 12 | void * operator new (std::size_t size, const std::nothrow_t &) noexcept { 13 | 14 | try { 15 | 16 | return Memory::RawAllocate(size); 17 | 18 | } catch (...) { 19 | 20 | return nullptr; 21 | 22 | } 23 | 24 | } 25 | 26 | 27 | void * operator new [] (std::size_t size) { 28 | 29 | return Memory::RawAllocate(size); 30 | 31 | } 32 | 33 | 34 | void * operator new [] (std::size_t size, const std::nothrow_t &) noexcept { 35 | 36 | try { 37 | 38 | return Memory::RawAllocate(size); 39 | 40 | } catch (...) { 41 | 42 | return nullptr; 43 | 44 | } 45 | 46 | } 47 | 48 | 49 | void operator delete (void * ptr) noexcept { 50 | 51 | Memory::Free(ptr); 52 | 53 | } 54 | 55 | 56 | void operator delete (void * ptr, const std::nothrow_t &) noexcept { 57 | 58 | Memory::Free(ptr); 59 | 60 | } 61 | 62 | 63 | void operator delete [] (void * ptr) noexcept { 64 | 65 | Memory::Free(ptr); 66 | 67 | } 68 | 69 | 70 | void operator delete [] (void * ptr, const std::nothrow_t &) noexcept { 71 | 72 | Memory::Free(ptr); 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/world/get_column.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | ColumnContainer * World::get_column (ColumnID id, bool create) { 8 | 9 | return lock.Execute([&] () -> ColumnContainer * { 10 | 11 | ColumnContainer * retr; 12 | 13 | // Attempt to retrieve column if it 14 | // already exists 15 | auto iter=world.find(id); 16 | if (iter==world.end()) { 17 | 18 | // Column does not exist 19 | 20 | // If we've been told not to create 21 | // the column, simply return a 22 | // null pointer 23 | if (!create) return nullptr; 24 | 25 | // Create a new column 26 | std::unique_ptr column(new ColumnContainer(id)); 27 | retr=column.get(); 28 | 29 | // Insert it 30 | world.emplace( 31 | id, 32 | std::move(column) 33 | ); 34 | 35 | } else { 36 | 37 | // Column exists, we can just 38 | // return it 39 | retr=iter->second.get(); 40 | 41 | } 42 | 43 | // We must acquire interest in the 44 | // column before releasing the lock 45 | // to prevent it from being spuriously 46 | // unloaded 47 | retr->Interested(); 48 | 49 | return retr; 50 | 51 | }); 52 | 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/world/has_skylight.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace MCPP { 6 | 7 | 8 | class HasSkylightHelper { 9 | 10 | 11 | private: 12 | 13 | 14 | bool has_skylight [256]; 15 | 16 | 17 | public: 18 | 19 | 20 | inline bool & operator [] (SByte offset) noexcept { 21 | 22 | // Union converts signed dimension 23 | // into unsigned array index 24 | union { 25 | SByte in; 26 | Byte out; 27 | }; 28 | 29 | in=offset; 30 | 31 | return has_skylight[out]; 32 | 33 | } 34 | 35 | 36 | HasSkylightHelper () noexcept { 37 | 38 | memset( 39 | has_skylight, 40 | 0, 41 | sizeof(has_skylight) 42 | ); 43 | 44 | // Overworld has skylight, all 45 | // other vanilla dimensions do 46 | // not 47 | (*this)[0]=true; 48 | 49 | } 50 | 51 | 52 | }; 53 | 54 | 55 | static HasSkylightHelper has_skylight; 56 | 57 | 58 | bool HasSkylight (SByte dimension) noexcept { 59 | 60 | return has_skylight[dimension]; 61 | 62 | } 63 | 64 | 65 | void SetHasSkylight (SByte dimension, bool has_skylight) noexcept { 66 | 67 | MCPP::has_skylight[dimension]=has_skylight; 68 | 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/recursive_mutex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | void RecursiveMutex::Acquire () noexcept { 8 | 9 | // Get this thread's ID 10 | auto id=Thread::ID(); 11 | 12 | lock.Execute([&] () mutable { 13 | 14 | // If the ID is not null, we may 15 | // not be able to acquire at once 16 | if (!this->id.IsNull()) { 17 | 18 | // If this thread is actually currently 19 | // holding the lock, just increment the 20 | // depth and return 21 | if (*(this->id)==id) { 22 | 23 | ++depth; 24 | 25 | return; 26 | 27 | } 28 | 29 | // Another thread is holding the lock, 30 | // wait for it not to be 31 | do wait.Sleep(lock); 32 | while (!this->id.IsNull()); 33 | 34 | } 35 | 36 | // Acquire the lock 37 | this->id.Construct(id); 38 | depth=1; 39 | 40 | }); 41 | 42 | } 43 | 44 | 45 | void RecursiveMutex::Release () noexcept { 46 | 47 | lock.Execute([&] () mutable { 48 | 49 | // Decrement the depth counter, if 50 | // this is the bottom, release the 51 | // lock 52 | if ((--depth)==0) { 53 | 54 | id.Destroy(); 55 | 56 | wait.Wake(); 57 | 58 | } 59 | 60 | }); 61 | 62 | } 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/network/windows/receive_command.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | // Minimum size of a receive buffer 11 | static const Word buffer_size=1024; 12 | 13 | 14 | ReceiveCommand::ReceiveCommand () noexcept : CompletionCommand(CommandType::Receive), flags(0) { } 15 | 16 | 17 | DWORD ReceiveCommand::Dispatch (SOCKET socket) { 18 | 19 | // Does the buffer need to be resized? 20 | // If so, resize it 21 | if (Buffer.Count()==0) Buffer.SetCapacity(buffer_size); 22 | else if (Buffer.Count()==Buffer.Capacity()) Buffer.SetCapacity(); 23 | 24 | // Keep the WSABUF up to date 25 | buf.len=static_cast(SafeWord(Buffer.Capacity()-Buffer.Count())); 26 | buf.buf=reinterpret_cast(Buffer.end()); 27 | 28 | // Dispatch receive 29 | if (WSARecv( 30 | socket, 31 | &buf, 32 | 1, 33 | nullptr, 34 | &flags, 35 | reinterpret_cast(this), 36 | nullptr 37 | )==SOCKET_ERROR) { 38 | 39 | // It's only an error if we 40 | // actually failed 41 | auto result=WSAGetLastError(); 42 | if (result!=WSA_IO_PENDING) return result; 43 | 44 | } 45 | 46 | return 0; 47 | 48 | } 49 | 50 | 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/serializer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | namespace MCPP { 7 | 8 | 9 | const char * SerializerError::InsufficientBytes="Insufficient bytes"; 10 | const char * SerializerError::InvalidBoolean="Invalid boolean value"; 11 | 12 | 13 | ByteBufferError::ByteBufferError (const char * what_str, Word where) noexcept : what_str(what_str), where(where) { } 14 | 15 | 16 | const char * ByteBufferError::what () const noexcept { 17 | 18 | return what_str; 19 | 20 | } 21 | 22 | 23 | Word ByteBufferError::Where () const noexcept { 24 | 25 | return where; 26 | 27 | } 28 | 29 | 30 | ByteBuffer ByteBuffer::Load (const String & key) { 31 | 32 | ByteBuffer retr; 33 | 34 | auto buffer=Server::Get().Data().GetBinary(key); 35 | 36 | if (!buffer.IsNull()) retr.buffer=std::move(*buffer); 37 | 38 | return retr; 39 | 40 | } 41 | 42 | 43 | ByteBuffer::ByteBuffer () noexcept : loc(nullptr) { } 44 | 45 | 46 | void ByteBuffer::Save (const String & key) { 47 | 48 | Server::Get().Data().SaveBinary( 49 | key, 50 | buffer.begin(), 51 | buffer.Count() 52 | ); 53 | 54 | } 55 | 56 | 57 | Word ByteBuffer::Count () const noexcept { 58 | 59 | return buffer.Count(); 60 | 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/cli/args.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace MCPP { 6 | 7 | 8 | static const Regex regex("^\\s*(?:\\-|\\/)(.*)$"); 9 | 10 | 11 | static Nullable get (const String * & begin, const String * end) { 12 | 13 | Nullable retr; 14 | 15 | // If there's nothing left to parse, 16 | // return nothing 17 | if (begin==end) return retr; 18 | 19 | retr.Construct(); 20 | 21 | // Record raw flag 22 | retr->RawFlag=*begin; 23 | 24 | // Try and parse flag 25 | auto match=regex.Match(*begin); 26 | ++begin; 27 | if (!match.Success()) return retr; 28 | 29 | // Store parsed flag 30 | retr->Flag.Construct(match[1].Value()); 31 | 32 | // Get all arguments which follow 33 | for ( 34 | ; 35 | (begin!=end) && 36 | !regex.IsMatch(*begin); 37 | ++begin 38 | ) retr->Arguments.Add(*begin); 39 | 40 | return retr; 41 | 42 | } 43 | 44 | 45 | void ParseCommandLineArguments (const Vector & args, std::function callback) { 46 | 47 | // If there are no arguments to 48 | // parse, just return 49 | if (args.Count()<=1) return; 50 | 51 | Nullable arg; 52 | auto begin=args.begin()+1; 53 | while (!(arg=get(begin,args.end())).IsNull()) callback(std::move(*arg)); 54 | 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/basic_chat/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace MCPP; 10 | 11 | 12 | static const String name("Basic Chat Support"); 13 | static const Word priority=1; 14 | 15 | 16 | class BasicChat : public ChatProvider, public Module { 17 | 18 | 19 | public: 20 | 21 | 22 | virtual const String & Name () const noexcept override { 23 | 24 | return name; 25 | 26 | } 27 | 28 | 29 | virtual Word Priority () const noexcept override { 30 | 31 | return priority; 32 | 33 | } 34 | 35 | 36 | virtual void Install () override { 37 | 38 | // Install our chat handler 39 | Chat::Get().Chat=[] (ChatEvent event) { 40 | 41 | // Send 42 | Chat::Get().Send( 43 | ChatMessage( 44 | std::move(event.From), 45 | event.Body 46 | ) 47 | ); 48 | 49 | }; 50 | 51 | // Install ourselves as the chat 52 | // handler into the server 53 | Server::Get().SetChatProvider(this); 54 | 55 | } 56 | 57 | 58 | virtual void Send (const String & message) override { 59 | 60 | Chat::Get().Send( 61 | ChatMessage( 62 | SmartPointer(), 63 | message 64 | ) 65 | ); 66 | 67 | } 68 | 69 | 70 | }; 71 | 72 | 73 | INSTALL_MODULE(BasicChat) 74 | -------------------------------------------------------------------------------- /include/traits.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | namespace MCPP { 13 | 14 | 15 | /** 16 | * \cond 17 | */ 18 | 19 | 20 | namespace GetTypeImpl { 21 | 22 | 23 | template 24 | class GetType { 25 | 26 | 27 | public: 28 | 29 | 30 | typedef typename GetType::Type Type; 31 | 32 | 33 | }; 34 | 35 | 36 | template 37 | class GetType { 38 | 39 | 40 | public: 41 | 42 | 43 | typedef T Type; 44 | 45 | 46 | }; 47 | 48 | 49 | } 50 | 51 | 52 | /** 53 | * \endcond 54 | */ 55 | 56 | 57 | /** 58 | * Contains a typedef which is the 59 | * \em ith type of the variadic template 60 | * argument \em Args. 61 | * 62 | * \tparam i 63 | * The zero-relative index of the 64 | * type to retrieve. 65 | * \tparam Args 66 | * The types to choose from. 67 | */ 68 | template 69 | class GetType { 70 | 71 | 72 | public: 73 | 74 | 75 | /** 76 | * A typedef which contains the \em ith 77 | * type of the variadic template argument 78 | * \em Args. 79 | */ 80 | typedef typename GetTypeImpl::GetType::Type Type; 81 | 82 | 83 | }; 84 | 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/world/block_id.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | bool BlockID::operator == (const BlockID & other) const noexcept { 8 | 9 | return ( 10 | (X==other.X) && 11 | (Y==other.Y) && 12 | (Z==other.Z) && 13 | (Dimension==other.Dimension) 14 | ); 15 | 16 | } 17 | 18 | 19 | bool BlockID::operator != (const BlockID & other) const noexcept { 20 | 21 | return !(*this==other); 22 | 23 | } 24 | 25 | 26 | ColumnID BlockID::GetContaining () const noexcept { 27 | 28 | ColumnID retr{ 29 | X/16, 30 | Z/16, 31 | Dimension 32 | }; 33 | 34 | if ( 35 | (X<0) && 36 | ((X%16)!=0) 37 | ) --retr.X; 38 | if ( 39 | (Z<0) && 40 | ((Z%16)!=0) 41 | ) --retr.Z; 42 | 43 | return retr; 44 | 45 | } 46 | 47 | 48 | Word BlockID::GetOffset () const noexcept { 49 | 50 | Int32 x=X%16; 51 | if ((X<0) && (x!=0)) x=16-(x*-1); 52 | Int32 z=Z%16; 53 | if ((Z<0) && (z!=0)) z=16-(z*-1); 54 | 55 | return static_cast(x)+(static_cast(z)*16)+(static_cast(Y)*16*16); 56 | 57 | } 58 | 59 | 60 | bool BlockID::IsContainedBy (const ColumnID & column) const noexcept { 61 | 62 | if (column.Dimension!=Dimension) return false; 63 | 64 | Int32 x=X/16; 65 | if (X<0) --x; 66 | 67 | if (column.X!=x) return false; 68 | 69 | Int32 z=Z/16; 70 | if (Z<0) --z; 71 | 72 | return column.Z==z; 73 | 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/data_provider.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | DataProvider::DataProvider () noexcept { } 8 | 9 | 10 | DataProvider::~DataProvider () noexcept { } 11 | 12 | 13 | static const String success("Success"); 14 | static const String error("Error"); 15 | static const String information("Information"); 16 | static const String warning("Warning"); 17 | static const String debug("Debug"); 18 | static const String security_success("Security Success"); 19 | static const String security_failure("Security Failure"); 20 | static const String critical("Critical"); 21 | static const String alert("Alert"); 22 | static const String emergency("Emergency"); 23 | 24 | 25 | const String & DataProvider::GetLogType (Service::LogType type) noexcept { 26 | 27 | switch (type) { 28 | 29 | case Service::LogType::Success:return success; 30 | case Service::LogType::Error:return error; 31 | case Service::LogType::Information: 32 | default:return information; 33 | case Service::LogType::Warning:return warning; 34 | case Service::LogType::Debug:return debug; 35 | case Service::LogType::SecuritySuccess:return security_success; 36 | case Service::LogType::SecurityFailure:return security_failure; 37 | case Service::LogType::Critical:return critical; 38 | case Service::LogType::Alert:return alert; 39 | case Service::LogType::Emergency:return emergency; 40 | 41 | } 42 | 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Minecraft++ 2 | =========== 3 | 4 | Modular, multi-threaded, event-driven Minecraft server written in C++. 5 | 6 | Very early development. 7 | 8 | Screenshots 9 | ----------- 10 | 11 | Terrain generation: 12 | 13 | ![Mountain](http://i.imgur.com/YeUTAql.jpg) 14 | 15 | ![Mountaintop](http://i.imgur.com/hs3sG2r.jpg) 16 | 17 | ![Distant mountains](http://i.imgur.com/qQxgz3z.jpg) 18 | 19 | Interactive (i.e. non-service/-daemon) front-end: 20 | 21 | ![Interactive front-end](http://i.imgur.com/9WHZwFB.png) 22 | 23 | Dependencies 24 | ------------ 25 | 26 | - [c-ares](http://c-ares.haxx.se/) 27 | - [cURL](http://curl.haxx.se/) 28 | - [MySQL](http://dev.mysql.com/doc/refman/5.6/en/c-api.html) 29 | - [OpenSSL](http://www.openssl.org/) 30 | - [RLeahyLib](https://github.com/RobertLeahy/RLeahyLib) 31 | - [zlib](http://zlib.net/) 32 | 33 | Operating System Support 34 | ------------------------ 35 | 36 | As all the above libraries are available for Windows and Linux (zlib has to be custom-built from source for Windows x64), the server will support Linux and Windows equally. 37 | 38 | Compiler Support 39 | ---------------- 40 | 41 | GCC 4.8.1 is used to build the server. 42 | 43 | RLeahyLib is hardcoded not to build on anything but GCC 4.8.0 or higher, but this is so the type system can depend on certain GCC-specific macros, and can likely be quickly adapted to suit a different compiler. 44 | 45 | Neither Minecraft++ or RLeahyLib will build on anything that doesn't support C++11. 46 | 47 | They definitely do not build on VC++. -------------------------------------------------------------------------------- /src/world/generators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace RLeahyLib { 6 | 7 | 8 | bool operator == (const Tuple & a, const Tuple & b) { 9 | 10 | return ( 11 | (a.Item<0>()==b.Item<0>()) && 12 | (a.Item<1>()==b.Item<1>()) 13 | ); 14 | 15 | } 16 | 17 | 18 | } 19 | 20 | 21 | namespace MCPP { 22 | 23 | 24 | static const ASCIIChar * gen_not_found="No such world generator"; 25 | 26 | 27 | void World::Add (const WorldGenerator * generator, SByte dimension) { 28 | 29 | if (generator==nullptr) return; 30 | 31 | default_generators[dimension]=std::move(generator); 32 | 33 | } 34 | 35 | 36 | void World::Add (const WorldGenerator * generator, String type, SByte dimension) { 37 | 38 | if (generator==nullptr) return; 39 | 40 | generators[ 41 | Tuple( 42 | std::move(type), 43 | dimension 44 | ) 45 | ]=std::move(generator); 46 | 47 | } 48 | 49 | 50 | const WorldGenerator & World::get_generator (SByte dimension) const { 51 | 52 | auto iter=generators.find( 53 | Tuple( 54 | type, 55 | dimension 56 | ) 57 | ); 58 | 59 | if (iter==generators.end()) { 60 | 61 | auto default_iter=default_generators.find(dimension); 62 | 63 | if (default_iter==default_generators.end()) throw std::runtime_error(gen_not_found); 64 | 65 | return *(default_iter->second); 66 | 67 | } 68 | 69 | return *(iter->second); 70 | 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/world/column_id.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | static inline Int32 adjust_coord (Double coord) { 8 | 9 | if (coord<0) { 10 | 11 | Double rem=coord-static_cast( 12 | static_cast( 13 | coord 14 | ) 15 | ); 16 | 17 | if (rem!=0) coord-=1; 18 | 19 | } 20 | 21 | return static_cast(coord); 22 | 23 | } 24 | 25 | 26 | ColumnID ColumnID::GetContaining (Double x, Double z, SByte dimension) noexcept { 27 | 28 | return BlockID{ 29 | adjust_coord(x), 30 | 0, 31 | adjust_coord(z), 32 | dimension 33 | }.GetContaining(); 34 | 35 | } 36 | 37 | 38 | bool ColumnID::operator == (const ColumnID & other) const noexcept { 39 | 40 | return ( 41 | (X==other.X) && 42 | (Z==other.Z) && 43 | (Dimension==other.Dimension) 44 | ); 45 | 46 | } 47 | 48 | 49 | bool ColumnID::operator != (const ColumnID & other) const noexcept { 50 | 51 | return !(*this==other); 52 | 53 | } 54 | 55 | 56 | bool ColumnID::DoesContain (const BlockID & block) const noexcept { 57 | 58 | return block.IsContainedBy(*this); 59 | 60 | } 61 | 62 | 63 | Int32 ColumnID::GetStartX () const noexcept { 64 | 65 | return X*16; 66 | 67 | } 68 | 69 | 70 | Int32 ColumnID::GetStartZ () const noexcept { 71 | 72 | return Z*16; 73 | 74 | } 75 | 76 | 77 | Int32 ColumnID::GetEndX () const noexcept { 78 | 79 | return GetStartX()+15; 80 | 81 | } 82 | 83 | 84 | Int32 ColumnID::GetEndZ () const noexcept { 85 | 86 | return GetStartZ()+15; 87 | 88 | } 89 | 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/blacklist/info.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | using namespace MCPP; 8 | 9 | 10 | static const Word priority=1; 11 | static const String name("Blacklist Information Provider"); 12 | static const String help("Displays the blacklist."); 13 | static const String identifier("blacklist"); 14 | static const String blacklist_banner("BLACKLIST:"); 15 | 16 | 17 | class BlacklistInfoProvider : public Module, public InformationProvider { 18 | 19 | 20 | public: 21 | 22 | 23 | virtual Word Priority () const noexcept override { 24 | 25 | return priority; 26 | 27 | } 28 | 29 | 30 | virtual const String & Name () const noexcept override { 31 | 32 | return name; 33 | 34 | } 35 | 36 | 37 | virtual void Install () override { 38 | 39 | Information::Get().Add(this); 40 | 41 | } 42 | 43 | 44 | virtual const String & Identifier () const noexcept override { 45 | 46 | return identifier; 47 | 48 | } 49 | 50 | 51 | virtual const String & Help () const noexcept override { 52 | 53 | return help; 54 | 55 | } 56 | 57 | 58 | virtual void Execute (ChatMessage & message) const override { 59 | 60 | auto info=Blacklist::Get().GetInfo(); 61 | 62 | message << ChatStyle::Bold 63 | << blacklist_banner 64 | << ChatFormat::Pop; 65 | 66 | std::sort( 67 | info.Ranges.begin(), 68 | info.Ranges.end() 69 | ); 70 | 71 | for (auto & range : info.Ranges) message << Newline << String(range); 72 | 73 | } 74 | 75 | 76 | }; 77 | 78 | 79 | INSTALL_MODULE(BlacklistInfoProvider) 80 | -------------------------------------------------------------------------------- /src/network/windows/connect_command.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | ConnectCommand::ConnectCommand () noexcept : CompletionCommand(CommandType::Connect) { } 11 | 12 | 13 | void ConnectCommand::Dispatch (SOCKET socket, IPAddress ip, UInt16 port) { 14 | 15 | // Initialize the remote endpoint 16 | ip.ToOS(&addr,port); 17 | 18 | // Create a bogus, local IP to 19 | // bind to 20 | struct sockaddr_storage local_addr; 21 | IPAddress::Any(ip.IsV6()).ToOS(&local_addr,0); 22 | 23 | // Bind to local address 24 | if (bind( 25 | socket, 26 | reinterpret_cast(&local_addr), 27 | sizeof(local_addr) 28 | )==SOCKET_ERROR) RaiseWSA(); 29 | 30 | // Get the address of ConnectEx 31 | GUID guid=WSAID_CONNECTEX; 32 | LPFN_CONNECTEX connect_ex; 33 | DWORD bytes; // Ignored 34 | if (WSAIoctl( 35 | socket, 36 | SIO_GET_EXTENSION_FUNCTION_POINTER, 37 | &guid, 38 | sizeof(guid), 39 | &connect_ex, 40 | sizeof(connect_ex), 41 | &bytes, 42 | nullptr, 43 | nullptr 44 | )==SOCKET_ERROR) RaiseWSA(); 45 | 46 | // Dispatch an asynchronous connect 47 | if (!connect_ex( 48 | socket, 49 | reinterpret_cast(&addr), 50 | sizeof(addr), 51 | nullptr, 52 | 0, 53 | nullptr, 54 | reinterpret_cast(this) 55 | )) { 56 | 57 | // Make sure I/O isn't pending -- that's 58 | // fine 59 | auto result=WSAGetLastError(); 60 | if (result!=ERROR_IO_PENDING) Raise(result); 61 | 62 | } 63 | 64 | } 65 | 66 | 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/world/set_seed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | namespace MCPP { 7 | 8 | 9 | static const String log_new_seed("Generated new seed - {0}"); 10 | static const String log_str_seed("Set seed to \"{0}\" => {1}"); 11 | static const String log_int_seed("Set seed to {0}"); 12 | 13 | 14 | void World::set_seed (Nullable material) { 15 | 16 | String log; 17 | 18 | // If there is no material, use the 19 | // cryptographically secure PRNG to 20 | // get the seed 21 | if (material.IsNull()) { 22 | 23 | seed=RandomDevice{}(); 24 | 25 | log=String::Format( 26 | log_new_seed, 27 | seed 28 | ); 29 | 30 | // Otherwise figure out how to translate 31 | // the string 32 | // 33 | // Attempt to parse an integer from the 34 | // string 35 | } else if (material->ToInteger(&seed)) { 36 | 37 | // Generate log string 38 | 39 | log=String::Format( 40 | log_int_seed, 41 | seed 42 | ); 43 | 44 | } else { 45 | 46 | // That didn't work... 47 | 48 | // Apply djb2 49 | 50 | // Normalize first 51 | material->Normalize(NormalizationForm::NFC); 52 | 53 | seed=5381; 54 | 55 | for (auto cp : material->CodePoints()) { 56 | 57 | seed*=33; 58 | seed^=cp; 59 | 60 | } 61 | 62 | log=String::Format( 63 | log_str_seed, 64 | *material, 65 | seed 66 | ); 67 | 68 | } 69 | 70 | // Write to log 71 | Server::Get().WriteLog( 72 | log, 73 | Service::LogType::Information 74 | ); 75 | 76 | } 77 | 78 | 79 | UInt64 World::Seed () const noexcept { 80 | 81 | return seed; 82 | 83 | } 84 | 85 | 86 | } 87 | -------------------------------------------------------------------------------- /include/sha1.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | 9 | 10 | namespace MCPP { 11 | 12 | 13 | /** 14 | * Implements the SHA-1 hashing 15 | * algorithm. 16 | */ 17 | class SHA1 { 18 | 19 | 20 | private: 21 | 22 | 23 | EVP_MD_CTX sha; 24 | 25 | 26 | public: 27 | 28 | 29 | /** 30 | * Creates a new digest by copying 31 | * another digest. 32 | * 33 | * \param [in] other 34 | * The digest to copy. 35 | */ 36 | SHA1 (const SHA1 & other); 37 | /** 38 | * Copies another digest into this 39 | * digest. 40 | * 41 | * \param [in] other 42 | * The digest to copy. 43 | * 44 | * \return 45 | * A reference to this object. 46 | */ 47 | SHA1 & operator = (const SHA1 & other); 48 | 49 | 50 | /** 51 | * Initializes a new SHA-1 hash. 52 | */ 53 | SHA1 (); 54 | /** 55 | * Cleans up the digest. 56 | */ 57 | ~SHA1 () noexcept; 58 | 59 | 60 | /** 61 | * Hashes a given amount of data, 62 | * adding it to the digest. 63 | * 64 | * \param [in] data 65 | * The data to hash. 66 | */ 67 | void Update (const Vector & data); 68 | /** 69 | * Returns the message digest and 70 | * reinitializes the object so it 71 | * may be used to compute another 72 | * digest. 73 | * 74 | * \return 75 | * The completed digest. 76 | */ 77 | Vector Complete (); 78 | /** 79 | * Returns the message digest 80 | * formatted as a hex digest and 81 | * reinitializes the object so it 82 | * may be used to compute another 83 | * digest. 84 | * 85 | * \return 86 | * The hex digest string. 87 | */ 88 | String HexDigest (); 89 | 90 | 91 | }; 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/network/windows/iocp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | CompletionPort::CompletionPort () { 11 | 12 | // Create the completion port 13 | if ((iocp=CreateIoCompletionPort( 14 | INVALID_HANDLE_VALUE, 15 | nullptr, 16 | 0, 17 | 0 18 | ))==nullptr) Raise(); 19 | 20 | } 21 | 22 | 23 | CompletionPort::~CompletionPort () noexcept { 24 | 25 | CloseHandle(iocp); 26 | 27 | } 28 | 29 | 30 | void CompletionPort::Attach (SOCKET socket, void * ptr) { 31 | 32 | // Attempt to attach to the completion port 33 | if (CreateIoCompletionPort( 34 | reinterpret_cast(socket), 35 | iocp, 36 | reinterpret_cast(ptr), 37 | 0 38 | )==nullptr) Raise(); 39 | 40 | } 41 | 42 | 43 | Packet CompletionPort::Get () { 44 | 45 | // Attempt to dequeue 46 | Packet retr; 47 | retr.Result=GetQueuedCompletionStatus( 48 | iocp, 49 | &retr.Count, 50 | reinterpret_cast(&retr.Data), 51 | reinterpret_cast(&retr.Command), 52 | INFINITE 53 | ); 54 | 55 | if (retr.Result) { 56 | 57 | // Both the dequeue and the I/O operation 58 | // associated with the dequeued packet 59 | // succeeded 60 | retr.Error=0; 61 | 62 | } else if (retr.Command==nullptr) { 63 | 64 | // The dequeue itself failed 65 | Raise(); 66 | 67 | } else { 68 | 69 | // The dequeue succeeded, but the I/O 70 | // operation associated with this packet 71 | // failed 72 | retr.Error=GetLastError(); 73 | retr.Count=0; 74 | 75 | } 76 | 77 | return retr; 78 | 79 | } 80 | 81 | 82 | void CompletionPort::Post () { 83 | 84 | if (!PostQueuedCompletionStatus( 85 | iocp, 86 | 0, 87 | 0, 88 | nullptr 89 | )) Raise(); 90 | 91 | } 92 | 93 | 94 | } 95 | 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/info/mcpp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | using namespace MCPP; 8 | 9 | 10 | static const String name("MCPP Information"); 11 | static const Word priority=1; 12 | static const String identifier("mcpp"); 13 | static const String help("Display information about mcpp.dll."); 14 | static const String mcpp_banner("MINECRAFT++:"); 15 | static const String compiled_by_template("Compiled by {0} on {1}"); 16 | static const String minecraft_compat("Compatible with Minecraft {0} (protocol version {1})"); 17 | 18 | 19 | class MCPPInfo : public Module, public InformationProvider { 20 | 21 | 22 | public: 23 | 24 | 25 | virtual const String & Name () const noexcept override { 26 | 27 | return name; 28 | 29 | } 30 | 31 | 32 | virtual Word Priority () const noexcept override { 33 | 34 | return priority; 35 | 36 | } 37 | 38 | 39 | virtual void Install () override { 40 | 41 | Information::Get().Add(this); 42 | 43 | } 44 | 45 | 46 | virtual const String & Identifier () const noexcept override { 47 | 48 | return identifier; 49 | 50 | } 51 | 52 | 53 | virtual const String & Help () const noexcept override { 54 | 55 | return help; 56 | 57 | } 58 | 59 | 60 | virtual void Execute (ChatMessage & message) const override { 61 | 62 | message << ChatStyle::Bold 63 | << mcpp_banner 64 | << ChatFormat::Pop 65 | << Newline 66 | << String::Format( 67 | compiled_by_template, 68 | Server::CompiledWith, 69 | Server::BuildDate 70 | ) 71 | << Newline 72 | << String::Format( 73 | minecraft_compat, 74 | FormatVersion( 75 | MinecraftMajorVersion, 76 | MinecraftMinorVersion, 77 | MinecraftPatch 78 | ), 79 | ProtocolVersion 80 | ); 81 | 82 | } 83 | 84 | 85 | }; 86 | 87 | 88 | INSTALL_MODULE(MCPPInfo) 89 | -------------------------------------------------------------------------------- /src/network/posix/misc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | namespace MCPP { 11 | 12 | 13 | namespace NetworkImpl { 14 | 15 | 16 | void SetBlocking (FDType fd, bool blocking) { 17 | 18 | // Retrieve the flags for this file 19 | // descriptor 20 | auto flags=fcntl(fd,F_GETFL); 21 | 22 | // Set the non-blocking flag appropriate 23 | if (blocking) flags&=~static_cast(O_NONBLOCK); 24 | else flags|=O_NONBLOCK; 25 | 26 | // Set the flags back to the file 27 | // descriptor 28 | if (fcntl(fd,F_SETFL,flags)==-1) Raise(); 29 | 30 | } 31 | 32 | 33 | FDType GetSocket (bool is_v6) { 34 | 35 | // Make a socket for the appropriate 36 | // address family 37 | auto socket=::socket( 38 | is_v6 ? AF_INET6 : AF_INET, 39 | SOCK_STREAM, 40 | IPPROTO_TCP 41 | ); 42 | 43 | // Make sure it's non-blocking 44 | SetBlocking(socket,false); 45 | 46 | return socket; 47 | 48 | } 49 | 50 | 51 | static UInt16 get_port (const struct sockaddr_storage * addr) noexcept { 52 | 53 | union { 54 | UInt16 retr; 55 | Byte buffer [sizeof(UInt16)]; 56 | }; 57 | retr=static_cast( 58 | (addr->ss_family==AF_INET6) 59 | ? reinterpret_cast(addr)->sin6_port 60 | : reinterpret_cast(addr)->sin_port 61 | ); 62 | 63 | if (!Endianness::IsBigEndian()) std::reverse( 64 | std::begin(buffer), 65 | std::end(buffer) 66 | ); 67 | 68 | return retr; 69 | 70 | } 71 | 72 | 73 | Endpoint GetEndpoint (const struct sockaddr_storage * addr) noexcept { 74 | 75 | Endpoint ep; 76 | ep.IP=const_cast(addr); 77 | ep.Port=get_port(addr); 78 | 79 | return ep; 80 | 81 | } 82 | 83 | 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/brand/info.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | using namespace MCPP; 9 | 10 | 11 | static const String name("Brand Information"); 12 | static const Word priority=1; 13 | static const String identifier("brands"); 14 | static const String help("Displays the brand of all connected clients."); 15 | static const String brand_banner("CLIENT BRANDS:"); 16 | static const String client_template("{0} ({1}:{2}): "); 17 | 18 | 19 | class BrandInfo : public Module, public InformationProvider { 20 | 21 | 22 | public: 23 | 24 | 25 | virtual Word Priority () const noexcept override { 26 | 27 | return priority; 28 | 29 | } 30 | 31 | 32 | virtual const String & Name () const noexcept override { 33 | 34 | return name; 35 | 36 | } 37 | 38 | 39 | virtual const String & Identifier () const noexcept override { 40 | 41 | return identifier; 42 | 43 | } 44 | 45 | 46 | virtual const String & Help () const noexcept override { 47 | 48 | return help; 49 | 50 | } 51 | 52 | 53 | virtual void Install () override { 54 | 55 | Information::Get().Add(this); 56 | 57 | } 58 | 59 | 60 | virtual void Execute (ChatMessage & message) const override { 61 | 62 | message << ChatStyle::Bold 63 | << brand_banner 64 | << ChatFormat::Pop; 65 | 66 | for (auto & client : Server::Get().Clients) { 67 | 68 | // Ignore unauthenticated users 69 | if (client->GetState()==ProtocolState::Play) { 70 | 71 | auto brand=Brands::Get().Get(client); 72 | 73 | message << Newline 74 | << ChatStyle::Bold 75 | << String::Format( 76 | client_template, 77 | client->GetUsername(), 78 | client->IP(), 79 | client->Port() 80 | ) 81 | << ChatFormat::Pop 82 | << (brand.IsNull() ? String() : *brand); 83 | 84 | } 85 | 86 | } 87 | 88 | } 89 | 90 | 91 | }; 92 | 93 | 94 | INSTALL_MODULE(BrandInfo) 95 | -------------------------------------------------------------------------------- /src/whitelist/info.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace MCPP; 10 | 11 | 12 | static const String name("Whitelist Information"); 13 | static const Word priority=1; 14 | static const String identifier("whitelist"); 15 | static const String help("Displays information about the whitelist."); 16 | static const String whitelist_banner("WHITELIST:"); 17 | static const String enabled("Enabled: "); 18 | static const String whitelist("Whitelist:"); 19 | static const String true_string("Yes"); 20 | static const String false_string("No"); 21 | 22 | 23 | class WhitelistInfo : public Module, public InformationProvider { 24 | 25 | 26 | public: 27 | 28 | 29 | virtual const String & Name () const noexcept override { 30 | 31 | return name; 32 | 33 | } 34 | 35 | 36 | virtual Word Priority () const noexcept override { 37 | 38 | return priority; 39 | 40 | } 41 | 42 | 43 | virtual void Install () override { 44 | 45 | Information::Get().Add(this); 46 | 47 | } 48 | 49 | 50 | virtual const String & Identifier () const noexcept override { 51 | 52 | return identifier; 53 | 54 | } 55 | 56 | 57 | virtual const String & Help () const noexcept override { 58 | 59 | return help; 60 | 61 | } 62 | 63 | 64 | virtual void Execute (ChatMessage & message) const override { 65 | 66 | auto & whitelist=Whitelist::Get(); 67 | 68 | message << ChatStyle::Bold 69 | << whitelist_banner 70 | << Newline 71 | << enabled 72 | << ChatFormat::Pop 73 | << (whitelist.Enabled() ? true_string : false_string) 74 | << Newline 75 | << ChatStyle::Bold 76 | << ::whitelist 77 | << ChatFormat::Pop; 78 | 79 | auto info=whitelist.GetInfo(); 80 | 81 | std::sort(info.begin(),info.end()); 82 | 83 | for (auto & str : info) message << Newline << std::move(str); 84 | 85 | } 86 | 87 | 88 | }; 89 | 90 | 91 | INSTALL_MODULE(WhitelistInfo) 92 | -------------------------------------------------------------------------------- /src/chat_login/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | using namespace MCPP; 8 | 9 | 10 | static const String name("Chat Login/Logout Broadcast"); 11 | static const Word priority=1; 12 | static const String online(" has come online"); 13 | static const String offline(" has gone offline"); 14 | static const String start_reason(" ("); 15 | static const String end_reason(")"); 16 | 17 | 18 | class LoginLogout : public Module { 19 | 20 | 21 | public: 22 | 23 | 24 | virtual const String & Name () const noexcept override { 25 | 26 | return name; 27 | 28 | } 29 | 30 | 31 | virtual Word Priority () const noexcept override { 32 | 33 | return priority; 34 | 35 | } 36 | 37 | 38 | virtual void Install () override { 39 | 40 | auto & server=Server::Get(); 41 | 42 | // Subscribe to the on login event 43 | server.OnLogin.Add([] (SmartPointer client) { 44 | 45 | ChatMessage message; 46 | message << ChatStyle::BrightGreen 47 | << ChatStyle::Bold 48 | << client->GetUsername() 49 | << ChatFormat::Pop 50 | << online; 51 | 52 | Chat::Get().Send(message); 53 | 54 | }); 55 | 56 | // Subscribe to the on disconnect event 57 | server.OnDisconnect.Add([] (SmartPointer client, const String & reason) { 58 | 59 | // Only proceed if the user was authenticated 60 | if (client->GetState()!=ProtocolState::Play) return; 61 | 62 | ChatMessage message; 63 | message << ChatStyle::Red 64 | << ChatStyle::Bold 65 | << client->GetUsername() 66 | << ChatFormat::Pop 67 | << offline; 68 | 69 | // If there was a reason associated with the disconnect, 70 | // append that 71 | if (reason.Size()!=0) message << start_reason 72 | << ChatStyle::Bold 73 | << reason 74 | << ChatFormat::Pop 75 | << end_reason; 76 | 77 | Chat::Get().Send(message); 78 | 79 | }); 80 | 81 | } 82 | 83 | 84 | }; 85 | 86 | 87 | INSTALL_MODULE(LoginLogout) 88 | -------------------------------------------------------------------------------- /src/base_64.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace Base64 { 6 | 7 | 8 | // Encoding table 9 | static const ASCIIChar encode []={ 10 | 'A','B','C','D','E','F','G','H', 11 | 'I','J','K','L','M','N','O','P', 12 | 'Q','R','S','T','U','V','W','X', 13 | 'Y','Z','a','b','c','d','e','f', 14 | 'g','h','i','j','k','l','m','n', 15 | 'o','p','q','r','s','t','u','v', 16 | 'w','x','y','z','0','1','2','3', 17 | '4','5','6','7','8','9','+','/' 18 | }; 19 | static const ASCIIChar pad='='; 20 | 21 | 22 | String Encode (const Vector & buffer) { 23 | 24 | // Determine the number of code 25 | // points it will take maximally 26 | // to describe this buffer 27 | SafeWord len(buffer.Count()/3); 28 | if ((buffer.Count()%3)!=0) ++len; 29 | len*=4; 30 | 31 | // This will be converted to 32 | // a string and then returned 33 | Vector cps(static_cast(len)); 34 | 35 | auto begin=buffer.begin(); 36 | auto end=buffer.end(); 37 | 38 | // Iterate over entire input 39 | // buffer 40 | while (begin!=end) { 41 | 42 | // We need 24 bits for 4 base 64 43 | // characters, they'll be stored 44 | // in here 45 | UInt32 build=0; 46 | // Keep track of the number of significant 47 | // bytes for padding purposes 48 | Word significant=0; 49 | // Extract 3 bytes (24 bits) 50 | for (Word i=0;i<3;++i) { 51 | 52 | // Make room for the next byte 53 | build<<=BitsPerByte(); 54 | 55 | // Only add the next byte if 56 | // there's a byte to extract 57 | if (begin!=end) { 58 | 59 | build|=*begin; 60 | 61 | // Advance buffer pointer 62 | ++begin; 63 | // A byte from the buffer 64 | // is significant 65 | ++significant; 66 | 67 | } 68 | 69 | } 70 | 71 | Word printed=0; 72 | // Print base 64 characters 73 | for (;printed<(significant+1);++printed) cps.Add(static_cast(encode[(build>>(6*(3-printed)))&63])); 74 | // Print padding (if necessary) 75 | for (;printed<4;++printed) cps.Add(static_cast(pad)); 76 | 77 | } 78 | 79 | return String(std::move(cps)); 80 | 81 | } 82 | 83 | 84 | } 85 | -------------------------------------------------------------------------------- /include/uniform_int_distribution.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | namespace MCPP { 16 | 17 | 18 | /** 19 | * Wraps the std::uniform_int_distribution 20 | * class and allows for that type to additionally 21 | * be instantiated for char, unsigned char, and 22 | * signed char. 23 | */ 24 | template 25 | class UniformIntDistribution { 26 | 27 | 28 | private: 29 | 30 | 31 | typedef typename std::conditional< 32 | ( 33 | std::is_same::value || 34 | std::is_same::value || 35 | std::is_same::value 36 | ), 37 | typename std::conditional< 38 | std::is_signed::value, 39 | short, 40 | unsigned short 41 | >::type, 42 | T 43 | >::type type; 44 | 45 | 46 | typedef std::uniform_int_distribution dist_type; 47 | 48 | 49 | dist_type dist; 50 | 51 | 52 | public: 53 | 54 | 55 | typedef T result_type; 56 | 57 | 58 | explicit UniformIntDistribution ( 59 | T min=std::numeric_limits::min(), 60 | T max=std::numeric_limits::max() 61 | ) noexcept( 62 | std::is_nothrow_constructible< 63 | dist_type, 64 | type, 65 | type 66 | >::value 67 | ) : dist( 68 | static_cast(min), 69 | static_cast(max) 70 | ) { } 71 | 72 | 73 | template 74 | T operator () (TGenerator & gen) noexcept( 75 | noexcept(std::declval()(gen)) 76 | ) { 77 | 78 | return static_cast(dist(gen)); 79 | 80 | } 81 | 82 | 83 | T min () const noexcept( 84 | noexcept(std::declval().min()) 85 | ) { 86 | 87 | return static_cast(dist.min()); 88 | 89 | } 90 | 91 | 92 | T max () const noexcept( 93 | noexcept(std::declval().max()) 94 | ) { 95 | 96 | return static_cast(dist.max()); 97 | 98 | } 99 | 100 | 101 | }; 102 | 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/info/dp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | using namespace MCPP; 8 | 9 | 10 | static const Word priority=1; 11 | static const String name("Data Provider Information"); 12 | static const String identifier("dp"); 13 | static const String help("Displays information about the data provider the server is using."); 14 | static const String dp_banner("DATA PROVIDER:"); 15 | 16 | 17 | class DPInfo : public Module, public InformationProvider { 18 | 19 | 20 | public: 21 | 22 | 23 | virtual Word Priority () const noexcept override { 24 | 25 | return priority; 26 | 27 | } 28 | 29 | 30 | virtual const String & Name () const noexcept override { 31 | 32 | return name; 33 | 34 | } 35 | 36 | 37 | virtual const String & Identifier () const noexcept override { 38 | 39 | return identifier; 40 | 41 | } 42 | 43 | 44 | virtual const String & Help () const noexcept override { 45 | 46 | return help; 47 | 48 | } 49 | 50 | 51 | virtual void Install () override { 52 | 53 | Information::Get().Add(this); 54 | 55 | } 56 | 57 | 58 | virtual void Execute (ChatMessage & message) const override { 59 | 60 | // Get data about the data provider 61 | // the server is using 62 | auto info=Server::Get().Data().GetInfo(); 63 | 64 | // Sort the key/value pairs 65 | std::sort( 66 | info.Data.begin(), 67 | info.Data.end(), 68 | [] (const DataProviderDatum & a, const DataProviderDatum & b) { 69 | 70 | return a.Name 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | namespace MCPP { 16 | 17 | 18 | /** 19 | * Contains a singleton. 20 | * 21 | * The singleton is not constructed 22 | * until an attempt is made to access it, 23 | * in which case it is thread safely 24 | * constructed and persists until manually 25 | * destroyed. 26 | * 27 | * Destruction is not thread safe. 28 | * 29 | * After the singleton is manually destroyed, 30 | * an further attempts to access it will 31 | * reconstruct it. 32 | */ 33 | template 34 | class Singleton { 35 | 36 | 37 | private: 38 | 39 | 40 | Nullable obj; 41 | Mutex lock; 42 | std::atomic constructed; 43 | 44 | 45 | public: 46 | 47 | 48 | /** 49 | * Creates a new singleton container. 50 | */ 51 | Singleton () noexcept { 52 | 53 | constructed=false; 54 | 55 | } 56 | 57 | 58 | Singleton (const Singleton &) = delete; 59 | Singleton (Singleton &&) = delete; 60 | Singleton & operator = (const Singleton &) = delete; 61 | Singleton & operator = (Singleton &&) = delete; 62 | 63 | 64 | /** 65 | * Retrieves the singleton, constructing 66 | * it if necessary. 67 | * 68 | * Thread safe. 69 | * 70 | * \return 71 | * A reference to the contained 72 | * singleton. 73 | */ 74 | T & Get () noexcept(std::is_nothrow_constructible::value) { 75 | 76 | if (!constructed) { 77 | 78 | lock.Execute([&] () { 79 | 80 | if (!constructed) { 81 | 82 | obj.Construct(); 83 | 84 | constructed=true; 85 | 86 | } 87 | 88 | }); 89 | 90 | } 91 | 92 | return *obj; 93 | 94 | } 95 | 96 | 97 | /** 98 | * Destroys the contained singleton. 99 | * 100 | * Not thread safe. 101 | */ 102 | void Destroy () noexcept { 103 | 104 | obj.Destroy(); 105 | 106 | constructed=false; 107 | 108 | } 109 | 110 | 111 | }; 112 | 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/world/save.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | namespace MCPP { 9 | 10 | 11 | static const String save_failed("Failed saving {0} after {1}ns"); 12 | static const String end_save("Saved column {0} - {1} bytes in {2}ns"); 13 | 14 | 15 | bool World::save (ColumnContainer & column) { 16 | 17 | // Start timer 18 | Timer timer(Timer::CreateAndStart()); 19 | 20 | column.Acquire(); 21 | 22 | // Only save if necessary 23 | if (!column.Dirty()) { 24 | 25 | column.Release(); 26 | 27 | return false; 28 | 29 | } 30 | 31 | // We copy the column so that 32 | // other threads do not have 33 | // to wait for the backing 34 | // store save operation 35 | Byte buffer [ColumnContainer::Size]; 36 | memcpy( 37 | buffer, 38 | column.Get(), 39 | ColumnContainer::Size 40 | ); 41 | 42 | // Column is no longer dirty 43 | column.Clean(); 44 | 45 | column.Release(); 46 | 47 | auto & server=Server::Get(); 48 | 49 | // Perform save 50 | try { 51 | 52 | auto compressed=Deflate( 53 | std::begin(buffer), 54 | std::end(buffer) 55 | ); 56 | server.Data().SaveBinary( 57 | key(column), 58 | compressed.begin(), 59 | compressed.Count() 60 | ); 61 | 62 | } catch (...) { 63 | 64 | try { 65 | 66 | server.WriteLog( 67 | String::Format( 68 | save_failed, 69 | column.ToString(), 70 | timer.ElapsedNanoseconds() 71 | ), 72 | Service::LogType::Error 73 | ); 74 | 75 | // We don't care whether this 76 | // actually happens or not 77 | } catch (...) { } 78 | 79 | // PANIC 80 | server.Panic(); 81 | 82 | throw; 83 | 84 | } 85 | 86 | auto elapsed=timer.ElapsedNanoseconds(); 87 | save_time+=elapsed; 88 | ++saved; 89 | 90 | // Log if applicable 91 | if (server.IsVerbose(verbose)) server.WriteLog( 92 | String::Format( 93 | end_save, 94 | column.ToString(), 95 | ColumnContainer::Size, 96 | elapsed 97 | ), 98 | Service::LogType::Debug 99 | ); 100 | 101 | return true; 102 | 103 | } 104 | 105 | 106 | } 107 | -------------------------------------------------------------------------------- /include/recursive_mutex.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | namespace MCPP { 15 | 16 | 17 | /** 18 | * Implements a mutex that may be recursively 19 | * acquired and released. 20 | */ 21 | class RecursiveMutex { 22 | 23 | 24 | private: 25 | 26 | 27 | Mutex lock; 28 | CondVar wait; 29 | Nullable id; 30 | Word depth; 31 | 32 | 33 | public: 34 | 35 | 36 | void Acquire () noexcept; 37 | void Release () noexcept; 38 | 39 | 40 | template 41 | auto Execute (T && callback, Args &&... args) noexcept( 42 | noexcept(callback(std::forward(args)...)) 43 | ) -> typename std::enable_if< 44 | std::is_same< 45 | decltype(callback(std::forward(args)...)), 46 | void 47 | >::value 48 | >::type { 49 | 50 | Acquire(); 51 | 52 | try { 53 | 54 | callback(std::forward(args)...); 55 | 56 | } catch (...) { 57 | 58 | Release(); 59 | 60 | throw; 61 | 62 | } 63 | 64 | Release(); 65 | 66 | } 67 | 68 | 69 | template 70 | auto Execute (T && callback, Args &&... args) noexcept ( 71 | noexcept(callback(std::forward(args)...)) && 72 | std::is_nothrow_move_constructible< 73 | decltype(callback(std::forward(args)...)) 74 | >::value 75 | ) -> typename std::enable_if< 76 | !std::is_same< 77 | decltype(callback(std::forward(args)...)), 78 | void 79 | >::value, 80 | decltype(callback(std::forward(args)...)) 81 | >::type { 82 | 83 | Nullable(args)...))> retr; 84 | 85 | Acquire(); 86 | 87 | try { 88 | 89 | retr.Construct(callback(std::forward(args)...)); 90 | 91 | } catch (...) { 92 | 93 | Release(); 94 | 95 | throw; 96 | 97 | } 98 | 99 | Release(); 100 | 101 | return std::move(*retr); 102 | 103 | } 104 | 105 | 106 | }; 107 | 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/network/windows/accept_data.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | AcceptData::AcceptData (SOCKET socket) noexcept : socket(socket) { } 11 | 12 | 13 | void AcceptData::cleanup () noexcept { 14 | 15 | if (socket!=INVALID_SOCKET) { 16 | 17 | closesocket(socket); 18 | socket=INVALID_SOCKET; 19 | 20 | } 21 | 22 | } 23 | 24 | 25 | AcceptData::~AcceptData () noexcept { 26 | 27 | cleanup(); 28 | 29 | } 30 | 31 | 32 | AcceptData::AcceptData (AcceptData && other) noexcept 33 | : socket(other.socket), 34 | RemoteIP(other.RemoteIP), 35 | RemotePort(other.RemotePort), 36 | LocalIP(other.LocalIP), 37 | LocalPort(other.LocalPort) 38 | { 39 | 40 | other.socket=INVALID_SOCKET; 41 | 42 | } 43 | 44 | 45 | AcceptData & AcceptData::operator = (AcceptData && other) noexcept { 46 | 47 | // Guard against self-assignment 48 | if (this!=&other) { 49 | 50 | cleanup(); 51 | 52 | socket=other.socket; 53 | other.socket=INVALID_SOCKET; 54 | RemoteIP=std::move(other.RemoteIP); 55 | RemotePort=other.RemotePort; 56 | LocalIP=std::move(other.LocalIP); 57 | LocalPort=other.LocalPort; 58 | 59 | } 60 | 61 | return *this; 62 | 63 | } 64 | 65 | 66 | AcceptData::AcceptData (const AcceptData & other) noexcept { 67 | 68 | // We don't copy the socket 69 | socket=INVALID_SOCKET; 70 | 71 | RemoteIP=other.RemoteIP; 72 | RemotePort=other.RemotePort; 73 | LocalIP=other.LocalIP; 74 | LocalPort=other.LocalPort; 75 | 76 | } 77 | 78 | 79 | AcceptData & AcceptData::operator = (const AcceptData & other) noexcept { 80 | 81 | // Guard against self-assignment 82 | if (this!=&other) { 83 | 84 | cleanup(); 85 | 86 | RemoteIP=other.RemoteIP; 87 | RemotePort=other.RemotePort; 88 | LocalIP=other.LocalIP; 89 | LocalPort=other.LocalPort; 90 | 91 | } 92 | 93 | return *this; 94 | 95 | } 96 | 97 | 98 | SOCKET AcceptData::Get () noexcept { 99 | 100 | auto retr=socket; 101 | socket=INVALID_SOCKET; 102 | 103 | return retr; 104 | 105 | } 106 | 107 | 108 | } 109 | 110 | 111 | } 112 | -------------------------------------------------------------------------------- /include/info/info.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | namespace MCPP { 17 | 18 | 19 | /** 20 | * Interface which all information 21 | * providers must implement. 22 | */ 23 | class InformationProvider { 24 | 25 | 26 | public: 27 | 28 | 29 | virtual const String & Identifier () const noexcept = 0; 30 | virtual const String & Help () const noexcept = 0; 31 | virtual void Execute (ChatMessage & message) const = 0; 32 | 33 | 34 | }; 35 | 36 | 37 | /** 38 | * Allows information about various 39 | * server components to be obtained 40 | * by server operators through chat. 41 | */ 42 | class Information : public Module, public Command { 43 | 44 | 45 | private: 46 | 47 | 48 | std::unordered_map map; 49 | Vector list; 50 | 51 | 52 | InformationProvider * get (const String &); 53 | 54 | 55 | public: 56 | 57 | 58 | /** 59 | * Retrieves a reference to a valid 60 | * instance of this class. 61 | * 62 | * \return 63 | * A reference to a valid instance 64 | * of this class. 65 | */ 66 | static Information & Get () noexcept; 67 | 68 | 69 | /** 70 | * \cond 71 | */ 72 | 73 | 74 | virtual Word Priority () const noexcept override; 75 | virtual const String & Name () const noexcept override; 76 | virtual void Install () override; 77 | 78 | 79 | virtual void Summary (const String &, ChatMessage &) override; 80 | virtual void Help (const String &, ChatMessage &) override; 81 | virtual Vector AutoComplete (const CommandEvent &) override; 82 | virtual bool Check (const CommandEvent &) override; 83 | virtual CommandResult Execute (CommandEvent) override; 84 | 85 | 86 | /** 87 | * \endcond 88 | */ 89 | 90 | 91 | /** 92 | * Adds a new information provider to be 93 | * managed by the module. 94 | * 95 | * \param [in] provider 96 | * The information provider to add. 97 | */ 98 | void Add (InformationProvider * provider); 99 | 100 | 101 | }; 102 | 103 | 104 | } 105 | -------------------------------------------------------------------------------- /include/synchronized_random.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | namespace MCPP { 16 | 17 | 18 | /** 19 | * Provides a thread safe wrapper for random 20 | * number sources. 21 | */ 22 | template 23 | class SynchronizedRandom { 24 | 25 | 26 | private: 27 | 28 | 29 | T func; 30 | Mutex lock; 31 | 32 | 33 | public: 34 | 35 | 36 | typedef typename T::result_type result_type; 37 | 38 | 39 | SynchronizedRandom () = default; 40 | 41 | 42 | /** 43 | * Wraps a random number source in a thread 44 | * safe wrapper. 45 | * 46 | * \param [in] func 47 | * The object to wrap. 48 | */ 49 | SynchronizedRandom (T func) noexcept( 50 | std::is_nothrow_move_constructible::value 51 | ) : func(std::move(func)) { } 52 | 53 | 54 | /** 55 | * Produces a random number in a thread safe 56 | * manner. 57 | * 58 | * \return 59 | * A random number from the underlying 60 | * random number source. 61 | */ 62 | template 63 | result_type operator () (Args &&... args) noexcept( 64 | noexcept(std::declval()(std::forward(args)...)) 65 | ) { 66 | 67 | lock.Acquire(); 68 | auto guard=AtExit([&] () noexcept { lock.Release(); }); 69 | return func(std::forward(args)...); 70 | 71 | } 72 | 73 | 74 | /** 75 | * Returns the minimum value that will be 76 | * generated by the underlying object. 77 | * 78 | * \return 79 | * The lowest value the underlying 80 | * object will produce. 81 | */ 82 | result_type min () noexcept( 83 | noexcept(std::declval().min()) 84 | ) { 85 | 86 | return func.min(); 87 | 88 | } 89 | 90 | 91 | /** 92 | * Returns the maximum value that will 93 | * be generated by the underlying 94 | * object. 95 | * 96 | * \return 97 | * The highest value the underlying 98 | * object will produce. 99 | */ 100 | result_type max () noexcept( 101 | noexcept(std::declval().max()) 102 | ) { 103 | 104 | return func.max(); 105 | 106 | } 107 | 108 | 109 | }; 110 | 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/network/windows/error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace MCPP { 6 | 7 | 8 | namespace NetworkImpl { 9 | 10 | 11 | void Raise () { 12 | 13 | throw std::system_error( 14 | std::error_code( 15 | GetLastError(), 16 | std::system_category() 17 | ) 18 | ); 19 | 20 | } 21 | 22 | 23 | void RaiseWSA () { 24 | 25 | throw std::system_error( 26 | std::error_code( 27 | WSAGetLastError(), 28 | std::system_category() 29 | ) 30 | ); 31 | 32 | } 33 | 34 | 35 | void Raise (DWORD code) { 36 | 37 | throw std::system_error( 38 | std::error_code( 39 | code, 40 | std::system_category() 41 | ) 42 | ); 43 | 44 | } 45 | 46 | 47 | std::exception_ptr GetError (DWORD code) noexcept { 48 | 49 | return std::make_exception_ptr( 50 | std::system_error( 51 | std::error_code( 52 | code, 53 | std::system_category() 54 | ) 55 | ) 56 | ); 57 | 58 | } 59 | 60 | 61 | String GetErrorMessage (DWORD code) { 62 | 63 | // Attempt to get string from 64 | // system 65 | LPWSTR ptr; 66 | auto num=FormatMessageW( 67 | FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, 68 | nullptr, 69 | code, 70 | 0, 71 | reinterpret_cast(&ptr), 72 | 0, 73 | nullptr 74 | ); 75 | 76 | // If no characters were returned, 77 | // just return the empty string 78 | if (num==0) return String(); 79 | 80 | // Get rid of trailing carriage return 81 | // line feed, if present 82 | for ( 83 | ; 84 | (num!=0) && 85 | ( 86 | (ptr[num-1]=='\r') || 87 | (ptr[num-1]=='\n') 88 | ); 89 | --num 90 | ); 91 | 92 | // Return empty string if there 93 | // are no characters left after 94 | // eliminating trailing carriage 95 | // returns and line feeds, otherwise 96 | // decode 97 | String retr; 98 | if (num!=0) try { 99 | 100 | retr=UTF16(false).Decode( 101 | reinterpret_cast(ptr), 102 | reinterpret_cast(ptr+num) 103 | ); 104 | 105 | } catch (...) { 106 | 107 | LocalFree(ptr); 108 | 109 | throw; 110 | 111 | } 112 | 113 | LocalFree(ptr); 114 | 115 | return retr; 116 | 117 | } 118 | 119 | 120 | } 121 | 122 | 123 | } 124 | -------------------------------------------------------------------------------- /include/blacklist/blacklist.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | namespace MCPP { 16 | 17 | 18 | /** 19 | * Contains information about a Blacklist instance 20 | * at a particular point in time. 21 | */ 22 | class BlacklistInfo { 23 | 24 | 25 | public: 26 | 27 | 28 | /** 29 | * IP ranges that are blacklisted. 30 | */ 31 | Vector Ranges; 32 | 33 | 34 | }; 35 | 36 | 37 | /** 38 | * Allows IPs and IP ranges to be blocked from connecting 39 | * to the server. 40 | */ 41 | class Blacklist : public Module { 42 | 43 | 44 | private: 45 | 46 | 47 | std::unordered_set ranges; 48 | mutable RWLock lock; 49 | 50 | 51 | static bool is_verbose (); 52 | void save () const; 53 | void load (); 54 | 55 | 56 | public: 57 | 58 | 59 | static Blacklist & Get () noexcept; 60 | 61 | 62 | /** 63 | * \cond 64 | */ 65 | 66 | 67 | virtual Word Priority () const noexcept override; 68 | virtual const String & Name () const noexcept override; 69 | virtual void Install () override; 70 | 71 | 72 | /** 73 | * \endcond 74 | */ 75 | 76 | 77 | /** 78 | * Checks to see if the given IP is blacklisted. 79 | * 80 | * \param [in] ip 81 | * The IP to check. 82 | * 83 | * \return 84 | * \em true if \em ip is blacklisted, \em false 85 | * otherwise. 86 | */ 87 | bool Check (IPAddress ip) const noexcept; 88 | 89 | 90 | /** 91 | * Blacklists the given range of IPs. If any clients 92 | * in the given range are connected, they will be 93 | * disconnected. 94 | */ 95 | void Add (IPAddressRange range); 96 | 97 | 98 | /** 99 | * Removes the given IP range from the blacklist if 100 | * it is blacklisted. 101 | * 102 | * \param [in] range 103 | * The IP range to remove from the blacklist. 104 | */ 105 | void Remove (IPAddressRange range); 106 | 107 | 108 | /** 109 | * Retrieves a structure containing information about 110 | * this blacklist instance. 111 | * 112 | * \return 113 | * A BlacklistInfo structure. 114 | */ 115 | BlacklistInfo GetInfo () const; 116 | 117 | 118 | }; 119 | 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/packet_router.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | namespace MCPP { 8 | 9 | 10 | static const String packet_dne="Packet 0x{0} has no recognized handler"; 11 | 12 | 13 | template 14 | void init_array (T (& arr) [n]) noexcept(std::is_nothrow_constructible::value) { 15 | 16 | for (auto & i : arr) new (&i) T (); 17 | 18 | } 19 | 20 | 21 | template 22 | void destroy_array (T (& arr) [n]) noexcept { 23 | 24 | for (auto & i : arr) i.~T(); 25 | 26 | } 27 | 28 | 29 | inline void PacketRouter::destroy () noexcept { 30 | 31 | destroy_array(play_routes); 32 | destroy_array(status_routes); 33 | destroy_array(login_routes); 34 | destroy_array(handshake_routes); 35 | 36 | } 37 | 38 | 39 | inline void PacketRouter::init () noexcept { 40 | 41 | init_array(play_routes); 42 | init_array(status_routes); 43 | init_array(login_routes); 44 | init_array(handshake_routes); 45 | 46 | } 47 | 48 | 49 | PacketRouter::PacketRouter () noexcept { 50 | 51 | init(); 52 | 53 | } 54 | 55 | 56 | PacketRouter::~PacketRouter () noexcept { 57 | 58 | destroy(); 59 | 60 | } 61 | 62 | 63 | auto PacketRouter::operator () (UInt32 id, ProtocolState state) noexcept -> Type & { 64 | 65 | switch (state) { 66 | 67 | case ProtocolState::Handshaking:return handshake_routes[id]; 68 | case ProtocolState::Status:return status_routes[id]; 69 | case ProtocolState::Login:return login_routes[id]; 70 | case ProtocolState::Play: 71 | default:return play_routes[id]; 72 | 73 | } 74 | 75 | } 76 | 77 | 78 | auto PacketRouter::operator () (UInt32 id, ProtocolState state) const noexcept -> const Type & { 79 | 80 | switch (state) { 81 | 82 | case ProtocolState::Handshaking:return handshake_routes[id]; 83 | case ProtocolState::Status:return status_routes[id]; 84 | case ProtocolState::Login:return login_routes[id]; 85 | case ProtocolState::Play: 86 | default:return play_routes[id]; 87 | 88 | } 89 | 90 | } 91 | 92 | 93 | void PacketRouter::operator () (PacketEvent event, ProtocolState state) const { 94 | 95 | auto & callback=(*this)(event.Data.ID,state); 96 | 97 | if (callback) callback(std::move(event)); 98 | 99 | } 100 | 101 | 102 | void PacketRouter::Clear () noexcept { 103 | 104 | destroy(); 105 | 106 | init(); 107 | 108 | } 109 | 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/save/info.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | using namespace MCPP; 7 | 8 | 9 | static const String name("Save Manager Information"); 10 | static const Word priority=1; 11 | static const String identifier("save"); 12 | static const String help("Displays information about the save system."); 13 | static const String save_banner("SAVE SYSTEM:"); 14 | static const String paused("Paused: "); 15 | static const String true_string("Yes"); 16 | static const String false_string("No"); 17 | static const String frequency("Save Frequency: {0}ms"); 18 | static const String saves("Saves: {0}"); 19 | static const String elapsed("Elapsed: {0}ns"); 20 | static const String average("Average: {0}ns"); 21 | 22 | 23 | template 24 | T avg (T total, Word count) noexcept { 25 | 26 | return (count==0) ? 0 : (total/count); 27 | 28 | } 29 | 30 | 31 | class SaveInfo : public Module, public InformationProvider { 32 | 33 | 34 | public: 35 | 36 | 37 | virtual const String & Name () const noexcept override { 38 | 39 | return name; 40 | 41 | } 42 | 43 | 44 | virtual Word Priority () const noexcept override { 45 | 46 | return priority; 47 | 48 | } 49 | 50 | 51 | virtual void Install () override { 52 | 53 | Information::Get().Add(this); 54 | 55 | } 56 | 57 | 58 | virtual const String & Identifier () const noexcept override { 59 | 60 | return identifier; 61 | 62 | } 63 | 64 | 65 | virtual const String & Help () const noexcept override { 66 | 67 | return help; 68 | 69 | } 70 | 71 | 72 | virtual void Execute (ChatMessage & message) const override { 73 | 74 | auto info=SaveManager::Get().GetInfo(); 75 | 76 | message << ChatStyle::Bold 77 | << save_banner 78 | << ChatFormat::Pop 79 | << Newline 80 | << paused 81 | << (info.Paused ? true_string : false_string) 82 | << Newline 83 | << String::Format( 84 | frequency, 85 | info.Frequency 86 | ) 87 | << Newline 88 | << String::Format( 89 | saves, 90 | info.Count 91 | ) 92 | << Newline 93 | << String::Format( 94 | elapsed, 95 | info.Elapsed 96 | ) 97 | << Newline 98 | << String::Format( 99 | average, 100 | avg( 101 | info.Elapsed, 102 | info.Count 103 | ) 104 | ); 105 | 106 | } 107 | 108 | 109 | }; 110 | 111 | 112 | INSTALL_MODULE(SaveInfo) 113 | -------------------------------------------------------------------------------- /src/ban/info.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | using namespace MCPP; 8 | 9 | 10 | static const String name("Bans Information"); 11 | static const String identifier("bans"); 12 | static const String help("Displays the banlist."); 13 | static const Word priority=1; 14 | static const String bans_banner("BANLIST:"); 15 | static const String parenthetical []={ 16 | " (", 17 | ")" 18 | }; 19 | static const String by("by {0}"); 20 | static const String reason("with reason \"{0}\""); 21 | static const String separator(" "); 22 | 23 | 24 | class BanInfoProvider : public Module, public InformationProvider { 25 | 26 | 27 | public: 28 | 29 | 30 | virtual const String & Name () const noexcept override { 31 | 32 | return name; 33 | 34 | } 35 | 36 | 37 | virtual Word Priority () const noexcept override { 38 | 39 | return priority; 40 | 41 | } 42 | 43 | 44 | virtual void Install () override { 45 | 46 | Information::Get().Add(this); 47 | 48 | } 49 | 50 | 51 | virtual const String & Identifier () const noexcept override { 52 | 53 | return identifier; 54 | 55 | } 56 | 57 | 58 | virtual const String & Help () const noexcept override { 59 | 60 | return help; 61 | 62 | } 63 | 64 | 65 | virtual void Execute (ChatMessage & message) const { 66 | 67 | // Get information 68 | auto info=Bans::Get().GetInfo(); 69 | 70 | // Sort 71 | std::sort( 72 | info.begin(), 73 | info.end(), 74 | [] (const BanInfo & a, const BanInfo & b) { return a.Username 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | void ClientListIterator::destroy () noexcept { 8 | 9 | if (list!=nullptr) { 10 | 11 | list->map_lock.CompleteRead(); 12 | 13 | list=nullptr; 14 | 15 | } 16 | 17 | } 18 | 19 | 20 | ClientListIterator::ClientListIterator (ClientList * list, iter_type iter) noexcept 21 | : iter(std::move(iter)), 22 | list(list) 23 | { 24 | 25 | list->map_lock.Read(); 26 | 27 | } 28 | 29 | 30 | ClientListIterator::ClientListIterator (const ClientListIterator & other) noexcept : iter(other.iter), list(other.list) { 31 | 32 | list->map_lock.Read(); 33 | 34 | } 35 | 36 | 37 | ClientListIterator::ClientListIterator (ClientListIterator && other) noexcept : iter(std::move(other.iter)), list(other.list) { 38 | 39 | other.list=nullptr; 40 | 41 | } 42 | 43 | 44 | ClientListIterator & ClientListIterator::operator = (const ClientListIterator & other) noexcept { 45 | 46 | if (this!=&other) { 47 | 48 | destroy(); 49 | 50 | iter=other.iter; 51 | list=other.list; 52 | 53 | list->map_lock.Read(); 54 | 55 | } 56 | 57 | return *this; 58 | 59 | } 60 | 61 | 62 | ClientListIterator & ClientListIterator::operator = (ClientListIterator && other) noexcept { 63 | 64 | if (this!=&other) { 65 | 66 | destroy(); 67 | 68 | iter=std::move(other.iter); 69 | list=other.list; 70 | other.list=nullptr; 71 | 72 | } 73 | 74 | return *this; 75 | 76 | } 77 | 78 | 79 | ClientListIterator::~ClientListIterator () noexcept { 80 | 81 | destroy(); 82 | 83 | } 84 | 85 | 86 | SmartPointer & ClientListIterator::operator * () noexcept { 87 | 88 | return iter->second; 89 | 90 | } 91 | 92 | 93 | SmartPointer * ClientListIterator::operator -> () noexcept { 94 | 95 | return &(iter->second); 96 | 97 | } 98 | 99 | 100 | ClientListIterator & ClientListIterator::operator ++ () noexcept { 101 | 102 | ++iter; 103 | 104 | return *this; 105 | 106 | } 107 | 108 | 109 | ClientListIterator ClientListIterator::operator ++ (int) noexcept { 110 | 111 | auto retr=*this; 112 | 113 | ++iter; 114 | 115 | return retr; 116 | 117 | } 118 | 119 | 120 | bool ClientListIterator::operator == (const ClientListIterator & other) const noexcept { 121 | 122 | return iter==other.iter; 123 | 124 | } 125 | 126 | 127 | bool ClientListIterator::operator != (const ClientListIterator & other) const noexcept { 128 | 129 | return iter!=other.iter; 130 | 131 | } 132 | 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/network/posix/worker_channel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | using namespace MCPP::NetworkImpl; 7 | 8 | 9 | namespace MCPP { 10 | 11 | 12 | // Ends of the socket pair that the master 13 | // and slave will variously use 14 | static const Word master=0; 15 | static const Word slave=1; 16 | 17 | 18 | ConnectionHandler::WorkerChannel::WorkerChannel () { 19 | 20 | // Make sure sockets are blocking/non-blocking 21 | // as appropriate 22 | SetBlocking(pair[master],true); 23 | SetBlocking(pair[slave],false); 24 | 25 | } 26 | 27 | 28 | void ConnectionHandler::WorkerChannel::Attach (NetworkImpl::Notifier & n) { 29 | 30 | n.Attach(pair[slave]); 31 | n.Update(pair[slave],true,false); 32 | 33 | } 34 | 35 | 36 | void ConnectionHandler::WorkerChannel::Send (Command c) { 37 | 38 | lock.Execute([&] () { 39 | 40 | // Send one byte through to slave to 41 | // wake them up so they'll process 42 | // this message 43 | Byte b=0; 44 | for (;;) switch (send(pair[master],&b,1,0)) { 45 | 46 | case 0: 47 | // Nothing was sent, try again 48 | continue; 49 | 50 | case 1: 51 | // We're done 52 | goto done; 53 | 54 | default: 55 | // Error 56 | if (WasInterrupted()) continue; 57 | Raise(); 58 | 59 | } 60 | 61 | done: 62 | 63 | // Add to the collection 64 | commands.Add(std::move(c)); 65 | 66 | }); 67 | } 68 | 69 | 70 | static const char * end_of_stream="Unexpected end of stream"; 71 | Nullable ConnectionHandler::WorkerChannel::Receive () { 72 | 73 | // Get everything out of the socket 74 | Byte b; 75 | for (;;) switch (recv(pair[slave],&b,1,0)) { 76 | 77 | case 0: 78 | // THIS SHOULD NEVER HAPPEN 79 | throw std::runtime_error(end_of_stream); 80 | 81 | case 1: 82 | // Keep going... 83 | continue; 84 | 85 | default: 86 | // ERROR 87 | if (WouldBlock()) goto done; 88 | if (WasInterrupted()) continue; 89 | Raise(); 90 | 91 | } 92 | 93 | done: 94 | 95 | // Actually extract a command (if applicable) 96 | return lock.Execute([&] () { 97 | 98 | Nullable retr; 99 | 100 | if (commands.Count()==0) return retr; 101 | 102 | retr.Construct(std::move(commands[0])); 103 | commands.Delete(0); 104 | 105 | return retr; 106 | 107 | }); 108 | 109 | } 110 | 111 | 112 | bool ConnectionHandler::WorkerChannel::Is (FDType fd) const noexcept { 113 | 114 | return pair[slave]==fd; 115 | 116 | } 117 | 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/world/world.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | using namespace MCPP; 8 | 9 | 10 | namespace MCPP { 11 | 12 | 13 | static const String name("World Support"); 14 | static const Word priority=1; 15 | const String World::verbose("world"); 16 | static const String default_world_type("DEFAULT"); 17 | static const Word default_maintenance_interval=5*60*1000; // 5 minutes 18 | static const String seed_key("seed"); 19 | static const String maintenance_interval_key("maintenance_interval"); 20 | static const String type_key("world_type"); 21 | static const String log_type("Set world type to \"{0}\""); 22 | 23 | 24 | World::World () noexcept { 25 | 26 | // Initialize stat counters 27 | maintenances=0; 28 | maintenance_time=0; 29 | unloaded=0; 30 | loaded=0; 31 | load_time=0; 32 | saved=0; 33 | save_time=0; 34 | generated=0; 35 | generate_time=0; 36 | populated=0; 37 | populate_time=0; 38 | 39 | } 40 | 41 | 42 | const String & World::Name () const noexcept { 43 | 44 | return name; 45 | 46 | } 47 | 48 | 49 | Word World::Priority () const noexcept { 50 | 51 | return priority; 52 | 53 | } 54 | 55 | 56 | void World::Install () { 57 | 58 | auto & server=Server::Get(); 59 | 60 | // Get settings 61 | 62 | // Seed 63 | auto seed=server.Data().GetSetting( 64 | seed_key 65 | ); 66 | set_seed(seed); 67 | if (seed.IsNull()) server.Data().SetSetting( 68 | seed_key, 69 | String(this->seed) 70 | ); 71 | 72 | // World type 73 | auto type=server.Data().GetSetting( 74 | type_key 75 | ); 76 | this->type=type.IsNull() ? default_world_type : *type; 77 | server.WriteLog( 78 | String::Format( 79 | log_type, 80 | this->type 81 | ), 82 | Service::LogType::Information 83 | ); 84 | 85 | // Install shutdown handler to cleanup 86 | // any module code 87 | server.OnShutdown.Add([this] () mutable { cleanup_events(); }); 88 | 89 | // Tie into the save loop 90 | SaveManager::Get().Add([this] () mutable { maintenance(); }); 91 | 92 | } 93 | 94 | 95 | const String & World::Type () const noexcept { 96 | 97 | return type; 98 | 99 | } 100 | 101 | 102 | static Singleton singleton; 103 | 104 | 105 | World & World::Get () noexcept { 106 | 107 | return singleton.Get(); 108 | 109 | } 110 | 111 | 112 | } 113 | 114 | 115 | extern "C" { 116 | 117 | 118 | Module * Load () { 119 | 120 | return &(World::Get()); 121 | } 122 | 123 | 124 | void Unload () { 125 | 126 | singleton.Destroy(); 127 | 128 | } 129 | 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/network/windows/reference_manager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | namespace NetworkImpl { 8 | 9 | 10 | void ReferenceManagerHandle::destroy () noexcept { 11 | 12 | if (manager!=nullptr) { 13 | 14 | manager->End(); 15 | 16 | manager=nullptr; 17 | 18 | } 19 | 20 | } 21 | 22 | 23 | ReferenceManagerHandle::ReferenceManagerHandle (ReferenceManager & manager) noexcept : manager(&manager) { 24 | 25 | manager.Begin(); 26 | 27 | } 28 | 29 | 30 | ReferenceManagerHandle::ReferenceManagerHandle (const ReferenceManagerHandle & other) noexcept : manager(other.manager) { 31 | 32 | if (manager!=nullptr) manager->Begin(); 33 | 34 | } 35 | 36 | 37 | ReferenceManagerHandle::ReferenceManagerHandle (ReferenceManagerHandle && other) noexcept : manager(other.manager) { 38 | 39 | other.manager=nullptr; 40 | 41 | } 42 | 43 | 44 | ReferenceManagerHandle & ReferenceManagerHandle::operator = (const ReferenceManagerHandle & other) noexcept { 45 | 46 | if (this!=&other) { 47 | 48 | destroy(); 49 | 50 | manager=other.manager; 51 | if (manager!=nullptr) manager->Begin(); 52 | 53 | } 54 | 55 | return *this; 56 | 57 | } 58 | 59 | 60 | ReferenceManagerHandle & ReferenceManagerHandle::operator = (ReferenceManagerHandle && other) noexcept { 61 | 62 | if (this!=&other) { 63 | 64 | destroy(); 65 | 66 | manager=other.manager; 67 | other.manager=nullptr; 68 | 69 | } 70 | 71 | return *this; 72 | 73 | } 74 | 75 | 76 | ReferenceManagerHandle::~ReferenceManagerHandle () noexcept { 77 | 78 | destroy(); 79 | 80 | } 81 | 82 | 83 | ReferenceManager::ReferenceManager () noexcept : count(0) { } 84 | 85 | 86 | ReferenceManager::~ReferenceManager () noexcept { 87 | 88 | Wait(); 89 | 90 | } 91 | 92 | 93 | void ReferenceManager::Begin () noexcept { 94 | 95 | lock.Execute([&] () mutable { ++count; }); 96 | 97 | } 98 | 99 | 100 | void ReferenceManager::End () noexcept { 101 | 102 | lock.Execute([&] () mutable { if ((--count)==0) wait.WakeAll(); }); 103 | 104 | } 105 | 106 | 107 | ReferenceManagerHandle ReferenceManager::Get () noexcept { 108 | 109 | return ReferenceManagerHandle(*this); 110 | 111 | } 112 | 113 | 114 | void ReferenceManager::Wait () const noexcept { 115 | 116 | lock.Execute([&] () mutable { while (count!=0) wait.Sleep(lock); }); 117 | 118 | } 119 | 120 | 121 | void ReferenceManager::Reset () noexcept { 122 | 123 | lock.Execute([&] () mutable { count=0; }); 124 | 125 | } 126 | 127 | 128 | } 129 | 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/info/pool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace MCPP; 10 | 11 | 12 | static const String name("Thread Pool Information"); 13 | static const Word priority=1; 14 | static const String identifier("pool"); 15 | static const String help("Displays information about the server's pool of worker threads."); 16 | 17 | 18 | static const String pool_template("Running: {0}, Queued: {1}, Scheduled: {2}"); 19 | static const String worker_template("Executed: {0}, Failed: {1}, Running: {2}ns, Average Per: {3}ns"); 20 | static const String pool_banner("MAIN SERVER THREAD POOL:"); 21 | static const String worker_label_template("Worker ID {0} Information: "); 22 | static const String pool_stats("Pool Information: "); 23 | 24 | 25 | class PoolInfo : public Module, public InformationProvider { 26 | 27 | 28 | public: 29 | 30 | 31 | virtual Word Priority () const noexcept override { 32 | 33 | return priority; 34 | 35 | } 36 | 37 | 38 | virtual const String & Name () const noexcept override { 39 | 40 | return name; 41 | 42 | } 43 | 44 | 45 | virtual void Install () override { 46 | 47 | Information::Get().Add(this); 48 | 49 | } 50 | 51 | 52 | virtual const String & Identifier () const noexcept override { 53 | 54 | return identifier; 55 | 56 | } 57 | 58 | 59 | virtual const String & Help () const noexcept override { 60 | 61 | return help; 62 | 63 | } 64 | 65 | 66 | virtual void Execute (ChatMessage & message) const override { 67 | 68 | // Get data about the thread pool 69 | auto info=Server::Get().Pool().GetInfo(); 70 | 71 | message << ChatStyle::Bold 72 | << pool_banner 73 | << ChatFormat::Pop 74 | << Newline 75 | << pool_stats 76 | << ChatFormat::Pop 77 | << String::Format( 78 | pool_template, 79 | info.Running, 80 | info.Queued, 81 | info.Scheduled 82 | ); 83 | 84 | // Stats for each worker 85 | for (Word i=0;i 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | namespace MCPP { 19 | 20 | 21 | /** 22 | * 23 | */ 24 | class Authentication : public Module { 25 | 26 | 27 | private: 28 | 29 | 30 | // Master server encryption key 31 | RSAKey key; 32 | // Yggdrasil authenticator 33 | Yggdrasil::Client ygg; 34 | // Random number generator 35 | SynchronizedRandom gen; 36 | // Distributes random numbers to produce 37 | // a random length for the server ID 38 | // string 39 | SynchronizedRandom> id_len; 40 | // Distributes random numbers to produce 41 | // ASCII characters for the server ID 42 | // string 43 | SynchronizedRandom> id_dist; 44 | 45 | 46 | public: 47 | 48 | 49 | /** 50 | * \cond 51 | */ 52 | 53 | 54 | Authentication (); 55 | 56 | 57 | virtual const String & Name () const noexcept override; 58 | virtual Word Priority () const noexcept override; 59 | virtual void Install () override; 60 | 61 | 62 | /** 63 | * \endcond 64 | */ 65 | 66 | 67 | /** 68 | * Retrieves a reference to an instance of this 69 | * class. 70 | * 71 | * \return 72 | * A reference to an instance of this class. 73 | */ 74 | static Authentication & Get (); 75 | 76 | 77 | /** 78 | * Verifies two verify tokens by ensuring that 79 | * they match. 80 | * 81 | * \param [in] a 82 | * A verify token. 83 | * \param [in] b 84 | * A verify token. 85 | * 86 | * \return 87 | * \em true if \em a and \em b match, \em false 88 | * otherwise. 89 | */ 90 | static bool VerifyTokens (const Vector & a, const Vector & b) noexcept; 91 | 92 | 93 | /** 94 | * Retrieves this authenticator's RSA public/private 95 | * key pair. 96 | * 97 | * \return 98 | * A reference to an RSA public/private key 99 | * pair. 100 | */ 101 | const RSAKey & GetKey () const noexcept; 102 | /** 103 | * Gets this authenticator's Yggdrasil client. 104 | * 105 | * \return 106 | * A reference to a Yggdrasil client. 107 | */ 108 | Yggdrasil::Client & GetClient () noexcept; 109 | /** 110 | * Generates a random verify token. 111 | * 112 | * \return 113 | * A buffer of bytes containing a 114 | * randomly-generated verify token. 115 | */ 116 | Vector GetVerifyToken (); 117 | /** 118 | * Generates a random server ID string. 119 | * 120 | * \return 121 | * A randomly-generated server ID string 122 | * suitable for use in vanilla authentication. 123 | */ 124 | String GetServerID (); 125 | 126 | 127 | }; 128 | 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/url.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | String URL::Encode (const String & encode) { 8 | 9 | // Convert to UTF-8 representation 10 | Vector utf8(UTF8().Encode(encode)); 11 | 12 | // Loop and reconstruct 13 | String encoded; 14 | for (const Byte b : utf8) { 15 | 16 | if ( 17 | // Outside ASCII range 18 | (b>=128) || 19 | ( 20 | // Not A-Z 21 | ( 22 | (b<'A') || 23 | (b>'Z') 24 | ) && 25 | // Not a-z 26 | ( 27 | (b<'a') || 28 | (b>'z') 29 | ) && 30 | // Not 0-9 31 | ( 32 | (b<'0') || 33 | (b>'9') 34 | ) && 35 | // Not - 36 | (b!='-') && 37 | // Not _ 38 | (b!='_') && 39 | // Not . 40 | (b!='.') && 41 | // Not ~ 42 | (b!='~') 43 | ) 44 | ) { 45 | 46 | // Percent encode 47 | 48 | encoded << "%"; 49 | 50 | String representation(b,16); 51 | 52 | if (representation.Count()==1) encoded << "0"; 53 | 54 | encoded << representation; 55 | 56 | } else { 57 | 58 | // Add as regular character 59 | encoded << GraphemeCluster( 60 | static_cast(b) 61 | ); 62 | 63 | } 64 | 65 | } 66 | 67 | return encoded; 68 | 69 | } 70 | 71 | 72 | String URL::Decode (const String & decode) { 73 | 74 | Vector utf8(UTF8().Encode(decode)); 75 | 76 | Vector decoded; 77 | for ( 78 | auto begin=utf8.begin(), end=utf8.end(); 79 | begin!=end; 80 | ++begin 81 | ) { 82 | 83 | if (*begin=='%') { 84 | 85 | // Save current position 86 | // so we can roll back if 87 | // parsing fails 88 | auto restore=begin; 89 | 90 | // Extract the two characters 91 | // that follow the % and designate 92 | // the code unit 93 | String hex; 94 | for (Word i=0;i<2;++i) { 95 | 96 | // Attempt to advance 97 | if ( 98 | // Fail because there's not 99 | // enough data 100 | (++begin==end) || 101 | // Fail because this byte 102 | // is not an ASCII character 103 | (*begin>=128) 104 | ) { 105 | 106 | // Fail 107 | begin=restore; 108 | 109 | goto literal; 110 | 111 | } 112 | 113 | hex << GraphemeCluster( 114 | static_cast(*begin) 115 | ); 116 | 117 | } 118 | 119 | // Attempt to parse the two 120 | // values into a byte 121 | Byte b; 122 | if (!hex.ToInteger(&b,16)) { 123 | 124 | // Failed, restore and 125 | // treat as literals 126 | begin=restore; 127 | 128 | goto literal; 129 | 130 | } 131 | 132 | // Add byte 133 | decoded.Add(b); 134 | 135 | } else { 136 | 137 | literal: 138 | 139 | // Copy byte verbatim 140 | decoded.Add(*begin); 141 | 142 | } 143 | 144 | } 145 | 146 | // Decode and return 147 | return UTF8().Decode(decoded.begin(),decoded.end()); 148 | 149 | } 150 | 151 | 152 | } 153 | -------------------------------------------------------------------------------- /include/mod.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | namespace MCPP { 13 | 14 | 15 | /** 16 | * The base of the object from which 17 | * all modules which the server 18 | * dynamically loads are expected 19 | * to derive. 20 | */ 21 | class Module { 22 | 23 | 24 | protected: 25 | 26 | 27 | Module () noexcept; 28 | 29 | 30 | public: 31 | 32 | 33 | Module (const Module &) = delete; 34 | Module (Module &&) = delete; 35 | Module & operator = (const Module &) = delete; 36 | Module & operator = (Module &&) = delete; 37 | 38 | 39 | /** 40 | * When overriden in a derived class, 41 | * cleans this object up. 42 | */ 43 | virtual ~Module () noexcept; 44 | 45 | 46 | /** 47 | * When overriden in a derived class, 48 | * returns a number which the server 49 | * shall use to decide in what order 50 | * to install this module. 51 | * 52 | * Lower numbers are loaded first. 53 | * 54 | * Modules that supply dependencies (other 55 | * than simply being linked against and 56 | * in the same address space) for other 57 | * modules should set a low number. 58 | * 59 | * Provided MCPP modules all specify 0 60 | * so that they are loaded first and 61 | * may be easily overridden. 62 | * 63 | * No guarantees are made about the state 64 | * of the server when this function is 65 | * called. Do not attempt to access 66 | * the server, just return a constant 67 | * number, do nothing else. 68 | * 69 | * \return 70 | * The priority this module should 71 | * be given in being installed. 72 | */ 73 | virtual Word Priority () const noexcept = 0; 74 | 75 | 76 | /** 77 | * When overriden in a derived class, 78 | * hooks the module into the server. 79 | * 80 | * When this function is called the server 81 | * is guaranteed to be properly initialized 82 | * with the exception of networking components. 83 | */ 84 | virtual void Install () = 0; 85 | 86 | 87 | /** 88 | * When overriden in a derived class, 89 | * returns the name that this module shall 90 | * be known by. 91 | * 92 | * \return 93 | * A reference to a string which gives 94 | * the name by which this module shall 95 | * be known. As it is a reference that 96 | * string must be statically allocated 97 | * in some way. 98 | */ 99 | virtual const String & Name () const noexcept = 0; 100 | 101 | 102 | }; 103 | 104 | 105 | } 106 | 107 | 108 | #define INSTALL_MODULE(x) \ 109 | static Nullable module; \ 110 | \ 111 | \ 112 | extern "C" { \ 113 | \ 114 | \ 115 | Module * Load () { \ 116 | \ 117 | if (module.IsNull()) module.Construct(); \ 118 | \ 119 | return &(*module); \ 120 | \ 121 | } \ 122 | \ 123 | \ 124 | void Unload () { \ 125 | \ 126 | module.Destroy(); \ 127 | \ 128 | } \ 129 | \ 130 | \ 131 | } 132 | -------------------------------------------------------------------------------- /include/packet_router.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | namespace MCPP { 16 | 17 | 18 | /** 19 | * Encapsulates information about a packet 20 | * being received from a client. 21 | */ 22 | class PacketEvent { 23 | 24 | 25 | public: 26 | 27 | 28 | /** 29 | * The client from which the packet was 30 | * received. 31 | */ 32 | SmartPointer From; 33 | /** 34 | * The packet which was received. 35 | */ 36 | Packet & Data; 37 | 38 | 39 | }; 40 | 41 | 42 | /** 43 | * Provides the facilities for routing 44 | * incoming packets to handlers. 45 | */ 46 | class PacketRouter { 47 | 48 | 49 | public: 50 | 51 | 52 | /** 53 | * The type of callback which may subscribe 54 | * to receive events. 55 | */ 56 | typedef std::function Type; 57 | 58 | 59 | private: 60 | 61 | 62 | // Routes 63 | Type play_routes [PacketImpl::LargestID+1]; 64 | Type status_routes [PacketImpl::LargestID+1]; 65 | Type login_routes [PacketImpl::LargestID+1]; 66 | Type handshake_routes [PacketImpl::LargestID+1]; 67 | 68 | 69 | inline void destroy () noexcept; 70 | inline void init () noexcept; 71 | 72 | 73 | public: 74 | 75 | 76 | /** 77 | * Creates a new packet router with no 78 | * routes. 79 | */ 80 | PacketRouter () noexcept; 81 | 82 | 83 | /** 84 | * Cleans up a packet router. 85 | */ 86 | ~PacketRouter () noexcept; 87 | 88 | 89 | /** 90 | * Fetches a packet route. 91 | * 92 | * \param [in] id 93 | * The ID of the packet whole route 94 | * shall be retrieved. 95 | * \param [it] state 96 | * The state of the packet whose route 97 | * shall be retreived. 98 | * 99 | * \return 100 | * A reference to the requested route. 101 | */ 102 | Type & operator () (UInt32 id, ProtocolState state) noexcept; 103 | /** 104 | * Fetches a packet route. 105 | * 106 | * \param [in] id 107 | * The ID of the packet whole route 108 | * shall be retrieved. 109 | * \param [it] state 110 | * The state of the packet whose route 111 | * shall be retreived. 112 | * 113 | * \return 114 | * A reference to the requested route. 115 | */ 116 | const Type & operator () (UInt32 id, ProtocolState state) const noexcept; 117 | 118 | 119 | /** 120 | * Dispatches a packet to the appropriate 121 | * handler. 122 | * 123 | * \param [in] event 124 | * The event object which represents 125 | * this receive event. 126 | * \param [in] state 127 | * The state the protocol is in. 128 | */ 129 | void operator () (PacketEvent event, ProtocolState state) const; 130 | 131 | 132 | /** 133 | * Clears all handlers. 134 | */ 135 | void Clear () noexcept; 136 | 137 | 138 | }; 139 | 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/info/mods.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace MCPP; 10 | 11 | 12 | static const String name("Loaded Module Information"); 13 | static const Word priority=1; 14 | static const String identifier("mods"); 15 | static const String help("Displays loaded modules."); 16 | static const String loader_banner("MODULE LOADER:"); 17 | static const String num_modules("Loaded Modules: "); 18 | static const String modules_dir("Modules Directory: "); 19 | static const String mods_banner("LOADED MODULES:"); 20 | static const String info_separator(": "); 21 | static const String file_label("File: "); 22 | static const String priority_label("Priority: "); 23 | static const String data_separator(", "); 24 | 25 | 26 | class ModsInfo : public Module, public InformationProvider { 27 | 28 | 29 | public: 30 | 31 | 32 | virtual Word Priority () const noexcept override { 33 | 34 | return priority; 35 | 36 | } 37 | 38 | 39 | virtual const String & Name () const noexcept override { 40 | 41 | return name; 42 | 43 | } 44 | 45 | 46 | virtual const String & Identifier () const noexcept override { 47 | 48 | return identifier; 49 | 50 | } 51 | 52 | 53 | virtual const String & Help () const noexcept override { 54 | 55 | return help; 56 | 57 | } 58 | 59 | 60 | virtual void Install () override { 61 | 62 | Information::Get().Add(this); 63 | 64 | } 65 | 66 | 67 | virtual void Execute (ChatMessage & message) const override { 68 | 69 | auto info=Server::Get().Loader().GetInfo(); 70 | 71 | std::sort( 72 | info.Modules.begin(), 73 | info.Modules.end(), 74 | [] (const ModuleInfo & a, const ModuleInfo & b) { return a.Mod->Name()Name(); } 75 | ); 76 | 77 | message << ChatStyle::Bold 78 | << loader_banner 79 | << Newline 80 | << num_modules 81 | << ChatFormat::Pop 82 | << info.Modules.Count() 83 | << Newline 84 | << ChatStyle::Bold 85 | << modules_dir 86 | << ChatFormat::Pop 87 | << info.Directory 88 | << Newline 89 | << ChatStyle::Bold 90 | << mods_banner 91 | << ChatFormat::Pop; 92 | 93 | for (const auto & module : info.Modules) { 94 | 95 | message << Newline 96 | << ChatStyle::Bold 97 | << module.Mod->Name() 98 | << info_separator 99 | << ChatFormat::Pop 100 | << file_label 101 | << module.Filename 102 | << data_separator 103 | << priority_label 104 | << module.Mod->Priority(); 105 | 106 | } 107 | 108 | } 109 | 110 | 111 | }; 112 | 113 | 114 | static Nullable module; 115 | 116 | 117 | extern "C" { 118 | 119 | 120 | Module * Load () { 121 | 122 | if (module.IsNull()) module.Construct(); 123 | 124 | return &(*module); 125 | 126 | } 127 | 128 | 129 | void Unload () { 130 | 131 | module.Destroy(); 132 | 133 | } 134 | 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/mysql_data_provider/prepared_statement.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | namespace MCPP { 7 | 8 | 9 | namespace MySQL { 10 | 11 | 12 | void PreparedStatement::destroy () noexcept { 13 | 14 | if (handle!=nullptr) { 15 | 16 | mysql_stmt_close(handle); 17 | 18 | handle=nullptr; 19 | 20 | } 21 | 22 | } 23 | 24 | 25 | static const char * stmt_allocate="Could not allocate prepared statement"; 26 | 27 | 28 | PreparedStatement::PreparedStatement (Connection & conn, const String & text) { 29 | 30 | // Allocate a statement handle 31 | if ((handle=mysql_stmt_init(conn))==nullptr) throw std::runtime_error(stmt_allocate); 32 | 33 | // Clean up the handle if something 34 | // goes wrong 35 | try { 36 | 37 | // Prepare the statement 38 | auto c_str=text.ToCString(); 39 | if (mysql_stmt_prepare( 40 | handle, 41 | c_str.begin(), 42 | safe_cast(c_str.Count()) 43 | )!=0) Raise(); 44 | 45 | } catch (...) { 46 | 47 | destroy(); 48 | 49 | throw; 50 | 51 | } 52 | 53 | } 54 | 55 | 56 | PreparedStatement::PreparedStatement (PreparedStatement && other) noexcept : handle(other.handle) { 57 | 58 | other.handle=nullptr; 59 | 60 | } 61 | 62 | 63 | PreparedStatement & PreparedStatement::operator = (PreparedStatement && other) noexcept { 64 | 65 | if (&other!=this) { 66 | 67 | destroy(); 68 | 69 | handle=other.handle; 70 | other.handle=nullptr; 71 | 72 | } 73 | 74 | return *this; 75 | 76 | } 77 | 78 | 79 | PreparedStatement::~PreparedStatement () noexcept { 80 | 81 | destroy(); 82 | 83 | } 84 | 85 | 86 | static const char * generic_error="MySQL prepared statement error"; 87 | 88 | 89 | void PreparedStatement::Raise () { 90 | 91 | std::string str; 92 | try { 93 | 94 | str=mysql_stmt_error(handle); 95 | 96 | } catch (...) { 97 | 98 | throw std::runtime_error(generic_error); 99 | 100 | } 101 | 102 | throw std::runtime_error(std::move(str)); 103 | 104 | } 105 | 106 | 107 | void PreparedStatement::Execute () { 108 | 109 | if (mysql_stmt_execute(handle)!=0) Raise(); 110 | 111 | } 112 | 113 | 114 | bool PreparedStatement::Fetch () { 115 | 116 | switch (mysql_stmt_fetch(handle)) { 117 | 118 | default: 119 | return true; 120 | case MYSQL_NO_DATA: 121 | return false; 122 | case 1: 123 | Raise(); 124 | 125 | } 126 | 127 | } 128 | 129 | 130 | void PreparedStatement::Fetch (MYSQL_BIND & bind, Word column) { 131 | 132 | if (mysql_stmt_fetch_column( 133 | handle, 134 | &bind, 135 | safe_cast(column), 136 | 0 137 | )!=0) Raise(); 138 | 139 | } 140 | 141 | 142 | void PreparedStatement::Complete () { 143 | 144 | while (Fetch()); 145 | 146 | } 147 | 148 | 149 | } 150 | 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/info/handler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace MCPP; 10 | 11 | 12 | static const String name("Connection Handler Information"); 13 | static const Word priority=1; 14 | static const String identifier("handler"); 15 | static const String help("Displays information about the server's connection handler."); 16 | static const String label_separator(": "); 17 | 18 | 19 | static const String handler_banner("MAIN SERVER CONNECTION HANDLER:"); 20 | static const String sent_label("Bytes Sent"); 21 | static const String received_label("Bytes Received"); 22 | static const String outgoing_label("Successful Outgoing Connections"); 23 | static const String incoming_label("Successful Incoming Connections"); 24 | static const String accepted_label("Connections Accepted"); 25 | static const String disconnected_label("Connections Terminated"); 26 | static const String listening_label("Listening Sockets"); 27 | static const String connected_label("Connected Sockets"); 28 | static const String workers_label("Number of Worker Threads"); 29 | 30 | 31 | class HandlerInfo : public Module, public InformationProvider { 32 | 33 | 34 | private: 35 | 36 | 37 | template 38 | static void line (ChatMessage & message, const String & label, const T & data) { 39 | 40 | message << Newline 41 | << ChatStyle::Bold 42 | << label 43 | << label_separator 44 | << ChatFormat::Pop 45 | << data; 46 | 47 | } 48 | 49 | 50 | public: 51 | 52 | 53 | virtual Word Priority () const noexcept override { 54 | 55 | return priority; 56 | 57 | } 58 | 59 | 60 | virtual const String & Name () const noexcept override { 61 | 62 | return name; 63 | 64 | } 65 | 66 | 67 | virtual void Install () override { 68 | 69 | Information::Get().Add(this); 70 | 71 | } 72 | 73 | 74 | virtual const String & Identifier () const noexcept override { 75 | 76 | return identifier; 77 | 78 | } 79 | 80 | 81 | virtual const String & Help () const noexcept override { 82 | 83 | return help; 84 | 85 | } 86 | 87 | 88 | virtual void Execute (ChatMessage & message) const override { 89 | 90 | // Get data about the connection handler 91 | auto info=Server::Get().Handler().GetInfo(); 92 | 93 | message << ChatStyle::Bold 94 | << handler_banner 95 | << ChatFormat::Pop; 96 | 97 | line(message,sent_label,info.Sent); 98 | line(message,received_label,info.Received); 99 | line(message,accepted_label,info.Accepted); 100 | line(message,disconnected_label,info.Disconnected); 101 | line(message,incoming_label,info.Incoming); 102 | line(message,outgoing_label,info.Outgoing); 103 | line(message,listening_label,info.Listening); 104 | line(message,connected_label,info.Connected); 105 | line(message,workers_label,info.Workers); 106 | 107 | } 108 | 109 | 110 | }; 111 | 112 | 113 | INSTALL_MODULE(HandlerInfo) 114 | -------------------------------------------------------------------------------- /src/world/add_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | namespace MCPP { 5 | 6 | 7 | void World::Add (SmartPointer client, ColumnID id, bool async) { 8 | 9 | auto column=get_column(id); 10 | 11 | try { 12 | 13 | // Wait until column is populated, or 14 | // see if column will eventually enter 15 | // desired state 16 | if (!( 17 | async 18 | ? column->Check(ColumnState::Populated) 19 | : column->WaitUntil(ColumnState::Populated) 20 | )) process(*column); 21 | 22 | clients_lock.Execute([&] () { 23 | 24 | auto iter=clients.find(client); 25 | 26 | if (iter==clients.end()) { 27 | 28 | auto pair=clients.emplace( 29 | client, 30 | std::unordered_set() 31 | ); 32 | 33 | try { 34 | 35 | pair.first->second.insert(id); 36 | 37 | } catch (...) { 38 | 39 | clients.erase(client); 40 | 41 | throw; 42 | 43 | } 44 | 45 | } else if (iter->second.count(id)!=0) { 46 | 47 | // Client already has column, abort 48 | return; 49 | 50 | } else { 51 | 52 | iter->second.insert(id); 53 | 54 | } 55 | 56 | try { 57 | 58 | column->AddPlayer(client); 59 | 60 | } catch (...) { 61 | 62 | clients.find(client)->second.erase(id); 63 | 64 | throw; 65 | 66 | } 67 | 68 | }); 69 | 70 | } catch (...) { 71 | 72 | column->EndInterest(); 73 | 74 | throw; 75 | 76 | } 77 | 78 | column->EndInterest(); 79 | 80 | } 81 | 82 | 83 | void World::Remove (SmartPointer client, ColumnID id, bool force) { 84 | 85 | if (clients_lock.Execute([&] () { 86 | 87 | auto iter=clients.find(client); 88 | 89 | if (iter==clients.end()) return false; 90 | 91 | return iter->second.erase(id)!=0; 92 | 93 | })) { 94 | 95 | auto column=get_column(id); 96 | 97 | try { 98 | 99 | column->RemovePlayer( 100 | std::move(client), 101 | force 102 | ); 103 | 104 | } catch (...) { 105 | 106 | column->EndInterest(); 107 | 108 | throw; 109 | 110 | } 111 | 112 | column->EndInterest(); 113 | 114 | } 115 | 116 | } 117 | 118 | 119 | void World::Remove (SmartPointer client, bool force) { 120 | 121 | std::unordered_set set; 122 | 123 | clients_lock.Execute([&] () { 124 | 125 | auto iter=clients.find(client); 126 | 127 | if (iter!=clients.end()) { 128 | 129 | set=std::move(iter->second); 130 | 131 | clients.erase(iter); 132 | 133 | } 134 | 135 | }); 136 | 137 | for (auto id : set) { 138 | 139 | auto column=get_column(id); 140 | 141 | try { 142 | 143 | column->RemovePlayer( 144 | client, 145 | force 146 | ); 147 | 148 | } catch (...) { 149 | 150 | column->EndInterest(); 151 | 152 | throw; 153 | 154 | } 155 | 156 | column->EndInterest(); 157 | 158 | } 159 | 160 | } 161 | 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/world/maintenance.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace MCPP { 6 | 7 | 8 | static const String maintenance_error("Error during world maintenance"); 9 | static const String end_maintenance("Finished world maintenance, took {0}ns, saved {1}, unloaded {2}"); 10 | static const String unload("Unloaded column {0}"); 11 | 12 | 13 | void World::maintenance () { 14 | 15 | auto & server=Server::Get(); 16 | 17 | bool is_verbose=server.IsVerbose(verbose); 18 | Word this_saved=0; 19 | Word this_unloaded=0; 20 | 21 | // Start maintenance cycle timer 22 | Timer timer(Timer::CreateAndStart()); 23 | 24 | // Get a list of all the loaded 25 | // columns. 26 | // 27 | // If we maintain the lock we'll 28 | // hold all other threads up, but 29 | // if we dispatch an asynchronous 30 | // callback per worker we could wind 31 | // up clogging the thread pool 32 | // waiting for synchronous I/O. 33 | Vector columns; 34 | lock.Execute([&] () { 35 | 36 | columns=Vector(world.size()); 37 | 38 | for (auto & pair : world) columns.Add(pair.second.get()); 39 | 40 | }); 41 | 42 | maintenance_lock.Execute([&] () { 43 | 44 | // Loop and perform maintenance on 45 | // each column 46 | for (auto column : columns) { 47 | 48 | // Save 49 | if (save(*column)) ++this_saved; 50 | 51 | // See if we can unload 52 | 53 | bool did_unload=false; 54 | 55 | column->Acquire(); 56 | 57 | // If we unload, we keep the 58 | // pointer here, so that it's 59 | // not cleaned up before 60 | // we released the lock 61 | std::unique_ptr extend_lifetime; 62 | 63 | if (column->CanUnload()) { 64 | 65 | lock.Execute([&] () { 66 | 67 | // Interest could have been acquired 68 | // between checking and acquiring 69 | // the lock, so check again 70 | if (column->CanUnload()) { 71 | 72 | did_unload=true; 73 | 74 | auto iter=world.find(column->ID()); 75 | 76 | extend_lifetime=std::move(iter->second); 77 | 78 | world.erase(iter); 79 | 80 | } 81 | 82 | }); 83 | 84 | } 85 | 86 | column->Release(); 87 | 88 | if (did_unload) { 89 | 90 | ++unloaded; 91 | ++this_unloaded; 92 | 93 | // TODO: Fire event 94 | 95 | if (is_verbose) server.WriteLog( 96 | String::Format( 97 | unload, 98 | column->ToString() 99 | ), 100 | Service::LogType::Debug 101 | ); 102 | 103 | } 104 | 105 | } 106 | 107 | }); 108 | 109 | auto elapsed=timer.ElapsedNanoseconds(); 110 | maintenance_time+=elapsed; 111 | ++maintenances; 112 | 113 | // Log if applicable 114 | if (is_verbose) server.WriteLog( 115 | String::Format( 116 | end_maintenance, 117 | elapsed, 118 | this_saved, 119 | this_unloaded 120 | ), 121 | Service::LogType::Debug 122 | ); 123 | 124 | } 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/kick/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace MCPP; 10 | 11 | 12 | static const String name("Kick Command"); 13 | static const Word priority=1; 14 | static const String identifier("kick"); 15 | static const String summary("Kicks a player."); 16 | static const String help( 17 | "Syntax: /kick \n" 18 | "Kicks the player whose name is given, disconnecting them from the server." 19 | ); 20 | static const String reason("Kicked"); 21 | static const String by(" by {0}"); 22 | 23 | 24 | class Kick : public Module, public Command { 25 | 26 | 27 | public: 28 | 29 | 30 | virtual const String & Name () const noexcept override { 31 | 32 | return name; 33 | 34 | } 35 | 36 | 37 | virtual Word Priority () const noexcept override { 38 | 39 | return priority; 40 | 41 | } 42 | 43 | 44 | virtual void Install () override { 45 | 46 | Commands::Get().Add( 47 | identifier, 48 | this 49 | ); 50 | 51 | } 52 | 53 | 54 | virtual void Summary (const String &, ChatMessage & message) override { 55 | 56 | message << summary; 57 | 58 | } 59 | 60 | 61 | virtual void Help (const String &, ChatMessage & message) override { 62 | 63 | message << help; 64 | 65 | } 66 | 67 | 68 | virtual Vector AutoComplete (const CommandEvent & event) override { 69 | 70 | if (event.Arguments.Count()!=1) return Vector(); 71 | 72 | auto clients=Server::Get().Clients.Get( 73 | event.Arguments[0], 74 | ClientSearch::Begin 75 | ); 76 | 77 | auto retr=Vector(clients.Count()); 78 | for (auto & client : clients) retr.Add( 79 | client->GetUsername() 80 | ); 81 | 82 | return retr; 83 | 84 | } 85 | 86 | 87 | virtual bool Check (const CommandEvent & event) override { 88 | 89 | return event.Issuer.IsNull() ? true : Permissions::Get().GetUser(event.Issuer).Check(event.Identifier); 90 | 91 | } 92 | 93 | 94 | virtual CommandResult Execute (CommandEvent event) override { 95 | 96 | CommandResult retr; 97 | 98 | // There needs to be an argument 99 | if (event.Arguments.Count()==0) { 100 | 101 | retr.Status=CommandStatus::SyntaxError; 102 | 103 | return retr; 104 | 105 | } 106 | 107 | // Create the reason 108 | String reason(::reason); 109 | // If this command was issued by 110 | // a connected user, we report who 111 | // it is 112 | if (!event.Issuer.IsNull()) reason << String::Format( 113 | by, 114 | event.Issuer->GetUsername() 115 | ); 116 | 117 | // Get all clients with the given 118 | // username. We use the raw arguments 119 | // so that we get the exact name that 120 | // the issuer specified 121 | for (auto & client : Server::Get().Clients.Get(event.RawArguments)) client->Disconnect(reason); 122 | 123 | return retr; 124 | 125 | } 126 | 127 | 128 | }; 129 | 130 | 131 | INSTALL_MODULE(Kick) 132 | -------------------------------------------------------------------------------- /src/client_list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace MCPP { 6 | 7 | 8 | SmartPointer ClientList::operator [] (const Connection & conn) { 9 | 10 | return map_lock.Read([&] () mutable { return map.at(&conn); }); 11 | 12 | } 13 | 14 | 15 | void ClientList::Add (SmartPointer client) { 16 | 17 | map_lock.Write([&] () mutable { 18 | 19 | auto conn=client->conn; 20 | 21 | map.emplace( 22 | static_cast(conn), 23 | std::move(client) 24 | ); 25 | 26 | }); 27 | 28 | } 29 | 30 | 31 | void ClientList::Remove (const Connection & conn) { 32 | 33 | map_lock.Write([&] () mutable { map.erase(&conn); }); 34 | 35 | } 36 | 37 | 38 | Word ClientList::Count () const noexcept { 39 | 40 | return map_lock.Read([&] () { return map.size(); }); 41 | 42 | } 43 | 44 | 45 | Word ClientList::AuthenticatedCount () const noexcept { 46 | 47 | return map_lock.Read([&] () { 48 | 49 | Word retr=0; 50 | 51 | for (const auto & pair : map) if (pair.second->GetState()==ProtocolState::Play) ++retr; 52 | 53 | return retr; 54 | 55 | }); 56 | 57 | } 58 | 59 | 60 | void ClientList::Clear () noexcept { 61 | 62 | map_lock.Write([&] () mutable { map.clear(); }); 63 | 64 | } 65 | 66 | 67 | ClientListIterator ClientList::begin () noexcept { 68 | 69 | return map_lock.Read([&] () { return ClientListIterator(this,map.begin()); }); 70 | 71 | } 72 | 73 | 74 | ClientListIterator ClientList::end () noexcept { 75 | 76 | return map_lock.Read([&] () { return ClientListIterator(this,map.end()); }); 77 | 78 | } 79 | 80 | 81 | Vector> ClientList::Get (const String & str, ClientSearch type) const { 82 | 83 | Vector> retr; 84 | 85 | // Choose the regular expression 86 | // pattern that will be used 87 | String pattern; 88 | String regex_str(Regex::Escape(str)); 89 | switch (type) { 90 | 91 | case ClientSearch::Exact: 92 | default: 93 | pattern=String::Format( 94 | "^{0}$", 95 | regex_str 96 | ); 97 | break; 98 | 99 | case ClientSearch::Begin: 100 | pattern=String::Format( 101 | "^{0}", 102 | regex_str 103 | ); 104 | break; 105 | 106 | case ClientSearch::End: 107 | pattern=String::Format( 108 | "{0}$", 109 | regex_str 110 | ); 111 | break; 112 | 113 | case ClientSearch::Match: 114 | pattern=std::move(regex_str); 115 | break; 116 | 117 | } 118 | 119 | // Create the regular expression 120 | Regex regex( 121 | pattern, 122 | RegexOptions().SetIgnoreCase() 123 | ); 124 | 125 | // Loop and search clients 126 | for (const auto & client : *const_cast(this)) if ( 127 | // Only match authenticated clients 128 | // (the only kind who have usernames 129 | // anyway) 130 | (client->GetState()==ProtocolState::Play) && 131 | // Attempt to match against regular 132 | // expression 133 | regex.IsMatch(client->GetUsername()) 134 | ) retr.Add(client); 135 | 136 | return retr; 137 | 138 | } 139 | 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/player/update_position.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | namespace MCPP { 9 | 10 | 11 | void Players::update_position (SmartPointer & player, std::function then) { 12 | 13 | // Create a list of columns 14 | // which must be sent to the player 15 | Vector add; 16 | // Create a list of columns which 17 | // must be removed from the player 18 | Vector remove; 19 | 20 | player->Lock.Execute([&] () { 21 | 22 | // Determine which column 23 | // the player is currently 24 | // in 25 | auto curr=ColumnID::GetContaining( 26 | player->Position.X, 27 | player->Position.Z, 28 | player->Dimension 29 | ); 30 | 31 | // Determine bounds for 32 | // caching 33 | Int32 x_lower=curr.X-cache_distance; 34 | Int32 x_upper=curr.X+cache_distance; 35 | Int32 z_lower=curr.Z-cache_distance; 36 | Int32 z_upper=curr.Z+cache_distance; 37 | 38 | // Which columns do we have 39 | // to remove? 40 | for (auto & id : player->Columns) if ( 41 | (id.Dimension!=player->Dimension) || 42 | (id.Xx_upper) || 44 | (id.Zz_upper) 46 | ) remove.Add(id); 47 | 48 | for (auto & id : remove) player->Columns.erase(id); 49 | 50 | // Which columns do we have to add? 51 | for ( 52 | Int32 x=curr.X-view_distance; 53 | x(view_distance)+1; 54 | ++x 55 | ) for ( 56 | Int32 z=curr.Z-view_distance; 57 | z(view_distance)+1; 58 | ++z 59 | ) { 60 | 61 | ColumnID id{ 62 | x, 63 | z, 64 | player->Dimension 65 | }; 66 | 67 | if (player->Columns.count(id)==0) { 68 | 69 | // We must add this column 70 | 71 | add.Add(id); 72 | 73 | player->Columns.insert(id); 74 | 75 | } 76 | 77 | } 78 | 79 | }); 80 | 81 | // Perform adds and removes 82 | 83 | for (auto & id : remove) World::Get().Remove( 84 | player->Conn, 85 | id 86 | ); 87 | 88 | // Scope guard to enable continuation 89 | MultiScopeGuard sg; 90 | 91 | bool async; 92 | if (then) { 93 | 94 | sg=MultiScopeGuard( 95 | std::move(then), 96 | std::function(), // Nothing 97 | [] () { Server::Get().Panic(); } 98 | ); 99 | 100 | async=false; 101 | 102 | } else { 103 | 104 | async=true; 105 | 106 | } 107 | 108 | for (auto & id : add) try { 109 | 110 | cm->Enqueue( 111 | [=] (MultiScopeGuard) { 112 | 113 | try { 114 | 115 | World::Get().Add(player->Conn,id,async); 116 | 117 | } catch (...) { 118 | 119 | Server::Get().Panic( 120 | std::current_exception() 121 | ); 122 | 123 | } 124 | 125 | }, 126 | sg 127 | ); 128 | 129 | } catch (...) { 130 | 131 | try { 132 | 133 | Server::Get().Panic(); 134 | 135 | } catch (...) { } 136 | 137 | throw; 138 | 139 | } 140 | 141 | } 142 | 143 | 144 | } 145 | -------------------------------------------------------------------------------- /src/chat/chat_misc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | namespace MCPP { 7 | 8 | 9 | // 10 | // CHAT TOKEN 11 | // 12 | 13 | 14 | ChatToken::ChatToken (String segment) noexcept : Type(ChatFormat::Segment), Segment(std::move(segment)) { } 15 | 16 | 17 | ChatToken::ChatToken (ChatStyle style) noexcept : Type(ChatFormat::Push), Style(style) { } 18 | 19 | 20 | ChatToken::ChatToken (ChatFormat type) noexcept : Type(type) { 21 | 22 | if (type==ChatFormat::Segment) new (&Segment) String (); 23 | 24 | } 25 | 26 | 27 | inline void ChatToken::destroy () noexcept { 28 | 29 | if (Type==ChatFormat::Segment) Segment.~String(); 30 | 31 | } 32 | 33 | 34 | inline void ChatToken::copy (const ChatToken & other) { 35 | 36 | if (Type==ChatFormat::Segment) { 37 | 38 | new (&Segment) String (other.Segment); 39 | 40 | } else if (Type==ChatFormat::Push) { 41 | 42 | Style=other.Style; 43 | 44 | } 45 | 46 | } 47 | 48 | 49 | inline void ChatToken::move (ChatToken && other) noexcept { 50 | 51 | if (Type==ChatFormat::Segment) { 52 | 53 | new (&Segment) String (std::move(other.Segment)); 54 | 55 | } else { 56 | 57 | copy(other); 58 | 59 | } 60 | 61 | } 62 | 63 | 64 | ChatToken::~ChatToken () noexcept { 65 | 66 | destroy(); 67 | 68 | } 69 | 70 | 71 | ChatToken::ChatToken (const ChatToken & other) : Type(other.Type) { 72 | 73 | copy(other); 74 | 75 | } 76 | 77 | 78 | ChatToken::ChatToken (ChatToken && other) noexcept : Type(other.Type) { 79 | 80 | move(std::move(other)); 81 | 82 | } 83 | 84 | 85 | ChatToken & ChatToken::operator = (const ChatToken & other) { 86 | 87 | if (&other!=this) { 88 | 89 | Type=other.Type; 90 | 91 | destroy(); 92 | 93 | copy(other); 94 | 95 | } 96 | 97 | return *this; 98 | 99 | } 100 | 101 | 102 | ChatToken & ChatToken::operator = (ChatToken && other) noexcept { 103 | 104 | if (&other!=this) { 105 | 106 | Type=other.Type; 107 | 108 | destroy(); 109 | 110 | move(std::move(other)); 111 | 112 | } 113 | 114 | return *this; 115 | 116 | } 117 | 118 | 119 | // 120 | // CHAT MESSAGE 121 | // 122 | 123 | 124 | ChatMessage::ChatMessage (String message) : Echo(false) { 125 | 126 | Message.EmplaceBack(ChatFormat::Label); 127 | Message.EmplaceBack(ChatFormat::LabelSeparator); 128 | Message.EmplaceBack(std::move(message)); 129 | 130 | } 131 | 132 | 133 | ChatMessage::ChatMessage (SmartPointer from, String message) : From(std::move(from)), Echo(false) { 134 | 135 | Message.EmplaceBack(ChatFormat::Label); 136 | Message.EmplaceBack(ChatFormat::LabelSeparator); 137 | Message.EmplaceBack(std::move(message)); 138 | 139 | } 140 | 141 | 142 | ChatMessage::ChatMessage (SmartPointer from, String to, String message) : From(std::move(from)), Echo(false) { 143 | 144 | To.EmplaceBack(std::move(to)); 145 | 146 | Message.EmplaceBack(ChatStyle::Pink); 147 | Message.EmplaceBack(ChatFormat::Label); 148 | Message.EmplaceBack(ChatFormat::LabelSeparator); 149 | Message.EmplaceBack(std::move(message)); 150 | 151 | } 152 | 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/network/posix/error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | namespace MCPP { 8 | 9 | 10 | namespace NetworkImpl { 11 | 12 | 13 | static std::system_error get_exception () noexcept { 14 | 15 | return std::system_error( 16 | std::error_code( 17 | errno, 18 | std::system_category() 19 | ) 20 | ); 21 | 22 | } 23 | 24 | 25 | std::exception_ptr GetException () noexcept { 26 | 27 | return std::make_exception_ptr(get_exception()); 28 | 29 | } 30 | 31 | 32 | void Raise () { 33 | 34 | throw get_exception(); 35 | 36 | } 37 | 38 | 39 | String GetErrorMessage () { 40 | 41 | Vector buffer; 42 | 43 | // Loop until the entire error 44 | // message has been extracted, 45 | // or we fail 46 | int result; 47 | do { 48 | 49 | // Get more space 50 | buffer.SetCapacity(); 51 | 52 | // Attempt to extract the error 53 | // message 54 | result=strerror_r( 55 | errno, 56 | reinterpret_cast(buffer.begin()), 57 | buffer.Capacity() 58 | ); 59 | 60 | } while (result==ERANGE); 61 | 62 | // Did we fail? 63 | if (result!=0) { 64 | 65 | errno=result; 66 | 67 | Raise(); 68 | 69 | } 70 | 71 | // Decode and return 72 | return UTF8().Decode( 73 | buffer.begin(), 74 | buffer.begin()+std::strlen( 75 | reinterpret_cast(buffer.begin()) 76 | ) 77 | ); 78 | 79 | } 80 | 81 | 82 | bool WouldBlock () noexcept { 83 | 84 | return ( 85 | (errno==EAGAIN) 86 | #if EAGAIN!=EWOULDBLOCK 87 | || (errno==EWOULDBLOCK) 88 | #endif 89 | ); 90 | 91 | } 92 | 93 | 94 | bool WasInterrupted () noexcept { 95 | 96 | return errno==EINTR; 97 | 98 | } 99 | 100 | 101 | ErrorType GetSocketError (FDType fd) { 102 | 103 | ErrorType code; 104 | socklen_t code_len=sizeof(code); 105 | if (getsockopt( 106 | fd, 107 | SOL_SOCKET, 108 | SO_ERROR, 109 | &code, 110 | &code_len 111 | )==-1) Raise(); 112 | 113 | return code; 114 | 115 | } 116 | 117 | 118 | void SetError (ErrorType code) noexcept { 119 | 120 | errno=code; 121 | 122 | } 123 | 124 | 125 | Error::Error () noexcept : set(false) { } 126 | 127 | 128 | Error::operator bool () const noexcept { 129 | 130 | return lock.Execute([&] () { return set; }); 131 | 132 | } 133 | 134 | 135 | void Error::Capture () { 136 | 137 | lock.Execute([&] () { 138 | 139 | // If an error has already been captured, 140 | // do nothing 141 | if (set) return; 142 | 143 | // Otherwise, capture 144 | Message=GetErrorMessage(); 145 | Exception=GetException(); 146 | 147 | set=true; 148 | 149 | }); 150 | 151 | } 152 | 153 | 154 | void Error::Set (String message) noexcept { 155 | 156 | lock.Execute([&] () { 157 | 158 | // Ignore if already set 159 | if (set) return; 160 | 161 | Message=std::move(message); 162 | 163 | set=true; 164 | 165 | }); 166 | 167 | } 168 | 169 | 170 | } 171 | 172 | 173 | } 174 | -------------------------------------------------------------------------------- /src/network/linux/notifier.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | namespace MCPP { 8 | 9 | 10 | namespace NetworkImpl { 11 | 12 | 13 | Word Notifier::wait (void * ptr, Word len) { 14 | 15 | // Convert the len parameter 16 | // into something safe for epoll 17 | auto os_len=safe_cast(len); 18 | // Loop until we extract some 19 | // events 20 | for (;;) { 21 | 22 | auto result=epoll_wait( 23 | handle, 24 | reinterpret_cast(ptr), 25 | os_len, 26 | -1 // INFINITE 27 | ); 28 | 29 | // Was there an error? 30 | if (result==-1) { 31 | 32 | // Interruption is not really 33 | // an error, we just ignore it 34 | // and repeat 35 | if (WasInterrupted()) continue; 36 | 37 | // Otherwise it was an actual 38 | // error, throw 39 | Raise(); 40 | 41 | } 42 | 43 | // Were no events extracted? 44 | if (result==0) continue; 45 | 46 | // Some events were extracted 47 | // 48 | // This can't be an unsafe cast 49 | // because above we cast the MAXIMUM 50 | // that this value can be from a 51 | // Word to an int, and that was 52 | // successful 53 | return static_cast(result); 54 | 55 | } 56 | 57 | } 58 | 59 | 60 | // Old versions of Linux need a size, modern 61 | // versions require that it be non-zero, here's 62 | // a sensible default 63 | static const int epoll_size=256; 64 | 65 | 66 | Notifier::Notifier () { 67 | 68 | // Create an epoll FD 69 | if ((handle=epoll_create(epoll_size))==-1) Raise(); 70 | 71 | } 72 | 73 | 74 | Notifier::~Notifier () noexcept { 75 | 76 | close(handle); 77 | 78 | } 79 | 80 | 81 | static struct epoll_event get_event (FDType fd) noexcept { 82 | 83 | struct epoll_event retr; 84 | std::memset(&retr,0,sizeof(retr)); 85 | retr.data.fd=fd; 86 | 87 | return retr; 88 | 89 | } 90 | 91 | 92 | void Notifier::Attach (FDType fd) { 93 | 94 | auto event=get_event(fd); 95 | if (epoll_ctl( 96 | handle, 97 | EPOLL_CTL_ADD, 98 | fd, 99 | &event 100 | )==-1) Raise(); 101 | 102 | } 103 | 104 | 105 | void Notifier::Update (FDType fd, bool read, bool write) { 106 | 107 | auto event=get_event(fd); 108 | event.events=EPOLLET|EPOLLERR|EPOLLHUP; 109 | if (read) event.events|=EPOLLIN; 110 | if (write) event.events|=EPOLLOUT; 111 | if (epoll_ctl( 112 | handle, 113 | EPOLL_CTL_MOD, 114 | fd, 115 | &event 116 | )==-1) Raise(); 117 | 118 | } 119 | 120 | 121 | void Notifier::Detach (FDType fd) { 122 | 123 | // In newer Linuxes, this is unnecessary, 124 | // but older Linuxes require a non-null 125 | // event pointer, so, for legacy support, 126 | // we do this 127 | auto event=get_event(fd); 128 | if (epoll_ctl( 129 | handle, 130 | EPOLL_CTL_DEL, 131 | fd, 132 | &event 133 | )==-1) Raise(); 134 | 135 | } 136 | 137 | 138 | Word Notifier::Wait (Notification & n) { 139 | 140 | return wait(&n,1); 141 | 142 | } 143 | 144 | 145 | } 146 | 147 | 148 | } 149 | -------------------------------------------------------------------------------- /include/save/save.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | namespace MCPP { 16 | 17 | 18 | /** 19 | * Encapsulates statistics and information 20 | * about a SaveManager instance. 21 | */ 22 | class SaveManagerInfo { 23 | 24 | 25 | public: 26 | 27 | 28 | /** 29 | * The number of milliseconds between 30 | * automatic save operations. 31 | */ 32 | Word Frequency; 33 | /** 34 | * Whether the save loop is currently 35 | * paused or not. 36 | */ 37 | bool Paused; 38 | /** 39 | * The number of save operations that 40 | * have been performed. 41 | */ 42 | Word Count; 43 | /** 44 | * The total amount of time that has been 45 | * spent saving. 46 | */ 47 | UInt64 Elapsed; 48 | 49 | 50 | }; 51 | 52 | 53 | /** 54 | * Provides a facility for other modules to 55 | * periodically save data, in addition to performing 56 | * a final save as the server is shutdown. 57 | */ 58 | class SaveManager : public Module { 59 | 60 | 61 | private: 62 | 63 | 64 | Word frequency; 65 | Vector> callbacks; 66 | // Held while a save operation is ongoing 67 | // to prevent other save operations from 68 | // executing at the same time 69 | Mutex lock; 70 | // Whether the save loop is paused or not 71 | std::atomic paused; 72 | 73 | 74 | std::atomic count; 75 | std::atomic elapsed; 76 | 77 | 78 | static bool is_verbose (); 79 | void save (); 80 | void save_loop (); 81 | 82 | 83 | public: 84 | 85 | 86 | /** 87 | * Retrieves a reference to a valid instance 88 | * of this class. 89 | * 90 | * \return 91 | * A reference to an instance of this class. 92 | */ 93 | static SaveManager & Get () noexcept; 94 | 95 | 96 | /** 97 | * \cond 98 | */ 99 | 100 | 101 | SaveManager () noexcept; 102 | 103 | 104 | virtual Word Priority () const noexcept override; 105 | virtual const String & Name () const noexcept override; 106 | virtual void Install () override; 107 | 108 | 109 | /** 110 | * \endcond 111 | */ 112 | 113 | 114 | /** 115 | * Adds a callback, which will be fired periodically 116 | * and before the server shuts down. 117 | * 118 | * \param [in] callback 119 | * The callback to add. 120 | */ 121 | void Add (std::function callback); 122 | 123 | 124 | /** 125 | * Immediately performs a save operation. 126 | */ 127 | void operator () (); 128 | 129 | 130 | /** 131 | * Pauses the save loop. 132 | */ 133 | void Pause () noexcept; 134 | /** 135 | * Resumes the save loop. 136 | */ 137 | void Resume () noexcept; 138 | 139 | 140 | /** 141 | * Retrieves information and statistics about 142 | * the save manager. 143 | * 144 | * \return 145 | * A structure which contains information and 146 | * statistics about the save manager. 147 | */ 148 | SaveManagerInfo GetInfo () const noexcept; 149 | 150 | 151 | }; 152 | 153 | 154 | } 155 | -------------------------------------------------------------------------------- /include/rsa_key.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | 11 | 12 | namespace MCPP { 13 | 14 | 15 | /** 16 | * Encapsulates an RSA public/private 17 | * key pair. 18 | */ 19 | class RSAKey { 20 | 21 | 22 | private: 23 | 24 | 25 | // RSA key pair from OpenSSL 26 | void * key; 27 | 28 | 29 | public: 30 | 31 | 32 | /** 33 | * Creates a new public/private key pair 34 | * suitable for use with the Minecraft 35 | * protocol. 36 | * 37 | * \param [in] seed 38 | * An optional string which shall 39 | * be used to seed the OpenSSL 40 | * pseudo random number generator. 41 | */ 42 | RSAKey (const String & seed=String()); 43 | /** 44 | * Creates a new public key from a 45 | * SubjectPublicKeyInfo, i.e. an 46 | * ASN.1 DER-encoded public key. 47 | * 48 | * Note that functions that depend 49 | * on the private key will fail after 50 | * the object has been constructed 51 | * in this way. 52 | * 53 | * \param [in] pub_key 54 | * The ASN.1 DER-encoded public 55 | * key from which to construct 56 | * this object. 57 | */ 58 | RSAKey (const Vector & pub_key); 59 | /** 60 | * Cleans up this public/private key pair. 61 | */ 62 | ~RSAKey () noexcept; 63 | 64 | 65 | RSAKey (const RSAKey &) = delete; 66 | RSAKey (RSAKey &&) = delete; 67 | RSAKey & operator = (const RSAKey &) = delete; 68 | RSAKey & operator = (RSAKey &&) = delete; 69 | 70 | 71 | /** 72 | * Encrypts a buffer of bytes using the private 73 | * key such that it may be decrypted using 74 | * the public key. 75 | * 76 | * \param [in] buffer 77 | * A buffer of unencrypted bytes. 78 | * 79 | * \return 80 | * A buffer of encrypted bytes. 81 | */ 82 | Vector PrivateEncrypt (const Vector & buffer) const; 83 | /** 84 | * Recovers a message encrypted with the 85 | * public key. 86 | * 87 | * \param [in] buffer 88 | * A buffer of encrypted bytes. 89 | * 90 | * \return 91 | * A buffer of unencrypted bytes. 92 | */ 93 | Vector PrivateDecrypt (const Vector & buffer) const; 94 | /** 95 | * Encrypts a buffer of bytes using the public 96 | * key such that it may be decrypted using 97 | * the private key. 98 | * 99 | * \param [in] buffer 100 | * A buffer of unencrypted bytes. 101 | * 102 | * \return 103 | * A buffer of encrypted bytes. 104 | */ 105 | Vector PublicEncrypt (const Vector & buffer) const; 106 | /** 107 | * Recovers a message encrypted with the 108 | * private key. 109 | * 110 | * \param [in] buffer 111 | * A buffer of encrypted bytes. 112 | * 113 | * \return 114 | * A buffer of unencrypted bytes. 115 | */ 116 | Vector PublicDecrypt (const Vector & buffer) const; 117 | 118 | 119 | /** 120 | * Retrieves the ASN.1 DER-encoded representation 121 | * of the public key. 122 | * 123 | * \return 124 | * The ASN.1, DER-encoded representation of 125 | * the public key. 126 | */ 127 | Vector PublicKey () const; 128 | 129 | 130 | }; 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/mysql_data_provider/factory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace MCPP { 6 | 7 | 8 | // Name of the configuration file 9 | static const String filename("mysql.ini"); 10 | 11 | 12 | static String get_path () { 13 | 14 | return Path::Combine( 15 | Path::GetPath( 16 | File::GetCurrentExecutableFileName() 17 | ), 18 | filename 19 | ); 20 | 21 | } 22 | 23 | 24 | static Nullable get_file_contents () { 25 | 26 | // Get the filename 27 | auto c_str=get_path().ToCString(); 28 | 29 | // Open a binary stream to read the 30 | // file in 31 | std::fstream stream(c_str.begin(),std::ios::in|std::ios::binary); 32 | 33 | Nullable retr; 34 | 35 | // If the file could not be opened, fail 36 | // out 37 | if (!stream) return retr; 38 | 39 | // Extract the entire contents of the file 40 | Vector buffer; 41 | do { 42 | 43 | buffer.SetCapacity(); 44 | 45 | stream.read( 46 | reinterpret_cast(buffer.end()), 47 | buffer.Capacity()-buffer.Count() 48 | ); 49 | 50 | buffer.SetCount( 51 | static_cast( 52 | SafeWord(buffer.Count())+ 53 | SafeWord(stream.gcount()) 54 | ) 55 | ); 56 | 57 | } while (buffer.Capacity()==buffer.Count()); 58 | 59 | // If there was nothing, don't even bother 60 | if (buffer.Count()==0) return retr; 61 | 62 | // Decode 63 | retr.Construct( 64 | UTF8().Decode( 65 | buffer.begin(), 66 | buffer.end() 67 | ) 68 | ); 69 | 70 | return retr; 71 | 72 | } 73 | 74 | 75 | // Default maximum number of connections 76 | // in the connection pool -- 0 which is 77 | // unlimited 78 | static const Word default_pool_max=0; 79 | 80 | 81 | DataProvider * DataProvider::GetDataProvider () { 82 | 83 | // Setup defaults, they'll get replaced 84 | // as we parse 85 | Nullable host; 86 | Nullable username; 87 | Nullable password; 88 | Nullable database; 89 | Nullable port; 90 | Word max=default_pool_max; 91 | 92 | auto contents=get_file_contents(); 93 | 94 | if (!contents.IsNull()) { 95 | 96 | // Grab each line 97 | auto matches=Regex("^.*$",RegexOptions().SetMultiline()).Matches(*contents); 98 | 99 | // Separate out each line, skipping 100 | // lines that are comments 101 | Regex regex("^([^#=][^=]*)=(.*)$"); 102 | 103 | for (auto & m : matches) { 104 | 105 | auto match=regex.Match(m.Value()); 106 | if (match.Success()) { 107 | 108 | auto key=match[1].Value().Trim(); 109 | auto value=match[2].Value().Trim(); 110 | 111 | if (key=="host") host=value; 112 | else if (key=="username") username=value; 113 | else if (key=="password") password=value; 114 | else if (key=="database") database=value; 115 | else if (key=="port") { 116 | 117 | UInt16 temp; 118 | if (value.ToInteger(&temp)) port=temp; 119 | 120 | } else if (key=="pool_max") value.ToInteger(&max); 121 | 122 | } 123 | 124 | } 125 | 126 | } 127 | 128 | return new MySQL::DataProvider( 129 | std::move(host), 130 | std::move(username), 131 | std::move(password), 132 | std::move(database), 133 | std::move(port), 134 | max 135 | ); 136 | 137 | } 138 | 139 | 140 | } 141 | -------------------------------------------------------------------------------- /include/whitelist/whitelist.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | namespace MCPP { 17 | 18 | 19 | /** 20 | * Manages a whitelist, which controls access 21 | * to the server by only permitting those 22 | * explicitly authorized. 23 | */ 24 | class Whitelist : public Module { 25 | 26 | 27 | private: 28 | 29 | 30 | mutable Mutex lock; 31 | std::unordered_set whitelist; 32 | 33 | std::atomic enabled; 34 | 35 | 36 | public: 37 | 38 | 39 | /** 40 | * Retrieves a valid instance of this class. 41 | * 42 | * \return 43 | * A reference to a valid instance of this 44 | * class. 45 | */ 46 | static Whitelist & Get () noexcept; 47 | 48 | 49 | /** 50 | * \cond 51 | */ 52 | 53 | 54 | virtual const String & Name () const noexcept override; 55 | virtual Word Priority () const noexcept override; 56 | virtual void Install () override; 57 | 58 | 59 | /** 60 | * \endcond 61 | */ 62 | 63 | 64 | /** 65 | * Enables the whitelist. 66 | * 67 | * \return 68 | * \em true if the whitelist was 69 | * enabled, \em false if the whitelist 70 | * was already enabled. 71 | */ 72 | bool Enable () noexcept; 73 | /** 74 | * Disables the whitelist. 75 | * 76 | * \return 77 | * \em true if the whitelist was 78 | * disabled, \em false if the whitelist 79 | * was already disabled. 80 | */ 81 | bool Disable () noexcept; 82 | 83 | 84 | /** 85 | * Determines if the whitelist is enabled. 86 | * 87 | * \return 88 | * \em true if the whitelist is enabled, 89 | * \em false otherwise. 90 | */ 91 | bool Enabled () const noexcept; 92 | 93 | 94 | /** 95 | * Adds a user to the whitelist. 96 | * 97 | * \param [in] username 98 | * The username of the user to add 99 | * to the whitelist. 100 | * 101 | * \return 102 | * \em true if \em username was added 103 | * to the whitelist, \em false if 104 | * \em username was already 105 | * whitelisted. 106 | */ 107 | bool Add (String username); 108 | /** 109 | * Removes a user from the whitelist. 110 | * 111 | * \param [in] username 112 | * The username of the user to remove 113 | * from the whitelist. 114 | * 115 | * \return 116 | * \em true if \em username was removed 117 | * from the whitelist, \em false if 118 | * \em username wasn't on the whitelist. 119 | */ 120 | bool Remove (String username); 121 | 122 | 123 | /** 124 | * Checks to see if a user is whitelisted. 125 | * 126 | * \param [in] username 127 | * The username to check. 128 | * 129 | * \return 130 | * \em true if \em username is whitelisted, 131 | * \em false otherwise. 132 | */ 133 | bool Check (String username) const; 134 | 135 | 136 | /** 137 | * Retrieves the whitelist. 138 | * 139 | * \return 140 | * A list of strings containing 141 | * the usernames on the whitelist. 142 | */ 143 | Vector GetInfo () const; 144 | 145 | 146 | 147 | }; 148 | 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/multi_scope_guard.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace MCPP { 6 | 7 | 8 | MultiScopeGuardIndirect::MultiScopeGuardIndirect ( 9 | std::function all, 10 | std::function each, 11 | std::function panic 12 | ) noexcept : ExitAll(std::move(all)), ExitEach(std::move(each)), Panic(std::move(panic)) { 13 | 14 | count=1; 15 | 16 | } 17 | 18 | 19 | void MultiScopeGuardIndirect::Acquire () noexcept { 20 | 21 | ++count; 22 | 23 | } 24 | 25 | 26 | bool MultiScopeGuardIndirect::Release () noexcept { 27 | 28 | return (--count)==0; 29 | 30 | } 31 | 32 | 33 | inline void MultiScopeGuard::destroy () noexcept { 34 | 35 | if (indirect!=nullptr) { 36 | 37 | try { 38 | 39 | if (indirect->ExitEach) indirect->ExitEach(); 40 | 41 | } catch (...) { 42 | 43 | try { 44 | 45 | if (indirect->Panic) indirect->Panic(); 46 | 47 | } catch (...) { } 48 | 49 | } 50 | 51 | if (indirect->Release()) { 52 | 53 | try { 54 | 55 | if (indirect->ExitAll) indirect->ExitAll(); 56 | 57 | } catch (...) { 58 | 59 | try { 60 | 61 | if (indirect->Panic) indirect->Panic(); 62 | 63 | } catch (...) { } 64 | 65 | } 66 | 67 | delete indirect; 68 | 69 | } 70 | 71 | } 72 | 73 | } 74 | 75 | 76 | MultiScopeGuard::MultiScopeGuard () noexcept : indirect(nullptr) { } 77 | 78 | 79 | MultiScopeGuard::MultiScopeGuard (std::function all) : indirect(new MultiScopeGuardIndirect( 80 | std::move(all), 81 | std::function(), 82 | std::function() 83 | )) { } 84 | 85 | 86 | MultiScopeGuard::MultiScopeGuard ( 87 | std::function all, 88 | std::function each 89 | ) : indirect(new MultiScopeGuardIndirect( 90 | std::move(all), 91 | std::move(each), 92 | std::function() 93 | )) { } 94 | 95 | 96 | MultiScopeGuard::MultiScopeGuard ( 97 | std::function all, 98 | std::function each, 99 | std::function panic 100 | ) : indirect(new MultiScopeGuardIndirect( 101 | std::move(all), 102 | std::move(each), 103 | std::move(panic) 104 | )) { } 105 | 106 | 107 | MultiScopeGuard::MultiScopeGuard (const MultiScopeGuard & other) noexcept : indirect(other.indirect) { 108 | 109 | if (indirect!=nullptr) indirect->Acquire(); 110 | 111 | } 112 | 113 | 114 | MultiScopeGuard::MultiScopeGuard (MultiScopeGuard && other) noexcept : indirect(other.indirect) { 115 | 116 | other.indirect=nullptr; 117 | 118 | } 119 | 120 | 121 | MultiScopeGuard & MultiScopeGuard::operator = (const MultiScopeGuard & other) noexcept { 122 | 123 | if (&other!=this) { 124 | 125 | destroy(); 126 | 127 | indirect=other.indirect; 128 | if (indirect!=nullptr) indirect->Acquire(); 129 | 130 | } 131 | 132 | return *this; 133 | 134 | } 135 | 136 | 137 | MultiScopeGuard & MultiScopeGuard::operator = (MultiScopeGuard && other) noexcept { 138 | 139 | if (&other!=this) { 140 | 141 | destroy(); 142 | 143 | indirect=other.indirect; 144 | other.indirect=nullptr; 145 | 146 | } 147 | 148 | return *this; 149 | 150 | } 151 | 152 | 153 | MultiScopeGuard::~MultiScopeGuard () noexcept { 154 | 155 | destroy(); 156 | 157 | } 158 | 159 | 160 | } 161 | -------------------------------------------------------------------------------- /src/player/event_handlers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | namespace MCPP { 9 | 10 | 11 | void Players::on_connect (SmartPointer client) { 12 | 13 | players_lock.Write([&] () { 14 | 15 | players.emplace( 16 | std::move(client), 17 | SmartPointer() 18 | ); 19 | 20 | }); 21 | 22 | } 23 | 24 | 25 | void Players::on_disconnect (SmartPointer client, const String &) { 26 | 27 | players_lock.Write([&] () { players.erase(client); }); 28 | 29 | } 30 | 31 | 32 | void Players::on_login (SmartPointer client) { 33 | 34 | // Get spawn coordinates 35 | 36 | Int32 spawn_x; 37 | Int32 spawn_y; 38 | Int32 spawn_z; 39 | SByte dimension; 40 | 41 | spawn_lock.Read([&] () { 42 | 43 | spawn_x=this->spawn_x; 44 | spawn_y=this->spawn_y; 45 | spawn_z=this->spawn_z; 46 | dimension=spawn_dimension; 47 | 48 | }); 49 | 50 | // TODO: Get player's actual 51 | // position/other information 52 | // from backing store 53 | 54 | // TODO: Manipulate spawn_y so that 55 | // the player always spawns in air, 56 | // but right above ground 57 | 58 | // Setup this player's object 59 | auto player=SmartPointer::Make(); 60 | player->ID=EntityIDGenerator::Get(); 61 | player->Conn=client; 62 | player->Position.X=spawn_x; // TEMP 63 | player->Position.Y=spawn_y; // TEMP 64 | player->Position.Z=spawn_z; // TEMP 65 | player->Position.Yaw=0; // TEMP 66 | player->Position.Pitch=0; // TEMP 67 | player->Position.Stance=74; // TEMP 68 | player->Dimension=dimension; 69 | 70 | // Add to list of players before 71 | // send 72 | players_lock.Write([&] () { players.find(client)->second=player; }); 73 | 74 | // Send Join Game 75 | 76 | Packets::Play::Clientbound::JoinGame jg; 77 | jg.EntityID=player->ID; 78 | jg.GameMode=0; // Temp 79 | jg.Dimension=dimension; 80 | jg.Difficulty=0; // Temp 81 | auto max_players=Server::Get().MaximumPlayers; 82 | typedef decltype(jg.MaxPlayers) max_players_t; 83 | constexpr auto max=std::numeric_limits::max(); 84 | jg.MaxPlayers=( 85 | (max_players==0) || 86 | (max_players>max) 87 | ) ? max : static_cast(max); 88 | jg.LevelType=World::Get().Type(); 89 | 90 | client->Send(jg); 91 | 92 | // Send Spawn Position 93 | 94 | Packets::Play::Clientbound::SpawnPosition sp; 95 | sp.X=spawn_x; 96 | sp.Y=spawn_y; 97 | sp.Z=spawn_z; 98 | 99 | client->Send(sp); 100 | 101 | // Send Player Position and Look 102 | client->Send( 103 | player->Position.ToPacket() 104 | ); 105 | 106 | // Get the columns flowing 107 | update_position(player); 108 | 109 | } 110 | 111 | 112 | static const String protocol_error("Protocol error"); 113 | 114 | 115 | void Players::position_handler (PacketEvent event) { 116 | 117 | auto player=get(event.From); 118 | 119 | if (player.IsNull()) return; 120 | 121 | // Update player's position 122 | auto t=player->Lock.Execute([&] () { return player->Position.FromPacket(event.Data); }); 123 | 124 | // If the player moved, update 125 | // their position 126 | if (t.Item<0>()!=0) { 127 | 128 | Server::Get().Pool().Enqueue([=] () mutable { update_position(player); }); 129 | 130 | } 131 | 132 | } 133 | 134 | 135 | } 136 | -------------------------------------------------------------------------------- /include/ban/ban.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | namespace MCPP { 16 | 17 | 18 | /** 19 | * Represents a single ban. 20 | */ 21 | class BanInfo { 22 | 23 | 24 | public: 25 | 26 | 27 | /** 28 | * The username of the banned user. 29 | */ 30 | String Username; 31 | /** 32 | * The username of the user who banned 33 | * the user-in-question, if specified. 34 | */ 35 | Nullable By; 36 | /** 37 | * The reason for the ban, if any. 38 | */ 39 | Nullable Reason; 40 | 41 | 42 | }; 43 | 44 | 45 | /** 46 | * Allows usernames to be banned from logging 47 | * into the server. 48 | */ 49 | class Bans : public Module { 50 | 51 | 52 | private: 53 | 54 | 55 | std::unordered_map bans; 56 | mutable RWLock lock; 57 | 58 | 59 | public: 60 | 61 | 62 | /** 63 | * Retrieves a valid instance of this class. 64 | * 65 | * \return 66 | * A reference to a valid instance of 67 | * this class. 68 | */ 69 | static Bans & Get () noexcept; 70 | 71 | 72 | /** 73 | * \cond 74 | */ 75 | 76 | 77 | virtual const String & Name () const noexcept override; 78 | virtual Word Priority () const noexcept override; 79 | virtual void Install () override; 80 | 81 | 82 | /** 83 | * \endcond 84 | */ 85 | 86 | 87 | /** 88 | * Bans a particular user. 89 | * 90 | * \param [in] ban 91 | * The ban to enact. 92 | * 93 | * \return 94 | * \em true if the ban was 95 | * successfully applied, \em false 96 | * if the given username was already 97 | * banned. 98 | */ 99 | bool Ban (BanInfo ban); 100 | /** 101 | * Unbans a particular username. 102 | * 103 | * \param [in] username 104 | * The username to unban. 105 | * \param [in] by 106 | * The username of the person unbanned 107 | * \em username, if applicable. Defaults 108 | * to null. 109 | * 110 | * \return 111 | * \em true if \em username was 112 | * unbanned. \em false if \em username 113 | * was not banned to begin with. 114 | */ 115 | bool Unban (String username, Nullable by=Nullable()); 116 | 117 | 118 | /** 119 | * Checks to see if a particular username is 120 | * banned. 121 | * 122 | * \param [in] username 123 | * The username to check. 124 | * 125 | * \return 126 | * \em true if \em username is banned, 127 | * \em false otherwise. 128 | */ 129 | bool Check (String username) const; 130 | /** 131 | * Attempts to retrieve the information about 132 | * the ban applied to a certain username. 133 | * 134 | * \param [in] username 135 | * The username to retrieve. 136 | * 137 | * \return 138 | * A structure containing the information 139 | * about the ban of \em username if 140 | * \em username is banned, null otherwise. 141 | */ 142 | Nullable Retrieve (String username) const; 143 | 144 | 145 | /** 146 | * Retrieves the banlist. 147 | * 148 | * \return 149 | * A vector of bans containing every 150 | * ban. 151 | */ 152 | Vector GetInfo () const; 153 | 154 | 155 | }; 156 | 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/brand/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace MCPP; 10 | 11 | 12 | namespace MCPP { 13 | 14 | 15 | static const String name("MC|Brand Handling"); 16 | static const Word priority=1; 17 | static const String channel("MC|Brand"); 18 | static const String verbose_key("brand"); 19 | static const String identifying_template("Identifying as \"{0}\" to {1}:{2}"); 20 | static const String identified_template("{0}:{1} identified as \"{2}\""); 21 | 22 | 23 | const String & Brands::Name () const noexcept { 24 | 25 | return name; 26 | 27 | } 28 | 29 | 30 | Word Brands::Priority () const noexcept { 31 | 32 | return priority; 33 | 34 | } 35 | 36 | 37 | void Brands::Install () { 38 | 39 | auto & server=Server::Get(); 40 | 41 | // Install into server 42 | 43 | server.OnConnect.Add([this] (SmartPointer client) mutable { 44 | 45 | lock.Write([&] () { brands.emplace(std::move(client),Nullable()); }); 46 | 47 | }); 48 | 49 | server.OnDisconnect.Add([this] (SmartPointer client, const String &) mutable { 50 | 51 | lock.Write([&] () { brands.erase(client); }); 52 | 53 | }); 54 | 55 | server.OnLogin.Add([this] (SmartPointer client) { 56 | 57 | // Debug 58 | auto & server=Server::Get(); 59 | if (server.IsVerbose(verbose_key)) server.WriteLog( 60 | String::Format( 61 | identifying_template, 62 | Server::Name, 63 | client->IP(), 64 | client->Port() 65 | ), 66 | Service::LogType::Debug 67 | ); 68 | 69 | // Send brand 70 | PluginMessages::Get().Send(PluginMessage{ 71 | std::move(client), 72 | channel, 73 | UTF8().Encode(Server::Name) 74 | }); 75 | 76 | }); 77 | 78 | // Install as handler for 79 | // MC|Brand 80 | PluginMessages::Get().Add( 81 | channel, 82 | [this] (PluginMessage message) mutable { 83 | 84 | auto brand=UTF8().Decode( 85 | message.Buffer.begin(), 86 | message.Buffer.end() 87 | ); 88 | 89 | auto & server=Server::Get(); 90 | if (server.IsVerbose(verbose_key)) server.WriteLog( 91 | String::Format( 92 | identified_template, 93 | message.Endpoint->IP(), 94 | message.Endpoint->Port(), 95 | brand 96 | ), 97 | Service::LogType::Debug 98 | ); 99 | 100 | lock.Write([&] () mutable { 101 | 102 | auto iter=brands.find(message.Endpoint); 103 | 104 | if (iter!=brands.end()) iter->second.Construct( 105 | std::move(brand) 106 | ); 107 | 108 | }); 109 | 110 | } 111 | ); 112 | 113 | } 114 | 115 | 116 | Nullable Brands::Get (const SmartPointer & client) { 117 | 118 | return lock.Read([&] () { 119 | 120 | Nullable retr; 121 | 122 | auto iter=brands.find(client); 123 | 124 | if (!( 125 | (iter==brands.end()) || 126 | (iter->second.IsNull()) 127 | )) retr.Construct(*(iter->second)); 128 | 129 | return retr; 130 | 131 | }); 132 | 133 | } 134 | 135 | 136 | static Singleton singleton; 137 | 138 | 139 | Brands & Brands::Get () noexcept { 140 | 141 | return singleton.Get(); 142 | 143 | } 144 | 145 | 146 | } 147 | 148 | 149 | extern "C" { 150 | 151 | 152 | Module * Load () { 153 | 154 | return &(Brands::Get()); 155 | 156 | } 157 | 158 | 159 | void Unload () { 160 | 161 | singleton.Destroy(); 162 | 163 | } 164 | 165 | 166 | } 167 | -------------------------------------------------------------------------------- /include/aes_128_cfb_8.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | 12 | 13 | namespace MCPP { 14 | 15 | 16 | /** 17 | * Provides AES/CFB8 encryption and 18 | * decryption using 128-bit keys. 19 | */ 20 | class AES128CFB8 { 21 | 22 | 23 | private: 24 | 25 | 26 | Mutex encrypt_lock; 27 | EVP_CIPHER_CTX encrypt; 28 | Mutex decrypt_lock; 29 | EVP_CIPHER_CTX decrypt; 30 | Vector key; 31 | Vector iv; 32 | 33 | 34 | public: 35 | 36 | 37 | AES128CFB8 () = delete; 38 | AES128CFB8 (const AES128CFB8 &) = delete; 39 | AES128CFB8 (AES128CFB8 &&) = delete; 40 | AES128CFB8 & operator = (const AES128CFB8 &) = delete; 41 | AES128CFB8 & operator = (AES128CFB8 &&) = delete; 42 | 43 | 44 | /** 45 | * Creates a new encryptor/decryptor 46 | * which encrypts and decrypts using 47 | * the given key and initialization 48 | * vector, both of which must be 49 | * at least 16 bytes long. 50 | * 51 | * If either the key or initialization 52 | * vector is longer than 16 bytes, 53 | * bytes past the 16th are simply 54 | * ignored. 55 | * 56 | * \param [in] key 57 | * The encryption key. 58 | * \param [in] iv 59 | * The initialization vector. 60 | */ 61 | AES128CFB8 (Vector key, Vector iv); 62 | /** 63 | * Cleans up this object, freeing all 64 | * used resources and erasing sensitive 65 | * encryption information from memory. 66 | */ 67 | ~AES128CFB8 () noexcept; 68 | 69 | 70 | /** 71 | * Acquires the necessary lock to encrypt 72 | * in a threadsafe manner. 73 | * 74 | * This lock should be held until the data 75 | * is dispatched over the wire or queued 76 | * for such dispatch otherwise the decryptor 77 | * at the other end will not be in sync 78 | * and will not be able to decrypt. 79 | */ 80 | void BeginEncrypt () noexcept; 81 | /** 82 | * Releases the encryption lock. 83 | * 84 | * Every call to BeginEncrypt must have 85 | * a matching EndEncrypt call. 86 | */ 87 | void EndEncrypt () noexcept; 88 | /** 89 | * Encrypts a given segment of cleartext. 90 | * 91 | * \param [in] cleartext 92 | * The cleartext to be encrypted. 93 | * 94 | * \return 95 | * The ciphertext which corresponds to 96 | * \em cleartext. 97 | */ 98 | Vector Encrypt (const Vector & cleartext); 99 | 100 | 101 | /** 102 | * Acquires the necessary lock to 103 | * decrypt in a threadsafe manner. 104 | */ 105 | void BeginDecrypt () noexcept; 106 | /** 107 | * Releases the decryption lock. 108 | */ 109 | void EndDecrypt () noexcept; 110 | /** 111 | * Decrypts a given segment of ciphertext. 112 | * 113 | * \param [in] ciphertext 114 | * The ciphertext to be decrypted. 115 | * 116 | * \return 117 | * The cleartext which corresponds to 118 | * \em ciphertext. 119 | */ 120 | Vector Decrypt (const Vector & ciphertext); 121 | /** 122 | * Extracts ciphertext from one buffer and places 123 | * plaintext in another buffer. 124 | * 125 | * \param [in,out] ciphertext 126 | * The buffer from which ciphertext shall be 127 | * extracted. 128 | * \param [in,out] plaintext 129 | * The buffer into which plaintext shall be 130 | * appended. 131 | */ 132 | void Decrypt (Vector * ciphertext, Vector * plaintext); 133 | 134 | 135 | }; 136 | 137 | 138 | } 139 | -------------------------------------------------------------------------------- /include/plugin_message/plugin_message.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | namespace MCPP { 22 | 23 | 24 | /** 25 | * Represents a plugin message, either 26 | * to be sent or received. 27 | */ 28 | class PluginMessage { 29 | 30 | 31 | public: 32 | 33 | 34 | /** 35 | * The client either that this plugin 36 | * message should be sent to, or that 37 | * this plugin message was received 38 | * from. 39 | */ 40 | SmartPointer Endpoint; 41 | /** 42 | * The channel on which this plugin 43 | * message was received, or on which 44 | * it should be sent. 45 | */ 46 | String Channel; 47 | /** 48 | * The plugin message to be sent, or 49 | * the plugin message which was received. 50 | */ 51 | Vector Buffer; 52 | 53 | 54 | }; 55 | 56 | 57 | /** 58 | * Handles sending and receiving 0xFA plugin 59 | * messages. 60 | */ 61 | class PluginMessages : public Module { 62 | 63 | 64 | private: 65 | 66 | 67 | // Packet types 68 | typedef Packets::Play::Serverbound::PluginMessage incoming; 69 | typedef Packets::Play::Clientbound::PluginMessage outgoing; 70 | 71 | 72 | // Maps channels to callbacks 73 | // which handle incoming messages 74 | // on that channel 75 | std::unordered_map< 76 | String, 77 | std::function 78 | > callbacks; 79 | // Maps clients to channels they've 80 | // subscribed to 81 | std::unordered_map< 82 | SmartPointer, 83 | std::unordered_set 84 | > clients; 85 | RWLock lock; 86 | 87 | 88 | void reg (SmartPointer, Vector); 89 | void unreg (SmartPointer, Vector); 90 | void handler (PacketEvent); 91 | 92 | 93 | public: 94 | 95 | 96 | /** 97 | * Retrieves a reference to a valid 98 | * instance of this class. 99 | * 100 | * \return 101 | * A reference to a valid instance 102 | * of this class. 103 | */ 104 | static PluginMessages & Get () noexcept; 105 | 106 | 107 | /** 108 | * \cond 109 | */ 110 | 111 | 112 | virtual Word Priority () const noexcept override; 113 | virtual const String & Name () const noexcept override; 114 | virtual void Install () override; 115 | 116 | 117 | /** 118 | * \endcond 119 | */ 120 | 121 | 122 | /** 123 | * Adds a handler to a channel. 124 | * 125 | * \param [in] channel 126 | * The channel the handler 127 | * shall be associated with. 128 | * \param [in] callback 129 | * The callback to be invoked 130 | * whenever a plugin message 131 | * on \em channel is received. 132 | */ 133 | void Add (String channel, std::function callback); 134 | /** 135 | * Attempts to send a plugin message 136 | * on a certain channel to a certain 137 | * client. 138 | * 139 | * If the client has not registered 140 | * for the channel-in-question, the 141 | * message will not be sent. 142 | * 143 | * \param [in] message 144 | * The message to send. 145 | * 146 | * \return 147 | * A promise for the results of the 148 | * send if a send was performed, null 149 | * otherwise. 150 | */ 151 | Nullable> Send (PluginMessage message); 152 | 153 | 154 | }; 155 | 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/permissions/info.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace MCPP; 10 | 11 | 12 | static const String name("Permissions Information Provider"); 13 | static const Word priority=1; 14 | static const String identifier("permissions"); 15 | static const String help("Displays permissions tables."); 16 | static const String permissions_banner("PERMISSIONS TABLES:"); 17 | static const String users_banner("USERS:"); 18 | static const String groups_banner("GROUPS:"); 19 | static const String permissions_label("Permissions: "); 20 | static const String groups_label("Groups: "); 21 | static const String all("ALL"); 22 | static const String minus(" - "); 23 | static const String separator(", "); 24 | 25 | 26 | class PermissionsInfoProvider : public Module, public InformationProvider { 27 | 28 | 29 | private: 30 | 31 | 32 | static void render (Vector vec, ChatMessage & message) { 33 | 34 | std::sort(vec.begin(),vec.end()); 35 | 36 | bool first=true; 37 | for (auto & str : vec) { 38 | 39 | if (first) first=false; 40 | else message << separator; 41 | 42 | message << str; 43 | 44 | } 45 | 46 | } 47 | 48 | 49 | static void render (Vector entries, ChatMessage & message) { 50 | 51 | // Sort entries 52 | std::sort( 53 | entries.begin(), 54 | entries.end(), 55 | [] (const PermissionsTableEntry & a, const PermissionsTableEntry & b) { return a.Name 2 | #include 3 | #include 4 | #include