add: add implementing interactive command line using linenoise

- Integrated linenoise library for line editing and completion in the server console.
- Updated ServerLogger to handle external writes safely during logging.
- Modified ServerMain to initialize and manage the ServerCli for command input.
- The implementation is separate from everything else, so it doesn't affect anything else.
- The command input section and execution section are separated into threads.
This commit is contained in:
kuwacom 2026-03-07 16:48:58 +09:00
parent 9e4052ece5
commit 965a859b40
29 changed files with 2720 additions and 1 deletions

View file

@ -91,6 +91,17 @@ target_link_libraries(MinecraftClient PRIVATE
set(MINECRAFT_SERVER_SOURCES ${MINECRAFT_CLIENT_SOURCES}) set(MINECRAFT_SERVER_SOURCES ${MINECRAFT_CLIENT_SOURCES})
list(APPEND MINECRAFT_SERVER_SOURCES list(APPEND MINECRAFT_SERVER_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Windows64/ServerMain.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Windows64/ServerMain.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCli.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliInput.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliParser.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliEngine.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliRegistry.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandHelp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandStop.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandList.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandTp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandGamemode.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/vendor/linenoise/linenoise.c"
) )
add_executable(MinecraftServer ${MINECRAFT_SERVER_SOURCES}) add_executable(MinecraftServer ${MINECRAFT_SERVER_SOURCES})

View file

@ -0,0 +1,44 @@
#include "stdafx.h"
#include "ServerCli.h"
#include "ServerCliEngine.h"
#include "ServerCliInput.h"
namespace ServerRuntime
{
ServerCli::ServerCli()
: m_engine(new ServerCliEngine())
, m_input(new ServerCliInput())
{
}
ServerCli::~ServerCli()
{
Stop();
}
void ServerCli::Start()
{
if (m_input && m_engine)
{
m_input->Start(m_engine.get());
}
}
void ServerCli::Stop()
{
if (m_input)
{
m_input->Stop();
}
}
void ServerCli::Poll()
{
if (m_engine)
{
m_engine->Poll();
}
}
}

View file

@ -0,0 +1,50 @@
#pragma once
#include <memory>
namespace ServerRuntime
{
class ServerCliEngine;
class ServerCliInput;
/**
* **Server CLI facade**
*
* Owns the command engine and input component, and exposes a small lifecycle API.
* CLI
*/
class ServerCli
{
public:
ServerCli();
~ServerCli();
/**
* **Start console input processing**
*
* Connects input to the engine and starts background reading.
*
*/
void Start();
/**
* **Stop console input processing**
*
* Stops background input safely and detaches from the engine.
*
*/
void Stop();
/**
* **Process queued command lines**
*
* Drains commands collected by input and executes them in the main loop.
*
*/
void Poll();
private:
std::unique_ptr<ServerCliEngine> m_engine;
std::unique_ptr<ServerCliInput> m_input;
};
}

View file

@ -0,0 +1,314 @@
#include "stdafx.h"
#include "ServerCliEngine.h"
#include "ServerCliParser.h"
#include "ServerCliRegistry.h"
#include "commands\IServerCliCommand.h"
#include "commands\CliCommandGamemode.h"
#include "commands\CliCommandHelp.h"
#include "commands\CliCommandList.h"
#include "commands\CliCommandStop.h"
#include "commands\CliCommandTp.h"
#include "..\ServerLogger.h"
#include "..\..\Minecraft.Client\MinecraftServer.h"
#include "..\..\Minecraft.Client\PlayerList.h"
#include "..\..\Minecraft.Client\ServerPlayer.h"
#include "..\..\Minecraft.World\LevelSettings.h"
#include "..\..\Minecraft.World\StringHelpers.h"
#include <ctype.h>
#include <stdlib.h>
#include <unordered_set>
namespace ServerRuntime
{
ServerCliEngine::ServerCliEngine()
: m_registry(new ServerCliRegistry())
{
RegisterDefaultCommands();
}
ServerCliEngine::~ServerCliEngine()
{
}
void ServerCliEngine::RegisterDefaultCommands()
{
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandHelp()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandStop()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandList()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandTp()));
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandGamemode()));
}
void ServerCliEngine::EnqueueCommandLine(const std::string &line)
{
std::lock_guard<std::mutex> lock(m_queueMutex);
m_pendingLines.push(line);
}
void ServerCliEngine::Poll()
{
for (;;)
{
std::string line;
{
// Keep the lock scope minimal: dequeue only, execute outside.
std::lock_guard<std::mutex> lock(m_queueMutex);
if (m_pendingLines.empty())
{
break;
}
line = m_pendingLines.front();
m_pendingLines.pop();
}
ExecuteCommandLine(line);
}
}
std::string ServerCliEngine::Normalize(const std::string &value)
{
std::string lowered = value;
for (size_t i = 0; i < lowered.size(); ++i)
{
lowered[i] = (char)tolower((unsigned char)lowered[i]);
}
return lowered;
}
std::wstring ServerCliEngine::ToWide(const std::string &value)
{
return convStringToWstring(value);
}
std::string ServerCliEngine::ToUtf8(const std::wstring &value)
{
return WideToUtf8(value);
}
bool ServerCliEngine::ExecuteCommandLine(const std::string &line)
{
// Normalize user input before parsing (trim + optional leading slash).
std::wstring wide = trimString(ToWide(line));
if (wide.empty())
{
return true;
}
std::string normalizedLine = ToUtf8(wide);
if (!normalizedLine.empty() && normalizedLine[0] == '/')
{
normalizedLine = normalizedLine.substr(1);
}
ServerCliParsedLine parsed = ServerCliParser::Parse(normalizedLine);
if (parsed.tokens.empty())
{
return true;
}
IServerCliCommand *command = m_registry->FindMutable(parsed.tokens[0]);
if (command == NULL)
{
LogWarn("Unknown command: " + parsed.tokens[0]);
return false;
}
return command->Execute(parsed, this);
}
void ServerCliEngine::BuildCompletions(const std::string &line, std::vector<std::string> *out) const
{
if (out == NULL)
{
return;
}
out->clear();
ServerCliCompletionContext context = ServerCliParser::BuildCompletionContext(line);
bool slashPrefixedCommand = false;
std::string commandToken;
if (!context.parsed.tokens.empty())
{
// Completion accepts both "tp" and "/tp" style command heads.
commandToken = context.parsed.tokens[0];
if (!commandToken.empty() && commandToken[0] == '/')
{
commandToken = commandToken.substr(1);
slashPrefixedCommand = true;
}
}
if (context.currentTokenIndex == 0)
{
std::string prefix = context.prefix;
if (!prefix.empty() && prefix[0] == '/')
{
prefix = prefix.substr(1);
slashPrefixedCommand = true;
}
std::string linePrefix = context.linePrefix;
if (slashPrefixedCommand && linePrefix.empty())
{
// Preserve leading slash when user started with "/".
linePrefix = "/";
}
m_registry->SuggestCommandNames(prefix, linePrefix, out);
}
else
{
const IServerCliCommand *command = m_registry->Find(commandToken);
if (command != NULL)
{
command->Complete(context, this, out);
}
}
std::unordered_set<std::string> seen;
std::vector<std::string> unique;
for (size_t i = 0; i < out->size(); ++i)
{
// Remove duplicates while keeping first-seen ordering.
if (seen.insert((*out)[i]).second)
{
unique.push_back((*out)[i]);
}
}
out->swap(unique);
}
void ServerCliEngine::LogInfo(const std::string &message) const
{
LogInfof("console", "%s", message.c_str());
}
void ServerCliEngine::LogWarn(const std::string &message) const
{
LogWarnf("console", "%s", message.c_str());
}
void ServerCliEngine::LogError(const std::string &message) const
{
LogErrorf("console", "%s", message.c_str());
}
void ServerCliEngine::RequestShutdown() const
{
MinecraftServer::HaltServer();
}
std::vector<std::string> ServerCliEngine::GetOnlinePlayerNamesUtf8() const
{
std::vector<std::string> result;
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL)
{
return result;
}
PlayerList *players = server->getPlayers();
if (players == NULL)
{
return result;
}
for (size_t i = 0; i < players->players.size(); ++i)
{
std::shared_ptr<ServerPlayer> player = players->players[i];
if (player != NULL)
{
result.push_back(ToUtf8(player->getName()));
}
}
return result;
}
std::shared_ptr<ServerPlayer> ServerCliEngine::FindPlayerByNameUtf8(const std::string &name) const
{
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL)
{
return nullptr;
}
PlayerList *players = server->getPlayers();
if (players == NULL)
{
return nullptr;
}
std::wstring target = ToWide(name);
for (size_t i = 0; i < players->players.size(); ++i)
{
std::shared_ptr<ServerPlayer> player = players->players[i];
if (player != NULL && equalsIgnoreCase(player->getName(), target))
{
return player;
}
}
return nullptr;
}
void ServerCliEngine::SuggestPlayers(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const
{
std::vector<std::string> players = GetOnlinePlayerNamesUtf8();
std::string loweredPrefix = Normalize(prefix);
for (size_t i = 0; i < players.size(); ++i)
{
std::string loweredName = Normalize(players[i]);
if (loweredName.compare(0, loweredPrefix.size(), loweredPrefix) == 0)
{
out->push_back(linePrefix + players[i]);
}
}
}
void ServerCliEngine::SuggestGamemodes(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const
{
static const char *kModes[] = { "survival", "creative", "s", "c", "0", "1" };
std::string loweredPrefix = Normalize(prefix);
for (size_t i = 0; i < sizeof(kModes) / sizeof(kModes[0]); ++i)
{
std::string candidate = kModes[i];
std::string loweredCandidate = Normalize(candidate);
if (loweredCandidate.compare(0, loweredPrefix.size(), loweredPrefix) == 0)
{
out->push_back(linePrefix + candidate);
}
}
}
GameType *ServerCliEngine::ParseGamemode(const std::string &token) const
{
std::string lowered = Normalize(token);
if (lowered == "survival" || lowered == "s" || lowered == "0")
{
return GameType::SURVIVAL;
}
if (lowered == "creative" || lowered == "c" || lowered == "1")
{
return GameType::CREATIVE;
}
char *end = NULL;
long id = strtol(lowered.c_str(), &end, 10);
if (end != NULL && *end == 0)
{
// Numeric fallback supports extended ids handled by level settings.
return LevelSettings::validateGameType((int)id);
}
return NULL;
}
const ServerCliRegistry &ServerCliEngine::Registry() const
{
return *m_registry;
}
}

View file

@ -0,0 +1,114 @@
#pragma once
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <vector>
class GameType;
class ServerPlayer;
namespace ServerRuntime
{
class ServerCliRegistry;
/**
* **CLI execution engine**
*
* Handles parsing, command dispatch, completion suggestions, and server-side helpers.
*
*/
class ServerCliEngine
{
public:
ServerCliEngine();
~ServerCliEngine();
/**
* **Queue one raw command line**
*
* Called by input thread; execution is deferred to `Poll()`.
*
*/
void EnqueueCommandLine(const std::string &line);
/**
* **Execute queued commands**
*
* Drains pending lines and dispatches them in order.
*
*/
void Poll();
/**
* **Execute one command line immediately**
*
* Parses and dispatches a normalized line to a registered command.
* 1
*/
bool ExecuteCommandLine(const std::string &line);
/**
* **Build completion candidates for current line**
*
* Produces command or argument suggestions based on parser context.
*
*/
void BuildCompletions(const std::string &line, std::vector<std::string> *out) const;
void LogInfo(const std::string &message) const;
void LogWarn(const std::string &message) const;
void LogError(const std::string &message) const;
void RequestShutdown() const;
/**
* **List connected players as UTF-8 names**
*
*
*/
std::vector<std::string> GetOnlinePlayerNamesUtf8() const;
/**
* **Find a player by UTF-8 name**
*/
std::shared_ptr<ServerPlayer> FindPlayerByNameUtf8(const std::string &name) const;
/**
* **Suggest player-name arguments**
*
* Appends matching player candidates using the given completion prefix.
*
*/
void SuggestPlayers(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const;
/**
* **Suggest gamemode arguments**
*
* Appends standard gamemode aliases (survival/creative/0/1).
*
*/
void SuggestGamemodes(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const;
/**
* **Parse gamemode token**
*
* Supports names, short aliases, and numeric ids.
*
*/
GameType *ParseGamemode(const std::string &token) const;
const ServerCliRegistry &Registry() const;
private:
void RegisterDefaultCommands();
static std::string Normalize(const std::string &value);
static std::wstring ToWide(const std::string &value);
static std::string ToUtf8(const std::wstring &value);
private:
mutable std::mutex m_queueMutex;
std::queue<std::string> m_pendingLines;
std::unique_ptr<ServerCliRegistry> m_registry;
};
}

View file

@ -0,0 +1,120 @@
#include "stdafx.h"
#include "ServerCliInput.h"
#include "ServerCliEngine.h"
#include "..\ServerLogger.h"
#include "..\vendor\linenoise\linenoise.h"
namespace ServerRuntime
{
// C-style completion callback bridge requires a static instance pointer.
ServerCliInput *ServerCliInput::s_instance = NULL;
ServerCliInput::ServerCliInput()
: m_running(false)
, m_engine(NULL)
{
}
ServerCliInput::~ServerCliInput()
{
Stop();
}
void ServerCliInput::Start(ServerCliEngine *engine)
{
if (engine == NULL || m_running.exchange(true))
{
return;
}
m_engine = engine;
s_instance = this;
linenoiseResetStop();
linenoiseHistorySetMaxLen(128);
linenoiseSetCompletionCallback(&ServerCliInput::CompletionThunk);
m_inputThread = std::thread(&ServerCliInput::RunInputLoop, this);
LogInfo("console", "CLI input thread started.");
}
void ServerCliInput::Stop()
{
if (!m_running.exchange(false))
{
return;
}
// Ask linenoise to break out first, then join thread safely.
linenoiseRequestStop();
if (m_inputThread.joinable())
{
m_inputThread.join();
}
linenoiseSetCompletionCallback(NULL);
if (s_instance == this)
{
s_instance = NULL;
}
m_engine = NULL;
LogInfo("console", "CLI input thread stopped.");
}
bool ServerCliInput::IsRunning() const
{
return m_running.load();
}
void ServerCliInput::RunInputLoop()
{
while (m_running)
{
char *line = linenoise("server> ");
if (line == NULL)
{
// NULL is expected on stop request (or Ctrl+C inside linenoise).
if (!m_running)
{
break;
}
Sleep(10);
continue;
}
if (line[0] != 0 && m_engine != NULL)
{
// Keep local history and forward command for main-thread execution.
linenoiseHistoryAdd(line);
m_engine->EnqueueCommandLine(line);
}
linenoiseFree(line);
}
}
void ServerCliInput::CompletionThunk(const char *line, linenoiseCompletions *completions)
{
// Static thunk forwards callback into instance state.
if (s_instance != NULL)
{
s_instance->BuildCompletions(line, completions);
}
}
void ServerCliInput::BuildCompletions(const char *line, linenoiseCompletions *completions)
{
if (line == NULL || completions == NULL || m_engine == NULL)
{
return;
}
std::vector<std::string> suggestions;
m_engine->BuildCompletions(line, &suggestions);
for (size_t i = 0; i < suggestions.size(); ++i)
{
linenoiseAddCompletion(completions, suggestions[i].c_str());
}
}
}

View file

@ -0,0 +1,60 @@
#pragma once
#include <atomic>
#include <thread>
struct linenoiseCompletions;
namespace ServerRuntime
{
class ServerCliEngine;
/**
* **CLI input worker**
*
* Owns the interactive input thread and bridges linenoise callbacks to the engine.
*
*/
class ServerCliInput
{
public:
ServerCliInput();
~ServerCliInput();
/**
* **Start input loop**
*
* Binds to an engine and starts reading user input from the console.
*
*/
void Start(ServerCliEngine *engine);
/**
* **Stop input loop**
*
* Requests stop and joins the input thread.
*
*/
void Stop();
/**
* **Check running state**
*
* Returns true while the input thread is active.
*
*/
bool IsRunning() const;
private:
void RunInputLoop();
static void CompletionThunk(const char *line, linenoiseCompletions *completions);
void BuildCompletions(const char *line, linenoiseCompletions *completions);
private:
std::atomic<bool> m_running;
std::thread m_inputThread;
ServerCliEngine *m_engine;
static ServerCliInput *s_instance;
};
}

View file

@ -0,0 +1,116 @@
#include "stdafx.h"
#include "ServerCliParser.h"
namespace ServerRuntime
{
static void TokenizeLine(const std::string &line, std::vector<std::string> *tokens, bool *trailingSpace)
{
std::string current;
bool inQuotes = false;
bool escaped = false;
tokens->clear();
*trailingSpace = false;
for (size_t i = 0; i < line.size(); ++i)
{
char ch = line[i];
if (escaped)
{
// Keep escaped character literally (e.g. \" or \ ).
current.push_back(ch);
escaped = false;
continue;
}
if (ch == '\\')
{
escaped = true;
continue;
}
if (ch == '"')
{
// Double quotes group spaces into one token.
inQuotes = !inQuotes;
continue;
}
if (!inQuotes && (ch == ' ' || ch == '\t'))
{
if (!current.empty())
{
tokens->push_back(current);
current.clear();
}
continue;
}
current.push_back(ch);
}
if (!current.empty())
{
tokens->push_back(current);
}
if (!line.empty())
{
char tail = line[line.size() - 1];
// Trailing space means completion targets the next token slot.
*trailingSpace = (!inQuotes && (tail == ' ' || tail == '\t'));
}
}
ServerCliParsedLine ServerCliParser::Parse(const std::string &line)
{
ServerCliParsedLine parsed;
parsed.raw = line;
TokenizeLine(line, &parsed.tokens, &parsed.trailingSpace);
return parsed;
}
ServerCliCompletionContext ServerCliParser::BuildCompletionContext(const std::string &line)
{
ServerCliCompletionContext context;
context.parsed = Parse(line);
if (context.parsed.tokens.empty())
{
context.currentTokenIndex = 0;
context.prefix.clear();
context.linePrefix.clear();
return context;
}
if (context.parsed.trailingSpace)
{
// Cursor is after a separator, so complete a new token.
context.currentTokenIndex = context.parsed.tokens.size();
context.prefix.clear();
}
else
{
// Cursor is inside current token, so complete by its prefix.
context.currentTokenIndex = context.parsed.tokens.size() - 1;
context.prefix = context.parsed.tokens.back();
}
for (size_t i = 0; i < context.currentTokenIndex; ++i)
{
// linePrefix is the immutable left side reused by completion output.
if (!context.linePrefix.empty())
{
context.linePrefix.push_back(' ');
}
context.linePrefix += context.parsed.tokens[i];
}
if (!context.linePrefix.empty())
{
context.linePrefix.push_back(' ');
}
return context;
}
}

View file

@ -0,0 +1,63 @@
#pragma once
#include <string>
#include <vector>
namespace ServerRuntime
{
/**
* **Parsed command line**
*/
struct ServerCliParsedLine
{
std::string raw;
std::vector<std::string> tokens;
bool trailingSpace;
ServerCliParsedLine()
: trailingSpace(false)
{
}
};
/**
* **Completion context for one input line**
*
* Indicates current token index, token prefix, and the fixed line prefix.
*/
struct ServerCliCompletionContext
{
ServerCliParsedLine parsed;
size_t currentTokenIndex;
std::string prefix;
std::string linePrefix;
ServerCliCompletionContext()
: currentTokenIndex(0)
{
}
};
/**
* **CLI parser helpers**
*
* Converts raw input text into tokenized data used by execution and completion.
*/
class ServerCliParser
{
public:
/**
* **Tokenize one command line**
*
* Supports quoted segments and escaped characters.
*/
static ServerCliParsedLine Parse(const std::string &line);
/**
* **Build completion metadata**
*
* Determines active token position and reusable prefix parts.
*/
static ServerCliCompletionContext BuildCompletionContext(const std::string &line);
};
}

View file

@ -0,0 +1,127 @@
#include "stdafx.h"
#include "ServerCliRegistry.h"
#include "commands\IServerCliCommand.h"
#include <ctype.h>
namespace ServerRuntime
{
static bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix)
{
if (prefix.size() > value.size())
{
return false;
}
for (size_t i = 0; i < prefix.size(); ++i)
{
char a = (char)tolower((unsigned char)value[i]);
char b = (char)tolower((unsigned char)prefix[i]);
if (a != b)
{
return false;
}
}
return true;
}
std::string ServerCliRegistry::Normalize(const std::string &value)
{
std::string normalized = value;
for (size_t i = 0; i < normalized.size(); ++i)
{
normalized[i] = (char)tolower((unsigned char)normalized[i]);
}
return normalized;
}
bool ServerCliRegistry::Register(std::unique_ptr<IServerCliCommand> command)
{
if (!command)
{
return false;
}
IServerCliCommand *raw = command.get();
std::string baseName = Normalize(raw->Name());
// Reject empty/duplicate primary command names.
if (baseName.empty() || m_lookup.find(baseName) != m_lookup.end())
{
return false;
}
std::vector<std::string> aliases = raw->Aliases();
std::vector<std::string> normalizedAliases;
normalizedAliases.reserve(aliases.size());
for (size_t i = 0; i < aliases.size(); ++i)
{
std::string alias = Normalize(aliases[i]);
// Alias must also be unique across all names and aliases.
if (alias.empty() || m_lookup.find(alias) != m_lookup.end())
{
return false;
}
normalizedAliases.push_back(alias);
}
m_lookup[baseName] = raw;
for (size_t i = 0; i < normalizedAliases.size(); ++i)
{
m_lookup[normalizedAliases[i]] = raw;
}
// Command objects are owned here; lookup stores non-owning pointers.
m_commands.push_back(std::move(command));
return true;
}
const IServerCliCommand *ServerCliRegistry::Find(const std::string &name) const
{
std::string key = Normalize(name);
auto it = m_lookup.find(key);
if (it == m_lookup.end())
{
return NULL;
}
return it->second;
}
IServerCliCommand *ServerCliRegistry::FindMutable(const std::string &name)
{
std::string key = Normalize(name);
auto it = m_lookup.find(key);
if (it == m_lookup.end())
{
return NULL;
}
return it->second;
}
void ServerCliRegistry::SuggestCommandNames(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const
{
for (size_t i = 0; i < m_commands.size(); ++i)
{
const IServerCliCommand *command = m_commands[i].get();
std::string name = command->Name();
if (StartsWithIgnoreCase(name, prefix))
{
out->push_back(linePrefix + name);
}
// Include aliases so users can discover shorthand commands.
std::vector<std::string> aliases = command->Aliases();
for (size_t aliasIndex = 0; aliasIndex < aliases.size(); ++aliasIndex)
{
if (StartsWithIgnoreCase(aliases[aliasIndex], prefix))
{
out->push_back(linePrefix + aliases[aliasIndex]);
}
}
}
}
const std::vector<std::unique_ptr<IServerCliCommand>> &ServerCliRegistry::Commands() const
{
return m_commands;
}
}

View file

@ -0,0 +1,61 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace ServerRuntime
{
class IServerCliCommand;
/**
* **CLI command registry**
*/
class ServerCliRegistry
{
public:
/**
* **Register a command object**
*
* Validates name/aliases and adds lookup entries.
*
*/
bool Register(std::unique_ptr<IServerCliCommand> command);
/**
* **Find command by name or alias (const)**
*
* Returns null when no match exists.
*/
const IServerCliCommand *Find(const std::string &name) const;
/**
* **Find mutable command by name or alias**
*
* Used by runtime dispatch path.
*/
IServerCliCommand *FindMutable(const std::string &name);
/**
* **Suggest top-level command names**
*
* Adds matching command names and aliases to the output list.
*/
void SuggestCommandNames(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const;
/**
* **Get registered command list**
*
* Intended for help output and inspection.
*/
const std::vector<std::unique_ptr<IServerCliCommand>> &Commands() const;
private:
static std::string Normalize(const std::string &value);
private:
std::vector<std::unique_ptr<IServerCliCommand>> m_commands;
std::unordered_map<std::string, IServerCliCommand *> m_lookup;
};
}

View file

@ -0,0 +1,95 @@
#include "stdafx.h"
#include "CliCommandGamemode.h"
#include "..\ServerCliEngine.h"
#include "..\ServerCliParser.h"
#include "..\..\ServerLogger.h"
#include "..\..\..\Minecraft.Client\MinecraftServer.h"
#include "..\..\..\Minecraft.Client\PlayerList.h"
#include "..\..\..\Minecraft.Client\ServerPlayer.h"
#include "..\..\..\Minecraft.World\LevelSettings.h"
namespace ServerRuntime
{
const char *CliCommandGamemode::Name() const
{
return "gamemode";
}
std::vector<std::string> CliCommandGamemode::Aliases() const
{
return { "gm" };
}
const char *CliCommandGamemode::Usage() const
{
return "gamemode <survival|creative|0|1> [player]";
}
const char *CliCommandGamemode::Description() const
{
return "Set a player's game mode.";
}
bool CliCommandGamemode::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() < 2)
{
engine->LogWarn("Usage: gamemode <survival|creative|0|1> [player]");
return false;
}
GameType *mode = engine->ParseGamemode(line.tokens[1]);
if (mode == NULL)
{
engine->LogWarn("Unknown gamemode: " + line.tokens[1]);
return false;
}
std::shared_ptr<ServerPlayer> target = nullptr;
if (line.tokens.size() >= 3)
{
target = engine->FindPlayerByNameUtf8(line.tokens[2]);
if (target == NULL)
{
engine->LogWarn("Unknown player: " + line.tokens[2]);
return false;
}
}
else
{
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL || server->getPlayers() == NULL)
{
engine->LogWarn("Player list is not available.");
return false;
}
PlayerList *players = server->getPlayers();
if (players->players.size() != 1 || players->players[0] == NULL)
{
engine->LogWarn("Usage: gamemode <survival|creative|0|1> <player>");
return false;
}
target = players->players[0];
}
target->setGameMode(mode);
target->fallDistance = 0.0f;
engine->LogInfo("Set " + WideToUtf8(target->getName()) + " gamemode to " + WideToUtf8(mode->getName()) + ".");
return true;
}
void CliCommandGamemode::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1)
{
engine->SuggestGamemodes(context.prefix, context.linePrefix, out);
}
else if (context.currentTokenIndex == 2)
{
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandGamemode : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual std::vector<std::string> Aliases() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
};
}

View file

@ -0,0 +1,45 @@
#include "stdafx.h"
#include "CliCommandHelp.h"
#include "..\ServerCliEngine.h"
#include "..\ServerCliRegistry.h"
namespace ServerRuntime
{
const char *CliCommandHelp::Name() const
{
return "help";
}
std::vector<std::string> CliCommandHelp::Aliases() const
{
return { "?" };
}
const char *CliCommandHelp::Usage() const
{
return "help";
}
const char *CliCommandHelp::Description() const
{
return "Show available server console commands.";
}
bool CliCommandHelp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
(void)line;
const std::vector<std::unique_ptr<IServerCliCommand>> &commands = engine->Registry().Commands();
engine->LogInfo("Available commands:");
for (size_t i = 0; i < commands.size(); ++i)
{
std::string row = " ";
row += commands[i]->Usage();
row += " - ";
row += commands[i]->Description();
engine->LogInfo(row);
}
return true;
}
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandHelp : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual std::vector<std::string> Aliases() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
};
}

View file

@ -0,0 +1,47 @@
#include "stdafx.h"
#include "CliCommandList.h"
#include "..\ServerCliEngine.h"
#include "..\..\ServerLogger.h"
#include "..\..\..\Minecraft.Client\MinecraftServer.h"
#include "..\..\..\Minecraft.Client\PlayerList.h"
namespace ServerRuntime
{
const char *CliCommandList::Name() const
{
return "list";
}
const char *CliCommandList::Usage() const
{
return "list";
}
const char *CliCommandList::Description() const
{
return "List connected players.";
}
bool CliCommandList::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
(void)line;
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL || server->getPlayers() == NULL)
{
engine->LogWarn("Player list is not available.");
return false;
}
PlayerList *players = server->getPlayers();
std::string names = WideToUtf8(players->getPlayerNames());
if (names.empty())
{
names = "(none)";
}
engine->LogInfo("Players (" + std::to_string(players->getPlayerCount()) + "): " + names);
return true;
}
}

View file

@ -0,0 +1,15 @@
#pragma once
#include "IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandList : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
};
}

View file

@ -0,0 +1,31 @@
#include "stdafx.h"
#include "CliCommandStop.h"
#include "..\ServerCliEngine.h"
namespace ServerRuntime
{
const char *CliCommandStop::Name() const
{
return "stop";
}
const char *CliCommandStop::Usage() const
{
return "stop";
}
const char *CliCommandStop::Description() const
{
return "Stop the dedicated server.";
}
bool CliCommandStop::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
(void)line;
engine->LogInfo("Stopping server...");
engine->RequestShutdown();
return true;
}
}

View file

@ -0,0 +1,15 @@
#pragma once
#include "IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandStop : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
};
}

View file

@ -0,0 +1,78 @@
#include "stdafx.h"
#include "CliCommandTp.h"
#include "..\ServerCliEngine.h"
#include "..\ServerCliParser.h"
#include "..\..\..\Minecraft.Client\PlayerConnection.h"
#include "..\..\..\Minecraft.Client\ServerPlayer.h"
#include "..\..\..\Minecraft.World\net.minecraft.world.level.h"
#include "..\..\..\Minecraft.World\net.minecraft.world.level.dimension.h"
namespace ServerRuntime
{
const char *CliCommandTp::Name() const
{
return "tp";
}
std::vector<std::string> CliCommandTp::Aliases() const
{
return { "teleport" };
}
const char *CliCommandTp::Usage() const
{
return "tp <player> <target>";
}
const char *CliCommandTp::Description() const
{
return "Teleport one player to another player.";
}
bool CliCommandTp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
{
if (line.tokens.size() < 3)
{
engine->LogWarn("Usage: tp <player> <target>");
return false;
}
std::shared_ptr<ServerPlayer> subject = engine->FindPlayerByNameUtf8(line.tokens[1]);
std::shared_ptr<ServerPlayer> destination = engine->FindPlayerByNameUtf8(line.tokens[2]);
if (subject == NULL)
{
engine->LogWarn("Unknown player: " + line.tokens[1]);
return false;
}
if (destination == NULL)
{
engine->LogWarn("Unknown player: " + line.tokens[2]);
return false;
}
if (subject->connection == NULL)
{
engine->LogWarn("Cannot teleport because source player connection is inactive.");
return false;
}
if (subject->level == NULL || destination->level == NULL || subject->level->dimension->id != destination->level->dimension->id || !subject->isAlive())
{
engine->LogWarn("Teleport failed because players are in different dimensions or source player is dead.");
return false;
}
subject->ride(nullptr);
subject->connection->teleport(destination->x, destination->y, destination->z, destination->yRot, destination->xRot);
engine->LogInfo("Teleported " + line.tokens[1] + " to " + line.tokens[2] + ".");
return true;
}
void CliCommandTp::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
if (context.currentTokenIndex == 1 || context.currentTokenIndex == 2)
{
engine->SuggestPlayers(context.prefix, context.linePrefix, out);
}
}
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "IServerCliCommand.h"
namespace ServerRuntime
{
class CliCommandTp : public IServerCliCommand
{
public:
virtual const char *Name() const;
virtual std::vector<std::string> Aliases() const;
virtual const char *Usage() const;
virtual const char *Description() const;
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
};
}

View file

@ -0,0 +1,50 @@
#pragma once
#include <string>
#include <vector>
namespace ServerRuntime
{
class ServerCliEngine;
struct ServerCliParsedLine;
struct ServerCliCompletionContext;
/**
* **Command interface for server CLI**
*
* Implement this contract to add new commands without changing engine internals.
*/
class IServerCliCommand
{
public:
virtual ~IServerCliCommand() {}
/** Primary command name */
virtual const char *Name() const = 0;
/** Optional aliases */
virtual std::vector<std::string> Aliases() const { return std::vector<std::string>(); }
/** Usage text for help */
virtual const char *Usage() const = 0;
/** Short command description*/
virtual const char *Description() const = 0;
/**
* **Execute command logic**
*
* Called after tokenization and command lookup.
*/
virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) = 0;
/**
* **Provide argument completion candidates**
*
* Override when command-specific completion is needed.
*/
virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
{
(void)context;
(void)engine;
(void)out;
}
};
}

View file

@ -647,9 +647,22 @@
<ClCompile Include="..\Minecraft.Client\compat_shims.cpp"> <ClCompile Include="..\Minecraft.Client\compat_shims.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader> <PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile> </ClCompile>
<ClCompile Include="Console\ServerCli.cpp" />
<ClCompile Include="Console\ServerCliInput.cpp" />
<ClCompile Include="Console\commands\CliCommandGamemode.cpp" />
<ClCompile Include="Console\commands\CliCommandHelp.cpp" />
<ClCompile Include="Console\commands\CliCommandList.cpp" />
<ClCompile Include="Console\commands\CliCommandStop.cpp" />
<ClCompile Include="Console\commands\CliCommandTp.cpp" />
<ClCompile Include="Console\ServerCliEngine.cpp" />
<ClCompile Include="Console\ServerCliParser.cpp" />
<ClCompile Include="Console\ServerCliRegistry.cpp" />
<ClCompile Include="..\Minecraft.Client\glWrapper.cpp" /> <ClCompile Include="..\Minecraft.Client\glWrapper.cpp" />
<ClCompile Include="ServerLogger.cpp" /> <ClCompile Include="ServerLogger.cpp" />
<ClCompile Include="ServerProperties.cpp" /> <ClCompile Include="ServerProperties.cpp" />
<ClCompile Include="vendor\linenoise\linenoise.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="WorldManager.cpp" /> <ClCompile Include="WorldManager.cpp" />
<ClCompile Include="..\Minecraft.Client\stdafx.cpp"> <ClCompile Include="..\Minecraft.Client\stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
@ -659,8 +672,20 @@
<ClCompile Include="Windows64\ServerMain.cpp" /> <ClCompile Include="Windows64\ServerMain.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Console\ServerCli.h" />
<ClInclude Include="Console\ServerCliInput.h" />
<ClInclude Include="Console\commands\CliCommandGamemode.h" />
<ClInclude Include="Console\commands\CliCommandHelp.h" />
<ClInclude Include="Console\commands\CliCommandList.h" />
<ClInclude Include="Console\commands\CliCommandStop.h" />
<ClInclude Include="Console\commands\CliCommandTp.h" />
<ClInclude Include="Console\commands\IServerCliCommand.h" />
<ClInclude Include="Console\ServerCliEngine.h" />
<ClInclude Include="Console\ServerCliParser.h" />
<ClInclude Include="Console\ServerCliRegistry.h" />
<ClInclude Include="ServerLogger.h" /> <ClInclude Include="ServerLogger.h" />
<ClInclude Include="ServerProperties.h" /> <ClInclude Include="ServerProperties.h" />
<ClInclude Include="vendor\linenoise\linenoise.h" />
<ClInclude Include="WorldManager.h" /> <ClInclude Include="WorldManager.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -4,6 +4,15 @@
<Filter Include="Server"> <Filter Include="Server">
<UniqueIdentifier>{A8A47C24-66C0-4912-9D34-2CBF87F1D707}</UniqueIdentifier> <UniqueIdentifier>{A8A47C24-66C0-4912-9D34-2CBF87F1D707}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Server\Console">
<UniqueIdentifier>{39B037A0-9B57-454A-AF34-7D9164E22A0F}</UniqueIdentifier>
</Filter>
<Filter Include="Server\Console\Commands">
<UniqueIdentifier>{7C28D123-0DA3-4B17-84C0-E326F5A75740}</UniqueIdentifier>
</Filter>
<Filter Include="Server\Vendor">
<UniqueIdentifier>{3E4D5A41-CAB8-4A10-82B5-8B2AE2E25CB2}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="ServerLogger.cpp"> <ClCompile Include="ServerLogger.cpp">
@ -15,19 +24,584 @@
<ClCompile Include="WorldManager.cpp"> <ClCompile Include="WorldManager.cpp">
<Filter>Server</Filter> <Filter>Server</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Console\ServerCli.cpp">
<Filter>Server\Console</Filter>
</ClCompile>
<ClCompile Include="Console\ServerCliEngine.cpp">
<Filter>Server\Console</Filter>
</ClCompile>
<ClCompile Include="Console\ServerCliParser.cpp">
<Filter>Server\Console</Filter>
</ClCompile>
<ClCompile Include="Console\ServerCliRegistry.cpp">
<Filter>Server\Console</Filter>
</ClCompile>
<ClCompile Include="vendor\linenoise\linenoise.c">
<Filter>Server\Vendor</Filter>
</ClCompile>
<ClCompile Include="Windows64\ServerMain.cpp"> <ClCompile Include="Windows64\ServerMain.cpp">
<Filter>Server</Filter> <Filter>Server</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\Minecraft.Client\AbstractTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\AchievementPopup.cpp" />
<ClCompile Include="..\Minecraft.Client\AchievementScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\AllowAllCuller.cpp" />
<ClCompile Include="..\Minecraft.Client\ArchiveFile.cpp" />
<ClCompile Include="..\Minecraft.Client\ArrowRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BatModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BatRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BeaconRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BlazeModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BlazeRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BoatModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BoatRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\BookModel.cpp" />
<ClCompile Include="..\Minecraft.Client\BossMobGuiInfo.cpp" />
<ClCompile Include="..\Minecraft.Client\BreakingItemParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\BubbleParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\BufferedImage.cpp" />
<ClCompile Include="..\Minecraft.Client\Button.cpp" />
<ClCompile Include="..\Minecraft.Client\Camera.cpp" />
<ClCompile Include="..\Minecraft.Client\CaveSpiderRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ChatScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ChestModel.cpp" />
<ClCompile Include="..\Minecraft.Client\ChestRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ChickenModel.cpp" />
<ClCompile Include="..\Minecraft.Client\ChickenRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Chunk.cpp" />
<ClCompile Include="..\Minecraft.Client\ClientConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\ClientConstants.cpp" />
<ClCompile Include="..\Minecraft.Client\ClockTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Audio\Consoles_SoundEngine.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Audio\SoundEngine.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Audio\SoundNames.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Colours\ColourTable.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\ConsoleGameMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Console_Utils.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Consoles_App.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCAudioFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCCapeFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCColourTableFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCGameRulesFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCGameRulesHeader.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCLocalisationFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCPack.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCSkinFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCTextureFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\DLC\DLCUIDataFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Filesystem\Filesystem.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\AddEnchantmentRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\AddItemRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\ApplySchematicRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\BiomeOverride.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\CollectItemRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\CompleteAllRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\CompoundGameRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\ConsoleGenerateStructure.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\ConsoleSchematicFile.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRule.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRuleManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelGenerationOptions.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelGenerators.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelRules.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelRuleset.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\NamedAreaRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\StartFeature.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\UpdatePlayerRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\UseTileRuleDefinition.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionGenerateBox.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceBlock.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceContainer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceSpawner.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Leaderboards\LeaderboardInterface.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Leaderboards\LeaderboardManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Network\GameNetworkManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Network\PlatformNetworkManagerStub.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Telemetry\TelemetryManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Trial\TrialMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaConstraint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ChangeStateConstraint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ChoiceTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\CompleteUsingItemTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ControllerTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\CraftTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\DiggerItemHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\EffectChangedTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorial.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorialActiveTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorialMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\HorseChoiceTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\InfoTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\InputConstraint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\LookAtEntityHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\LookAtTileHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\PickupTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ProcedureCompoundTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\ProgressFlagTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\RideEntityTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\StatTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TakeItemHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\Tutorial.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialHint.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialMode.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\UseItemTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\UseTileTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\Tutorial\XuiCraftingTask.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_AbstractContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_AnvilMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_BeaconMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_BrewingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CommandBlockMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_ContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CraftingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CreativeMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_DispenserMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_EnchantingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_FireworksMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_FurnaceMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HUD.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HopperMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HorseInventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_InventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_PauseMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_StartGame.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_TradingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIBitmapFont.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Chat.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_DebugUIConsole.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_DebugUIMarketingGuide.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Logo.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_MenuBackground.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Panorama.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_PressStartToPlay.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Tooltips.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_TutorialPopup.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Base.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_BeaconEffectButton.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_BitmapIcon.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Button.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_ButtonList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_CheckBox.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Cursor.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_DLCList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_DynamicLabel.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_EnchantmentBook.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_EnchantmentButton.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_HTMLLabel.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Label.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_LeaderboardList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_MinecraftHorse.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_MinecraftPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_PlayerList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_PlayerSkinPreview.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Progress.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SaveList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Slider.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SlotList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SpaceIndicatorBar.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_TextInput.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_TexturePackList.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIController.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIFontData.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIGroup.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UILayer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_AbstractContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_AnvilMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_BeaconMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_BrewingStandMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ConnectingProgress.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ContainerMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ControlsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CraftingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CreateWorldMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CreativeMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Credits.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DLCMainMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DLCOffersMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DeathMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugCreateSchematic.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugOptions.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugOverlay.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugSetCamera.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DispenserMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EULA.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EnchantingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EndPoem.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FireworksMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FullscreenProgress.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FurnaceMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HUD.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HelpAndOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HopperMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HorseInventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HowToPlay.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HowToPlayMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGameHostOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGameInfoMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGamePlayerOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Intro.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InventoryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_JoinMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Keyboard.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LanguageSelector.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LaunchMoreOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LeaderboardsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LoadMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LoadOrJoinMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_MainMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_MessageBox.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_NewUpdateMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_PauseMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_QuadrantSignin.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ReinstallMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SaveMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsAudioMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsControlMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsGraphicsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsOptionsMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsUIMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SignEntryMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SkinSelectMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TeleportMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Timer.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TradingMenu.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TrialExitUpsell.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UIString.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\UI\UITTFFont.cpp" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\adler32.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\compress.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\crc32.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\deflate.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzclose.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzlib.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzread.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\gzwrite.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\infback.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\inffast.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\inflate.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\inftrees.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\trees.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\uncompr.c" />
<ClCompile Include="..\Minecraft.Client\Common\zlib\zutil.c" />
<ClCompile Include="..\Minecraft.Client\CompassTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\ConfirmScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ConsoleInput.cpp" />
<ClCompile Include="..\Minecraft.Client\ControlsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\CowModel.cpp" />
<ClCompile Include="..\Minecraft.Client\CowRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\CreateWorldScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\CreeperModel.cpp" />
<ClCompile Include="..\Minecraft.Client\CreeperRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\CritParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\CritParticle2.cpp" />
<ClCompile Include="..\Minecraft.Client\Cube.cpp" />
<ClCompile Include="..\Minecraft.Client\DLCTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\DeathScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\DefaultRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\DefaultTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\DemoUser.cpp" />
<ClCompile Include="..\Minecraft.Client\DerivedServerLevel.cpp" />
<ClCompile Include="..\Minecraft.Client\DirtyChunkSorter.cpp" />
<ClCompile Include="..\Minecraft.Client\DispenserBootstrap.cpp" />
<ClCompile Include="..\Minecraft.Client\DistanceChunkSorter.cpp" />
<ClCompile Include="..\Minecraft.Client\DragonBreathParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\DragonModel.cpp" />
<ClCompile Include="..\Minecraft.Client\DripParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\EchantmentTableParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\EditBox.cpp" />
<ClCompile Include="..\Minecraft.Client\EnchantTableRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderChestRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderCrystalModel.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderCrystalRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderDragonRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EnderParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\EndermanModel.cpp" />
<ClCompile Include="..\Minecraft.Client\EndermanRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityRenderDispatcher.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityTileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\EntityTracker.cpp" />
<ClCompile Include="..\Minecraft.Client\ErrorScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ExperienceOrbRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ExplodeParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Extrax64Stubs.cpp" />
<ClCompile Include="..\Minecraft.Client\FallingTileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\FileTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\FireballRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\FireworksParticles.cpp" />
<ClCompile Include="..\Minecraft.Client\FishingHookRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\FlameParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\FolderTexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\Font.cpp" />
<ClCompile Include="..\Minecraft.Client\FootstepParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Frustum.cpp" />
<ClCompile Include="..\Minecraft.Client\FrustumCuller.cpp" />
<ClCompile Include="..\Minecraft.Client\FrustumData.cpp" />
<ClCompile Include="..\Minecraft.Client\GameRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\GhastModel.cpp" />
<ClCompile Include="..\Minecraft.Client\GhastRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\GiantMobRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Gui.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiComponent.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiMessage.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\GuiParticles.cpp" />
<ClCompile Include="..\Minecraft.Client\HeartParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\HorseRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\HttpTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\HugeExplosionParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\HugeExplosionSeedParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\HumanoidMobRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\HumanoidModel.cpp" />
<ClCompile Include="..\Minecraft.Client\InBedChatScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\Input.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemFrameRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemInHandRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\ItemSpriteRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\JoinMultiplayerScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\KeyMapping.cpp" />
<ClCompile Include="..\Minecraft.Client\LargeChestModel.cpp" />
<ClCompile Include="..\Minecraft.Client\LavaParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\LavaSlimeModel.cpp" />
<ClCompile Include="..\Minecraft.Client\LavaSlimeRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LeashKnotModel.cpp" />
<ClCompile Include="..\Minecraft.Client\LeashKnotRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LevelRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Lighting.cpp" />
<ClCompile Include="..\Minecraft.Client\LightningBoltRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LivingEntityRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\LocalPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\MemTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\MemoryTracker.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecartModel.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecartRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecartSpawnerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Minecraft.cpp" />
<ClCompile Include="..\Minecraft.Client\MinecraftServer.cpp" />
<ClCompile Include="..\Minecraft.Client\Minimap.cpp" />
<ClCompile Include="..\Minecraft.Client\MobRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\MobSkinMemTextureProcessor.cpp" />
<ClCompile Include="..\Minecraft.Client\MobSkinTextureProcessor.cpp" />
<ClCompile Include="..\Minecraft.Client\MobSpawnerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Model.cpp" />
<ClCompile Include="..\Minecraft.Client\ModelHorse.cpp" />
<ClCompile Include="..\Minecraft.Client\ModelPart.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerChunkCache.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerGameMode.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerLevel.cpp" />
<ClCompile Include="..\Minecraft.Client\MultiPlayerLocalPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\MushroomCowRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\NameEntryScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\NetherPortalParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\NoteParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\OcelotModel.cpp" />
<ClCompile Include="..\Minecraft.Client\OcelotRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\OffsettedRenderList.cpp" />
<ClCompile Include="..\Minecraft.Client\Options.cpp" />
<ClCompile Include="..\Minecraft.Client\OptionsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.cpp" />
<ClCompile Include="..\Minecraft.Client\PaintingRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Particle.cpp" />
<ClCompile Include="..\Minecraft.Client\ParticleEngine.cpp" />
<ClCompile Include="..\Minecraft.Client\PauseScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\PendingConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\PigModel.cpp" />
<ClCompile Include="..\Minecraft.Client\PigRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\PistonPieceRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerChunkMap.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerCloudParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerList.cpp" />
<ClCompile Include="..\Minecraft.Client\PlayerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Polygon.cpp" />
<ClCompile Include="..\Minecraft.Client\PreStitchedTextureMap.cpp" />
<ClCompile Include="..\Minecraft.Client\ProgressRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\QuadrupedModel.cpp" />
<ClCompile Include="..\Minecraft.Client\Rect2i.cpp" />
<ClCompile Include="..\Minecraft.Client\RedDustParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\RemotePlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\RenameWorldScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\Screen.cpp" />
<ClCompile Include="..\Minecraft.Client\ScreenSizeCalculator.cpp" />
<ClCompile Include="..\Minecraft.Client\ScrolledSelectionList.cpp" />
<ClCompile Include="..\Minecraft.Client\SelectWorldScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerChunkCache.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerCommandDispatcher.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerConnection.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerLevel.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerLevelListener.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerPlayer.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerPlayerGameMode.cpp" />
<ClCompile Include="..\Minecraft.Client\ServerScoreboard.cpp" />
<ClCompile Include="..\Minecraft.Client\Settings.cpp" />
<ClCompile Include="..\Minecraft.Client\SheepFurModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SheepModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SheepRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SignModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SignRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SilverfishModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SilverfishRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SimpleIcon.cpp" />
<ClCompile Include="..\Minecraft.Client\SkeletonHeadModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SkeletonModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SkeletonRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SkiModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SkullTileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SlideButton.cpp" />
<ClCompile Include="..\Minecraft.Client\SlimeModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SlimeRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SmallButton.cpp" />
<ClCompile Include="..\Minecraft.Client\SmokeParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SnowManModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SnowManRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SnowShovelParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SpellParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SpiderModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SpiderRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\SplashParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SquidModel.cpp" />
<ClCompile Include="..\Minecraft.Client\SquidRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\StatsCounter.cpp" />
<ClCompile Include="..\Minecraft.Client\StatsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\StatsSyncher.cpp" />
<ClCompile Include="..\Minecraft.Client\StitchSlot.cpp" />
<ClCompile Include="..\Minecraft.Client\StitchedTexture.cpp" />
<ClCompile Include="..\Minecraft.Client\Stitcher.cpp" />
<ClCompile Include="..\Minecraft.Client\StringTable.cpp" />
<ClCompile Include="..\Minecraft.Client\SuspendedParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\SuspendedTownParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\TakeAnimationParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\TeleportCommand.cpp" />
<ClCompile Include="..\Minecraft.Client\TerrainParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Tesselator.cpp" />
<ClCompile Include="..\Minecraft.Client\TexOffs.cpp" />
<ClCompile Include="..\Minecraft.Client\Texture.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureAtlas.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureHolder.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureManager.cpp" />
<ClCompile Include="..\Minecraft.Client\TextureMap.cpp" />
<ClCompile Include="..\Minecraft.Client\TexturePack.cpp" />
<ClCompile Include="..\Minecraft.Client\TexturePackRepository.cpp" />
<ClCompile Include="..\Minecraft.Client\Textures.cpp" />
<ClCompile Include="..\Minecraft.Client\TheEndPortalRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TileEntityRenderDispatcher.cpp" />
<ClCompile Include="..\Minecraft.Client\TileEntityRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TileRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\Timer.cpp" />
<ClCompile Include="..\Minecraft.Client\TitleScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\TntMinecartRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TntRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\TrackedEntity.cpp" />
<ClCompile Include="..\Minecraft.Client\User.cpp" />
<ClCompile Include="..\Minecraft.Client\Vertex.cpp" />
<ClCompile Include="..\Minecraft.Client\VideoSettingsScreen.cpp" />
<ClCompile Include="..\Minecraft.Client\ViewportCuller.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerGolemModel.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerGolemRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerModel.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\VillagerZombieModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WaterDropParticle.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Iggy\gdraw\gdraw_d3d11.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\KeyboardMouseInput.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Leaderboards\WindowsLeaderboardManager.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\PostProcesser.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Windows64_App.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Windows64_Minecraft.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Windows64_UIController.cpp" />
<ClCompile Include="..\Minecraft.Client\Windows64\Network\WinsockNetLayer.cpp" />
<ClCompile Include="..\Minecraft.Client\WitchModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WitchRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WitherBossModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WitherBossRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WitherSkullRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WolfModel.cpp" />
<ClCompile Include="..\Minecraft.Client\WolfRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\WstringLookup.cpp" />
<ClCompile Include="..\Minecraft.Client\Xbox\Network\NetworkPlayerXbox.cpp" />
<ClCompile Include="..\Minecraft.Client\ZombieModel.cpp" />
<ClCompile Include="..\Minecraft.Client\ZombieRenderer.cpp" />
<ClCompile Include="..\Minecraft.Client\compat_shims.cpp" />
<ClCompile Include="Console\ServerCliInput.cpp" />
<ClCompile Include="..\Minecraft.Client\glWrapper.cpp" />
<ClCompile Include="..\Minecraft.Client\stdafx.cpp" />
<ClCompile Include="..\Minecraft.Client\stubs.cpp" />
<ClCompile Include="Console\commands\CliCommandGamemode.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\CliCommandHelp.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\CliCommandList.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\CliCommandStop.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
<ClCompile Include="Console\commands\CliCommandTp.cpp">
<Filter>Server\Console\Commands</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Console\ServerCli.h">
<Filter>Server\Console</Filter>
</ClInclude>
<ClInclude Include="Console\ServerCliEngine.h">
<Filter>Server\Console</Filter>
</ClInclude>
<ClInclude Include="Console\ServerCliParser.h">
<Filter>Server\Console</Filter>
</ClInclude>
<ClInclude Include="Console\ServerCliRegistry.h">
<Filter>Server\Console</Filter>
</ClInclude>
<ClInclude Include="ServerLogger.h"> <ClInclude Include="ServerLogger.h">
<Filter>Server</Filter> <Filter>Server</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="ServerProperties.h"> <ClInclude Include="ServerProperties.h">
<Filter>Server</Filter> <Filter>Server</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="vendor\linenoise\linenoise.h">
<Filter>Server\Vendor</Filter>
</ClInclude>
<ClInclude Include="WorldManager.h"> <ClInclude Include="WorldManager.h">
<Filter>Server</Filter> <Filter>Server</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Console\ServerCliInput.h" />
<ClInclude Include="Console\commands\CliCommandGamemode.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\CliCommandHelp.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\CliCommandList.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\CliCommandStop.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\CliCommandTp.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
<ClInclude Include="Console\commands\IServerCliCommand.h">
<Filter>Server\Console\Commands</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> <ItemGroup>
<ResourceCompile Include="..\Minecraft.Client\Xbox\MinecraftWindows.rc" />
</ItemGroup>
<ItemGroup>
<MASM Include="..\Minecraft.Client\iob_shim.asm" />
</ItemGroup>
</Project>

View file

@ -1,6 +1,7 @@
#include "stdafx.h" #include "stdafx.h"
#include "ServerLogger.h" #include "ServerLogger.h"
#include "vendor\\linenoise\\linenoise.h"
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
@ -86,6 +87,8 @@ static void WriteLogLine(EServerLogLevel level, const char *category, const char
return; return;
} }
linenoiseExternalWriteBegin();
const char *safeCategory = NormalizeCategory(category); const char *safeCategory = NormalizeCategory(category);
const char *safeMessage = (message != NULL) ? message : ""; const char *safeMessage = (message != NULL) ? message : "";
@ -116,6 +119,8 @@ static void WriteLogLine(EServerLogLevel level, const char *category, const char
{ {
SetConsoleTextAttribute(stdoutHandle, originalInfo.wAttributes); SetConsoleTextAttribute(stdoutHandle, originalInfo.wAttributes);
} }
linenoiseExternalWriteEnd();
} }
static void WriteLogLineV(EServerLogLevel level, const char *category, const char *format, va_list args) static void WriteLogLineV(EServerLogLevel level, const char *category, const char *format, va_list args)

View file

@ -8,6 +8,7 @@
#include "..\ServerLogger.h" #include "..\ServerLogger.h"
#include "..\ServerProperties.h" #include "..\ServerProperties.h"
#include "..\WorldManager.h" #include "..\WorldManager.h"
#include "..\Console\ServerCli.h"
#include "Tesselator.h" #include "Tesselator.h"
#include "Windows64/4JLibs/inc/4J_Render.h" #include "Windows64/4JLibs/inc/4J_Render.h"
#include "Windows64/GameConfig/Minecraft.spa.h" #include "Windows64/GameConfig/Minecraft.spa.h"
@ -536,11 +537,14 @@ int main(int argc, char **argv)
} }
DWORD nextAutosaveTick = GetTickCount() + autosaveIntervalMs; DWORD nextAutosaveTick = GetTickCount() + autosaveIntervalMs;
bool autosaveRequested = false; bool autosaveRequested = false;
ServerRuntime::ServerCli serverCli;
serverCli.Start();
while (!g_shutdownRequested && !app.m_bShutdown) while (!g_shutdownRequested && !app.m_bShutdown)
{ {
TickCoreSystems(); TickCoreSystems();
HandleXuiActions(); HandleXuiActions();
serverCli.Poll();
if (autosaveRequested && app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle) if (autosaveRequested && app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle)
{ {
@ -567,6 +571,7 @@ int main(int argc, char **argv)
Sleep(10); Sleep(10);
} }
serverCli.Stop();
LogStartupStep("stopping dedicated server"); LogStartupStep("stopping dedicated server");
MinecraftServer *server = MinecraftServer::getInstance(); MinecraftServer *server = MinecraftServer::getInstance();

View file

@ -0,0 +1,25 @@
This vendored component is based on the linenoise project idea/API.
Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,542 @@
#include "linenoise.h"
#include <conio.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#define LINENOISE_MAX_LINE 4096
#define LINENOISE_MAX_PROMPT 128
typedef struct linenoiseHistory {
char **items;
int len;
int cap;
int maxLen;
} linenoiseHistory;
static linenoiseCompletionCallback *g_completionCallback = NULL;
static volatile LONG g_stopRequested = 0;
static linenoiseHistory g_history = { NULL, 0, 0, 128 };
/* Guards redraw/log interleaving so prompt and log lines do not overlap. */
static CRITICAL_SECTION g_ioLock;
static volatile LONG g_ioLockState = 0; /* 0=not init, 1=init in progress, 2=ready */
/* Snapshot of current editor line used to restore prompt after external output. */
static volatile LONG g_editorActive = 0;
static char g_editorPrompt[LINENOISE_MAX_PROMPT] = { 0 };
static char g_editorBuf[LINENOISE_MAX_LINE] = { 0 };
static int g_editorLen = 0;
static int g_editorPos = 0;
static int g_editorPrevLen = 0;
/**
* Lazily initialize the console I/O critical section.
* This avoids static init order issues and keeps startup cost minimal.
*/
static void linenoiseEnsureIoLockInit(void)
{
LONG state = InterlockedCompareExchange(&g_ioLockState, 0, 0);
if (state == 2)
return;
if (state == 0 && InterlockedCompareExchange(&g_ioLockState, 1, 0) == 0)
{
InitializeCriticalSection(&g_ioLock);
InterlockedExchange(&g_ioLockState, 2);
return;
}
while (InterlockedCompareExchange(&g_ioLockState, 0, 0) != 2)
{
Sleep(0);
}
}
static void linenoiseLockIo(void)
{
linenoiseEnsureIoLockInit();
EnterCriticalSection(&g_ioLock);
}
static void linenoiseUnlockIo(void)
{
LeaveCriticalSection(&g_ioLock);
}
/**
* Save current prompt/buffer/cursor state for later redraw.
* Called after each redraw while editor is active.
*/
static void linenoiseUpdateEditorState(const char *prompt, const char *buf, int len, int pos, int prevLen)
{
if (prompt == NULL)
prompt = "";
if (buf == NULL)
buf = "";
strncpy_s(g_editorPrompt, sizeof(g_editorPrompt), prompt, _TRUNCATE);
strncpy_s(g_editorBuf, sizeof(g_editorBuf), buf, _TRUNCATE);
g_editorLen = len;
g_editorPos = pos;
g_editorPrevLen = prevLen;
InterlockedExchange(&g_editorActive, 1);
}
static void linenoiseDeactivateEditorState(void)
{
InterlockedExchange(&g_editorActive, 0);
g_editorPrompt[0] = 0;
g_editorBuf[0] = 0;
g_editorLen = 0;
g_editorPos = 0;
g_editorPrevLen = 0;
}
static char *linenoiseStrdup(const char *src)
{
size_t n = strlen(src) + 1;
char *out = (char *)malloc(n);
if (out == NULL)
return NULL;
memcpy(out, src, n);
return out;
}
static void linenoiseEnsureHistoryCapacity(int wanted)
{
if (wanted <= g_history.cap)
return;
int newCap = g_history.cap == 0 ? 32 : g_history.cap;
while (newCap < wanted)
newCap *= 2;
char **newItems = (char **)realloc(g_history.items, sizeof(char *) * (size_t)newCap);
if (newItems == NULL)
return;
g_history.items = newItems;
g_history.cap = newCap;
}
static void linenoiseClearCompletions(linenoiseCompletions *lc)
{
size_t i = 0;
for (i = 0; i < lc->len; ++i)
{
free(lc->cvec[i]);
}
free(lc->cvec);
lc->cvec = NULL;
lc->len = 0;
}
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str)
{
char **newVec = (char **)realloc(lc->cvec, sizeof(char *) * (lc->len + 1));
if (newVec == NULL)
return;
lc->cvec = newVec;
lc->cvec[lc->len] = linenoiseStrdup(str);
if (lc->cvec[lc->len] == NULL)
return;
lc->len += 1;
}
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn)
{
g_completionCallback = fn;
}
void linenoiseFree(void *ptr)
{
free(ptr);
}
int linenoiseHistorySetMaxLen(int len)
{
if (len <= 0)
return 0;
g_history.maxLen = len;
while (g_history.len > g_history.maxLen)
{
free(g_history.items[0]);
memmove(g_history.items, g_history.items + 1, sizeof(char *) * (size_t)(g_history.len - 1));
g_history.len -= 1;
}
return 1;
}
int linenoiseHistoryAdd(const char *line)
{
if (line == NULL || line[0] == 0)
return 0;
if (g_history.len > 0)
{
const char *last = g_history.items[g_history.len - 1];
if (last != NULL && strcmp(last, line) == 0)
return 1;
}
linenoiseEnsureHistoryCapacity(g_history.len + 1);
if (g_history.cap <= g_history.len)
return 0;
g_history.items[g_history.len] = linenoiseStrdup(line);
if (g_history.items[g_history.len] == NULL)
return 0;
g_history.len += 1;
while (g_history.len > g_history.maxLen)
{
free(g_history.items[0]);
memmove(g_history.items, g_history.items + 1, sizeof(char *) * (size_t)(g_history.len - 1));
g_history.len -= 1;
}
return 1;
}
void linenoiseRequestStop(void)
{
InterlockedExchange(&g_stopRequested, 1);
}
void linenoiseResetStop(void)
{
InterlockedExchange(&g_stopRequested, 0);
}
static int linenoiseIsStopRequested(void)
{
return InterlockedCompareExchange(&g_stopRequested, 0, 0) != 0;
}
static void linenoiseRedrawUnsafe(const char *prompt, const char *buf, int len, int pos, int *prevLen)
{
int i;
fputc('\r', stdout);
fputs(prompt, stdout);
if (len > 0)
{
fwrite(buf, 1, (size_t)len, stdout);
}
if (*prevLen > len)
{
for (i = len; i < *prevLen; ++i)
fputc(' ', stdout);
}
fputc('\r', stdout);
fputs(prompt, stdout);
if (pos > 0)
{
fwrite(buf, 1, (size_t)pos, stdout);
}
fflush(stdout);
*prevLen = len;
linenoiseUpdateEditorState(prompt, buf, len, pos, *prevLen);
}
static void linenoiseRedraw(const char *prompt, const char *buf, int len, int pos, int *prevLen)
{
linenoiseLockIo();
linenoiseRedrawUnsafe(prompt, buf, len, pos, prevLen);
linenoiseUnlockIo();
}
static int linenoiseStartsWith(const char *full, const char *prefix)
{
while (*prefix != 0)
{
if (*full != *prefix)
return 0;
++full;
++prefix;
}
return 1;
}
static int linenoiseComputeCommonPrefix(const linenoiseCompletions *lc, const char *seed, char *out, size_t outSize)
{
size_t commonLen = 0;
size_t i;
if (lc->len == 0 || outSize == 0)
return 0;
strncpy_s(out, outSize, lc->cvec[0], _TRUNCATE);
commonLen = strlen(out);
for (i = 1; i < lc->len; ++i)
{
const char *candidate = lc->cvec[i];
size_t j = 0;
while (j < commonLen && out[j] != 0 && candidate[j] != 0 && out[j] == candidate[j])
++j;
commonLen = j;
out[commonLen] = 0;
if (commonLen == 0)
break;
}
if (strlen(out) <= strlen(seed))
return 0;
return linenoiseStartsWith(out, seed);
}
static void linenoiseApplyCompletion(const char *prompt, char *buf, int *len, int *pos, int *prevLen)
{
linenoiseCompletions lc;
int i;
if (g_completionCallback == NULL)
{
Beep(750, 15);
return;
}
lc.len = 0;
lc.cvec = NULL;
g_completionCallback(buf, &lc);
if (lc.len == 0)
{
Beep(750, 15);
linenoiseClearCompletions(&lc);
return;
}
if (lc.len == 1)
{
strncpy_s(buf, LINENOISE_MAX_LINE, lc.cvec[0], _TRUNCATE);
*len = (int)strlen(buf);
*pos = *len;
linenoiseRedraw(prompt, buf, *len, *pos, prevLen);
linenoiseClearCompletions(&lc);
return;
}
{
char common[LINENOISE_MAX_LINE] = { 0 };
if (linenoiseComputeCommonPrefix(&lc, buf, common, sizeof(common)))
{
strncpy_s(buf, LINENOISE_MAX_LINE, common, _TRUNCATE);
*len = (int)strlen(buf);
*pos = *len;
linenoiseRedraw(prompt, buf, *len, *pos, prevLen);
}
}
linenoiseLockIo();
fputc('\n', stdout);
for (i = 0; i < (int)lc.len; ++i)
{
fputs(lc.cvec[i], stdout);
fputs(" ", stdout);
}
fputc('\n', stdout);
linenoiseRedrawUnsafe(prompt, buf, *len, *pos, prevLen);
linenoiseUnlockIo();
linenoiseClearCompletions(&lc);
}
char *linenoise(const char *prompt)
{
char buf[LINENOISE_MAX_LINE];
int len = 0;
int pos = 0;
int prevLen = 0;
int historyIndex = g_history.len;
if (prompt == NULL)
prompt = "";
buf[0] = 0;
linenoiseLockIo();
linenoiseUpdateEditorState(prompt, buf, len, pos, prevLen);
fputs(prompt, stdout);
fflush(stdout);
linenoiseUnlockIo();
while (!linenoiseIsStopRequested())
{
if (!_kbhit())
{
Sleep(10);
continue;
}
{
int c = _getwch();
if (c == 0 || c == 224)
{
int ext = _getwch();
if (ext == 72)
{
if (g_history.len > 0 && historyIndex > 0)
{
historyIndex -= 1;
strncpy_s(buf, sizeof(buf), g_history.items[historyIndex], _TRUNCATE);
len = (int)strlen(buf);
pos = len;
linenoiseRedraw(prompt, buf, len, pos, &prevLen);
}
}
else if (ext == 80)
{
if (g_history.len > 0 && historyIndex < g_history.len)
{
historyIndex += 1;
if (historyIndex == g_history.len)
buf[0] = 0;
else
strncpy_s(buf, sizeof(buf), g_history.items[historyIndex], _TRUNCATE);
len = (int)strlen(buf);
pos = len;
linenoiseRedraw(prompt, buf, len, pos, &prevLen);
}
}
else if (ext == 75)
{
if (pos > 0)
{
pos -= 1;
linenoiseRedraw(prompt, buf, len, pos, &prevLen);
}
}
else if (ext == 77)
{
if (pos < len)
{
pos += 1;
linenoiseRedraw(prompt, buf, len, pos, &prevLen);
}
}
continue;
}
if (c == 3)
{
linenoiseLockIo();
linenoiseDeactivateEditorState();
fputc('\n', stdout);
fflush(stdout);
linenoiseUnlockIo();
return NULL;
}
if (c == '\r' || c == '\n')
{
char *out;
linenoiseLockIo();
linenoiseDeactivateEditorState();
fputc('\n', stdout);
fflush(stdout);
linenoiseUnlockIo();
out = linenoiseStrdup(buf);
return out;
}
if (c == '\t')
{
linenoiseApplyCompletion(prompt, buf, &len, &pos, &prevLen);
continue;
}
if (c == 8)
{
if (pos > 0 && len > 0)
{
memmove(buf + pos - 1, buf + pos, (size_t)(len - pos + 1));
pos -= 1;
len -= 1;
linenoiseRedraw(prompt, buf, len, pos, &prevLen);
}
continue;
}
if (isprint((unsigned char)c) && len < LINENOISE_MAX_LINE - 1)
{
if (pos == len)
{
buf[pos++] = (char)c;
len += 1;
buf[len] = 0;
}
else
{
memmove(buf + pos + 1, buf + pos, (size_t)(len - pos + 1));
buf[pos] = (char)c;
pos += 1;
len += 1;
}
linenoiseRedraw(prompt, buf, len, pos, &prevLen);
}
}
}
linenoiseLockIo();
linenoiseDeactivateEditorState();
fputc('\n', stdout);
fflush(stdout);
linenoiseUnlockIo();
return NULL;
}
void linenoiseExternalWriteBegin(void)
{
int i;
int totalChars = 0;
/* Lock shared console state and clear current prompt area before external output. */
linenoiseLockIo();
if (InterlockedCompareExchange(&g_editorActive, 0, 0) == 0)
{
return;
}
totalChars = (int)strlen(g_editorPrompt) + g_editorPrevLen;
if (totalChars < 0)
{
totalChars = 0;
}
fputc('\r', stdout);
for (i = 0; i < totalChars; ++i)
{
fputc(' ', stdout);
}
fputc('\r', stdout);
fflush(stdout);
}
void linenoiseExternalWriteEnd(void)
{
/* Restore prompt line after external output has been printed. */
if (InterlockedCompareExchange(&g_editorActive, 0, 0) != 0)
{
int prevLen = g_editorPrevLen;
linenoiseRedrawUnsafe(g_editorPrompt, g_editorBuf, g_editorLen, g_editorPos, &prevLen);
g_editorPrevLen = prevLen;
}
linenoiseUnlockIo();
}

View file

@ -0,0 +1,37 @@
#ifndef VENDORED_LINENOISE_H
#define VENDORED_LINENOISE_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
typedef void(linenoiseCompletionCallback)(const char *buf, linenoiseCompletions *lc);
char *linenoise(const char *prompt);
void linenoiseFree(void *ptr);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn);
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str);
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
void linenoiseRequestStop(void);
void linenoiseResetStop(void);
/* Wrap external stdout/stderr writes so active prompt can be cleared/restored safely. */
void linenoiseExternalWriteBegin(void);
void linenoiseExternalWriteEnd(void);
#ifdef __cplusplus
}
#endif
#endif