commit c356c7b911b45d4bc08bd5120a82007381da361a Author: NOTPIES Date: Sun Mar 15 22:25:51 2026 -0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1be317a --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +x64_Server_Debug/ +x64_Server_Release/ +ipch/ +*.user +*.obj +*.pch +*.pdb +*.tlog +*.lastbuildstate +*.log +*.idb +*.ilk +*.exe +*.sdf +*.opensdf +*.suo +*.sln +server.properties \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..861a79b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,107 @@ +cmake_minimum_required(VERSION 3.10) +project("MinecraftDedicatedServer") + +set(CMAKE_CXX_STANDARD 11) + +if(NOT UNIX) + message(FATAL_ERROR "The dedicated server currently only builds on Linux, use Visual Studio to build the Windows server") +endif() + +set(CMAKE_C_FLAGS_RELEASE "-Os -DNDEBUG") +set(CMAKE_CXX_FLAGS_RELEASE "-Os -DNDEBUG") +set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-s") + +set(ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") + +include("${ROOT_DIR}/cmake/Sources.cmake") +include("${ROOT_DIR}/cmake/ServerSources.cmake") + +list(TRANSFORM MINECRAFT_WORLD_SOURCES PREPEND "${ROOT_DIR}/Minecraft.World/") +list(TRANSFORM MINECRAFT_SERVER_OWN_SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") +list(TRANSFORM MINECRAFT_SERVER_SHARED_SOURCES PREPEND "${ROOT_DIR}/Minecraft.Client/") + +set(BUNDLED_ZLIB_SOURCES + "${ROOT_DIR}/Minecraft.Client/Common/zlib/adler32.c" + "${ROOT_DIR}/Minecraft.Client/Common/zlib/compress.c" + "${ROOT_DIR}/Minecraft.Client/Common/zlib/crc32.c" + "${ROOT_DIR}/Minecraft.Client/Common/zlib/deflate.c" + "${ROOT_DIR}/Minecraft.Client/Common/zlib/inffast.c" + "${ROOT_DIR}/Minecraft.Client/Common/zlib/inflate.c" + "${ROOT_DIR}/Minecraft.Client/Common/zlib/inftrees.c" + "${ROOT_DIR}/Minecraft.Client/Common/zlib/trees.c" + "${ROOT_DIR}/Minecraft.Client/Common/zlib/uncompr.c" + "${ROOT_DIR}/Minecraft.Client/Common/zlib/zutil.c" +) + +add_library(MinecraftWorld STATIC ${MINECRAFT_WORLD_SOURCES} ${BUNDLED_ZLIB_SOURCES}) + +target_include_directories(MinecraftWorld PUBLIC + "${ROOT_DIR}/Minecraft.World" + "${ROOT_DIR}/Minecraft.World/x64headers" +) + +target_include_directories(MinecraftWorld BEFORE PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Linux/stubs" +) + +target_include_directories(MinecraftWorld PRIVATE + "${ROOT_DIR}/Minecraft.Client" + "${ROOT_DIR}/Minecraft.Client/Windows64/Iggy/include" + "${ROOT_DIR}/Minecraft.Client/Xbox/Sentient/Include" +) + +target_compile_options(MinecraftWorld PRIVATE + -include "${CMAKE_CURRENT_SOURCE_DIR}/Linux/LinuxCompat.h" + -Wno-write-strings -Wno-narrowing -Wno-deprecated + -ffunction-sections -fdata-sections +) + +target_compile_definitions(MinecraftWorld PRIVATE + _LARGE_WORLDS _LIB _WINDOWS64 + _CRT_NON_CONFORMING_SWPRINTFS _CRT_SECURE_NO_WARNINGS +) + +add_executable(MinecraftDedicatedServer + ${MINECRAFT_SERVER_OWN_SOURCES} + ${MINECRAFT_SERVER_SHARED_SOURCES} +) + +target_compile_definitions(MinecraftDedicatedServer PRIVATE + _DEDICATED_SERVER + _CONTENT_PACKAGE + _LARGE_WORLDS + _WINDOWS64 + WITH_SERVER_CODE + _CRT_NON_CONFORMING_SWPRINTFS + _CRT_SECURE_NO_WARNINGS +) + +target_include_directories(MinecraftDedicatedServer BEFORE PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Linux/stubs" +) + +target_include_directories(MinecraftDedicatedServer PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/Linux" + "${ROOT_DIR}/Minecraft.Client" + "${ROOT_DIR}/Minecraft.Client/Windows64/4JLibs/inc" + "${ROOT_DIR}/Minecraft.Client/Windows64/Iggy/include" + "${ROOT_DIR}/Minecraft.Client/Xbox/Sentient/Include" + "${ROOT_DIR}/Minecraft.World/x64headers" +) + +target_compile_options(MinecraftDedicatedServer PRIVATE + -include "${CMAKE_CURRENT_SOURCE_DIR}/Linux/LinuxCompat.h" + -Wno-write-strings -Wno-narrowing -Wno-deprecated + -ffunction-sections -fdata-sections +) + +find_package(Threads REQUIRED) +target_link_libraries(MinecraftDedicatedServer PRIVATE + MinecraftWorld + Threads::Threads +) + +target_link_options(MinecraftDedicatedServer PRIVATE + -Wl,--gc-sections +) diff --git a/Commands/BanCommand.cpp b/Commands/BanCommand.cpp new file mode 100644 index 0000000..62f0fff --- /dev/null +++ b/Commands/BanCommand.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "BanCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.Client/PlayerConnection.h" +#include "../../Minecraft.World/DisconnectPacket.h" +#include "../Core/ServerLists.h" +#include "ServerTextList.h" + +#include + +void BanCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring playerName; + if (!(ss >> playerName)) + { + src->warn(L"Usage: /ban "); + return; + } + + ServerLists_GetBannedPlayers()->add(playerName); + ServerCommand::notifyAdmins(src, server, L"Banned player " + playerName); + + shared_ptr player = server->getPlayers()->getPlayer(playerName); + if (player) + player->connection->disconnect(DisconnectPacket::eDisconnect_Banned); +} diff --git a/Commands/BanCommand.h b/Commands/BanCommand.h new file mode 100644 index 0000000..7d309d8 --- /dev/null +++ b/Commands/BanCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class BanCommand : public ServerCommand +{ +public: + wstring getName() { return L"ban"; } + wstring getUsage() { return L"/ban - Bans a player from the server"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/BanIpCommand.cpp b/Commands/BanIpCommand.cpp new file mode 100644 index 0000000..f1c2251 --- /dev/null +++ b/Commands/BanIpCommand.cpp @@ -0,0 +1,97 @@ +#include "stdafx.h" +#include "BanIpCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.Client/PlayerConnection.h" +#include "../../Minecraft.World/DisconnectPacket.h" +#include "../../Minecraft.World/Connection.h" +#include "../../Minecraft.World/Socket.h" +#include "../../Minecraft.Client/Windows64/Network/WinsockNetLayer.h" +#include "../Core/ServerLists.h" +#include "ServerTextList.h" + +#include + +static bool isValidIP(const wstring& str) +{ + int dots = 0; + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] == L'.') dots++; + else if (str[i] < L'0' || str[i] > L'9') return false; + } + return dots == 3 && str.size() >= 7; +} + +static wstring narrowToWide(const std::string& s) +{ + return wstring(s.begin(), s.end()); +} + +void BanIpCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring arg; + if (!(ss >> arg)) + { + src->warn(L"Usage: " + getUsage()); + return; + } + + wstring ip; + if (isValidIP(arg)) + { + ip = arg; + } + else + { + if (!server || !server->getPlayers()) + { + src->warn(L"Cannot resolve player name, no players online"); + return; + } + shared_ptr target = server->getPlayers()->getPlayer(arg); + if (!target || !target->connection) + { + src->warn(L"Cannot find player \"" + arg + L"\""); + return; + } + Connection *conn = target->connection->connection; + if (!conn || !conn->getSocket()) + { + src->warn(L"Cannot determine IP for \"" + arg + L"\""); + return; + } + BYTE smallId = conn->getSocket()->getSmallId(); + std::string ipStr = WinsockNetLayer::GetIPForSmallId(smallId); + if (ipStr.empty()) + { + src->warn(L"Cannot determine IP for \"" + arg + L"\""); + return; + } + ip = narrowToWide(ipStr); + } + + ServerLists_GetBannedIPs()->add(ip); + ServerCommand::notifyAdmins(src, server, L"Banned IP address " + ip); + + if (server && server->getPlayers()) + { + vector>& pl = server->getPlayers()->players; + for (unsigned int i = 0; i < pl.size(); i++) + { + if (!pl[i] || !pl[i]->connection) continue; + Connection *conn = pl[i]->connection->connection; + if (!conn || !conn->getSocket()) continue; + BYTE sid = conn->getSocket()->getSmallId(); + std::string pIp = WinsockNetLayer::GetIPForSmallId(sid); + if (narrowToWide(pIp) == ip) + { + server->getPlayers()->kickPlayerByShortId(sid); + } + } + } +} diff --git a/Commands/BanIpCommand.h b/Commands/BanIpCommand.h new file mode 100644 index 0000000..4ee0f56 --- /dev/null +++ b/Commands/BanIpCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class BanIpCommand : public ServerCommand +{ +public: + wstring getName() { return L"ban-ip"; } + wstring getUsage() { return L"/ban-ip - Bans an IP address"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/BanListCommand.cpp b/Commands/BanListCommand.cpp new file mode 100644 index 0000000..4718dd5 --- /dev/null +++ b/Commands/BanListCommand.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "BanListCommand.h" +#include "ServerTextList.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../Core/ServerLists.h" +#include "ServerTextList.h" + +#include + +void BanListCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring type; + ss >> type; + + if (type == L"ips") + { + const set& entries = ServerLists_GetBannedIPs()->getEntries(); + wchar_t buf[128]; + swprintf_s(buf, 128, L"There are %d banned IP addresses:", (int)entries.size()); + src->info(wstring(buf)); + wstring list; + for (set::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + if (!list.empty()) list += L", "; + list += *it; + } + if (!list.empty()) src->info(list); + } + else + { + const set& entries = ServerLists_GetBannedPlayers()->getEntries(); + wchar_t buf[128]; + swprintf_s(buf, 128, L"There are %d banned players:", (int)entries.size()); + src->info(wstring(buf)); + wstring list; + for (set::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + if (!list.empty()) list += L", "; + list += *it; + } + if (!list.empty()) src->info(list); + } +} diff --git a/Commands/BanListCommand.h b/Commands/BanListCommand.h new file mode 100644 index 0000000..0fc73bd --- /dev/null +++ b/Commands/BanListCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class BanListCommand : public ServerCommand +{ +public: + wstring getName() { return L"banlist"; } + wstring getUsage() { return L"/banlist [ips|players] - Shows the ban list"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/ConsoleCommandDispatcher.cpp b/Commands/ConsoleCommandDispatcher.cpp new file mode 100644 index 0000000..189c259 --- /dev/null +++ b/Commands/ConsoleCommandDispatcher.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "ConsoleCommandDispatcher.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" + +ConsoleCommandDispatcher::~ConsoleCommandDispatcher() +{ + for (AUTO_VAR(it, commandsByName.begin()); it != commandsByName.end(); ++it) + delete it->second; + commandsByName.clear(); +} + +void ConsoleCommandDispatcher::addCommand(ServerCommand *command) +{ + commandsByName[command->getName()] = command; +} + +bool ConsoleCommandDispatcher::performCommand(const wstring& name, const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + AUTO_VAR(it, commandsByName.find(name)); + if (it != commandsByName.end()) + { + it->second->execute(args, src, server); + return true; + } + return false; +} + +const map& ConsoleCommandDispatcher::getCommands() const +{ + return commandsByName; +} diff --git a/Commands/ConsoleCommandDispatcher.h b/Commands/ConsoleCommandDispatcher.h new file mode 100644 index 0000000..ed368f9 --- /dev/null +++ b/Commands/ConsoleCommandDispatcher.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ServerCommand.h" +#include + +class ConsoleCommandDispatcher +{ +private: + map commandsByName; + +public: + ~ConsoleCommandDispatcher(); + + void addCommand(ServerCommand *command); + bool performCommand(const wstring& name, const wstring& args, ConsoleInputSource *src, MinecraftServer *server); + const map& getCommands() const; +}; diff --git a/Commands/ConsoleCommandSender.cpp b/Commands/ConsoleCommandSender.cpp new file mode 100644 index 0000000..6096d51 --- /dev/null +++ b/Commands/ConsoleCommandSender.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" +#include "ConsoleCommandSender.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" + +void ConsoleCommandSender::sendMessage(const wstring& message, ChatPacket::EChatPacketMessage type, int customData, const wstring& additionalMessage) +{ + src->info(message); +} + +bool ConsoleCommandSender::hasPermission(EGameCommand command) +{ + return true; +} diff --git a/Commands/ConsoleCommandSender.h b/Commands/ConsoleCommandSender.h new file mode 100644 index 0000000..ed68eda --- /dev/null +++ b/Commands/ConsoleCommandSender.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../../Minecraft.World/CommandSender.h" + +class ConsoleInputSource; + +class ConsoleCommandSender : public CommandSender +{ +private: + ConsoleInputSource *src; + +public: + ConsoleCommandSender(ConsoleInputSource *src) : src(src) {} + + void sendMessage(const wstring& message, ChatPacket::EChatPacketMessage type = ChatPacket::e_ChatCustom, int customData = -1, const wstring& additionalMessage = L""); + bool hasPermission(EGameCommand command); +}; diff --git a/Commands/DeOpCommand.cpp b/Commands/DeOpCommand.cpp new file mode 100644 index 0000000..6ac8ca6 --- /dev/null +++ b/Commands/DeOpCommand.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "DeOpCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.World/net.minecraft.network.packet.h" +#include "../Core/ServerLists.h" +#include "ServerTextList.h" + +void DeOpCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstring name = args; + size_t start = name.find_first_not_of(L" \t"); + if (start == wstring::npos || name.empty()) + { + src->warn(L"Usage: /deop "); + return; + } + name = name.substr(start); + size_t end = name.find_first_of(L" \t"); + if (end != wstring::npos) + name = name.substr(0, end); + + ServerLists_GetOps()->remove(name); + + shared_ptr player = server->getPlayers()->getPlayer(name); + if (player != nullptr) + { + player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op, 0); + server->getPlayers()->broadcastAll(shared_ptr(new PlayerInfoPacket(player))); + } + + ServerCommand::notifyAdmins(src, server, L"De-opped " + name); +} diff --git a/Commands/DeOpCommand.h b/Commands/DeOpCommand.h new file mode 100644 index 0000000..34dc500 --- /dev/null +++ b/Commands/DeOpCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class DeOpCommand : public ServerCommand +{ +public: + wstring getName() { return L"deop"; } + wstring getUsage() { return L"/deop - Removes operator status from a player"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/DebugCommand.cpp b/Commands/DebugCommand.cpp new file mode 100644 index 0000000..e97d472 --- /dev/null +++ b/Commands/DebugCommand.cpp @@ -0,0 +1,55 @@ +#include "stdafx.h" +#include "DebugCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" + +#include +#include + +static bool s_profiling = false; +static time_t s_profilingStart = 0; +static int s_profilingStartTick = 0; + +void DebugCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring subCmd; + if (!(ss >> subCmd)) + { + src->warn(L"Usage: /debug "); + return; + } + + if (subCmd == L"start") + { + if (s_profiling) + { + src->warn(L"Debug profiling is already started"); + return; + } + s_profiling = true; + s_profilingStart = time(NULL); + s_profilingStartTick = server->tickCount; + ServerCommand::notifyAdmins(src, server, L"Starting debug profiling"); + } + else if (subCmd == L"stop") + { + if (!s_profiling) + { + src->warn(L"Debug profiling is not started"); + return; + } + s_profiling = false; + time_t elapsed = time(NULL) - s_profilingStart; + int ticks = server->tickCount - s_profilingStartTick; + float tps = elapsed > 0 ? (float)ticks / (float)elapsed : 0.0f; + wchar_t buf[256]; + swprintf_s(buf, 256, L"Stopped debug profiling after %.2f seconds and %d ticks (%.2f ticks per second)", (float)elapsed, ticks, tps); + ServerCommand::notifyAdmins(src, server, wstring(buf)); + } + else + { + src->warn(L"Usage: /debug "); + } +} diff --git a/Commands/DebugCommand.h b/Commands/DebugCommand.h new file mode 100644 index 0000000..3a2c0ca --- /dev/null +++ b/Commands/DebugCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class DebugCommand : public ServerCommand +{ +public: + wstring getName() { return L"debug"; } + wstring getUsage() { return L"/debug - Starts or stops a debugging session"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/DefaultGameModeCommand.cpp b/Commands/DefaultGameModeCommand.cpp new file mode 100644 index 0000000..6a207f2 --- /dev/null +++ b/Commands/DefaultGameModeCommand.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "DefaultGameModeCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.World/LevelSettings.h" + +#include + +void DefaultGameModeCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring modeStr; + if (!(ss >> modeStr)) + { + src->warn(L"Usage: /defaultgamemode "); + return; + } + + GameType *mode = NULL; + if (modeStr == L"0" || modeStr == L"s" || modeStr == L"survival") + mode = GameType::SURVIVAL; + else if (modeStr == L"1" || modeStr == L"c" || modeStr == L"creative") + mode = GameType::CREATIVE; + else if (modeStr == L"2" || modeStr == L"a" || modeStr == L"adventure") + mode = GameType::ADVENTURE; + else + { + src->warn(L"Unknown game mode: " + modeStr); + return; + } + + server->getPlayers()->setOverrideGameMode(mode); + ServerCommand::notifyAdmins(src, server, L"The world's default game mode is now " + mode->getName()); +} diff --git a/Commands/DefaultGameModeCommand.h b/Commands/DefaultGameModeCommand.h new file mode 100644 index 0000000..690733b --- /dev/null +++ b/Commands/DefaultGameModeCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class DefaultGameModeCommand : public ServerCommand +{ +public: + wstring getName() { return L"defaultgamemode"; } + wstring getUsage() { return L"/defaultgamemode - Sets the default game mode"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/EnchantServerCommand.cpp b/Commands/EnchantServerCommand.cpp new file mode 100644 index 0000000..a31f998 --- /dev/null +++ b/Commands/EnchantServerCommand.cpp @@ -0,0 +1,48 @@ +#include "stdafx.h" +#include "EnchantServerCommand.h" +#include "ConsoleCommandSender.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.World/CommandDispatcher.h" +#include "../../Minecraft.World/ByteArrayOutputStream.h" +#include "../../Minecraft.World/DataOutputStream.h" + +#include + +void EnchantServerCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring playerName; + int enchantmentId = 0; + int enchantmentLevel = 1; + + if (!(ss >> playerName >> enchantmentId)) + { + src->warn(L"\u00A7cUsage: /enchant [level]"); + return; + } + + ss >> enchantmentLevel; + + PlayerList *players = server->getPlayers(); + shared_ptr player = players->getPlayer(playerName); + if (!player) + { + src->warn(L"\u00A7cThat player cannot be found"); + return; + } + + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + dos.writePlayerUID(player->getXuid()); + dos.writeInt(enchantmentId); + dos.writeInt(enchantmentLevel); + + shared_ptr sender(new ConsoleCommandSender(src)); + CommandDispatcher *dispatcher = server->getCommandDispatcher(); + dispatcher->performCommand(sender, eGameCommand_EnchantItem, baos.toByteArray()); +} diff --git a/Commands/EnchantServerCommand.h b/Commands/EnchantServerCommand.h new file mode 100644 index 0000000..4e14b49 --- /dev/null +++ b/Commands/EnchantServerCommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ServerCommand.h" + +class EnchantServerCommand : public ServerCommand +{ +public: + wstring getName() { return L"enchant"; } + wstring getUsage() { return L"/enchant [level]"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/GameModeServerCommand.cpp b/Commands/GameModeServerCommand.cpp new file mode 100644 index 0000000..db7a6f2 --- /dev/null +++ b/Commands/GameModeServerCommand.cpp @@ -0,0 +1,56 @@ +#include "stdafx.h" +#include "GameModeServerCommand.h" +#include "ServerCommand.h" +#include "ConsoleCommandSender.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.World/LevelSettings.h" + +#include + +void GameModeServerCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring modeStr; + + if (!(ss >> modeStr)) + { + src->warn(L"\u00A7cUsage: /gamemode [player]"); + return; + } + + GameType *mode = NULL; + if (modeStr == L"0" || modeStr == L"s" || modeStr == L"survival") + mode = GameType::SURVIVAL; + else if (modeStr == L"1" || modeStr == L"c" || modeStr == L"creative") + mode = GameType::CREATIVE; + else if (modeStr == L"2" || modeStr == L"a" || modeStr == L"adventure") + mode = GameType::ADVENTURE; + else + { + src->warn(L"\u00A7cUnknown game mode: " + modeStr); + return; + } + + wstring playerName; + if (!(ss >> playerName)) + { + src->warn(L"\u00A7cYou must specify which player you wish to perform this action on."); + return; + } + + PlayerList *players = server->getPlayers(); + shared_ptr player = players->getPlayer(playerName); + if (!player) + { + src->warn(L"\u00A7cThat player cannot be found"); + return; + } + player->setGameMode(mode); + wchar_t buf[256]; + swprintf_s(buf, 256, L"Set %s's game mode to %s", player->name.c_str(), mode->getName().c_str()); + ServerCommand::notifyAdmins(src, server, wstring(buf)); +} diff --git a/Commands/GameModeServerCommand.h b/Commands/GameModeServerCommand.h new file mode 100644 index 0000000..b9765cb --- /dev/null +++ b/Commands/GameModeServerCommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ServerCommand.h" + +class GameModeServerCommand : public ServerCommand +{ +public: + wstring getName() { return L"gamemode"; } + wstring getUsage() { return L"/gamemode [player]"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/GiveServerCommand.cpp b/Commands/GiveServerCommand.cpp new file mode 100644 index 0000000..8fa4c3f --- /dev/null +++ b/Commands/GiveServerCommand.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include "GiveServerCommand.h" +#include "ConsoleCommandSender.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.World/CommandDispatcher.h" +#include "../../Minecraft.World/ByteArrayOutputStream.h" +#include "../../Minecraft.World/DataOutputStream.h" + +#include + +void GiveServerCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring playerName; + int item = 0; + int amount = 1; + int aux = 0; + + if (!(ss >> playerName >> item)) + { + src->warn(L"\u00A7cUsage: /give [amount] [data]"); + return; + } + + if (ss >> amount) + { + if (amount < 1 || amount > 64) + { + src->warn(L"\u00A7cItem amount must be between 1 and 64"); + return; + } + } + ss >> aux; + + PlayerList *players = server->getPlayers(); + shared_ptr player = players->getPlayer(playerName); + if (!player) + { + src->warn(L"\u00A7cThat player cannot be found"); + return; + } + + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + dos.writePlayerUID(player->getXuid()); + dos.writeInt(item); + dos.writeInt(amount); + dos.writeInt(aux); + dos.writeUTF(L""); + + shared_ptr sender(new ConsoleCommandSender(src)); + CommandDispatcher *dispatcher = server->getCommandDispatcher(); + dispatcher->performCommand(sender, eGameCommand_Give, baos.toByteArray()); +} diff --git a/Commands/GiveServerCommand.h b/Commands/GiveServerCommand.h new file mode 100644 index 0000000..b345d03 --- /dev/null +++ b/Commands/GiveServerCommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ServerCommand.h" + +class GiveServerCommand : public ServerCommand +{ +public: + wstring getName() { return L"give"; } + wstring getUsage() { return L"/give [amount] [data]"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/HelpCommand.cpp b/Commands/HelpCommand.cpp new file mode 100644 index 0000000..fd96955 --- /dev/null +++ b/Commands/HelpCommand.cpp @@ -0,0 +1,73 @@ +#include "stdafx.h" +#include "HelpCommand.h" +#include "ConsoleCommandDispatcher.h" + +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include +#include +#include + +HelpCommand::HelpCommand(ConsoleCommandDispatcher *dispatcher) + : dispatcher(dispatcher) +{ +} + +void HelpCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + const map& commandMap = dispatcher->getCommands(); + + vector list; + for (AUTO_VAR(it, commandMap.begin()); it != commandMap.end(); ++it) + list.push_back(it->second); + + sort(list.begin(), list.end(), [](ServerCommand *a, ServerCommand *b) { + return a->getName() < b->getName(); + }); + + int itemsPerPage = 7; + int totalPages = (int)list.size() / itemsPerPage; + int page = 0; + + wstringstream ss(args); + wstring arg; + if (ss >> arg) + { + wstringstream numTest(arg); + int num; + if (numTest >> num && numTest.eof()) + { + if (num < 1) num = 1; + if (num > totalPages + 1) num = totalPages + 1; + page = num - 1; + } + else + { + for (size_t i = 0; i < arg.size(); i++) + arg[i] = towlower(arg[i]); + + AUTO_VAR(it, commandMap.find(arg)); + if (it != commandMap.end()) + { + src->info(L"\u00A7cUsage: " + it->second->getUsage()); + return; + } + + src->warn(L"\u00A7cUnknown command. Try /help for a list of commands"); + return; + } + } + + int endIndex = (int)list.size(); + if ((page + 1) * itemsPerPage < endIndex) + endIndex = (page + 1) * itemsPerPage; + + wchar_t buf[256]; + swprintf_s(buf, 256, L"\u00A72--- Showing help page %d of %d (/help ) ---", page + 1, totalPages + 1); + src->info(wstring(buf)); + + for (int k = page * itemsPerPage; k < endIndex; k++) + src->info(list[k]->getUsage()); + + if (page == 0) + src->info(L"\u00A7aTip: Use the key while typing a command to auto-complete the command or its arguments"); +} diff --git a/Commands/HelpCommand.h b/Commands/HelpCommand.h new file mode 100644 index 0000000..0fae01c --- /dev/null +++ b/Commands/HelpCommand.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ServerCommand.h" + +class ConsoleCommandDispatcher; + +class HelpCommand : public ServerCommand +{ +private: + ConsoleCommandDispatcher *dispatcher; + +public: + HelpCommand(ConsoleCommandDispatcher *dispatcher); + + wstring getName() { return L"help"; } + wstring getUsage() { return L"/help [page|command name]"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/KickServerCommand.cpp b/Commands/KickServerCommand.cpp new file mode 100644 index 0000000..3b84778 --- /dev/null +++ b/Commands/KickServerCommand.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "KickServerCommand.h" +#include "ServerCommand.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.Client/PlayerConnection.h" +#include "../../Minecraft.World/DisconnectPacket.h" + +#include + +void KickServerCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring playerName; + + if (!(ss >> playerName)) + { + src->warn(L"\u00A7cUsage: /kick [reason ...]"); + return; + } + + PlayerList *players = server->getPlayers(); + shared_ptr player = players->getPlayer(playerName); + if (!player) + { + src->warn(L"\u00A7cThat player cannot be found"); + return; + } + + wstring reason; + getline(ss, reason); + size_t rstart = reason.find_first_not_of(L" \t"); + if (rstart != wstring::npos) reason = reason.substr(rstart); + else reason.clear(); + + player->connection->disconnect(DisconnectPacket::eDisconnect_Kicked); + + if (!reason.empty()) + ServerCommand::notifyAdmins(src, server, L"Kicked player " + player->name + L": " + reason); + else + ServerCommand::notifyAdmins(src, server, L"Kicked player " + player->name); +} diff --git a/Commands/KickServerCommand.h b/Commands/KickServerCommand.h new file mode 100644 index 0000000..a30d8ba --- /dev/null +++ b/Commands/KickServerCommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ServerCommand.h" + +class KickServerCommand : public ServerCommand +{ +public: + wstring getName() { return L"kick"; } + wstring getUsage() { return L"/kick [reason ...]"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/KillServerCommand.cpp b/Commands/KillServerCommand.cpp new file mode 100644 index 0000000..050e262 --- /dev/null +++ b/Commands/KillServerCommand.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "KillServerCommand.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.World/DamageSource.h" + +#include + +void KillServerCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring playerName; + + if (!(ss >> playerName)) + { + src->warn(L"\u00A7cYou must specify which player you wish to perform this action on."); + return; + } + + PlayerList *players = server->getPlayers(); + shared_ptr player = players->getPlayer(playerName); + if (!player) + { + src->warn(L"\u00A7cThat player cannot be found"); + return; + } + + player->hurt(DamageSource::outOfWorld, 1000); + src->info(L"Ouch. That look like it hurt."); +} diff --git a/Commands/KillServerCommand.h b/Commands/KillServerCommand.h new file mode 100644 index 0000000..8e2f554 --- /dev/null +++ b/Commands/KillServerCommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ServerCommand.h" + +class KillServerCommand : public ServerCommand +{ +public: + wstring getName() { return L"kill"; } + wstring getUsage() { return L"/kill "; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/ListServerCommand.cpp b/Commands/ListServerCommand.cpp new file mode 100644 index 0000000..3ee6761 --- /dev/null +++ b/Commands/ListServerCommand.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "ListServerCommand.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" + +void ListServerCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + PlayerList *playerList = server->getPlayers(); + int count = playerList->getPlayerCount(); + int max = playerList->getMaxPlayers(); + + wchar_t buf[128]; + swprintf_s(buf, 128, L"There are %d/%d players online:", count, max); + src->info(wstring(buf)); + src->info(playerList->getPlayerNames()); +} diff --git a/Commands/ListServerCommand.h b/Commands/ListServerCommand.h new file mode 100644 index 0000000..4dce1a2 --- /dev/null +++ b/Commands/ListServerCommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ServerCommand.h" + +class ListServerCommand : public ServerCommand +{ +public: + wstring getName() { return L"list"; } + wstring getUsage() { return L"/list"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/MeCommand.cpp b/Commands/MeCommand.cpp new file mode 100644 index 0000000..79b4e6c --- /dev/null +++ b/Commands/MeCommand.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "MeCommand.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.World/ChatPacket.h" + +void MeCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + if (args.empty()) + { + src->warn(L"Usage: /me "); + return; + } + wstring msg = L"* Server " + args; + server->getPlayers()->broadcastAll(shared_ptr(new ChatPacket(msg))); + src->info(msg); +} diff --git a/Commands/MeCommand.h b/Commands/MeCommand.h new file mode 100644 index 0000000..13c4d06 --- /dev/null +++ b/Commands/MeCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class MeCommand : public ServerCommand +{ +public: + wstring getName() { return L"me"; } + wstring getUsage() { return L"/me - Performs the specified action"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/OpCommand.cpp b/Commands/OpCommand.cpp new file mode 100644 index 0000000..d5036cc --- /dev/null +++ b/Commands/OpCommand.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "OpCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.World/net.minecraft.network.packet.h" +#include "../Core/ServerLists.h" +#include "ServerTextList.h" + +void OpCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstring name = args; + size_t start = name.find_first_not_of(L" \t"); + if (start == wstring::npos || name.empty()) + { + src->warn(L"Usage: /op "); + return; + } + name = name.substr(start); + size_t end = name.find_first_of(L" \t"); + if (end != wstring::npos) + name = name.substr(0, end); + + ServerLists_GetOps()->add(name); + + shared_ptr player = server->getPlayers()->getPlayer(name); + if (player != nullptr) + { + player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op, 1); // idfk if this works + server->getPlayers()->broadcastAll(shared_ptr(new PlayerInfoPacket(player))); + } + + ServerCommand::notifyAdmins(src, server, L"Opped " + name); +} + +bool IsPlayerOp(const wstring& name) +{ + return ServerLists_IsPlayerOp(name); +} diff --git a/Commands/OpCommand.h b/Commands/OpCommand.h new file mode 100644 index 0000000..456b6b8 --- /dev/null +++ b/Commands/OpCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class OpCommand : public ServerCommand +{ +public: + wstring getName() { return L"op"; } + wstring getUsage() { return L"/op - Grants operator status to a player"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/PardonCommand.cpp b/Commands/PardonCommand.cpp new file mode 100644 index 0000000..bf021a7 --- /dev/null +++ b/Commands/PardonCommand.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "PardonCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../Core/ServerLists.h" +#include "ServerTextList.h" + +#include + +void PardonCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring playerName; + if (!(ss >> playerName)) + { + src->warn(L"Usage: /pardon "); + return; + } + + ServerLists_GetBannedPlayers()->remove(playerName); + ServerCommand::notifyAdmins(src, server, L"Unbanned player " + playerName); +} diff --git a/Commands/PardonCommand.h b/Commands/PardonCommand.h new file mode 100644 index 0000000..2d40438 --- /dev/null +++ b/Commands/PardonCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class PardonCommand : public ServerCommand +{ +public: + wstring getName() { return L"pardon"; } + wstring getUsage() { return L"/pardon - Unbans a player"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/PardonIpCommand.cpp b/Commands/PardonIpCommand.cpp new file mode 100644 index 0000000..536194b --- /dev/null +++ b/Commands/PardonIpCommand.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "PardonIpCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../Core/ServerLists.h" +#include "ServerTextList.h" + +#include + +void PardonIpCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring ip; + if (!(ss >> ip)) + { + src->warn(L"Usage: /pardon-ip "); + return; + } + + ServerLists_GetBannedIPs()->remove(ip); + ServerCommand::notifyAdmins(src, server, L"Unbanned IP address " + ip); +} diff --git a/Commands/PardonIpCommand.h b/Commands/PardonIpCommand.h new file mode 100644 index 0000000..1103dd2 --- /dev/null +++ b/Commands/PardonIpCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class PardonIpCommand : public ServerCommand +{ +public: + wstring getName() { return L"pardon-ip"; } + wstring getUsage() { return L"/pardon-ip - Unbans an IP address"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/SaveAllCommand.cpp b/Commands/SaveAllCommand.cpp new file mode 100644 index 0000000..187c80a --- /dev/null +++ b/Commands/SaveAllCommand.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "SaveAllCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerLevel.h" + +void SaveAllCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + ServerCommand::notifyAdmins(src, server, L"Forcing save.."); + PlayerList *players = server->getPlayers(); + if (players) + players->saveAll(NULL); + for (unsigned int i = 0; i < server->levels.length; i++) + { + ServerLevel *level = server->levels[i]; + if (level) + level->save(true, NULL); + } + ServerCommand::notifyAdmins(src, server, L"Save complete."); +} diff --git a/Commands/SaveAllCommand.h b/Commands/SaveAllCommand.h new file mode 100644 index 0000000..3945be2 --- /dev/null +++ b/Commands/SaveAllCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class SaveAllCommand : public ServerCommand +{ +public: + wstring getName() { return L"save-all"; } + wstring getUsage() { return L"/save-all - Forces the server to save"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/SaveOffCommand.cpp b/Commands/SaveOffCommand.cpp new file mode 100644 index 0000000..3513ea3 --- /dev/null +++ b/Commands/SaveOffCommand.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "SaveOffCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/ServerLevel.h" + +void SaveOffCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + for (unsigned int i = 0; i < server->levels.length; i++) + { + ServerLevel *level = server->levels[i]; + if (level) + level->noSave = true; + } + ServerCommand::notifyAdmins(src, server, L"Turned off world auto-saving"); +} diff --git a/Commands/SaveOffCommand.h b/Commands/SaveOffCommand.h new file mode 100644 index 0000000..116be57 --- /dev/null +++ b/Commands/SaveOffCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class SaveOffCommand : public ServerCommand +{ +public: + wstring getName() { return L"save-off"; } + wstring getUsage() { return L"/save-off - Disables automatic server saves"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/SaveOnCommand.cpp b/Commands/SaveOnCommand.cpp new file mode 100644 index 0000000..98dc6e6 --- /dev/null +++ b/Commands/SaveOnCommand.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "SaveOnCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/ServerLevel.h" + +void SaveOnCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + for (unsigned int i = 0; i < server->levels.length; i++) + { + ServerLevel *level = server->levels[i]; + if (level) + level->noSave = false; + } + ServerCommand::notifyAdmins(src, server, L"Turned on world auto-saving"); +} diff --git a/Commands/SaveOnCommand.h b/Commands/SaveOnCommand.h new file mode 100644 index 0000000..214ea56 --- /dev/null +++ b/Commands/SaveOnCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class SaveOnCommand : public ServerCommand +{ +public: + wstring getName() { return L"save-on"; } + wstring getUsage() { return L"/save-on - Enables automatic server saves"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/SayCommand.cpp b/Commands/SayCommand.cpp new file mode 100644 index 0000000..c1bda27 --- /dev/null +++ b/Commands/SayCommand.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "SayCommand.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.World/ChatPacket.h" + +void SayCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + if (args.empty()) + { + src->warn(L"Usage: /say "); + return; + } + wstring msg = L"[Server] " + args; + server->getPlayers()->broadcastAll(shared_ptr(new ChatPacket(msg))); + src->info(L"\u00A7d[Server] " + args); +} diff --git a/Commands/SayCommand.h b/Commands/SayCommand.h new file mode 100644 index 0000000..8da0cd2 --- /dev/null +++ b/Commands/SayCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class SayCommand : public ServerCommand +{ +public: + wstring getName() { return L"say"; } + wstring getUsage() { return L"/say - Broadcasts a message to all players"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/SeedCommand.cpp b/Commands/SeedCommand.cpp new file mode 100644 index 0000000..1d91efe --- /dev/null +++ b/Commands/SeedCommand.cpp @@ -0,0 +1,14 @@ +#include "stdafx.h" +#include "SeedCommand.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/ServerLevel.h" +#include "../../Minecraft.World/Level.h" + +void SeedCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + __int64 seed = server->getLevel(0)->getSeed(); + wchar_t buf[64]; + swprintf_s(buf, 64, L"Seed: %lld", seed); + src->info(wstring(buf)); +} diff --git a/Commands/SeedCommand.h b/Commands/SeedCommand.h new file mode 100644 index 0000000..06eb12e --- /dev/null +++ b/Commands/SeedCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class SeedCommand : public ServerCommand +{ +public: + wstring getName() { return L"seed"; } + wstring getUsage() { return L"/seed - Displays the world seed"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/ServerCommand.h b/Commands/ServerCommand.h new file mode 100644 index 0000000..61de9a0 --- /dev/null +++ b/Commands/ServerCommand.h @@ -0,0 +1,15 @@ +#pragma once + +class MinecraftServer; +class ConsoleInputSource; + +class ServerCommand +{ +public: + virtual ~ServerCommand() {} + virtual wstring getName() = 0; + virtual wstring getUsage() = 0; + virtual void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) = 0; + + static void notifyAdmins(ConsoleInputSource *src, MinecraftServer *server, const wstring& message); +}; diff --git a/Commands/ServerCommands.cpp b/Commands/ServerCommands.cpp new file mode 100644 index 0000000..fafdda8 --- /dev/null +++ b/Commands/ServerCommands.cpp @@ -0,0 +1,186 @@ +#include "stdafx.h" +#include "ServerCommands.h" +#include "ConsoleCommandDispatcher.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ServerPlayer.h" + +#include "StopCommand.h" +#include "HelpCommand.h" +#include "TpCommand.h" +#include "TimeServerCommand.h" +#include "ToggleDownfallServerCommand.h" +#include "GiveServerCommand.h" +#include "EnchantServerCommand.h" +#include "KillServerCommand.h" +#include "GameModeServerCommand.h" +#include "ListServerCommand.h" +#include "KickServerCommand.h" +#include "SayCommand.h" +#include "MeCommand.h" +#include "SeedCommand.h" +#include "XpCommand.h" +#include "DefaultGameModeCommand.h" +#include "SaveAllCommand.h" +#include "SaveOffCommand.h" +#include "SaveOnCommand.h" +#include "DebugCommand.h" +#include "OpCommand.h" +#include "DeOpCommand.h" +#include "BanCommand.h" +#include "PardonCommand.h" +#include "BanIpCommand.h" +#include "PardonIpCommand.h" +#include "BanListCommand.h" +#include "WhitelistCommand.h" + +#include "../../Minecraft.Client/ConsoleInputSource.h" + +void ServerCommand::notifyAdmins(ConsoleInputSource *src, MinecraftServer *server, const wstring& message) +{ + src->info(message); + if (server && server->getPlayers()) + server->getPlayers()->broadcastToAllOps(L"\u00A77\u00A7o[" + src->getConsoleName() + L": " + message + L"]"); +} + +static ConsoleCommandDispatcher *s_dispatcher = NULL; + +ConsoleCommandDispatcher *CreateConsoleCommandDispatcher() +{ + ConsoleCommandDispatcher *dispatcher = new ConsoleCommandDispatcher(); + + dispatcher->addCommand(new StopCommand()); + dispatcher->addCommand(new TpCommand()); + + dispatcher->addCommand(new TimeServerCommand()); + dispatcher->addCommand(new ToggleDownfallServerCommand()); + dispatcher->addCommand(new GiveServerCommand()); + dispatcher->addCommand(new EnchantServerCommand()); + dispatcher->addCommand(new KillServerCommand()); + dispatcher->addCommand(new GameModeServerCommand()); + dispatcher->addCommand(new ListServerCommand()); + dispatcher->addCommand(new KickServerCommand()); + dispatcher->addCommand(new SayCommand()); + dispatcher->addCommand(new MeCommand()); + dispatcher->addCommand(new SeedCommand()); + dispatcher->addCommand(new XpCommand()); + dispatcher->addCommand(new DefaultGameModeCommand()); + dispatcher->addCommand(new SaveAllCommand()); + dispatcher->addCommand(new SaveOffCommand()); + dispatcher->addCommand(new SaveOnCommand()); + dispatcher->addCommand(new DebugCommand()); + dispatcher->addCommand(new OpCommand()); + dispatcher->addCommand(new DeOpCommand()); + dispatcher->addCommand(new BanCommand()); + dispatcher->addCommand(new PardonCommand()); + dispatcher->addCommand(new BanIpCommand()); + dispatcher->addCommand(new PardonIpCommand()); + dispatcher->addCommand(new BanListCommand()); + dispatcher->addCommand(new WhitelistCommand()); + + dispatcher->addCommand(new HelpCommand(dispatcher)); + + return dispatcher; +} + +bool HandleServerCommand(const wstring& rawCmd, ConsoleInputSource *src, MinecraftServer *server) +{ + wstring cmd = rawCmd; + + size_t start = cmd.find_first_not_of(L" \t"); + if (start == wstring::npos) return false; + cmd = cmd.substr(start); + size_t end = cmd.find_last_not_of(L" \t"); + if (end != wstring::npos) cmd = cmd.substr(0, end + 1); + + if (!cmd.empty() && cmd[0] == L'/') cmd = cmd.substr(1); + if (cmd.empty()) return false; + + wstring cmdName; + wstring cmdArgs; + size_t spacePos = cmd.find(L' '); + if (spacePos != wstring::npos) + { + cmdName = cmd.substr(0, spacePos); + cmdArgs = cmd.substr(spacePos + 1); + } + else + { + cmdName = cmd; + } + + for (size_t i = 0; i < cmdName.size(); i++) + cmdName[i] = towlower(cmdName[i]); + + if (!s_dispatcher) + s_dispatcher = CreateConsoleCommandDispatcher(); + + if (cmdName == L"?") + cmdName = L"help"; + + if (!s_dispatcher->performCommand(cmdName, cmdArgs, src, server)) + { + src->warn(L"Unknown command: " + cmdName + L". Type \"help\" for help."); + } + + return true; +} + +vector GetServerCommandCompletions(const wstring& input, MinecraftServer *server) +{ + vector results; + + if (!s_dispatcher) + s_dispatcher = CreateConsoleCommandDispatcher(); + + wstring trimmed = input; + if (!trimmed.empty() && trimmed[0] == L'/') + trimmed = trimmed.substr(1); + + size_t spacePos = trimmed.find(L' '); + + if (spacePos == wstring::npos) + { + wstring partial = trimmed; + for (size_t i = 0; i < partial.size(); i++) + partial[i] = towlower(partial[i]); + + const map& cmds = s_dispatcher->getCommands(); + for (map::const_iterator it = cmds.begin(); it != cmds.end(); ++it) + { + if (it->first.find(partial) == 0) + results.push_back(it->first); + } + } + else + { + wstring lastWord; + size_t lastSpacePos = trimmed.find_last_of(L' '); + if (lastSpacePos != wstring::npos && lastSpacePos + 1 < trimmed.size()) + lastWord = trimmed.substr(lastSpacePos + 1); + + wstring partialLower = lastWord; + for (size_t i = 0; i < partialLower.size(); i++) + partialLower[i] = towlower(partialLower[i]); + + if (server) + { + PlayerList *playerList = server->getPlayers(); + if (playerList) + { + for (size_t i = 0; i < playerList->players.size(); i++) + { + wstring pname = playerList->players[i]->name; + wstring pnameLower = pname; + for (size_t j = 0; j < pnameLower.size(); j++) + pnameLower[j] = towlower(pnameLower[j]); + + if (pnameLower.find(partialLower) == 0) + results.push_back(pname); + } + } + } + } + + return results; +} \ No newline at end of file diff --git a/Commands/ServerCommands.h b/Commands/ServerCommands.h new file mode 100644 index 0000000..673057a --- /dev/null +++ b/Commands/ServerCommands.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class MinecraftServer; +class ConsoleInputSource; +class ConsoleCommandDispatcher; + +ConsoleCommandDispatcher *CreateConsoleCommandDispatcher(); +bool HandleServerCommand(const wstring& cmd, ConsoleInputSource *src, MinecraftServer *server); +vector GetServerCommandCompletions(const wstring& input, MinecraftServer *server); \ No newline at end of file diff --git a/Commands/ServerTextList.h b/Commands/ServerTextList.h new file mode 100644 index 0000000..d5afde4 --- /dev/null +++ b/Commands/ServerTextList.h @@ -0,0 +1,67 @@ +#pragma once +#include +#include +#include + +class ServerTextList +{ +private: + std::wstring filename; + std::set entries; + +public: + ServerTextList(const std::wstring& filename) : filename(filename) { load(); } + + void load() + { + entries.clear(); + std::wifstream file( +#ifdef __linux__ + std::string(filename.begin(), filename.end()).c_str() +#else + filename.c_str() +#endif + ); + if (!file.is_open()) return; + std::wstring line; + while (std::getline(file, line)) + { + if (!line.empty()) + entries.insert(line); + } + } + + void save() + { + std::wofstream file( +#ifdef __linux__ + std::string(filename.begin(), filename.end()).c_str() +#else + filename.c_str() +#endif + ); + if (!file.is_open()) return; + for (std::set::const_iterator it = entries.begin(); it != entries.end(); ++it) + file << *it << L"\n"; + } + + bool contains(const std::wstring& entry) const + { + return entries.find(entry) != entries.end(); + } + + void add(const std::wstring& entry) + { + entries.insert(entry); + save(); + } + + void remove(const std::wstring& entry) + { + entries.erase(entry); + save(); + } + + const std::set& getEntries() const { return entries; } + int size() const { return (int)entries.size(); } +}; diff --git a/Commands/StopCommand.cpp b/Commands/StopCommand.cpp new file mode 100644 index 0000000..1cc9d2b --- /dev/null +++ b/Commands/StopCommand.cpp @@ -0,0 +1,12 @@ +#include "stdafx.h" +#include "StopCommand.h" +#include "ServerCommand.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" + +void StopCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + ServerCommand::notifyAdmins(src, server, L"Stopping the server"); + MinecraftServer::HaltServer(); +} diff --git a/Commands/StopCommand.h b/Commands/StopCommand.h new file mode 100644 index 0000000..7d0564e --- /dev/null +++ b/Commands/StopCommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ServerCommand.h" + +class StopCommand : public ServerCommand +{ +public: + wstring getName() { return L"stop"; } + wstring getUsage() { return L"/stop"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/TimeServerCommand.cpp b/Commands/TimeServerCommand.cpp new file mode 100644 index 0000000..603fa0a --- /dev/null +++ b/Commands/TimeServerCommand.cpp @@ -0,0 +1,93 @@ +#include "stdafx.h" +#include "TimeServerCommand.h" +#include "ServerCommand.h" +#include "ConsoleCommandSender.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ServerLevel.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.World/GameCommandPacket.h" +#include "../../Minecraft.World/TimeCommand.h" +#include "../../Minecraft.World/ByteArrayOutputStream.h" +#include "../../Minecraft.World/DataOutputStream.h" + +#include + +void TimeServerCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring subCmd; + if (!(ss >> subCmd)) + { + src->warn(L"\u00A7cUsage: /time "); + return; + } + + for (size_t i = 0; i < subCmd.size(); i++) + subCmd[i] = towlower(subCmd[i]); + + if (subCmd == L"set") + { + wstring valueStr; + if (!(ss >> valueStr)) + { + src->warn(L"\u00A7cUsage: /time "); + return; + } + + int value; + if (valueStr == L"day") + value = 0; + else if (valueStr == L"night") + value = 12500; + else + { + value = _wtoi(valueStr.c_str()); + if (value < 0) + { + wchar_t errBuf[256]; + swprintf_s(errBuf, 256, L"\u00A7cThe number you have entered (%d) is too small, it must be at least 0", value); + src->warn(wstring(errBuf)); + return; + } + } + + for (int i = 0; i < (int)server->levels.length; i++) + server->levels[i]->setTimeAndAdjustTileTicks(value); + + wchar_t buf[128]; + swprintf_s(buf, 128, L"Set the time to %d", value); + ServerCommand::notifyAdmins(src, server, wstring(buf)); + } + else if (subCmd == L"add") + { + wstring valueStr; + if (!(ss >> valueStr)) + { + src->warn(L"\u00A7cUsage: /time "); + return; + } + int value = _wtoi(valueStr.c_str()); + if (value < 0) + { + wchar_t errBuf[256]; + swprintf_s(errBuf, 256, L"\u00A7cThe number you have entered (%d) is too small, it must be at least 0", value); + src->warn(wstring(errBuf)); + return; + } + + for (int i = 0; i < (int)server->levels.length; i++) + { + ServerLevel *level = server->levels[i]; + level->setTimeAndAdjustTileTicks(level->getTime() + value); + } + + wchar_t buf[128]; + swprintf_s(buf, 128, L"Added %d to the time", value); + ServerCommand::notifyAdmins(src, server, wstring(buf)); + } + else + { + src->warn(L"\u00A7cUsage: /time "); + } +} diff --git a/Commands/TimeServerCommand.h b/Commands/TimeServerCommand.h new file mode 100644 index 0000000..005eda8 --- /dev/null +++ b/Commands/TimeServerCommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ServerCommand.h" + +class TimeServerCommand : public ServerCommand +{ +public: + wstring getName() { return L"time"; } + wstring getUsage() { return L"/time "; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/ToggleDownfallServerCommand.cpp b/Commands/ToggleDownfallServerCommand.cpp new file mode 100644 index 0000000..ae281c6 --- /dev/null +++ b/Commands/ToggleDownfallServerCommand.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "ToggleDownfallServerCommand.h" +#include "ServerCommand.h" +#include "ConsoleCommandSender.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.World/CommandDispatcher.h" +#include "../../Minecraft.World/ByteArrayOutputStream.h" + +void ToggleDownfallServerCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + shared_ptr sender(new ConsoleCommandSender(src)); + CommandDispatcher *dispatcher = server->getCommandDispatcher(); + dispatcher->performCommand(sender, eGameCommand_ToggleDownfall, byteArray()); + ServerCommand::notifyAdmins(src, server, L"Toggled downfall"); +} diff --git a/Commands/ToggleDownfallServerCommand.h b/Commands/ToggleDownfallServerCommand.h new file mode 100644 index 0000000..b452715 --- /dev/null +++ b/Commands/ToggleDownfallServerCommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ServerCommand.h" + +class ToggleDownfallServerCommand : public ServerCommand +{ +public: + wstring getName() { return L"toggledownfall"; } + wstring getUsage() { return L"/toggledownfall"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/TpCommand.cpp b/Commands/TpCommand.cpp new file mode 100644 index 0000000..0a3e6c1 --- /dev/null +++ b/Commands/TpCommand.cpp @@ -0,0 +1,67 @@ +#include "stdafx.h" +#include "TpCommand.h" +#include "ServerCommand.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.Client/PlayerConnection.h" + +#include +#include + +void TpCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + vector tokens; + wstring token; + while (ss >> token) + tokens.push_back(token); + + PlayerList *players = server->getPlayers(); + + if (tokens.size() == 2) + { + shared_ptr subject = players->getPlayer(tokens[0]); + if (!subject) + { + src->warn(L"\u00A7cThat player cannot be found"); + return; + } + shared_ptr dest = players->getPlayer(tokens[1]); + if (!dest) + { + src->warn(L"\u00A7cThat player cannot be found"); + return; + } + subject->moveTo(dest->x, dest->y, dest->z, dest->yRot, dest->xRot); + if (subject->connection) + subject->connection->teleport(dest->x, dest->y, dest->z, dest->yRot, dest->xRot); + wchar_t buf[256]; + swprintf_s(buf, 256, L"Teleported %s to %s", subject->name.c_str(), dest->name.c_str()); + ServerCommand::notifyAdmins(src, server, wstring(buf)); + } + else if (tokens.size() == 4) + { + shared_ptr subject = players->getPlayer(tokens[0]); + if (!subject) + { + src->warn(L"\u00A7cThat player cannot be found"); + return; + } + int x = _wtoi(tokens[1].c_str()); + int y = _wtoi(tokens[2].c_str()); + int z = _wtoi(tokens[3].c_str()); + subject->moveTo(x + 0.5, (double)y, z + 0.5, subject->yRot, subject->xRot); + if (subject->connection) + subject->connection->teleport(x + 0.5, (double)y, z + 0.5, subject->yRot, subject->xRot); + wchar_t buf[256]; + swprintf_s(buf, 256, L"Teleported %s to %d, %d, %d", subject->name.c_str(), x, y, z); + ServerCommand::notifyAdmins(src, server, wstring(buf)); + } + else + { + src->warn(L"\u00A7cUsage: /tp [player] OR /tp [player] "); + } +} diff --git a/Commands/TpCommand.h b/Commands/TpCommand.h new file mode 100644 index 0000000..6e6b2a8 --- /dev/null +++ b/Commands/TpCommand.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ServerCommand.h" + +class TpCommand : public ServerCommand +{ +public: + wstring getName() { return L"tp"; } + wstring getUsage() { return L"/tp [player] OR /tp [player] "; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/WhitelistCommand.cpp b/Commands/WhitelistCommand.cpp new file mode 100644 index 0000000..35918a3 --- /dev/null +++ b/Commands/WhitelistCommand.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" +#include "WhitelistCommand.h" +#include "ServerCommand.h" +#include "ServerTextList.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../Core/ServerLists.h" +#include "ServerTextList.h" + +#include +#include + +void WhitelistCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring subCmd; + if (!(ss >> subCmd)) + { + src->warn(L"Usage: /whitelist "); + return; + } + + if (subCmd == L"on") + { + ServerLists_SetWhitelistEnabled(true); + ServerCommand::notifyAdmins(src, server, L"Turned on the whitelist"); + } + else if (subCmd == L"off") + { + ServerLists_SetWhitelistEnabled(false); + ServerCommand::notifyAdmins(src, server, L"Turned off the whitelist"); + } + else if (subCmd == L"list") + { + const set& entries = ServerLists_GetWhitelist()->getEntries(); + wchar_t buf[128]; + swprintf_s(buf, 128, L"There are %d whitelisted players:", (int)entries.size()); + src->info(wstring(buf)); + wstring list; + for (set::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + if (!list.empty()) list += L", "; + list += *it; + } + if (!list.empty()) src->info(list); + } + else if (subCmd == L"add") + { + wstring playerName; + if (!(ss >> playerName)) + { + src->warn(L"Usage: /whitelist add "); + return; + } + wstring lower = playerName; + for (size_t i = 0; i < lower.size(); i++) + lower[i] = towlower(lower[i]); + ServerLists_GetWhitelist()->add(lower); + ServerCommand::notifyAdmins(src, server, L"Added " + playerName + L" to the whitelist"); + } + else if (subCmd == L"remove") + { + wstring playerName; + if (!(ss >> playerName)) + { + src->warn(L"Usage: /whitelist remove "); + return; + } + wstring lower = playerName; + for (size_t i = 0; i < lower.size(); i++) + lower[i] = towlower(lower[i]); + ServerLists_GetWhitelist()->remove(lower); + ServerCommand::notifyAdmins(src, server, L"Removed " + playerName + L" from the whitelist"); + } + else if (subCmd == L"reload") + { + ServerLists_GetWhitelist()->load(); + ServerCommand::notifyAdmins(src, server, L"Reloaded the whitelist"); + } + else + { + src->warn(L"Usage: /whitelist "); + } +} diff --git a/Commands/WhitelistCommand.h b/Commands/WhitelistCommand.h new file mode 100644 index 0000000..7ae9112 --- /dev/null +++ b/Commands/WhitelistCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class WhitelistCommand : public ServerCommand +{ +public: + wstring getName() { return L"whitelist"; } + wstring getUsage() { return L"/whitelist [player] - Manages the whitelist"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Commands/XpCommand.cpp b/Commands/XpCommand.cpp new file mode 100644 index 0000000..a4c64a7 --- /dev/null +++ b/Commands/XpCommand.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "XpCommand.h" +#include "ServerCommand.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" + +#include + +void XpCommand::execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server) +{ + wstringstream ss(args); + wstring amountStr; + if (!(ss >> amountStr)) + { + src->warn(L"Usage: /xp [player] OR /xp L [player]"); + return; + } + + bool isLevels = false; + if (!amountStr.empty() && (amountStr.back() == L'l' || amountStr.back() == L'L')) + { + isLevels = true; + amountStr = amountStr.substr(0, amountStr.size() - 1); + } + + int amount = _wtoi(amountStr.c_str()); + + wstring playerName; + if (!(ss >> playerName)) + { + src->warn(L"Usage: /xp "); + return; + } + + PlayerList *players = server->getPlayers(); + shared_ptr player = players->getPlayer(playerName); + if (!player) + { + src->warn(L"Player not found: " + playerName); + return; + } + + wchar_t buf[256]; + if (isLevels) + { + player->withdrawExperienceLevels(-amount); + swprintf_s(buf, 256, L"Given %d experience levels to %s", amount, player->name.c_str()); + } + else + { + if (amount < 0) + { + src->warn(L"Cannot give negative experience points"); + return; + } + player->increaseXp(amount); + swprintf_s(buf, 256, L"Given %d experience to %s", amount, player->name.c_str()); + } + ServerCommand::notifyAdmins(src, server, wstring(buf)); +} diff --git a/Commands/XpCommand.h b/Commands/XpCommand.h new file mode 100644 index 0000000..1137f34 --- /dev/null +++ b/Commands/XpCommand.h @@ -0,0 +1,10 @@ +#pragma once +#include "ServerCommand.h" + +class XpCommand : public ServerCommand +{ +public: + wstring getName() { return L"xp"; } + wstring getUsage() { return L"/xp [player] OR /xp L [player] - Gives experience"; } + void execute(const wstring& args, ConsoleInputSource *src, MinecraftServer *server); +}; diff --git a/Core/DedicatedServer.cpp b/Core/DedicatedServer.cpp new file mode 100644 index 0000000..ba9d4d2 --- /dev/null +++ b/Core/DedicatedServer.cpp @@ -0,0 +1,559 @@ +#include "stdafx.h" +#include "DedicatedServer.h" + +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/Minecraft.h" +#include "../../Minecraft.Client/ProgressRenderer.h" +#include "../../Minecraft.Client/GameRenderer.h" +#include "../../Minecraft.Client/Options.h" +#include "../../Minecraft.Client/User.h" +#include "../../Minecraft.Client/StatsCounter.h" +#include "../../Minecraft.Client/Windows64/Network/WinsockNetLayer.h" +#include "../../Minecraft.Client/Common/Network/PlatformNetworkManagerStub.h" +#include "../../Minecraft.World/Minecraft.World.h" +#include "../../Minecraft.World/System.h" +#include "../../Minecraft.World/AABB.h" +#include "../../Minecraft.World/Vec3.h" +#include "../../Minecraft.World/IntCache.h" +#include "../../Minecraft.World/compression.h" +#include "../../Minecraft.World/OldChunkStorage.h" +#include "../../Minecraft.World/Level.h" +#include "../../Minecraft.World/Tile.h" +#include "../../Minecraft.World/Entity.h" +#include "../../Minecraft.World/SharedConstants.h" +#include "../../Minecraft.World/ChunkSource.h" +#include "ServerLogger.h" +#include "ServerLists.h" +#include "../Commands/ServerCommands.h" + +#ifdef __linux__ +#include +#include +#else +#include +#endif + +DedicatedServer::DedicatedServer() + : m_running(false) +{ +} + +DedicatedServer::~DedicatedServer() +{ +} + +bool DedicatedServer::init() +{ + if (!m_properties.load(L"server.properties")) + { + ServerLog(L"server.properties not found, generating defaults\n"); + m_properties.save(L"server.properties"); + } + + ServerLog(L"Loading properties\n"); + ServerLog(L"Default game type: %s\n", m_properties.gamemode == 0 ? L"SURVIVAL" : L"CREATIVE"); + ServerLog(L"Max players: %d\n", m_properties.maxPlayers); + ServerLog(L"Difficulty: %d\n", m_properties.difficulty); + ServerLog(L"Level size: %s\n", m_properties.levelSize.c_str()); + + ServerLists_Init(m_properties.whiteList, &m_properties); + + extern bool g_voiceChatEnabled; + g_voiceChatEnabled = m_properties.voiceChat; + + extern bool g_ServerAdvertiseLAN; + g_ServerAdvertiseLAN = m_properties.advertiseLan; + + extern char g_ServerBindAddress[256]; + if (!m_properties.serverIp.empty()) + { + wcstombs(g_ServerBindAddress, m_properties.serverIp.c_str(), sizeof(g_ServerBindAddress) - 1); + g_ServerBindAddress[sizeof(g_ServerBindAddress) - 1] = '\0'; + } + else + { + g_ServerBindAddress[0] = '\0'; + } + if (g_voiceChatEnabled && m_properties.maxPlayers > 8) + { + ServerLog(L"Voice chat has been disabled: max-players (%d) exceeds the maximum supported for voice chat (8)\n", m_properties.maxPlayers); + g_voiceChatEnabled = false; + } + else if (g_voiceChatEnabled) + { + ServerLog(L"Voice chat is enabled\n"); + } + + AABB::CreateNewThreadStorage(); + Vec3::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + Compression::CreateNewThreadStorage(); + OldChunkStorage::CreateNewThreadStorage(); + Level::enableLightingCache(); + Tile::CreateNewThreadStorage(); + + MinecraftWorld_RunStaticCtors(); + + { + wchar_t exePath[MAX_PATH]; + if (GetModuleFileNameW(NULL, exePath, MAX_PATH)) + { + wstring exeDir(exePath); + size_t lastSlash = exeDir.find_last_of(L"\\/"); + if (lastSlash != wstring::npos) + exeDir = exeDir.substr(0, lastSlash); + SetCurrentDirectoryW(exeDir.c_str()); +#ifdef __linux__ + wstring gameHDDPath = exeDir + L"/Linux/GameHDD"; + CreateDirectoryW((exeDir + L"/Linux").c_str(), NULL); +#else + wstring gameHDDPath = exeDir + L"\\Windows64\\GameHDD"; + CreateDirectoryW((exeDir + L"\\Windows64").c_str(), NULL); +#endif + CreateDirectoryW(gameHDDPath.c_str(), NULL); + } + } + + StorageManager.Init(1, app.GetString(IDS_DEFAULT_SAVENAME), "savegame.dat", FIFTY_ONE_MB, NULL, NULL, "MinecraftDedicatedServer"); + StorageManager.SetMaxSaves(99); + StorageManager.StoreTMSPathName(); + + g_NetworkManager.Initialise(); + + for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++) + { + IQNet::m_player[i].m_smallId = (BYTE)i; + IQNet::m_player[i].m_isRemote = false; + IQNet::m_player[i].m_isHostPlayer = (i == 0); + swprintf_s(IQNet::m_player[i].m_gamertag, 32, L"Server%d", i); + } + wcscpy_s(IQNet::m_player[0].m_gamertag, 32, m_properties.motd.c_str()); + + WinsockNetLayer::Initialize(); + + app.SetGameHostOption(eGameHostOption_Difficulty, m_properties.difficulty); + app.SetGameHostOption(eGameHostOption_GameType, m_properties.gamemode); + app.SetGameHostOption(eGameHostOption_PvP, m_properties.pvp ? 1 : 0); + app.SetGameHostOption(eGameHostOption_TrustPlayers, m_properties.trustPlayers ? 1 : 0); + app.SetGameHostOption(eGameHostOption_FireSpreads, m_properties.fireSpreads ? 1 : 0); + app.SetGameHostOption(eGameHostOption_TNT, m_properties.tntExplodes ? 1 : 0); + app.SetGameHostOption(eGameHostOption_Structures, m_properties.structures ? 1 : 0); + app.SetGameHostOption(eGameHostOption_Gamertags, m_properties.showGamertags ? 1 : 0); + + Minecraft *pMinecraft = new Minecraft(NULL, NULL, NULL, 1, 1, false); + pMinecraft->options = new Options(pMinecraft, File(L".")); + pMinecraft->progressRenderer = new ProgressRenderer(pMinecraft); + pMinecraft->gameRenderer = new GameRenderer(pMinecraft); + for (int i = 0; i < 4; i++) + pMinecraft->stats[i] = new StatsCounter(); + + { + static DWORD dwProfileSettingsA[5] = {0, 0, 0, 0, 0}; + ProfileManager.Initialise(TITLEID_MINECRAFT, + app.m_dwOfferID, + PROFILE_VERSION_10, + 5, + 4, + dwProfileSettingsA, + app.GAME_DEFINED_PROFILE_DATA_BYTES * XUSER_MAX_COUNT, + &app.uiGameDefinedDataChangedBitmask); + } + + app.InitGameSettings(); + + if (!m_properties.serverIp.empty()) + ServerLog(L"Starting Minecraft server on %s:%d\n", m_properties.serverIp.c_str(), m_properties.serverPort); + else + ServerLog(L"Starting Minecraft server on *:%d\n", m_properties.serverPort); + m_running = true; + return true; +} + +int DedicatedServer::run() +{ + if (!m_running) + { + ServerWarn(L"Server not initialized!\n"); + return 1; + } + + ServerLog(L"Preparing level \"%s\"\n", m_properties.levelName.c_str()); + + StorageManager.GetSavesInfo(0, NULL, NULL, NULL); + PSAVE_DETAILS pSaveDetails = StorageManager.ReturnSavesInfo(); + if (pSaveDetails && pSaveDetails->iSaveC > 0) + { + int bestIdx = 0; + time_t bestTime = pSaveDetails->SaveInfoA[0].metaData.modifiedTime; + for (int i = 1; i < pSaveDetails->iSaveC; i++) + { + if (pSaveDetails->SaveInfoA[i].metaData.modifiedTime > bestTime) + { + bestTime = pSaveDetails->SaveInfoA[i].metaData.modifiedTime; + bestIdx = i; + } + } + ServerLog(L"Loading existing world from save \"%S\"\n", pSaveDetails->SaveInfoA[bestIdx].UTF8SaveFilename); + StorageManager.LoadSaveData(&pSaveDetails->SaveInfoA[bestIdx], NULL, NULL); + } + else + { + ServerLog(L"No existing save found, creating new world\n"); + wstring title = m_properties.levelName; + StorageManager.SetSaveTitle(title.c_str()); + } + + g_NetworkManager.HostGame(1, true, false, (unsigned char)m_properties.maxPlayers, 0); + + extern CPlatformNetworkManagerStub *g_pPlatformNetworkManager; + g_pPlatformNetworkManager->NotifyPlayerJoined(&IQNet::m_player[0]); + + g_NetworkManager.ServerReadyCreate(true); + g_NetworkManager.ServerStoppedCreate(true); + + NetworkGameInitData initData; + initData.seed = m_properties.levelSeed; + initData.saveData = NULL; + initData.settings = 0; + initData.levelGen = NULL; + initData.texturePackId = 0; + initData.findSeed = (m_properties.levelSeed == 0); + + if (m_properties.levelSize == L"classic") + { + initData.xzSize = 1 * 54; + initData.hellScale = 3; + } + else if (m_properties.levelSize == L"small") + { + initData.xzSize = 1 * 64; + initData.hellScale = 3; + } + else if (m_properties.levelSize == L"medium") + { + initData.xzSize = 3 * 64; + initData.hellScale = 6; + } + else + { + initData.xzSize = LEVEL_MAX_WIDTH; + initData.hellScale = HELL_LEVEL_MAX_SCALE; + } + + AABB::CreateNewThreadStorage(); + Vec3::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + Compression::UseDefaultThreadStorage(); + Entity::useSmallIds(); + Level::enableLightingCache(); + Tile::CreateNewThreadStorage(); + + ServerLog(L"Preparing start region for level 0\n"); + MinecraftServer::main(m_properties.levelSeed, &initData); + + g_NetworkManager.ServerReadyDestroy(); + g_NetworkManager.ServerStoppedDestroy(); + + ServerLog(L"Server stopped\n"); + return 0; +} + +void DedicatedServer::shutdown() +{ + ServerLog(L"Stopping server\n"); + m_running = false; + MinecraftServer::HaltServer(); + + Tile::ReleaseThreadStorage(); + IntCache::ReleaseThreadStorage(); + AABB::ReleaseThreadStorage(); + Vec3::ReleaseThreadStorage(); + Level::destroyLightingCache(); + + g_NetworkManager.Terminate(); + ServerLog(L"Shutdown complete\n"); +} + +void DedicatedServer::info(const wstring& string) +{ + ServerLog(L"%s\n", string.c_str()); +} + +void DedicatedServer::warn(const wstring& string) +{ + ServerWarn(L"%s\n", string.c_str()); +} + +wstring DedicatedServer::getConsoleName() +{ + return L"Server"; +} + +int DedicatedServer::consoleInputThread(void *param) +{ + DedicatedServer *server = (DedicatedServer *)param; + +#ifdef __linux__ + struct termios origTermios; + tcgetattr(STDIN_FILENO, &origTermios); + { + struct termios raw = origTermios; + raw.c_lflag &= ~(ICANON | ECHO); + raw.c_cc[VMIN] = 1; + raw.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); + } +#endif + + wstring buffer; + vector history; + int historyPos = -1; + wstring savedBuffer; + ServerLog_RegisterInput(&buffer); + + auto clearLine = [&]() { + if (buffer.empty()) return; +#ifdef __linux__ + for (size_t i = 0; i < buffer.size(); i++) + printf("\b \b"); + fflush(stdout); +#else + for (size_t i = 0; i < buffer.size(); i++) + wprintf(L"\b \b"); +#endif + }; + + auto redrawLine = [&]() { +#ifdef __linux__ + printf("%ls", buffer.c_str()); + fflush(stdout); +#else + wprintf(L"%s", buffer.c_str()); +#endif + }; + + while (server->m_running) + { +#ifdef __linux__ + char c; + if (read(STDIN_FILENO, &c, 1) <= 0) break; + wchar_t wc = (wchar_t)(unsigned char)c; +#else + wchar_t wc = _getwch(); +#endif + + if (wc == L'\t') + { + ServerLog_LockOutput(); + MinecraftServer *mcServer = MinecraftServer::getInstance(); + vector completions = GetServerCommandCompletions(buffer, mcServer); + + if (completions.size() == 1) + { + size_t lastSpace = buffer.find_last_of(L' '); + wstring currentWord = (lastSpace == wstring::npos) ? buffer : buffer.substr(lastSpace + 1); + wstring toAppend = completions[0].substr(currentWord.size()) + L" "; + buffer += toAppend; +#ifdef __linux__ + printf("%ls", toAppend.c_str()); + fflush(stdout); +#else + wprintf(L"%s", toAppend.c_str()); +#endif + } + else if (completions.size() > 1) + { + wstring common = completions[0]; + for (size_t i = 1; i < completions.size(); i++) + { + size_t j = 0; + while (j < common.size() && j < completions[i].size() && + towlower(common[j]) == towlower(completions[i][j])) + j++; + common = common.substr(0, j); + } + + size_t lastSpace = buffer.find_last_of(L' '); + wstring currentWord = (lastSpace == wstring::npos) ? buffer : buffer.substr(lastSpace + 1); + + if (common.size() > currentWord.size()) + { + wstring toAppend = common.substr(currentWord.size()); + buffer += toAppend; +#ifdef __linux__ + printf("%ls", toAppend.c_str()); + fflush(stdout); +#else + wprintf(L"%s", toAppend.c_str()); +#endif + } + else + { +#ifdef __linux__ + printf("\n"); + for (size_t i = 0; i < completions.size(); i++) + printf("%ls ", completions[i].c_str()); + printf("\n%ls", buffer.c_str()); + fflush(stdout); +#else + wprintf(L"\n"); + for (size_t i = 0; i < completions.size(); i++) + wprintf(L"%s ", completions[i].c_str()); + wprintf(L"\n%s", buffer.c_str()); +#endif + } + } + ServerLog_UnlockOutput(); + } + else if (wc == L'\r' || wc == L'\n') + { + ServerLog_LockOutput(); +#ifdef __linux__ + printf("\n"); + fflush(stdout); +#else + wprintf(L"\n"); +#endif + wstring cmd = buffer; + buffer.clear(); + ServerLog_UnlockOutput(); + if (!cmd.empty()) + { + if (history.empty() || history.back() != cmd) + history.push_back(cmd); + historyPos = -1; + + if (cmd == L"stop" || cmd == L"quit" || cmd == L"exit") + { + ServerLog(L"Stopping the server\n"); + MinecraftServer::HaltServer(); + server->m_running = false; + break; + } + MinecraftServer *mcServer = MinecraftServer::getInstance(); + if (mcServer) + mcServer->handleConsoleInput(cmd, server); + } + } + else if (wc == 127 || wc == 8) + { + if (!buffer.empty()) + { + ServerLog_LockOutput(); + buffer.pop_back(); +#ifdef __linux__ + printf("\b \b"); + fflush(stdout); +#else + wprintf(L"\b \b"); +#endif + ServerLog_UnlockOutput(); + } + } +#ifdef __linux__ + else if (c == 27) + { + char seq[2]; + if (read(STDIN_FILENO, &seq[0], 1) != 1) continue; + if (seq[0] != '[') continue; + if (read(STDIN_FILENO, &seq[1], 1) != 1) continue; + + if (seq[1] == 'A') + { + if (history.empty()) continue; + if (historyPos == -1) + { + savedBuffer = buffer; + historyPos = (int)history.size() - 1; + } + else if (historyPos > 0) + { + historyPos--; + } + ServerLog_LockOutput(); + clearLine(); + buffer = history[historyPos]; + redrawLine(); + ServerLog_UnlockOutput(); + } + else if (seq[1] == 'B') + { + if (historyPos == -1) continue; + ServerLog_LockOutput(); + clearLine(); + if (historyPos < (int)history.size() - 1) + { + historyPos++; + buffer = history[historyPos]; + } + else + { + historyPos = -1; + buffer = savedBuffer; + } + redrawLine(); + ServerLog_UnlockOutput(); + } + } +#else + else if (wc == 0 || wc == 0xE0) + { + wchar_t ext = _getwch(); + if (ext == 72) + { + if (history.empty()) continue; + if (historyPos == -1) + { + savedBuffer = buffer; + historyPos = (int)history.size() - 1; + } + else if (historyPos > 0) + { + historyPos--; + } + ServerLog_LockOutput(); + clearLine(); + buffer = history[historyPos]; + redrawLine(); + ServerLog_UnlockOutput(); + } + else if (ext == 80) + { + if (historyPos == -1) continue; + ServerLog_LockOutput(); + clearLine(); + if (historyPos < (int)history.size() - 1) + { + historyPos++; + buffer = history[historyPos]; + } + else + { + historyPos = -1; + buffer = savedBuffer; + } + redrawLine(); + ServerLog_UnlockOutput(); + } + } +#endif + else if (wc >= 32) + { + ServerLog_LockOutput(); + buffer += wc; +#ifdef __linux__ + printf("%lc", wc); + fflush(stdout); +#else + wprintf(L"%c", wc); +#endif + ServerLog_UnlockOutput(); + } + } + + ServerLog_UnregisterInput(); +#ifdef __linux__ + tcsetattr(STDIN_FILENO, TCSAFLUSH, &origTermios); +#endif + return 0; +} \ No newline at end of file diff --git a/Core/DedicatedServer.h b/Core/DedicatedServer.h new file mode 100644 index 0000000..c19d2f8 --- /dev/null +++ b/Core/DedicatedServer.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../../Minecraft.Client/ConsoleInputSource.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "ServerProperties.h" + +class DedicatedServer : public ConsoleInputSource +{ +public: + DedicatedServer(); + ~DedicatedServer(); + + bool init(); + + int run(); + + void shutdown(); + + virtual void info(const wstring& string); + virtual void warn(const wstring& string); + virtual wstring getConsoleName(); + + static int consoleInputThread(void *param); + +private: + void processConsoleInput(); + + bool m_running; + ServerProperties m_properties; +}; \ No newline at end of file diff --git a/Core/DedicatedServer_Main.cpp b/Core/DedicatedServer_Main.cpp new file mode 100644 index 0000000..fec36be --- /dev/null +++ b/Core/DedicatedServer_Main.cpp @@ -0,0 +1,140 @@ + +#include "stdafx.h" +#include "DedicatedServer.h" +#include "ServerLogger.h" +#include "../../Minecraft.World/C4JThread.h" +#include "../../Minecraft.Client/Common/BuildVer.h" +#include "../../Minecraft.World/SharedConstants.h" + +#ifdef __linux__ +#include +#include +#include +#include +#else +#include +#include +#include +#endif + +static DedicatedServer g_dedicatedServer; +static volatile bool g_shutdownRequested = false; + +#ifdef __linux__ + +static void SignalHandler(int sig) +{ + if (sig == SIGINT || sig == SIGTERM) + { + printf("\n"); + ServerLog(L"Received shutdown signal, saving and stopping...\n"); + g_shutdownRequested = true; + MinecraftServer::HaltServer(); + } +} + +int main(int argc, char *argv[]) +{ + setlocale(LC_ALL, ""); + ServerLog_Init(); + + ServerLog(L"Starting Minecraft LCE server version %s (protocol %d)\n", VER_FILEVERSION_STR_W, SharedConstants::NETWORK_PROTOCOL_VERSION); + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SignalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + signal(SIGPIPE, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + + if (!g_dedicatedServer.init()) + { + ServerLog(L"Failed to initialize. Exiting.\n"); + return 1; + } + + C4JThread *inputThread = new C4JThread(DedicatedServer::consoleInputThread, &g_dedicatedServer, "ConsoleInput"); + inputThread->Run(); + + int result = g_dedicatedServer.run(); + + g_dedicatedServer.shutdown(); + delete inputThread; + + ServerLog(L"Goodbye.\n"); + ServerLog_Close(); + return result; +} + +#else // Windows + +static HANDLE g_shutdownCompleteEvent = NULL; + +BOOL WINAPI ConsoleCtrlHandler(DWORD ctrlType) +{ + switch (ctrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + wprintf(L"\n"); + ServerLog(L"Received shutdown signal, saving and stopping...\n"); + g_shutdownRequested = true; + MinecraftServer::HaltServer(); + if (g_shutdownCompleteEvent) + WaitForSingleObject(g_shutdownCompleteEvent, 30000); + return TRUE; + case CTRL_CLOSE_EVENT: + FreeConsole(); + g_shutdownRequested = true; + MinecraftServer::HaltServer(); + if (g_shutdownCompleteEvent) + WaitForSingleObject(g_shutdownCompleteEvent, 30000); + ExitProcess(0); + return TRUE; + default: + return FALSE; + } +} + +int wmain(int argc, wchar_t *argv[]) +{ + SetConsoleOutputCP(CP_UTF8); + SetConsoleCP(CP_UTF8); + SetConsoleTitleW(L"LCEMP Server"); + ServerLog_Init(); + + ServerLog(L"Starting Minecraft LCE server version %s (protocol %d)\n", VER_FILEVERSION_STR_W, SharedConstants::NETWORK_PROTOCOL_VERSION); + + SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); + + g_shutdownCompleteEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + + if (!g_dedicatedServer.init()) + { + ServerLog(L"Failed to initialize. Exiting.\n"); + return 1; + } + + C4JThread *inputThread = new C4JThread(DedicatedServer::consoleInputThread, &g_dedicatedServer, "ConsoleInput"); + inputThread->Run(); + + int result = g_dedicatedServer.run(); + + g_dedicatedServer.shutdown(); + if (g_shutdownCompleteEvent) + { + SetEvent(g_shutdownCompleteEvent); + CloseHandle(g_shutdownCompleteEvent); + } + delete inputThread; + + ServerLog(L"Goodbye.\n"); + ServerLog_Close(); + return result; +} + +#endif // __linux__ \ No newline at end of file diff --git a/Core/ServerLists.cpp b/Core/ServerLists.cpp new file mode 100644 index 0000000..b58f424 --- /dev/null +++ b/Core/ServerLists.cpp @@ -0,0 +1,73 @@ +#include "stdafx.h" +#include "ServerLists.h" +#include "ServerProperties.h" +#include "../Commands/ServerTextList.h" +#include + +static ServerTextList* s_bannedPlayers = NULL; +static ServerTextList* s_bannedIPs = NULL; +static ServerTextList* s_whitelist = NULL; +static ServerTextList* s_ops = NULL; +static bool s_whitelistEnabled = false; +static ServerProperties* s_properties = NULL; + +void ServerLists_Init(bool whitelistEnabled, ServerProperties* props) +{ + static ServerTextList bannedPlayers(L"banned-players.txt"); + static ServerTextList bannedIPs(L"banned-ips.txt"); + static ServerTextList whitelist(L"white-list.txt"); + static ServerTextList ops(L"ops.txt"); + + s_bannedPlayers = &bannedPlayers; + s_bannedIPs = &bannedIPs; + s_whitelist = &whitelist; + s_ops = &ops; + s_whitelistEnabled = whitelistEnabled; + s_properties = props; +} + +ServerTextList* ServerLists_GetBannedPlayers() { return s_bannedPlayers; } +ServerTextList* ServerLists_GetBannedIPs() { return s_bannedIPs; } +ServerTextList* ServerLists_GetWhitelist() { return s_whitelist; } +ServerTextList* ServerLists_GetOps() { return s_ops; } + +bool ServerLists_IsWhitelistEnabled() { return s_whitelistEnabled; } +void ServerLists_SetWhitelistEnabled(bool enabled) +{ + s_whitelistEnabled = enabled; + if (s_properties) + { + s_properties->whiteList = enabled; + s_properties->save(L"server.properties"); + } +} + +bool ServerLists_IsPlayerBanned(const std::wstring& name) +{ + if (!s_bannedPlayers) return false; + return s_bannedPlayers->contains(name); +} + +bool ServerLists_IsIPBanned(const std::wstring& ip) +{ + if (!s_bannedIPs) return false; + return s_bannedIPs->contains(ip); +} + +bool ServerLists_IsPlayerOp(const std::wstring& name) +{ + if (!s_ops) return false; + return s_ops->contains(name); +} + +bool ServerLists_IsPlayerWhitelisted(const std::wstring& name) +{ + if (!s_whitelistEnabled) return true; + if (ServerLists_IsPlayerOp(name)) return true; + if (!s_whitelist) return true; + + std::wstring lower = name; + for (size_t i = 0; i < lower.size(); i++) + lower[i] = towlower(lower[i]); + return s_whitelist->contains(lower); +} diff --git a/Core/ServerLists.h b/Core/ServerLists.h new file mode 100644 index 0000000..297bb63 --- /dev/null +++ b/Core/ServerLists.h @@ -0,0 +1,20 @@ +#pragma once +#include + +class ServerTextList; +class ServerProperties; + +void ServerLists_Init(bool whitelistEnabled, ServerProperties* props = 0); + +ServerTextList* ServerLists_GetBannedPlayers(); +ServerTextList* ServerLists_GetBannedIPs(); +ServerTextList* ServerLists_GetWhitelist(); +ServerTextList* ServerLists_GetOps(); + +bool ServerLists_IsWhitelistEnabled(); +void ServerLists_SetWhitelistEnabled(bool enabled); + +bool ServerLists_IsPlayerBanned(const std::wstring& name); +bool ServerLists_IsPlayerWhitelisted(const std::wstring& name); +bool ServerLists_IsPlayerOp(const std::wstring& name); +bool ServerLists_IsIPBanned(const std::wstring& ip); diff --git a/Core/ServerLogger.cpp b/Core/ServerLogger.cpp new file mode 100644 index 0000000..fc4e30b --- /dev/null +++ b/Core/ServerLogger.cpp @@ -0,0 +1,312 @@ +#include "stdafx.h" +#include "ServerLogger.h" + +#include +#include +#include +#include +#include + +using std::string; +using std::wstring; + +#ifdef __linux__ +#include +static pthread_mutex_t g_logMutex = PTHREAD_MUTEX_INITIALIZER; +#else +static CRITICAL_SECTION g_logCS; +#endif + +static FILE* g_logFile = NULL; +static bool g_initialized = false; +static wstring* g_inputBuffer = nullptr; + +void ServerLog_Init() +{ + if (g_initialized) return; + +#ifndef __linux__ + InitializeCriticalSection(&g_logCS); +#endif + +#ifdef __linux__ + g_logFile = fopen("server.log", "a"); +#else + g_logFile = _wfopen(L"server.log", L"a"); +#endif + + if (!g_logFile) + { + fprintf(stderr, "WARNING: Failed to log to server.log\n"); + } + + g_initialized = true; +} + +void ServerLog_Close() +{ + if (!g_initialized) return; + + if (g_logFile) + { + fclose(g_logFile); + g_logFile = NULL; + } + +#ifndef __linux__ + DeleteCriticalSection(&g_logCS); +#endif + + g_initialized = false; +} + +#ifdef __linux__ + +static string processNarrowFormattingCodes(const char* input) +{ + string result; + bool hasColor = false; + for (size_t i = 0; input[i]; i++) + { + if ((unsigned char)input[i] == 0xC2 && (unsigned char)input[i + 1] == 0xA7 && input[i + 2]) + { + char code = tolower((unsigned char)input[i + 2]); + i += 2; + hasColor = true; + switch (code) + { + case '0': result += "\x1b[30m"; break; + case '1': result += "\x1b[34m"; break; + case '2': result += "\x1b[32m"; break; + case '3': result += "\x1b[36m"; break; + case '4': result += "\x1b[31m"; break; + case '5': result += "\x1b[35m"; break; + case '6': result += "\x1b[33m"; break; + case '7': result += "\x1b[37m"; break; + case '8': result += "\x1b[90m"; break; + case '9': result += "\x1b[94m"; break; + case 'a': result += "\x1b[92m"; break; + case 'b': result += "\x1b[96m"; break; + case 'c': result += "\x1b[91m"; break; + case 'd': result += "\x1b[95m"; break; + case 'e': result += "\x1b[93m"; break; + case 'f': result += "\x1b[97m"; break; + case 'l': result += "\x1b[1m"; break; + case 'm': result += "\x1b[9m"; break; + case 'n': result += "\x1b[4m"; break; + case 'o': result += "\x1b[3m"; break; + case 'r': result += "\x1b[0m"; break; + default: break; + } + } + else + { + result += input[i]; + } + } + if (hasColor) result += "\x1b[0m"; + return result; +} + +static string stripNarrowFormattingCodes(const char* input) +{ + string result; + for (size_t i = 0; input[i]; i++) + { + if ((unsigned char)input[i] == 0xC2 && (unsigned char)input[i + 1] == 0xA7 && input[i + 2]) + i += 2; + else + result += input[i]; + } + return result; +} + +static void logImpl(const char* level, const wchar_t* fmt, va_list ap) +{ + time_t now = time(NULL); + struct tm t; + localtime_r(&now, &t); + + char narrowFmt[2048]; + _wfmtToNarrow(fmt, narrowFmt, sizeof(narrowFmt)); + + char narrowMsg[4096]; + vsnprintf(narrowMsg, sizeof(narrowMsg), narrowFmt, ap); + + string consoleMsg = processNarrowFormattingCodes(narrowMsg); + + pthread_mutex_lock(&g_logMutex); + + if (g_inputBuffer && !g_inputBuffer->empty()) + printf("\r\033[K"); + + printf("%04d-%02d-%02d %02d:%02d:%02d [%s] %s", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec, level, consoleMsg.c_str()); + fflush(stdout); + + if (g_logFile) + { + string fileMsg = stripNarrowFormattingCodes(narrowMsg); + fprintf(g_logFile, "%04d-%02d-%02d %02d:%02d:%02d [%s] %s", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec, level, fileMsg.c_str()); + fflush(g_logFile); + } + + if (g_inputBuffer && !g_inputBuffer->empty()) + { + printf("%ls", g_inputBuffer->c_str()); + fflush(stdout); + } + + pthread_mutex_unlock(&g_logMutex); +} + +#else + +static wstring stripWideFormattingCodes(const wchar_t* input) +{ + wstring result; + for (size_t i = 0; input[i]; i++) + { + if (input[i] == L'\u00A7' && input[i + 1]) + i++; + else + result += input[i]; + } + return result; +} + +struct ColorSpan { + size_t start; + size_t end; + WORD attr; +}; + +static void printColored(const wchar_t* text) +{ + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(hConsole, &csbi); + WORD defaultAttr = csbi.wAttributes; + WORD currentAttr = defaultAttr; + + const wchar_t* p = text; + while (*p) + { + if (*p == L'\u00A7' && *(p + 1)) + { + wchar_t code = towlower(*(p + 1)); + p += 2; + switch (code) + { + case L'0': currentAttr = 0; break; + case L'1': currentAttr = FOREGROUND_BLUE; break; + case L'2': currentAttr = FOREGROUND_GREEN; break; + case L'3': currentAttr = FOREGROUND_GREEN | FOREGROUND_BLUE; break; + case L'4': currentAttr = FOREGROUND_RED; break; + case L'5': currentAttr = FOREGROUND_RED | FOREGROUND_BLUE; break; + case L'6': currentAttr = FOREGROUND_RED | FOREGROUND_GREEN; break; + case L'7': currentAttr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; + case L'8': currentAttr = FOREGROUND_INTENSITY; break; + case L'9': currentAttr = FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; + case L'a': currentAttr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; + case L'b': currentAttr = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; + case L'c': currentAttr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; + case L'd': currentAttr = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; + case L'e': currentAttr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; + case L'f': currentAttr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break; + case L'l': currentAttr |= FOREGROUND_INTENSITY; break; + case L'r': currentAttr = defaultAttr; break; + default: break; + } + SetConsoleTextAttribute(hConsole, currentAttr); + } + else + { + wchar_t ch[2] = { *p, 0 }; + wprintf(L"%ls", ch); + p++; + } + } + SetConsoleTextAttribute(hConsole, defaultAttr); +} + +static void logImpl(const char* level, const wchar_t* fmt, va_list ap) +{ + time_t now = time(NULL); + struct tm t; + localtime_s(&t, &now); + + wchar_t wbuf[4096]; + vswprintf_s(wbuf, 4096, fmt, ap); + + wstring stripped = stripWideFormattingCodes(wbuf); + + EnterCriticalSection(&g_logCS); + + if (g_inputBuffer && !g_inputBuffer->empty()) + { + wstring spaces(g_inputBuffer->size(), L' '); + wprintf(L"\r%ls\r", spaces.c_str()); + } + + wprintf(L"%04d-%02d-%02d %02d:%02d:%02d [%S] ", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec, level); + printColored(wbuf); + + if (g_logFile) + { + char narrowMsg[4096]; + size_t converted; + wcstombs_s(&converted, narrowMsg, sizeof(narrowMsg), stripped.c_str(), _TRUNCATE); + + fprintf(g_logFile, "%04d-%02d-%02d %02d:%02d:%02d [%s] %s", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec, level, narrowMsg); + fflush(g_logFile); + } + + if (g_inputBuffer && !g_inputBuffer->empty()) + wprintf(L"%ls", g_inputBuffer->c_str()); + + LeaveCriticalSection(&g_logCS); +} + +#endif // __linux__ + +void ServerLog(const wchar_t* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + logImpl("INFO", fmt, ap); + va_end(ap); +} + +void ServerWarn(const wchar_t* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + logImpl("WARNING", fmt, ap); + va_end(ap); +} + +void ServerLog_RegisterInput(wstring* buffer) +{ + g_inputBuffer = buffer; +} + +void ServerLog_UnregisterInput() +{ + g_inputBuffer = nullptr; +} + +#ifdef __linux__ +void ServerLog_LockOutput() { pthread_mutex_lock(&g_logMutex); } +void ServerLog_UnlockOutput() { pthread_mutex_unlock(&g_logMutex); } +#else +void ServerLog_LockOutput() { EnterCriticalSection(&g_logCS); } +void ServerLog_UnlockOutput() { LeaveCriticalSection(&g_logCS); } +#endif diff --git a/Core/ServerLogger.h b/Core/ServerLogger.h new file mode 100644 index 0000000..dd31982 --- /dev/null +++ b/Core/ServerLogger.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +void ServerLog_Init(); +void ServerLog_Close(); +void ServerLog(const wchar_t* fmt, ...); +void ServerWarn(const wchar_t* fmt, ...); +void ServerLog_RegisterInput(std::wstring* buffer); +void ServerLog_UnregisterInput(); +void ServerLog_LockOutput(); +void ServerLog_UnlockOutput(); diff --git a/Core/ServerProperties.cpp b/Core/ServerProperties.cpp new file mode 100644 index 0000000..907d4da --- /dev/null +++ b/Core/ServerProperties.cpp @@ -0,0 +1,184 @@ +#include "stdafx.h" +#include "ServerProperties.h" +#include +#include +#include + +ServerProperties::ServerProperties() +{ + loadDefaults(); +} + +void ServerProperties::loadDefaults() +{ + serverPort = 25565; + levelName = L"world"; + levelSeed = 0; + gamemode = 0; + difficulty = 2; + maxPlayers = 8; + pvp = true; + trustPlayers = true; + fireSpreads = true; + tntExplodes = true; + structures = true; + spawnAnimals = true; + spawnNpcs = true; + onlineMode = false; + showGamertags = true; + motd = L"A Minecraft LCE Server"; + whiteList = false; + voiceChat = false; + levelSize = L"large"; + advertiseLan = true; + serverIp = L""; +} + +static wstring trimWs(const wstring& s) +{ + size_t start = s.find_first_not_of(L" \t\r\n"); + if (start == wstring::npos) return L""; + size_t end = s.find_last_not_of(L" \t\r\n"); + return s.substr(start, end - start + 1); +} + +bool ServerProperties::load(const wstring& path) +{ + FILE *f = NULL; + _wfopen_s(&f, path.c_str(), L"r, ccs=UTF-8"); + if (!f) + return false; + + wchar_t line[1024]; + while (fgetws(line, 1024, f)) + { + wstring ws(line); + size_t nl = ws.find_last_not_of(L"\r\n"); + if (nl != wstring::npos) + ws = ws.substr(0, nl + 1); + else + ws.clear(); + + if (ws.empty()) continue; + if (ws[0] == L'#') continue; + + size_t eq = ws.find(L'='); + if (eq == wstring::npos) continue; + + wstring key = trimWs(ws.substr(0, eq)); + wstring val = trimWs(ws.substr(eq + 1)); + m_properties[key] = val; + } + fclose(f); + + serverPort = getInt(L"server-port", 25565); + if (serverPort <= 0 || serverPort > 65535) serverPort = 25565; + levelName = getString(L"level-name", L"world"); + levelSeed = getInt64(L"level-seed", 0); + gamemode = getInt(L"gamemode", 0); + difficulty = getInt(L"difficulty", 2); + maxPlayers = getInt(L"max-players", 8); + if (maxPlayers < 1) maxPlayers = 1; + if (maxPlayers > 8) maxPlayers = 8; + pvp = getBool(L"pvp", true); + trustPlayers = getBool(L"trust-players", true); + fireSpreads = getBool(L"fire-spreads", true); + tntExplodes = getBool(L"tnt-explodes", true); + structures = getBool(L"structures", true); + spawnAnimals = getBool(L"spawn-animals", true); + spawnNpcs = getBool(L"spawn-npcs", true); + onlineMode = getBool(L"online-mode", false); + showGamertags = getBool(L"show-gamertags", true); + motd = getString(L"motd", L"A Minecraft LCE Server"); + whiteList = getBool(L"white-list", false); + voiceChat = getBool(L"voice-chat", false); + levelSize = getString(L"level-size", L"large"); + advertiseLan = getBool(L"advertise-lan", true); + serverIp = getString(L"server-ip", L""); + + return true; +} + +void ServerProperties::save(const wstring& path) +{ + FILE *f = NULL; + _wfopen_s(&f, path.c_str(), L"w, ccs=UTF-8"); + if (!f) return; + + fwprintf(f, L"#Minecraft server properties\n"); + { + time_t now = time(NULL); + struct tm t; + localtime_s(&t, &now); + static const wchar_t* dayNames[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" }; + static const wchar_t* monNames[] = { L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" }; + fwprintf(f, L"#%ls %ls %02d %02d:%02d:%02d %04d\n", + dayNames[t.tm_wday], monNames[t.tm_mon], t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec, t.tm_year + 1900); + } + fwprintf(f, L"server-port=%d\n", serverPort); + fwprintf(f, L"level-name=%ls\n", levelName.c_str()); + if (levelSeed != 0) + fwprintf(f, L"level-seed=%lld\n", levelSeed); + else + fwprintf(f, L"level-seed=\n"); + fwprintf(f, L"gamemode=%d\n", gamemode); + fwprintf(f, L"difficulty=%d\n", difficulty); + fwprintf(f, L"max-players=%d\n", maxPlayers); + fwprintf(f, L"pvp=%ls\n", pvp ? L"true" : L"false"); + fwprintf(f, L"trust-players=%ls\n", trustPlayers ? L"true" : L"false"); + fwprintf(f, L"fire-spreads=%ls\n", fireSpreads ? L"true" : L"false"); + fwprintf(f, L"tnt-explodes=%ls\n", tntExplodes ? L"true" : L"false"); + fwprintf(f, L"structures=%ls\n", structures ? L"true" : L"false"); + fwprintf(f, L"spawn-animals=%ls\n", spawnAnimals ? L"true" : L"false"); + fwprintf(f, L"spawn-npcs=%ls\n", spawnNpcs ? L"true" : L"false"); + fwprintf(f, L"online-mode=%ls\n", onlineMode ? L"true" : L"false"); + fwprintf(f, L"show-gamertags=%ls\n", showGamertags ? L"true" : L"false"); + fwprintf(f, L"motd=%ls\n", motd.c_str()); + fwprintf(f, L"white-list=%ls\n", whiteList ? L"true" : L"false"); + fwprintf(f, L"voice-chat=%ls\n", voiceChat ? L"true" : L"false"); + fwprintf(f, L"level-size=%ls\n", levelSize.c_str()); + fwprintf(f, L"advertise-lan=%ls\n", advertiseLan ? L"true" : L"false"); + if (!serverIp.empty()) + fwprintf(f, L"server-ip=%ls\n", serverIp.c_str()); + else + fwprintf(f, L"server-ip=\n"); + + fclose(f); +} + +wstring ServerProperties::getString(const wstring& key, const wstring& defaultVal) +{ + map::iterator it = m_properties.find(key); + if (it != m_properties.end()) + return it->second; + return defaultVal; +} + +int ServerProperties::getInt(const wstring& key, int defaultVal) +{ + map::iterator it = m_properties.find(key); + if (it != m_properties.end() && !it->second.empty()) + return _wtoi(it->second.c_str()); + return defaultVal; +} + +__int64 ServerProperties::getInt64(const wstring& key, __int64 defaultVal) +{ + map::iterator it = m_properties.find(key); + if (it != m_properties.end() && !it->second.empty()) + return _wtoi64(it->second.c_str()); + return defaultVal; +} + +bool ServerProperties::getBool(const wstring& key, bool defaultVal) +{ + map::iterator it = m_properties.find(key); + if (it != m_properties.end()) + { + wstring val = it->second; + if (val == L"true" || val == L"1") return true; + if (val == L"false" || val == L"0") return false; + } + return defaultVal; +} diff --git a/Core/ServerProperties.h b/Core/ServerProperties.h new file mode 100644 index 0000000..e77ff60 --- /dev/null +++ b/Core/ServerProperties.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +using namespace std; + +class ServerProperties +{ +public: + ServerProperties(); + + bool load(const wstring& path); + void save(const wstring& path); + + wstring getString(const wstring& key, const wstring& defaultVal); + int getInt(const wstring& key, int defaultVal); + __int64 getInt64(const wstring& key, __int64 defaultVal); + bool getBool(const wstring& key, bool defaultVal); + + int serverPort; + wstring levelName; + __int64 levelSeed; + int gamemode; + int difficulty; + int maxPlayers; + bool pvp; + bool trustPlayers; + bool fireSpreads; + bool tntExplodes; + bool structures; + bool spawnAnimals; + bool spawnNpcs; + bool onlineMode; + bool showGamertags; + wstring motd; + bool whiteList; + bool voiceChat; + wstring levelSize; + bool advertiseLan; + wstring serverIp; + +private: + map m_properties; + void loadDefaults(); +}; diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3e83a7a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,23 @@ +Fork-Only Source License +Copyright © 2026 LCEMP + +You have permission to: + +- fork this repository +- make changes in your own fork +- build new projects that are clearly derived from this one + +You may share / distribute your fork, but you must: + +- keep this copyright notice and license text +- clearly show that your work came from this repository (include a link to the original repo and mention it visibly, e.g. in README, about box, credits, etc.) + +What you may NOT do: + +- copy & paste parts of this code (files, classes, functions, big chunks, small clever snippets, etc.) into a different project that is not a fork/derivative of this repository +- use this code as a starting point for something that isn’t presented as being based on this project +- incorporate this code into any closed-source, proprietary, or unrelated open-source project + +if you're not keeping the fork relationship alive and giving clear credit back to this repo, you need to ask me for permission first. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. \ No newline at end of file diff --git a/Linux/LinuxCompat.h b/Linux/LinuxCompat.h new file mode 100644 index 0000000..ae178b3 --- /dev/null +++ b/Linux/LinuxCompat.h @@ -0,0 +1,1309 @@ +#pragma once + + +#ifdef __linux__ + +#ifdef __cplusplus +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +#define __declspec(spec) __LINUX_DECLSPEC_##spec +#define __LINUX_DECLSPEC_thread __thread +#ifdef __cplusplus +#include +#include +#include +#include +#include +using std::floor; using std::ceil; using std::cos; using std::sin; +using std::atan; using std::atan2; using std::sqrt; using std::fabs; + +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +inline void _linuxFixPath(char* p) { for (; *p; ++p) if (*p == '\\') *p = '/'; } +inline void _linuxFixPathW(wchar_t* p) { for (; *p; ++p) if (*p == L'\\') *p = L'/'; } + +static inline void _wfmtToNarrow(const wchar_t* wfmt, char* nfmt, size_t nfmtSize) { + size_t ni = 0; + for (size_t i = 0; wfmt[i] && ni < nfmtSize - 3; i++) { + if (wfmt[i] == L'%') { + nfmt[ni++] = '%'; i++; + if (!wfmt[i]) break; + if (wfmt[i] == L'%') { nfmt[ni++] = '%'; continue; } + while (wfmt[i] == L'-' || wfmt[i] == L'+' || wfmt[i] == L' ' || wfmt[i] == L'#' || wfmt[i] == L'0') + nfmt[ni++] = (char)wfmt[i++]; + if (wfmt[i] == L'*') { nfmt[ni++] = '*'; i++; } + else while (wfmt[i] >= L'0' && wfmt[i] <= L'9') + nfmt[ni++] = (char)wfmt[i++]; + if (wfmt[i] == L'.') { + nfmt[ni++] = '.'; i++; + if (wfmt[i] == L'*') { nfmt[ni++] = '*'; i++; } + else while (wfmt[i] >= L'0' && wfmt[i] <= L'9') + nfmt[ni++] = (char)wfmt[i++]; + } + if (wfmt[i] == L'l') { nfmt[ni++] = 'l'; i++; if (wfmt[i] == L'l') { nfmt[ni++] = 'l'; i++; } } + else if (wfmt[i] == L'h') { nfmt[ni++] = 'h'; i++; if (wfmt[i] == L'h') { nfmt[ni++] = 'h'; i++; } } + if (wfmt[i] == L's') { nfmt[ni++] = 'l'; nfmt[ni++] = 's'; } + else if (wfmt[i] == L'S') { nfmt[ni++] = 's'; } + else { nfmt[ni++] = (char)wfmt[i]; } + } else { + nfmt[ni++] = (char)wfmt[i]; + } + } + nfmt[ni] = '\0'; +} + +#ifdef linux +#undef linux +#endif +#ifdef windows +#undef windows +#endif + +typedef uint8_t BYTE; +typedef uint16_t WORD; +typedef int16_t SHORT; +typedef uint16_t USHORT; +typedef char CHAR; +typedef float FLOAT; + +#ifdef __cplusplus +typedef bool boolean; +typedef uint32_t DWORD; +typedef int32_t LONG; +typedef long long LONG64; +typedef long long LONGLONG; +typedef unsigned long long ULONGLONG; +typedef int BOOL; +typedef void VOID; +typedef void* LPVOID; +typedef void* PVOID; +typedef const void* LPCVOID; +typedef char* LPSTR; +typedef const char* LPCSTR; +typedef wchar_t* LPWSTR; +typedef const wchar_t* LPCWSTR; +typedef unsigned char* PBYTE; +typedef DWORD* PDWORD; +typedef BOOL* PBOOL; +typedef size_t SIZE_T; +typedef uintptr_t ULONG_PTR; +typedef unsigned int UINT; +typedef int INT; +typedef int HRESULT; +typedef int errno_t; +typedef void* HANDLE; +typedef void* HWND; +typedef LONG* PLONG; +typedef DWORD* LPDWORD; +#ifndef __int64 +#define __int64 long long +#endif +typedef unsigned long long __uint64; +#ifndef __int32 +#define __int32 int +#endif +#ifndef __int16 +#define __int16 short +#endif +#ifndef __int8 +#define __int8 char +#endif + +typedef wchar_t WCHAR; +typedef union _LARGE_INTEGER { + struct { + DWORD LowPart; + LONG HighPart; + }; + LONGLONG QuadPart; +} LARGE_INTEGER; + +#ifndef ZeroMemory +#define ZeroMemory(Destination, Length) memset((Destination), 0, (Length)) +#endif +#define CONST const +#define CALLBACK +#define MAKE_HRESULT(sev,fac,code) ((HRESULT)(((unsigned long)(sev)<<31)|((unsigned long)(fac)<<16)|((unsigned long)(code)))) +#define __cdecl + +#ifndef _TRUNCATE +#define _TRUNCATE ((size_t)-1) +#endif + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef NULL +#ifdef __cplusplus +#define NULL nullptr +#else +#define NULL ((void*)0) +#endif +#endif + +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_RETURN 0x0D +#define VK_ESCAPE 0x1B +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_LSHIFT 0xA0 +#define VK_LCONTROL 0xA2 + +#define VK_PAD_A 0x5800 +#define VK_PAD_B 0x5801 +#define VK_PAD_X 0x5802 +#define VK_PAD_Y 0x5803 +#define VK_PAD_RSHOULDER 0x5804 +#define VK_PAD_LSHOULDER 0x5805 +#define VK_PAD_LTRIGGER 0x5806 +#define VK_PAD_RTRIGGER 0x5807 +#define VK_PAD_DPAD_UP 0x5810 +#define VK_PAD_DPAD_DOWN 0x5811 +#define VK_PAD_DPAD_LEFT 0x5812 +#define VK_PAD_DPAD_RIGHT 0x5813 +#define VK_PAD_START 0x5814 +#define VK_PAD_BACK 0x5815 +#define VK_PAD_LTHUMB_PRESS 0x5816 +#define VK_PAD_LTHUMB_UP 0x5820 +#define VK_PAD_LTHUMB_DOWN 0x5821 +#define VK_PAD_LTHUMB_RIGHT 0x5822 +#define VK_PAD_LTHUMB_LEFT 0x5823 +#define VK_PAD_LTHUMB_UPLEFT 0x5824 +#define VK_PAD_LTHUMB_UPRIGHT 0x5825 +#define VK_PAD_LTHUMB_DOWNRIGHT 0x5826 +#define VK_PAD_LTHUMB_DOWNLEFT 0x5827 +#define VK_PAD_RTHUMB_UP 0x5830 +#define VK_PAD_RTHUMB_DOWN 0x5831 +#define VK_PAD_RTHUMB_RIGHT 0x5832 +#define VK_PAD_RTHUMB_LEFT 0x5833 +#define VK_PAD_RTHUMB_UPLEFT 0x5834 +#define VK_PAD_RTHUMB_UPRIGHT 0x5835 +#define VK_PAD_RTHUMB_DOWNRIGHT 0x5836 +#define VK_PAD_RTHUMB_DOWNLEFT 0x5837 + +inline errno_t _wfopen_s(FILE** pFile, const wchar_t* filename, const wchar_t* mode) { + if (!pFile) return EINVAL; + char narrowName[1024]; + char narrowMode[64]; + wcstombs(narrowName, filename, sizeof(narrowName)); + _linuxFixPath(narrowName); + wcstombs(narrowMode, mode, sizeof(narrowMode)); + char* comma = strchr(narrowMode, ','); + if (comma) *comma = '\0'; + *pFile = fopen(narrowName, narrowMode); + return (*pFile) ? 0 : errno; +} + +inline int _wtoi(const wchar_t* str) { + return (int)wcstol(str, NULL, 10); +} + +inline long long _wtoi64(const wchar_t* str) { + return wcstoll(str, NULL, 10); +} + +inline errno_t fopen_s(FILE** pFile, const char* filename, const char* mode) { + if (!pFile) return EINVAL; + char fixed[1024]; + strncpy(fixed, filename, sizeof(fixed) - 1); fixed[sizeof(fixed) - 1] = '\0'; + _linuxFixPath(fixed); + *pFile = fopen(fixed, mode); + return (*pFile) ? 0 : errno; +} + +#define _stricmp strcasecmp +#define _wcsicmp wcscasecmp + +inline unsigned long long _wcstoui64(const wchar_t* str, wchar_t** endptr, int base) { + return wcstoull(str, endptr, base); +} + +inline DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBuffer) { + if (getcwd(lpBuffer, nBufferLength)) + return (DWORD)strlen(lpBuffer); + return 0; +} + +#define S_OK 0 +#define S_FALSE 1 +#define E_FAIL (-1) +#define HRESULT_SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) +#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) +#define FAILED(hr) (((HRESULT)(hr)) < 0) + +#define ERROR_IO_PENDING 997L +#define ERROR_CANCELLED 1223L + +#define CDECL + +#define CP_ACP 0 + +inline int WideCharToMultiByte(unsigned int codePage, unsigned long dwFlags, + const wchar_t* lpWideCharStr, int cchWideChar, + char* lpMultiByteStr, int cbMultiByte, + const char* lpDefaultChar, int* lpUsedDefaultChar) { + (void)codePage; (void)dwFlags; (void)lpDefaultChar; (void)lpUsedDefaultChar; + if (cchWideChar == -1) cchWideChar = (int)wcslen(lpWideCharStr) + 1; + if (cbMultiByte == 0) return cchWideChar; // query size + return (int)wcstombs(lpMultiByteStr, lpWideCharStr, cbMultiByte); +} + +inline int __vsnprintf_s_impl(char* buffer, size_t sizeOfBuffer, size_t count, const char* format, va_list argptr) { + if (count == _TRUNCATE) count = sizeOfBuffer - 1; + return vsnprintf(buffer, count + 1, format, argptr); +} +#define _vsnprintf_s(buf, count, fmt, ap) __vsnprintf_s_impl(buf, sizeof(buf), count, fmt, ap) + +#define swscanf_s swscanf + +inline errno_t _itoa_s(int value, char* buffer, size_t sizeOfBuffer, int radix) { + if (radix == 10) { snprintf(buffer, sizeOfBuffer, "%d", value); return 0; } + if (radix == 16) { snprintf(buffer, sizeOfBuffer, "%x", value); return 0; } + char* p = buffer; + char* end = buffer + sizeOfBuffer - 1; + int isNeg = (value < 0 && radix == 10); + unsigned int uval = isNeg ? (unsigned int)-value : (unsigned int)value; + char tmp[65]; int i = 0; + do { int d = uval % radix; tmp[i++] = (d < 10) ? ('0'+d) : ('a'+d-10); uval /= radix; } while(uval && i < 64); + if (isNeg) tmp[i++] = '-'; + for (int j = i-1; j >= 0 && p < end; j--) *p++ = tmp[j]; + *p = '\0'; + return 0; +} + +inline errno_t _i64toa_s(long long value, char* buffer, size_t sizeOfBuffer, int radix) { + if (radix == 10) { snprintf(buffer, sizeOfBuffer, "%lld", value); return 0; } + if (radix == 16) { snprintf(buffer, sizeOfBuffer, "%llx", value); return 0; } + snprintf(buffer, sizeOfBuffer, "%lld", value); + return 0; +} + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1) +#endif + +#ifndef GENERIC_READ +#define GENERIC_READ 0x80000000 +#endif +#ifndef GENERIC_WRITE +#define GENERIC_WRITE 0x40000000 +#endif + +#ifndef OPEN_EXISTING +#define OPEN_EXISTING 3 +#endif +#ifndef OPEN_ALWAYS +#define OPEN_ALWAYS 4 +#endif + +#ifndef FILE_ATTRIBUTE_NORMAL +#define FILE_ATTRIBUTE_NORMAL 0x80 +#endif +#ifndef FILE_FLAG_RANDOM_ACCESS +#define FILE_FLAG_RANDOM_ACCESS 0x10000000 +#endif +#ifndef FILE_FLAG_SEQUENTIAL_SCAN +#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000 +#endif + +#ifndef FILE_BEGIN +#define FILE_BEGIN 0 +#endif +#ifndef FILE_END +#define FILE_END 2 +#endif + +#ifndef PAGE_READWRITE +#define PAGE_READWRITE 0x04 +#endif +#ifndef MEM_COMMIT +#define MEM_COMMIT 0x1000 +#endif +#ifndef MEM_RESERVE +#define MEM_RESERVE 0x2000 +#endif +#ifndef MEM_DECOMMIT +#define MEM_DECOMMIT 0x4000 +#endif +#ifndef MEM_RELEASE +#define MEM_RELEASE 0x8000 +#endif +#ifndef MEM_LARGE_PAGES +#define MEM_LARGE_PAGES 0x20000000 +#endif + +#ifndef MAXULONG_PTR +#define MAXULONG_PTR ((ULONG_PTR)~0) +#endif + +#ifndef MAX_PATH +#define MAX_PATH 260 +#endif + +#ifndef ERROR_SUCCESS +#define ERROR_SUCCESS 0L +#endif + +typedef struct _FILETIME { + DWORD dwLowDateTime; + DWORD dwHighDateTime; +} FILETIME; + +typedef pthread_mutex_t CRITICAL_SECTION; + +inline void InitializeCriticalSection(CRITICAL_SECTION* cs) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(cs, &attr); + pthread_mutexattr_destroy(&attr); +} + +inline void InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION* cs, DWORD spinCount) +{ + (void)spinCount; + InitializeCriticalSection(cs); +} + +inline void EnterCriticalSection(CRITICAL_SECTION* cs) +{ + pthread_mutex_lock(cs); +} + +inline BOOL TryEnterCriticalSection(CRITICAL_SECTION* cs) +{ + return pthread_mutex_trylock(cs) == 0 ? TRUE : FALSE; +} + +inline void LeaveCriticalSection(CRITICAL_SECTION* cs) +{ + pthread_mutex_unlock(cs); +} + +inline void DeleteCriticalSection(CRITICAL_SECTION* cs) +{ + pthread_mutex_destroy(cs); +} + +enum LinuxHandleType { LINUX_HANDLE_EVENT, LINUX_HANDLE_FILE }; + +struct LinuxEvent { + LinuxHandleType handleType; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool signaled; + bool autoReset; +}; + +struct LinuxFileHandle { + LinuxHandleType handleType; + int fd; +}; + +inline HANDLE CreateEventW(void* /*sec*/, BOOL manualReset, BOOL initialState, const wchar_t* /*name*/) +{ + LinuxEvent* ev = new LinuxEvent(); + ev->handleType = LINUX_HANDLE_EVENT; + pthread_mutex_init(&ev->mutex, NULL); + pthread_cond_init(&ev->cond, NULL); + ev->signaled = (initialState != 0); + ev->autoReset = (manualReset == 0); + return (HANDLE)ev; +} + +inline HANDLE CreateEvent(void* sec, BOOL manualReset, BOOL initialState, const char* /*name*/) +{ + return CreateEventW(sec, manualReset, initialState, NULL); +} + +inline BOOL SetEvent(HANDLE h) +{ + LinuxEvent* ev = (LinuxEvent*)h; + pthread_mutex_lock(&ev->mutex); + ev->signaled = true; + pthread_cond_broadcast(&ev->cond); + pthread_mutex_unlock(&ev->mutex); + return TRUE; +} + +inline BOOL ResetEvent(HANDLE h) +{ + LinuxEvent* ev = (LinuxEvent*)h; + pthread_mutex_lock(&ev->mutex); + ev->signaled = false; + pthread_mutex_unlock(&ev->mutex); + return TRUE; +} + +#define WAIT_OBJECT_0 0 +#define WAIT_TIMEOUT 0x102 +#define INFINITE 0xFFFFFFFF +#define STILL_ACTIVE 259 +#define CREATE_SUSPENDED 0x00000004 + +inline DWORD WaitForSingleObject(HANDLE h, DWORD milliseconds) +{ + LinuxEvent* ev = (LinuxEvent*)h; + pthread_mutex_lock(&ev->mutex); + if (milliseconds == INFINITE) + { + while (!ev->signaled) + pthread_cond_wait(&ev->cond, &ev->mutex); + } + else + { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000) { ts.tv_sec++; ts.tv_nsec -= 1000000000; } + + while (!ev->signaled) + { + int rc = pthread_cond_timedwait(&ev->cond, &ev->mutex, &ts); + if (rc == ETIMEDOUT) break; + } + } + bool was = ev->signaled; + if (ev->autoReset) ev->signaled = false; + pthread_mutex_unlock(&ev->mutex); + return was ? WAIT_OBJECT_0 : WAIT_TIMEOUT; +} + +inline BOOL CloseHandle(HANDLE h) +{ + if (!h || h == INVALID_HANDLE_VALUE) return FALSE; + LinuxHandleType type = *(LinuxHandleType*)h; + if (type == LINUX_HANDLE_FILE) + { + LinuxFileHandle* fh = (LinuxFileHandle*)h; + int rc = close(fh->fd); + delete fh; + return rc == 0 ? TRUE : FALSE; + } + LinuxEvent* ev = (LinuxEvent*)h; + pthread_mutex_destroy(&ev->mutex); + pthread_cond_destroy(&ev->cond); + delete ev; + return TRUE; +} + +#ifndef GENERIC_READ +#define GENERIC_READ 0x80000000 +#endif +#ifndef GENERIC_WRITE +#define GENERIC_WRITE 0x40000000 +#endif +#ifndef CREATE_ALWAYS +#define CREATE_ALWAYS 2 +#endif +#ifndef OPEN_EXISTING +#define OPEN_EXISTING 3 +#endif +#ifndef FILE_SHARE_READ +#define FILE_SHARE_READ 1 +#endif +#ifndef FILE_SHARE_WRITE +#define FILE_SHARE_WRITE 2 +#endif + +inline HANDLE CreateFile(const char* path, DWORD access, DWORD /*share*/, void* /*sec*/, DWORD creation, DWORD /*flags*/, HANDLE /*templ*/) +{ + if (!path) return INVALID_HANDLE_VALUE; + char fixed[MAX_PATH]; + strncpy(fixed, path, MAX_PATH - 1); fixed[MAX_PATH - 1] = '\0'; + _linuxFixPath(fixed); + + int oflags = 0; + if ((access & GENERIC_READ) && (access & GENERIC_WRITE)) oflags = O_RDWR; + else if (access & GENERIC_WRITE) oflags = O_WRONLY; + else oflags = O_RDONLY; + + if (creation == CREATE_ALWAYS) oflags |= O_CREAT | O_TRUNC; + else if (creation == OPEN_EXISTING) { /* no extra flags */ } + + int fd = open(fixed, oflags, 0644); + if (fd < 0) return INVALID_HANDLE_VALUE; + + LinuxFileHandle* fh = new LinuxFileHandle(); + fh->handleType = LINUX_HANDLE_FILE; + fh->fd = fd; + return (HANDLE)fh; +} +#define CreateFileA CreateFile + +#ifndef INVALID_FILE_SIZE +#define INVALID_FILE_SIZE ((DWORD)0xFFFFFFFF) +#endif + +inline DWORD GetFileSize(HANDLE h, DWORD* high) +{ + if (!h || h == INVALID_HANDLE_VALUE) return INVALID_FILE_SIZE; + LinuxFileHandle* fh = (LinuxFileHandle*)h; + struct stat st; + if (fstat(fh->fd, &st) != 0) return INVALID_FILE_SIZE; + if (high) *high = (DWORD)((st.st_size >> 32) & 0xFFFFFFFF); + return (DWORD)(st.st_size & 0xFFFFFFFF); +} + +inline BOOL ReadFile(HANDLE h, void* buf, DWORD toRead, DWORD* readOut, void* /*ov*/) +{ + if (!h || h == INVALID_HANDLE_VALUE) { if (readOut) *readOut = 0; return FALSE; } + LinuxFileHandle* fh = (LinuxFileHandle*)h; + ssize_t n = read(fh->fd, buf, toRead); + if (n < 0) { if (readOut) *readOut = 0; return FALSE; } + if (readOut) *readOut = (DWORD)n; + return TRUE; +} + +inline BOOL WriteFile(HANDLE h, const void* buf, DWORD toWrite, DWORD* writtenOut, void* /*ov*/) +{ + if (!h || h == INVALID_HANDLE_VALUE) { if (writtenOut) *writtenOut = 0; return FALSE; } + LinuxFileHandle* fh = (LinuxFileHandle*)h; + ssize_t n = write(fh->fd, buf, toWrite); + if (n < 0) { if (writtenOut) *writtenOut = 0; return FALSE; } + if (writtenOut) *writtenOut = (DWORD)n; + return TRUE; +} + +inline DWORD GetLastError() +{ + return (DWORD)errno; +} + +inline LPVOID VirtualAlloc(LPVOID /*addr*/, size_t size, DWORD /*allocType*/, DWORD /*protect*/) +{ + return malloc(size); +} + +inline BOOL VirtualFree(LPVOID addr, size_t /*size*/, DWORD /*freeType*/) +{ + free(addr); + return TRUE; +} + +typedef struct _SYSTEMTIME { + WORD wYear; + WORD wMonth; + WORD wDayOfWeek; + WORD wDay; + WORD wHour; + WORD wMinute; + WORD wSecond; + WORD wMilliseconds; +} SYSTEMTIME, *LPSYSTEMTIME; + +inline void GetSystemTime(LPSYSTEMTIME st) +{ + if (!st) return; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + struct tm t; + gmtime_r(&ts.tv_sec, &t); + st->wYear = (WORD)(t.tm_year + 1900); + st->wMonth = (WORD)(t.tm_mon + 1); + st->wDayOfWeek = (WORD)t.tm_wday; + st->wDay = (WORD)t.tm_mday; + st->wHour = (WORD)t.tm_hour; + st->wMinute = (WORD)t.tm_min; + st->wSecond = (WORD)t.tm_sec; + st->wMilliseconds = (WORD)(ts.tv_nsec / 1000000); +} + +inline BOOL SystemTimeToFileTime(const SYSTEMTIME* st, FILETIME* ft) +{ + if (!st || !ft) return FALSE; + struct tm t = {}; + t.tm_year = st->wYear - 1900; + t.tm_mon = st->wMonth - 1; + t.tm_mday = st->wDay; + t.tm_hour = st->wHour; + t.tm_min = st->wMinute; + t.tm_sec = st->wSecond; + time_t epoch = timegm(&t); + uint64_t ticks = ((uint64_t)epoch + 11644473600ULL) * 10000000ULL + (uint64_t)st->wMilliseconds * 10000ULL; + ft->dwLowDateTime = (DWORD)(ticks & 0xFFFFFFFF); + ft->dwHighDateTime = (DWORD)(ticks >> 32); + return TRUE; +} + +typedef struct _MEMORYSTATUS { + DWORD dwLength; + DWORD dwMemoryLoad; + size_t dwTotalPhys; + size_t dwAvailPhys; + size_t dwTotalPageFile; + size_t dwAvailPageFile; + size_t dwTotalVirtual; + size_t dwAvailVirtual; +} MEMORYSTATUS, *LPMEMORYSTATUS; + +inline void GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer) +{ + if (!lpBuffer) return; + memset(lpBuffer, 0, sizeof(MEMORYSTATUS)); + lpBuffer->dwLength = sizeof(MEMORYSTATUS); +} + +inline BOOL QueryPerformanceFrequency(LARGE_INTEGER* v) +{ + if (!v) return FALSE; + v->QuadPart = 1000000000LL; + return TRUE; +} + +inline BOOL QueryPerformanceCounter(LARGE_INTEGER* v) +{ + if (!v) return FALSE; + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + v->QuadPart = (LONGLONG)ts.tv_sec * 1000000000LL + (LONGLONG)ts.tv_nsec; + return TRUE; +} + +inline LONGLONG InterlockedCompareExchangeRelease64(volatile LONGLONG* destination, LONGLONG exchange, LONGLONG comparand) +{ + return __sync_val_compare_and_swap(destination, comparand, exchange); +} + +typedef DWORD (*LPTHREAD_START_ROUTINE)(LPVOID); + +struct LinuxThreadData { + LPTHREAD_START_ROUTINE func; + LPVOID param; +}; + +inline void* LinuxThreadWrapper(void* arg) +{ + LinuxThreadData* td = (LinuxThreadData*)arg; + LPTHREAD_START_ROUTINE fn = td->func; + LPVOID p = td->param; + delete td; + DWORD result = fn(p); + return (void*)(uintptr_t)result; +} + +inline HANDLE CreateThread(void* /*sec*/, size_t /*stack*/, LPTHREAD_START_ROUTINE func, LPVOID param, DWORD /*flags*/, DWORD* /*tid*/) +{ + pthread_t* pt = new pthread_t; + LinuxThreadData* td = new LinuxThreadData{func, param}; + if (pthread_create(pt, NULL, LinuxThreadWrapper, td) != 0) { + delete td; + delete pt; + return NULL; + } + return (HANDLE)pt; +} + +inline std::vector& LinuxTlsKeys() +{ + static std::vector keys; + return keys; +} + +inline CRITICAL_SECTION& LinuxTlsLock() +{ + static CRITICAL_SECTION lock; + static bool initialized = false; + if (!initialized) + { + InitializeCriticalSection(&lock); + initialized = true; + } + return lock; +} + +inline DWORD TlsAlloc() +{ + pthread_key_t key; + if (pthread_key_create(&key, NULL) != 0) + return 0xFFFFFFFF; + + EnterCriticalSection(&LinuxTlsLock()); + std::vector& keys = LinuxTlsKeys(); + keys.push_back(key); + DWORD idx = (DWORD)(keys.size() - 1); + LeaveCriticalSection(&LinuxTlsLock()); + return idx; +} + +inline BOOL TlsSetValue(DWORD idx, LPVOID value) +{ + EnterCriticalSection(&LinuxTlsLock()); + std::vector& keys = LinuxTlsKeys(); + if (idx >= keys.size()) + { + LeaveCriticalSection(&LinuxTlsLock()); + return FALSE; + } + pthread_key_t key = keys[idx]; + LeaveCriticalSection(&LinuxTlsLock()); + return pthread_setspecific(key, value) == 0 ? TRUE : FALSE; +} + +inline LPVOID TlsGetValue(DWORD idx) +{ + EnterCriticalSection(&LinuxTlsLock()); + std::vector& keys = LinuxTlsKeys(); + if (idx >= keys.size()) + { + LeaveCriticalSection(&LinuxTlsLock()); + return NULL; + } + pthread_key_t key = keys[idx]; + LeaveCriticalSection(&LinuxTlsLock()); + return (LPVOID)pthread_getspecific(key); +} + + +inline void Sleep(DWORD ms) { usleep(ms * 1000); } + +inline DWORD GetTickCount() +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (DWORD)(ts.tv_sec * 1000 + ts.tv_nsec / 1000000); +} + +inline int sprintf_s(char* buf, size_t sz, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = vsnprintf(buf, sz, fmt, ap); + va_end(ap); + return r; +} + +inline int sprintf_s(char* buf, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = vsnprintf(buf, 256, fmt, ap); + va_end(ap); + return r; +} + +static inline void _wfmtFixWide(const wchar_t* src, wchar_t* dst, size_t dstSize) { + size_t di = 0; + for (size_t i = 0; src[i] && di < dstSize - 3; i++) { + if (src[i] == L'%') { + dst[di++] = L'%'; i++; + if (!src[i]) break; + if (src[i] == L'%') { dst[di++] = L'%'; continue; } + while (src[i] == L'-' || src[i] == L'+' || src[i] == L' ' || src[i] == L'#' || src[i] == L'0') + dst[di++] = src[i++]; + if (src[i] == L'*') { dst[di++] = L'*'; i++; } + else while (src[i] >= L'0' && src[i] <= L'9') + dst[di++] = src[i++]; + if (src[i] == L'.') { + dst[di++] = L'.'; i++; + if (src[i] == L'*') { dst[di++] = L'*'; i++; } + else while (src[i] >= L'0' && src[i] <= L'9') + dst[di++] = src[i++]; + } + if (src[i] == L'l') { dst[di++] = L'l'; i++; if (src[i] == L'l') { dst[di++] = L'l'; i++; } } + else if (src[i] == L'h') { dst[di++] = L'h'; i++; if (src[i] == L'h') { dst[di++] = L'h'; i++; } } + if (src[i] == L's') { dst[di++] = L'l'; dst[di++] = L's'; } + else if (src[i] == L'S') { dst[di++] = L's'; } + else { dst[di++] = src[i]; } + } else { + dst[di++] = src[i]; + } + } + dst[di] = L'\0'; +} + +inline int swprintf_s(wchar_t* buf, size_t sz, const wchar_t* fmt, ...) +{ + wchar_t fixedFmt[2048]; + _wfmtFixWide(fmt, fixedFmt, 2048); + va_list ap; + va_start(ap, fmt); + int r = vswprintf(buf, sz, fixedFmt, ap); + va_end(ap); + return r; +} + +inline int wcscpy_s(wchar_t* dst, size_t sz, const wchar_t* src) +{ + wcsncpy(dst, src, sz); + if (sz > 0) dst[sz - 1] = L'\0'; + return 0; +} + +inline int wcsncpy_s(wchar_t* dst, size_t dstsz, const wchar_t* src, size_t count) +{ + size_t to_copy = (count < dstsz - 1) ? count : dstsz - 1; + wcsncpy(dst, src, to_copy); + dst[to_copy] = L'\0'; + return 0; +} + +#ifdef __cplusplus +template +inline int wcsncpy_s(wchar_t (&dst)[N], const wchar_t* src, size_t count) +{ + return wcsncpy_s(dst, N, src, count); +} +#endif // __cplusplus + +#ifndef _TRUNCATE +#define _TRUNCATE ((size_t)-1) +#endif + +inline int strncpy_s(char* dst, size_t dstsz, const char* src, size_t count) +{ + if (count == _TRUNCATE) count = dstsz - 1; + size_t to_copy = (count < dstsz - 1) ? count : dstsz - 1; + strncpy(dst, src, to_copy); + dst[to_copy] = '\0'; + return 0; +} + +inline int strcpy_s(char* dst, size_t dstsz, const char* src) +{ + if (!dst || dstsz == 0) return -1; + strncpy(dst, src, dstsz - 1); + dst[dstsz - 1] = '\0'; + return 0; +} + +template +inline int strcpy_s(char (&dst)[N], const char* src) +{ + return strcpy_s(dst, N, src); +} + +inline errno_t localtime_s(struct tm* result, const time_t* timer) +{ + return localtime_r(timer, result) ? 0 : errno; +} + +inline wchar_t* _itow(int value, wchar_t* str, int radix) +{ + if (radix < 2 || radix > 36) { str[0] = L'\0'; return str; } + wchar_t* p = str; + bool neg = (value < 0 && radix == 10); + unsigned int uval = neg ? (unsigned int)(-(long long)value) : (unsigned int)value; + wchar_t buf[34]; + int i = 0; + do { + int d = uval % radix; + buf[i++] = (wchar_t)(d < 10 ? L'0' + d : L'a' + d - 10); + uval /= radix; + } while (uval); + if (neg) *p++ = L'-'; + while (i > 0) *p++ = buf[--i]; + *p = L'\0'; + return str; +} + +inline BOOL SetConsoleOutputCP(unsigned int) { return TRUE; } +inline BOOL SetConsoleCP(unsigned int) { return TRUE; } +inline BOOL SetConsoleTitleW(const wchar_t*) { return TRUE; } +inline BOOL FreeConsole() { return TRUE; } +inline void ExitProcess(int code) { _exit(code); } + +inline BOOL CreateDirectoryW(const wchar_t* path, void*) +{ + char mbpath[MAX_PATH]; + wcstombs(mbpath, path, MAX_PATH); + _linuxFixPath(mbpath); + return mkdir(mbpath, 0755) == 0 || errno == EEXIST; +} + +inline DWORD GetModuleFileNameW(void*, wchar_t* buf, DWORD sz) +{ + char path[MAX_PATH]; + ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1); + if (len <= 0) return 0; + path[len] = '\0'; + mbstowcs(buf, path, sz); + return (DWORD)wcslen(buf); +} + +inline BOOL SetCurrentDirectoryW(const wchar_t* path) +{ + char mbpath[MAX_PATH]; + wcstombs(mbpath, path, MAX_PATH); + _linuxFixPath(mbpath); + return chdir(mbpath) == 0; +} + +#define CP_UTF8 65001 +#define WINAPI + +#define __debugbreak() __builtin_trap() + +#define WM_CLOSE 0x0010 +inline BOOL PostMessage(HWND, unsigned int, unsigned long, long) { return TRUE; } + +inline DWORD GetCurrentThreadId() +{ + return (DWORD)(uintptr_t)pthread_self(); +} + +inline HANDLE GetCurrentThread() +{ + return (HANDLE)(uintptr_t)pthread_self(); +} + +inline DWORD ResumeThread(HANDLE /*h*/) +{ + return 0; +} + +#ifndef THREAD_PRIORITY_LOWEST +#define THREAD_PRIORITY_LOWEST (-2) +#endif +#ifndef THREAD_PRIORITY_BELOW_NORMAL +#define THREAD_PRIORITY_BELOW_NORMAL (-1) +#endif +#ifndef THREAD_PRIORITY_NORMAL +#define THREAD_PRIORITY_NORMAL 0 +#endif +#ifndef THREAD_PRIORITY_ABOVE_NORMAL +#define THREAD_PRIORITY_ABOVE_NORMAL 1 +#endif +#ifndef THREAD_PRIORITY_HIGHEST +#define THREAD_PRIORITY_HIGHEST 2 +#endif + +inline BOOL SetThreadPriority(HANDLE /*h*/, int /*priority*/) +{ + return TRUE; +} + +inline BOOL GetExitCodeThread(HANDLE /*h*/, DWORD* code) +{ + if (code) *code = 0; + return TRUE; +} + +inline DWORD WaitForMultipleObjects(DWORD count, const HANDLE* handles, BOOL waitAll, DWORD timeout) +{ + if (!handles || count == 0) return WAIT_TIMEOUT; + if (waitAll) + { + for (DWORD i = 0; i < count; ++i) + { + DWORD r = WaitForSingleObject(handles[i], timeout); + if (r != WAIT_OBJECT_0) return r; + } + return WAIT_OBJECT_0; + } + return WaitForSingleObject(handles[0], timeout); +} + +typedef struct tagRECT { LONG left, top, right, bottom; } RECT; +typedef struct tagPOINT { LONG x, y; } POINT; + +typedef struct { unsigned short usUsagePage; unsigned short usUsage; DWORD dwFlags; HWND hwndTarget; } RAWINPUTDEVICE; +#define RIDEV_NOLEGACY 0 +#define HID_USAGE_PAGE_GENERIC 1 +#define HID_USAGE_GENERIC_MOUSE 2 +#define HID_USAGE_GENERIC_KEYBOARD 6 +inline BOOL RegisterRawInputDevices(const RAWINPUTDEVICE*, unsigned int, unsigned int) { return TRUE; } +inline BOOL GetClientRect(HWND, RECT*) { return FALSE; } +inline BOOL ClientToScreen(HWND, POINT*) { return FALSE; } +inline BOOL SetCursorPos(int, int) { return FALSE; } +inline int ShowCursor(BOOL) { return 0; } +inline BOOL ClipCursor(const RECT*) { return FALSE; } + +#ifndef FILE_ATTRIBUTE_DIRECTORY +#define FILE_ATTRIBUTE_DIRECTORY 0x10 +#endif +#ifndef FILE_ATTRIBUTE_READONLY +#define FILE_ATTRIBUTE_READONLY 0x01 +#endif +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#endif + + +inline DWORD GetFileAttributes(const char* path) +{ + char fixed[MAX_PATH]; + strncpy(fixed, path, MAX_PATH - 1); fixed[MAX_PATH - 1] = '\0'; + _linuxFixPath(fixed); + struct stat st; + if (stat(fixed, &st) != 0) return INVALID_FILE_ATTRIBUTES; + DWORD attrs = FILE_ATTRIBUTE_NORMAL; + if (S_ISDIR(st.st_mode)) attrs |= FILE_ATTRIBUTE_DIRECTORY; + return attrs; +} +#define GetFileAttributesA GetFileAttributes + +inline BOOL DeleteFile(const char* path) +{ + char fixed[MAX_PATH]; + strncpy(fixed, path, MAX_PATH - 1); fixed[MAX_PATH - 1] = '\0'; + _linuxFixPath(fixed); + return (remove(fixed) == 0) ? TRUE : FALSE; +} + +inline BOOL CreateDirectory(const char* path, void* /*sec*/) +{ + char fixed[MAX_PATH]; + strncpy(fixed, path, MAX_PATH - 1); fixed[MAX_PATH - 1] = '\0'; + _linuxFixPath(fixed); + return (mkdir(fixed, 0755) == 0 || errno == EEXIST) ? TRUE : FALSE; +} +#define CreateDirectoryA CreateDirectory + +inline BOOL MoveFile(const char* src, const char* dst) +{ + char fsrc[MAX_PATH], fdst[MAX_PATH]; + strncpy(fsrc, src, MAX_PATH - 1); fsrc[MAX_PATH - 1] = '\0'; _linuxFixPath(fsrc); + strncpy(fdst, dst, MAX_PATH - 1); fdst[MAX_PATH - 1] = '\0'; _linuxFixPath(fdst); + return (rename(fsrc, fdst) == 0) ? TRUE : FALSE; +} + + +struct WIN32_FIND_DATA { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + DWORD dwReserved0; + DWORD dwReserved1; + char cFileName[MAX_PATH]; + char cAlternateFileName[14]; +}; + +struct LinuxFindHandle { + DIR* dir; + std::string basePath; +}; + +inline HANDLE FindFirstFile(const char* pattern, WIN32_FIND_DATA* wfd) +{ + if (!pattern || !wfd) return INVALID_HANDLE_VALUE; + + char normalized[MAX_PATH]; + strncpy(normalized, pattern, MAX_PATH - 1); normalized[MAX_PATH - 1] = '\0'; + _linuxFixPath(normalized); + + std::string pat(normalized); + size_t star = pat.rfind('*'); + if (star != std::string::npos) pat = pat.substr(0, star); + while (!pat.empty() && (pat.back() == '/' || pat.back() == '\\')) + pat.pop_back(); + if (pat.empty()) pat = "."; + + DIR* d = opendir(pat.c_str()); + if (!d) return INVALID_HANDLE_VALUE; + + struct dirent* entry = readdir(d); + if (!entry) { closedir(d); return INVALID_HANDLE_VALUE; } + + memset(wfd, 0, sizeof(WIN32_FIND_DATA)); + strncpy(wfd->cFileName, entry->d_name, MAX_PATH - 1); + + std::string fullPath = pat + "/" + entry->d_name; + struct stat st; + if (stat(fullPath.c_str(), &st) == 0) + { + if (S_ISDIR(st.st_mode)) wfd->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; + else wfd->dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + wfd->nFileSizeLow = (DWORD)(st.st_size & 0xFFFFFFFF); + wfd->nFileSizeHigh = (DWORD)((st.st_size >> 32) & 0xFFFFFFFF); + } + + LinuxFindHandle* fh = new LinuxFindHandle(); + fh->dir = d; + fh->basePath = pat; + return (HANDLE)fh; +} + +inline BOOL FindNextFile(HANDLE h, WIN32_FIND_DATA* wfd) +{ + if (!h || h == INVALID_HANDLE_VALUE || !wfd) return FALSE; + LinuxFindHandle* fh = (LinuxFindHandle*)h; + struct dirent* entry = readdir(fh->dir); + if (!entry) return FALSE; + + memset(wfd, 0, sizeof(WIN32_FIND_DATA)); + strncpy(wfd->cFileName, entry->d_name, MAX_PATH - 1); + + std::string fullPath = fh->basePath + "/" + entry->d_name; + struct stat st; + if (stat(fullPath.c_str(), &st) == 0) + { + if (S_ISDIR(st.st_mode)) wfd->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; + else wfd->dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + wfd->nFileSizeLow = (DWORD)(st.st_size & 0xFFFFFFFF); + wfd->nFileSizeHigh = (DWORD)((st.st_size >> 32) & 0xFFFFFFFF); + } + return TRUE; +} + +inline BOOL FindClose(HANDLE h) +{ + if (!h || h == INVALID_HANDLE_VALUE) return FALSE; + LinuxFindHandle* fh = (LinuxFindHandle*)h; + closedir(fh->dir); + delete fh; + return TRUE; +} +typedef WIN32_FIND_DATA _WIN32_FIND_DATAA; +typedef WIN32_FIND_DATA WIN32_FIND_DATAA; +#define FindFirstFileA FindFirstFile +#define FindNextFileA FindNextFile + +enum FINDEX_INFO_LEVELS { FindExInfoStandard = 0, FindExInfoBasic = 1 }; +enum FINDEX_SEARCH_OPS { FindExSearchNameMatch = 0, FindExSearchLimitToDirectories = 1 }; + +inline HANDLE FindFirstFileExA(const char* pattern, FINDEX_INFO_LEVELS /*lvl*/, void* findData, + FINDEX_SEARCH_OPS /*searchOp*/, void* /*filter*/, DWORD /*flags*/) +{ + return FindFirstFile(pattern, (WIN32_FIND_DATA*)findData); +} + +#define DeleteFileA DeleteFile + +inline BOOL RemoveDirectoryA(const char* path) +{ + char fixed[MAX_PATH]; + strncpy(fixed, path, MAX_PATH - 1); fixed[MAX_PATH - 1] = '\0'; + _linuxFixPath(fixed); + return (rmdir(fixed) == 0) ? TRUE : FALSE; +} + +inline BOOL CopyFileA(const char* src, const char* dst, BOOL failIfExists) +{ + char srcFixed[MAX_PATH], dstFixed[MAX_PATH]; + strncpy(srcFixed, src, MAX_PATH - 1); srcFixed[MAX_PATH - 1] = '\0'; + strncpy(dstFixed, dst, MAX_PATH - 1); dstFixed[MAX_PATH - 1] = '\0'; + _linuxFixPath(srcFixed); + _linuxFixPath(dstFixed); + + if (failIfExists) { + struct stat st; + if (stat(dstFixed, &st) == 0) return FALSE; + } + int in = open(srcFixed, O_RDONLY); + if (in < 0) return FALSE; + int out = open(dstFixed, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (out < 0) { close(in); return FALSE; } + char buf[8192]; + ssize_t n; + while ((n = read(in, buf, sizeof(buf))) > 0) { + ssize_t w = write(out, buf, n); + if (w != n) { close(in); close(out); return FALSE; } + } + close(in); + close(out); + return TRUE; +} + +inline int MultiByteToWideChar(unsigned int /*codePage*/, unsigned long /*flags*/, + const char* mbStr, int mbLen, + wchar_t* wcStr, int wcLen) +{ + if (!mbStr) return 0; + size_t srcLen = (mbLen == -1) ? strlen(mbStr) + 1 : (size_t)mbLen; + if (wcLen == 0 || !wcStr) return (int)srcLen; // estimate + size_t toCopy = (srcLen < (size_t)wcLen) ? srcLen : (size_t)wcLen; + for (size_t i = 0; i < toCopy; i++) wcStr[i] = (wchar_t)(unsigned char)mbStr[i]; + return (int)toCopy; +} + +typedef struct _WIN32_FILE_ATTRIBUTE_DATA { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; +} WIN32_FILE_ATTRIBUTE_DATA; + +#define GetFileExInfoStandard 0 + +inline BOOL GetFileAttributesEx(const char* path, int /*level*/, void* info) +{ + if (!path || !info) return FALSE; + WIN32_FILE_ATTRIBUTE_DATA* data = (WIN32_FILE_ATTRIBUTE_DATA*)info; + char fixed[MAX_PATH]; + strncpy(fixed, path, MAX_PATH - 1); fixed[MAX_PATH - 1] = '\0'; + _linuxFixPath(fixed); + struct stat st; + if (stat(fixed, &st) != 0) return FALSE; + + memset(data, 0, sizeof(WIN32_FILE_ATTRIBUTE_DATA)); + data->dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + if (S_ISDIR(st.st_mode)) data->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; + + data->nFileSizeLow = (DWORD)(st.st_size & 0xFFFFFFFF); + data->nFileSizeHigh = (DWORD)((st.st_size >> 32) & 0xFFFFFFFF); + + uint64_t ticks = ((uint64_t)st.st_mtime + 11644473600ULL) * 10000000ULL; + data->ftLastWriteTime.dwLowDateTime = (DWORD)(ticks & 0xFFFFFFFF); + data->ftLastWriteTime.dwHighDateTime = (DWORD)(ticks >> 32); + + return TRUE; +} +#define GetFileAttributesExA GetFileAttributesEx + +inline void OutputDebugStringA(const char*) {} +inline void OutputDebugStringW(const wchar_t*) {} +#define OutputDebugString OutputDebugStringA + +using std::wstring; +using std::string; + +#endif // __cplusplus + +#endif // __linux__ diff --git a/Linux/PosixNetLayer.cpp b/Linux/PosixNetLayer.cpp new file mode 100644 index 0000000..6de5b61 --- /dev/null +++ b/Linux/PosixNetLayer.cpp @@ -0,0 +1,1076 @@ + +#include "stdafx.h" + +#ifdef __linux__ + +#include "PosixNetLayer.h" +#include "../../Minecraft.Client/Common/Network/PlatformNetworkManagerStub.h" +#include "../../Minecraft.World/Socket.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SOCKET WinsockNetLayer::s_listenSocket = INVALID_SOCKET; +SOCKET WinsockNetLayer::s_hostConnectionSocket = INVALID_SOCKET; +pthread_t WinsockNetLayer::s_acceptThread; +bool WinsockNetLayer::s_acceptThreadActive = false; +pthread_t WinsockNetLayer::s_clientRecvThread; +bool WinsockNetLayer::s_clientRecvThreadActive = false; + +bool WinsockNetLayer::s_isHost = false; +bool WinsockNetLayer::s_connected = false; +bool WinsockNetLayer::s_active = false; +bool WinsockNetLayer::s_initialized = false; + +uint8_t WinsockNetLayer::s_localSmallId = 0; +uint8_t WinsockNetLayer::s_hostSmallId = 0; +uint8_t WinsockNetLayer::s_nextSmallId = 1; + +pthread_mutex_t WinsockNetLayer::s_sendLock = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t WinsockNetLayer::s_connectionsLock = PTHREAD_MUTEX_INITIALIZER; + +Win64RemoteConnection WinsockNetLayer::s_connections[WIN64_NET_MAX_CLIENTS + 1]; + +SOCKET WinsockNetLayer::s_advertiseSock = INVALID_SOCKET; +pthread_t WinsockNetLayer::s_advertiseThread; +bool WinsockNetLayer::s_advertiseThreadActive = false; +volatile bool WinsockNetLayer::s_advertising = false; +Win64LANBroadcast WinsockNetLayer::s_advertiseData = {}; +pthread_mutex_t WinsockNetLayer::s_advertiseLock = PTHREAD_MUTEX_INITIALIZER; +int WinsockNetLayer::s_hostGamePort = WIN64_NET_DEFAULT_PORT; + +SOCKET WinsockNetLayer::s_discoverySock = INVALID_SOCKET; +pthread_t WinsockNetLayer::s_discoveryThread; +bool WinsockNetLayer::s_discoveryThreadActive = false; +volatile bool WinsockNetLayer::s_discovering = false; +pthread_mutex_t WinsockNetLayer::s_discoveryLock = PTHREAD_MUTEX_INITIALIZER; +std::vector WinsockNetLayer::s_discoveredSessions; + +pthread_mutex_t WinsockNetLayer::s_disconnectLock = PTHREAD_MUTEX_INITIALIZER; +std::vector WinsockNetLayer::s_disconnectedSmallIds; + +pthread_mutex_t WinsockNetLayer::s_pendingJoinLock = PTHREAD_MUTEX_INITIALIZER; +std::vector WinsockNetLayer::s_pendingJoinSmallIds; + +pthread_mutex_t WinsockNetLayer::s_freeSmallIdLock = PTHREAD_MUTEX_INITIALIZER; +std::vector WinsockNetLayer::s_freeSmallIds; + +pthread_mutex_t WinsockNetLayer::s_earlyDataLock = PTHREAD_MUTEX_INITIALIZER; +std::vector WinsockNetLayer::s_earlyDataBuffers[WIN64_NET_MAX_CLIENTS + 1]; + +bool g_Win64MultiplayerHost = false; +bool g_Win64MultiplayerJoin = false; +int g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT; +char g_Win64MultiplayerIP[256] = "127.0.0.1"; + +bool g_ServerAdvertiseLAN = true; +char g_ServerBindAddress[256] = ""; + +static void InitMutex(pthread_mutex_t* m) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(m, &attr); + pthread_mutexattr_destroy(&attr); +} + +bool WinsockNetLayer::Initialize() +{ + if (s_initialized) return true; + + + InitMutex(&s_sendLock); + InitMutex(&s_connectionsLock); + InitMutex(&s_advertiseLock); + InitMutex(&s_discoveryLock); + InitMutex(&s_disconnectLock); + InitMutex(&s_pendingJoinLock); + InitMutex(&s_freeSmallIdLock); + InitMutex(&s_earlyDataLock); + + for (int i = 0; i < WIN64_NET_MAX_CLIENTS + 1; i++) + { + s_connections[i].tcpSocket = INVALID_SOCKET; + s_connections[i].smallId = 0; + s_connections[i].recvThreadActive = false; + s_connections[i].active = false; + InitMutex(&s_connections[i].sendLock); + } + + s_initialized = true; + +#ifndef WITH_SERVER_CODE + StartDiscovery(); +#endif + + return true; +} + +void WinsockNetLayer::Shutdown() +{ + StopAdvertising(); + StopDiscovery(); + + s_active = false; + s_connected = false; + + if (s_listenSocket != INVALID_SOCKET) + { + close(s_listenSocket); + s_listenSocket = INVALID_SOCKET; + } + + if (s_hostConnectionSocket != INVALID_SOCKET) + { + close(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + } + + pthread_mutex_lock(&s_connectionsLock); + for (int i = 0; i < WIN64_NET_MAX_CLIENTS + 1; i++) + { + s_connections[i].active = false; + if (s_connections[i].tcpSocket != INVALID_SOCKET) + { + close(s_connections[i].tcpSocket); + s_connections[i].tcpSocket = INVALID_SOCKET; + } + if (s_connections[i].recvThreadActive) + { + pthread_join(s_connections[i].recvThread, NULL); + s_connections[i].recvThreadActive = false; + } + pthread_mutex_destroy(&s_connections[i].sendLock); + } + pthread_mutex_unlock(&s_connectionsLock); + + if (s_acceptThreadActive) + { + pthread_join(s_acceptThread, NULL); + s_acceptThreadActive = false; + } + + if (s_clientRecvThreadActive) + { + pthread_join(s_clientRecvThread, NULL); + s_clientRecvThreadActive = false; + } + + if (s_initialized) + { + pthread_mutex_destroy(&s_sendLock); + pthread_mutex_destroy(&s_connectionsLock); + pthread_mutex_destroy(&s_advertiseLock); + pthread_mutex_destroy(&s_discoveryLock); + pthread_mutex_destroy(&s_disconnectLock); + s_disconnectedSmallIds.clear(); + pthread_mutex_destroy(&s_pendingJoinLock); + s_pendingJoinSmallIds.clear(); + pthread_mutex_destroy(&s_freeSmallIdLock); + s_freeSmallIds.clear(); + s_initialized = false; + } +} + +bool WinsockNetLayer::HostGame(int port) +{ + if (!s_initialized && !Initialize()) return false; + + s_isHost = true; + s_localSmallId = 0; + s_hostSmallId = 0; + s_nextSmallId = 1; + s_hostGamePort = port; + + pthread_mutex_lock(&s_freeSmallIdLock); + s_freeSmallIds.clear(); + pthread_mutex_unlock(&s_freeSmallIdLock); + + s_listenSocket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s_listenSocket == INVALID_SOCKET) + { + app.DebugPrintf("socket() failed: %s", strerror(errno)); + return false; + } + + int opt = 1; + ::setsockopt(s_listenSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons((uint16_t)port); + + extern char g_ServerBindAddress[256]; + if (g_ServerBindAddress[0] != '\0') + addr.sin_addr.s_addr = inet_addr(g_ServerBindAddress); + else + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + int iResult = ::bind(s_listenSocket, (struct sockaddr *)&addr, sizeof(addr)); + if (iResult < 0) + { + app.DebugPrintf("bind() failed: %s", strerror(errno)); + ::close(s_listenSocket); + s_listenSocket = INVALID_SOCKET; + return false; + } + + iResult = ::listen(s_listenSocket, SOMAXCONN); + if (iResult < 0) + { + app.DebugPrintf("listen() failed: %s", strerror(errno)); + ::close(s_listenSocket); + s_listenSocket = INVALID_SOCKET; + return false; + } + + s_active = true; + s_connected = true; + + if (pthread_create(&s_acceptThread, NULL, AcceptThreadProc, NULL) == 0) + s_acceptThreadActive = true; + + app.DebugPrintf("POSIX LAN: Hosting on port %d", port); + return true; +} + +bool WinsockNetLayer::JoinGame(const char *ip, int port) +{ + if (!s_initialized && !Initialize()) return false; + + s_isHost = false; + s_hostSmallId = 0; + s_connected = false; + s_active = false; + + if (s_hostConnectionSocket != INVALID_SOCKET) + { + close(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + } + + struct addrinfo hints = {}; + struct addrinfo *result = NULL; + + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + char portStr[16]; + snprintf(portStr, sizeof(portStr), "%d", port); + + int iResult = getaddrinfo(ip, portStr, &hints, &result); + if (iResult != 0) + { + app.DebugPrintf("getaddrinfo failed for %s:%d - %s\n", ip, port, gai_strerror(iResult)); + return false; + } + + bool connected = false; + uint8_t assignedSmallId = 0; + const int maxAttempts = 3; + const int connectTimeoutMs = 3000; + + for (int attempt = 0; attempt < maxAttempts; ++attempt) + { + s_hostConnectionSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (s_hostConnectionSocket == INVALID_SOCKET) + { + app.DebugPrintf("socket() failed: %s\n", strerror(errno)); + break; + } + + int noDelay = 1; + setsockopt(s_hostConnectionSocket, IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay)); + + int flags = fcntl(s_hostConnectionSocket, F_GETFL, 0); + fcntl(s_hostConnectionSocket, F_SETFL, flags | O_NONBLOCK); + + iResult = connect(s_hostConnectionSocket, result->ai_addr, result->ai_addrlen); + if (iResult < 0 && errno == EINPROGRESS) + { + struct pollfd pfd; + pfd.fd = s_hostConnectionSocket; + pfd.events = POLLOUT; + pfd.revents = 0; + + int pollResult = poll(&pfd, 1, connectTimeoutMs); + if (pollResult <= 0 || (pfd.revents & (POLLERR | POLLHUP))) + { + app.DebugPrintf("connect() to %s:%d timed out or failed (attempt %d/%d)\n", ip, port, attempt + 1, maxAttempts); + close(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + continue; + } + + int sockErr = 0; + socklen_t sockErrLen = sizeof(sockErr); + getsockopt(s_hostConnectionSocket, SOL_SOCKET, SO_ERROR, &sockErr, &sockErrLen); + if (sockErr != 0) + { + app.DebugPrintf("connect() to %s:%d failed with SO_ERROR %d (attempt %d/%d)\n", ip, port, sockErr, attempt + 1, maxAttempts); + close(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + continue; + } + } + else if (iResult < 0) + { + app.DebugPrintf("connect() to %s:%d failed (attempt %d/%d): %s\n", ip, port, attempt + 1, maxAttempts, strerror(errno)); + close(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + continue; + } + + fcntl(s_hostConnectionSocket, F_SETFL, flags & ~O_NONBLOCK); + + struct timeval recvTimeout; + recvTimeout.tv_sec = 3; + recvTimeout.tv_usec = 0; + setsockopt(s_hostConnectionSocket, SOL_SOCKET, SO_RCVTIMEO, &recvTimeout, sizeof(recvTimeout)); + + uint8_t assignBuf[1]; + ssize_t bytesRecv = recv(s_hostConnectionSocket, (char *)assignBuf, 1, 0); + if (bytesRecv != 1) + { + app.DebugPrintf("Failed to receive small ID assignment from host (attempt %d/%d)\n", attempt + 1, maxAttempts); + close(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + continue; + } + + assignedSmallId = assignBuf[0]; + connected = true; + break; + } + freeaddrinfo(result); + + if (!connected) + { + return false; + } + s_localSmallId = assignedSmallId; + + struct timeval noTimeout = {0, 0}; + setsockopt(s_hostConnectionSocket, SOL_SOCKET, SO_RCVTIMEO, &noTimeout, sizeof(noTimeout)); + + app.DebugPrintf("POSIX LAN: Connected to %s:%d, assigned smallId=%d\n", ip, port, s_localSmallId); + + s_active = true; + s_connected = true; + + if (pthread_create(&s_clientRecvThread, NULL, ClientRecvThreadProc, NULL) == 0) + s_clientRecvThreadActive = true; + + return true; +} + +bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void *data, int dataSize) +{ + if (sock == INVALID_SOCKET || dataSize <= 0 || dataSize > WIN64_NET_MAX_PACKET_SIZE) return false; + + uint8_t header[4]; + header[0] = (uint8_t)((dataSize >> 24) & 0xFF); + header[1] = (uint8_t)((dataSize >> 16) & 0xFF); + header[2] = (uint8_t)((dataSize >> 8) & 0xFF); + header[3] = (uint8_t)(dataSize & 0xFF); + + int totalSent = 0; + int toSend = 4; + while (totalSent < toSend) + { + ssize_t sent = send(sock, (const char *)header + totalSent, toSend - totalSent, MSG_NOSIGNAL); + if (sent <= 0) return false; + totalSent += sent; + } + + totalSent = 0; + while (totalSent < dataSize) + { + ssize_t sent = send(sock, (const char *)data + totalSent, dataSize - totalSent, MSG_NOSIGNAL); + if (sent <= 0) return false; + totalSent += sent; + } + + return true; +} + +bool WinsockNetLayer::SendToSmallId(uint8_t targetSmallId, const void *data, int dataSize) +{ + if (!s_active) return false; + + if (s_isHost) + { + pthread_mutex_lock(&s_connectionsLock); + if (targetSmallId >= WIN64_NET_MAX_CLIENTS + 1 || !s_connections[targetSmallId].active) + { + pthread_mutex_unlock(&s_connectionsLock); + return false; + } + SOCKET sock = s_connections[targetSmallId].tcpSocket; + pthread_mutex_t *pLock = &s_connections[targetSmallId].sendLock; + pthread_mutex_unlock(&s_connectionsLock); + + pthread_mutex_lock(pLock); + bool result = SendOnSocket(sock, data, dataSize); + pthread_mutex_unlock(pLock); + return result; + } + else + { + pthread_mutex_lock(&s_sendLock); + bool result = SendOnSocket(s_hostConnectionSocket, data, dataSize); + pthread_mutex_unlock(&s_sendLock); + return result; + } +} + +SOCKET WinsockNetLayer::GetSocketForSmallId(uint8_t smallId) +{ + pthread_mutex_lock(&s_connectionsLock); + if (smallId < WIN64_NET_MAX_CLIENTS + 1 && s_connections[smallId].active) + { + SOCKET sock = s_connections[smallId].tcpSocket; + pthread_mutex_unlock(&s_connectionsLock); + return sock; + } + pthread_mutex_unlock(&s_connectionsLock); + return INVALID_SOCKET; +} + +std::string WinsockNetLayer::GetIPForSmallId(uint8_t smallId) +{ + SOCKET sock = GetSocketForSmallId(smallId); + if (sock == INVALID_SOCKET) return ""; + struct sockaddr_in addr; + socklen_t addrLen = sizeof(addr); + if (getpeername(sock, (struct sockaddr *)&addr, &addrLen) != 0) return ""; + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &addr.sin_addr, buf, sizeof(buf)) == NULL) return ""; + return std::string(buf); +} + +static bool RecvExact(SOCKET sock, uint8_t *buf, int len) +{ + int totalRecv = 0; + while (totalRecv < len) + { + ssize_t r = recv(sock, (char *)buf + totalRecv, len - totalRecv, 0); + if (r <= 0) return false; + totalRecv += r; + } + return true; +} + +void WinsockNetLayer::HandleDataReceived(uint8_t fromSmallId, uint8_t toSmallId, unsigned char *data, unsigned int dataSize) +{ + INetworkPlayer *pPlayerFrom = g_NetworkManager.GetPlayerBySmallId(fromSmallId); + INetworkPlayer *pPlayerTo = g_NetworkManager.GetPlayerBySmallId(toSmallId); + + if (pPlayerFrom == NULL || pPlayerTo == NULL) + { + if (s_isHost && fromSmallId > 0 && fromSmallId < WIN64_NET_MAX_CLIENTS + 1) + { + pthread_mutex_lock(&s_earlyDataLock); + s_earlyDataBuffers[fromSmallId].insert( + s_earlyDataBuffers[fromSmallId].end(), data, data + dataSize); + pthread_mutex_unlock(&s_earlyDataLock); + } + return; + } + + if (s_isHost) + { + ::Socket *pSocket = pPlayerFrom->GetSocket(); + if (pSocket != NULL) + pSocket->pushDataToQueue(data, dataSize, false); + else + { + pthread_mutex_lock(&s_earlyDataLock); + s_earlyDataBuffers[fromSmallId].insert( + s_earlyDataBuffers[fromSmallId].end(), data, data + dataSize); + pthread_mutex_unlock(&s_earlyDataLock); + } + } + else + { + ::Socket *pSocket = pPlayerTo->GetSocket(); + if (pSocket != NULL) + pSocket->pushDataToQueue(data, dataSize, true); + } +} + +void WinsockNetLayer::FlushPendingData() +{ + pthread_mutex_lock(&s_earlyDataLock); + for (int i = 1; i < WIN64_NET_MAX_CLIENTS + 1; i++) + { + if (s_earlyDataBuffers[i].empty()) continue; + + INetworkPlayer *pPlayer = g_NetworkManager.GetPlayerBySmallId((uint8_t)i); + if (pPlayer == NULL) continue; + + ::Socket *pSocket = pPlayer->GetSocket(); + if (pSocket == NULL) continue; + + pSocket->pushDataToQueue(s_earlyDataBuffers[i].data(), + (DWORD)s_earlyDataBuffers[i].size(), false); + s_earlyDataBuffers[i].clear(); + } + pthread_mutex_unlock(&s_earlyDataLock); +} + +void* WinsockNetLayer::AcceptThreadProc(void* /*param*/) +{ + while (s_active) + { + SOCKET clientSocket = accept(s_listenSocket, NULL, NULL); + if (clientSocket == INVALID_SOCKET) + { + if (s_active) + app.DebugPrintf("accept() failed: %s\n", strerror(errno)); + break; + } + + int noDelay = 1; + setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay)); + + extern QNET_STATE _iQNetStubState; + if (_iQNetStubState != QNET_STATE_GAME_PLAY) + { + app.DebugPrintf("POSIX LAN: Rejecting connection, game not ready\n"); + close(clientSocket); + continue; + } + + uint8_t assignedSmallId; + pthread_mutex_lock(&s_freeSmallIdLock); + if (!s_freeSmallIds.empty()) + { + assignedSmallId = s_freeSmallIds.back(); + s_freeSmallIds.pop_back(); + } + else if (s_nextSmallId < MINECRAFT_NET_MAX_PLAYERS) + { + assignedSmallId = s_nextSmallId++; + } + else + { + pthread_mutex_unlock(&s_freeSmallIdLock); + app.DebugPrintf("POSIX LAN: Server full, rejecting connection\n"); + close(clientSocket); + continue; + } + pthread_mutex_unlock(&s_freeSmallIdLock); + + uint8_t assignBuf[1] = { assignedSmallId }; + ssize_t sent = send(clientSocket, (const char *)assignBuf, 1, MSG_NOSIGNAL); + if (sent != 1) + { + app.DebugPrintf("Failed to send small ID to client\n"); + close(clientSocket); + continue; + } + + Win64RemoteConnection &conn = s_connections[assignedSmallId]; + + pthread_mutex_lock(&s_connectionsLock); + if (conn.recvThreadActive) + { + pthread_join(conn.recvThread, NULL); + conn.recvThreadActive = false; + } + conn.tcpSocket = clientSocket; + conn.smallId = assignedSmallId; + conn.active = true; + pthread_mutex_unlock(&s_connectionsLock); + + app.DebugPrintf("POSIX LAN: Client connected, assigned smallId=%d\n", assignedSmallId); + + IQNetPlayer *qnetPlayer = &IQNet::m_player[assignedSmallId]; + + extern void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, uint8_t smallId, bool isHost, bool isLocal); + Win64_SetupRemoteQNetPlayer(qnetPlayer, assignedSmallId, false, false); + + pthread_mutex_lock(&s_pendingJoinLock); + s_pendingJoinSmallIds.push_back(assignedSmallId); + pthread_mutex_unlock(&s_pendingJoinLock); + + DWORD *threadParam = new DWORD; + *threadParam = assignedSmallId; + + pthread_mutex_lock(&s_connectionsLock); + if (pthread_create(&s_connections[assignedSmallId].recvThread, NULL, RecvThreadProc, threadParam) == 0) + s_connections[assignedSmallId].recvThreadActive = true; + else + delete threadParam; + pthread_mutex_unlock(&s_connectionsLock); + } + return NULL; +} + +void* WinsockNetLayer::RecvThreadProc(void* param) +{ + uint8_t clientSmallId = (uint8_t)*(DWORD *)param; + delete (DWORD *)param; + + pthread_mutex_lock(&s_connectionsLock); + if (clientSmallId >= WIN64_NET_MAX_CLIENTS + 1 || !s_connections[clientSmallId].active) + { + pthread_mutex_unlock(&s_connectionsLock); + return NULL; + } + SOCKET sock = s_connections[clientSmallId].tcpSocket; + pthread_mutex_unlock(&s_connectionsLock); + + std::vector recvBuf; + recvBuf.resize(WIN64_NET_RECV_BUFFER_SIZE); + + while (s_active) + { + uint8_t header[4]; + if (!RecvExact(sock, header, 4)) + { + app.DebugPrintf("POSIX LAN: Client smallId=%d disconnected (header)\n", clientSmallId); + break; + } + + int packetSize = + ((uint32_t)header[0] << 24) | + ((uint32_t)header[1] << 16) | + ((uint32_t)header[2] << 8) | + ((uint32_t)header[3]); + + if (packetSize <= 0 || (unsigned int)packetSize > WIN64_NET_MAX_PACKET_SIZE) + { + app.DebugPrintf("POSIX LAN: Invalid packet size %d from client smallId=%d (max=%d)\n", + packetSize, clientSmallId, (int)WIN64_NET_MAX_PACKET_SIZE); + break; + } + + if ((int)recvBuf.size() < packetSize) + { + recvBuf.resize(packetSize); + app.DebugPrintf("POSIX LAN: Resized host recv buffer to %d bytes for client smallId=%d\n", packetSize, clientSmallId); + } + + if (!RecvExact(sock, &recvBuf[0], packetSize)) + { + app.DebugPrintf("POSIX LAN: Client smallId=%d disconnected (body)\n", clientSmallId); + break; + } + + HandleDataReceived(clientSmallId, s_hostSmallId, &recvBuf[0], packetSize); + } + + pthread_mutex_lock(&s_connectionsLock); + s_connections[clientSmallId].active = false; + if (s_connections[clientSmallId].tcpSocket != INVALID_SOCKET) + { + close(s_connections[clientSmallId].tcpSocket); + s_connections[clientSmallId].tcpSocket = INVALID_SOCKET; + } + pthread_mutex_unlock(&s_connectionsLock); + + pthread_mutex_lock(&s_disconnectLock); + s_disconnectedSmallIds.push_back(clientSmallId); + pthread_mutex_unlock(&s_disconnectLock); + + return NULL; +} + +bool WinsockNetLayer::PopDisconnectedSmallId(uint8_t *outSmallId) +{ + bool found = false; + pthread_mutex_lock(&s_disconnectLock); + if (!s_disconnectedSmallIds.empty()) + { + *outSmallId = s_disconnectedSmallIds.back(); + s_disconnectedSmallIds.pop_back(); + found = true; + } + pthread_mutex_unlock(&s_disconnectLock); + return found; +} + +void WinsockNetLayer::PushFreeSmallId(uint8_t smallId) +{ + pthread_mutex_lock(&s_freeSmallIdLock); + s_freeSmallIds.push_back(smallId); + pthread_mutex_unlock(&s_freeSmallIdLock); +} + +bool WinsockNetLayer::PopPendingJoinSmallId(uint8_t *outSmallId) +{ + bool found = false; + pthread_mutex_lock(&s_pendingJoinLock); + if (!s_pendingJoinSmallIds.empty()) + { + *outSmallId = s_pendingJoinSmallIds.back(); + s_pendingJoinSmallIds.pop_back(); + found = true; + } + pthread_mutex_unlock(&s_pendingJoinLock); + return found; +} + +bool WinsockNetLayer::IsSmallIdConnected(uint8_t smallId) +{ + if (smallId >= WIN64_NET_MAX_CLIENTS + 1) return false; + return s_connections[smallId].active; +} + +void WinsockNetLayer::CloseConnectionBySmallId(uint8_t smallId) +{ + pthread_mutex_lock(&s_connectionsLock); + if (smallId < WIN64_NET_MAX_CLIENTS + 1 && s_connections[smallId].active && s_connections[smallId].tcpSocket != INVALID_SOCKET) + { + close(s_connections[smallId].tcpSocket); + s_connections[smallId].tcpSocket = INVALID_SOCKET; + app.DebugPrintf("POSIX LAN: Force-closed TCP connection for smallId=%d\n", smallId); + } + pthread_mutex_unlock(&s_connectionsLock); + + pthread_mutex_lock(&s_earlyDataLock); + if (smallId < WIN64_NET_MAX_CLIENTS + 1) + s_earlyDataBuffers[smallId].clear(); + pthread_mutex_unlock(&s_earlyDataLock); +} + +void* WinsockNetLayer::ClientRecvThreadProc(void* /*param*/) +{ + std::vector recvBuf; + recvBuf.resize(WIN64_NET_RECV_BUFFER_SIZE); + + while (s_active && s_hostConnectionSocket != INVALID_SOCKET) + { + uint8_t header[4]; + if (!RecvExact(s_hostConnectionSocket, header, 4)) + { + app.DebugPrintf("POSIX LAN: Disconnected from host (header)\n"); + break; + } + + int packetSize = ((uint32_t)header[0] << 24) | ((uint32_t)header[1] << 16) | ((uint32_t)header[2] << 8) | (uint32_t)header[3]; + + if (packetSize <= 0 || (unsigned int)packetSize > WIN64_NET_MAX_PACKET_SIZE) + { + app.DebugPrintf("POSIX LAN: Invalid packet size %d from host (max=%d)\n", + packetSize, (int)WIN64_NET_MAX_PACKET_SIZE); + break; + } + + if ((int)recvBuf.size() < packetSize) + recvBuf.resize(packetSize); + + if (!RecvExact(s_hostConnectionSocket, &recvBuf[0], packetSize)) + { + app.DebugPrintf("POSIX LAN: Disconnected from host (body)\n"); + break; + } + + HandleDataReceived(s_hostSmallId, s_localSmallId, &recvBuf[0], packetSize); + } + + s_connected = false; + return NULL; +} + +bool WinsockNetLayer::StartAdvertising(int gamePort, const wchar_t *hostName, unsigned int gameSettings, unsigned int texPackId, unsigned char subTexId, unsigned short netVer) +{ + if (s_advertising) return true; + if (!s_initialized) return false; + + pthread_mutex_lock(&s_advertiseLock); + memset(&s_advertiseData, 0, sizeof(s_advertiseData)); + s_advertiseData.magic = WIN64_LAN_BROADCAST_MAGIC; + s_advertiseData.netVersion = netVer; + s_advertiseData.gamePort = (uint16_t)gamePort; + for (int i = 0; i < 31 && hostName[i] != L'\0'; i++) + s_advertiseData.hostName[i] = (uint16_t)hostName[i]; + s_advertiseData.playerCount = 1; + s_advertiseData.maxPlayers = MINECRAFT_NET_MAX_PLAYERS; + s_advertiseData.gameHostSettings = gameSettings; + s_advertiseData.texturePackParentId = texPackId; + s_advertiseData.subTexturePackId = subTexId; + s_advertiseData.isJoinable = 0; +#ifdef WITH_SERVER_CODE + s_advertiseData.isDedicatedServer = 1; +#else + s_advertiseData.isDedicatedServer = 0; +#endif + s_hostGamePort = gamePort; + pthread_mutex_unlock(&s_advertiseLock); + + s_advertiseSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s_advertiseSock == INVALID_SOCKET) + { + app.DebugPrintf("POSIX LAN: Failed to create advertise socket: %s\n", strerror(errno)); + return false; + } + + int broadcast = 1; + setsockopt(s_advertiseSock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)); + + s_advertising = true; + if (pthread_create(&s_advertiseThread, NULL, AdvertiseThreadProc, NULL) == 0) + s_advertiseThreadActive = true; + + app.DebugPrintf("POSIX LAN: Started advertising on UDP port %d\n", WIN64_LAN_DISCOVERY_PORT); + return true; +} + +void WinsockNetLayer::StopAdvertising() +{ + s_advertising = false; + + if (s_advertiseSock != INVALID_SOCKET) + { + close(s_advertiseSock); + s_advertiseSock = INVALID_SOCKET; + } + + if (s_advertiseThreadActive) + { + pthread_join(s_advertiseThread, NULL); + s_advertiseThreadActive = false; + } +} + +void WinsockNetLayer::UpdateAdvertisePlayerCount(uint8_t count) +{ + pthread_mutex_lock(&s_advertiseLock); + s_advertiseData.playerCount = count; + pthread_mutex_unlock(&s_advertiseLock); +} + +void WinsockNetLayer::UpdateAdvertisePlayerNames(uint8_t count, const char playerNames[][32]) +{ + pthread_mutex_lock(&s_advertiseLock); + memset(s_advertiseData.playerNames, 0, sizeof(s_advertiseData.playerNames)); + s_advertiseData.playerCount = count; + for (int i = 0; i < count && i < WIN64_LAN_BROADCAST_PLAYERS; i++) + memcpy(s_advertiseData.playerNames[i], playerNames[i], 32); + pthread_mutex_unlock(&s_advertiseLock); +} + +void WinsockNetLayer::UpdateAdvertiseJoinable(bool joinable) +{ + pthread_mutex_lock(&s_advertiseLock); + s_advertiseData.isJoinable = joinable ? 1 : 0; + pthread_mutex_unlock(&s_advertiseLock); +} + +void* WinsockNetLayer::AdvertiseThreadProc(void* /*param*/) +{ + struct sockaddr_in broadcastAddr; + memset(&broadcastAddr, 0, sizeof(broadcastAddr)); + broadcastAddr.sin_family = AF_INET; + broadcastAddr.sin_port = htons(WIN64_LAN_DISCOVERY_PORT); + broadcastAddr.sin_addr.s_addr = INADDR_BROADCAST; + + while (s_advertising) + { + pthread_mutex_lock(&s_advertiseLock); + Win64LANBroadcast data = s_advertiseData; + pthread_mutex_unlock(&s_advertiseLock); + + ssize_t sent = sendto(s_advertiseSock, (const char *)&data, sizeof(data), 0, + (struct sockaddr *)&broadcastAddr, sizeof(broadcastAddr)); + + if (sent < 0 && s_advertising) + app.DebugPrintf("POSIX LAN: Broadcast sendto failed: %s\n", strerror(errno)); + + sleep(1); + } + + return NULL; +} + +bool WinsockNetLayer::StartDiscovery() +{ + if (s_discovering) return true; + if (!s_initialized) return false; + + s_discoverySock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s_discoverySock == INVALID_SOCKET) + { + app.DebugPrintf("POSIX LAN: Failed to create discovery socket: %s\n", strerror(errno)); + return false; + } + + int reuseAddr = 1; + setsockopt(s_discoverySock, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)); + + struct sockaddr_in bindAddr; + memset(&bindAddr, 0, sizeof(bindAddr)); + bindAddr.sin_family = AF_INET; + bindAddr.sin_port = htons(WIN64_LAN_DISCOVERY_PORT); + bindAddr.sin_addr.s_addr = INADDR_ANY; + + if (bind(s_discoverySock, (struct sockaddr *)&bindAddr, sizeof(bindAddr)) < 0) + { + app.DebugPrintf("POSIX LAN: Discovery bind failed: %s\n", strerror(errno)); + close(s_discoverySock); + s_discoverySock = INVALID_SOCKET; + return false; + } + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 500000; + setsockopt(s_discoverySock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + + s_discovering = true; + if (pthread_create(&s_discoveryThread, NULL, DiscoveryThreadProc, NULL) == 0) + s_discoveryThreadActive = true; + + app.DebugPrintf("POSIX LAN: Listening for LAN games on UDP port %d\n", WIN64_LAN_DISCOVERY_PORT); + return true; +} + +void WinsockNetLayer::StopDiscovery() +{ + s_discovering = false; + + if (s_discoverySock != INVALID_SOCKET) + { + close(s_discoverySock); + s_discoverySock = INVALID_SOCKET; + } + + if (s_discoveryThreadActive) + { + pthread_join(s_discoveryThread, NULL); + s_discoveryThreadActive = false; + } + + pthread_mutex_lock(&s_discoveryLock); + s_discoveredSessions.clear(); + pthread_mutex_unlock(&s_discoveryLock); +} + +std::vector WinsockNetLayer::GetDiscoveredSessions() +{ + std::vector result; + pthread_mutex_lock(&s_discoveryLock); + result = s_discoveredSessions; + pthread_mutex_unlock(&s_discoveryLock); + return result; +} + +void* WinsockNetLayer::DiscoveryThreadProc(void* /*param*/) +{ + char recvBuf[1024]; + const size_t MAX_DISCOVERED_SESSIONS = 64; + + while (s_discovering) + { + struct sockaddr_in senderAddr; + socklen_t senderLen = sizeof(senderAddr); + + ssize_t recvLen = recvfrom(s_discoverySock, recvBuf, sizeof(recvBuf), 0, + (struct sockaddr *)&senderAddr, &senderLen); + + if (recvLen < 0) + continue; + + if ((size_t)recvLen < sizeof(Win64LANBroadcast)) + continue; + + Win64LANBroadcast *broadcast = (Win64LANBroadcast *)recvBuf; + if (broadcast->magic != WIN64_LAN_BROADCAST_MAGIC) + continue; + + broadcast->hostName[31] = 0; + + for (int pn = 0; pn < WIN64_LAN_BROADCAST_PLAYERS; pn++) + broadcast->playerNames[pn][31] = '\0'; + + char senderIP[64]; + inet_ntop(AF_INET, &senderAddr.sin_addr, senderIP, sizeof(senderIP)); + + DWORD now = GetTickCount(); + + pthread_mutex_lock(&s_discoveryLock); + + bool found = false; + for (size_t i = 0; i < s_discoveredSessions.size(); i++) + { + if (strcmp(s_discoveredSessions[i].hostIP, senderIP) == 0 && + s_discoveredSessions[i].hostPort == (int)broadcast->gamePort) + { + s_discoveredSessions[i].netVersion = broadcast->netVersion; + for (int c = 0; c < 32; c++) + s_discoveredSessions[i].hostName[c] = (wchar_t)broadcast->hostName[c]; + s_discoveredSessions[i].playerCount = broadcast->playerCount; + s_discoveredSessions[i].maxPlayers = broadcast->maxPlayers; + s_discoveredSessions[i].gameHostSettings = broadcast->gameHostSettings; + s_discoveredSessions[i].texturePackParentId = broadcast->texturePackParentId; + s_discoveredSessions[i].subTexturePackId = broadcast->subTexturePackId; + s_discoveredSessions[i].isJoinable = (broadcast->isJoinable != 0); + s_discoveredSessions[i].isDedicatedServer = (broadcast->isDedicatedServer != 0); + s_discoveredSessions[i].lastSeenTick = now; + memcpy(s_discoveredSessions[i].playerNames, broadcast->playerNames, sizeof(broadcast->playerNames)); + found = true; + break; + } + } + + if (!found) + { + if (s_discoveredSessions.size() >= MAX_DISCOVERED_SESSIONS) + { + pthread_mutex_unlock(&s_discoveryLock); + continue; + } + + Win64LANSession session; + memset(&session, 0, sizeof(session)); + strncpy(session.hostIP, senderIP, sizeof(session.hostIP) - 1); + session.hostPort = (int)broadcast->gamePort; + session.netVersion = broadcast->netVersion; + for (int c = 0; c < 32; c++) + session.hostName[c] = (wchar_t)broadcast->hostName[c]; + session.playerCount = broadcast->playerCount; + session.maxPlayers = broadcast->maxPlayers; + session.gameHostSettings = broadcast->gameHostSettings; + session.texturePackParentId = broadcast->texturePackParentId; + session.subTexturePackId = broadcast->subTexturePackId; + session.isJoinable = (broadcast->isJoinable != 0); + session.isDedicatedServer = (broadcast->isDedicatedServer != 0); + session.lastSeenTick = now; + memcpy(session.playerNames, broadcast->playerNames, sizeof(broadcast->playerNames)); + s_discoveredSessions.push_back(session); + + app.DebugPrintf("POSIX LAN: Discovered game \"%ls\" at %s:%d\n", + session.hostName, session.hostIP, session.hostPort); + } + + for (size_t i = s_discoveredSessions.size(); i > 0; i--) + { + if (now - s_discoveredSessions[i - 1].lastSeenTick > 5000) + { + app.DebugPrintf("POSIX LAN: Session \"%ls\" at %s timed out\n", + s_discoveredSessions[i - 1].hostName, s_discoveredSessions[i - 1].hostIP); + s_discoveredSessions.erase(s_discoveredSessions.begin() + (i - 1)); + } + } + + pthread_mutex_unlock(&s_discoveryLock); + } + + return NULL; +} + +#endif // __linux__ diff --git a/Linux/PosixNetLayer.h b/Linux/PosixNetLayer.h new file mode 100644 index 0000000..5e151a3 --- /dev/null +++ b/Linux/PosixNetLayer.h @@ -0,0 +1,188 @@ +#pragma once + + +#ifdef __linux__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../Minecraft.Client/Common/Network/NetworkPlayerInterface.h" + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) + +inline int closesocket(SOCKET s) { return close(s); } + +#define WIN64_NET_DEFAULT_PORT 25565 +#define WIN64_NET_MAX_CLIENTS 7 +#define WIN64_NET_RECV_BUFFER_SIZE 65536 +#define WIN64_NET_MAX_PACKET_SIZE (3 * 1024 * 1024) +#define WIN64_LAN_DISCOVERY_PORT 25566 +#define WIN64_LAN_BROADCAST_MAGIC 0x4D434C4E +#define WIN64_LAN_BROADCAST_PLAYERS 8 + +class Socket; + +#pragma pack(push, 1) +struct Win64LANBroadcast +{ + DWORD magic; + uint16_t netVersion; + uint16_t gamePort; + uint16_t hostName[32]; + uint8_t playerCount; + uint8_t maxPlayers; + DWORD gameHostSettings; + DWORD texturePackParentId; + uint8_t subTexturePackId; + uint8_t isJoinable; + uint8_t isDedicatedServer; + char playerNames[WIN64_LAN_BROADCAST_PLAYERS][32]; // XUSER_NAME_SIZE +}; +#pragma pack(pop) + +struct Win64LANSession +{ + char hostIP[64]; + int hostPort; + wchar_t hostName[32]; + unsigned short netVersion; + unsigned char playerCount; + unsigned char maxPlayers; + unsigned int gameHostSettings; + unsigned int texturePackParentId; + unsigned char subTexturePackId; + bool isJoinable; + bool isDedicatedServer; + DWORD lastSeenTick; + char playerNames[WIN64_LAN_BROADCAST_PLAYERS][32]; +}; + +struct Win64RemoteConnection +{ + SOCKET tcpSocket; + uint8_t smallId; + pthread_t recvThread; + bool recvThreadActive; + volatile bool active; + pthread_mutex_t sendLock; +}; + +class WinsockNetLayer +{ +public: + static bool Initialize(); + static void Shutdown(); + + static bool HostGame(int port); + static bool JoinGame(const char *ip, int port); + + static bool SendToSmallId(uint8_t targetSmallId, const void *data, int dataSize); + static bool SendOnSocket(SOCKET sock, const void *data, int dataSize); + + static bool IsHosting() { return s_isHost; } + static bool IsConnected() { return s_connected; } + static bool IsActive() { return s_active; } + + static uint8_t GetLocalSmallId() { return s_localSmallId; } + static uint8_t GetHostSmallId() { return s_hostSmallId; } + + static SOCKET GetSocketForSmallId(uint8_t smallId); + static std::string GetIPForSmallId(uint8_t smallId); + + static void HandleDataReceived(uint8_t fromSmallId, uint8_t toSmallId, unsigned char *data, unsigned int dataSize); + static void FlushPendingData(); + + static bool PopDisconnectedSmallId(uint8_t *outSmallId); + static void PushFreeSmallId(uint8_t smallId); + static void CloseConnectionBySmallId(uint8_t smallId); + + static bool PopPendingJoinSmallId(uint8_t *outSmallId); + + static bool IsSmallIdConnected(uint8_t smallId); + + static bool StartAdvertising(int gamePort, const wchar_t *hostName, unsigned int gameSettings, unsigned int texPackId, unsigned char subTexId, unsigned short netVer); + static void StopAdvertising(); + static void UpdateAdvertisePlayerCount(uint8_t count); + static void UpdateAdvertiseJoinable(bool joinable); + static void UpdateAdvertisePlayerNames(uint8_t count, const char playerNames[][32]); + + static bool StartDiscovery(); + static void StopDiscovery(); + static std::vector GetDiscoveredSessions(); + + static int GetHostPort() { return s_hostGamePort; } + +private: + static void* AcceptThreadProc(void* param); + static void* RecvThreadProc(void* param); + static void* ClientRecvThreadProc(void* param); + static void* AdvertiseThreadProc(void* param); + static void* DiscoveryThreadProc(void* param); + + static SOCKET s_listenSocket; + static SOCKET s_hostConnectionSocket; + static pthread_t s_acceptThread; + static bool s_acceptThreadActive; + static pthread_t s_clientRecvThread; + static bool s_clientRecvThreadActive; + + static bool s_isHost; + static bool s_connected; + static bool s_active; + static bool s_initialized; + + static uint8_t s_localSmallId; + static uint8_t s_hostSmallId; + static uint8_t s_nextSmallId; + + static pthread_mutex_t s_sendLock; + static pthread_mutex_t s_connectionsLock; + + static Win64RemoteConnection s_connections[WIN64_NET_MAX_CLIENTS + 1]; + + static SOCKET s_advertiseSock; + static pthread_t s_advertiseThread; + static bool s_advertiseThreadActive; + static volatile bool s_advertising; + static Win64LANBroadcast s_advertiseData; + static pthread_mutex_t s_advertiseLock; + static int s_hostGamePort; + + static SOCKET s_discoverySock; + static pthread_t s_discoveryThread; + static bool s_discoveryThreadActive; + static volatile bool s_discovering; + static pthread_mutex_t s_discoveryLock; + static std::vector s_discoveredSessions; + + static pthread_mutex_t s_disconnectLock; + static std::vector s_disconnectedSmallIds; + + static pthread_mutex_t s_pendingJoinLock; + static std::vector s_pendingJoinSmallIds; + + static pthread_mutex_t s_freeSmallIdLock; + static std::vector s_freeSmallIds; + + static pthread_mutex_t s_earlyDataLock; + static std::vector s_earlyDataBuffers[WIN64_NET_MAX_CLIENTS + 1]; +}; + +extern bool g_Win64MultiplayerHost; +extern bool g_Win64MultiplayerJoin; +extern int g_Win64MultiplayerPort; +extern char g_Win64MultiplayerIP[256]; + +#endif // __linux__ diff --git a/Linux/WinsockNetLayer.h b/Linux/WinsockNetLayer.h new file mode 100644 index 0000000..e96a90e --- /dev/null +++ b/Linux/WinsockNetLayer.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef __linux__ +#include "../../Minecraft.Server/Linux/PosixNetLayer.h" +#else +#error "This redirect header should only be used on Linux builds" +#endif diff --git a/Linux/build.bat b/Linux/build.bat new file mode 100644 index 0000000..2eac8b1 --- /dev/null +++ b/Linux/build.bat @@ -0,0 +1,8 @@ +@echo off +set "ACTION=%~1" +if "%ACTION%"=="" set "ACTION=Release" +set "SCRIPT_DIR=%~dp0" +set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%" +for /f "usebackq delims=" %%P in (`wsl wslpath "%SCRIPT_DIR%"`) do set "WSL_DIR=%%P" +wsl bash "%WSL_DIR%/build.sh" "%ACTION%" +exit /b %ERRORLEVEL% diff --git a/Linux/build.sh b/Linux/build.sh new file mode 100644 index 0000000..0a85ef1 --- /dev/null +++ b/Linux/build.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" +SRC="$HOME/lcemp-src" +BUILD="$HOME/lcemp-build" +ACTION="${1:-Release}" + +case "$ACTION" in + Clean) + rm -rf "$BUILD" "$SRC" + echo "Cleaned." + exit 0 + ;; + Sync) + rm -rf "$SRC" + ;& +esac + +if [ ! -d "$SRC/Minecraft.World" ]; then + echo "copying source files to $SRC..." + mkdir -p "$SRC" + cp -a "$ROOT_DIR/." "$SRC/" + rm -rf "$SRC/build_linux" "$SRC/build_dedicated" "$SRC/x64" "$SRC/x64_Server_Release" "$SRC/ipch" "$SRC/.vs" +else + rsync -a "$ROOT_DIR/CMakeLists.txt" "$SRC/" + rsync -a "$ROOT_DIR/cmake/" "$SRC/cmake/" + rsync -a "$ROOT_DIR/Minecraft.Server/" "$SRC/Minecraft.Server/" --exclude='*.obj' --exclude='*.pdb' --exclude='*.sdf' --exclude='*.tlog' + rsync -a "$ROOT_DIR/Minecraft.World/" "$SRC/Minecraft.World/" --exclude='*.obj' --exclude='*.pdb' + rsync -a "$ROOT_DIR/Minecraft.Client/" "$SRC/Minecraft.Client/" --exclude='*.obj' --exclude='*.pdb' +fi + +mkdir -p "$BUILD" +cd "$BUILD" +cmake "$SRC/Minecraft.Server" -DCMAKE_BUILD_TYPE=Release +cmake --build . --target MinecraftDedicatedServer -- -j$(nproc) +strip MinecraftDedicatedServer + +echo "" +ls -lh MinecraftDedicatedServer diff --git a/Linux/stubs/DirectXMath.h b/Linux/stubs/DirectXMath.h new file mode 100644 index 0000000..956e049 --- /dev/null +++ b/Linux/stubs/DirectXMath.h @@ -0,0 +1,3 @@ +#pragma once +namespace DirectX { +} diff --git a/Linux/stubs/WS2tcpip.h b/Linux/stubs/WS2tcpip.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/Linux/stubs/WS2tcpip.h @@ -0,0 +1 @@ +#pragma once diff --git a/Linux/stubs/WinSock2.h b/Linux/stubs/WinSock2.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/Linux/stubs/WinSock2.h @@ -0,0 +1 @@ +#pragma once diff --git a/Linux/stubs/d3d11.h b/Linux/stubs/d3d11.h new file mode 100644 index 0000000..f522daf --- /dev/null +++ b/Linux/stubs/d3d11.h @@ -0,0 +1,37 @@ +#pragma once + +struct ID3D11Device; +struct ID3D11DeviceContext; +struct ID3D11RenderTargetView; +struct ID3D11DepthStencilView; +struct ID3D11ShaderResourceView; +struct ID3D11Buffer; +struct ID3D11Texture2D; +struct IDXGISwapChain; + +typedef struct D3D11_RECT { + long left; + long top; + long right; + long bottom; +} D3D11_RECT; + +enum { + D3D11_BLEND_ZERO = 1, + D3D11_BLEND_ONE = 2, + D3D11_BLEND_SRC_ALPHA = 3, + D3D11_BLEND_INV_SRC_ALPHA = 4, + D3D11_BLEND_DEST_ALPHA = 5, + D3D11_BLEND_SRC_COLOR = 6, + D3D11_BLEND_DEST_COLOR = 7, + D3D11_BLEND_INV_DEST_COLOR = 8, + D3D11_BLEND_INV_SRC_COLOR = 9, + D3D11_BLEND_BLEND_FACTOR = 10, + D3D11_BLEND_INV_BLEND_FACTOR = 11, + + D3D11_COMPARISON_LESS_EQUAL = 20, + D3D11_COMPARISON_EQUAL = 21, + D3D11_COMPARISON_GREATER = 22, + D3D11_COMPARISON_GREATER_EQUAL = 23, + D3D11_COMPARISON_ALWAYS = 24 +}; diff --git a/Linux/stubs/malloc.h b/Linux/stubs/malloc.h new file mode 100644 index 0000000..b2407a3 --- /dev/null +++ b/Linux/stubs/malloc.h @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/Linux/stubs/sal.h b/Linux/stubs/sal.h new file mode 100644 index 0000000..c969205 --- /dev/null +++ b/Linux/stubs/sal.h @@ -0,0 +1,28 @@ +#pragma once +#ifndef _In_ +#define _In_ +#endif +#ifndef _Out_ +#define _Out_ +#endif +#ifndef _Inout_ +#define _Inout_ +#endif +#ifndef _In_opt_ +#define _In_opt_ +#endif +#ifndef _Out_opt_ +#define _Out_opt_ +#endif +#ifndef _Inout_opt_ +#define _Inout_opt_ +#endif +#ifndef _In_z_ +#define _In_z_ +#endif +#ifndef _Printf_format_string_ +#define _Printf_format_string_ +#endif +#ifndef _Analysis_assume_ +#define _Analysis_assume_(x) +#endif diff --git a/Linux/stubs/tchar.h b/Linux/stubs/tchar.h new file mode 100644 index 0000000..0ab2b96 --- /dev/null +++ b/Linux/stubs/tchar.h @@ -0,0 +1,19 @@ +#pragma once +#include + +#ifndef _T +#define _T(x) L##x +#endif +#ifndef _TCHAR_DEFINED +typedef wchar_t TCHAR; +#define _TCHAR_DEFINED +#endif +#ifndef _tcslen +#define _tcslen wcslen +#endif +#ifndef _tcscpy +#define _tcscpy wcscpy +#endif +#ifndef _tcsncpy +#define _tcsncpy wcsncpy +#endif diff --git a/Linux/stubs/windows.h b/Linux/stubs/windows.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/Linux/stubs/windows.h @@ -0,0 +1 @@ +#pragma once diff --git a/Linux/stubs/xhash b/Linux/stubs/xhash new file mode 100644 index 0000000..1ad26bc --- /dev/null +++ b/Linux/stubs/xhash @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/Linux/stubs/xsocialpost.h b/Linux/stubs/xsocialpost.h new file mode 100644 index 0000000..44f067d --- /dev/null +++ b/Linux/stubs/xsocialpost.h @@ -0,0 +1,8 @@ +#pragma once + +#define XSOCIAL_CAPABILITY_POSTIMAGE 0x01 +#define XSOCIAL_CAPABILITY_POSTLINK 0x02 + +typedef struct _XSOCIAL_POST { + unsigned int dwFlags; +} XSOCIAL_POST; diff --git a/Minecraft.Server.vcxproj b/Minecraft.Server.vcxproj new file mode 100644 index 0000000..714bc7a --- /dev/null +++ b/Minecraft.Server.vcxproj @@ -0,0 +1,567 @@ + + + + + Debug + x64 + + + Release + x64 + + + Debug-Linux + x64 + + + Release-Linux + x64 + + + + en-US + {12A22166-696D-402E-A1B8-806F46567BF2} + Win32Proj + MinecraftServer + Minecraft.Server + + + + Application + MultiByte + v110 + + + Application + MultiByte + v110 + + + Makefile + v143 + + + Makefile + v143 + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)x64_Server_Debug\ + x64_Server_Debug\ + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\Xbox\Sentient\Include;$(IncludePath) + + + false + $(SolutionDir)x64_Server_Release\ + x64_Server_Release\ + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\Xbox\Sentient\Include;$(IncludePath) + + $(SolutionDir)Linux_Server_Debug\ + Linux_Server_Debug\ + "$(ProjectDir)Linux\build.bat" Debug + "$(ProjectDir)Linux\build.bat" Clean + "$(ProjectDir)Linux\build.bat" Rebuild-Debug + $(SolutionDir)build_linux\MinecraftDedicatedServer + __linux__;_DEDICATED_SERVER;_CONTENT_PACKAGE;_LARGE_WORLDS;_WINDOWS64;WITH_SERVER_CODE;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS + $(ProjectDir)Linux;$(ProjectDir)Linux\stubs;$(ProjectDir);$(ProjectDir)\..\Minecraft.Client;$(ProjectDir)\..\Minecraft.World;$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\Windows64\Iggy\include;$(ProjectDir)\..\Minecraft.Client\Xbox\Sentient\Include + -std=c++11 + + + $(SolutionDir)Linux_Server_Release\ + Linux_Server_Release\ + "$(ProjectDir)Linux\build.bat" Release + "$(ProjectDir)Linux\build.bat" Clean + "$(ProjectDir)Linux\build.bat" Rebuild-Release + $(SolutionDir)build_linux\MinecraftDedicatedServer + __linux__;_DEDICATED_SERVER;_CONTENT_PACKAGE;_LARGE_WORLDS;_WINDOWS64;WITH_SERVER_CODE;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS + $(ProjectDir)Linux;$(ProjectDir)Linux\stubs;$(ProjectDir);$(ProjectDir)\..\Minecraft.Client;$(ProjectDir)\..\Minecraft.World;$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\Windows64\Iggy\include;$(ProjectDir)\..\Minecraft.Client\Xbox\Sentient\Include + -std=c++11 + + + Use + stdafx.h + Level3 + ProgramDatabase + Disabled + Sync + false + $(IntDir)$(ProjectName).pch + MultiThreadedDebug + _DEDICATED_SERVER;_CONTENT_PACKAGE;WITH_SERVER_CODE;_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)\..\Minecraft.Client;$(ProjectDir)\..\Minecraft.Client\Windows64\Iggy\include;%(AdditionalIncludeDirectories) + true + true + Default + false + + + true + $(OutDir)$(ProjectName).pdb + Console + ws2_32.lib;..\Minecraft.World\x64_Debug\Minecraft.World.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Storage_d.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Profile_d.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Render_PC_d.lib;%(AdditionalDependencies) + NotSet + false + + + + + Use + stdafx.h + Level3 + None + Full + Sync + false + $(IntDir)$(ProjectName).pch + MultiThreaded + _DEDICATED_SERVER;_CONTENT_PACKAGE;WITH_SERVER_CODE;_LARGE_WORLDS;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)\..\Minecraft.Client;$(ProjectDir)\..\Minecraft.Client\Windows64\Iggy\include;%(AdditionalIncludeDirectories) + true + true + Default + false + Speed + + + false + Console + ws2_32.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Storage.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Profile_r.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Render_PC.lib;%(AdditionalDependencies) + NotSet + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + true + true + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + + + true + true + + + + + + + + + + + + + + + + + + + + + + + true + true + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + true + true + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + true + true + + + true + true + + + + true + true + + + + + + + + + + true + true + + + true + true + + + + + + diff --git a/Minecraft.Server.vcxproj.filters b/Minecraft.Server.vcxproj.filters new file mode 100644 index 0000000..56a9406 --- /dev/null +++ b/Minecraft.Server.vcxproj.filters @@ -0,0 +1,833 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {F4549051-0176-4AC3-91C4-626AC6AE3E45} + + + {7DDBBB6C-E16F-4CF0-A8C4-588ED0B6DC71} + + + {5823EB64-6F69-48EA-8D46-81FFA7F276FC} + + + {9270DB82-A155-43C0-9D54-455417E97748} + + + {EC39CF82-06DB-4BC5-9C80-F39B60E048ED} + + + {02CFBE7F-6A35-4620-B804-3D612385ACD0} + + + {4D352E32-7D20-4EB2-9BDA-89E7AD5E9772} + + + {F595EFE2-4D31-425B-B58D-C0E06402A8EB} + + + {916A333F-37B9-4310-9F8D-9AB833A0C913} + + + {6ABCD7BC-D2D7-40D8-905F-72F86DCD1FD3} + + + {58EC24F6-30EF-425A-87AF-CFF8CAFA7037} + + + {897BAA49-1C89-435F-A2C5-970920F9CC4F} + + + {D2A911EF-1245-48F7-BD3E-3B336558008F} + + + {F20822ED-AC55-413F-95E8-BCD9DCE43455} + + + {1126E4C0-31CF-4D9C-B5EF-A953541C9A7F} + + + {12B0324F-F606-40EF-9806-4CD453D2BFF3} + + + + + Header Files + + + Header Files\Core + + + Header Files\Core + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Commands + + + Header Files\Core + + + Header Files\Core + + + Header Files\Linux + + + Header Files\Linux + + + Header Files\Linux + + + Header Files\Linux + + + Header Files\Linux\stubs + + + Header Files\Linux\stubs + + + Header Files\Linux\stubs + + + Header Files\Linux\stubs + + + Header Files\Linux\stubs + + + Header Files\Linux\stubs + + + Header Files\Linux\stubs + + + Header Files\Linux\stubs + + + Header Files\Linux\stubs + + + Header Files\Linux\stubs + + + + + Source Files + + + Source Files\Core + + + Source Files\Core + + + Source Files\Core + + + Source Files\Stubs + + + Source Files\Stubs + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Commands + + + Source Files\Core + + + Source Files\Core + + + Source Files\Stubs + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Server + + + Source Files\Common + + + Source Files\Common + + + Source Files\Common + + + Source Files\Common + + + Source Files\Common + + + Source Files\Common + + + Source Files\Common + + + Source Files\Common + + + Source Files\Common + + + Source Files\Common + + + Source Files\Common + + + Source Files\Common + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\DLC + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\GameRules + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\Tutorial + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\UI + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\zlib + + + Source Files\Platform + + + Source Files\Platform + + + Source Files\Platform + + + Source Files\Platform + + + Source Files\Platform + + + Source Files\Platform + + + Source Files\Platform + + + Source Files\Platform + + + Source Files\Server + + + Source Files\Linux + + + Source Files\Linux + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..70a9f8e --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# LCEMP Dedicated Server + +A dedicated server for Legacy Console Edition. This allows you to host a standalone server for LCE. + +## Building + +You need the main LCEMP repository to build the dedicated server, as it depends on the shared Minecraft.World and Minecraft.Client sources. + +### Linux + +Requires CMake 3.10+ and a C++ compiler with C++11 support (GCC or Clang). + +```bash +mkdir build && cd build +cmake ../Minecraft.Server -DCMAKE_BUILD_TYPE=Release +cmake --build . --target MinecraftDedicatedServer +``` + +The binary will be at `build/MinecraftDedicatedServer`. + +### Windows + +Open `Minecraft.Server.vcxproj` in Visual Studio (2012 or later with the v110 toolset, or 2022 with v143) and build. + +## Running + +On Linux, run the binary directly: + +```bash +./MinecraftDedicatedServer +``` + +On Windows, run `MinecraftDedicatedServer.exe` from a command prompt or by double-clicking it. + +On first launch the server generates a default `server.properties` file, which you can edit to configure the server. + +## server.properties + +| Property | Type | Default | Description | +|---|---|---|---| +| `server-port` | integer | `25565` | Port the server listens on. | +| `server-ip` | string | *(empty)* | IP address to bind to. Leave empty to bind on all interfaces. | +| `level-name` | string | `world` | Name of the world. | +| `level-seed` | integer | *(empty)* | World seed. Leave empty for a random seed. | +| `level-size` | string | `large` | World size for newly created worlds. Options: `classic`, `small`, `medium`, `large`. | +| `gamemode` | integer | `0` | Default game mode. `0` = Survival, `1` = Creative. | +| `difficulty` | integer | `2` | Difficulty level. `0` = Peaceful, `1` = Easy, `2` = Normal, `3` = Hard. | +| `max-players` | integer | `8` | Maximum number of players (1-8). | +| `pvp` | boolean | `true` | Whether players can damage each other. | +| `trust-players` | boolean | `true` | Whether to trust player positions sent by the client. | +| `fire-spreads` | boolean | `true` | Whether fire spreads to nearby blocks. | +| `tnt-explodes` | boolean | `true` | Whether TNT explosions are enabled. | +| `structures` | boolean | `true` | Whether structures generate in new worlds. | +| `spawn-animals` | boolean | `true` | Whether animals spawn naturally. | +| `spawn-npcs` | boolean | `true` | Whether villagers spawn naturally. | +| `online-mode` | boolean | `false` | Reserved for future use. | +| `show-gamertags` | boolean | `true` | Whether player name tags are visible in-game. | +| `motd` | string | `A Minecraft LCE Server` | Server name shown in the LAN browser. | +| `white-list` | boolean | `false` | Whether to use a whitelist. Only players listed in `whitelist.txt` can join. | +| `voice-chat` | boolean | `false` | Whether voice chat is enabled. Only works with 8 or fewer max players. | +| `advertise-lan` | boolean | `true` | Whether the server broadcasts itself on the local network so clients can find it automatically. | + +### World sizes + +| Size | Overworld | Nether scale | +|---|---|---| +| `classic` | 864x864 blocks | 3x | +| `small` | 1024x1024 blocks | 3x | +| `medium` | 3072x3072 blocks | 6x | +| `large` | 5120x5120 blocks | 8x | + +The `level-size` property only takes effect when the server creates a new world. Existing saves keep their original size. + +## Commands + +Commands can be typed directly into the server console. Use `/help` to see a paginated list or `/help ` for details on a specific command (these commands are the same as vanilla java minecraft). + +### Player Management + +| Command | Usage | Description | +|---|---|---| +| `op` | `/op ` | Grants operator status to a player. | +| `deop` | `/deop ` | Removes operator status from a player. | +| `kick` | `/kick [reason]` | Kicks a player from the server. | +| `ban` | `/ban ` | Bans a player by name. | +| `ban-ip` | `/ban-ip
` | Bans an IP address, or resolves a player name to their IP and bans it. | +| `pardon` | `/pardon ` | Unbans a player. | +| `pardon-ip` | `/pardon-ip
` | Unbans an IP address. | +| `banlist` | `/banlist [ips\|players]` | Shows the ban list. Defaults to players. | +| `whitelist` | `/whitelist [player]` | Manages the whitelist. | +| `list` | `/list` | Shows online players. | + +### Gameplay + +| Command | Usage | Description | +|---|---|---| +| `gamemode` | `/gamemode [player]` | Changes a player's game mode. Accepts `0`/`s`/`survival`, `1`/`c`/`creative`, `2`/`a`/`adventure`. | +| `defaultgamemode` | `/defaultgamemode ` | Sets the default game mode for new players joining. | +| `give` | `/give [amount] [data]` | Gives an item to a player. | +| `enchant` | `/enchant [level]` | Enchants the item a player is holding. | +| `tp` | `/tp [player] ` or `/tp [player] ` | Teleports a player to another player or to coordinates. | +| `kill` | `/kill ` | Kills a player. | +| `xp` | `/xp [player]` or `/xp L [player]` | Gives experience. Append `L` to give levels instead of points. | + +### World + +| Command | Usage | Description | +|---|---|---| +| `time` | `/time ` | Sets or adds to the world time. Accepts `day`, `night`, or a number. | +| `toggledownfall` | `/toggledownfall` | Toggles rain/snow on or off. | +| `seed` | `/seed` | Displays the world seed. | +| `save-all` | `/save-all` | Forces an immediate save of the world. | +| `save-off` | `/save-off` | Disables automatic saving. | +| `save-on` | `/save-on` | Re-enables automatic saving. | + +### Other + +| Command | Usage | Description | +|---|---|---| +| `say` | `/say ` | Broadcasts a message to all players as `[Server]`. | +| `me` | `/me ` | Broadcasts an action message as `* Server `. | +| `help` | `/help [page or command]` | Shows the command list or usage for a specific command. | +| `stop` | `/stop` | Saves and shuts down the server. | +| `debug` | `/debug ` | Starts or stops a debug profiling session. | diff --git a/Stubs/ServerStubs.cpp b/Stubs/ServerStubs.cpp new file mode 100644 index 0000000..d4d44ca --- /dev/null +++ b/Stubs/ServerStubs.cpp @@ -0,0 +1,196 @@ +#include "stdafx.h" + +#include "../../Minecraft.Client/Minecraft.h" +#include "../../Minecraft.Client/ProgressRenderer.h" +#include "../../Minecraft.Client/GameRenderer.h" +#include "../../Minecraft.Client/Textures.h" +#include "../../Minecraft.Client/Font.h" +#include "../../Minecraft.Client/Gui.h" +#include "../../Minecraft.Client/Options.h" +#include "../../Minecraft.Client/User.h" +#include "../../Minecraft.Client/StatsCounter.h" +#include "../../Minecraft.Client/LevelRenderer.h" +#include "../../Minecraft.Client/AchievementPopup.h" +#include "../../Minecraft.Client/KeyboardMouseInput.h" +#include "../../Minecraft.Client/Windows64/Sentient/SentientManager.h" +#include "../../Minecraft.Client/Windows64/Social/SocialManager.h" +#include "../../Minecraft.Client/StringTable.h" +#include "../../Minecraft.Client/MemoryTracker.h" + +#include "../../Minecraft.Client/Windows64Media/strings.h" +#include +#include "../Core/ServerLogger.h" + +CRITICAL_SECTION ProgressRenderer::s_progress; + +static const wchar_t* GetProgressTitleName(int id) +{ + switch(id) + { + case IDS_PROGRESS_INITIALISING_SERVER: return L"Starting integrated server"; + case IDS_PROGRESS_GENERATING_LEVEL: return L"Generating level"; + case IDS_PROGRESS_GENERATING_SPAWN_AREA: return L"Preparing spawn area"; + case IDS_PROGRESS_LOADING_LEVEL: return L"Loading level"; + case IDS_PROGRESS_LOADING_SPAWN_AREA: return L"Loading spawn area"; + case IDS_PROGRESS_BUILDING_TERRAIN: return L"Building terrain"; + case IDS_PROGRESS_SAVING_LEVEL: return L"Saving level"; + case IDS_PROGRESS_SAVING_CHUNKS: return L"Saving chunks"; + case IDS_PROGRESS_SAVING_PLAYERS: return L"Saving players"; + case IDS_PROGRESS_SAVING_TO_DISC: return L"Saving to disc"; + case IDS_PROGRESS_AUTOSAVING_LEVEL: return L"Autosaving level"; + case IDS_PROGRESS_HOST_SAVING: return L"Saving"; + case IDS_PROGRESS_RESPAWNING: return L"Respawning"; + case IDS_PROGRESS_ENTERING_NETHER: return L"Entering the Nether"; + case IDS_PROGRESS_LEAVING_NETHER: return L"Leaving the Nether"; + case IDS_PROGRESS_ENTERING_END: return L"Entering the End"; + case IDS_PROGRESS_LEAVING_END: return L"Leaving the End"; + default: return NULL; + } +} + +ProgressRenderer::ProgressRenderer(Minecraft *minecraft) : minecraft(minecraft) +{ + lastPercent = 0; + status = 0; + title = 0; + lastTime = 0; + noAbort = false; + m_eType = eProgressStringType_ID; +} + +void ProgressRenderer::progressStart(int title) +{ + _progressStart(title); +} + +void ProgressRenderer::progressStartNoAbort(int string) +{ + noAbort = true; + _progressStart(string); +} + +void ProgressRenderer::_progressStart(int t) +{ + this->title = t; + this->lastPercent = -1; + this->lastTime = 0; + + const wchar_t *name = GetProgressTitleName(t); + if (name) + ServerLog(L"%ls\n", name); +} + +void ProgressRenderer::progressStage(int s) +{ + this->status = s; +} + +void ProgressRenderer::progressStage(wstring &wstrText) +{ + m_wstrText = wstrText; + m_eType = eProgressStringType_String; +} + +void ProgressRenderer::progressStagePercentage(int i) +{ + if (i != lastPercent) + { + lastPercent = i; + if (title == IDS_PROGRESS_GENERATING_SPAWN_AREA || title == IDS_PROGRESS_LOADING_SPAWN_AREA) + { + if (i % 10 == 0 && i > 0) + { + const wchar_t *name = GetProgressTitleName(title); + if (name) + ServerLog(L"Preparing spawn area: %d%%%%\n", i); + } + } + } +} + +int ProgressRenderer::getCurrentPercent() { return lastPercent; } +int ProgressRenderer::getCurrentTitle() { return title; } +int ProgressRenderer::getCurrentStatus() { return status; } +wstring& ProgressRenderer::getProgressString(void) { return m_wstrText; } +ProgressRenderer::eProgressStringType ProgressRenderer::getType() { return m_eType; } +void ProgressRenderer::setType(eProgressStringType eType) { m_eType = eType; } + +bool GameRenderer::anaglyph3d = false; +int GameRenderer::anaglyphPass = 0; + +GameRenderer::GameRenderer(Minecraft *mc) +{ + // should be good enough + this->mc = mc; + renderDistance = 0; + _tick = 0; + hovered = nullptr; + thirdDistance = 4; + thirdDistanceO = 4; + thirdRotation = 0; + thirdRotationO = 0; + thirdTilt = 0; + thirdTiltO = 0; + accumulatedSmoothXO = 0; + accumulatedSmoothYO = 0; + tickSmoothXO = 0; + tickSmoothYO = 0; + lastTickA = 0; + cameraPos = NULL; + fovOffset = 0; + fovOffsetO = 0; + cameraRoll = 0; + cameraRollO = 0; + isInClouds = false; + zoom = 1; + zoom_x = 0; + zoom_y = 0; + rainXa = NULL; + rainZa = NULL; + lastActiveTime = 0; + lastNsTime = 0; + random = NULL; + rainSoundTime = 0; + xMod = 0; + yMod = 0; + lb = NULL; + fr = 0; fg = 0; fb = 0; + fogBrO = 0; fogBr = 0; + cameraFlip = 0; + _updateLightTexture = false; + blr = 0; blrt = 0; blg = 0; + itemInHandRenderer = NULL; + for (int i = 0; i < 4; i++) { fov[i] = 0; oFov[i] = 0; tFov[i] = 0; } +} + +void GameRenderer::DisableUpdateThread() +{ +} + +void GameRenderer::EnableUpdateThread() +{ +} + +#ifdef MULTITHREAD_ENABLE +C4JThread* GameRenderer::m_updateThread = NULL; +C4JThread::EventArray* GameRenderer::m_updateEvents = NULL; +bool GameRenderer::nearThingsToDo = false; +bool GameRenderer::updateRunning = false; +#endif + +vector GameRenderer::m_deleteStackByte; +vector GameRenderer::m_deleteStackSparseLightStorage; +vector GameRenderer::m_deleteStackCompressedTileStorage; +vector GameRenderer::m_deleteStackSparseDataStorage; + +void MemSect(int sect) {} + +void glFlush() {} +void glTexGeni(int, int, int) {} +void glTexGen(int, int, FloatBuffer *) {} +void glReadPixels(int, int, int, int, int, int, ByteBuffer *) {} +void glClearDepth(double) {} +void glCullFace(int) {} +void glDeleteLists(int, int) {} + +void PlayerRenderer_InitNametagColors() {} \ No newline at end of file diff --git a/Stubs/ServerStubs2.cpp b/Stubs/ServerStubs2.cpp new file mode 100644 index 0000000..c62752b --- /dev/null +++ b/Stubs/ServerStubs2.cpp @@ -0,0 +1,779 @@ + +#include "stdafx.h" + +#include "../../Minecraft.World/Dimension.h" +#include "../../Minecraft.Client/Windows64/4JLibs/inc/4J_Input.h" +#include "../../Minecraft.Client/Windows64/4JLibs/inc/4J_Storage.h" +#include "../../Minecraft.Client/TexturePackRepository.h" +#include "../../Minecraft.Client/LevelRenderer.h" +#include "../../Minecraft.Client/Textures.h" +#include "../../Minecraft.Client/Font.h" +#include "../../Minecraft.Client/Gui.h" +#include "../../Minecraft.Client/ScreenSizeCalculator.h" +#include "../../Minecraft.Client/Timer.h" +#include "../../Minecraft.Client/HumanoidModel.h" +#include "../../Minecraft.Client/ParticleEngine.h" +#include "../../Minecraft.Client/Tesselator.h" +#include "../../Minecraft.Client/ItemInHandRenderer.h" +#include "../../Minecraft.Client/EntityRenderDispatcher.h" +#include "../../Minecraft.Client/TileEntityRenderDispatcher.h" +#include "../../Minecraft.Client/GuiParticles.h" +#include "../../Minecraft.Client/TitleScreen.h" +#include "../../Minecraft.Client/AchievementPopup.h" +#include "../../Minecraft.Client/Input.h" +#include "../../Minecraft.Client/TextureManager.h" +#include "../../Minecraft.Client/TileRenderer.h" +#include "../../Minecraft.Client/Chunk.h" +class EnderDragon; +#include "../../Minecraft.Client/EnderDragonRenderer.h" +#include "../../Minecraft.Client/GameRenderer.h" +#include "../../Minecraft.Client/GameMode.h" +#include "../../Minecraft.Client/KeyMapping.h" +#include "../../Minecraft.Client/ArchiveFile.h" +#include "../../Minecraft.Client/BufferedImage.h" +#include "../../Minecraft.Client/Lighting.h" +#include "../../Minecraft.Client/Model.h" +#include "../../Minecraft.Client/OffsettedRenderList.h" +#include "../../Minecraft.Client/Common/Audio/SoundEngine.h" +#include "../../Minecraft.Client/Common/UI/UIController.h" +#include "../../Minecraft.Client/Windows64/Windows64_UIController.h" +#include "../../Minecraft.Client/Common/UI/UIControl_Progress.h" +#include "../../Minecraft.Client/Common/UI/UIControl_Label.h" +#include "../../Minecraft.Client/Common/UI/UIControl_Button.h" +#include "../../Minecraft.Client/Common/UI/UIScene_FullscreenProgress.h" +#include "../../Minecraft.Client/Common/UI/IUIScene_PauseMenu.h" +#include "../../Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h" +#include "../../Minecraft.Client/Common/UI/IUIScene_TradingMenu.h" +#include "../../Minecraft.Client/Common/UI/IUIScene_CraftingMenu.h" +#include "../../Minecraft.Client/Common/UI/UIFontData.h" +#include "../../Minecraft.Client/Particle.h" +#include "../../Minecraft.Client/CritParticle.h" +#include "../../Minecraft.Client/TakeAnimationParticle.h" +#include "../../Minecraft.Client/Screen.h" +#include "../../Minecraft.Client/GuiComponent.h" + +C_4JInput InputManager; +wchar_t g_Win64UsernameW[17] = L"Server"; +char g_Win64Username[17] = "Server"; +HWND g_hWnd = NULL; + +bool TileRenderer::fancy = false; +EntityRenderDispatcher *EntityRenderDispatcher::instance = NULL; +TileEntityRenderDispatcher *TileEntityRenderDispatcher::instance = NULL; +int Chunk::updates = 0; +LevelRenderer *Chunk::levelRenderer = NULL; +shared_ptr EnderDragonRenderer::bossInstance; +int EnderDragonRenderer::currentModel = 0; + +double EntityRenderDispatcher::xOff = 0; +double EntityRenderDispatcher::yOff = 0; +double EntityRenderDispatcher::zOff = 0; + +double TileEntityRenderDispatcher::xOff = 0; +double TileEntityRenderDispatcher::yOff = 0; +double TileEntityRenderDispatcher::zOff = 0; + +bool Textures::MIPMAP = false; +C4JRender::eTextureFormat Textures::TEXTURE_FORMAT = C4JRender::TEXTURE_FORMAT_RxGyBzAw; + +float Gui::currentGuiBlendFactor = 1.0f; +float Gui::currentGuiScaleFactor = 1.0f; + +const int LevelRenderer::MAX_LEVEL_RENDER_SIZE[3] = { 356, 42, 18 }; +const int LevelRenderer::DIMENSION_OFFSETS[3] = { 0, (356 * 356 * 16), (356 * 356 * 16) + (42 * 42 * 16) }; + +int ItemInHandRenderer::list = 0; +int ItemInHandRenderer::listGlint = 0; + +TexturePack *TexturePackRepository::DEFAULT_TEXTURE_PACK = NULL; + +FloatBuffer *Lighting::lb = NULL; + +void C_4JInput::Tick() {} +unsigned int C_4JInput::GetGameJoypadMaps(unsigned char, unsigned char) { return 0; } +unsigned char C_4JInput::GetJoypadMapVal(int) { return 0; } +void C_4JInput::SetJoypadMapVal(int, unsigned char) {} +unsigned int C_4JInput::GetValue(int, unsigned char, bool) { return 0; } +bool C_4JInput::ButtonPressed(int, unsigned char) { return false; } +bool C_4JInput::ButtonReleased(int, unsigned char) { return false; } +bool C_4JInput::ButtonDown(int, unsigned char) { return false; } +bool C_4JInput::IsPadConnected(int) { return false; } +float C_4JInput::GetJoypadStick_LX(int, bool) { return 0.0f; } +float C_4JInput::GetJoypadStick_LY(int, bool) { return 0.0f; } +float C_4JInput::GetJoypadStick_RX(int, bool) { return 0.0f; } +float C_4JInput::GetJoypadStick_RY(int, bool) { return 0.0f; } +float C_4JInput::GetIdleSeconds(int) { return 0.0f; } +void C_4JInput::SetJoypadStickAxisMap(int, unsigned int, unsigned int) {} +void C_4JInput::SetJoypadStickTriggerMap(int, unsigned int, unsigned int) {} +void C_4JInput::SetDebugSequence(const char *, int (*)(LPVOID), LPVOID) {} +void C_4JInput::SetMenuDisplayed(int, bool) {} + +void glTranslatef(float, float, float) {} +void glRotatef(float, float, float, float) {} +void glScalef(float, float, float) {} +void glPushMatrix() {} +void glPopMatrix() {} +void glEnable(int) {} +void glDisable(int) {} +void glDepthFunc(int) {} +void glMatrixMode(int) {} +void glLoadIdentity() {} +void glBindTexture(int, int) {} +void glShadeModel(int) {} +void glLineWidth(float) {} +void glClear(int) {} +void glViewport(int, int, int, int) {} +void glAlphaFunc(int, float) {} +void glOrtho(float, float, float, float, float, float) {} +void glClearColor(float, float, float, float) {} +void glBlendFunc(int, int) {} +void glDeleteTextures(int) {} +void glDeleteTextures(IntBuffer *) {} +void glDepthMask(bool) {} +void glColor4f(float, float, float, float) {} +void glColor3f(float, float, float) {} +void glNormal3f(float, float, float) {} +void glNewList(int, int) {} +void glEndList(int) {} +void glCallList(int) {} +void glCallLists(IntBuffer *) {} +void glDrawArrays(int, int, int) {} +void glTexCoordPointer(int, int, int, int) {} +void glTexCoordPointer(int, int, FloatBuffer *) {} +void glNormalPointer(int, int, int) {} +void glNormalPointer(int, ByteBuffer *) {} +void glEnableClientState(int) {} +void glDisableClientState(int) {} +void glColorPointer(int, bool, int, ByteBuffer *) {} +void glColorPointer(int, int, int, int) {} +void glVertexPointer(int, int, int, int) {} +void glVertexPointer(int, int, FloatBuffer *) {} +void glMultMatrixf(float *) {} +void glTexParameteri(int, int, int) {} +int glGenTextures() { return 0; } +int glGenLists(int) { return 0; } +void glTexImage2D(int, int, int, int, int, int, int, int, ByteBuffer *) {} +void glColorMask(bool, bool, bool, bool) {} +void glGenQueriesARB(IntBuffer *) {} +void glBeginQueryARB(int, int) {} +void glEndQueryARB(int) {} +void glGetQueryObjectuARB(int, int, IntBuffer *) {} +void glPolygonOffset(float, float) {} +void glScaled(double, double, double) {} +void gluPerspective(float, float, float, float) {} +void glFogi(int, int) {} +void glFogf(int, float) {} +void glFog(int, FloatBuffer *) {} +void glColorMaterial(int, int) {} +void glMultiTexCoord2f(int, float, float) {} +void glClientActiveTexture(int) {} +void glActiveTexture(int) {} + +void Display::update() {} +void Display::swapBuffers() {} + +Textures::Textures(TexturePackRepository *, Options *) {} +int Textures::loadTexture(int) { return 0; } +void Textures::tick(bool, bool) {} +void Textures::reloadAll() {} +void Textures::stitch() {} +void Textures::bind(int) {} +int Textures::getTexture(BufferedImage *, C4JRender::eTextureFormat, bool) { return 0; } +void Textures::replaceTextureDirect(intArray, int, int, int) {} +void Textures::releaseTexture(int) {} +intArray Textures::loadTexturePixels(TEXTURE_NAME, const wstring &) { return intArray(); } +void Textures::bindTexture(const wstring &) {} +void Textures::bindTexture(int) {} +void Textures::clearLastBoundId() {} +void Textures::loadTexture(BufferedImage *, int) {} +void Textures::loadTexture(BufferedImage *, int, bool, bool) {} +void Textures::replaceTexture(intArray, int, int, int) {} +void Textures::replaceTextureDirect(shortArray, int, int, int) {} +int Textures::loadHttpTexture(const wstring &, const wstring &) { return 0; } +int Textures::loadHttpTexture(const wstring &, int) { return 0; } +bool Textures::hasHttpTexture(const wstring &) { return false; } +HttpTexture *Textures::addHttpTexture(const wstring &, HttpTextureProcessor *) { return NULL; } +void Textures::removeHttpTexture(const wstring &) {} +int Textures::loadMemTexture(const wstring &, const wstring &) { return 0; } +int Textures::loadMemTexture(const wstring &, int) { return 0; } +MemTexture *Textures::addMemTexture(const wstring &, MemTextureProcessor *) { return NULL; } +void Textures::removeMemTexture(const wstring &) {} +Icon *Textures::getMissingIcon(int) { return NULL; } +BufferedImage *Textures::readImage(TEXTURE_NAME, const wstring &) { return NULL; } +bool Textures::IsTUImage(TEXTURE_NAME, const wstring &) { return false; } +bool Textures::IsOriginalImage(TEXTURE_NAME, const wstring &) { return false; } + +Font::Font(Options *, const wstring &, Textures *, bool, TEXTURE_NAME, int, int, int, int, unsigned short *) {} +#ifndef _XBOX +Font::~Font() {} +#endif +void Font::drawShadow(const wstring &, int, int, int) {} +void Font::drawShadowWordWrap(const wstring &, int, int, int, int, int) {} +void Font::draw(const wstring &, int, int, int) {} +void Font::renderFakeCB(IntBuffer *) {} + +Gui::Gui(Minecraft *) {} +void Gui::render(float, bool, int, int) {} +void Gui::tick() {} +void Gui::clearMessages(int) {} +void Gui::addMessage(const wstring &, int, bool) {} +void Gui::setNowPlaying(const wstring &) {} +void Gui::displayClientMessage(int, int) {} +float Gui::getOpacity(int, DWORD) { return 0.0f; } + +ScreenSizeCalculator::ScreenSizeCalculator(Options *, int, int, int) {} +int ScreenSizeCalculator::getWidth() { return 800; } +int ScreenSizeCalculator::getHeight() { return 600; } + +Timer::Timer(float) {} +void Timer::advanceTime() {} +void Timer::advanceTimeQuickly() {} +void Timer::skipTime() {} + +LevelRenderer::LevelRenderer(Minecraft *, Textures *) {} +void LevelRenderer::setLevel(int, MultiPlayerLevel *) {} +void LevelRenderer::allChanged() {} +void LevelRenderer::allChanged(int) {} +void LevelRenderer::tick() {} +void LevelRenderer::clear() {} +int LevelRenderer::render(shared_ptr, int, double, bool) { return 0; } +void LevelRenderer::renderEntities(Vec3 *, Culler *, float) {} +void LevelRenderer::renderSky(float) {} +void LevelRenderer::renderClouds(float) {} +bool LevelRenderer::updateDirtyChunks() { return false; } +void LevelRenderer::renderHit(shared_ptr, HitResult *, int, shared_ptr, float) {} +void LevelRenderer::renderDestroyAnimation(Tesselator *, shared_ptr, float) {} +void LevelRenderer::renderHitOutline(shared_ptr, HitResult *, int, shared_ptr, float) {} +wstring LevelRenderer::gatherStats1() { return L""; } +wstring LevelRenderer::gatherStats2() { return L""; } +void LevelRenderer::setDirty(int, int, int, int, int, int, Level *) {} +void LevelRenderer::tileChanged(int, int, int) {} +void LevelRenderer::tileLightChanged(int, int, int) {} +void LevelRenderer::setTilesDirty(int, int, int, int, int, int, Level *) {} +void LevelRenderer::cull(Culler *, float) {} +void LevelRenderer::playStreamingMusic(const wstring &, int, int, int) {} +void LevelRenderer::playSound(int, double, double, double, float, float, float) {} +void LevelRenderer::playSound(shared_ptr, int, double, double, double, float, float, float) {} +void LevelRenderer::addParticle(ePARTICLE_TYPE, double, double, double, double, double, double) {} +shared_ptr LevelRenderer::addParticleInternal(ePARTICLE_TYPE, double, double, double, double, double, double) { return nullptr; } +void LevelRenderer::entityAdded(shared_ptr) {} +void LevelRenderer::entityRemoved(shared_ptr) {} +void LevelRenderer::skyColorChanged() {} +void LevelRenderer::levelEvent(shared_ptr, int, int, int, int, int) {} +void LevelRenderer::destroyTileProgress(int, int, int, int, int) {} +void LevelRenderer::registerTextures(IconRegister *) {} +void LevelRenderer::render(AABB *) {} +int LevelRenderer::activePlayers() { return 0; } +void LevelRenderer::renderSameAsLast(int, double) {} +void LevelRenderer::renderHaloRing(float) {} +bool LevelRenderer::isInCloud(double, double, double, float) { return false; } +void LevelRenderer::renderAdvancedClouds(float) {} +void LevelRenderer::AddDLCSkinsToMemTextures() {} +void LevelRenderer::fullyFlagRenderableTileEntitiesToBeRemoved() {} + +int LevelRenderer::getDimensionIndexFromId(int id) { return (3 - id) % 3; } + +int LevelRenderer::getGlobalIndexForChunk(int x, int y, int z, Level *level) { + return getGlobalIndexForChunk(x, y, z, level->dimension->id); +} +int LevelRenderer::getGlobalIndexForChunk(int x, int y, int z, int dimensionId) { + int dimIdx = getDimensionIndexFromId(dimensionId); + int xx = (x / CHUNK_XZSIZE) + (MAX_LEVEL_RENDER_SIZE[dimIdx] / 2); + int yy = y / CHUNK_SIZE; + int zz = (z / CHUNK_XZSIZE) + (MAX_LEVEL_RENDER_SIZE[dimIdx] / 2); + if ((xx < 0) || (xx >= MAX_LEVEL_RENDER_SIZE[dimIdx])) return -1; + if ((zz < 0) || (zz >= MAX_LEVEL_RENDER_SIZE[dimIdx])) return -1; + if ((yy < 0) || (yy >= CHUNK_Y_COUNT)) return -1; + int offset = DIMENSION_OFFSETS[dimIdx]; + offset += (zz * MAX_LEVEL_RENDER_SIZE[dimIdx] + xx) * CHUNK_Y_COUNT; + offset += yy; + return offset; +} +bool LevelRenderer::isGlobalIndexInSameDimension(int idx, Level *level) { + int dim = getDimensionIndexFromId(level->dimension->id); + int idxDim = 0; + if (idx >= DIMENSION_OFFSETS[2]) idxDim = 2; + else if (idx >= DIMENSION_OFFSETS[1]) idxDim = 1; + return (dim == idxDim); +} +int LevelRenderer::getGlobalChunkCount() { + return (MAX_LEVEL_RENDER_SIZE[0] * MAX_LEVEL_RENDER_SIZE[0] * CHUNK_Y_COUNT) + + (MAX_LEVEL_RENDER_SIZE[1] * MAX_LEVEL_RENDER_SIZE[1] * CHUNK_Y_COUNT) + + (MAX_LEVEL_RENDER_SIZE[2] * MAX_LEVEL_RENDER_SIZE[2] * CHUNK_Y_COUNT); +} +int LevelRenderer::getGlobalChunkCountForOverworld() { + return (MAX_LEVEL_RENDER_SIZE[0] * MAX_LEVEL_RENDER_SIZE[0] * CHUNK_Y_COUNT); +} +bool LevelRenderer::getGlobalChunkFlag(int, int, int, Level *, unsigned char, unsigned char) { return false; } +void LevelRenderer::setGlobalChunkFlag(int, int, int, Level *, unsigned char, unsigned char) {} +void LevelRenderer::setGlobalChunkFlag(int, unsigned char, unsigned char) {} +void LevelRenderer::clearGlobalChunkFlag(int, int, int, Level *, unsigned char, unsigned char) {} + +LevelRenderer::DestroyedTileManager::DestroyedTileManager() {} +LevelRenderer::DestroyedTileManager::~DestroyedTileManager() {} +void LevelRenderer::DestroyedTileManager::destroyingTileAt(Level *, int, int, int) {} +void LevelRenderer::DestroyedTileManager::updatedChunkAt(Level *, int, int, int, int) {} +void LevelRenderer::DestroyedTileManager::addAABBs(Level *, AABB *, AABBList *) {} +void LevelRenderer::DestroyedTileManager::tick() {} + +ParticleEngine::ParticleEngine(Level *, Textures *) {} +ParticleEngine::~ParticleEngine() {} +void ParticleEngine::add(shared_ptr) {} +void ParticleEngine::tick() {} +void ParticleEngine::render(shared_ptr, float) {} +void ParticleEngine::renderLit(shared_ptr, float) {} +void ParticleEngine::setLevel(Level *) {} +void ParticleEngine::destroy(int, int, int, int, int) {} +void ParticleEngine::crack(int, int, int, int) {} +wstring ParticleEngine::countParticles() { return L"0"; } + +void GameRenderer::tick(bool) {} +void GameRenderer::pick(float) {} +void GameRenderer::render(float, bool) {} +void GameRenderer::AddForDelete(SparseLightStorage *) {} +void GameRenderer::AddForDelete(CompressedTileStorage *) {} +void GameRenderer::AddForDelete(SparseDataStorage *) {} +void GameRenderer::FinishedReassigning() {} + +Tesselator *Tesselator::getInstance() +{ + static char buffer[4096] = {}; + return reinterpret_cast(buffer); +} +void Tesselator::CreateNewThreadStorage(int) {} +void Tesselator::end() {} +void Tesselator::begin() {} +void Tesselator::begin(int) {} +void Tesselator::useCompactVertices(bool) {} +bool Tesselator::getCompactVertices() { return false; } +void Tesselator::useProjectedTexture(bool) {} +void Tesselator::tex(float, float) {} +void Tesselator::tex2(int) {} +void Tesselator::color(float, float, float) {} +void Tesselator::color(float, float, float, float) {} +void Tesselator::color(int, int, int) {} +void Tesselator::color(int, int, int, int) {} +void Tesselator::color(byte, byte, byte) {} +void Tesselator::vertexUV(float, float, float, float, float) {} +void Tesselator::vertex(float, float, float) {} +void Tesselator::color(int) {} +void Tesselator::color(int, int) {} +void Tesselator::noColor() {} +void Tesselator::normal(float, float, float) {} +void Tesselator::offset(float, float, float) {} +void Tesselator::addOffset(float, float, float) {} +bool Tesselator::setMipmapEnable(bool) { return false; } +bool Tesselator::hasMaxVertices() { return false; } + +HumanoidModel::HumanoidModel() {} +HumanoidModel::HumanoidModel(float) {} +HumanoidModel::HumanoidModel(float, float, int, int) {} + +ItemInHandRenderer::ItemInHandRenderer(Minecraft *, bool) {} +void ItemInHandRenderer::itemPlaced() {} +void ItemInHandRenderer::itemUsed() {} +void ItemInHandRenderer::render(float) {} +void ItemInHandRenderer::tick() {} + +void EntityRenderDispatcher::staticCtor() {} +EntityRenderer *EntityRenderDispatcher::getRenderer(eINSTANCEOF) { return NULL; } +EntityRenderer *EntityRenderDispatcher::getRenderer(shared_ptr) { return NULL; } +void EntityRenderDispatcher::prepare(Level *, Textures *, Font *, shared_ptr, Options *, float) {} +void EntityRenderDispatcher::render(shared_ptr, float) {} +void EntityRenderDispatcher::render(shared_ptr, double, double, double, float, float, bool, bool) {} +void EntityRenderDispatcher::setLevel(Level *) {} +double EntityRenderDispatcher::distanceToSqr(double, double, double) { return 0; } +Font *EntityRenderDispatcher::getFont() { return NULL; } +void EntityRenderDispatcher::registerTerrainTextures(IconRegister *) {} + +void TileEntityRenderDispatcher::staticCtor() {} +TileEntityRenderer *TileEntityRenderDispatcher::getRenderer(eINSTANCEOF) { return NULL; } +bool TileEntityRenderDispatcher::hasRenderer(shared_ptr) { return false; } +TileEntityRenderer *TileEntityRenderDispatcher::getRenderer(shared_ptr) { return NULL; } +void TileEntityRenderDispatcher::prepare(Level *, Textures *, Font *, shared_ptr, float) {} +void TileEntityRenderDispatcher::render(shared_ptr, float, bool) {} +void TileEntityRenderDispatcher::render(shared_ptr, double, double, double, float, bool, float, bool) {} +void TileEntityRenderDispatcher::setLevel(Level *) {} +double TileEntityRenderDispatcher::distanceToSqr(double, double, double) { return 0; } +Font *TileEntityRenderDispatcher::getFont() { return NULL; } + +GuiParticles::GuiParticles(Minecraft *) {} +void GuiParticles::tick() {} +void GuiParticles::render(float) {} + +TitleScreen::TitleScreen() {} + +AchievementPopup::AchievementPopup(Minecraft *) {} +void AchievementPopup::render() {} + +Input::Input() : xa(0), ya(0), wasJumping(false), jumping(false), sneaking(false), sprinting(false) {} +void Input::tick(LocalPlayer *) {} + +void TextureManager::createInstance() {} +TextureManager *TextureManager::getInstance() { return NULL; } + +void Lighting::turnOff() {} +void Lighting::turnOn() {} +void Lighting::turnOnGui() {} + +TexturePackRepository::TexturePackRepository(File, Minecraft *) {} +void TexturePackRepository::addDebugPacks() {} +TexturePack *TexturePackRepository::getSelected() { return NULL; } +bool TexturePackRepository::selectTexturePackById(DWORD) { return false; } +TexturePack *TexturePackRepository::addTexturePackFromDLC(DLCPack *, DWORD) { return NULL; } +void TexturePackRepository::updateUI() {} +bool TexturePackRepository::needsUIUpdate() { return false; } +unsigned int TexturePackRepository::getTexturePackCount() { return 0; } +TexturePack *TexturePackRepository::getTexturePackByIndex(unsigned int) { return NULL; } +unsigned int TexturePackRepository::getTexturePackIndex(unsigned int) { return 0; } +TexturePack *TexturePackRepository::getTexturePackById(DWORD) { return NULL; } +void TexturePackRepository::clearInvalidTexturePacks() {} +void TexturePackRepository::updateList() {} +vector *TexturePackRepository::getAll() { return NULL; } +bool TexturePackRepository::selectSkin(TexturePack *) { return false; } + +ArchiveFile::ArchiveFile(File) {} +ArchiveFile::~ArchiveFile() {} +bool ArchiveFile::hasFile(const wstring &) { return false; } +int ArchiveFile::getFileSize(const wstring &) { return 0; } +byteArray ArchiveFile::getFile(const wstring &) { return byteArray(); } +vector *ArchiveFile::getFileList() { return NULL; } + +KeyMapping::KeyMapping(const wstring &, int) {} + +GameMode::GameMode(Minecraft *mc) : minecraft(mc), instaBuild(false) {} +void GameMode::initLevel(Level *) {} +bool GameMode::destroyBlock(int, int, int, int) { return false; } +void GameMode::render(float) {} +void GameMode::initPlayer(shared_ptr) {} +void GameMode::tick() {} +void GameMode::adjustPlayer(shared_ptr) {} +bool GameMode::useItem(shared_ptr, Level *, shared_ptr, bool) { return false; } +shared_ptr GameMode::createPlayer(Level *) { return nullptr; } +bool GameMode::interact(shared_ptr, shared_ptr) { return false; } +void GameMode::attack(shared_ptr, shared_ptr) {} +shared_ptr GameMode::handleInventoryMouseClick(int, int, int, bool, shared_ptr) { return nullptr; } +void GameMode::handleCloseInventory(int, shared_ptr) {} +void GameMode::handleInventoryButtonClick(int, int) {} +bool GameMode::isCutScene() { return false; } +void GameMode::releaseUsingItem(shared_ptr) {} +bool GameMode::hasExperience() { return false; } +bool GameMode::hasMissTime() { return false; } +bool GameMode::hasInfiniteItems() { return false; } +bool GameMode::hasFarPickRange() { return false; } +void GameMode::handleCreativeModeItemAdd(shared_ptr, int) {} +void GameMode::handleCreativeModeItemDrop(shared_ptr) {} +bool GameMode::handleCraftItem(int, shared_ptr) { return false; } +void GameMode::handleDebugOptions(unsigned int, shared_ptr) {} + +BufferedImage::BufferedImage(int, int, int) { memset(data, 0, sizeof(data)); width = 0; height = 0; } +BufferedImage::BufferedImage(const wstring &, bool, bool, const wstring &) { memset(data, 0, sizeof(data)); width = 0; height = 0; } +BufferedImage::BufferedImage(DLCPack *, const wstring &, bool) { memset(data, 0, sizeof(data)); width = 0; height = 0; } +BufferedImage::BufferedImage(BYTE *, DWORD) { memset(data, 0, sizeof(data)); width = 0; height = 0; } +BufferedImage::~BufferedImage() {} +int BufferedImage::getWidth() { return width; } +int BufferedImage::getHeight() { return height; } +void BufferedImage::getRGB(int, int, int, int, intArray, int, int, int) {} +int *BufferedImage::getData() { return NULL; } +int *BufferedImage::getData(int) { return NULL; } +Graphics *BufferedImage::getGraphics() { return NULL; } +int BufferedImage::getTransparency() { return 0; } +BufferedImage *BufferedImage::getSubimage(int, int, int, int) { return NULL; } +void BufferedImage::preMultiplyAlpha() {} + +void Windows64_UpdateGamma(unsigned short) {} + +__int64 UIController::iggyAllocCount = 0; +CRITICAL_SECTION UIController::ms_reloadSkinCS; +bool UIController::ms_bReloadSkinCSInitialised = false; +DWORD UIController::m_dwTrialTimerLimitSecs = 0; + +UIController::UIController() +{ + m_uiDebugConsole = NULL; + m_uiDebugMarketingGuide = NULL; + memset(&m_navigationLock, 0, sizeof(m_navigationLock)); + memset(m_actionRepeatTimer, 0, sizeof(m_actionRepeatTimer)); + m_fScreenWidth = 0; + m_fScreenHeight = 0; + m_bScreenWidthSetup = false; + m_tileOriginX = 0; + m_tileOriginY = 0; + m_mcBitmapFont = NULL; + m_mcTTFFont = NULL; + m_moj7 = NULL; + m_moj11 = NULL; + gdraw_funcs = NULL; + iggy_explorer = NULL; + iggy_perfmon = NULL; + m_iggyPerfmonEnabled = false; + memset(m_bMenuDisplayed, 0, sizeof(m_bMenuDisplayed)); + memset(m_bMenuToBeClosed, 0, sizeof(m_bMenuToBeClosed)); + memset(m_iCountDown, 0, sizeof(m_iCountDown)); + memset(m_bCloseAllScenes, 0, sizeof(m_bCloseAllScenes)); + m_iPressStartQuadrantsMask = 0; + m_bCustomRenderPosition = false; + m_winUserIndex = 0; + m_bSystemUIShowing = false; + m_reloadSkinThread = NULL; + m_navigateToHomeOnReload = false; + m_accumulatedTicks = 0; + memset(&m_customRenderingClearRect, 0, sizeof(m_customRenderingClearRect)); + memset(&m_registeredCallbackScenesCS, 0, sizeof(m_registeredCallbackScenesCS)); + for (int i = 0; i < eUIGroup_COUNT; i++) m_groups[i] = NULL; + memset(&m_Allocatorlock, 0, sizeof(m_Allocatorlock)); + m_defaultBuffer = NULL; + m_tempBuffer = NULL; +} + +void UIController::tick() {} +void UIController::StartReloadSkinThread() {} +bool UIController::IsReloadingSkin() { return false; } +bool UIController::IsExpectingOrReloadingSkin() { return false; } +void UIController::CleanUpSkinReload() {} +bool UIController::NavigateToScene(int, EUIScene, void *, EUILayer, EUIGroup) { return false; } +bool UIController::NavigateBack(int, bool, EUIScene, EUILayer) { return false; } +void UIController::NavigateToHomeMenu() {} +UIScene *UIController::GetTopScene(int, EUILayer, EUIGroup) { return NULL; } +void UIController::CloseUIScenes(int, bool) {} +void UIController::CloseAllPlayersScenes() {} +bool UIController::IsPauseMenuDisplayed(int) { return false; } +bool UIController::IsContainerMenuDisplayed(int) { return false; } +bool UIController::IsIgnorePlayerJoinMenuDisplayed(int) { return false; } +bool UIController::IsIgnoreAutosaveMenuDisplayed(int) { return false; } +void UIController::SetIgnoreAutosaveMenuDisplayed(int, bool) {} +bool UIController::IsSceneInStack(int, EUIScene) { return false; } +void UIController::CheckMenuDisplayed() {} +void UIController::AnimateKeyPress(int, int, bool, bool, bool) {} +void UIController::OverrideSFX(int, int, bool) {} +void UIController::SetTooltipText(unsigned int, unsigned int, int) {} +void UIController::SetEnableTooltips(unsigned int, BOOL) {} +void UIController::ShowTooltip(unsigned int, unsigned int, bool) {} +void UIController::SetTooltips(unsigned int, int, int, int, int, int, int, int, int, int, bool) {} +void UIController::EnableTooltip(unsigned int, unsigned int, bool) {} +void UIController::RefreshTooltips(unsigned int) {} +void UIController::PlayUISFX(ESoundEffect) {} +void UIController::DisplayGamertag(unsigned int, bool) {} +void UIController::SetSelectedItem(unsigned int, const wstring &) {} +void UIController::UpdateSelectedItemPos(unsigned int) {} +void UIController::HandleDLCMountingComplete() {} +void UIController::HandleDLCInstalled(int) {} +void UIController::HandleTMSDLCFileRetrieved(int) {} +void UIController::HandleTMSBanFileRetrieved(int) {} +void UIController::HandleInventoryUpdated(int) {} +void UIController::HandleGameTick() {} +void UIController::SetTutorial(int, Tutorial *) {} +void UIController::SetTutorialDescription(int, TutorialPopupInfo *) {} +void UIController::RemoveInteractSceneReference(int, UIScene *) {} +void UIController::SetTutorialVisible(int, bool) {} +bool UIController::IsTutorialVisible(int) { return false; } +void UIController::UpdatePlayerBasePositions() {} +void UIController::SetEmptyQuadrantLogo(int) {} +void UIController::HideAllGameUIElements() {} +void UIController::ShowOtherPlayersBaseScene(unsigned int, bool) {} +void UIController::ShowTrialTimer(bool) {} +void UIController::SetTrialTimerLimitSecs(unsigned int) {} +void UIController::UpdateTrialTimer(unsigned int) {} +void UIController::ReduceTrialTimerValue() {} +void UIController::ShowAutosaveCountdownTimer(bool) {} +void UIController::UpdateAutosaveCountdownTimer(unsigned int) {} +void UIController::ShowSavingMessage(unsigned int, C4JStorage::ESavingMessage) {} +void UIController::ShowPlayerDisplayname(bool) {} +bool UIController::PressStartPlaying(unsigned int) { return false; } +void UIController::ShowPressStart(unsigned int) {} +void UIController::HidePressStart() {} +void UIController::ClearPressStart() {} +void UIController::SetWinUserIndex(unsigned int) {} +unsigned int UIController::GetWinUserIndex() { return 0; } +bool UIController::GetMenuDisplayed(int) { return false; } +void UIController::SetMenuDisplayed(int, bool) {} +void UIController::SetupFont() {} +void UIController::ReloadSkin() {} +byteArray UIController::getMovieData(const wstring &) { return byteArray(); } +void UIController::getRenderDimensions(C4JRender::eViewportType, S32 &, S32 &) {} +void UIController::setupRenderPosition(C4JRender::eViewportType) {} +void UIController::setupRenderPosition(S32, S32) {} +void UIController::SetSysUIShowing(bool) {} +void UIController::SetSystemUIShowing(LPVOID, bool) {} +void UIController::setupCustomDrawGameState() {} +void UIController::endCustomDrawGameState() {} +void UIController::setupCustomDrawMatrices(UIScene *, CustomDrawData *) {} +void UIController::setupCustomDrawGameStateAndMatrices(UIScene *, CustomDrawData *) {} +void UIController::endCustomDrawMatrices() {} +void UIController::endCustomDrawGameStateAndMatrices() {} +void UIController::registerSubstitutionTexture(const wstring &, PBYTE, DWORD) {} +void UIController::unregisterSubstitutionTexture(const wstring &, bool) {} +size_t UIController::RegisterForCallbackId(UIScene *) { return 0; } +void UIController::UnregisterCallbackId(size_t) {} +UIScene *UIController::GetSceneFromCallbackId(size_t) { return NULL; } +void UIController::EnterCallbackIdCriticalSection() {} +void UIController::LeaveCallbackIdCriticalSection() {} +#ifndef __PS3__ +C4JStorage::EMessageResult UIController::RequestMessageBox(UINT, UINT, UINT *, UINT, DWORD, int (*)(LPVOID, int, const C4JStorage::EMessageResult), LPVOID, C4JStringTable *, WCHAR *, DWORD, bool) { return (C4JStorage::EMessageResult)0; } +#endif +C4JStorage::EMessageResult UIController::RequestUGCMessageBox(UINT, UINT, int, int (*)(LPVOID, int, const C4JStorage::EMessageResult), LPVOID) { return (C4JStorage::EMessageResult)0; } +C4JStorage::EMessageResult UIController::RequestContentRestrictedMessageBox(UINT, UINT, int, int (*)(LPVOID, int, const C4JStorage::EMessageResult), LPVOID) { return (C4JStorage::EMessageResult)0; } +void UIController::ShowUIDebugConsole(bool) {} +void UIController::ShowUIDebugMarketingGuide(bool) {} +void UIController::logDebugString(const string &) {} +UIScene *UIController::FindScene(EUIScene) { return NULL; } +void UIController::setFontCachingCalculationBuffer(int) {} + +void UIController::preInit(S32, S32) {} +void UIController::postInit() {} +void UIController::renderScenes() {} + +void ConsoleUIController::init(ID3D11Device *, ID3D11DeviceContext *, ID3D11RenderTargetView *, ID3D11DepthStencilView *, S32, S32) {} +void ConsoleUIController::render() {} +void ConsoleUIController::beginIggyCustomDraw4J(IggyCustomDrawCallbackRegion *, CustomDrawData *) {} +CustomDrawData *ConsoleUIController::setupCustomDraw(UIScene *, IggyCustomDrawCallbackRegion *) { return NULL; } +CustomDrawData *ConsoleUIController::calculateCustomDraw(IggyCustomDrawCallbackRegion *) { return NULL; } +void ConsoleUIController::endCustomDraw(IggyCustomDrawCallbackRegion *) {} +void ConsoleUIController::setTileOrigin(S32, S32) {} +GDrawTexture *ConsoleUIController::getSubstitutionTexture(int) { return NULL; } +void ConsoleUIController::destroySubstitutionTexture(void *, GDrawTexture *) {} +void ConsoleUIController::shutdown() {} + +ConsoleUIController ui; + +TileRenderer::TileRenderer() : setColor(true) {} +TileRenderer::TileRenderer(LevelSource *) : setColor(true) {} +TileRenderer::~TileRenderer() {} + +Chunk::Chunk() {} +Chunk::~Chunk() {} + +GuiComponent::GuiComponent() : blitOffset(0) {} +void GuiComponent::hLine(int, int, int, int) {} +void GuiComponent::vLine(int, int, int, int) {} +void GuiComponent::fill(int, int, int, int, int) {} +void GuiComponent::fillGradient(int, int, int, int, int, int) {} +void GuiComponent::drawCenteredString(Font *, const wstring &, int, int, int) {} +void GuiComponent::drawString(Font *, const wstring &, int, int, int) {} +void GuiComponent::blit(int, int, int, int, int, int) {} + +Screen::Screen() {} +void Screen::render(int, int, float) {} +void Screen::keyPressed(wchar_t, int) {} +void Screen::mouseClicked(int, int, int) {} +void Screen::mouseReleased(int, int, int) {} +void Screen::buttonClicked(Button *) {} +void Screen::init(Minecraft *, int, int) {} +void Screen::setSize(int, int) {} +void Screen::init() {} +void Screen::updateEvents() {} +void Screen::mouseEvent() {} +void Screen::keyboardEvent() {} +void Screen::tick() {} +void Screen::removed() {} +void Screen::renderBackground() {} +void Screen::renderBackground(int) {} +void Screen::renderDirtBackground(int) {} +bool Screen::isPauseScreen() { return false; } +void Screen::confirmResult(bool, int) {} +void Screen::tabPressed() {} +wstring Screen::getClipboard() { return L""; } +void Screen::setClipboard(const wstring &) {} + +Model::Model() : attackTime(0), riding(false), young(false), texWidth(64), texHeight(32), yHeadOffs(0), zHeadOffs(0) {} +OffsettedRenderList::OffsettedRenderList() : x(0), y(0), z(0), xOff(0), yOff(0), zOff(0), lists(NULL), inited(false), rendered(false) {} + +void HumanoidModel::render(shared_ptr, float, float, float, float, float, float, bool) {} +void HumanoidModel::setupAnim(float, float, float, float, float, float, unsigned int) {} +ModelPart *HumanoidModel::AddOrRetrievePart(SKIN_BOX *) { return NULL; } + +void TitleScreen::tick() {} +void TitleScreen::keyPressed(wchar_t, int) {} +void TitleScreen::init() {} +void TitleScreen::buttonClicked(Button *) {} +void TitleScreen::render(int, int, float) {} + +SoundEngine::SoundEngine() {} +void SoundEngine::SetStreamingSounds(int, int, int, int, int, int, int) {} +void SoundEngine::destroy() {} +void SoundEngine::play(int, float, float, float, float, float) {} +void SoundEngine::playStreaming(const wstring &, float, float, float, float, float, bool) {} +void SoundEngine::playUI(int, float, float) {} +void SoundEngine::playMusicTick() {} +void SoundEngine::updateMusicVolume(float) {} +void SoundEngine::updateSystemMusicPlaying(bool) {} +void SoundEngine::updateSoundEffectVolume(float) {} +void SoundEngine::init(Options *) {} +void SoundEngine::tick(shared_ptr *, float) {} +void SoundEngine::add(const wstring &, File *) {} +void SoundEngine::addMusic(const wstring &, File *) {} +void SoundEngine::addStreaming(const wstring &, File *) {} +char *SoundEngine::ConvertSoundPathToName(const wstring &, bool) { return NULL; } + +bool ConsoleSoundEngine::GetIsPlayingStreamingCDMusic() { return false; } +bool ConsoleSoundEngine::GetIsPlayingStreamingGameMusic() { return false; } +void ConsoleSoundEngine::SetIsPlayingStreamingCDMusic(bool) {} +void ConsoleSoundEngine::SetIsPlayingStreamingGameMusic(bool) {} +bool ConsoleSoundEngine::GetIsPlayingEndMusic() { return false; } +bool ConsoleSoundEngine::GetIsPlayingNetherMusic() { return false; } +void ConsoleSoundEngine::SetIsPlayingEndMusic(bool) {} +void ConsoleSoundEngine::SetIsPlayingNetherMusic(bool) {} + +void LevelRenderer::staticCtor() {} +void IUIScene_CreativeMenu::staticCtor() {} +vector< shared_ptr > IUIScene_CreativeMenu::categoryGroups[IUIScene_CreativeMenu::eCreativeInventoryGroupsCount]; +IUIScene_CreativeMenu::TabSpec **IUIScene_CreativeMenu::specs = NULL; + +unsigned short SFontData::Codepoints[SFontData::FONTSIZE] = {}; +SFontData SFontData::Mojangles_7 = {}; +SFontData SFontData::Mojangles_11 = {}; + +int IUIScene_PauseMenu::SaveWorldThreadProc(void *) { return 0; } +int IUIScene_PauseMenu::ExitWorldThreadProc(void *) { return 0; } +void IUIScene_PauseMenu::_ExitWorld(LPVOID) {} +shared_ptr IUIScene_TradingMenu::getMerchant() { return shared_ptr(); } +bool IUIScene_CraftingMenu::isItemSelected(int) { return false; } + +void UIScene_FullscreenProgress::SetWasCancelled(bool) {} + +double Particle::xOff = 0; +double Particle::yOff = 0; +double Particle::zOff = 0; + +void Particle::_init(Level *level, double x, double y, double z) {} +Particle::Particle(Level *level, double x, double y, double z) : Entity(level) {} +Particle::Particle(Level *level, double x, double y, double z, double xa, double ya, double za) : Entity(level) {} +shared_ptr Particle::setPower(float) { return shared_ptr(); } +shared_ptr Particle::scale(float) { return shared_ptr(); } +void Particle::setColor(float, float, float) {} +void Particle::setAlpha(float) {} +float Particle::getRedCol() { return 0; } +float Particle::getGreenCol() { return 0; } +float Particle::getBlueCol() { return 0; } +float Particle::getAlpha() { return 0; } +bool Particle::makeStepSound() { return false; } +void Particle::defineSynchedData() {} +void Particle::tick() {} +void Particle::render(Tesselator *, float, float, float, float, float, float) {} +int Particle::getParticleTexture() { return 0; } +void Particle::addAdditonalSaveData(CompoundTag *) {} +void Particle::readAdditionalSaveData(CompoundTag *) {} +void Particle::setTex(Textures *, Icon *) {} +void Particle::setMiscTex(int) {} +void Particle::setNextMiscAnimTex() {} +bool Particle::isAttackable() { return false; } +wstring Particle::toString() { return L""; } + +void CritParticle::_init(Level *, shared_ptr, ePARTICLE_TYPE) {} +CritParticle::CritParticle(Level *level, shared_ptr entity) : Particle(level, 0, 0, 0, 0, 0, 0), life(0), lifeTime(0), particleName(eParticleType_crit) {} +CritParticle::CritParticle(Level *level, shared_ptr entity, ePARTICLE_TYPE type) : Particle(level, 0, 0, 0, 0, 0, 0), life(0), lifeTime(0), particleName(type) {} +void CritParticle::CritParticlePostConstructor() {} +void CritParticle::render(Tesselator *, float, float, float, float, float, float) {} +void CritParticle::tick() {} +int CritParticle::getParticleTexture() { return 0; } + +TakeAnimationParticle::TakeAnimationParticle(Level *level, shared_ptr item, shared_ptr target, float yOffs) : Particle(level, 0, 0, 0, 0, 0, 0), life(0), lifeTime(0), yOffs(yOffs) {} +TakeAnimationParticle::~TakeAnimationParticle() {} +void TakeAnimationParticle::render(Tesselator *, float, float, float, float, float, float) {} +void TakeAnimationParticle::tick() {} +int TakeAnimationParticle::getParticleTexture() { return 0; } + +#ifdef _WIN32 +void C4JStorage::SetMaxSaves(int) {} +C4JStorage::ESaveGameState C4JStorage::GetSaveState() { return C4JStorage::ESaveGame_Idle; } +#endif \ No newline at end of file diff --git a/Stubs/ServerStubs3.cpp b/Stubs/ServerStubs3.cpp new file mode 100644 index 0000000..94198fb --- /dev/null +++ b/Stubs/ServerStubs3.cpp @@ -0,0 +1,202 @@ +#include "stdafx.h" + +#include "../../Minecraft.Client/Windows64/4JLibs/inc/4J_Render.h" +#include "../../Minecraft.Client/Windows64/4JLibs/inc/4J_Storage.h" +#include "../../Minecraft.Client/Windows64/Windows64_App.h" +#include "../../Minecraft.World/Item.h" +#include "../../Minecraft.World/Tile.h" +#include "../../Minecraft.Client/DeathScreen.h" +#include "../../Minecraft.Client/ErrorScreen.h" +#include "../../Minecraft.Client/ChatScreen.h" +#include "../../Minecraft.Client/InBedChatScreen.h" +#include "../../Minecraft.Client/Common/UI/UIScene.h" + +CConsoleMinecraftApp app; +C4JRender RenderManager; + +CConsoleMinecraftApp::CConsoleMinecraftApp() : CMinecraftApp() {} +void CConsoleMinecraftApp::SetRichPresenceContext(int, int) {} +void CConsoleMinecraftApp::CaptureSaveThumbnail() {} +void CConsoleMinecraftApp::GetSaveThumbnail(PBYTE*, DWORD*) {} +void CConsoleMinecraftApp::FatalLoadError() {} +void CConsoleMinecraftApp::StoreLaunchData() {} +void CConsoleMinecraftApp::ExitGame() {} +void CConsoleMinecraftApp::ReleaseSaveThumbnail() {} +void CConsoleMinecraftApp::GetScreenshot(int, PBYTE*, DWORD*) {} +int CConsoleMinecraftApp::LoadLocalTMSFile(WCHAR*) { return 0; } +int CConsoleMinecraftApp::LoadLocalTMSFile(WCHAR*, eFileExtensionType) { return 0; } +void CConsoleMinecraftApp::FreeLocalTMSFiles(eTMSFileType) {} +int CConsoleMinecraftApp::GetLocalTMSFileIndex(WCHAR*, bool, eFileExtensionType) { return -1; } +void CConsoleMinecraftApp::TemporaryCreateGameStart() {} + +void C4JRender::Tick() {} +void C4JRender::UpdateGamma(unsigned short) {} +void C4JRender::StartFrame() {} +bool C4JRender::IsWidescreen() { return false; } +bool C4JRender::IsHiDef() { return false; } +void C4JRender::Present() {} +void C4JRender::CBuffLockStaticCreations() {} +void C4JRender::StateSetViewport(eViewportType) {} + +const int Item::apple_Id; +const int Item::arrow_Id; +const int Item::beef_cooked_Id; +const int Item::beef_raw_Id; +const int Item::book_Id; +const int Item::boots_chain_Id; +const int Item::boots_cloth_Id; +const int Item::boots_diamond_Id; +const int Item::boots_iron_Id; +const int Item::bread_Id; +const int Item::chestplate_chain_Id; +const int Item::chestplate_cloth_Id; +const int Item::chestplate_diamond_Id; +const int Item::chestplate_iron_Id; +const int Item::chicken_cooked_Id; +const int Item::chicken_raw_Id; +const int Item::clock_Id; +const int Item::coal_Id; +const int Item::compass_Id; +const int Item::cookie_Id; +const int Item::diamond_Id; +const int Item::enderPearl_Id; +const int Item::expBottle_Id; +const int Item::eyeOfEnder_Id; +const int Item::fish_cooked_Id; +const int Item::flintAndSteel_Id; +const int Item::goldIngot_Id; +const int Item::hatchet_diamond_Id; +const int Item::hatchet_iron_Id; +const int Item::helmet_chain_Id; +const int Item::helmet_cloth_Id; +const int Item::helmet_diamond_Id; +const int Item::helmet_iron_Id; +const int Item::hoe_diamond_Id; +const int Item::hoe_iron_Id; +const int Item::ironIngot_Id; +const int Item::leggings_chain_Id; +const int Item::leggings_cloth_Id; +const int Item::leggings_diamond_Id; +const int Item::leggings_iron_Id; +const int Item::melon_Id; +const int Item::paper_Id; +const int Item::pickAxe_diamond_Id; +const int Item::pickAxe_iron_Id; +const int Item::porkChop_cooked_Id; +const int Item::porkChop_raw_Id; +const int Item::redStone_Id; +const int Item::rotten_flesh_Id; +const int Item::saddle_Id; +const int Item::seeds_melon_Id; +const int Item::seeds_pumpkin_Id; +const int Item::seeds_wheat_Id; +const int Item::shears_Id; +const int Item::shovel_diamond_Id; +const int Item::shovel_iron_Id; +const int Item::sword_diamond_Id; +const int Item::sword_iron_Id; +const int Item::wheat_Id; + +const int Tile::bookshelf_Id; +const int Tile::cloth_Id; +const int Tile::glass_Id; +const int Tile::lightGem_Id; + + +void DeathScreen::init() {} +void DeathScreen::keyPressed(char, int) {} +void DeathScreen::buttonClicked(Button*) {} +void DeathScreen::render(int, int, float) {} +bool DeathScreen::isPauseScreen() { return false; } + +ErrorScreen::ErrorScreen(const wstring&, const wstring&) {} +void ErrorScreen::init() {} +void ErrorScreen::render(int, int, float) {} +void ErrorScreen::keyPressed(wchar_t, int) {} + +void InBedChatScreen::init() {} +void InBedChatScreen::removed() {} +void InBedChatScreen::keyPressed(wchar_t, int) {} +void InBedChatScreen::render(int, int, float) {} +void InBedChatScreen::buttonClicked(Button*) {} + +UIScene::UIScene(int iPad, UILayer* parentLayer) : m_iPad(iPad), m_parentLayer(parentLayer), swf(nullptr), m_rootPath(nullptr), m_pItemRenderer(nullptr), m_iFocusControl(0), m_iFocusChild(0), m_lastOpacity(1.0f), m_bUpdateOpacity(false), m_bVisible(false), m_bCanHandleInput(false), m_backScene(nullptr), m_callbackUniqueId(0), m_loadedResolution((ESceneResolution)0), m_bIsReloading(false), m_bFocussedOnce(false), m_movieWidth(0), m_movieHeight(0), m_renderWidth(0), m_renderHeight(0), bHasFocus(false), m_hasTickedOnce(false), m_cacheSlotRenders(false), m_needsCacheRendered(false), m_expectedCachedSlotCount(0) {} +UIScene::~UIScene() {} +void UIScene::reloadMovie(bool) {} +bool UIScene::needsReloaded() { return false; } +bool UIScene::hasMovie() { return false; } +void UIScene::updateSafeZone() {} +F64 UIScene::getSafeZoneHalfHeight() { return 0; } +F64 UIScene::getSafeZoneHalfWidth() { return 0; } +bool UIScene::mapElementsAndNames() { return false; } +void UIScene::tick() {} +void UIScene::updateTooltips() {} +void UIScene::handleGainFocus(bool) {} +UIControl* UIScene::GetMainPanel() { return nullptr; } +void UIScene::render(S32, S32, C4JRender::eViewportType) {} +void UIScene::customDraw(IggyCustomDrawCallbackRegion*) {} +bool UIScene::allowRepeat(int) { return false; } +bool UIScene::isReadyToDelete() { return true; } +IggyName UIScene::registerFastName(const wstring&) { return 0; } + +#include "../../Minecraft.Client/Common/UI/UIControl_Base.h" +UIControl_Base::UIControl_Base() {} +bool UIControl_Base::setupControl(UIScene*, IggyValuePath*, const string&) { return false; } +void UIControl_Base::tick() {} +void UIControl_Base::setLabel(const wstring&, bool, bool) {} +void UIControl_Base::setLabel(const string&) {} +void UIControl_Base::setAllPossibleLabels(int, wchar_t[][256]) {} +bool UIControl_Base::hasFocus() { return false; } + +UIControl::UIControl() {} +bool UIControl::setupControl(UIScene*, IggyValuePath*, const string&) { return false; } +void UIControl::ReInit() {} +IggyValuePath* UIControl::getIggyValuePath() { return nullptr; } + +#include "../../Minecraft.Client/Common/UI/UIControl_Label.h" +UIControl_Label::UIControl_Label() {} +bool UIControl_Label::setupControl(UIScene*, IggyValuePath*, const string&) { return false; } +void UIControl_Label::ReInit() {} + +#include "../../Minecraft.Client/Common/UI/UIControl_SlotList.h" +UIControl_SlotList::UIControl_SlotList() {} +bool UIControl_SlotList::setupControl(UIScene*, IggyValuePath*, const string&) { return false; } +void UIControl_SlotList::setFocus(bool) {} + +ChatScreen::ChatScreen() {} +void ChatScreen::init() {} +void ChatScreen::removed() {} +void ChatScreen::tick() {} +void ChatScreen::render(int, int, float) {} +void ChatScreen::keyPressed(wchar_t, int) {} +void ChatScreen::mouseClicked(int, int, int) {} + +extern "C" { +IggyValuePath* IggyPlayerRootPath(Iggy*) { return nullptr; } +} + +#include "../../Minecraft.Client/Common/UI/UIScene_TradingMenu.h" + +UIScene_TradingMenu::UIScene_TradingMenu(int iPad, void* initData, UILayer* parentLayer) : UIScene(iPad, parentLayer), IUIScene_TradingMenu(), m_showingRightArrow(false), m_showingLeftArrow(false) {} +wstring UIScene_TradingMenu::getMoviePath() { return wstring(); } +void UIScene_TradingMenu::updateTooltips() {} +void UIScene_TradingMenu::handleDestroy() {} +void UIScene_TradingMenu::handleReload() {} +void UIScene_TradingMenu::tick() {} +void UIScene_TradingMenu::handleInput(int, int, bool, bool, bool, bool&) {} +void UIScene_TradingMenu::customDraw(IggyCustomDrawCallbackRegion*) {} +void UIScene_TradingMenu::showScrollRightArrow(bool) {} +void UIScene_TradingMenu::showScrollLeftArrow(bool) {} +void UIScene_TradingMenu::moveSelector(bool) {} +void UIScene_TradingMenu::setTitle(const wstring&) {} +void UIScene_TradingMenu::setRequest1Name(const wstring&) {} +void UIScene_TradingMenu::setRequest2Name(const wstring&) {} +void UIScene_TradingMenu::setRequest1RedBox(bool) {} +void UIScene_TradingMenu::setRequest2RedBox(bool) {} +void UIScene_TradingMenu::setTradeRedBox(int, bool) {} +void UIScene_TradingMenu::setOfferDescription(const wstring&, vector&) {} + +IUIScene_TradingMenu::IUIScene_TradingMenu() : m_menu(nullptr), m_validOffersCount(0), m_selectedSlot(0), m_offersStartIndex(0), m_bHasUpdatedOnce(false) {} +void IUIScene_TradingMenu::setRequest1Item(shared_ptr) {} +void IUIScene_TradingMenu::setRequest2Item(shared_ptr) {} +void IUIScene_TradingMenu::setTradeItem(int, shared_ptr) {} diff --git a/stdafx.cpp b/stdafx.cpp new file mode 100644 index 0000000..6575610 --- /dev/null +++ b/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/stdafx.h b/stdafx.h new file mode 100644 index 0000000..f7af3ab --- /dev/null +++ b/stdafx.h @@ -0,0 +1,15 @@ +#pragma once + +#ifndef _DEDICATED_SERVER +#define _DEDICATED_SERVER +#endif + +#ifdef __linux__ +typedef unsigned __int128 __uint128; +#endif + +#include "../Minecraft.Client/stdafx.h" + +#include "../Minecraft.World/TilePos.h" + +#define FIFTY_ONE_MB (1000000*51) \ No newline at end of file