Dedicated Server Software - Minecraft.Server.exe (#498)

* add: Dedicated Server implementation

- Introduced `ServerMain.cpp` for the dedicated server logic, handling command-line arguments, server initialization, and network management.
- Created `postbuild_server.ps1` script for post-build tasks, including copying necessary resources and DLLs for the dedicated server.
- Added `CopyServerAssets.cmake` to manage the copying of server assets during the build process, ensuring required files are available for the dedicated server.
- Defined project filters in `Minecraft.Server.vcxproj.filters` for better organization of server-related files.

* add: refactor world loader & add server properties

- Introduced ServerLogger for logging startup steps and world I/O operations.
- Implemented ServerProperties for loading and saving server configuration from `server.properties`.
- Added WorldManager to handle world loading and creation based on server properties.
- Updated ServerMain to integrate server properties loading and world management.
- Enhanced project files to include new source and header files for the server components.

* update: implement enhanced logging functionality with configurable log levels

* update: update keyboard and mouse input initialization 1dc8a005ed

* fix: change virtual screen resolution to 1920x1080(HD)

Since 31881af56936aeef38ff322b975fd0 , `skinHud.swf` for 720 is not included in `MediaWindows64.arc`,
the app crashes unless the virtual screen is set to HD.

* fix: dedicated server build settings for miniaudio migration and missing sources

- remove stale Windows64 Miles (mss64) link/copy references from server build
- add Common/Filesystem/Filesystem.cpp to Minecraft.Server.vcxproj
- add Windows64/PostProcesser.cpp to Minecraft.Server.vcxproj
- fix unresolved externals (PostProcesser::*, FileExists) in dedicated server build

* update: changed the virtual screen to 720p

Since the crash caused by the 720p `skinHud.swf` not being included in `MediaWindows64.arc` has been resolved, switching back to 720p to reduce resource usage.

* add: add Docker support for Dedicated Server

add with entrypoint and build scripts

* fix: add initial save for newly created worlds in dedicated server

on the server side, I fixed the behavior introduced after commit aadb511, where newly created worlds are intentionally not saved to disk immediately.

* update: add basically all configuration options that are implemented in the classes to `server.properties`

* update: add LAN advertising configuration for server.properties

LAN-Discovery, which isn’t needed in server mode and could potentially be a security risk, has also been disabled(only server mode).

* 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.

* update: enhance command line completion with predictive hints

Like most command line tools, it highlights predictions in gray.

* add: implement `StringUtils` for string manipulation and refactor usages

Unified the scattered utility functions.

* fix: send DisconnectPacket on shutdown and fix Win64 recv-thread teardown race

Before this change, server/host shutdown closed sockets directly in
ServerConnection::stop(), which bypassed the normal disconnect flow.
As a result, clients could be dropped without receiving a proper
DisconnectPacket during stop/kill/world-close paths.

Also, WinsockNetLayer::Shutdown() could destroy synchronization objects
while host-side recv threads were still exiting, causing a crash in
RecvThreadProc (access violation on world close in host mode).

* fix: return client to menus when Win64 host connection drops

- Add client-side host disconnect handling in CPlatformNetworkManagerStub::DoWork() for _WINDOWS64.
- When in QNET_STATE_GAME_PLAY as a non-host and WinsockNetLayer::IsConnected() becomes false, trigger g_NetworkManager.HandleDisconnect(false) to enter the normal disconnect/UI flow.
- Use m_bLeaveGameOnTick as a one-shot guard to prevent repeated disconnect handling while the link remains down.
- Reset m_bLeaveGameOnTick on LeaveGame(), HostGame(), and JoinGame() to avoid stale state across sessions.

* update: converted Japanese comments to English

* add: create `Minecraft.Server` developer guide in English and Japanese

* update: add note about issue

* add: add `nlohmann/json` json lib

* add: add FileUtils

Moved file operations to `utils`.

* add: Dedicated Server BAN access manager with persistent player and IP bans

- add Access frontend that publishes thread-safe ban manager snapshots for dedicated server use
- add BanManager storage for banned-players.json and banned-ips.json with load/save/update flows
- add persistent player and IP ban checks during dedicated server connection handling
- add UTF-8 BOM-safe JSON parsing and shared file helpers backed by nlohmann/json
- add Unicode-safe ban file read/write and safer atomic replacement behavior on Windows
- add active-ban snapshot APIs and expiry-aware filtering for expires metadata
- add RAII-based dedicated access shutdown handling during server startup and teardown

* update: changed file read/write operations to use `FileUtils`.

- As a side effect, saving has become faster!

* fix: Re-added the source that had somehow disappeared.

* add: significantly improved the dedicated server logging system

- add ServerLogManager to Minecraft.Server as the single entry point for dedicated-server log output
- forward CMinecraftApp logger output to the server logger when running with g_Win64DedicatedServer
- add named network logs for incoming, accepted, rejected, and disconnected connections
- cache connection metadata by smallId so player name and remote IP remain available for disconnect logs
- keep Minecraft.Client changes minimal by using lightweight hook points and handling log orchestration on the server side

* fix: added the updated library source

* add: add `ban` and `pardon` commands for Player and IP

* fix: fix stop command shutdown process

add dedicated server shutdown request handling

* fix: fixed the save logic during server shutdown

Removed redundant repeated saves and eliminated the risks of async writes.

* update: added new sever files to Docker entrypoint

* fix: replace shutdown flag with atomic variable for thread safety

* update: update Dedicated Server developer guide

English is machine translated.
Please forgive me.

* update: check for the existence of `GameHDD` and create

* add: add Whitelist to Dedicated Server

* refactor: clean up and refactor the code

- unify duplicated implementations that were copied repeatedly
- update outdated patterns to more modern ones

* fix: include UI header (new update fix)

* fix: fix the detection range for excessive logging

`getHighestNonEmptyY()` returning `-1` occurs normally when the chunk is entirely air.
The caller (`Minecraft.World/LevelChunk.cpp:2400`) normalizes `-1` to `0`.

* update: add world size config to dedicated server properties

* update: update README add explanation of  `server.properties` & launch arguments

* update: add nightly release workflow for dedicated server and client builds to Actions

* fix: update name for workflow

* add random seed generation

* add: add Docker nightly workflow for Dedicated Server publish to GitHub Container Registry

* fix: ghost player when clients disconnect out of order

#4

* fix: fix 7zip option

* fix: fix Docker workflow for Dedicated Server artifact handling

* add: add no build Dedicated Server startup scripts and Docker Compose

* update: add README for Docker Dedicated Server setup with no local build

* refactor: refactor command path structure

As the number of commands has increased and become harder to navigate, each command has been organized into separate folders.

* update: support stream(file stdin) input mode for server CLI

Support for the stream (file stdin) required when attaching a tty to a Docker container on Linux.

* add: add new CLI Console Commands for Dedicated Server

Most of these commands are executed using the command dispatcher implemented on the `Minecraft.World` side. When registering them with the dispatcher, the sender uses a permission-enabled configuration that treats the CLI as a player.

- default game.
- enchant
- experience.
- give
- kill(currently, getting a permission error for some reason)
- time
- weather.
- update tp & gamemode command

* fix: change player map icon to random select

* update: increase the player limit

* add: restore the basic anti-cheat implementation and add spawn protection

Added the following anti-cheat measures and add spawn protection to `server.properties`.
- instant break
- speed
- reach

* fix: fix Docker image tag

---------

Co-authored-by: sylvessa <225480449+sylvessa@users.noreply.github.com>
This commit is contained in:
kuwa
2026-03-15 16:32:50 +09:00
committed by GitHub
parent 4d200a589d
commit f483074cd2
110 changed files with 38957 additions and 147 deletions

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,649 @@
#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 linenoiseWriteHint(const char *hint, size_t hintLen)
{
HANDLE stdoutHandle;
CONSOLE_SCREEN_BUFFER_INFO originalInfo;
int hasColorConsole = 0;
if (hint == NULL || hintLen == 0)
{
return;
}
stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
if (stdoutHandle != INVALID_HANDLE_VALUE && stdoutHandle != NULL)
{
if (GetConsoleScreenBufferInfo(stdoutHandle, &originalInfo))
{
hasColorConsole = 1;
/* Draw predictive tail in dim gray, then restore original console colors. */
SetConsoleTextAttribute(stdoutHandle, FOREGROUND_INTENSITY);
}
}
fwrite(hint, 1, hintLen, stdout);
if (hasColorConsole)
{
SetConsoleTextAttribute(stdoutHandle, originalInfo.wAttributes);
}
}
static int linenoiseStartsWithIgnoreCase(const char *full, const char *prefix)
{
while (*prefix != 0)
{
if (tolower((unsigned char)*full) != tolower((unsigned char)*prefix))
{
return 0;
}
++full;
++prefix;
}
return 1;
}
static size_t linenoiseBuildHint(const char *buf, char *hint, size_t hintSize)
{
linenoiseCompletions lc;
size_t inputLen = 0;
size_t i = 0;
if (hint == NULL || hintSize == 0)
{
return 0;
}
hint[0] = 0;
if (buf == NULL || buf[0] == 0 || g_completionCallback == NULL)
{
return 0;
}
lc.len = 0;
lc.cvec = NULL;
/* Reuse the completion callback and derive a "ghost text" suffix from the first extending match. */
g_completionCallback(buf, &lc);
inputLen = strlen(buf);
for (i = 0; i < lc.len; ++i)
{
const char *candidate = lc.cvec[i];
if (candidate == NULL)
{
continue;
}
if (strlen(candidate) <= inputLen)
{
continue;
}
if (!linenoiseStartsWithIgnoreCase(candidate, buf))
{
continue;
}
/* Keep only the part not yet typed by the user (rendered as hint text). */
strncpy_s(hint, hintSize, candidate + inputLen, _TRUNCATE);
break;
}
linenoiseClearCompletions(&lc);
return strlen(hint);
}
static void linenoiseRedrawUnsafe(const char *prompt, const char *buf, int len, int pos, int *prevLen)
{
int i;
char hint[LINENOISE_MAX_LINE] = {0};
int renderedLen = len;
/* Hint length contributes to the rendered width so stale tail characters can be cleared correctly. */
int hintLen = (int)linenoiseBuildHint(buf, hint, sizeof(hint));
if (hintLen > 0)
{
renderedLen += hintLen;
}
fputc('\r', stdout);
fputs(prompt, stdout);
if (len > 0)
{
fwrite(buf, 1, (size_t)len, stdout);
}
if (hintLen > 0)
{
linenoiseWriteHint(hint, (size_t)hintLen);
}
if (*prevLen > renderedLen)
{
for (i = renderedLen; i < *prevLen; ++i)
{
fputc(' ', stdout);
}
}
fputc('\r', stdout);
fputs(prompt, stdout);
if (pos > 0)
{
/* Cursor positioning reflects only real user input, not the ghost hint suffix. */
fwrite(buf, 1, (size_t)pos, stdout);
}
fflush(stdout);
*prevLen = renderedLen;
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

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2025 Niels Lohmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.

25526
Minecraft.Server/vendor/nlohmann/json.hpp vendored Normal file

File diff suppressed because it is too large Load Diff