Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c23e3e54c |
@@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: ❗NEW❗ MinecraftConsoles Community Discord
|
||||
url: https://discord.gg/dH8AZWGcau
|
||||
- name: MinecraftConsoles Community Discord
|
||||
url: https://discord.gg/jrum7HhegA
|
||||
about: If you need help, please ask for it in our Discord! You will get assistance much faster there, including help getting the project to compile.
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
# Collect files, excluding unwanted extensions
|
||||
$files = Get-ChildItem -Path $source -Recurse -File |
|
||||
Where-Object { $_.Extension -notin '.pch', '.pdb', '.zip', '.ipdb', '.iobj', '.exp', '.lib' }
|
||||
Where-Object { $_.Extension -notin '.pch', '.zip', '.ipdb', '.iobj' }
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
@@ -80,7 +80,6 @@ jobs:
|
||||
New-Item -ItemType Directory -Force -Path staging
|
||||
Copy-Item LCE-Revelations-Client-Win64.zip staging/
|
||||
Copy-Item ./build/windows64/Minecraft.Client/Release/Minecraft.Client.exe staging/
|
||||
Copy-Item ./build/windows64/Minecraft.Client/Release/Minecraft.Client.pdb staging/
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
@@ -124,7 +123,7 @@ jobs:
|
||||
$topLevel = "LCE-Revelations-Server-Win64"
|
||||
|
||||
$files = Get-ChildItem -Path $source -Recurse -File |
|
||||
Where-Object { $_.Extension -notin '.pch', '.zip', '.ipdb', '.iobj', '.exp', '.lib' }
|
||||
Where-Object { $_.Extension -notin '.pch', '.zip', '.ipdb', '.iobj' }
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
@@ -159,7 +158,7 @@ jobs:
|
||||
$topLevel = "LCE-Revelations-Server-Win64-FourKit"
|
||||
|
||||
$files = Get-ChildItem -Path $source -Recurse -File |
|
||||
Where-Object { $_.Extension -notin '.pch', '.zip', '.ipdb', '.iobj', '.exp', '.lib' }
|
||||
Where-Object { $_.Extension -notin '.pch', '.zip', '.ipdb', '.iobj' }
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
@@ -284,7 +283,6 @@ jobs:
|
||||
subject-path: |
|
||||
artifacts/LCE-Revelations-Client-Win64.zip
|
||||
artifacts/Minecraft.Client.exe
|
||||
artifacts/Minecraft.Client.pdb
|
||||
|
||||
- name: Get short SHA
|
||||
id: sha
|
||||
@@ -323,10 +321,7 @@ jobs:
|
||||
- To play, simply run `Minecraft.Client.exe`.
|
||||
|
||||
**For those that wish to update their existing installation with the latest build:**
|
||||
- Download `Minecraft.Client.exe` and copy it over to your existing LCE-Revelations-Client-Win64 build (overwrite your old version of Minecraft.Client.exe).
|
||||
|
||||
**For developers:**
|
||||
- `Minecraft.Client.pdb` contains debug symbols for crash analysis and development. Place it next to `Minecraft.Client.exe` for stack traces to show function names and line numbers.
|
||||
- Download `Minecraft.Client.exe` and `Minecraft.Client.pdb` and copy them over to your existing LCE-Revelations-Client-Win64 build (overwrite your old version of Minecraft.Client.exe and Minecraft.Client.pdb).
|
||||
|
||||
**Steam Deck & Linux:**
|
||||
- Y'all know the drill. Download the `LCE-Revelations-Client-Win64.zip`, extract it, add the `Minecraft.Client.exe` as a "Non-Steam Game" within the Steam library, turn on compatibility mode with Proton Experimental, and then run it!
|
||||
|
||||
@@ -429,6 +429,3 @@ result-*
|
||||
.direnv/
|
||||
.xwin-cache/
|
||||
.xwin/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
+2
-2
@@ -39,10 +39,10 @@ function(configure_compiler_target target)
|
||||
# MSVC
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_compile_options(${target} PRIVATE
|
||||
$<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:C,CXX>>:/GL /Zi>
|
||||
$<$<AND:$<CONFIG:Release>,$<COMPILE_LANGUAGE:C,CXX>>:/GL>
|
||||
)
|
||||
target_link_options(${target} PRIVATE
|
||||
$<$<CONFIG:Release>:/LTCG:incremental /DEBUG /OPT:REF /OPT:ICF>
|
||||
$<$<CONFIG:Release>:/LTCG:incremental>
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -82,6 +82,8 @@ set(ASSET_FOLDER_PAIRS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/music" "music"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Media" "Common/Media"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Common/res" "Common/res"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Trial" "Common/Trial"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Tutorial" "Common/Tutorial"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}Media" "${PLATFORM_NAME}Media"
|
||||
)
|
||||
setup_asset_folder_copy(Minecraft.Client "${ASSET_FOLDER_PAIRS}")
|
||||
|
||||
@@ -12,7 +12,6 @@ const wstring ChatScreen::allowedChars = SharedConstants::acceptableLetters;
|
||||
vector<wstring> ChatScreen::s_chatHistory;
|
||||
int ChatScreen::s_historyIndex = -1;
|
||||
wstring ChatScreen::s_historyDraft;
|
||||
int ChatScreen::s_chatIndex = 0;
|
||||
|
||||
bool ChatScreen::isAllowedChatChar(wchar_t c)
|
||||
{
|
||||
@@ -29,8 +28,6 @@ ChatScreen::ChatScreen()
|
||||
frame = 0;
|
||||
cursorIndex = 0;
|
||||
s_historyIndex = -1;
|
||||
|
||||
ChatScreen::s_chatIndex = 0;
|
||||
}
|
||||
|
||||
void ChatScreen::init()
|
||||
@@ -92,20 +89,6 @@ void ChatScreen::handleHistoryDown()
|
||||
applyHistoryMessage();
|
||||
}
|
||||
|
||||
int ChatScreen::getChatIndex()
|
||||
{
|
||||
return ChatScreen::s_chatIndex;
|
||||
}
|
||||
|
||||
void ChatScreen::correctChatIndex(int newChatIndex) {
|
||||
ChatScreen::s_chatIndex = newChatIndex;
|
||||
}
|
||||
|
||||
void ChatScreen::setWheelValue(int wheel) {
|
||||
ChatScreen::s_chatIndex += wheel;
|
||||
if (ChatScreen::s_chatIndex < 0) ChatScreen::s_chatIndex = 0;
|
||||
}
|
||||
|
||||
void ChatScreen::keyPressed(wchar_t ch, int eventKey)
|
||||
{
|
||||
if (eventKey == Keyboard::KEY_ESCAPE)
|
||||
@@ -157,7 +140,7 @@ void ChatScreen::keyPressed(wchar_t ch, int eventKey)
|
||||
cursorIndex--;
|
||||
return;
|
||||
}
|
||||
if (isAllowedChatChar(ch) && static_cast<int>(message.length()) < SharedConstants::maxVisibleLength)
|
||||
if (isAllowedChatChar(ch) && static_cast<int>(message.length()) < SharedConstants::maxChatLength)
|
||||
{
|
||||
message.insert(cursorIndex, 1, ch);
|
||||
cursorIndex++;
|
||||
|
||||
@@ -16,7 +16,6 @@ private:
|
||||
static std::vector<wstring> s_chatHistory;
|
||||
static int s_historyIndex;
|
||||
static wstring s_historyDraft;
|
||||
static int s_chatIndex;
|
||||
static const wstring allowedChars;
|
||||
static bool isAllowedChatChar(wchar_t c);
|
||||
|
||||
@@ -29,9 +28,6 @@ public:
|
||||
virtual void handleHistoryUp();
|
||||
virtual void handleHistoryDown();
|
||||
|
||||
static int getChatIndex();
|
||||
static void correctChatIndex(int newChatIndex);
|
||||
static void setWheelValue(int wheel);
|
||||
protected:
|
||||
void keyPressed(wchar_t ch, int eventKey);
|
||||
public:
|
||||
|
||||
@@ -1607,28 +1607,17 @@ void ClientConnection::handleChat(shared_ptr<ChatPacket> packet)
|
||||
bool replaceEntitySource = false;
|
||||
bool replaceItem = false;
|
||||
|
||||
int stringArgsSize = packet->m_stringArgs.size();
|
||||
|
||||
wstring playerDisplayName = L"";
|
||||
wstring sourceDisplayName = L"";
|
||||
|
||||
// On platforms other than Xbox One this just sets display name to gamertag
|
||||
if (stringArgsSize >= 1) playerDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[0]);
|
||||
if (stringArgsSize >= 2) sourceDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[1]);
|
||||
if (packet->m_stringArgs.size() >= 1) playerDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[0]);
|
||||
if (packet->m_stringArgs.size() >= 2) sourceDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[1]);
|
||||
|
||||
switch(packet->m_messageType)
|
||||
{
|
||||
case ChatPacket::e_ChatCustom:
|
||||
case ChatPacket::e_ChatActionBar:
|
||||
if (stringArgsSize >= 1) {
|
||||
message = packet->m_stringArgs[0];
|
||||
|
||||
message = app.EscapeHTMLString(message); //do this to enforce escaped string
|
||||
message = app.FormatChatMessage(message); //this needs to be last cause it converts colors to html colors that would have been escaped
|
||||
} else {
|
||||
message = L"";
|
||||
}
|
||||
displayOnGui = (packet->m_messageType == ChatPacket::e_ChatCustom);
|
||||
message = (packet->m_stringArgs.size() >= 1) ? packet->m_stringArgs[0] : L"";
|
||||
break;
|
||||
case ChatPacket::e_ChatBedOccupied:
|
||||
message = app.GetString(IDS_TILE_BED_OCCUPIED);
|
||||
@@ -1978,7 +1967,7 @@ void ClientConnection::handleChat(shared_ptr<ChatPacket> packet)
|
||||
|
||||
if(replacePlayer)
|
||||
{
|
||||
message = replaceAll(message,L"{*PLAYER*}", playerDisplayName);
|
||||
message = replaceAll(message,L"{*PLAYER*}",playerDisplayName);
|
||||
}
|
||||
|
||||
if(replaceEntitySource)
|
||||
@@ -2013,9 +2002,7 @@ void ClientConnection::handleChat(shared_ptr<ChatPacket> packet)
|
||||
// flag that a message is a death message
|
||||
bool bIsDeathMessage = (packet->m_messageType>=ChatPacket::e_ChatDeathInFire) && (packet->m_messageType<=ChatPacket::e_ChatDeathIndirectMagicItem);
|
||||
|
||||
if( displayOnGui ) minecraft->gui->addMessage(message, m_userIndex, bIsDeathMessage);
|
||||
|
||||
if (!displayOnGui && !message.empty()) minecraft->gui->setActionBarMessage(message);
|
||||
if( displayOnGui ) minecraft->gui->addMessage(message,m_userIndex, bIsDeathMessage);
|
||||
}
|
||||
|
||||
void ClientConnection::handleAnimate(shared_ptr<AnimatePacket> packet)
|
||||
|
||||
@@ -223,8 +223,6 @@ const WCHAR *ConsoleSoundEngine::wchSoundNames[eSoundType_MAX]=
|
||||
// 4J-PB - Some sounds were updated, but we can't do that for the 360 or we have to do a new sound bank
|
||||
// instead, we'll add the sounds as new ones and change the code to reference them
|
||||
L"fire.new_ignite",
|
||||
|
||||
L"damage.critical", //eSoundType_DAMAGE_CRITICAL,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
#endif
|
||||
|
||||
#include "../Common/Leaderboards/LeaderboardManager.h"
|
||||
#include <regex>
|
||||
|
||||
//CMinecraftApp app;
|
||||
unsigned int CMinecraftApp::m_uiLastSignInData = 0;
|
||||
@@ -6654,97 +6653,6 @@ wstring CMinecraftApp::FormatHTMLString(int iPad, const wstring &desc, int shado
|
||||
return text;
|
||||
}
|
||||
|
||||
//found list of html escapes at https://stackoverflow.com/questions/7381974/which-characters-need-to-be-escaped-in-html
|
||||
wstring CMinecraftApp::EscapeHTMLString(const wstring& desc)
|
||||
{
|
||||
static std::unordered_map<wchar_t, wchar_t*> replacementMap = {
|
||||
{L'&', L"&"},
|
||||
{L'<', L"<"},
|
||||
{L'>', L">"},
|
||||
};
|
||||
|
||||
wstring finalString = L"";
|
||||
for (int i = 0; i < desc.size(); i++) {
|
||||
wchar_t _char = desc[i];
|
||||
auto it = replacementMap.find(_char);
|
||||
|
||||
if (it != replacementMap.end()) finalString += it->second;
|
||||
else finalString += _char;
|
||||
}
|
||||
|
||||
return finalString;
|
||||
}
|
||||
|
||||
wstring CMinecraftApp::FormatChatMessage(const wstring& desc, bool applyStyling)
|
||||
{
|
||||
static std::wregex IDS_Pattern(LR"(\{\*IDS_(\d+)\*\})"); //maybe theres a better way to do translateable IDS
|
||||
static std::wstring_view colorFormatString = L"<font color=\"#%08x\">";
|
||||
|
||||
wstring results = desc;
|
||||
wchar_t replacements[64];
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_0), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§0", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_1), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§1", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_2), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§2", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_3), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§3", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_4), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§4", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_5), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§5", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_6), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§6", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_7), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§7", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_8), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§8", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_9), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§9", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_a), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§a", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_b), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§b", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_c), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§c", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_d), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§d", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_e), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§e", replacements);
|
||||
|
||||
swprintf(replacements, 64, (applyStyling ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_f), 0xFFFFFFFF);
|
||||
results = replaceAll(results, L"§f", replacements);
|
||||
results = replaceAll(results, L"§r", replacements); //we only support color so reset is the same as white color
|
||||
|
||||
results = replaceAll(results, L"'", L"\u2019");
|
||||
|
||||
if (applyStyling) {
|
||||
std::wsmatch match;
|
||||
while (std::regex_search(results, match, IDS_Pattern)) {
|
||||
results = replaceAll(results, match[0], app.GetString(std::stoi(match[1].str())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
wstring CMinecraftApp::GetActionReplacement(int iPad, unsigned char ucAction)
|
||||
{
|
||||
unsigned int input = InputManager.GetGameJoypadMaps(InputManager.GetJoypadMapVal(iPad) ,ucAction);
|
||||
|
||||
@@ -564,9 +564,7 @@ public:
|
||||
int GetHTMLColour(eMinecraftColour colour);
|
||||
int GetHTMLColor(eMinecraftColour colour) { return GetHTMLColour(colour); }
|
||||
int GetHTMLFontSize(EHTMLFontSize size);
|
||||
wstring FormatHTMLString(int iPad, const wstring& desc, int shadowColour = 0xFFFFFFFF);
|
||||
wstring EscapeHTMLString(const wstring &desc);
|
||||
wstring FormatChatMessage(const wstring& desc, bool applyStyling = true);
|
||||
wstring FormatHTMLString(int iPad, const wstring &desc, int shadowColour = 0xFFFFFFFF);
|
||||
wstring GetActionReplacement(int iPad, unsigned char ucAction);
|
||||
wstring GetVKReplacement(unsigned int uiVKey);
|
||||
wstring GetIconReplacement(unsigned int uiIcon);
|
||||
|
||||
@@ -204,12 +204,6 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
|
||||
ProfileManager.SetDeferredSignoutEnabled(true);
|
||||
#endif
|
||||
|
||||
// Clear any stale cancel flag latched by the previous join's progress
|
||||
// UI teardown, otherwise the next join's first tick insta-closes.
|
||||
EnterCriticalSection(&bCancelRequestedCS);
|
||||
g_NetworkManager.m_bCancelRequested = false;
|
||||
LeaveCriticalSection(&bCancelRequestedCS);
|
||||
|
||||
int64_t seed = 0;
|
||||
bool dedicatedNoLocalHostPlayer = false;
|
||||
if (lpParameter != nullptr)
|
||||
|
||||
@@ -134,14 +134,7 @@ void CPlatformNetworkManagerStub::NotifyPlayerLeaving(IQNetPlayer* pQNetPlayer)
|
||||
if (socket != nullptr)
|
||||
{
|
||||
if (m_pIQNet->IsHost())
|
||||
{
|
||||
g_NetworkManager.CloseConnection(networkPlayer);
|
||||
|
||||
// Propagate the TCP drop to the game Socket so any orphaned
|
||||
// PendingConnection at this smallId cleans up before its login
|
||||
// timer fires and leaks a DisconnectPacket to the reused slot.
|
||||
socket->close(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pIQNet->IsHost())
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "stdafx.h"
|
||||
#include "UIController.h"
|
||||
#include <ChatScreen.h>
|
||||
#include "UI.h"
|
||||
#include "UIScene.h"
|
||||
#include "UIControl_Slider.h"
|
||||
@@ -1439,9 +1438,6 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (key == 4) ChatScreen::setWheelValue(1);
|
||||
if (key == 5) ChatScreen::setWheelValue(-1);
|
||||
|
||||
if(pressed) app.DebugPrintf("Pressed %d\n",key);
|
||||
if(released) app.DebugPrintf("Released %d\n",key);
|
||||
// Repeat handling
|
||||
|
||||
@@ -310,27 +310,19 @@ void UIScene::loadMovie()
|
||||
|
||||
if(!app.hasArchiveFile(moviePath))
|
||||
{
|
||||
app.DebugPrintf("WARNING: Could not find iggy movie %ls, trying other resolutions\n", moviePath.c_str());
|
||||
app.DebugPrintf("WARNING: Could not find iggy movie %ls, falling back on 720\n", moviePath.c_str());
|
||||
|
||||
// Try 720 first, then 1080 as final fallback
|
||||
moviePath = getMoviePath();
|
||||
moviePath.append(L"720.swf");
|
||||
m_loadedResolution = eSceneResolution_720;
|
||||
|
||||
if(!app.hasArchiveFile(moviePath))
|
||||
{
|
||||
moviePath = getMoviePath();
|
||||
moviePath.append(L"1080.swf");
|
||||
m_loadedResolution = eSceneResolution_1080;
|
||||
|
||||
if(!app.hasArchiveFile(moviePath))
|
||||
{
|
||||
app.DebugPrintf("ERROR: Could not find any iggy movie for %ls!\n", moviePath.c_str());
|
||||
app.DebugPrintf("ERROR: Could not find any iggy movie for %ls!\n", moviePath.c_str());
|
||||
#ifndef _CONTENT_PACKAGE
|
||||
__debugbreak();
|
||||
__debugbreak();
|
||||
#endif
|
||||
app.FatalLoadError();
|
||||
}
|
||||
app.FatalLoadError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "../../EnderDragonRenderer.h"
|
||||
#include "../../../Minecraft.World/net.minecraft.world.inventory.h"
|
||||
#include "../../../Minecraft.World/StringHelpers.h"
|
||||
#include <ChatScreen.h>
|
||||
|
||||
UIScene_HUD::UIScene_HUD(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
|
||||
{
|
||||
@@ -24,10 +23,8 @@ UIScene_HUD::UIScene_HUD(int iPad, void *initData, UILayer *parentLayer) : UISce
|
||||
for(unsigned int i = 0; i < CHAT_LINES_COUNT; ++i)
|
||||
{
|
||||
m_labelChatText[i].init(L"");
|
||||
IggyValueSetBooleanRS(m_labelChatText[i].getIggyValuePath(), 0, "m_bUseHtmlText", true);
|
||||
}
|
||||
m_labelJukebox.init(L"");
|
||||
IggyValueSetBooleanRS(m_labelJukebox.getIggyValuePath(), 0, "m_bUseHtmlText", true);
|
||||
|
||||
addTimer(0, 100);
|
||||
}
|
||||
@@ -257,10 +254,8 @@ void UIScene_HUD::handleReload()
|
||||
for(unsigned int i = 0; i < CHAT_LINES_COUNT; ++i)
|
||||
{
|
||||
m_labelChatText[i].init(L"");
|
||||
IggyValueSetBooleanRS(m_labelChatText[i].getIggyValuePath(), 0, "m_bUseHtmlText", true);
|
||||
}
|
||||
m_labelJukebox.init(L"");
|
||||
IggyValueSetBooleanRS(m_labelJukebox.getIggyValuePath(), 0, "m_bUseHtmlText", true);
|
||||
|
||||
int iGuiScale;
|
||||
Minecraft *pMinecraft = Minecraft::GetInstance();
|
||||
@@ -783,31 +778,16 @@ void UIScene_HUD::render(S32 width, S32 height, C4JRender::eViewportType viewpor
|
||||
void UIScene_HUD::handleTimerComplete(int id)
|
||||
{
|
||||
Minecraft *pMinecraft = Minecraft::GetInstance();
|
||||
bool isChatOpen = (dynamic_cast<ChatScreen*>(pMinecraft->getScreen()) != nullptr);
|
||||
|
||||
bool anyVisible = false;
|
||||
if(pMinecraft->localplayers[m_iPad]!= nullptr)
|
||||
{
|
||||
Gui *pGui = pMinecraft->gui;
|
||||
DWORD totalMessages = pGui->getMessagesCount(m_iPad);
|
||||
DWORD messagesToDisplay = min( CHAT_LINES_COUNT, totalMessages);
|
||||
DWORD maxScroll = max(0, totalMessages - messagesToDisplay);
|
||||
|
||||
bool canScroll = messagesToDisplay < totalMessages;
|
||||
int startIndex = (canScroll && isChatOpen ? ChatScreen::getChatIndex() : 0);
|
||||
|
||||
if (startIndex > maxScroll) {
|
||||
ChatScreen::correctChatIndex(maxScroll);
|
||||
startIndex = maxScroll;
|
||||
}
|
||||
|
||||
app.DebugPrintf("handleTimerComplete: %d | %d | %d\n", maxScroll, startIndex, totalMessages);
|
||||
|
||||
for( unsigned int i = 0; i < messagesToDisplay; ++i )
|
||||
//DWORD messagesToDisplay = min( CHAT_LINES_COUNT, pGui->getMessagesCount(m_iPad) );
|
||||
for( unsigned int i = 0; i < CHAT_LINES_COUNT; ++i )
|
||||
{
|
||||
unsigned int msgIndex = startIndex + i;
|
||||
float opacity = pGui->getOpacity(m_iPad, msgIndex);
|
||||
if( opacity > 0 || isChatOpen)
|
||||
float opacity = pGui->getOpacity(m_iPad, i);
|
||||
if( opacity > 0 )
|
||||
{
|
||||
#if 0 // def _WINDOWS64 // Use Iggy chat until Gui::render has visual parity
|
||||
// Chat drawn by Gui::render with color codes. Hides Iggy chat to avoid double chats.
|
||||
@@ -815,10 +795,9 @@ void UIScene_HUD::handleTimerComplete(int id)
|
||||
m_labelChatText[i].setOpacity(0);
|
||||
m_labelChatText[i].setLabel(L"");
|
||||
#else
|
||||
|
||||
m_controlLabelBackground[i].setOpacity((isChatOpen ? 1 : opacity));
|
||||
m_labelChatText[i].setOpacity((isChatOpen ? 1 : opacity));
|
||||
m_labelChatText[i].setLabel(pGui->getMessage(m_iPad, msgIndex));
|
||||
m_controlLabelBackground[i].setOpacity(opacity);
|
||||
m_labelChatText[i].setOpacity(opacity);
|
||||
m_labelChatText[i].setLabel( pGui->getMessagesCount(m_iPad) ? pGui->getMessage(m_iPad,i) : L"" );
|
||||
#endif
|
||||
anyVisible = true;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ private:
|
||||
bool m_bSplitscreen;
|
||||
|
||||
protected:
|
||||
UIControl_HTMLLabel m_labelChatText[CHAT_LINES_COUNT];
|
||||
UIControl_Label m_labelChatText[CHAT_LINES_COUNT];
|
||||
UIControl_Label m_labelJukebox;
|
||||
UIControl m_controlLabelBackground[CHAT_LINES_COUNT];
|
||||
UIControl_Label m_labelDisplayName;
|
||||
|
||||
@@ -281,11 +281,6 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye
|
||||
WCHAR TempString[256];
|
||||
swprintf((WCHAR *)TempString, 256, L"%ls: %ls", app.GetString(IDS_SLIDER_DIFFICULTY), L"Hardcore");
|
||||
m_sliderDifficulty.init(TempString, eControl_Difficulty, 0, 4, 4);
|
||||
|
||||
// Hardcore locks game mode to Survival
|
||||
m_iGameModeId = GameType::SURVIVAL->getId();
|
||||
m_bGameModeCreative = false;
|
||||
m_buttonGamemode.setLabel(app.GetString(IDS_GAMEMODE_SURVIVAL));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -426,7 +421,15 @@ void UIScene_LoadMenu::updateTooltips()
|
||||
void UIScene_LoadMenu::updateComponents()
|
||||
{
|
||||
m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true);
|
||||
m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false);
|
||||
|
||||
if(RenderManager.IsWidescreen())
|
||||
{
|
||||
m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false);
|
||||
}
|
||||
}
|
||||
|
||||
wstring UIScene_LoadMenu::getMoviePath()
|
||||
@@ -570,9 +573,7 @@ void UIScene_LoadMenu::tick()
|
||||
m_MoreOptionsParams.bAllowFriendsOfFriends = TRUE;
|
||||
}
|
||||
|
||||
// Use thumbnail host options if available, otherwise preserve the level.dat value
|
||||
if (app.GetGameHostOption(uiHostOptions, eGameHostOption_Hardcore) > 0)
|
||||
m_bHardcore = true;
|
||||
m_bHardcore = app.GetGameHostOption(uiHostOptions, eGameHostOption_Hardcore) > 0;
|
||||
if (m_bHardcore)
|
||||
{
|
||||
WCHAR TempString[256];
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardcore = nullptr)
|
||||
{
|
||||
// Check for a worldname.txt sidecar written by the rename feature first
|
||||
wstring sidecarName = L"";
|
||||
size_t slashPos = filePath.rfind(L'\\');
|
||||
if (slashPos != wstring::npos)
|
||||
{
|
||||
@@ -51,7 +50,7 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc
|
||||
{
|
||||
wchar_t wbuf[128] = {};
|
||||
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, 127);
|
||||
sidecarName = wbuf;
|
||||
return wstring(wbuf);
|
||||
}
|
||||
}
|
||||
else fclose(fr);
|
||||
@@ -59,10 +58,10 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc
|
||||
}
|
||||
|
||||
HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
|
||||
if (hFile == INVALID_HANDLE_VALUE) return sidecarName;
|
||||
if (hFile == INVALID_HANDLE_VALUE) return L"";
|
||||
|
||||
DWORD fileSize = GetFileSize(hFile, nullptr);
|
||||
if (fileSize < 12 || fileSize == INVALID_FILE_SIZE) { CloseHandle(hFile); return sidecarName; }
|
||||
if (fileSize < 12 || fileSize == INVALID_FILE_SIZE) { CloseHandle(hFile); return L""; }
|
||||
|
||||
unsigned char *rawData = new unsigned char[fileSize];
|
||||
DWORD bytesRead = 0;
|
||||
@@ -70,7 +69,7 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc
|
||||
{
|
||||
CloseHandle(hFile);
|
||||
delete[] rawData;
|
||||
return sidecarName;
|
||||
return L"";
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
|
||||
@@ -85,7 +84,7 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc
|
||||
if (decompSize == 0 || decompSize > 128 * 1024 * 1024)
|
||||
{
|
||||
delete[] rawData;
|
||||
return sidecarName;
|
||||
return L"";
|
||||
}
|
||||
saveData = new unsigned char[decompSize];
|
||||
Compression::getCompression()->Decompress(saveData, &decompSize, rawData + 8, fileSize - 8);
|
||||
@@ -141,10 +140,6 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc
|
||||
|
||||
if (freeSaveData) delete[] saveData;
|
||||
delete[] rawData;
|
||||
|
||||
// Prefer the sidecar name (user-renamed) over the level.dat name
|
||||
if (!sidecarName.empty()) return sidecarName;
|
||||
|
||||
// "world" is the engine default - it means no real name was ever set,
|
||||
// so return empty to let the caller fall back to the save filename (timestamp).
|
||||
if (result == L"world") result = L"";
|
||||
@@ -779,16 +774,7 @@ void UIScene_LoadOrJoinMenu::tick()
|
||||
wchar_t wFilename[MAX_SAVEFILENAME_LENGTH];
|
||||
ZeroMemory(wFilename, sizeof(wFilename));
|
||||
mbstowcs(wFilename, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1);
|
||||
wchar_t wTitle[MAX_DISPLAYNAME_LENGTH];
|
||||
ZeroMemory(wTitle, sizeof(wTitle));
|
||||
mbstowcs(wTitle, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveTitle, MAX_DISPLAYNAME_LENGTH - 1);
|
||||
wstring filePath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\") + wstring(wTitle) + wstring(L".ms");
|
||||
// Fallback to legacy saveData.ms if new-style filename doesn't exist
|
||||
{
|
||||
DWORD attrs = GetFileAttributesW(filePath.c_str());
|
||||
if (attrs == INVALID_FILE_ATTRIBUTES)
|
||||
filePath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\saveData.ms");
|
||||
}
|
||||
wstring filePath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\saveData.ms");
|
||||
|
||||
HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
|
||||
DWORD fileSize = 0;
|
||||
|
||||
@@ -1127,6 +1127,15 @@ void UIScene_SkinSelectMenu::handlePackIndexChanged()
|
||||
updatePackDisplay();
|
||||
}
|
||||
|
||||
std::wstring fakeWideToRealWide(const wchar_t* original)
|
||||
{
|
||||
const char* name = reinterpret_cast<const char*>(original);
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, nullptr, 0);
|
||||
std::wstring wName(len, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, name, -1, &wName[0], len);
|
||||
return wName.c_str();
|
||||
}
|
||||
|
||||
void UIScene_SkinSelectMenu::updatePackDisplay()
|
||||
{
|
||||
m_currentPackCount = app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin) + SKIN_SELECT_MAX_DEFAULTS;
|
||||
@@ -1134,16 +1143,18 @@ void UIScene_SkinSelectMenu::updatePackDisplay()
|
||||
if(m_packIndex >= SKIN_SELECT_MAX_DEFAULTS)
|
||||
{
|
||||
DLCPack *thisPack = app.m_dlcManager.getPack(m_packIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin);
|
||||
setCentreLabel(thisPack->getName().c_str());
|
||||
// Fix the incorrect string type on title to display correctly
|
||||
setCentreLabel(fakeWideToRealWide(thisPack->getName().c_str()));
|
||||
//setCentreLabel(thisPack->getName().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(m_packIndex)
|
||||
{
|
||||
case SKIN_SELECT_PACK_DEFAULT:
|
||||
case SKIN_SELECT_PACK_DEFAULT:
|
||||
setCentreLabel(app.GetString(IDS_NO_SKIN_PACK));
|
||||
break;
|
||||
case SKIN_SELECT_PACK_FAVORITES:
|
||||
case SKIN_SELECT_PACK_FAVORITES:
|
||||
setCentreLabel(app.GetString(IDS_FAVORITES_SKIN_PACK));
|
||||
break;
|
||||
}
|
||||
@@ -1153,16 +1164,18 @@ void UIScene_SkinSelectMenu::updatePackDisplay()
|
||||
if(nextPackIndex >= SKIN_SELECT_MAX_DEFAULTS)
|
||||
{
|
||||
DLCPack *thisPack = app.m_dlcManager.getPack(nextPackIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin);
|
||||
setRightLabel(thisPack->getName().c_str());
|
||||
// Fix the incorrect string type on title to display correctly
|
||||
setRightLabel(fakeWideToRealWide(thisPack->getName().c_str()));
|
||||
//setRightLabel(thisPack->getName().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(nextPackIndex)
|
||||
{
|
||||
case SKIN_SELECT_PACK_DEFAULT:
|
||||
case SKIN_SELECT_PACK_DEFAULT:
|
||||
setRightLabel(app.GetString(IDS_NO_SKIN_PACK));
|
||||
break;
|
||||
case SKIN_SELECT_PACK_FAVORITES:
|
||||
case SKIN_SELECT_PACK_FAVORITES:
|
||||
setRightLabel(app.GetString(IDS_FAVORITES_SKIN_PACK));
|
||||
break;
|
||||
}
|
||||
@@ -1172,16 +1185,18 @@ void UIScene_SkinSelectMenu::updatePackDisplay()
|
||||
if(previousPackIndex >= SKIN_SELECT_MAX_DEFAULTS)
|
||||
{
|
||||
DLCPack *thisPack = app.m_dlcManager.getPack(previousPackIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin);
|
||||
setLeftLabel(thisPack->getName().c_str());
|
||||
// Fix the incorrect string type on title to display correctly
|
||||
setLeftLabel(fakeWideToRealWide(thisPack->getName().c_str()));
|
||||
//setLeftLabel(thisPack->getName().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(previousPackIndex)
|
||||
{
|
||||
case SKIN_SELECT_PACK_DEFAULT:
|
||||
case SKIN_SELECT_PACK_DEFAULT:
|
||||
setLeftLabel(app.GetString(IDS_NO_SKIN_PACK));
|
||||
break;
|
||||
case SKIN_SELECT_PACK_FAVORITES:
|
||||
case SKIN_SELECT_PACK_FAVORITES:
|
||||
setLeftLabel(app.GetString(IDS_FAVORITES_SKIN_PACK));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ void EntityTracker::addEntity(shared_ptr<Entity> e, int range, int updateInterva
|
||||
{
|
||||
assert(false); // Entity already tracked
|
||||
}
|
||||
if( e->entityId >= 16384 )
|
||||
if( e->entityId >= 2048 )
|
||||
{
|
||||
__debugbreak();
|
||||
}
|
||||
|
||||
@@ -404,8 +404,6 @@ void Font::draw(const wstring &str, bool dropShadow, int initialColor)
|
||||
t->begin();
|
||||
t->color(currentColor & 0x00ffffff, (currentColor >> 24) & 255);
|
||||
|
||||
bool prev = t->setMipmapEnable(false); // Disable mipmapping for fonts, and save previous enabled value to be restored later - Botch
|
||||
|
||||
for (int i = 0; i < static_cast<int>(cleanStr.length()); ++i)
|
||||
{
|
||||
// Map character
|
||||
@@ -483,8 +481,6 @@ void Font::draw(const wstring &str, bool dropShadow, int initialColor)
|
||||
}
|
||||
}
|
||||
|
||||
t->setMipmapEnable(prev); //Reinstates previously used enabled value - Botch
|
||||
|
||||
t->end();
|
||||
}
|
||||
|
||||
|
||||
+16
-87
@@ -697,8 +697,8 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
|
||||
glEnable(GL_COLOR_MATERIAL);
|
||||
|
||||
// 4J - TomK now using safe zone values directly instead of the magic number calculation that lived here before (which only worked for medium scale, the other two were off!)
|
||||
int xo = iSafezoneXHalf + 10; // TODO: fix relative scaling for atrocious aspect ratios
|
||||
int yo = iSafezoneTopYHalf + 10; // TODO: fix relative scaling for atrocious aspect ratios
|
||||
int xo = iSafezoneXHalf + 10;
|
||||
int yo = iSafezoneTopYHalf + 10;
|
||||
|
||||
#ifdef __PSVITA__
|
||||
// align directly with corners, there are no safe zones on vita
|
||||
@@ -708,29 +708,8 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(static_cast<float>(xo), static_cast<float>(yo), 50);
|
||||
|
||||
// correct paper doll aspect ratio
|
||||
float ss = 12.0f;
|
||||
float aspectScaleX = 1.0f;
|
||||
float aspectScaleY = 1.0f;
|
||||
|
||||
extern int g_rScreenWidth;
|
||||
extern int g_rScreenHeight;
|
||||
|
||||
if (g_rScreenWidth > 0 && g_rScreenHeight > 0) {
|
||||
float screenAspect = (float)g_rScreenWidth / (float)g_rScreenHeight;
|
||||
const float targetAspect = 16.0f / 9.0f;
|
||||
|
||||
// apply correction if window is not already at a 16:9 aspect ratio
|
||||
if (fabs(screenAspect - targetAspect) > 0.01f) {
|
||||
if (screenAspect > targetAspect)
|
||||
aspectScaleX = targetAspect / screenAspect;
|
||||
else
|
||||
aspectScaleY = screenAspect / targetAspect;
|
||||
}
|
||||
}
|
||||
|
||||
glScalef(-ss * aspectScaleX, ss * aspectScaleY, ss);
|
||||
float ss = 12;
|
||||
glScalef(-ss, ss, ss);
|
||||
glRotatef(180, 0, 0, 1);
|
||||
|
||||
float oyr = minecraft->player->yRot;
|
||||
@@ -1229,20 +1208,7 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
|
||||
|
||||
// Disable the depth test so the text shows on top of the paperdoll
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
#ifdef _WINDOWS64
|
||||
float scaleWidth = (g_rScreenWidth / 1920.0f);
|
||||
float scaleHeight = (g_rScreenHeight / 1080.0f);
|
||||
|
||||
float scale = min(scaleWidth, scaleHeight); //stop stretching
|
||||
|
||||
if (scale < 0.5f) scale = 0.5f; // force minimum scale
|
||||
if (scale > 1.2f) // resolutions over 1296 pixels tall
|
||||
{
|
||||
scale = scale - 0.33f; // tame overscaling on 1440p
|
||||
}
|
||||
|
||||
glScalef(scale, scale, 1);
|
||||
#endif
|
||||
// Loop through the lines and draw them all on screen
|
||||
int yPos = debugTop;
|
||||
for (const auto &line : lines)
|
||||
@@ -1251,9 +1217,6 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
|
||||
yPos += 10;
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
glScalef(1, 1, 1);
|
||||
#endif
|
||||
// Restore the depth test
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
@@ -1458,37 +1421,6 @@ void Gui::clearMessages(int iPad)
|
||||
}
|
||||
}
|
||||
|
||||
int getVisibleMessageLength(const wstring& _string) {
|
||||
int visibleMessageLength = 0;
|
||||
bool inHtmlTag = false;
|
||||
|
||||
for (wchar_t _char : _string) {
|
||||
if (_char == L'<') inHtmlTag = true;
|
||||
if (_char == L'>') inHtmlTag = false;
|
||||
|
||||
if (!inHtmlTag) visibleMessageLength++;
|
||||
}
|
||||
|
||||
return visibleMessageLength;
|
||||
}
|
||||
|
||||
int getVisibleIndexToRaw(const wstring& _string, size_t target) {
|
||||
int visibleMessageLength = 0;
|
||||
bool inHtmlTag = false;
|
||||
|
||||
for (size_t i = 0; i < _string.size(); i++) {
|
||||
if (_string[i] == L'<') inHtmlTag = true;
|
||||
if (_string[i] == L'>') inHtmlTag = false;
|
||||
|
||||
if (!inHtmlTag) {
|
||||
if (visibleMessageLength == target) return i;
|
||||
|
||||
visibleMessageLength++;
|
||||
}
|
||||
}
|
||||
return _string.size();
|
||||
}
|
||||
|
||||
|
||||
void Gui::addMessage(const wstring& _string,int iPad,bool bIsDeathMessage)
|
||||
{
|
||||
@@ -1572,25 +1504,29 @@ void Gui::addMessage(const wstring& _string,int iPad,bool bIsDeathMessage)
|
||||
break;
|
||||
}
|
||||
|
||||
while (getVisibleMessageLength(string) > maximumChars)
|
||||
{
|
||||
size_t cutOffset = getVisibleIndexToRaw(string, maximumChars);
|
||||
|
||||
size_t iLast=string.find_last_of(L" ", cutOffset);
|
||||
while (string.length() > maximumChars)
|
||||
{
|
||||
unsigned int i = 1;
|
||||
while (i < string.length() && (i + 1) <= maximumChars)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
size_t iLast=string.find_last_of(L" ",i);
|
||||
switch(XGetLanguage())
|
||||
{
|
||||
case XC_LANGUAGE_JAPANESE:
|
||||
case XC_LANGUAGE_TCHINESE:
|
||||
case XC_LANGUAGE_KOREAN:
|
||||
iLast = cutOffset;
|
||||
iLast = maximumChars;
|
||||
break;
|
||||
default:
|
||||
iLast=string.find_last_of(L" ", cutOffset);
|
||||
iLast=string.find_last_of(L" ",i);
|
||||
break;
|
||||
}
|
||||
|
||||
// if a space was found, include the space on this line
|
||||
if(iLast!=cutOffset)
|
||||
if(iLast!=i)
|
||||
{
|
||||
iLast++;
|
||||
}
|
||||
@@ -1642,13 +1578,6 @@ float Gui::getOpacity(int iPad, DWORD index)
|
||||
return opacityPercentage;
|
||||
}
|
||||
|
||||
//just like java functionality it overwrites the jukebox label
|
||||
void Gui::setActionBarMessage(wstring message)
|
||||
{
|
||||
overlayMessageString = message;
|
||||
overlayMessageTime = 20 * 4; //idk how long it should last, need to check java usage
|
||||
}
|
||||
|
||||
float Gui::getJukeboxOpacity(int iPad)
|
||||
{
|
||||
float t = overlayMessageTime - lastTickA;
|
||||
@@ -1664,7 +1593,7 @@ void Gui::setNowPlaying(const wstring& string)
|
||||
// overlayMessageString = L"Now playing: " + string;
|
||||
overlayMessageString = app.GetString(IDS_NOWPLAYING) + string;
|
||||
overlayMessageTime = 20 * 3;
|
||||
animateOverlayMessageColor = true; //appears to be unused, @DrPerkyLegit plans to add in later pr
|
||||
animateOverlayMessageColor = true;
|
||||
}
|
||||
|
||||
void Gui::displayClientMessage(int messageId, int iPad)
|
||||
|
||||
@@ -17,7 +17,6 @@ private:
|
||||
static const int m_iMaxMessageWidth = 280;
|
||||
static ItemRenderer *itemRenderer;
|
||||
vector<GuiMessage> guiMessages[XUSER_MAX_COUNT];
|
||||
int chatIndex = 0;
|
||||
Random *random;
|
||||
|
||||
Minecraft *minecraft;
|
||||
@@ -64,8 +63,6 @@ public:
|
||||
wstring getMessage(int iPad, DWORD index) { return guiMessages[iPad].at(index).string; }
|
||||
float getOpacity(int iPad, DWORD index);
|
||||
|
||||
void setActionBarMessage(wstring message); //uses jukebox label
|
||||
|
||||
wstring getJukeboxMessage(int iPad) { return overlayMessageString; }
|
||||
float getJukeboxOpacity(int iPad);
|
||||
|
||||
|
||||
@@ -1542,12 +1542,8 @@ void Minecraft::run_middle()
|
||||
// Utility keys always work regardless of KBM active state
|
||||
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_PAUSE) && !ui.GetMenuDisplayed(i))
|
||||
{
|
||||
if (dynamic_cast<ChatScreen*>(getScreen()) != nullptr) {
|
||||
setScreen(nullptr);
|
||||
} else {
|
||||
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_PAUSEMENU;
|
||||
app.DebugPrintf("PAUSE PRESSED (keyboard) - ipad = %d\n",i);
|
||||
}
|
||||
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_PAUSEMENU;
|
||||
app.DebugPrintf("PAUSE PRESSED (keyboard) - ipad = %d\n",i);
|
||||
}
|
||||
|
||||
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_THIRD_PERSON))
|
||||
|
||||
@@ -1815,7 +1815,6 @@ void MinecraftServer::run(int64_t seed, void *lpParameter)
|
||||
|
||||
chunkPacketManagement_PostTick();
|
||||
}
|
||||
lastTime = getCurrentTimeMillis();
|
||||
// int64_t afterall = System::currentTimeMillis();
|
||||
// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
|
||||
// PIXReportCounter(L"Server ticks",(float)tickcount);
|
||||
|
||||
@@ -327,9 +327,7 @@ bool PlayerList::placeNewPlayer(Connection *connection, shared_ptr<ServerPlayer>
|
||||
|
||||
// 4J-PB - removed, since it needs to be localised in the language the client is in
|
||||
//server->players->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(L"�e" + playerEntity->name + L" joined the game.") ) );
|
||||
#if !(defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD))
|
||||
broadcastAll(std::make_shared<ChatPacket>(player->name, ChatPacket::e_ChatPlayerJoinedGame));
|
||||
#endif
|
||||
|
||||
MemSect(14);
|
||||
add(player);
|
||||
|
||||
@@ -618,4 +618,4 @@ ResourceLocation *PlayerRenderer::getTextureLocation(shared_ptr<Entity> entity)
|
||||
{
|
||||
shared_ptr<Player> player = dynamic_pointer_cast<Player>(entity);
|
||||
return new ResourceLocation(static_cast<_TEXTURE_NAME>(player->getTexture()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ void Screen::updateEvents()
|
||||
static bool s_arrowFirstRepeat[2] = { false, false };
|
||||
const DWORD ARROW_REPEAT_DELAY_MS = 250;
|
||||
const DWORD ARROW_REPEAT_INTERVAL_MS = 50;
|
||||
DWORD now = GetTickCount64();
|
||||
DWORD now = GetTickCount();
|
||||
|
||||
// Poll keyboard events (special keys that may not come through WM_CHAR, e.g. Escape, arrows)
|
||||
for (int vk = 0; vk < 256; vk++)
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
#include "../Minecraft.World/compression.h"
|
||||
#include "../Minecraft.World/OldChunkStorage.h"
|
||||
#include "../Minecraft.World/Tile.h"
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
#include "../Minecraft.Server/FourKitBridge.h"
|
||||
#endif
|
||||
|
||||
ServerChunkCache::ServerChunkCache(ServerLevel *level, ChunkStorage *storage, ChunkSource *source)
|
||||
{
|
||||
@@ -128,10 +125,7 @@ LevelChunk *ServerChunkCache::create(int x, int z, bool asyncPostProcess) // 4J
|
||||
{
|
||||
EnterCriticalSection(&m_csLoadCreate);
|
||||
chunk = load(x, z);
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
bool isNewChunk = (chunk == nullptr);
|
||||
#endif
|
||||
if (chunk == nullptr)
|
||||
if (chunk == nullptr)
|
||||
{
|
||||
if (source == nullptr)
|
||||
{
|
||||
@@ -210,10 +204,6 @@ LevelChunk *ServerChunkCache::create(int x, int z, bool asyncPostProcess) // 4J
|
||||
if( hasChunk( x - 1, z ) && hasChunk( x + 1, z ) && hasChunk ( x, z - 1 ) && hasChunk( x, z + 1 ) ) chunk->checkChests( this, x, z );
|
||||
|
||||
LeaveCriticalSection(&m_csLoadCreate);
|
||||
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
FourKitBridge::FireChunkLoad(level->dimension->id, x, z, isNewChunk);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -361,38 +351,6 @@ void ServerChunkCache::overwriteHellLevelChunkFromSource(int x, int z, int minVa
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
void ServerChunkCache::regenerateChunk(int x, int z)
|
||||
{
|
||||
if (!source)
|
||||
return;
|
||||
|
||||
LevelChunk *freshChunk = source->getChunk(x, z);
|
||||
if (!freshChunk)
|
||||
return;
|
||||
|
||||
LevelChunk *cachedChunk = nullptr;
|
||||
if (hasChunk(x, z))
|
||||
cachedChunk = getChunk(x, z);
|
||||
|
||||
if (cachedChunk && cachedChunk != emptyChunk)
|
||||
{
|
||||
for (int lx = 0; lx < 16; lx++)
|
||||
for (int ly = 0; ly < 128; ly++)
|
||||
for (int lz = 0; lz < 16; lz++)
|
||||
cachedChunk->setTileAndData(lx, ly, lz, freshChunk->getTile(lx, ly, lz), freshChunk->getData(lx, ly, lz));
|
||||
save(cachedChunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
save(freshChunk);
|
||||
}
|
||||
|
||||
freshChunk->unload(false);
|
||||
delete freshChunk;
|
||||
}
|
||||
#endif
|
||||
|
||||
// 4J Added //
|
||||
#ifdef _LARGE_WORLDS
|
||||
void ServerChunkCache::dontDrop(int x, int z)
|
||||
@@ -956,19 +914,15 @@ bool ServerChunkCache::tick()
|
||||
// player's tick is called to remove them from the chunk they used to be in, and add them to their current chunk. This will only be a temporary state and
|
||||
// we should be able to unload the chunk on the next call to this tick.
|
||||
if( !chunk->containsPlayer() )
|
||||
{
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
if (!FourKitBridge::FireChunkUnload(level->dimension->id, chunk->x, chunk->z))
|
||||
{
|
||||
#endif
|
||||
{
|
||||
save(chunk);
|
||||
saveEntities(chunk);
|
||||
chunk->unload(true);
|
||||
|
||||
//loadedChunks.remove(cp);
|
||||
//loadedChunkList.remove(chunk);
|
||||
auto it = std::find(m_loadedChunkList.begin(), m_loadedChunkList.end(), chunk);
|
||||
if(it != m_loadedChunkList.end()) m_loadedChunkList.erase(it);
|
||||
auto it = std::find(m_loadedChunkList.begin(), m_loadedChunkList.end(), chunk);
|
||||
if(it != m_loadedChunkList.end()) m_loadedChunkList.erase(it);
|
||||
|
||||
int ix = chunk->x + XZOFFSET;
|
||||
int iz = chunk->z + XZOFFSET;
|
||||
@@ -976,9 +930,6 @@ bool ServerChunkCache::tick()
|
||||
delete m_unloadedCache[idx];
|
||||
m_unloadedCache[idx] = chunk;
|
||||
cache[idx] = nullptr;
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -53,9 +53,6 @@ public:
|
||||
|
||||
#endif
|
||||
virtual LevelChunk **getCache() { return cache; } // 4J added
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
void regenerateChunk(int x, int z);
|
||||
#endif
|
||||
|
||||
// 4J-JEV Added; Remove chunk from the toDrop queue.
|
||||
#ifdef _LARGE_WORLDS
|
||||
|
||||
@@ -295,12 +295,12 @@ void ServerPlayer::flagEntitiesToBeRemoved(unsigned int *flags, bool *removedFou
|
||||
{
|
||||
*removedFound = true;
|
||||
// before this left 192 bytes uninitialized!!!!!
|
||||
memset(flags, 0, (16384 / 32) * sizeof(unsigned int));
|
||||
memset(flags, 0, (2048 / 32) * sizeof(unsigned int));
|
||||
}
|
||||
|
||||
for(int index : entitiesToRemove)
|
||||
{
|
||||
if( index < 16384 )
|
||||
if( index < 2048 )
|
||||
{
|
||||
unsigned int i = index / 32;
|
||||
unsigned int j = index % 32;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -9,11 +9,6 @@
|
||||
#include "../../Minecraft.World/LevelSettings.h"
|
||||
#include "../../Minecraft.World/BiomeSource.h"
|
||||
#include "../../Minecraft.World/LevelType.h"
|
||||
#include "stb_image_write.h"
|
||||
|
||||
extern ID3D11Device* g_pd3dDevice;
|
||||
extern ID3D11DeviceContext* g_pImmediateContext;
|
||||
extern IDXGISwapChain* g_pSwapChain;
|
||||
|
||||
CConsoleMinecraftApp app;
|
||||
|
||||
@@ -38,120 +33,9 @@ void CConsoleMinecraftApp::FatalLoadError()
|
||||
{
|
||||
}
|
||||
|
||||
static const int THUMBNAIL_SIZE = 64;
|
||||
|
||||
void CConsoleMinecraftApp::CaptureSaveThumbnail()
|
||||
{
|
||||
if (!g_pSwapChain || !g_pd3dDevice || !g_pImmediateContext)
|
||||
return;
|
||||
|
||||
// Release any previous capture
|
||||
if (m_ThumbnailBuffer.Allocated())
|
||||
m_ThumbnailBuffer.Release();
|
||||
|
||||
// Get the backbuffer
|
||||
ID3D11Texture2D* pBackBuffer = nullptr;
|
||||
HRESULT hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer);
|
||||
if (FAILED(hr))
|
||||
return;
|
||||
|
||||
D3D11_TEXTURE2D_DESC backDesc = {};
|
||||
pBackBuffer->GetDesc(&backDesc);
|
||||
|
||||
// Create a staging texture at backbuffer size to read pixels
|
||||
D3D11_TEXTURE2D_DESC stagingDesc = backDesc;
|
||||
stagingDesc.Usage = D3D11_USAGE_STAGING;
|
||||
stagingDesc.BindFlags = 0;
|
||||
stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
stagingDesc.MiscFlags = 0;
|
||||
|
||||
ID3D11Texture2D* pStaging = nullptr;
|
||||
hr = g_pd3dDevice->CreateTexture2D(&stagingDesc, nullptr, &pStaging);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
pBackBuffer->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
g_pImmediateContext->CopyResource(pStaging, pBackBuffer);
|
||||
pBackBuffer->Release();
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mapped = {};
|
||||
hr = g_pImmediateContext->Map(pStaging, 0, D3D11_MAP_READ, 0, &mapped);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
pStaging->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Downsample to THUMBNAIL_SIZE x THUMBNAIL_SIZE with simple box filter
|
||||
unsigned char* thumb = new unsigned char[THUMBNAIL_SIZE * THUMBNAIL_SIZE * 4];
|
||||
|
||||
// Crop to square (center crop), then scale down
|
||||
UINT srcSize = (backDesc.Width < backDesc.Height) ? backDesc.Width : backDesc.Height;
|
||||
UINT offsetX = (backDesc.Width - srcSize) / 2;
|
||||
UINT offsetY = (backDesc.Height - srcSize) / 2;
|
||||
|
||||
for (int ty = 0; ty < THUMBNAIL_SIZE; ty++)
|
||||
{
|
||||
for (int tx = 0; tx < THUMBNAIL_SIZE; tx++)
|
||||
{
|
||||
// Map thumbnail pixel to source region
|
||||
UINT sx = offsetX + (tx * srcSize) / THUMBNAIL_SIZE;
|
||||
UINT sy = offsetY + (ty * srcSize) / THUMBNAIL_SIZE;
|
||||
|
||||
const unsigned char* src = (const unsigned char*)mapped.pData + sy * mapped.RowPitch + sx * 4;
|
||||
unsigned char* dst = thumb + (ty * THUMBNAIL_SIZE + tx) * 4;
|
||||
|
||||
dst[0] = src[0]; // R (or B depending on format, but BGRA->RGBA swap below)
|
||||
dst[1] = src[1]; // G
|
||||
dst[2] = src[2]; // B
|
||||
dst[3] = 0xFF; // A
|
||||
}
|
||||
}
|
||||
|
||||
g_pImmediateContext->Unmap(pStaging, 0);
|
||||
pStaging->Release();
|
||||
|
||||
// If backbuffer is BGRA, swap to RGBA for PNG
|
||||
if (backDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM || backDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)
|
||||
{
|
||||
for (int i = 0; i < THUMBNAIL_SIZE * THUMBNAIL_SIZE; i++)
|
||||
{
|
||||
unsigned char tmp = thumb[i * 4];
|
||||
thumb[i * 4] = thumb[i * 4 + 2];
|
||||
thumb[i * 4 + 2] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode to PNG in memory using stbi_write_png_to_func
|
||||
struct PngBuffer { unsigned char* data; int size; int capacity; } pngBuf = {};
|
||||
pngBuf.capacity = THUMBNAIL_SIZE * THUMBNAIL_SIZE * 4 + 256;
|
||||
pngBuf.data = (unsigned char*)malloc(pngBuf.capacity);
|
||||
pngBuf.size = 0;
|
||||
|
||||
stbi_write_png_to_func([](void* ctx, void* data, int size) {
|
||||
PngBuffer* buf = (PngBuffer*)ctx;
|
||||
if (buf->size + size > buf->capacity)
|
||||
{
|
||||
buf->capacity = (buf->size + size) * 2;
|
||||
buf->data = (unsigned char*)realloc(buf->data, buf->capacity);
|
||||
}
|
||||
memcpy(buf->data + buf->size, data, size);
|
||||
buf->size += size;
|
||||
}, &pngBuf, THUMBNAIL_SIZE, THUMBNAIL_SIZE, 4, thumb, THUMBNAIL_SIZE * 4);
|
||||
delete[] thumb;
|
||||
|
||||
if (pngBuf.size > 0)
|
||||
{
|
||||
m_ThumbnailBuffer.m_type = ImageFileBuffer::e_typePNG;
|
||||
m_ThumbnailBuffer.m_pBuffer = pngBuf.data;
|
||||
m_ThumbnailBuffer.m_bufferSize = pngBuf.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(pngBuf.data);
|
||||
}
|
||||
RenderManager.CaptureThumbnail(&m_ThumbnailBuffer);
|
||||
}
|
||||
void CConsoleMinecraftApp::GetSaveThumbnail(PBYTE *pbData,DWORD *pdwSize)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <dxgi1_4.h> // IDXGISwapChain3 for SetColorSpace1
|
||||
// FLIP-MODE TEST: headers for IDXGIFactory5 (tearing support probe) and
|
||||
// ID3D11InfoQueue (debug layer message capture).
|
||||
#include <dxgi1_5.h>
|
||||
#include <d3d11sdklayers.h>
|
||||
#include <dxgidebug.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
@@ -60,13 +64,6 @@ extern Renderer InternalRenderManager;
|
||||
|
||||
#include "Xbox/Resource.h"
|
||||
|
||||
// request use of dedicated GPU from AMD and Nvidia drivers
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "legacy_stdio_definitions.lib")
|
||||
#endif
|
||||
@@ -480,6 +477,9 @@ ID3D11Device* g_pd3dDevice = nullptr;
|
||||
ID3D11DeviceContext* g_pImmediateContext = nullptr;
|
||||
IDXGISwapChain* g_pSwapChain = nullptr;
|
||||
bool g_bVSync = false;
|
||||
// FLIP-MODE TEST: set by the DXGI_FEATURE_PRESENT_ALLOW_TEARING probe in InitDevice.
|
||||
// Drives the ALLOW_TEARING swap chain flag and the DXGI_PRESENT_ALLOW_TEARING Present flag.
|
||||
static bool g_bTearingSupported = false;
|
||||
static bool g_bPendingExclusiveFullscreen = false;
|
||||
static bool g_bPendingExclusiveFullscreenValue = false;
|
||||
|
||||
@@ -560,9 +560,262 @@ ID3D11Texture2D* g_pDepthStencilBuffer = nullptr;
|
||||
static const float kClearColorWhite[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
static const float kClearColorBlack[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
// True when the swap chain is in DXGI exclusive fullscreen. Lets ResizeD3D
|
||||
// skip its destroy-and-recreate path, which would break exclusive ownership.
|
||||
static bool g_bDxgiExclusiveFullscreen = false;
|
||||
// FLIP-MODE TEST: toggle for the D3D11 debug layer.
|
||||
// The debug layer has significant per-frame overhead (observed FPS drop),
|
||||
// so default it off. Flip to true, rebuild, and re-run when you need to
|
||||
// capture ResizeBuffers error details via the info queue.
|
||||
static const bool kFlipTestEnableDebugLayer = true;
|
||||
|
||||
// FLIP-MODE TEST: dedicated file logger that works in release builds.
|
||||
// Writes to flip_test.log next to the current working directory (normally the
|
||||
// folder containing the exe). Unbuffered so we see output even if the game
|
||||
// crashes mid-operation. Also echoes to OutputDebugString so DebugView / a
|
||||
// debugger sees it too.
|
||||
static FILE* g_pFlipTestLog = nullptr;
|
||||
static void FlipTestLogEnsureOpen()
|
||||
{
|
||||
if (g_pFlipTestLog) return;
|
||||
fopen_s(&g_pFlipTestLog, "flip_test.log", "w");
|
||||
if (!g_pFlipTestLog) return;
|
||||
setvbuf(g_pFlipTestLog, nullptr, _IONBF, 0); // unbuffered
|
||||
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
fprintf(g_pFlipTestLog,
|
||||
"=== FLIP TEST LOG (started %04d-%02d-%02d %02d:%02d:%02d) ===\n",
|
||||
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
|
||||
}
|
||||
static void FlipTestLog(const char* fmt, ...)
|
||||
{
|
||||
FlipTestLogEnsureOpen();
|
||||
|
||||
char buf[2048];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
_vsnprintf_s(buf, sizeof(buf), _TRUNCATE, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (g_pFlipTestLog) { fputs(buf, g_pFlipTestLog); fflush(g_pFlipTestLog); }
|
||||
OutputDebugStringA(buf);
|
||||
}
|
||||
|
||||
// FLIP-MODE TEST: drain and print every stored D3D11 debug layer message.
|
||||
// Call this after operations we want to diagnose (CreateDevice, ResizeBuffers).
|
||||
// Silently does nothing if the debug layer is unavailable (returns NULL for
|
||||
// the InfoQueue interface), but still writes a log line so we know it was
|
||||
// called.
|
||||
static void DumpD3D11InfoQueue(const char* label)
|
||||
{
|
||||
if (!g_pd3dDevice) { FlipTestLog("[InfoQueue] %s: no device\n", label); return; }
|
||||
ID3D11InfoQueue* pInfoQueue = nullptr;
|
||||
HRESULT hr = g_pd3dDevice->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&pInfoQueue);
|
||||
if (FAILED(hr) || !pInfoQueue)
|
||||
{
|
||||
FlipTestLog("[InfoQueue] %s: unavailable (debug layer not active)\n", label);
|
||||
return;
|
||||
}
|
||||
|
||||
UINT64 numMessages = pInfoQueue->GetNumStoredMessages();
|
||||
FlipTestLog("[InfoQueue] %s: %llu queued message(s)\n",
|
||||
label, (unsigned long long)numMessages);
|
||||
|
||||
for (UINT64 i = 0; i < numMessages; i++)
|
||||
{
|
||||
SIZE_T msgLen = 0;
|
||||
hr = pInfoQueue->GetMessage(i, nullptr, &msgLen);
|
||||
if (FAILED(hr) || msgLen == 0) continue;
|
||||
|
||||
D3D11_MESSAGE* pMsg = (D3D11_MESSAGE*)malloc(msgLen);
|
||||
if (!pMsg) continue;
|
||||
|
||||
hr = pInfoQueue->GetMessage(i, pMsg, &msgLen);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
const char* sev = "?";
|
||||
switch (pMsg->Severity)
|
||||
{
|
||||
case D3D11_MESSAGE_SEVERITY_CORRUPTION: sev = "CORRUPTION"; break;
|
||||
case D3D11_MESSAGE_SEVERITY_ERROR: sev = "ERROR"; break;
|
||||
case D3D11_MESSAGE_SEVERITY_WARNING: sev = "WARNING"; break;
|
||||
case D3D11_MESSAGE_SEVERITY_INFO: sev = "INFO"; break;
|
||||
case D3D11_MESSAGE_SEVERITY_MESSAGE: sev = "MESSAGE"; break;
|
||||
}
|
||||
FlipTestLog(" [%s] #%d: %.*s\n", sev, (int)pMsg->ID,
|
||||
(int)pMsg->DescriptionByteLength, pMsg->pDescription);
|
||||
}
|
||||
free(pMsg);
|
||||
}
|
||||
pInfoQueue->ClearStoredMessages();
|
||||
pInfoQueue->Release();
|
||||
}
|
||||
|
||||
// FLIP-MODE TEST: configure the D3D11 debug layer's info queue.
|
||||
static void SetupD3D11InfoQueue()
|
||||
{
|
||||
if (!g_pd3dDevice) return;
|
||||
ID3D11InfoQueue* pInfoQueue = nullptr;
|
||||
HRESULT hr = g_pd3dDevice->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&pInfoQueue);
|
||||
if (FAILED(hr) || !pInfoQueue)
|
||||
{
|
||||
FlipTestLog("[InfoQueue] not available (debug layer not installed or not enabled)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
D3D11_INFO_QUEUE_FILTER filter = {};
|
||||
D3D11_MESSAGE_SEVERITY denied[] = { D3D11_MESSAGE_SEVERITY_INFO };
|
||||
filter.DenyList.NumSeverities = ARRAYSIZE(denied);
|
||||
filter.DenyList.pSeverityList = denied;
|
||||
pInfoQueue->PushStorageFilter(&filter);
|
||||
|
||||
// Raise the storage limit well above the default (1024) so ReportLive
|
||||
// output from busy games doesn't get truncated.
|
||||
pInfoQueue->SetMessageCountLimit((UINT64)-1);
|
||||
|
||||
FlipTestLog("[InfoQueue] installed, CORRUPTION/ERROR/WARNING captured, limit=unlimited\n");
|
||||
pInfoQueue->Release();
|
||||
}
|
||||
|
||||
// FLIP-MODE TEST: the D3D11 InfoQueue only captures D3D11 messages.
|
||||
// IDXGISwapChain::ResizeBuffers is a DXGI call, so its errors go to the
|
||||
// SEPARATE DXGI info queue. We obtain it via DXGIGetDebugInterface from
|
||||
// dxgidebug.dll (loaded dynamically to avoid an import table dependency).
|
||||
//
|
||||
// kDXGIDebugAll is a GUID declared in dxgidebug.h via DEFINE_GUID, which
|
||||
// only produces an `extern` declaration unless `dxguid.lib` is linked OR
|
||||
// `INITGUID` is defined before the include. Our link line doesn't have
|
||||
// dxguid.lib, so we define the GUID locally as a static const to avoid
|
||||
// adding a lib dependency or fighting with INITGUID ordering.
|
||||
static const GUID kDXGIDebugAll =
|
||||
{ 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x08 } };
|
||||
|
||||
static IDXGIInfoQueue* g_pDXGIInfoQueue = nullptr;
|
||||
|
||||
// Forward declarations (ReportLiveObjects calls DumpDXGIInfoQueue which is defined below)
|
||||
static void DumpDXGIInfoQueue(const char* label);
|
||||
static void SetupDXGIInfoQueue()
|
||||
{
|
||||
HMODULE hDxgiDebug = LoadLibraryA("dxgidebug.dll");
|
||||
if (!hDxgiDebug)
|
||||
{
|
||||
FlipTestLog("[DXGIInfoQueue] dxgidebug.dll not loaded (GetLastError=%lu)\n", GetLastError());
|
||||
return;
|
||||
}
|
||||
typedef HRESULT (WINAPI *PFN_DXGI_GET_DEBUG_INTERFACE)(REFIID, void**);
|
||||
PFN_DXGI_GET_DEBUG_INTERFACE pGet =
|
||||
(PFN_DXGI_GET_DEBUG_INTERFACE)GetProcAddress(hDxgiDebug, "DXGIGetDebugInterface");
|
||||
if (!pGet)
|
||||
{
|
||||
FlipTestLog("[DXGIInfoQueue] DXGIGetDebugInterface symbol not found\n");
|
||||
return;
|
||||
}
|
||||
HRESULT hr = pGet(__uuidof(IDXGIInfoQueue), (void**)&g_pDXGIInfoQueue);
|
||||
if (FAILED(hr) || !g_pDXGIInfoQueue)
|
||||
{
|
||||
FlipTestLog("[DXGIInfoQueue] DXGIGetDebugInterface failed hr=0x%08X\n", (unsigned)hr);
|
||||
return;
|
||||
}
|
||||
// Filter out INFO noise, keep CORRUPTION/ERROR/WARNING.
|
||||
DXGI_INFO_QUEUE_FILTER filter = {};
|
||||
DXGI_INFO_QUEUE_MESSAGE_SEVERITY denied[] = { DXGI_INFO_QUEUE_MESSAGE_SEVERITY_INFO };
|
||||
filter.DenyList.NumSeverities = ARRAYSIZE(denied);
|
||||
filter.DenyList.pSeverityList = denied;
|
||||
g_pDXGIInfoQueue->PushStorageFilter(kDXGIDebugAll, &filter);
|
||||
g_pDXGIInfoQueue->SetMessageCountLimit(kDXGIDebugAll, (UINT64)-1);
|
||||
|
||||
FlipTestLog("[DXGIInfoQueue] installed, DXGI CORRUPTION/ERROR/WARNING captured, limit=unlimited\n");
|
||||
}
|
||||
|
||||
// FLIP-MODE TEST: dump every live D3D11 and DXGI object via ReportLiveObjects.
|
||||
// This is the definitive way to see what's holding refs to the swap chain
|
||||
// buffers. The output goes to the info queues as MESSAGE-severity items, which
|
||||
// our filter lets through.
|
||||
static void ReportLiveObjects(const char* label)
|
||||
{
|
||||
FlipTestLog("[ReportLive] %s:\n", label);
|
||||
|
||||
// D3D11 side
|
||||
if (g_pd3dDevice)
|
||||
{
|
||||
ID3D11Debug* pDebug = nullptr;
|
||||
HRESULT hr = g_pd3dDevice->QueryInterface(__uuidof(ID3D11Debug), (void**)&pDebug);
|
||||
if (SUCCEEDED(hr) && pDebug)
|
||||
{
|
||||
FlipTestLog(" calling ID3D11Debug::ReportLiveDeviceObjects(DETAIL)\n");
|
||||
pDebug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
|
||||
pDebug->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
FlipTestLog(" ID3D11Debug not available hr=0x%08X\n", (unsigned)hr);
|
||||
}
|
||||
}
|
||||
|
||||
// DXGI side (via dxgidebug.dll dynamic load)
|
||||
HMODULE hDxgiDebug = LoadLibraryA("dxgidebug.dll");
|
||||
if (hDxgiDebug)
|
||||
{
|
||||
typedef HRESULT (WINAPI *PFN_DXGI_GET_DEBUG_INTERFACE)(REFIID, void**);
|
||||
PFN_DXGI_GET_DEBUG_INTERFACE pGet =
|
||||
(PFN_DXGI_GET_DEBUG_INTERFACE)GetProcAddress(hDxgiDebug, "DXGIGetDebugInterface");
|
||||
if (pGet)
|
||||
{
|
||||
IDXGIDebug* pDXGIDebug = nullptr;
|
||||
HRESULT hr = pGet(__uuidof(IDXGIDebug), (void**)&pDXGIDebug);
|
||||
if (SUCCEEDED(hr) && pDXGIDebug)
|
||||
{
|
||||
FlipTestLog(" calling IDXGIDebug::ReportLiveObjects(ALL, RLO_ALL)\n");
|
||||
// DXGI_DEBUG_RLO_ALL = 0x7 (SUMMARY | DETAIL | IGNORE_INTERNAL)
|
||||
pDXGIDebug->ReportLiveObjects(kDXGIDebugAll, DXGI_DEBUG_RLO_ALL);
|
||||
pDXGIDebug->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
FlipTestLog(" IDXGIDebug not available hr=0x%08X\n", (unsigned)hr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drain both info queues to capture the ReportLive output
|
||||
DumpD3D11InfoQueue("ReportLive (D3D11)");
|
||||
DumpDXGIInfoQueue("ReportLive (DXGI)");
|
||||
}
|
||||
|
||||
static void DumpDXGIInfoQueue(const char* label)
|
||||
{
|
||||
if (!g_pDXGIInfoQueue) { FlipTestLog("[DXGIInfoQueue] %s: not installed\n", label); return; }
|
||||
|
||||
UINT64 numMessages = g_pDXGIInfoQueue->GetNumStoredMessages(kDXGIDebugAll);
|
||||
FlipTestLog("[DXGIInfoQueue] %s: %llu queued message(s)\n",
|
||||
label, (unsigned long long)numMessages);
|
||||
|
||||
for (UINT64 i = 0; i < numMessages; i++)
|
||||
{
|
||||
SIZE_T msgLen = 0;
|
||||
HRESULT hr = g_pDXGIInfoQueue->GetMessage(kDXGIDebugAll, i, nullptr, &msgLen);
|
||||
if (FAILED(hr) || msgLen == 0) continue;
|
||||
|
||||
DXGI_INFO_QUEUE_MESSAGE* pMsg = (DXGI_INFO_QUEUE_MESSAGE*)malloc(msgLen);
|
||||
if (!pMsg) continue;
|
||||
|
||||
hr = g_pDXGIInfoQueue->GetMessage(kDXGIDebugAll, i, pMsg, &msgLen);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
const char* sev = "?";
|
||||
switch (pMsg->Severity)
|
||||
{
|
||||
case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION: sev = "CORRUPTION"; break;
|
||||
case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR: sev = "ERROR"; break;
|
||||
case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING: sev = "WARNING"; break;
|
||||
case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_INFO: sev = "INFO"; break;
|
||||
case DXGI_INFO_QUEUE_MESSAGE_SEVERITY_MESSAGE: sev = "MESSAGE"; break;
|
||||
}
|
||||
FlipTestLog(" DXGI [%s] #%d: %.*s\n", sev, (int)pMsg->ID,
|
||||
(int)pMsg->DescriptionByteLength, pMsg->pDescription);
|
||||
}
|
||||
free(pMsg);
|
||||
}
|
||||
g_pDXGIInfoQueue->ClearStoredMessages(kDXGIDebugAll);
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
|
||||
@@ -889,10 +1142,16 @@ HRESULT InitDevice()
|
||||
height = g_rScreenHeight;
|
||||
//app.DebugPrintf("width: %d, height: %d\n", width, height);
|
||||
|
||||
FlipTestLog("[InitDevice] entering, requested %dx%d\n", width, height);
|
||||
|
||||
UINT createDeviceFlags = 0;
|
||||
#ifdef _DEBUG
|
||||
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
#endif
|
||||
// FLIP-MODE TEST: debug layer costs a lot of FPS, so only enable it when
|
||||
// the test toggle is set AND we actually want to capture info queue data.
|
||||
if (kFlipTestEnableDebugLayer)
|
||||
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
|
||||
D3D_DRIVER_TYPE driverTypes[] =
|
||||
{
|
||||
@@ -910,20 +1169,26 @@ HRESULT InitDevice()
|
||||
};
|
||||
UINT numFeatureLevels = ARRAYSIZE( featureLevels );
|
||||
|
||||
// Use the legacy bitblt DISCARD swap model (SwapEffect left as default 0).
|
||||
// DXGI_SWAP_EFFECT_FLIP_DISCARD gives lower latency and tearing support, but
|
||||
// takes exclusive ownership of the HWND — which makes window resize via
|
||||
// CreateSwapChain fail with E_ACCESSDENIED and ResizeBuffers fail with
|
||||
// DXGI_ERROR_INVALID_CALL (the closed-source 4J Renderer holds hidden
|
||||
// backbuffer refs we can't release). Bitblt DISCARD has no HWND lock, so
|
||||
// the "destroy old, create new" resize path in ResizeD3D() works cleanly.
|
||||
// VSync toggle still works via the SyncInterval parameter on Present().
|
||||
// RefreshRate=0/0 so DXGI matches the current display mode. Hardcoding a
|
||||
// rate would force a mode switch on SetFullscreenState, which can produce
|
||||
// "input signal out of range" errors on high-refresh monitors.
|
||||
// FLIP-MODE TEST: probe for tearing support before creating the swap chain.
|
||||
// Needs IDXGIFactory5 (Windows 10 Anniversary Update+).
|
||||
{
|
||||
IDXGIFactory5* factory5 = nullptr;
|
||||
if (SUCCEEDED(CreateDXGIFactory1(__uuidof(IDXGIFactory5), (void**)&factory5)))
|
||||
{
|
||||
BOOL allowTearing = FALSE;
|
||||
if (SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing))))
|
||||
g_bTearingSupported = (allowTearing == TRUE);
|
||||
factory5->Release();
|
||||
}
|
||||
FlipTestLog("[InitDevice] tearing support = %s\n", g_bTearingSupported ? "yes" : "no");
|
||||
}
|
||||
|
||||
// FLIP-MODE TEST: FLIP_DISCARD swap chain for low-latency presentation
|
||||
// with tearing support. BufferCount must be >= 2 for flip mode.
|
||||
// SHADER_INPUT is valid for flip mode (per DXGI docs) so we keep it.
|
||||
DXGI_SWAP_CHAIN_DESC sd;
|
||||
ZeroMemory( &sd, sizeof( sd ) );
|
||||
sd.BufferCount = 1;
|
||||
sd.BufferCount = 2;
|
||||
sd.BufferDesc.Width = width;
|
||||
sd.BufferDesc.Height = height;
|
||||
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
@@ -934,23 +1199,59 @@ HRESULT InitDevice()
|
||||
sd.SampleDesc.Count = 1;
|
||||
sd.SampleDesc.Quality = 0;
|
||||
sd.Windowed = TRUE;
|
||||
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
sd.Flags = g_bTearingSupported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
|
||||
|
||||
FlipTestLog("[InitDevice] calling D3D11CreateDeviceAndSwapChain (flags=0x%X, flip=yes, bc=2, tearing=%s)\n",
|
||||
createDeviceFlags, g_bTearingSupported ? "yes" : "no");
|
||||
|
||||
for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
|
||||
{
|
||||
g_driverType = driverTypes[driverTypeIndex];
|
||||
hr = D3D11CreateDeviceAndSwapChain( nullptr, g_driverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
|
||||
D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );
|
||||
FlipTestLog("[InitDevice] driver %u result hr=0x%08X\n", driverTypeIndex, (unsigned)hr);
|
||||
if( HRESULT_SUCCEEDED( hr ) )
|
||||
break;
|
||||
}
|
||||
// FLIP-MODE TEST: if the debug layer isn't installed, retry without the flag
|
||||
// so the game still boots. We lose info queue capture but at least we see
|
||||
// if flip mode itself works.
|
||||
if (FAILED(hr) && (createDeviceFlags & D3D11_CREATE_DEVICE_DEBUG))
|
||||
{
|
||||
FlipTestLog("[InitDevice] retrying without debug flag\n");
|
||||
createDeviceFlags &= ~D3D11_CREATE_DEVICE_DEBUG;
|
||||
for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
|
||||
{
|
||||
g_driverType = driverTypes[driverTypeIndex];
|
||||
hr = D3D11CreateDeviceAndSwapChain( nullptr, g_driverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
|
||||
D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );
|
||||
if( HRESULT_SUCCEEDED( hr ) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
FlipTestLog("[InitDevice] FAILED final hr=0x%08X\n", (unsigned)hr);
|
||||
return hr;
|
||||
}
|
||||
FlipTestLog("[InitDevice] D3D11CreateDeviceAndSwapChain OK, device=%p swapchain=%p context=%p\n",
|
||||
g_pd3dDevice, g_pSwapChain, g_pImmediateContext);
|
||||
|
||||
// FLIP-MODE TEST: install BOTH the D3D11 info queue (for D3D11 errors) and
|
||||
// the DXGI info queue (for DXGI/swap-chain errors — which is what ResizeBuffers
|
||||
// emits). These are separate layers and need separate setup.
|
||||
SetupD3D11InfoQueue();
|
||||
SetupDXGIInfoQueue();
|
||||
DumpD3D11InfoQueue("after CreateDevice");
|
||||
DumpDXGIInfoQueue("after CreateDevice");
|
||||
|
||||
// Create a render target view
|
||||
ID3D11Texture2D* pBackBuffer = nullptr;
|
||||
hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer );
|
||||
if( FAILED( hr ) )
|
||||
return hr;
|
||||
FlipTestLog("[InitDevice] initial backbuffer (GetBuffer 0 at init) = %p\n", (void*)pBackBuffer);
|
||||
|
||||
// Create a depth stencil buffer
|
||||
D3D11_TEXTURE2D_DESC descDepth;
|
||||
@@ -1018,12 +1319,10 @@ void Render()
|
||||
//--------------------------------------------------------------------------------------
|
||||
static bool ResizeD3D(int newW, int newH)
|
||||
{
|
||||
if (newW <= 0 || newH <= 0) return false;
|
||||
if (!g_pSwapChain) return false;
|
||||
if (!g_bResizeReady) return false;
|
||||
// In exclusive fullscreen the swap chain must not be recreated.
|
||||
if (g_bDxgiExclusiveFullscreen)
|
||||
return false;
|
||||
FlipTestLog("[ResizeD3D] called with %dx%d\n", newW, newH);
|
||||
if (newW <= 0 || newH <= 0) { FlipTestLog("[ResizeD3D] bad size, skip\n"); return false; }
|
||||
if (!g_pSwapChain) { FlipTestLog("[ResizeD3D] no swap chain, skip\n"); return false; }
|
||||
if (!g_bResizeReady) { FlipTestLog("[ResizeD3D] not ready, skip\n"); return false; }
|
||||
|
||||
int bbW = newW;
|
||||
int bbH = newH;
|
||||
@@ -1073,78 +1372,160 @@ static bool ResizeD3D(int newW, int newH)
|
||||
*pRM_BBWidth, *pRM_BBHeight, oldScDesc.BufferDesc.Width, oldScDesc.BufferDesc.Height);
|
||||
}
|
||||
|
||||
// FLIP-MODE TEST: granular refcount probing to find who holds the hidden
|
||||
// backbuffer ref. Probe returns the backbuffer refcount AFTER dropping the
|
||||
// ref we added via GetBuffer. In flip mode, the expected steady-state count
|
||||
// is typically 1 (DXGI) or 2 (DXGI + DWM composition).
|
||||
auto probeBBRefcount = [](const char* stage) {
|
||||
if (!g_pSwapChain) { FlipTestLog("[ResizeD3D] refcount %s = (no swap chain)\n", stage); return; }
|
||||
ID3D11Texture2D* pBB = nullptr;
|
||||
HRESULT hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBB);
|
||||
if (FAILED(hr) || !pBB) { FlipTestLog("[ResizeD3D] refcount %s = (GetBuffer failed hr=0x%08X)\n", stage, (unsigned)hr); return; }
|
||||
ULONG rc = pBB->Release();
|
||||
FlipTestLog("[ResizeD3D] refcount %s = %lu (backbuffer ptr=%p)\n", stage, rc, (void*)pBB);
|
||||
};
|
||||
|
||||
probeBBRefcount("at ResizeD3D entry");
|
||||
FlipTestLog("[ResizeD3D] g_pRenderTargetView=%p g_pDepthStencilView=%p g_pDepthStencilBuffer=%p\n",
|
||||
(void*)g_pRenderTargetView, (void*)g_pDepthStencilView, (void*)g_pDepthStencilBuffer);
|
||||
FlipTestLog("[ResizeD3D] Renderer slots on entry: RTV=%p SRV=%p DSV=%p\n",
|
||||
(void*)*ppRM_RTV, (void*)*ppRM_SRV, (void*)*ppRM_DSV);
|
||||
|
||||
RenderManager.Suspend();
|
||||
while (!RenderManager.Suspended()) { Sleep(1); }
|
||||
probeBBRefcount("after RenderManager.Suspend");
|
||||
|
||||
PostProcesser::GetInstance().Cleanup();
|
||||
probeBBRefcount("after PostProcesser::Cleanup");
|
||||
|
||||
// Explicitly unbind all render targets BEFORE ClearState. ClearState should
|
||||
// also do this, but being explicit costs nothing and protects against
|
||||
// driver quirks.
|
||||
{
|
||||
ID3D11RenderTargetView* nullRTV[8] = {};
|
||||
g_pImmediateContext->OMSetRenderTargets(8, nullRTV, NULL);
|
||||
}
|
||||
probeBBRefcount("after OMSetRenderTargets(null)");
|
||||
|
||||
g_pImmediateContext->ClearState();
|
||||
g_pImmediateContext->Flush();
|
||||
probeBBRefcount("after ClearState");
|
||||
|
||||
// Release OUR views and depth buffer
|
||||
g_pImmediateContext->Flush();
|
||||
probeBBRefcount("after Flush");
|
||||
|
||||
// FLIP-MODE TEST: wait for GPU to finish all pending work before touching
|
||||
// the swap chain. Flush() submits commands but doesn't wait. An event
|
||||
// query gives us a proper GPU-side fence.
|
||||
{
|
||||
D3D11_QUERY_DESC qd = {};
|
||||
qd.Query = D3D11_QUERY_EVENT;
|
||||
ID3D11Query* pQuery = nullptr;
|
||||
if (SUCCEEDED(g_pd3dDevice->CreateQuery(&qd, &pQuery)) && pQuery)
|
||||
{
|
||||
g_pImmediateContext->End(pQuery);
|
||||
BOOL done = FALSE;
|
||||
int spins = 0;
|
||||
while (spins++ < 1000)
|
||||
{
|
||||
HRESULT hrQ = g_pImmediateContext->GetData(pQuery, &done, sizeof(done), 0);
|
||||
if (hrQ == S_OK && done) break;
|
||||
Sleep(1);
|
||||
}
|
||||
FlipTestLog("[ResizeD3D] GPU idle wait: %d spins, done=%d\n", spins, (int)done);
|
||||
pQuery->Release();
|
||||
}
|
||||
}
|
||||
probeBBRefcount("after GPU idle wait");
|
||||
|
||||
// Probe refcounts on the RTV objects themselves. AddRef/Release trick:
|
||||
// Release returns the new refcount after dropping our AddRef, which tells
|
||||
// us how many other refs existed.
|
||||
if (g_pRenderTargetView) {
|
||||
g_pRenderTargetView->AddRef();
|
||||
ULONG rtvRc = g_pRenderTargetView->Release();
|
||||
FlipTestLog("[ResizeD3D] g_pRenderTargetView refcount (self) = %lu\n", rtvRc);
|
||||
}
|
||||
if (*ppRM_RTV) {
|
||||
(*ppRM_RTV)->AddRef();
|
||||
ULONG rmRtvRc = (*ppRM_RTV)->Release();
|
||||
FlipTestLog("[ResizeD3D] *ppRM_RTV refcount (self) = %lu\n", rmRtvRc);
|
||||
}
|
||||
|
||||
FlipTestLog("[ResizeD3D] about to Release(g_pRenderTargetView=%p)\n", (void*)g_pRenderTargetView);
|
||||
if (g_pRenderTargetView) { g_pRenderTargetView->Release(); g_pRenderTargetView = NULL; }
|
||||
probeBBRefcount("after Release(g_pRenderTargetView)");
|
||||
|
||||
if (g_pDepthStencilView) { g_pDepthStencilView->Release(); g_pDepthStencilView = NULL; }
|
||||
if (g_pDepthStencilBuffer) { g_pDepthStencilBuffer->Release(); g_pDepthStencilBuffer = NULL; }
|
||||
probeBBRefcount("after Release(g_pDepth*)");
|
||||
|
||||
// Release the Renderer's internal RTV (at offset 0x28). This is a
|
||||
// DIFFERENT RTV object from g_pRenderTargetView, created by something
|
||||
// during Renderer::Initialise, that also wraps the backbuffer. If
|
||||
// ResizeBuffers fails, the fallback recovery path will recreate it from
|
||||
// the current swap chain so in-world rendering keeps working.
|
||||
if (*ppRM_RTV) {
|
||||
FlipTestLog("[ResizeD3D] releasing *ppRM_RTV=%p\n", (void*)*ppRM_RTV);
|
||||
(*ppRM_RTV)->Release();
|
||||
*ppRM_RTV = nullptr;
|
||||
probeBBRefcount("after Release(*ppRM_RTV)");
|
||||
}
|
||||
|
||||
gdraw_D3D11_PreReset();
|
||||
probeBBRefcount("after gdraw_D3D11_PreReset");
|
||||
|
||||
// Get IDXGIFactory from the existing device BEFORE destroying the old swap
|
||||
// chain. If anything fails before we have a new swap chain, we abort
|
||||
// without destroying the old one — leaving the Renderer in a valid
|
||||
// (old-size) state.
|
||||
IDXGISwapChain* pOldSwapChain = g_pSwapChain;
|
||||
// FLIP-MODE TEST: use ResizeBuffers instead of destroy-and-create.
|
||||
IDXGISwapChain* pOldSwapChain = g_pSwapChain; // kept for recovery-path symmetry
|
||||
bool success = false;
|
||||
HRESULT hr;
|
||||
|
||||
IDXGIDevice* dxgiDevice = NULL;
|
||||
IDXGIAdapter* dxgiAdapter = NULL;
|
||||
IDXGIFactory* dxgiFactory = NULL;
|
||||
hr = g_pd3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
|
||||
if (FAILED(hr)) goto postReset;
|
||||
hr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&dxgiAdapter);
|
||||
if (FAILED(hr)) { dxgiDevice->Release(); goto postReset; }
|
||||
hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&dxgiFactory);
|
||||
dxgiAdapter->Release();
|
||||
dxgiDevice->Release();
|
||||
if (FAILED(hr)) goto postReset;
|
||||
FlipTestLog("[ResizeD3D] pre-resize cleanup done, about to probe backbuffer\n");
|
||||
|
||||
// Create a brand-new swap chain at the target size and swap it in.
|
||||
// Must use the SAME swap-chain config as InitDevice (legacy bitblt
|
||||
// DISCARD model), otherwise DXGI may return E_ACCESSDENIED. The
|
||||
// Renderer's old RTV/SRV/DSV are intentionally NOT released here — they
|
||||
// become orphaned with the old swap chain (tiny leak, but avoids
|
||||
// fighting unknown refs inside the closed-source Renderer library).
|
||||
// Probe the backbuffer refcount just before ResizeBuffers. If it's > 1,
|
||||
// something else is holding a hidden ref and ResizeBuffers will fail.
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC sd = {};
|
||||
sd.BufferCount = 1;
|
||||
sd.BufferDesc.Width = bbW;
|
||||
sd.BufferDesc.Height = bbH;
|
||||
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
// RefreshRate=0/0 matches InitDevice; see comment there.
|
||||
sd.BufferDesc.RefreshRate.Numerator = 0;
|
||||
sd.BufferDesc.RefreshRate.Denominator = 0;
|
||||
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
|
||||
sd.OutputWindow = g_hWnd;
|
||||
sd.SampleDesc.Count = 1;
|
||||
sd.SampleDesc.Quality = 0;
|
||||
sd.Windowed = TRUE;
|
||||
|
||||
IDXGISwapChain* pNewSwapChain = NULL;
|
||||
hr = dxgiFactory->CreateSwapChain(g_pd3dDevice, &sd, &pNewSwapChain);
|
||||
dxgiFactory->Release();
|
||||
if (FAILED(hr) || pNewSwapChain == NULL)
|
||||
ID3D11Texture2D* pBB = nullptr;
|
||||
HRESULT hrProbe = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBB);
|
||||
if (SUCCEEDED(hrProbe) && pBB != nullptr)
|
||||
{
|
||||
app.DebugPrintf("[RESIZE] CreateSwapChain FAILED hr=0x%08X — keeping old swap chain\n", (unsigned)hr);
|
||||
goto postReset;
|
||||
ULONG rc = pBB->Release(); // Release returns the new refcount
|
||||
FlipTestLog("[ResizeD3D] backbuffer refcount just before ResizeBuffers = %lu (want 1)\n", rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
FlipTestLog("[ResizeD3D] GetBuffer probe failed hr=0x%08X\n", (unsigned)hrProbe);
|
||||
}
|
||||
|
||||
// New swap chain created successfully — NOW destroy the old one.
|
||||
pOldSwapChain->Release();
|
||||
g_pSwapChain = pNewSwapChain;
|
||||
}
|
||||
|
||||
// Patch Renderer's swap chain pointer to the new raw swap chain.
|
||||
*ppRM_SC = g_pSwapChain;
|
||||
// Call ResizeBuffers. Pass 0 for BufferCount and DXGI_FORMAT_UNKNOWN so DXGI
|
||||
// preserves whatever the swap chain was originally created with. Flags MUST
|
||||
// match the creation flags (ALLOW_TEARING must be set if it was set at create).
|
||||
{
|
||||
UINT resizeFlags = g_bTearingSupported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
|
||||
// FLIP-MODE TEST: right before ResizeBuffers, ask both debug layers to
|
||||
// enumerate every live object. DXGI's error #19 says buffer references
|
||||
// are outstanding but doesn't name them; ReportLiveObjects will.
|
||||
ReportLiveObjects("pre-ResizeBuffers");
|
||||
|
||||
FlipTestLog("[ResizeD3D] calling ResizeBuffers(0, %d, %d, DXGI_FORMAT_UNKNOWN, 0x%X)\n",
|
||||
bbW, bbH, resizeFlags);
|
||||
// Drain any stale DXGI messages from before the call so the post-call dump is clean
|
||||
DumpDXGIInfoQueue("pre-ResizeBuffers (drain)");
|
||||
hr = g_pSwapChain->ResizeBuffers(0, (UINT)bbW, (UINT)bbH, DXGI_FORMAT_UNKNOWN, resizeFlags);
|
||||
FlipTestLog("[ResizeD3D] ResizeBuffers returned hr=0x%08X\n", (unsigned)hr);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DumpD3D11InfoQueue("after ResizeBuffers fail");
|
||||
DumpDXGIInfoQueue("after ResizeBuffers fail");
|
||||
goto postReset;
|
||||
}
|
||||
DumpD3D11InfoQueue("after ResizeBuffers success");
|
||||
DumpDXGIInfoQueue("after ResizeBuffers success");
|
||||
}
|
||||
|
||||
// Swap chain pointer is unchanged after ResizeBuffers. The Renderer's
|
||||
// internal m_pSwapChain field (offset 0x20) still points to the correct
|
||||
// object, so no patch needed.
|
||||
|
||||
// Create render target views from new backbuffer
|
||||
{
|
||||
@@ -1249,6 +1630,20 @@ postReset:
|
||||
pBB->Release();
|
||||
}
|
||||
}
|
||||
// FLIP-MODE TEST: restore *ppRM_RTV if we nullified it, so in-world
|
||||
// rendering keeps working after a failed ResizeBuffers. Create a new
|
||||
// RTV against the current (unchanged) backbuffer.
|
||||
if (*ppRM_RTV == NULL)
|
||||
{
|
||||
ID3D11Texture2D* pBB2 = NULL;
|
||||
if (SUCCEEDED(g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBB2)))
|
||||
{
|
||||
HRESULT hrRe = g_pd3dDevice->CreateRenderTargetView(pBB2, NULL, ppRM_RTV);
|
||||
FlipTestLog("[ResizeD3D] recovery: recreated *ppRM_RTV hr=0x%08X new=%p\n",
|
||||
(unsigned)hrRe, (void*)*ppRM_RTV);
|
||||
pBB2->Release();
|
||||
}
|
||||
}
|
||||
if (g_pDepthStencilView == NULL)
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC dd = {};
|
||||
@@ -1280,7 +1675,7 @@ postReset:
|
||||
ui.updateScreenSize(recW, recH);
|
||||
}
|
||||
|
||||
app.DebugPrintf("[RESIZE] FAILED but recovered views at %dx%d\n", g_rScreenWidth, g_rScreenHeight);
|
||||
FlipTestLog("[ResizeD3D] FAILED path: recovered views at %dx%d\n", g_rScreenWidth, g_rScreenHeight);
|
||||
}
|
||||
|
||||
gdraw_D3D11_PostReset();
|
||||
@@ -1292,6 +1687,8 @@ postReset:
|
||||
if (success)
|
||||
PostProcesser::GetInstance().Init();
|
||||
|
||||
FlipTestLog("[ResizeD3D] returning %s (final backbuffer %dx%d)\n",
|
||||
success ? "success" : "FAILURE", g_rScreenWidth, g_rScreenHeight);
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -1338,99 +1735,16 @@ void SetExclusiveFullscreen(bool enabled)
|
||||
g_bPendingExclusiveFullscreenValue = enabled;
|
||||
}
|
||||
|
||||
// Enter or leave true DXGI exclusive fullscreen. With our bitblt swap chain,
|
||||
// Present(SyncInterval=0) in exclusive mode produces real screen tearing via
|
||||
// direct scanout (DWM is out of the pipeline). Flip mode with ALLOW_TEARING
|
||||
// would also work but is blocked by the 4J Renderer's deferred context refs
|
||||
// on the backbuffer, which DXGI's ResizeBuffers cannot release.
|
||||
// Uses borderless fullscreen (ToggleFullscreen) rather than DXGI SetFullscreenState.
|
||||
// True DXGI exclusive fullscreen is blocked by the 4J Renderer holding hidden
|
||||
// backbuffer references that prevent ResizeBuffers.
|
||||
static void ApplyExclusiveFullscreen(bool enabled)
|
||||
{
|
||||
if (!g_pSwapChain)
|
||||
return;
|
||||
|
||||
LONG styleBefore = GetWindowLong(g_hWnd, GWL_STYLE);
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
// Grow the window to cover the monitor first. This fires WM_SIZE which
|
||||
// runs ResizeD3D and recreates the backbuffer at monitor-native size.
|
||||
// Otherwise a small windowed backbuffer would enter exclusive fullscreen
|
||||
// at that smaller size and DXGI would scale it to fill the monitor,
|
||||
// producing a filtered / washed-out look.
|
||||
HMONITOR hMon = MonitorFromWindow(g_hWnd, MONITOR_DEFAULTTOPRIMARY);
|
||||
MONITORINFO mi = {};
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (GetMonitorInfo(hMon, &mi))
|
||||
{
|
||||
int monW = mi.rcMonitor.right - mi.rcMonitor.left;
|
||||
int monH = mi.rcMonitor.bottom - mi.rcMonitor.top;
|
||||
SetWindowLong(g_hWnd, GWL_STYLE, (styleBefore & ~WS_OVERLAPPEDWINDOW) | WS_VISIBLE);
|
||||
SetWindowPos(g_hWnd, HWND_TOP,
|
||||
mi.rcMonitor.left, mi.rcMonitor.top, monW, monH,
|
||||
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
|
||||
|
||||
// ResizeTarget pins the display mode to the backbuffer size with
|
||||
// no scaling. Microsoft's pattern is ResizeTarget then
|
||||
// SetFullscreenState then ResizeTarget again (see below).
|
||||
DXGI_MODE_DESC targetMode = {};
|
||||
targetMode.Width = monW;
|
||||
targetMode.Height = monH;
|
||||
targetMode.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
targetMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
||||
targetMode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
||||
g_pSwapChain->ResizeTarget(&targetMode);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT hr = g_pSwapChain->SetFullscreenState(enabled ? TRUE : FALSE, nullptr);
|
||||
if (FAILED(hr))
|
||||
return;
|
||||
|
||||
g_bDxgiExclusiveFullscreen = enabled;
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
// Explicitly declare sRGB. Default for R8G8B8A8_UNORM but some drivers
|
||||
// behave differently if the color space is never set.
|
||||
IDXGISwapChain3* pSwapChain3 = nullptr;
|
||||
if (SUCCEEDED(g_pSwapChain->QueryInterface(__uuidof(IDXGISwapChain3), (void**)&pSwapChain3)) && pSwapChain3)
|
||||
{
|
||||
UINT colorSpaceSupport = 0;
|
||||
pSwapChain3->CheckColorSpaceSupport(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, &colorSpaceSupport);
|
||||
if (colorSpaceSupport & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)
|
||||
pSwapChain3->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709);
|
||||
pSwapChain3->Release();
|
||||
}
|
||||
|
||||
// Second ResizeTarget per Microsoft's recommendation to make the mode stick.
|
||||
HMONITOR hMon2 = MonitorFromWindow(g_hWnd, MONITOR_DEFAULTTOPRIMARY);
|
||||
MONITORINFO mi2 = {};
|
||||
mi2.cbSize = sizeof(mi2);
|
||||
if (GetMonitorInfo(hMon2, &mi2))
|
||||
{
|
||||
DXGI_MODE_DESC targetMode2 = {};
|
||||
targetMode2.Width = mi2.rcMonitor.right - mi2.rcMonitor.left;
|
||||
targetMode2.Height = mi2.rcMonitor.bottom - mi2.rcMonitor.top;
|
||||
targetMode2.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
targetMode2.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
||||
targetMode2.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
||||
g_pSwapChain->ResizeTarget(&targetMode2);
|
||||
}
|
||||
g_isFullscreen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Force a real decorated windowed state on exit. DXGI would otherwise
|
||||
// restore whatever state the window had before SetFullscreenState,
|
||||
// which may still be borderless.
|
||||
SetWindowLong(g_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
|
||||
const int w = 1280, h = 720;
|
||||
const int sw = GetSystemMetrics(SM_CXSCREEN);
|
||||
const int sh = GetSystemMetrics(SM_CYSCREEN);
|
||||
SetWindowPos(g_hWnd, HWND_TOP, (sw - w) / 2, (sh - h) / 2, w, h,
|
||||
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
|
||||
g_isFullscreen = false;
|
||||
}
|
||||
// Toggle into/out of borderless fullscreen if state doesn't match
|
||||
if (enabled && !g_isFullscreen)
|
||||
ToggleFullscreen();
|
||||
else if (!enabled && g_isFullscreen)
|
||||
ToggleFullscreen();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
@@ -1665,13 +1979,10 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Restore fullscreen state from previous session. Route through the
|
||||
// deferred exclusive fullscreen path so the main loop applies it on the
|
||||
// first tick (safer than transitioning during init).
|
||||
if ((LoadFullscreenOption() && !g_isFullscreen) || launchOptions.fullscreen)
|
||||
// Restore fullscreen state from previous session
|
||||
if (LoadFullscreenOption() && !g_isFullscreen || launchOptions.fullscreen)
|
||||
{
|
||||
g_bPendingExclusiveFullscreen = true;
|
||||
g_bPendingExclusiveFullscreenValue = true;
|
||||
ToggleFullscreen();
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -1963,16 +2274,15 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||
|
||||
RenderManager.Set_matrixDirty();
|
||||
#endif
|
||||
// Present the frame. RenderManager.Present() hardcodes SyncInterval=1,
|
||||
// so when VSync is off we bypass it for uncapped frames. In DXGI
|
||||
// exclusive fullscreen this produces real tearing via direct scanout.
|
||||
// Force VSync on the main menu regardless of user setting: an uncapped
|
||||
// simple menu scene drives the GPU to thousands of FPS and causes coil
|
||||
// whine on many cards.
|
||||
const bool forceVSyncForMenu = !app.GetGameStarted();
|
||||
if (!g_bVSync && !forceVSyncForMenu && g_pSwapChain)
|
||||
// Present the frame.
|
||||
// RenderManager.Present() hardcodes SyncInterval=1 internally.
|
||||
// When VSync is off, bypass it and call the swap chain directly
|
||||
// with SyncInterval=0 and DXGI_PRESENT_ALLOW_TEARING so DWM lets the
|
||||
// frame tear instead of VSync-locking us to the refresh rate.
|
||||
if (!g_bVSync && g_pSwapChain)
|
||||
{
|
||||
HRESULT hrPresent = g_pSwapChain->Present(0, 0);
|
||||
UINT presentFlags = g_bTearingSupported ? DXGI_PRESENT_ALLOW_TEARING : 0;
|
||||
HRESULT hrPresent = g_pSwapChain->Present(0, presentFlags);
|
||||
// If the direct Present fails (e.g. during fullscreen transition),
|
||||
// fall back to the library's VSync'd Present for this frame.
|
||||
if (FAILED(hrPresent))
|
||||
@@ -2071,11 +2381,11 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
||||
}
|
||||
#endif
|
||||
|
||||
// toggle fullscreen (DXGI exclusive via ApplyExclusiveFullscreen)
|
||||
// toggle fullscreen
|
||||
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_FULLSCREEN))
|
||||
{
|
||||
ApplyExclusiveFullscreen(!g_bDxgiExclusiveFullscreen);
|
||||
app.SetGameSettings(ProfileManager.GetPrimaryPad(), eGameSetting_ExclusiveFullscreen, g_bDxgiExclusiveFullscreen ? 1 : 0);
|
||||
ToggleFullscreen();
|
||||
app.SetGameSettings(ProfileManager.GetPrimaryPad(), eGameSetting_ExclusiveFullscreen, g_isFullscreen ? 1 : 0);
|
||||
}
|
||||
|
||||
// Apply deferred exclusive fullscreen toggle
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,12 @@
|
||||
set(BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Windows64/")
|
||||
|
||||
set(_MINECRAFT_CLIENT_WINDOWS_COMMON_RES_AUDIO
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Common/res/audio/minecraft.xsb"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Common/res/audio/resident.xwb"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Common/res/audio/streamed.xwb"
|
||||
)
|
||||
source_group("Common/res/audio" FILES ${_MINECRAFT_CLIENT_WINDOWS_COMMON_RES_AUDIO})
|
||||
|
||||
set(_MINECRAFT_CLIENT_WINDOWS_COMMON_AUDIO
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Common/Audio/SoundEngine.cpp"
|
||||
)
|
||||
@@ -465,6 +472,7 @@ set(_MINECRAFT_CLIENT_WINDOWS_NET_MINECRAFT_STATS
|
||||
source_group("net/minecraft/stats" FILES ${_MINECRAFT_CLIENT_WINDOWS_NET_MINECRAFT_STATS})
|
||||
|
||||
set(MINECRAFT_CLIENT_WINDOWS
|
||||
${_MINECRAFT_CLIENT_WINDOWS_COMMON_RES_AUDIO}
|
||||
${_MINECRAFT_CLIENT_WINDOWS_COMMON_AUDIO}
|
||||
${_MINECRAFT_CLIENT_WINDOWS_COMMON_NETWORK}
|
||||
${_MINECRAFT_CLIENT_WINDOWS_COMMON_UI}
|
||||
|
||||
@@ -123,7 +123,7 @@ void glClearColor(float r, float g, float b, float a)
|
||||
|
||||
RenderManager.SetClearColour(D3DCOLOR_RGBA(ir,ig,ib,ia));
|
||||
#else
|
||||
float rgba[4] = {r,g,b,1.0f}; // Force alpha=1 to prevent DWM window transparency
|
||||
float rgba[4] = {r,g,b,a};
|
||||
RenderManager.SetClearColour(rgba);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
namespace Minecraft.Server.FourKit.Block;
|
||||
|
||||
|
||||
public enum Biome
|
||||
{
|
||||
OCEAN = 0,
|
||||
PLAINS = 1,
|
||||
DESERT = 2,
|
||||
EXTREME_HILLS = 3,
|
||||
FOREST = 4,
|
||||
TAIGA = 5,
|
||||
SWAMPLAND = 6,
|
||||
RIVER = 7,
|
||||
HELL = 8,
|
||||
SKY = 9,
|
||||
FROZEN_OCEAN = 10,
|
||||
FROZEN_RIVER = 11,
|
||||
ICE_PLAINS = 12,
|
||||
ICE_MOUNTAINS = 13,
|
||||
MUSHROOM_ISLAND = 14,
|
||||
MUSHROOM_SHORE = 15,
|
||||
BEACH = 16,
|
||||
DESERT_HILLS = 17,
|
||||
FOREST_HILLS = 18,
|
||||
TAIGA_HILLS = 19,
|
||||
SMALL_MOUNTAINS = 20,
|
||||
JUNGLE = 21,
|
||||
JUNGLE_HILLS = 22,
|
||||
}
|
||||
|
||||
|
||||
// more for internal
|
||||
// eliminates unnecessary overhead
|
||||
internal static class BiomeHelper
|
||||
{
|
||||
private static readonly double[] _temperatures = new double[23];
|
||||
private static readonly double[] _rainfalls = new double[23];
|
||||
|
||||
static BiomeHelper()
|
||||
{
|
||||
_temperatures[(int)Biome.OCEAN] = 0.5;
|
||||
_temperatures[(int)Biome.PLAINS] = 0.8;
|
||||
_temperatures[(int)Biome.DESERT] = 2.0;
|
||||
_temperatures[(int)Biome.EXTREME_HILLS] = 0.2;
|
||||
_temperatures[(int)Biome.FOREST] = 0.7;
|
||||
_temperatures[(int)Biome.TAIGA] = 0.05;
|
||||
_temperatures[(int)Biome.SWAMPLAND] = 0.8;
|
||||
_temperatures[(int)Biome.RIVER] = 0.5;
|
||||
_temperatures[(int)Biome.HELL] = 2.0;
|
||||
_temperatures[(int)Biome.SKY] = 0.5;
|
||||
_temperatures[(int)Biome.FROZEN_OCEAN] = 0.0;
|
||||
_temperatures[(int)Biome.FROZEN_RIVER] = 0.0;
|
||||
_temperatures[(int)Biome.ICE_PLAINS] = 0.0;
|
||||
_temperatures[(int)Biome.ICE_MOUNTAINS] = 0.0;
|
||||
_temperatures[(int)Biome.MUSHROOM_ISLAND] = 0.9;
|
||||
_temperatures[(int)Biome.MUSHROOM_SHORE] = 0.9;
|
||||
_temperatures[(int)Biome.BEACH] = 0.8;
|
||||
_temperatures[(int)Biome.DESERT_HILLS] = 2.0;
|
||||
_temperatures[(int)Biome.FOREST_HILLS] = 0.7;
|
||||
_temperatures[(int)Biome.TAIGA_HILLS] = 0.05;
|
||||
_temperatures[(int)Biome.SMALL_MOUNTAINS] = 0.2;
|
||||
_temperatures[(int)Biome.JUNGLE] = 1.2;
|
||||
_temperatures[(int)Biome.JUNGLE_HILLS] = 1.2;
|
||||
|
||||
_rainfalls[(int)Biome.OCEAN] = 0.5;
|
||||
_rainfalls[(int)Biome.PLAINS] = 0.4;
|
||||
_rainfalls[(int)Biome.DESERT] = 0.0;
|
||||
_rainfalls[(int)Biome.EXTREME_HILLS] = 0.3;
|
||||
_rainfalls[(int)Biome.FOREST] = 0.8;
|
||||
_rainfalls[(int)Biome.TAIGA] = 0.8;
|
||||
_rainfalls[(int)Biome.SWAMPLAND] = 0.9;
|
||||
_rainfalls[(int)Biome.RIVER] = 0.5;
|
||||
_rainfalls[(int)Biome.HELL] = 0.0;
|
||||
_rainfalls[(int)Biome.SKY] = 0.5;
|
||||
_rainfalls[(int)Biome.FROZEN_OCEAN] = 0.5;
|
||||
_rainfalls[(int)Biome.FROZEN_RIVER] = 0.5;
|
||||
_rainfalls[(int)Biome.ICE_PLAINS] = 0.5;
|
||||
_rainfalls[(int)Biome.ICE_MOUNTAINS] = 0.5;
|
||||
_rainfalls[(int)Biome.MUSHROOM_ISLAND] = 1.0;
|
||||
_rainfalls[(int)Biome.MUSHROOM_SHORE] = 1.0;
|
||||
_rainfalls[(int)Biome.BEACH] = 0.4;
|
||||
_rainfalls[(int)Biome.DESERT_HILLS] = 0.0;
|
||||
_rainfalls[(int)Biome.FOREST_HILLS] = 0.8;
|
||||
_rainfalls[(int)Biome.TAIGA_HILLS] = 0.8;
|
||||
_rainfalls[(int)Biome.SMALL_MOUNTAINS] = 0.3;
|
||||
_rainfalls[(int)Biome.JUNGLE] = 0.9;
|
||||
_rainfalls[(int)Biome.JUNGLE_HILLS] = 0.9;
|
||||
}
|
||||
|
||||
public static double getTemperature(this Biome biome)
|
||||
{
|
||||
int id = (int)biome;
|
||||
if (id >= 0 && id < _temperatures.Length) return _temperatures[id];
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
public static double getRainfall(this Biome biome)
|
||||
{
|
||||
int id = (int)biome;
|
||||
if (id >= 0 && id < _rainfalls.Length) return _rainfalls[id];
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
public static Biome fromId(int id)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(Biome), id)) return (Biome)id;
|
||||
return Biome.PLAINS;
|
||||
}
|
||||
}
|
||||
@@ -89,19 +89,7 @@ public class Block
|
||||
/// <returns>Whether the change was successful.</returns>
|
||||
public bool setTypeId(int type)
|
||||
{
|
||||
return setTypeId(type, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type ID of this block.
|
||||
/// </summary>
|
||||
/// <param name="type">Type ID to change this block to.</param>
|
||||
/// <param name="applyPhysics">False to cancel physics on the changed block.</param>
|
||||
/// <returns>Whether the block was changed.</returns>
|
||||
public bool setTypeId(int type, bool applyPhysics)
|
||||
{
|
||||
int flags = applyPhysics ? 3 : 2;
|
||||
NativeBridge.SetTile?.Invoke(_world.getDimensionId(), _x, _y, _z, type, 0, flags);
|
||||
NativeBridge.SetTile?.Invoke(_world.getDimensionId(), _x, _y, _z, type, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -120,43 +108,7 @@ public class Block
|
||||
/// <param name="data">New block specific metadata.</param>
|
||||
public void setData(byte data)
|
||||
{
|
||||
setData(data, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the metadata for this block.
|
||||
/// </summary>
|
||||
/// <param name="data">New block specific metadata.</param>
|
||||
/// <param name="applyPhysics">False to cancel physics from the changed block.</param>
|
||||
public void setData(byte data, bool applyPhysics)
|
||||
{
|
||||
int flags = applyPhysics ? 3 : 2;
|
||||
NativeBridge.SetTileData?.Invoke(_world.getDimensionId(), _x, _y, _z, data, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type ID and data of this block.
|
||||
/// </summary>
|
||||
/// <param name="type">Type ID to change this block to.</param>
|
||||
/// <param name="data">The data value to change this block to.</param>
|
||||
/// <returns>Whether the block was changed.</returns>
|
||||
public bool setTypeIdAndData(int type, byte data)
|
||||
{
|
||||
return setTypeIdAndData(type, data, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type ID and data of this block.
|
||||
/// </summary>
|
||||
/// <param name="type">Type ID to change this block to.</param>
|
||||
/// <param name="data">The data value to change this block to.</param>
|
||||
/// <param name="applyPhysics">False to cancel physics on the changed block.</param>
|
||||
/// <returns>Whether the block was changed.</returns>
|
||||
public bool setTypeIdAndData(int type, byte data, bool applyPhysics)
|
||||
{
|
||||
int flags = applyPhysics ? 3 : 2;
|
||||
NativeBridge.SetTile?.Invoke(_world.getDimensionId(), _x, _y, _z, type, data, flags);
|
||||
return true;
|
||||
NativeBridge.SetTileData?.Invoke(_world.getDimensionId(), _x, _y, _z, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -182,15 +134,6 @@ public class Block
|
||||
return getWorld().getBlockAt(getX() + modX, getY() + modY, getZ() + modZ);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunk which contains this block.
|
||||
/// </summary>
|
||||
/// <returns>Containing Chunk.</returns>
|
||||
public Chunk.Chunk getChunk()
|
||||
{
|
||||
return getWorld().getChunkAt(getX() >> 4, getZ() >> 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block at the given face
|
||||
/// <para>This method is equal to getRelative(face, 1)</para>
|
||||
@@ -219,93 +162,5 @@ public class Block
|
||||
{
|
||||
return getRelative(face.getModX() * distance, face.getModY() * distance, face.getModZ() * distance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the biome that this block resides in.
|
||||
/// </summary>
|
||||
/// <returns>Biome type containing this block.</returns>
|
||||
public Biome getBiome()
|
||||
{
|
||||
if (NativeBridge.GetBiomeId != null)
|
||||
return BiomeHelper.fromId(NativeBridge.GetBiomeId(_world.getDimensionId(), _x, _z));
|
||||
return Biome.PLAINS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the biome that this block resides in.
|
||||
/// </summary>
|
||||
/// <param name="bio">New Biome type for this block.</param>
|
||||
public void setBiome(Biome bio)
|
||||
{
|
||||
NativeBridge.SetBiomeId?.Invoke(_world.getDimensionId(), _x, _z, (int)bio);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the humidity of the biome of this block.
|
||||
/// </summary>
|
||||
/// <returns>Humidity of this block.</returns>
|
||||
public double getHumidity()
|
||||
{
|
||||
return getBiome().getRainfall();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the temperature of the biome of this block.
|
||||
/// </summary>
|
||||
/// <returns>Temperature of this block.</returns>
|
||||
public double getTemperature()
|
||||
{
|
||||
return getBiome().getTemperature();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this block is liquid.
|
||||
/// <para>A block is considered liquid when <see cref="getType()"/> returns
|
||||
/// <see cref="Material.WATER"/>, <see cref="Material.STATIONARY_WATER"/>,
|
||||
/// <see cref="Material.LAVA"/> or <see cref="Material.STATIONARY_LAVA"/>.</para>
|
||||
/// </summary>
|
||||
/// <returns>true if this block is liquid.</returns>
|
||||
public bool isLiquid()
|
||||
{
|
||||
Material type = getType();
|
||||
return type == Material.WATER || type == Material.STATIONARY_WATER ||
|
||||
type == Material.LAVA || type == Material.STATIONARY_LAVA;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the light level between 0-15.
|
||||
/// </summary>
|
||||
/// <returns>Light level.</returns>
|
||||
public byte getLightLevel()
|
||||
{
|
||||
int sky = getLightFromSky();
|
||||
int block = getLightFromBlocks();
|
||||
return (byte)(sky > block ? sky : block);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the amount of light at this block from the sky.
|
||||
/// Any light given from other sources (such as blocks like torches) will be ignored.
|
||||
/// </summary>
|
||||
/// <returns>Sky light level.</returns>
|
||||
public byte getLightFromSky()
|
||||
{
|
||||
if (NativeBridge.GetSkyLight != null)
|
||||
return (byte)NativeBridge.GetSkyLight(_world.getDimensionId(), _x, _y, _z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the amount of light at this block from nearby blocks.
|
||||
/// Any light given from other sources (such as the sun) will be ignored.
|
||||
/// </summary>
|
||||
/// <returns>Block light level.</returns>
|
||||
public byte getLightFromBlocks()
|
||||
{
|
||||
if (NativeBridge.GetBlockLight != null)
|
||||
return (byte)NativeBridge.GetBlockLight(_world.getDimensionId(), _x, _y, _z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -92,15 +92,6 @@ public class BlockState
|
||||
/// <returns>Z-coordinate.</returns>
|
||||
public int getZ() => _z;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunk which contains this block.
|
||||
/// </summary>
|
||||
/// <returns>Containing Chunk.</returns>
|
||||
public Chunk.Chunk getChunk()
|
||||
{
|
||||
return _world.getChunkAt(_x >> 4, _z >> 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location of this block.
|
||||
/// </summary>
|
||||
@@ -200,16 +191,7 @@ public class BlockState
|
||||
if (!force && currentType != _typeId)
|
||||
return false;
|
||||
|
||||
NativeBridge.SetTile(_world.getDimensionId(), _x, _y, _z, _typeId, _data, applyPhysics ? 3 : 2);
|
||||
NativeBridge.SetTile(_world.getDimensionId(), _x, _y, _z, _typeId, _data);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the light level between 0-15.
|
||||
/// </summary>
|
||||
/// <returns>Light level.</returns>
|
||||
public byte getLightLevel()
|
||||
{
|
||||
return getBlock().getLightLevel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Minecraft.Server.FourKit;
|
||||
|
||||
/// <summary>
|
||||
/// All supported color values for chat.
|
||||
/// </summary>
|
||||
public class ChatColor
|
||||
{
|
||||
/// <summary>
|
||||
/// The special character which prefixes all chat colour codes.
|
||||
/// </summary>
|
||||
public static readonly char COLOR_CHAR = '\u00A7'; // F
|
||||
|
||||
private static readonly Regex STRIP_COLOR_PATTERN =
|
||||
new Regex("(?i)" + COLOR_CHAR + "[0-9A-FK-OR]", RegexOptions.Compiled);
|
||||
|
||||
private static readonly Dictionary<char, ChatColor> BY_CHAR = new();
|
||||
|
||||
/// <summary>Represents black.</summary>
|
||||
public static readonly ChatColor BLACK = new('0', false);
|
||||
/// <summary>Represents dark blue.</summary>
|
||||
public static readonly ChatColor DARK_BLUE = new('1', false);
|
||||
/// <summary>Represents dark green.</summary>
|
||||
public static readonly ChatColor DARK_GREEN = new('2', false);
|
||||
/// <summary>Represents dark blue (aqua).</summary>
|
||||
public static readonly ChatColor DARK_AQUA = new('3', false);
|
||||
/// <summary>Represents dark red.</summary>
|
||||
public static readonly ChatColor DARK_RED = new('4', false);
|
||||
/// <summary>Represents dark purple.</summary>
|
||||
public static readonly ChatColor DARK_PURPLE = new('5', false);
|
||||
/// <summary>Represents gold.</summary>
|
||||
public static readonly ChatColor GOLD = new('6', false);
|
||||
/// <summary>Represents gray.</summary>
|
||||
public static readonly ChatColor GRAY = new('7', false);
|
||||
/// <summary>Represents dark gray.</summary>
|
||||
public static readonly ChatColor DARK_GRAY = new('8', false);
|
||||
/// <summary>Represents blue.</summary>
|
||||
public static readonly ChatColor BLUE = new('9', false);
|
||||
/// <summary>Represents green.</summary>
|
||||
public static readonly ChatColor GREEN = new('a', false);
|
||||
/// <summary>Represents aqua.</summary>
|
||||
public static readonly ChatColor AQUA = new('b', false);
|
||||
/// <summary>Represents red.</summary>
|
||||
public static readonly ChatColor RED = new('c', false);
|
||||
/// <summary>Represents light purple.</summary>
|
||||
public static readonly ChatColor LIGHT_PURPLE = new('d', false);
|
||||
/// <summary>Represents yellow.</summary>
|
||||
public static readonly ChatColor YELLOW = new('e', false);
|
||||
/// <summary>Represents white.</summary>
|
||||
public static readonly ChatColor WHITE = new('f', false);
|
||||
/// <summary>Resets all previous chat colors or formats.</summary>
|
||||
public static readonly ChatColor RESET = new('r', false);
|
||||
|
||||
private readonly char _code;
|
||||
private readonly bool _isFormat;
|
||||
private readonly string _toString;
|
||||
|
||||
private ChatColor(char code, bool isFormat)
|
||||
{
|
||||
_code = code;
|
||||
_isFormat = isFormat;
|
||||
_toString = new string(new[] { COLOR_CHAR, code });
|
||||
BY_CHAR[code] = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the char value associated with this color.
|
||||
/// </summary>
|
||||
/// <returns>A char value of this color code.</returns>
|
||||
public char getChar() => _code;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this code is a format code as opposed to a color code.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if this is a format code.</returns>
|
||||
public bool isFormat() => _isFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this code is a color code as opposed to a format code.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if this is a color code.</returns>
|
||||
public bool isColor() => !_isFormat && this != RESET;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color represented by the specified color code.
|
||||
/// </summary>
|
||||
/// <param name="code">Code to check.</param>
|
||||
/// <returns>Associative ChatColor with the given code, or null if it doesn't exist.</returns>
|
||||
public static ChatColor? getByChar(char code)
|
||||
{
|
||||
return BY_CHAR.TryGetValue(char.ToLower(code), out var color) ? color : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color represented by the specified color code.
|
||||
/// </summary>
|
||||
/// <param name="code">Code to check.</param>
|
||||
/// <returns>Associative ChatColor with the given code, or null if it doesn't exist.</returns>
|
||||
public static ChatColor? getByChar(string code)
|
||||
{
|
||||
if (string.IsNullOrEmpty(code)) return null;
|
||||
return getByChar(code[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strips the given message of all color codes.
|
||||
/// </summary>
|
||||
/// <param name="input">String to strip of color.</param>
|
||||
/// <returns>A copy of the input string, without any coloring.</returns>
|
||||
public static string? stripColor(string? input)
|
||||
{
|
||||
if (input == null) return null;
|
||||
return STRIP_COLOR_PATTERN.Replace(input, "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates a string using an alternate color code character into a string
|
||||
/// that uses the internal <see cref="COLOR_CHAR"/> color code character.
|
||||
/// The alternate color code character will only be replaced if it is immediately
|
||||
/// followed by 0-9, A-F, a-f, K-O, k-o, R or r.
|
||||
/// </summary>
|
||||
/// <param name="altColorChar">The alternate color code character to replace. Ex: &</param>
|
||||
/// <param name="textToTranslate">Text containing the alternate color code character.</param>
|
||||
/// <returns>Text containing the <see cref="COLOR_CHAR"/> color code character.</returns>
|
||||
public static string translateAlternateColorCodes(char altColorChar, string textToTranslate)
|
||||
{
|
||||
char[] b = textToTranslate.ToCharArray();
|
||||
for (int i = 0; i < b.Length - 1; i++)
|
||||
{
|
||||
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".IndexOf(b[i + 1]) > -1)
|
||||
{
|
||||
b[i] = COLOR_CHAR;
|
||||
}
|
||||
}
|
||||
return new string(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ChatColors used at the end of the given input string.
|
||||
/// </summary>
|
||||
/// <param name="input">Input string to retrieve the colors from.</param>
|
||||
/// <returns>Any remaining ChatColors to pass onto the next line.</returns>
|
||||
public static string getLastColors(string input)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
int length = input.Length;
|
||||
|
||||
for (int index = length - 1; index > -1; index--)
|
||||
{
|
||||
char section = input[index];
|
||||
if (section == COLOR_CHAR && index < length - 1)
|
||||
{
|
||||
char c = input[index + 1];
|
||||
ChatColor? color = getByChar(c);
|
||||
if (color != null)
|
||||
{
|
||||
result.Insert(0, color.ToString());
|
||||
if (color.isColor() || color == RESET)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public static string operator +(ChatColor color, string text) => color.ToString() + text;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => _toString;
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
using Minecraft.Server.FourKit.Entity;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Minecraft.Server.FourKit.Chunk;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a chunk of blocks.
|
||||
/// </summary>
|
||||
public class Chunk
|
||||
{
|
||||
private readonly World _world;
|
||||
private readonly int _chunkX;
|
||||
private readonly int _chunkZ;
|
||||
|
||||
internal Chunk(World world, int chunkX, int chunkZ)
|
||||
{
|
||||
_world = world;
|
||||
_chunkX = chunkX;
|
||||
_chunkZ = chunkZ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X-coordinate of this chunk.
|
||||
/// </summary>
|
||||
/// <returns>X-coordinate.</returns>
|
||||
public int getX() => _chunkX;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Z-coordinate of this chunk.
|
||||
/// </summary>
|
||||
/// <returns>Z-coordinate.</returns>
|
||||
public int getZ() => _chunkZ;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world containing this chunk.
|
||||
/// </summary>
|
||||
/// <returns>Parent World.</returns>
|
||||
public World getWorld() => _world;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a block from this chunk.
|
||||
/// </summary>
|
||||
/// <param name="x">0-15</param>
|
||||
/// <param name="y">0-127</param>
|
||||
/// <param name="z">0-15</param>
|
||||
/// <returns>The Block.</returns>
|
||||
public Block.Block getBlock(int x, int y, int z)
|
||||
{
|
||||
return _world.getBlockAt((_chunkX << 4) + x, y, (_chunkZ << 4) + z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capture thread-safe read-only snapshot of chunk data.
|
||||
/// </summary>
|
||||
/// <returns>ChunkSnapshot.</returns>
|
||||
public ChunkSnapshot getChunkSnapshot()
|
||||
{
|
||||
return getChunkSnapshot(false, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capture thread-safe read-only snapshot of chunk data.
|
||||
/// </summary>
|
||||
/// <param name="includeBiome">If true, snapshot includes per-coordinate biome type.</param>
|
||||
/// <param name="includeBiomeTempRain">If true, snapshot includes per-coordinate raw biome temperature and rainfall.</param>
|
||||
/// <returns>ChunkSnapshot.</returns>
|
||||
public ChunkSnapshot getChunkSnapshot(bool includeBiome, bool includeBiomeTempRain)
|
||||
{
|
||||
// this has a lot of overhead
|
||||
// (SYLV)todo: clean this up
|
||||
int dimId = _world.getDimensionId();
|
||||
int[] blockIds = new int[16 * 128 * 16];
|
||||
int[] blockData = new int[16 * 128 * 16];
|
||||
int[] maxBlockY = new int[16 * 16];
|
||||
int[] skyLight = new int[16 * 128 * 16];
|
||||
int[] blockLight = new int[16 * 128 * 16];
|
||||
int[]? biomeIds = includeBiome ? new int[16 * 16] : null;
|
||||
double[]? biomeTemp = includeBiomeTempRain ? new double[16 * 16] : null;
|
||||
double[]? biomeRainfall = includeBiomeTempRain ? new double[16 * 16] : null;
|
||||
|
||||
if (NativeBridge.GetChunkSnapshot != null)
|
||||
{
|
||||
var hIds = GCHandle.Alloc(blockIds, GCHandleType.Pinned);
|
||||
var hData = GCHandle.Alloc(blockData, GCHandleType.Pinned);
|
||||
var hMaxY = GCHandle.Alloc(maxBlockY, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
NativeBridge.GetChunkSnapshot(dimId, _chunkX, _chunkZ,
|
||||
hIds.AddrOfPinnedObject(),
|
||||
hData.AddrOfPinnedObject(),
|
||||
hMaxY.AddrOfPinnedObject());
|
||||
}
|
||||
finally
|
||||
{
|
||||
hIds.Free();
|
||||
hData.Free();
|
||||
hMaxY.Free();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int lx = 0; lx < 16; lx++)
|
||||
{
|
||||
for (int lz = 0; lz < 16; lz++)
|
||||
{
|
||||
int worldX = (_chunkX << 4) + lx;
|
||||
int worldZ = (_chunkZ << 4) + lz;
|
||||
int highest = 0;
|
||||
for (int ly = 0; ly < 128; ly++)
|
||||
{
|
||||
int idx = (lx * 128 * 16) + (ly * 16) + lz;
|
||||
if (NativeBridge.GetTileId != null)
|
||||
blockIds[idx] = NativeBridge.GetTileId(dimId, worldX, ly, worldZ);
|
||||
if (NativeBridge.GetTileData != null)
|
||||
blockData[idx] = NativeBridge.GetTileData(dimId, worldX, ly, worldZ);
|
||||
if (blockIds[idx] != 0)
|
||||
highest = ly;
|
||||
}
|
||||
maxBlockY[lx * 16 + lz] = highest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int lx = 0; lx < 16; lx++)
|
||||
{
|
||||
for (int lz = 0; lz < 16; lz++)
|
||||
{
|
||||
int worldX = (_chunkX << 4) + lx;
|
||||
int worldZ = (_chunkZ << 4) + lz;
|
||||
for (int ly = 0; ly < 128; ly++)
|
||||
{
|
||||
int idx = (lx * 128 * 16) + (ly * 16) + lz;
|
||||
if (NativeBridge.GetSkyLight != null)
|
||||
skyLight[idx] = NativeBridge.GetSkyLight(dimId, worldX, ly, worldZ);
|
||||
if (NativeBridge.GetBlockLight != null)
|
||||
blockLight[idx] = NativeBridge.GetBlockLight(dimId, worldX, ly, worldZ);
|
||||
}
|
||||
if (includeBiome && NativeBridge.GetBiomeId != null)
|
||||
{
|
||||
int colIdx = lx * 16 + lz;
|
||||
int biomeId = NativeBridge.GetBiomeId(dimId, worldX, worldZ);
|
||||
biomeIds![colIdx] = biomeId;
|
||||
if (includeBiomeTempRain)
|
||||
{
|
||||
var biome = Block.BiomeHelper.fromId(biomeId);
|
||||
biomeTemp![colIdx] = biome.getTemperature();
|
||||
biomeRainfall![colIdx] = biome.getRainfall();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long captureTime = 0;
|
||||
if (NativeBridge.GetWorldInfo != null)
|
||||
{
|
||||
double[] info = new double[7];
|
||||
var hInfo = GCHandle.Alloc(info, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
NativeBridge.GetWorldInfo(dimId, hInfo.AddrOfPinnedObject());
|
||||
}
|
||||
finally
|
||||
{
|
||||
hInfo.Free();
|
||||
}
|
||||
captureTime = (long)info[4];
|
||||
}
|
||||
|
||||
return new ChunkSnapshot(_chunkX, _chunkZ, _world.getName(), captureTime,
|
||||
blockIds, blockData, maxBlockY,
|
||||
skyLight, blockLight, biomeIds, biomeTemp, biomeRainfall);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capture thread-safe read-only snapshot of chunk data.
|
||||
/// </summary>
|
||||
/// <param name="includeMaxblocky">(NONFUNCTIONAL) Only here for parity.</param>
|
||||
/// <param name="includeBiome">If true, snapshot includes per-coordinate biome type.</param>
|
||||
/// <param name="includeBiomeTempRain">If true, snapshot includes per-coordinate raw biome temperature and rainfall.</param>
|
||||
/// <returns>ChunkSnapshot.</returns>
|
||||
public ChunkSnapshot getChunkSnapshot(bool includeMaxblocky, bool includeBiome, bool includeBiomeTempRain)
|
||||
{
|
||||
return getChunkSnapshot(includeBiome, includeBiomeTempRain);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of all entities in the chunk.
|
||||
/// </summary>
|
||||
/// <returns>The entities.</returns>
|
||||
public Entity.Entity[] getEntities()
|
||||
{
|
||||
if (NativeBridge.GetChunkEntities == null) return Array.Empty<Entity.Entity>();
|
||||
|
||||
int dimId = _world.getDimensionId();
|
||||
int count = NativeBridge.GetChunkEntities(dimId, _chunkX, _chunkZ, out IntPtr buf);
|
||||
if (count <= 0 || buf == IntPtr.Zero) return Array.Empty<Entity.Entity>();
|
||||
|
||||
var result = new Entity.Entity[count];
|
||||
try
|
||||
{
|
||||
int[] data = new int[count * 3];
|
||||
Marshal.Copy(buf, data, 0, count * 3);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int entityId = data[i * 3];
|
||||
int mappedType = data[i * 3 + 1];
|
||||
int isLiving = data[i * 3 + 2];
|
||||
|
||||
var entityType = Enum.IsDefined(typeof(Entity.EntityType), mappedType)
|
||||
? (Entity.EntityType)mappedType
|
||||
: Entity.EntityType.UNKNOWN;
|
||||
|
||||
if (entityType == Entity.EntityType.PLAYER)
|
||||
{
|
||||
var player = FourKit.GetPlayerByEntityId(entityId);
|
||||
if (player != null)
|
||||
{
|
||||
result[i] = player;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isLiving == 1)
|
||||
{
|
||||
result[i] = new Entity.LivingEntity(entityId, entityType, dimId, 0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var entity = new Entity.Entity();
|
||||
entity.SetEntityIdInternal(entityId);
|
||||
entity.SetEntityTypeInternal(entityType);
|
||||
entity.SetDimensionInternal(dimId);
|
||||
result[i] = entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(buf);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the chunk is loaded.
|
||||
/// </summary>
|
||||
/// <returns>True if it is loaded.</returns>
|
||||
public bool isLoaded()
|
||||
{
|
||||
if (NativeBridge.IsChunkLoaded != null)
|
||||
return NativeBridge.IsChunkLoaded(_world.getDimensionId(), _chunkX, _chunkZ) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the chunk.
|
||||
/// </summary>
|
||||
/// <param name="generate">Whether or not to generate a chunk if it doesn't already exist.</param>
|
||||
/// <returns>true if the chunk has loaded successfully, otherwise false.</returns>
|
||||
public bool load(bool generate)
|
||||
{
|
||||
if (NativeBridge.LoadChunk != null)
|
||||
return NativeBridge.LoadChunk(_world.getDimensionId(), _chunkX, _chunkZ, generate ? 1 : 0) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the chunk.
|
||||
/// </summary>
|
||||
/// <returns>true if the chunk has loaded successfully, otherwise false.</returns>
|
||||
public bool load()
|
||||
{
|
||||
return load(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads and optionally saves the Chunk.
|
||||
/// </summary>
|
||||
/// <param name="save">Controls whether the chunk is saved.</param>
|
||||
/// <param name="safe">Controls whether to unload the chunk when players are nearby.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unload(bool save, bool safe)
|
||||
{
|
||||
if (NativeBridge.UnloadChunk != null)
|
||||
return NativeBridge.UnloadChunk(_world.getDimensionId(), _chunkX, _chunkZ, save ? 1 : 0, safe ? 1 : 0) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads and optionally saves the Chunk.
|
||||
/// </summary>
|
||||
/// <param name="save">Controls whether the chunk is saved.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unload(bool save)
|
||||
{
|
||||
return unload(save, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads and optionally saves the Chunk.
|
||||
/// </summary>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unload()
|
||||
{
|
||||
return unload(true, true);
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
namespace Minecraft.Server.FourKit.Chunk;
|
||||
|
||||
using Minecraft.Server.FourKit.Block;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a static, thread-safe snapshot of chunk of blocks.
|
||||
/// Purpose is to allow clean, efficient copy of a chunk data to be made, and
|
||||
/// then handed off for processing in another thread (e.g. map rendering).
|
||||
/// </summary>
|
||||
public class ChunkSnapshot
|
||||
{
|
||||
private readonly int _chunkX;
|
||||
private readonly int _chunkZ;
|
||||
private readonly string _worldName;
|
||||
private readonly long _captureFullTime;
|
||||
private readonly int[] _blockIds;
|
||||
private readonly int[] _blockData;
|
||||
private readonly int[] _maxBlockY;
|
||||
private readonly int[]? _skyLight;
|
||||
private readonly int[]? _blockLight;
|
||||
private readonly int[]? _biome;
|
||||
private readonly double[]? _biomeTemp;
|
||||
private readonly double[]? _biomeRainfall;
|
||||
|
||||
internal ChunkSnapshot(int chunkX, int chunkZ, string worldName, long captureFullTime,
|
||||
int[] blockIds, int[] blockData, int[] maxBlockY,
|
||||
int[]? skyLight = null, int[]? blockLight = null,
|
||||
int[]? biome = null, double[]? biomeTemp = null, double[]? biomeRainfall = null)
|
||||
{
|
||||
_chunkX = chunkX;
|
||||
_chunkZ = chunkZ;
|
||||
_worldName = worldName;
|
||||
_captureFullTime = captureFullTime;
|
||||
_blockIds = blockIds;
|
||||
_blockData = blockData;
|
||||
_maxBlockY = maxBlockY;
|
||||
_skyLight = skyLight;
|
||||
_blockLight = blockLight;
|
||||
_biome = biome;
|
||||
_biomeTemp = biomeTemp;
|
||||
_biomeRainfall = biomeRainfall;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X-coordinate of this chunk.
|
||||
/// </summary>
|
||||
/// <returns>X-coordinate.</returns>
|
||||
public int getX() => _chunkX;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Z-coordinate of this chunk.
|
||||
/// </summary>
|
||||
/// <returns>Z-coordinate.</returns>
|
||||
public int getZ() => _chunkZ;
|
||||
|
||||
/// <summary>
|
||||
/// Gets name of the world containing this chunk.
|
||||
/// </summary>
|
||||
/// <returns>Parent World Name.</returns>
|
||||
public string getWorldName() => _worldName;
|
||||
|
||||
/// <summary>
|
||||
/// Get block type for block at corresponding coordinate in the chunk.
|
||||
/// </summary>
|
||||
/// <param name="x">0-15</param>
|
||||
/// <param name="y">0-127</param>
|
||||
/// <param name="z">0-15</param>
|
||||
/// <returns>0-255</returns>
|
||||
public int getBlockTypeId(int x, int y, int z)
|
||||
{
|
||||
int idx = (x * 128 * 16) + (y * 16) + z;
|
||||
if (idx < 0 || idx >= _blockIds.Length) return 0;
|
||||
return _blockIds[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get block data for block at corresponding coordinate in the chunk.
|
||||
/// </summary>
|
||||
/// <param name="x">0-15</param>
|
||||
/// <param name="y">0-127</param>
|
||||
/// <param name="z">0-15</param>
|
||||
/// <returns>0-15</returns>
|
||||
public int getBlockData(int x, int y, int z)
|
||||
{
|
||||
int idx = (x * 128 * 16) + (y * 16) + z;
|
||||
if (idx < 0 || idx >= _blockData.Length) return 0;
|
||||
return _blockData[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the highest non-air coordinate at the given coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the blocks.</param>
|
||||
/// <param name="z">Z-coordinate of the blocks.</param>
|
||||
/// <returns>Y-coordinate of the highest non-air block.</returns>
|
||||
public int getHighestBlockYAt(int x, int z)
|
||||
{
|
||||
int idx = x * 16 + z;
|
||||
if (idx < 0 || idx >= _maxBlockY.Length) return 0;
|
||||
return _maxBlockY[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get world full time when chunk snapshot was captured.
|
||||
/// </summary>
|
||||
/// <returns>Time in ticks.</returns>
|
||||
public long getCaptureFullTime() => _captureFullTime;
|
||||
|
||||
/// <summary>
|
||||
/// Test if section is empty.
|
||||
/// </summary>
|
||||
/// <param name="sy">Section Y coordinate (block Y / 16).</param>
|
||||
/// <returns>true if empty, false if not.</returns>
|
||||
public bool isSectionEmpty(int sy)
|
||||
{
|
||||
int startY = sy * 16;
|
||||
int endY = startY + 16;
|
||||
if (endY > 128) endY = 128;
|
||||
for (int x = 0; x < 16; x++)
|
||||
{
|
||||
for (int z = 0; z < 16; z++)
|
||||
{
|
||||
for (int y = startY; y < endY; y++)
|
||||
{
|
||||
int idx = (x * 128 * 16) + (y * 16) + z;
|
||||
if (idx >= 0 && idx < _blockIds.Length && _blockIds[idx] != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get sky light level for block at corresponding coordinate in the chunk.
|
||||
/// </summary>
|
||||
/// <param name="x">0-15</param>
|
||||
/// <param name="y">0-127</param>
|
||||
/// <param name="z">0-15</param>
|
||||
/// <returns>0-15</returns>
|
||||
public int getBlockSkyLight(int x, int y, int z)
|
||||
{
|
||||
if (_skyLight == null) return 0;
|
||||
int idx = (x * 128 * 16) + (y * 16) + z;
|
||||
if (idx < 0 || idx >= _skyLight.Length) return 0;
|
||||
return _skyLight[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get light level emitted by block at corresponding coordinate in the chunk.
|
||||
/// </summary>
|
||||
/// <param name="x">0-15</param>
|
||||
/// <param name="y">0-127</param>
|
||||
/// <param name="z">0-15</param>
|
||||
/// <returns>0-15</returns>
|
||||
public int getBlockEmittedLight(int x, int y, int z)
|
||||
{
|
||||
if (_blockLight == null) return 0;
|
||||
int idx = (x * 128 * 16) + (y * 16) + z;
|
||||
if (idx < 0 || idx >= _blockLight.Length) return 0;
|
||||
return _blockLight[idx];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get biome at given coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate (0-15)</param>
|
||||
/// <param name="z">Z-coordinate (0-15)</param>
|
||||
/// <returns>Biome at given coordinate.</returns>
|
||||
public Biome getBiome(int x, int z)
|
||||
{
|
||||
if (_biome == null) return Biome.PLAINS;
|
||||
int idx = x * 16 + z;
|
||||
if (idx < 0 || idx >= _biome.Length) return Biome.PLAINS;
|
||||
return BiomeHelper.fromId(_biome[idx]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get raw biome temperature (0.0-1.0) at given coordinate.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate (0-15)</param>
|
||||
/// <param name="z">Z-coordinate (0-15)</param>
|
||||
/// <returns>Temperature at given coordinate.</returns>
|
||||
public double getRawBiomeTemperature(int x, int z)
|
||||
{
|
||||
if (_biomeTemp != null)
|
||||
{
|
||||
int idx = x * 16 + z;
|
||||
if (idx >= 0 && idx < _biomeTemp.Length) return _biomeTemp[idx];
|
||||
}
|
||||
return getBiome(x, z).getTemperature();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get raw biome rainfall (0.0-1.0) at given coordinate.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate (0-15)</param>
|
||||
/// <param name="z">Z-coordinate (0-15)</param>
|
||||
/// <returns>Rainfall at given coordinate.</returns>
|
||||
public double getRawBiomeRainfall(int x, int z)
|
||||
{
|
||||
if (_biomeRainfall != null)
|
||||
{
|
||||
int idx = x * 16 + z;
|
||||
if (idx >= 0 && idx < _biomeRainfall.Length) return _biomeRainfall[idx];
|
||||
}
|
||||
return getBiome(x, z).getRainfall();
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public abstract class HumanEntity : LivingEntity, InventoryHolder
|
||||
private GameMode _gameMode = GameMode.SURVIVAL;
|
||||
private string _name = string.Empty;
|
||||
internal PlayerInventory _playerInventory = new();
|
||||
internal EnderChestInventory? _enderChestInventory;
|
||||
internal Inventory _enderChestInventory = new("Ender Chest", InventoryType.ENDER_CHEST, 27);
|
||||
private ItemStack? _cursorItem;
|
||||
private bool _sleeping;
|
||||
private int _sleepTicks;
|
||||
@@ -59,8 +59,6 @@ public abstract class HumanEntity : LivingEntity, InventoryHolder
|
||||
/// <returns>The EnderChest of the player.</returns>
|
||||
public Inventory getEnderChest()
|
||||
{
|
||||
// AAAAAH
|
||||
_enderChestInventory ??= new EnderChestInventory(getEntityId());
|
||||
return _enderChestInventory;
|
||||
}
|
||||
|
||||
@@ -88,44 +86,14 @@ public abstract class HumanEntity : LivingEntity, InventoryHolder
|
||||
/// Will always be empty if the player currently has no open window.
|
||||
/// </summary>
|
||||
/// <returns>The ItemStack of the item you are currently moving around.</returns>
|
||||
public ItemStack? getItemOnCursor()
|
||||
{
|
||||
if (NativeBridge.GetCarriedItem != null)
|
||||
{
|
||||
int[] buf = new int[3];
|
||||
var gh = System.Runtime.InteropServices.GCHandle.Alloc(buf, System.Runtime.InteropServices.GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
NativeBridge.GetCarriedItem(getEntityId(), gh.AddrOfPinnedObject());
|
||||
}
|
||||
finally
|
||||
{
|
||||
gh.Free();
|
||||
}
|
||||
int id = buf[0];
|
||||
int aux = buf[1];
|
||||
int count = buf[2];
|
||||
if (id > 0 && count > 0)
|
||||
_cursorItem = new ItemStack(id, count, (short)aux);
|
||||
else
|
||||
_cursorItem = null;
|
||||
}
|
||||
return _cursorItem;
|
||||
}
|
||||
public ItemStack? getItemOnCursor() => _cursorItem;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the item to the given ItemStack, this will replace whatever the
|
||||
/// user was moving. Will always be empty if the player currently has no open window.
|
||||
/// </summary>
|
||||
/// <param name="item">The ItemStack which will end up in the hand.</param>
|
||||
public void setItemOnCursor(ItemStack? item)
|
||||
{
|
||||
_cursorItem = item;
|
||||
NativeBridge.SetCarriedItem?.Invoke(getEntityId(),
|
||||
item?.getTypeId() ?? 0,
|
||||
item?.getAmount() ?? 0,
|
||||
item?.getDurability() ?? 0);
|
||||
}
|
||||
public void setItemOnCursor(ItemStack? item) => _cursorItem = item;
|
||||
|
||||
/// <summary>
|
||||
/// If the player currently has an inventory window open, this method will
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
namespace Minecraft.Server.FourKit.Event.World;
|
||||
|
||||
using Minecraft.Server.FourKit.Chunk;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Chunk related event.
|
||||
/// </summary>
|
||||
public abstract class ChunkEvent : WorldEvent
|
||||
{
|
||||
protected Chunk chunk;
|
||||
|
||||
protected ChunkEvent(Chunk chunk) : base(chunk.getWorld())
|
||||
{
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunk being loaded/unloaded.
|
||||
/// </summary>
|
||||
/// <returns>Chunk that triggered this event.</returns>
|
||||
public Chunk getChunk() => chunk;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
namespace Minecraft.Server.FourKit.Event.World;
|
||||
|
||||
using Minecraft.Server.FourKit.Chunk;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a chunk is loaded.
|
||||
/// </summary>
|
||||
public class ChunkLoadEvent : ChunkEvent
|
||||
{
|
||||
private readonly bool _newChunk;
|
||||
|
||||
internal ChunkLoadEvent(Chunk chunk, bool newChunk) : base(chunk)
|
||||
{
|
||||
_newChunk = newChunk;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if this chunk was newly created or not. Note that if this chunk is
|
||||
/// new, it will not be populated at this time.
|
||||
/// </summary>
|
||||
/// <returns>true if the chunk is new, otherwise false.</returns>
|
||||
public bool isNewChunk() => _newChunk;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
namespace Minecraft.Server.FourKit.Event.World;
|
||||
|
||||
using Minecraft.Server.FourKit.Chunk;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a chunk is unloaded.
|
||||
/// </summary>
|
||||
public class ChunkUnloadEvent : ChunkEvent, Cancellable
|
||||
{
|
||||
private bool _cancel;
|
||||
|
||||
internal ChunkUnloadEvent(Chunk chunk) : base(chunk)
|
||||
{
|
||||
_cancel = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cancellation state of this event. A cancelled event will not
|
||||
/// be executed in the server, but will still pass to other plugins.
|
||||
/// </summary>
|
||||
/// <returns>true if this event is cancelled.</returns>
|
||||
public bool isCancelled() => _cancel;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the cancellation state of this event. A cancelled event will not
|
||||
/// be executed in the server, but will still pass to other plugins.
|
||||
/// </summary>
|
||||
/// <param name="cancel">true if you wish to cancel this event.</param>
|
||||
public void setCancelled(bool cancel)
|
||||
{
|
||||
_cancel = cancel;
|
||||
}
|
||||
}
|
||||
@@ -22,80 +22,60 @@ internal sealed class EventDispatcher
|
||||
public int CompareTo(RegisteredHandler other) => Priority.CompareTo(other.Priority);
|
||||
}
|
||||
|
||||
// Snapshot-on-write: writers swap _handlers atomically; Fire reads it lock-free.
|
||||
private volatile Dictionary<Type, RegisteredHandler[]> _handlers = new();
|
||||
private readonly object _writeLock = new();
|
||||
|
||||
// Fired when an event type gains its first handler.
|
||||
internal Action<Type>? OnSubscriptionChanged;
|
||||
|
||||
private readonly Dictionary<Type, List<RegisteredHandler>> _handlers = new();
|
||||
private readonly object _lock = new();
|
||||
public void Register(Listener listener)
|
||||
{
|
||||
var methods = listener.GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
List<(Type eventType, RegisteredHandler handler)>? pending = null;
|
||||
foreach (var method in methods)
|
||||
lock (_lock)
|
||||
{
|
||||
var attr = method.GetCustomAttribute<Event.EventHandlerAttribute>();
|
||||
if (attr == null)
|
||||
continue;
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != 1)
|
||||
foreach (var method in methods)
|
||||
{
|
||||
Console.WriteLine($"[FourKit] Warning: @EventHandler method {method.Name} must have exactly 1 parameter, skipping.");
|
||||
continue;
|
||||
var attr = method.GetCustomAttribute<Event.EventHandlerAttribute>();
|
||||
if (attr == null)
|
||||
continue;
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != 1)
|
||||
{
|
||||
Console.WriteLine($"[FourKit] Warning: @EventHandler method {method.Name} must have exactly 1 parameter, skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var eventType = parameters[0].ParameterType;
|
||||
if (!typeof(Event.Event).IsAssignableFrom(eventType))
|
||||
{
|
||||
Console.WriteLine($"[FourKit] Warning: @EventHandler method {method.Name} parameter must extend Event, skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_handlers.TryGetValue(eventType, out var list))
|
||||
{
|
||||
list = new List<RegisteredHandler>();
|
||||
_handlers[eventType] = list;
|
||||
}
|
||||
|
||||
list.Add(new RegisteredHandler(listener, method, attr.Priority, attr.IgnoreCancelled));
|
||||
_handlers[eventType] = list.OrderBy(h => h.Priority).ToList();
|
||||
}
|
||||
|
||||
var eventType = parameters[0].ParameterType;
|
||||
if (!typeof(Event.Event).IsAssignableFrom(eventType))
|
||||
{
|
||||
Console.WriteLine($"[FourKit] Warning: @EventHandler method {method.Name} parameter must extend Event, skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
pending ??= new List<(Type, RegisteredHandler)>();
|
||||
pending.Add((eventType, new RegisteredHandler(listener, method, attr.Priority, attr.IgnoreCancelled)));
|
||||
}
|
||||
|
||||
if (pending == null) return;
|
||||
|
||||
HashSet<Type> newlySubscribed = new();
|
||||
lock (_writeLock)
|
||||
{
|
||||
var newDict = new Dictionary<Type, RegisteredHandler[]>(_handlers);
|
||||
foreach (var (eventType, handler) in pending)
|
||||
{
|
||||
bool hadAny = newDict.TryGetValue(eventType, out var existing);
|
||||
existing ??= Array.Empty<RegisteredHandler>();
|
||||
|
||||
// OrderBy is stable; Array.Sort is not.
|
||||
var combined = existing.Append(handler).OrderBy(h => h.Priority).ToArray();
|
||||
newDict[eventType] = combined;
|
||||
|
||||
if (!hadAny) newlySubscribed.Add(eventType);
|
||||
}
|
||||
_handlers = newDict;
|
||||
}
|
||||
|
||||
if (OnSubscriptionChanged != null)
|
||||
{
|
||||
foreach (var t in newlySubscribed)
|
||||
OnSubscriptionChanged(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void Fire(Event.Event evt)
|
||||
{
|
||||
var snapshot = _handlers;
|
||||
if (!snapshot.TryGetValue(evt.GetType(), out var handlers))
|
||||
return;
|
||||
List<RegisteredHandler>? handlers;
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_handlers.TryGetValue(evt.GetType(), out handlers))
|
||||
return;
|
||||
|
||||
handlers = new List<RegisteredHandler>(handlers);
|
||||
}
|
||||
|
||||
var cancellable = evt as Cancellable;
|
||||
|
||||
for (int i = 0; i < handlers.Length; i++)
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
ref readonly var handler = ref handlers[i];
|
||||
if (handler.IgnoreCancelled && cancellable != null && cancellable.isCancelled())
|
||||
continue;
|
||||
|
||||
@@ -109,6 +89,4 @@ internal sealed class EventDispatcher
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsSubscribed(Type eventType) => _handlers.ContainsKey(eventType);
|
||||
}
|
||||
|
||||
@@ -11,68 +11,11 @@ using Minecraft.Server.FourKit.Plugin;
|
||||
/// </summary>
|
||||
public static class FourKit
|
||||
{
|
||||
private static readonly EventDispatcher _dispatcher;
|
||||
private static readonly EventDispatcher _dispatcher = new();
|
||||
private static readonly Dictionary<string, Player> _players = new(StringComparer.OrdinalIgnoreCase);
|
||||
private static readonly Dictionary<int, Player> _playersByEntityId = new();
|
||||
private static readonly object _playerLock = new();
|
||||
|
||||
// Must match HandlerKind in FourKitNatives.h.
|
||||
private enum HandlerKind
|
||||
{
|
||||
ChunkLoad = 0,
|
||||
ChunkUnload = 1,
|
||||
PlayerMove = 2,
|
||||
}
|
||||
|
||||
private static uint _handlerMask;
|
||||
private static readonly object _handlerMaskLock = new();
|
||||
|
||||
static FourKit()
|
||||
{
|
||||
_dispatcher = new EventDispatcher();
|
||||
_dispatcher.OnSubscriptionChanged = OnEventSubscribed;
|
||||
}
|
||||
|
||||
private static HandlerKind? MapEventTypeToHandlerKind(Type eventType)
|
||||
{
|
||||
if (eventType == typeof(Event.World.ChunkLoadEvent)) return HandlerKind.ChunkLoad;
|
||||
if (eventType == typeof(Event.World.ChunkUnloadEvent)) return HandlerKind.ChunkUnload;
|
||||
if (eventType == typeof(Event.Player.PlayerMoveEvent)) return HandlerKind.PlayerMove;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void OnEventSubscribed(Type eventType)
|
||||
{
|
||||
var kind = MapEventTypeToHandlerKind(eventType);
|
||||
if (kind == null) return;
|
||||
|
||||
lock (_handlerMaskLock)
|
||||
{
|
||||
uint newMask = _handlerMask | (1u << (int)kind.Value);
|
||||
if (newMask == _handlerMask) return;
|
||||
_handlerMask = newMask;
|
||||
NativeBridge.SetHandlerMask?.Invoke(_handlerMask);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ResyncHandlerMask()
|
||||
{
|
||||
lock (_handlerMaskLock)
|
||||
{
|
||||
NativeBridge.SetHandlerMask?.Invoke(_handlerMask);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current server tick count. Increments once per server tick
|
||||
/// (~20 per second under nominal load). Useful for measuring TPS by
|
||||
/// sampling the delta against wall clock time.
|
||||
/// </summary>
|
||||
public static int getServerTick()
|
||||
{
|
||||
return NativeBridge.GetServerTickCount?.Invoke() ?? 0;
|
||||
}
|
||||
|
||||
internal const int MAX_CHAT_LENGTH = 123;
|
||||
|
||||
private static readonly Dictionary<int, World> _worldsByDimId = new();
|
||||
|
||||
@@ -58,11 +58,11 @@ public static partial class FourKitHost
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SetInventoryCallbacks(IntPtr getPlayerInventory, IntPtr setPlayerInventorySlot, IntPtr getContainerContents, IntPtr setContainerSlot, IntPtr getContainerViewerEntityIds, IntPtr closeContainer, IntPtr openVirtualContainer, IntPtr getItemMeta, IntPtr setItemMeta, IntPtr setHeldItemSlot, IntPtr getCarriedItem, IntPtr setCarriedItem, IntPtr getEnderChestContents, IntPtr setEnderChestSlot)
|
||||
public static void SetInventoryCallbacks(IntPtr getPlayerInventory, IntPtr setPlayerInventorySlot, IntPtr getContainerContents, IntPtr setContainerSlot, IntPtr getContainerViewerEntityIds, IntPtr closeContainer, IntPtr openVirtualContainer, IntPtr getItemMeta, IntPtr setItemMeta, IntPtr setHeldItemSlot)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeBridge.SetInventoryCallbacks(getPlayerInventory, setPlayerInventorySlot, getContainerContents, setContainerSlot, getContainerViewerEntityIds, closeContainer, openVirtualContainer, getItemMeta, setItemMeta, setHeldItemSlot, getCarriedItem, setCarriedItem, getEnderChestContents, setEnderChestSlot);
|
||||
NativeBridge.SetInventoryCallbacks(getPlayerInventory, setPlayerInventorySlot, getContainerContents, setContainerSlot, getContainerViewerEntityIds, closeContainer, openVirtualContainer, getItemMeta, setItemMeta, setHeldItemSlot);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -121,71 +121,4 @@ public static partial class FourKitHost
|
||||
ServerLog.Error("fourkit", $"SetVehicleCallbacks error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SetChunkCallbacks(IntPtr isChunkLoaded, IntPtr loadChunk, IntPtr unloadChunk, IntPtr getLoadedChunks, IntPtr isChunkInUse, IntPtr getChunkSnapshot, IntPtr unloadChunkRequest, IntPtr regenerateChunk, IntPtr refreshChunk)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeBridge.SetChunkCallbacks(isChunkLoaded, loadChunk, unloadChunk, getLoadedChunks, isChunkInUse, getChunkSnapshot, unloadChunkRequest, regenerateChunk, refreshChunk);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"SetChunkCallbacks error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SetBlockInfoCallbacks(IntPtr getSkyLight, IntPtr getBlockLight, IntPtr getBiomeId, IntPtr setBiomeId)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeBridge.SetBlockInfoCallbacks(getSkyLight, getBlockLight, getBiomeId, setBiomeId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"SetBlockInfoCallbacks error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SetWorldEntityCallbacks(IntPtr getWorldEntities, IntPtr getChunkEntities)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeBridge.SetWorldEntityCallbacks(getWorldEntities, getChunkEntities);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"SetWorldEntityCallbacks error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SetSubscriptionCallbacks(IntPtr setHandlerMask)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeBridge.SetSubscriptionCallbacks(setHandlerMask);
|
||||
// Flush the mask accumulated during plugin onEnable.
|
||||
FourKit.ResyncHandlerMask();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"SetSubscriptionCallbacks error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SetServerCallbacks(IntPtr getServerTickCount)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeBridge.SetServerCallbacks(getServerTickCount);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"SetServerCallbacks error: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1250,38 +1250,4 @@ public static partial class FourKitHost
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void FireChunkLoad(int dimId, int chunkX, int chunkZ, int isNewChunk)
|
||||
{
|
||||
try
|
||||
{
|
||||
var world = FourKit.getWorld(dimId);
|
||||
var chunk = new Chunk.Chunk(world, chunkX, chunkZ);
|
||||
var evt = new Event.World.ChunkLoadEvent(chunk, isNewChunk != 0);
|
||||
FourKit.FireEvent(evt);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"FireChunkLoad error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static int FireChunkUnload(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
try
|
||||
{
|
||||
var world = FourKit.getWorld(dimId);
|
||||
var chunk = new Chunk.Chunk(world, chunkX, chunkZ);
|
||||
var evt = new Event.World.ChunkUnloadEvent(chunk);
|
||||
FourKit.FireEvent(evt);
|
||||
return evt.isCancelled() ? 1 : 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerLog.Error("fourkit", $"FireChunkUnload error: {ex}");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
namespace Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// todo: this needs to be removed at some point
|
||||
|
||||
internal class EnderChestInventory : Inventory
|
||||
{
|
||||
private readonly int _ownerEntityId;
|
||||
|
||||
internal EnderChestInventory(int ownerEntityId)
|
||||
: base("Ender Chest", InventoryType.ENDER_CHEST, 27)
|
||||
{
|
||||
_ownerEntityId = ownerEntityId;
|
||||
}
|
||||
|
||||
protected internal override void EnsureSynced()
|
||||
{
|
||||
if (NativeBridge.GetEnderChestContents == null)
|
||||
return;
|
||||
|
||||
int[] buf = new int[27 * 3];
|
||||
var gh = GCHandle.Alloc(buf, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
NativeBridge.GetEnderChestContents(_ownerEntityId, gh.AddrOfPinnedObject());
|
||||
}
|
||||
finally
|
||||
{
|
||||
gh.Free();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 27; i++)
|
||||
{
|
||||
int id = buf[i * 3 + 0];
|
||||
int aux = buf[i * 3 + 1];
|
||||
int packed = buf[i * 3 + 2];
|
||||
|
||||
ushort count = (ushort)((packed >> 8) & 0xFFFF);
|
||||
|
||||
_items[i]?.UnbindFromInventory();
|
||||
if (id > 0 && count > 0)
|
||||
{
|
||||
if (_items[i] == null)
|
||||
{
|
||||
_items[i] = new ItemStack(id, count, (short)aux);
|
||||
}
|
||||
else
|
||||
{
|
||||
_items[i]!.setTypeId(id);
|
||||
_items[i]!.setAmount(count);
|
||||
_items[i]!.setDurability((short)aux);
|
||||
}
|
||||
_items[i]!.BindToInventory(this, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
_items[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void setItem(int index, ItemStack? item)
|
||||
{
|
||||
if (index >= 0 && index < _items.Length)
|
||||
{
|
||||
var old = _items[index];
|
||||
if (old != item)
|
||||
{
|
||||
old?.UnbindFromInventory();
|
||||
item?.BindToInventory(this, index);
|
||||
}
|
||||
_items[index] = item;
|
||||
_slotModifiedByPlugin = true;
|
||||
}
|
||||
|
||||
if (NativeBridge.SetEnderChestSlot != null && index >= 0 && index < _items.Length)
|
||||
{
|
||||
int id = item?.getTypeId() ?? 0;
|
||||
int count = item?.getAmount() ?? 0;
|
||||
int aux = item?.getDurability() ?? 0;
|
||||
NativeBridge.SetEnderChestSlot(_ownerEntityId, index, id, count, aux);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,9 +174,7 @@ public class Inventory : IEnumerable<ItemStack>
|
||||
if (_items[slot] == null)
|
||||
{
|
||||
int added = Math.Min(64, remaining);
|
||||
var newItem = new ItemStack(toAdd.getType(), added, toAdd.getDurability());
|
||||
newItem.setItemMetaInternal(toAdd.getItemMetaInternal()?.clone());
|
||||
setItem(slot, newItem);
|
||||
setItem(slot, new ItemStack(toAdd.getType(), added, toAdd.getDurability()));
|
||||
remaining -= added;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public class ItemMeta
|
||||
/// <param name="enchants">The enchantments to set.</param>
|
||||
public void setEnchants(Dictionary<EnchantmentType, int>? enchants)
|
||||
{
|
||||
_enchants = enchants != null ? new Dictionary<EnchantmentType, int>(enchants) : null;
|
||||
enchants = enchants != null ? new Dictionary<EnchantmentType, int>(enchants) : null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -186,15 +186,6 @@ public class Location
|
||||
|
||||
public Location clone() => new Location(LocationWorld, X, Y, Z, Yaw, Pitch);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunk at the represented location.
|
||||
/// </summary>
|
||||
/// <returns>Chunk at the represented location.</returns>
|
||||
public Chunk.Chunk getChunk()
|
||||
{
|
||||
return getWorld().getChunkAt(getBlockX() >> 4, getBlockZ() >> 4);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"Location(world={LocationWorld.getName()}, x={X}, y={Y}, z={Z}, yaw={Yaw}, pitch={Pitch})";
|
||||
}
|
||||
|
||||
@@ -7,7 +7,5 @@
|
||||
<AssemblyName>Minecraft.Server.FourKit</AssemblyName>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<BaseOutputPath>bin</BaseOutputPath>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -43,10 +43,10 @@ internal static class NativeBridge
|
||||
internal delegate int NativeGetTileDataDelegate(int dimId, int x, int y, int z);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeSetTileDelegate(int dimId, int x, int y, int z, int tileId, int data, int flags);
|
||||
internal delegate void NativeSetTileDelegate(int dimId, int x, int y, int z, int tileId, int data);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeSetTileDataDelegate(int dimId, int x, int y, int z, int data, int flags);
|
||||
internal delegate void NativeSetTileDataDelegate(int dimId, int x, int y, int z, int data);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeBreakBlockDelegate(int dimId, int x, int y, int z);
|
||||
@@ -123,18 +123,6 @@ internal static class NativeBridge
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeSetHeldItemSlotDelegate(int entityId, int slot);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeGetCarriedItemDelegate(int entityId, IntPtr outData);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeSetCarriedItemDelegate(int entityId, int itemId, int count, int aux);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeGetEnderChestContentsDelegate(int entityId, IntPtr outData);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeSetEnderChestSlotDelegate(int entityId, int slot, int itemId, int count, int aux);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeSetSneakingDelegate(int entityId, int sneak);
|
||||
|
||||
@@ -192,58 +180,6 @@ internal static class NativeBridge
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeGetEntityInfoDelegate(int entityId, IntPtr outBuf);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeIsChunkLoadedDelegate(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeLoadChunkDelegate(int dimId, int chunkX, int chunkZ, int generate);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeUnloadChunkDelegate(int dimId, int chunkX, int chunkZ, int save, int safe);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetLoadedChunksDelegate(int dimId, out IntPtr coordBuf);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeIsChunkInUseDelegate(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeGetChunkSnapshotDelegate(int dimId, int chunkX, int chunkZ, IntPtr blockIds, IntPtr blockData, IntPtr maxBlockY);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeUnloadChunkRequestDelegate(int dimId, int chunkX, int chunkZ, int safe);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeRegenerateChunkDelegate(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeRefreshChunkDelegate(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetWorldEntitiesDelegate(int dimId, out IntPtr outBuf);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetChunkEntitiesDelegate(int dimId, int chunkX, int chunkZ, out IntPtr outBuf);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetSkyLightDelegate(int dimId, int x, int y, int z);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetBlockLightDelegate(int dimId, int x, int y, int z);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetBiomeIdDelegate(int dimId, int x, int z);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeSetBiomeIdDelegate(int dimId, int x, int z, int biomeId);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void NativeSetHandlerMaskDelegate(uint mask);
|
||||
internal static NativeSetHandlerMaskDelegate? SetHandlerMask;
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate int NativeGetServerTickCountDelegate();
|
||||
internal static NativeGetServerTickCountDelegate? GetServerTickCount;
|
||||
|
||||
internal static NativeDamageDelegate? DamagePlayer;
|
||||
internal static NativeSetHealthDelegate? SetPlayerHealth;
|
||||
@@ -285,10 +221,6 @@ internal static class NativeBridge
|
||||
internal static NativeGetItemMetaDelegate? GetItemMeta;
|
||||
internal static NativeSetItemMetaDelegate? SetItemMeta;
|
||||
internal static NativeSetHeldItemSlotDelegate? SetHeldItemSlot;
|
||||
internal static NativeGetCarriedItemDelegate? GetCarriedItem;
|
||||
internal static NativeSetCarriedItemDelegate? SetCarriedItem;
|
||||
internal static NativeGetEnderChestContentsDelegate? GetEnderChestContents;
|
||||
internal static NativeSetEnderChestSlotDelegate? SetEnderChestSlot;
|
||||
internal static NativeSetSneakingDelegate? SetSneaking;
|
||||
internal static NativeSetVelocityDelegate? SetVelocity;
|
||||
internal static NativeSetAllowFlightDelegate? SetAllowFlight;
|
||||
@@ -308,21 +240,6 @@ internal static class NativeBridge
|
||||
internal static NativeGetVehicleIdDelegate? GetVehicleId;
|
||||
internal static NativeGetPassengerIdDelegate? GetPassengerId;
|
||||
internal static NativeGetEntityInfoDelegate? GetEntityInfo;
|
||||
internal static NativeIsChunkLoadedDelegate? IsChunkLoaded;
|
||||
internal static NativeLoadChunkDelegate? LoadChunk;
|
||||
internal static NativeUnloadChunkDelegate? UnloadChunk;
|
||||
internal static NativeGetLoadedChunksDelegate? GetLoadedChunks;
|
||||
internal static NativeIsChunkInUseDelegate? IsChunkInUse;
|
||||
internal static NativeGetChunkSnapshotDelegate? GetChunkSnapshot;
|
||||
internal static NativeUnloadChunkRequestDelegate? UnloadChunkRequest;
|
||||
internal static NativeRegenerateChunkDelegate? RegenerateChunk;
|
||||
internal static NativeRefreshChunkDelegate? RefreshChunk;
|
||||
internal static NativeGetWorldEntitiesDelegate? GetWorldEntities;
|
||||
internal static NativeGetChunkEntitiesDelegate? GetChunkEntities;
|
||||
internal static NativeGetSkyLightDelegate? GetSkyLight;
|
||||
internal static NativeGetBlockLightDelegate? GetBlockLight;
|
||||
internal static NativeGetBiomeIdDelegate? GetBiomeId;
|
||||
internal static NativeSetBiomeIdDelegate? SetBiomeId;
|
||||
|
||||
internal static void SetCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity)
|
||||
{
|
||||
@@ -369,7 +286,7 @@ internal static class NativeBridge
|
||||
SendRaw = Marshal.GetDelegateForFunctionPointer<NativeSendRawDelegate>(sendRaw);
|
||||
}
|
||||
|
||||
internal static void SetInventoryCallbacks(IntPtr getPlayerInventory, IntPtr setPlayerInventorySlot, IntPtr getContainerContents, IntPtr setContainerSlot, IntPtr getContainerViewerEntityIds, IntPtr closeContainer, IntPtr openVirtualContainer, IntPtr getItemMeta, IntPtr setItemMeta, IntPtr setHeldItemSlot, IntPtr getCarriedItem, IntPtr setCarriedItem, IntPtr getEnderChestContents, IntPtr setEnderChestSlot)
|
||||
internal static void SetInventoryCallbacks(IntPtr getPlayerInventory, IntPtr setPlayerInventorySlot, IntPtr getContainerContents, IntPtr setContainerSlot, IntPtr getContainerViewerEntityIds, IntPtr closeContainer, IntPtr openVirtualContainer, IntPtr getItemMeta, IntPtr setItemMeta, IntPtr setHeldItemSlot)
|
||||
{
|
||||
GetPlayerInventory = Marshal.GetDelegateForFunctionPointer<NativeGetPlayerInventoryDelegate>(getPlayerInventory);
|
||||
SetPlayerInventorySlot = Marshal.GetDelegateForFunctionPointer<NativeSetPlayerInventorySlotDelegate>(setPlayerInventorySlot);
|
||||
@@ -381,10 +298,6 @@ internal static class NativeBridge
|
||||
GetItemMeta = Marshal.GetDelegateForFunctionPointer<NativeGetItemMetaDelegate>(getItemMeta);
|
||||
SetItemMeta = Marshal.GetDelegateForFunctionPointer<NativeSetItemMetaDelegate>(setItemMeta);
|
||||
SetHeldItemSlot = Marshal.GetDelegateForFunctionPointer<NativeSetHeldItemSlotDelegate>(setHeldItemSlot);
|
||||
GetCarriedItem = Marshal.GetDelegateForFunctionPointer<NativeGetCarriedItemDelegate>(getCarriedItem);
|
||||
SetCarriedItem = Marshal.GetDelegateForFunctionPointer<NativeSetCarriedItemDelegate>(setCarriedItem);
|
||||
GetEnderChestContents = Marshal.GetDelegateForFunctionPointer<NativeGetEnderChestContentsDelegate>(getEnderChestContents);
|
||||
SetEnderChestSlot = Marshal.GetDelegateForFunctionPointer<NativeSetEnderChestSlotDelegate>(setEnderChestSlot);
|
||||
}
|
||||
|
||||
internal static void SetEntityCallbacks(IntPtr setSneaking, IntPtr setVelocity, IntPtr setAllowFlight, IntPtr playSound, IntPtr setSleepingIgnored)
|
||||
@@ -421,41 +334,4 @@ internal static class NativeBridge
|
||||
GetPassengerId = Marshal.GetDelegateForFunctionPointer<NativeGetPassengerIdDelegate>(getPassengerId);
|
||||
GetEntityInfo = Marshal.GetDelegateForFunctionPointer<NativeGetEntityInfoDelegate>(getEntityInfo);
|
||||
}
|
||||
|
||||
internal static void SetChunkCallbacks(IntPtr isChunkLoaded, IntPtr loadChunk, IntPtr unloadChunk, IntPtr getLoadedChunks, IntPtr isChunkInUse, IntPtr getChunkSnapshot, IntPtr unloadChunkRequest, IntPtr regenerateChunk, IntPtr refreshChunk)
|
||||
{
|
||||
IsChunkLoaded = Marshal.GetDelegateForFunctionPointer<NativeIsChunkLoadedDelegate>(isChunkLoaded);
|
||||
LoadChunk = Marshal.GetDelegateForFunctionPointer<NativeLoadChunkDelegate>(loadChunk);
|
||||
UnloadChunk = Marshal.GetDelegateForFunctionPointer<NativeUnloadChunkDelegate>(unloadChunk);
|
||||
GetLoadedChunks = Marshal.GetDelegateForFunctionPointer<NativeGetLoadedChunksDelegate>(getLoadedChunks);
|
||||
IsChunkInUse = Marshal.GetDelegateForFunctionPointer<NativeIsChunkInUseDelegate>(isChunkInUse);
|
||||
GetChunkSnapshot = Marshal.GetDelegateForFunctionPointer<NativeGetChunkSnapshotDelegate>(getChunkSnapshot);
|
||||
UnloadChunkRequest = Marshal.GetDelegateForFunctionPointer<NativeUnloadChunkRequestDelegate>(unloadChunkRequest);
|
||||
RegenerateChunk = Marshal.GetDelegateForFunctionPointer<NativeRegenerateChunkDelegate>(regenerateChunk);
|
||||
RefreshChunk = Marshal.GetDelegateForFunctionPointer<NativeRefreshChunkDelegate>(refreshChunk);
|
||||
}
|
||||
|
||||
internal static void SetWorldEntityCallbacks(IntPtr getWorldEntities, IntPtr getChunkEntities)
|
||||
{
|
||||
GetWorldEntities = Marshal.GetDelegateForFunctionPointer<NativeGetWorldEntitiesDelegate>(getWorldEntities);
|
||||
GetChunkEntities = Marshal.GetDelegateForFunctionPointer<NativeGetChunkEntitiesDelegate>(getChunkEntities);
|
||||
}
|
||||
|
||||
internal static void SetBlockInfoCallbacks(IntPtr getSkyLight, IntPtr getBlockLight, IntPtr getBiomeId, IntPtr setBiomeId)
|
||||
{
|
||||
GetSkyLight = Marshal.GetDelegateForFunctionPointer<NativeGetSkyLightDelegate>(getSkyLight);
|
||||
GetBlockLight = Marshal.GetDelegateForFunctionPointer<NativeGetBlockLightDelegate>(getBlockLight);
|
||||
GetBiomeId = Marshal.GetDelegateForFunctionPointer<NativeGetBiomeIdDelegate>(getBiomeId);
|
||||
SetBiomeId = Marshal.GetDelegateForFunctionPointer<NativeSetBiomeIdDelegate>(setBiomeId);
|
||||
}
|
||||
|
||||
internal static void SetSubscriptionCallbacks(IntPtr setHandlerMask)
|
||||
{
|
||||
SetHandlerMask = Marshal.GetDelegateForFunctionPointer<NativeSetHandlerMaskDelegate>(setHandlerMask);
|
||||
}
|
||||
|
||||
internal static void SetServerCallbacks(IntPtr getServerTickCount)
|
||||
{
|
||||
GetServerTickCount = Marshal.GetDelegateForFunctionPointer<NativeGetServerTickCountDelegate>(getServerTickCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Minecraft.Server.FourKit.Chunk;
|
||||
using Minecraft.Server.FourKit.Entity;
|
||||
using Minecraft.Server.FourKit.Inventory;
|
||||
|
||||
@@ -246,115 +245,6 @@ public class World
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of all entities in this World.
|
||||
/// </summary>
|
||||
/// <returns>A list of all Entities currently residing in this world.</returns>
|
||||
public List<Entity.Entity> getEntities()
|
||||
{
|
||||
var result = new List<Entity.Entity>();
|
||||
if (NativeBridge.GetWorldEntities == null) return result;
|
||||
|
||||
int count = NativeBridge.GetWorldEntities(_dimensionId, out IntPtr buf);
|
||||
if (count <= 0 || buf == IntPtr.Zero) return result;
|
||||
|
||||
try
|
||||
{
|
||||
int[] data = new int[count * 3];
|
||||
Marshal.Copy(buf, data, 0, count * 3);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int entityId = data[i * 3];
|
||||
int mappedType = data[i * 3 + 1];
|
||||
int isLiving = data[i * 3 + 2];
|
||||
|
||||
var entityType = Enum.IsDefined(typeof(Entity.EntityType), mappedType)
|
||||
? (Entity.EntityType)mappedType
|
||||
: Entity.EntityType.UNKNOWN;
|
||||
|
||||
if (entityType == Entity.EntityType.PLAYER)
|
||||
{
|
||||
var player = FourKit.GetPlayerByEntityId(entityId);
|
||||
if (player != null)
|
||||
{
|
||||
result.Add(player);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isLiving == 1)
|
||||
{
|
||||
result.Add(new Entity.LivingEntity(entityId, entityType, _dimensionId, 0, 0, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
var entity = new Entity.Entity();
|
||||
entity.SetEntityIdInternal(entityId);
|
||||
entity.SetEntityTypeInternal(entityType);
|
||||
entity.SetDimensionInternal(_dimensionId);
|
||||
result.Add(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(buf);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of all living entities in this World.
|
||||
/// </summary>
|
||||
/// <returns>A list of all LivingEntities currently residing in this world.</returns>
|
||||
public List<Entity.LivingEntity> getLivingEntities()
|
||||
{
|
||||
var result = new List<Entity.LivingEntity>();
|
||||
if (NativeBridge.GetWorldEntities == null) return result;
|
||||
|
||||
int count = NativeBridge.GetWorldEntities(_dimensionId, out IntPtr buf);
|
||||
if (count <= 0 || buf == IntPtr.Zero) return result;
|
||||
|
||||
try
|
||||
{
|
||||
int[] data = new int[count * 3];
|
||||
Marshal.Copy(buf, data, 0, count * 3);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int entityId = data[i * 3];
|
||||
int mappedType = data[i * 3 + 1];
|
||||
int isLiving = data[i * 3 + 2];
|
||||
|
||||
if (isLiving != 1) continue;
|
||||
|
||||
var entityType = Enum.IsDefined(typeof(Entity.EntityType), mappedType)
|
||||
? (Entity.EntityType)mappedType
|
||||
: Entity.EntityType.UNKNOWN;
|
||||
|
||||
if (entityType == Entity.EntityType.PLAYER)
|
||||
{
|
||||
var player = FourKit.GetPlayerByEntityId(entityId);
|
||||
if (player != null)
|
||||
{
|
||||
result.Add(player);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result.Add(new Entity.LivingEntity(entityId, entityType, _dimensionId, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(buf);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates explosion at given coordinates with given power.
|
||||
/// </summary>
|
||||
@@ -484,240 +374,4 @@ public class World
|
||||
{
|
||||
NativeBridge.DropItem?.Invoke(_dimensionId, location.X, location.Y, location.Z, item.getTypeId(), item.getAmount(), item.getDurability(), 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Chunk at the given coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>Chunk at the given coordinates.</returns>
|
||||
public Chunk.Chunk getChunkAt(int x, int z)
|
||||
{
|
||||
return new Chunk.Chunk(this, x, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Chunk at the given Location.
|
||||
/// </summary>
|
||||
/// <param name="location">Location of the chunk.</param>
|
||||
/// <returns>Chunk at the given location.</returns>
|
||||
public Chunk.Chunk getChunkAt(Location location)
|
||||
{
|
||||
return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Chunk that contains the given Block.
|
||||
/// </summary>
|
||||
/// <param name="block">Block to get the containing chunk from.</param>
|
||||
/// <returns>The chunk that contains the given block.</returns>
|
||||
public Chunk.Chunk getChunkAt(Block.Block block)
|
||||
{
|
||||
return getChunkAt(block.getX() >> 4, block.getZ() >> 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified Chunk is loaded.
|
||||
/// </summary>
|
||||
/// <param name="chunk">The chunk to check.</param>
|
||||
/// <returns>true if the chunk is loaded, otherwise false.</returns>
|
||||
public bool isChunkLoaded(Chunk.Chunk chunk)
|
||||
{
|
||||
return isChunkLoaded(chunk.getX(), chunk.getZ());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Chunk at the specified coordinates is loaded.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>true if the chunk is loaded, otherwise false.</returns>
|
||||
public bool isChunkLoaded(int x, int z)
|
||||
{
|
||||
if (NativeBridge.IsChunkLoaded != null)
|
||||
return NativeBridge.IsChunkLoaded(_dimensionId, x, z) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array of all loaded Chunks.
|
||||
/// </summary>
|
||||
/// <returns>Chunk[] containing all loaded chunks.</returns>
|
||||
public Chunk.Chunk[] getLoadedChunks()
|
||||
{
|
||||
if (NativeBridge.GetLoadedChunks == null)
|
||||
return Array.Empty<Chunk.Chunk>();
|
||||
|
||||
int count = NativeBridge.GetLoadedChunks(_dimensionId, out IntPtr buf);
|
||||
if (count <= 0 || buf == IntPtr.Zero)
|
||||
return Array.Empty<Chunk.Chunk>();
|
||||
|
||||
try
|
||||
{
|
||||
int[] coords = new int[count * 2];
|
||||
Marshal.Copy(buf, coords, 0, count * 2);
|
||||
var chunks = new Chunk.Chunk[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
chunks[i] = new Chunk.Chunk(this, coords[i * 2], coords[i * 2 + 1]);
|
||||
return chunks;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the specified Chunk.
|
||||
/// </summary>
|
||||
/// <param name="chunk">The chunk to load.</param>
|
||||
public void loadChunk(Chunk.Chunk chunk)
|
||||
{
|
||||
loadChunk(chunk.getX(), chunk.getZ());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the Chunk at the specified coordinates.
|
||||
/// If the chunk does not exist, it will be generated. This method is
|
||||
/// analogous to loadChunk(int, int, boolean) where generate is true.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
public void loadChunk(int x, int z)
|
||||
{
|
||||
loadChunk(x, z, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the Chunk at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <param name="generate">Whether or not to generate a chunk if it doesn't already exist.</param>
|
||||
/// <returns>true if the chunk has loaded successfully, otherwise false.</returns>
|
||||
public bool loadChunk(int x, int z, bool generate)
|
||||
{
|
||||
if (NativeBridge.LoadChunk != null)
|
||||
return NativeBridge.LoadChunk(_dimensionId, x, z, generate ? 1 : 0) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Chunk at the specified coordinates is loaded and in use
|
||||
/// by one or more players.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>true if the chunk is loaded and in use by one or more players, otherwise false.</returns>
|
||||
public bool isChunkInUse(int x, int z)
|
||||
{
|
||||
if (NativeBridge.IsChunkInUse != null)
|
||||
return NativeBridge.IsChunkInUse(_dimensionId, x, z) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely unloads and saves the Chunk at the specified coordinates.
|
||||
/// This method is analogous to unloadChunk(int, int, boolean, boolean)
|
||||
/// where safe and save is true.
|
||||
/// </summary>
|
||||
/// <param name="chunk">The chunk to unload.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unloadChunk(Chunk.Chunk chunk)
|
||||
{
|
||||
return unloadChunk(chunk.getX(), chunk.getZ());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely unloads and saves the Chunk at the specified coordinates.
|
||||
/// This method is analogous to unloadChunk(int, int, boolean, boolean)
|
||||
/// where safe and save is true.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unloadChunk(int x, int z)
|
||||
{
|
||||
return unloadChunk(x, z, true, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely unloads and optionally saves the Chunk at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <param name="save">Whether or not to save the chunk.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unloadChunk(int x, int z, bool save)
|
||||
{
|
||||
return unloadChunk(x, z, save, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads and optionally saves the Chunk at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <param name="save">Controls whether the chunk is saved.</param>
|
||||
/// <param name="safe">Controls whether to unload the chunk when players are nearby.</param>
|
||||
/// <returns>true if the chunk has unloaded successfully, otherwise false.</returns>
|
||||
public bool unloadChunk(int x, int z, bool save, bool safe)
|
||||
{
|
||||
if (NativeBridge.UnloadChunk != null)
|
||||
return NativeBridge.UnloadChunk(_dimensionId, x, z, save ? 1 : 0, safe ? 1 : 0) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely queues the Chunk at the specified coordinates for unloading.
|
||||
/// This method is analogous to unloadChunkRequest(int, int, boolean)
|
||||
/// where safe is true.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>true is the queue attempt was successful, otherwise false.</returns>
|
||||
public bool unloadChunkRequest(int x, int z)
|
||||
{
|
||||
return unloadChunkRequest(x, z, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues the Chunk at the specified coordinates for unloading.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <param name="safe">Controls whether to queue the chunk when players are nearby.</param>
|
||||
/// <returns>Whether the chunk was actually queued.</returns>
|
||||
public bool unloadChunkRequest(int x, int z, bool safe)
|
||||
{
|
||||
if (NativeBridge.UnloadChunkRequest != null)
|
||||
return NativeBridge.UnloadChunkRequest(_dimensionId, x, z, safe ? 1 : 0) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates the Chunk at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>Whether the chunk was actually regenerated.</returns>
|
||||
public bool regenerateChunk(int x, int z)
|
||||
{
|
||||
if (NativeBridge.RegenerateChunk != null)
|
||||
return NativeBridge.RegenerateChunk(_dimensionId, x, z) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resends the Chunk to all clients.
|
||||
/// </summary>
|
||||
/// <param name="x">X-coordinate of the chunk.</param>
|
||||
/// <param name="z">Z-coordinate of the chunk.</param>
|
||||
/// <returns>Whether the chunk was actually refreshed.</returns>
|
||||
public bool refreshChunk(int x, int z)
|
||||
{
|
||||
if (NativeBridge.RefreshChunk != null)
|
||||
return NativeBridge.RefreshChunk(_dimensionId, x, z) != 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1132,60 +1132,4 @@ public void onClick(InventoryClickEvent e)
|
||||
|
||||
> **Cancellable:** Yes
|
||||
|
||||
|
||||
---
|
||||
|
||||
@section chunk_events Chunk Events
|
||||
|
||||
@subsection chunkloadevent ChunkLoadEvent
|
||||
|
||||
\ref Minecraft.Server.FourKit.Event.World.ChunkLoadEvent "ChunkLoadEvent" is fired when a chunk is loaded. If the chunk is newly generated it will not yet be populated when this event fires.
|
||||
|
||||
```csharp
|
||||
[EventHandler]
|
||||
public void onChunkLoad(ChunkLoadEvent e)
|
||||
{
|
||||
if (e.isNewChunk())
|
||||
{
|
||||
Console.WriteLine($"New chunk generated at {e.getChunk().getX()}, {e.getChunk().getZ()}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `getChunk()` | The `Chunk` that was loaded. |
|
||||
| `isNewChunk()` | True if this chunk was newly generated. Note that new chunks will not yet be populated at this time. |
|
||||
|
||||
> **Cancellable:** No
|
||||
|
||||
---
|
||||
|
||||
@subsection chunkunloadevent ChunkUnloadEvent
|
||||
|
||||
\ref Minecraft.Server.FourKit.Event.World.ChunkUnloadEvent "ChunkUnloadEvent" is fired when a chunk is about to be unloaded. You can cancel it to prevent the chunk from being unloaded.
|
||||
|
||||
```csharp
|
||||
[EventHandler]
|
||||
public void onChunkUnload(ChunkUnloadEvent e)
|
||||
{
|
||||
// keep chunks near world origin loaded
|
||||
Chunk chunk = e.getChunk();
|
||||
if (Math.Abs(chunk.getX()) <= 2 && Math.Abs(chunk.getZ()) <= 2)
|
||||
{
|
||||
e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `getChunk()` | The `Chunk` that is about to be unloaded. |
|
||||
| `isCancelled()` | Whether the unload is cancelled. |
|
||||
| `setCancelled(bool)` | Cancel or allow the chunk unload. |
|
||||
|
||||
> **Cancellable:** Yes
|
||||
|
||||
---
|
||||
|
||||
<h1>Page currently under construction</h1>
|
||||
@@ -62,7 +62,7 @@ typedef void(__stdcall *fn_set_player_connection_callbacks)(void *sendRaw);
|
||||
typedef long long(__stdcall *fn_fire_player_drop_item)(int entityId,
|
||||
int itemId, int itemCount, int itemAux,
|
||||
int *outItemId, int *outItemCount, int *outItemAux);
|
||||
typedef void(__stdcall *fn_set_inventory_callbacks)(void *getPlayerInventory, void *setPlayerInventorySlot, void *getContainerContents, void *setContainerSlot, void *getContainerViewerEntityIds, void *closeContainer, void *openVirtualContainer, void *getItemMeta, void *setItemMeta, void *setHeldItemSlot, void *getCarriedItem, void *setCarriedItem, void *getEnderChestContents, void *setEnderChestSlot);
|
||||
typedef void(__stdcall *fn_set_inventory_callbacks)(void *getPlayerInventory, void *setPlayerInventorySlot, void *getContainerContents, void *setContainerSlot, void *getContainerViewerEntityIds, void *closeContainer, void *openVirtualContainer, void *getItemMeta, void *setItemMeta, void *setHeldItemSlot);
|
||||
typedef int(__stdcall *fn_fire_player_interact)(int entityId, int action,
|
||||
int itemId, int itemCount, int itemAux,
|
||||
int clickedX, int clickedY, int clickedZ,
|
||||
@@ -103,13 +103,6 @@ typedef int(__stdcall *fn_fire_piston_extend)(int dimId, int x, int y, int z, in
|
||||
typedef int(__stdcall *fn_fire_piston_retract)(int dimId, int x, int y, int z, int direction);
|
||||
typedef int(__stdcall *fn_fire_command_preprocess)(int entityId, const char *cmdUtf8, int cmdByteLen, char *outBuf, int outBufSize, int *outLen);
|
||||
typedef int(__stdcall *fn_fire_block_from_to)(int dimId, int fromX, int fromY, int fromZ, int toX, int toY, int toZ, int face);
|
||||
typedef void(__stdcall *fn_set_chunk_callbacks)(void *isChunkLoaded, void *loadChunk, void *unloadChunk, void *getLoadedChunks, void *isChunkInUse, void *getChunkSnapshot, void *unloadChunkRequest, void *regenerateChunk, void *refreshChunk);
|
||||
typedef void(__stdcall *fn_set_block_info_callbacks)(void *getSkyLight, void *getBlockLight, void *getBiomeId, void *setBiomeId);
|
||||
typedef void(__stdcall *fn_set_world_entity_callbacks)(void *getWorldEntities, void *getChunkEntities);
|
||||
typedef void(__stdcall *fn_set_subscription_callbacks)(void *setHandlerMask);
|
||||
typedef void(__stdcall *fn_set_server_callbacks)(void *getServerTickCount);
|
||||
typedef void(__stdcall *fn_fire_chunk_load)(int dimId, int chunkX, int chunkZ, int isNewChunk);
|
||||
typedef int(__stdcall *fn_fire_chunk_unload)(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
struct OpenContainerInfo
|
||||
{
|
||||
@@ -167,13 +160,6 @@ static fn_fire_piston_extend s_managedFirePistonExtend = nullptr;
|
||||
static fn_fire_piston_retract s_managedFirePistonRetract = nullptr;
|
||||
static fn_fire_command_preprocess s_managedFireCommandPreprocess = nullptr;
|
||||
static fn_fire_block_from_to s_managedFireBlockFromTo = nullptr;
|
||||
static fn_set_chunk_callbacks s_managedSetChunkCallbacks = nullptr;
|
||||
static fn_set_block_info_callbacks s_managedSetBlockInfoCallbacks = nullptr;
|
||||
static fn_set_world_entity_callbacks s_managedSetWorldEntityCallbacks = nullptr;
|
||||
static fn_set_subscription_callbacks s_managedSetSubscriptionCallbacks = nullptr;
|
||||
static fn_set_server_callbacks s_managedSetServerCallbacks = nullptr;
|
||||
static fn_fire_chunk_load s_managedFireChunkLoad = nullptr;
|
||||
static fn_fire_chunk_unload s_managedFireChunkUnload = nullptr;
|
||||
|
||||
static bool s_initialized = false;
|
||||
|
||||
@@ -256,13 +242,6 @@ void Initialize()
|
||||
{L"FirePistonRetract", (void **)&s_managedFirePistonRetract},
|
||||
{L"FireCommandPreprocess", (void **)&s_managedFireCommandPreprocess},
|
||||
{L"FireBlockFromTo", (void **)&s_managedFireBlockFromTo},
|
||||
{L"SetChunkCallbacks", (void **)&s_managedSetChunkCallbacks},
|
||||
{L"SetBlockInfoCallbacks", (void **)&s_managedSetBlockInfoCallbacks},
|
||||
{L"SetWorldEntityCallbacks", (void **)&s_managedSetWorldEntityCallbacks},
|
||||
{L"SetSubscriptionCallbacks", (void **)&s_managedSetSubscriptionCallbacks},
|
||||
{L"SetServerCallbacks", (void **)&s_managedSetServerCallbacks},
|
||||
{L"FireChunkLoad", (void **)&s_managedFireChunkLoad},
|
||||
{L"FireChunkUnload", (void **)&s_managedFireChunkUnload},
|
||||
};
|
||||
|
||||
bool ok = true;
|
||||
@@ -328,11 +307,7 @@ void Initialize()
|
||||
(void *)&NativeOpenVirtualContainer,
|
||||
(void *)&NativeGetItemMeta,
|
||||
(void *)&NativeSetItemMeta,
|
||||
(void *)&NativeSetHeldItemSlot,
|
||||
(void *)&NativeGetCarriedItem,
|
||||
(void *)&NativeSetCarriedItem,
|
||||
(void *)&NativeGetEnderChestContents,
|
||||
(void *)&NativeSetEnderChestSlot);
|
||||
(void *)&NativeSetHeldItemSlot);
|
||||
|
||||
s_managedSetEntityCallbacks(
|
||||
(void *)&NativeSetSneaking,
|
||||
@@ -361,33 +336,6 @@ void Initialize()
|
||||
(void *)&NativeGetPassengerId,
|
||||
(void *)&NativeGetEntityInfo);
|
||||
|
||||
s_managedSetChunkCallbacks(
|
||||
(void *)&NativeIsChunkLoaded,
|
||||
(void *)&NativeLoadChunk,
|
||||
(void *)&NativeUnloadChunk,
|
||||
(void *)&NativeGetLoadedChunks,
|
||||
(void *)&NativeIsChunkInUse,
|
||||
(void *)&NativeGetChunkSnapshot,
|
||||
(void *)&NativeUnloadChunkRequest,
|
||||
(void *)&NativeRegenerateChunk,
|
||||
(void *)&NativeRefreshChunk);
|
||||
|
||||
s_managedSetBlockInfoCallbacks(
|
||||
(void *)&NativeGetSkyLight,
|
||||
(void *)&NativeGetBlockLight,
|
||||
(void *)&NativeGetBiomeId,
|
||||
(void *)&NativeSetBiomeId);
|
||||
|
||||
s_managedSetWorldEntityCallbacks(
|
||||
(void *)&NativeGetWorldEntities,
|
||||
(void *)&NativeGetChunkEntities);
|
||||
|
||||
s_managedSetSubscriptionCallbacks(
|
||||
(void *)&NativeSetHandlerMask);
|
||||
|
||||
s_managedSetServerCallbacks(
|
||||
(void *)&NativeGetServerTickCount);
|
||||
|
||||
LogInfo("fourkit", "FourKit initialized successfully.");
|
||||
}
|
||||
|
||||
@@ -533,12 +481,8 @@ bool FirePlayerMove(int entityId,
|
||||
double toX, double toY, double toZ,
|
||||
double *outToX, double *outToY, double *outToZ)
|
||||
{
|
||||
// Caller reads outTo* unconditionally; init on every early-return.
|
||||
if (!s_initialized || !s_managedFireMove || !HasHandlers(kHandlerKind_PlayerMove))
|
||||
if (!s_initialized || !s_managedFireMove)
|
||||
{
|
||||
*outToX = toX;
|
||||
*outToY = toY;
|
||||
*outToZ = toZ;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1070,22 +1014,4 @@ bool FireBlockFromTo(int dimId, int fromX, int fromY, int fromZ, int toX, int to
|
||||
return false;
|
||||
return s_managedFireBlockFromTo(dimId, fromX, fromY, fromZ, toX, toY, toZ, face) != 0;
|
||||
}
|
||||
|
||||
void FireChunkLoad(int dimId, int chunkX, int chunkZ, bool isNewChunk)
|
||||
{
|
||||
if (!s_initialized || !s_managedFireChunkLoad)
|
||||
return;
|
||||
if (!HasHandlers(kHandlerKind_ChunkLoad))
|
||||
return;
|
||||
s_managedFireChunkLoad(dimId, chunkX, chunkZ, isNewChunk ? 1 : 0);
|
||||
}
|
||||
|
||||
bool FireChunkUnload(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
if (!s_initialized || !s_managedFireChunkUnload)
|
||||
return false;
|
||||
if (!HasHandlers(kHandlerKind_ChunkUnload))
|
||||
return false;
|
||||
return s_managedFireChunkUnload(dimId, chunkX, chunkZ) != 0;
|
||||
}
|
||||
} // namespace FourKitBridge
|
||||
|
||||
@@ -105,8 +105,6 @@ namespace FourKitBridge
|
||||
bool FirePistonRetract(int dimId, int x, int y, int z, int direction);
|
||||
bool FireCommandPreprocess(int entityId, const std::wstring &commandLine, std::wstring &outCommand);
|
||||
bool FireBlockFromTo(int dimId, int fromX, int fromY, int fromZ, int toX, int toY, int toZ, int face);
|
||||
void FireChunkLoad(int dimId, int chunkX, int chunkZ, bool isNewChunk);
|
||||
bool FireChunkUnload(int dimId, int chunkX, int chunkZ);
|
||||
#else
|
||||
// Standalone build: every hook is an inline no-op. Cancellable hooks
|
||||
// return false so vanilla code paths run unmodified, AND every out
|
||||
@@ -213,7 +211,5 @@ namespace FourKitBridge
|
||||
inline bool FirePistonRetract(int, int, int, int, int) { return false; }
|
||||
inline bool FireCommandPreprocess(int, const std::wstring &commandLine, std::wstring &outCommand) { outCommand = commandLine; return false; }
|
||||
inline bool FireBlockFromTo(int, int, int, int, int, int, int, int) { return false; }
|
||||
inline void FireChunkLoad(int, int, int, bool) {}
|
||||
inline bool FireChunkUnload(int, int, int) { return false; }
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "Common/StringUtils.h"
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -14,10 +13,6 @@
|
||||
#include "../Minecraft.Client/ServerLevel.h"
|
||||
#include "../Minecraft.Client/ServerPlayer.h"
|
||||
#include "../Minecraft.Client/ServerPlayerGameMode.h"
|
||||
#include "../Minecraft.Client/ServerChunkCache.h"
|
||||
#include "../Minecraft.World/LevelChunk.h"
|
||||
#include "../Minecraft.World/Biome.h"
|
||||
#include "../Minecraft.World/LightLayer.h"
|
||||
#include "../Minecraft.Client/Windows64/Network/WinsockNetLayer.h"
|
||||
#include "../Minecraft.World/AbstractContainerMenu.h"
|
||||
#include "../Minecraft.World/AddGlobalEntityPacket.h"
|
||||
@@ -34,7 +29,6 @@
|
||||
#include "../Minecraft.World/Player.h"
|
||||
#include "../Minecraft.World/PlayerAbilitiesPacket.h"
|
||||
#include "../Minecraft.World/SetCarriedItemPacket.h"
|
||||
#include "../Minecraft.World/BlockRegionUpdatePacket.h"
|
||||
#include "../Minecraft.World/SetExperiencePacket.h"
|
||||
#include "../Minecraft.World/SetHealthPacket.h"
|
||||
#include "../Minecraft.World/LevelSoundPacket.h"
|
||||
@@ -52,8 +46,6 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
std::atomic<uint32_t> g_handlerMask{0};
|
||||
|
||||
static shared_ptr<ServerPlayer> FindPlayer(int entityId)
|
||||
{
|
||||
PlayerList *list = MinecraftServer::getPlayerList();
|
||||
@@ -112,24 +104,6 @@ class VirtualContainer : public SimpleContainer
|
||||
|
||||
namespace FourKitBridge
|
||||
{
|
||||
|
||||
void __cdecl NativeSetHandlerMask(uint32_t mask)
|
||||
{
|
||||
g_handlerMask.store(mask, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool HasHandlers(int kind)
|
||||
{
|
||||
if (kind < 0 || kind >= 32) return false;
|
||||
return (g_handlerMask.load(std::memory_order_acquire) & (1u << kind)) != 0;
|
||||
}
|
||||
|
||||
int __cdecl NativeGetServerTickCount()
|
||||
{
|
||||
MinecraftServer *srv = MinecraftServer::getInstance();
|
||||
return srv ? srv->tickCount : 0;
|
||||
}
|
||||
|
||||
void __cdecl NativeDamagePlayer(int entityId, float amount)
|
||||
{
|
||||
auto player = FindPlayer(entityId);
|
||||
@@ -330,20 +304,20 @@ int __cdecl NativeGetTileData(int dimId, int x, int y, int z)
|
||||
return level->getData(x, y, z);
|
||||
}
|
||||
|
||||
void __cdecl NativeSetTile(int dimId, int x, int y, int z, int tileId, int data, int flags)
|
||||
void __cdecl NativeSetTile(int dimId, int x, int y, int z, int tileId, int data)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return;
|
||||
level->setTileAndData(x, y, z, tileId, data, flags);
|
||||
level->setTileAndData(x, y, z, tileId, data, Tile::UPDATE_ALL);
|
||||
}
|
||||
|
||||
void __cdecl NativeSetTileData(int dimId, int x, int y, int z, int data, int flags)
|
||||
void __cdecl NativeSetTileData(int dimId, int x, int y, int z, int data)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return;
|
||||
level->setData(x, y, z, data, flags);
|
||||
level->setData(x, y, z, data, Tile::UPDATE_ALL);
|
||||
}
|
||||
|
||||
int __cdecl NativeBreakBlock(int dimId, int x, int y, int z)
|
||||
@@ -849,14 +823,16 @@ int __cdecl NativeGetItemMeta(int entityId, int slot, char *outBuf, int bufSize)
|
||||
return 0;
|
||||
|
||||
CompoundTag *tag = item->getTag();
|
||||
if (!tag || !tag->contains(L"display"))
|
||||
return 0;
|
||||
|
||||
CompoundTag *display = tag->getCompound(L"display");
|
||||
bool hasName = display->contains(L"Name");
|
||||
bool hasLore = display->contains(L"Lore");
|
||||
|
||||
bool hasEnchantments = item->isEnchanted();
|
||||
|
||||
CompoundTag *display = (tag && tag->contains(L"display")) ? tag->getCompound(L"display") : nullptr;
|
||||
bool hasName = display && display->contains(L"Name");
|
||||
bool hasLore = display && display->contains(L"Lore");
|
||||
|
||||
if (!hasName && !hasLore && !hasEnchantments)
|
||||
if (!hasName && !hasLore)
|
||||
return 0;
|
||||
|
||||
int offset = 0;
|
||||
@@ -1092,68 +1068,6 @@ void __cdecl NativeSetHeldItemSlot(int entityId, int slot)
|
||||
player->connection->queueSend(std::make_shared<SetCarriedItemPacket>(slot));
|
||||
}
|
||||
|
||||
void __cdecl NativeGetCarriedItem(int entityId, int *outData)
|
||||
{
|
||||
outData[0] = 0;
|
||||
outData[1] = 0;
|
||||
outData[2] = 0;
|
||||
auto player = FindPlayer(entityId);
|
||||
if (!player || !player->inventory)
|
||||
return;
|
||||
auto item = player->inventory->getCarried();
|
||||
if (item)
|
||||
{
|
||||
outData[0] = item->id;
|
||||
outData[1] = item->getAuxValue();
|
||||
outData[2] = (int)item->count;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl NativeSetCarriedItem(int entityId, int itemId, int count, int aux)
|
||||
{
|
||||
auto player = FindPlayer(entityId);
|
||||
if (!player || !player->inventory)
|
||||
return;
|
||||
if (itemId <= 0 || count <= 0)
|
||||
player->inventory->setCarried(nullptr);
|
||||
else
|
||||
player->inventory->setCarried(std::make_shared<ItemInstance>(itemId, count, aux));
|
||||
}
|
||||
|
||||
void __cdecl NativeGetEnderChestContents(int entityId, int *outData)
|
||||
{
|
||||
memset(outData, 0, 27 * 3 * sizeof(int));
|
||||
auto player = FindPlayer(entityId);
|
||||
if (!player)
|
||||
return;
|
||||
auto ec = player->getEnderChestInventory();
|
||||
if (!ec)
|
||||
return;
|
||||
unsigned int size = ec->getContainerSize();
|
||||
if (size > 27)
|
||||
size = 27;
|
||||
for (unsigned int i = 0; i < size; i++)
|
||||
{
|
||||
WriteInventoryItemData(ec->getItem(i), i, outData);
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl NativeSetEnderChestSlot(int entityId, int slot, int itemId, int count, int aux)
|
||||
{
|
||||
auto player = FindPlayer(entityId);
|
||||
if (!player)
|
||||
return;
|
||||
auto ec = player->getEnderChestInventory();
|
||||
if (!ec)
|
||||
return;
|
||||
if (slot < 0 || slot >= (int)ec->getContainerSize())
|
||||
return;
|
||||
if (itemId <= 0 || count <= 0)
|
||||
ec->setItem(slot, nullptr);
|
||||
else
|
||||
ec->setItem(slot, std::make_shared<ItemInstance>(itemId, count, aux));
|
||||
}
|
||||
|
||||
void __cdecl NativeSetSneaking(int entityId, int sneak)
|
||||
{
|
||||
auto player = FindPlayer(entityId);
|
||||
@@ -1354,284 +1268,4 @@ void __cdecl NativeGetEntityInfo(int entityId, double *outData)
|
||||
outData[4] = (double)entity->dimension;
|
||||
}
|
||||
|
||||
int __cdecl NativeGetWorldEntities(int dimId, int **outBuf)
|
||||
{
|
||||
*outBuf = nullptr;
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return 0;
|
||||
|
||||
EnterCriticalSection(&level->m_entitiesCS);
|
||||
int total = (int)level->entities.size();
|
||||
int *buf = (int *)CoTaskMemAlloc(total * 3 * sizeof(int));
|
||||
int count = 0;
|
||||
if (buf)
|
||||
{
|
||||
for (auto &entity : level->entities)
|
||||
{
|
||||
if (!entity)
|
||||
continue;
|
||||
int idx = count * 3;
|
||||
buf[idx] = entity->entityId;
|
||||
buf[idx + 1] = MapEntityType((int)entity->GetType());
|
||||
buf[idx + 2] = entity->instanceof(eTYPE_LIVINGENTITY) ? 1 : 0;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&level->m_entitiesCS);
|
||||
*outBuf = buf;
|
||||
return count;
|
||||
}
|
||||
|
||||
int __cdecl NativeGetChunkEntities(int dimId, int chunkX, int chunkZ, int **outBuf)
|
||||
{
|
||||
*outBuf = nullptr;
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return 0;
|
||||
|
||||
EnterCriticalSection(&level->m_entitiesCS);
|
||||
int total = (int)level->entities.size();
|
||||
int *buf = (int *)CoTaskMemAlloc(total * 3 * sizeof(int));
|
||||
int count = 0;
|
||||
if (buf)
|
||||
{
|
||||
for (auto &entity : level->entities)
|
||||
{
|
||||
if (!entity)
|
||||
continue;
|
||||
int ecx = Mth::floor(entity->x / 16.0);
|
||||
int ecz = Mth::floor(entity->z / 16.0);
|
||||
if (ecx != chunkX || ecz != chunkZ)
|
||||
continue;
|
||||
int idx = count * 3;
|
||||
buf[idx] = entity->entityId;
|
||||
buf[idx + 1] = MapEntityType((int)entity->GetType());
|
||||
buf[idx + 2] = entity->instanceof(eTYPE_LIVINGENTITY) ? 1 : 0;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&level->m_entitiesCS);
|
||||
*outBuf = buf;
|
||||
return count;
|
||||
}
|
||||
|
||||
int __cdecl NativeIsChunkLoaded(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
return level->cache->hasChunk(chunkX, chunkZ) ? 1 : 0;
|
||||
}
|
||||
|
||||
int __cdecl NativeLoadChunk(int dimId, int chunkX, int chunkZ, int generate)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
LevelChunk *chunk = level->cache->create(chunkX, chunkZ);
|
||||
return (chunk != nullptr) ? 1 : 0;
|
||||
}
|
||||
|
||||
int __cdecl NativeUnloadChunk(int dimId, int chunkX, int chunkZ, int save, int safe)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
if (safe)
|
||||
{
|
||||
if (!level->cache->hasChunk(chunkX, chunkZ))
|
||||
return 0;
|
||||
LevelChunk *chunk = level->cache->getChunk(chunkX, chunkZ);
|
||||
if (chunk && chunk->containsPlayer())
|
||||
return 0;
|
||||
}
|
||||
level->cache->drop(chunkX, chunkZ);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __cdecl NativeGetLoadedChunks(int dimId, int **coordBuf)
|
||||
{
|
||||
// wow gay
|
||||
*coordBuf = nullptr;
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
|
||||
std::vector<LevelChunk *> *list = level->cache->getLoadedChunkList();
|
||||
|
||||
if (!list)
|
||||
return 0;
|
||||
|
||||
|
||||
|
||||
int total = (int)list->size();
|
||||
int *buf = (int *)CoTaskMemAlloc(total * 2 * sizeof(int));
|
||||
int count = 0;
|
||||
|
||||
if (buf)
|
||||
{
|
||||
for (auto *chunk : *list)
|
||||
{
|
||||
if (chunk)
|
||||
{
|
||||
buf[count * 2] = chunk->x;
|
||||
buf[count * 2 + 1] = chunk->z;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*coordBuf = buf;
|
||||
return count;
|
||||
}
|
||||
|
||||
int __cdecl NativeIsChunkInUse(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
PlayerList *list = MinecraftServer::getPlayerList();
|
||||
if (!list)
|
||||
return 0;
|
||||
for (auto &p : list->players)
|
||||
{
|
||||
if (p && p->dimension == dimId)
|
||||
{
|
||||
int px = (int)floor(p->x) >> 4;
|
||||
int pz = (int)floor(p->z) >> 4;
|
||||
if (px == chunkX && pz == chunkZ)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __cdecl NativeGetChunkSnapshot(int dimId, int chunkX, int chunkZ, int *blockIds, int *blockData, int *maxBlockY)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
{
|
||||
memset(blockIds, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(blockData, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(maxBlockY, 0, 16 * 16 * sizeof(int));
|
||||
return;
|
||||
}
|
||||
if (!level->cache->hasChunk(chunkX, chunkZ))
|
||||
{
|
||||
memset(blockIds, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(blockData, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(maxBlockY, 0, 16 * 16 * sizeof(int));
|
||||
return;
|
||||
}
|
||||
LevelChunk *chunk = level->cache->getChunk(chunkX, chunkZ);
|
||||
if (!chunk)
|
||||
{
|
||||
memset(blockIds, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(blockData, 0, 16 * 128 * 16 * sizeof(int));
|
||||
memset(maxBlockY, 0, 16 * 16 * sizeof(int));
|
||||
return;
|
||||
}
|
||||
for (int lx = 0; lx < 16; lx++)
|
||||
{
|
||||
for (int lz = 0; lz < 16; lz++)
|
||||
{
|
||||
int highest = 0;
|
||||
for (int ly = 0; ly < 128; ly++)
|
||||
{
|
||||
int idx = (lx * 128 * 16) + (ly * 16) + lz;
|
||||
blockIds[idx] = chunk->getTile(lx, ly, lz);
|
||||
blockData[idx] = chunk->getData(lx, ly, lz);
|
||||
if (blockIds[idx] != 0)
|
||||
highest = ly;
|
||||
}
|
||||
maxBlockY[lx * 16 + lz] = highest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __cdecl NativeUnloadChunkRequest(int dimId, int chunkX, int chunkZ, int safe)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
if (safe)
|
||||
{
|
||||
if (!level->cache->hasChunk(chunkX, chunkZ))
|
||||
return 0;
|
||||
LevelChunk *chunk = level->cache->getChunk(chunkX, chunkZ);
|
||||
if (chunk && chunk->containsPlayer())
|
||||
return 0;
|
||||
}
|
||||
level->cache->drop(chunkX, chunkZ);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __cdecl NativeRegenerateChunk(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level || !level->cache)
|
||||
return 0;
|
||||
level->cache->regenerateChunk(chunkX, chunkZ);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __cdecl NativeRefreshChunk(int dimId, int chunkX, int chunkZ)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return 0;
|
||||
|
||||
PlayerList *list = MinecraftServer::getPlayerList();
|
||||
if (!list)
|
||||
return 0;
|
||||
|
||||
auto packet = std::make_shared<BlockRegionUpdatePacket>(chunkX * 16, 0, chunkZ * 16, 16, Level::maxBuildHeight, 16, level);
|
||||
for (auto &p : list->players)
|
||||
{
|
||||
if (!p || p->dimension != dimId || !p->connection || p->connection->isLocal())
|
||||
continue;
|
||||
p->connection->send(packet);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __cdecl NativeGetSkyLight(int dimId, int x, int y, int z)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return 0;
|
||||
return level->getBrightness(LightLayer::Sky, x, y, z);
|
||||
}
|
||||
|
||||
int __cdecl NativeGetBlockLight(int dimId, int x, int y, int z)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return 0;
|
||||
return level->getBrightness(LightLayer::Block, x, y, z);
|
||||
}
|
||||
|
||||
int __cdecl NativeGetBiomeId(int dimId, int x, int z)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return 1;
|
||||
Biome *biome = level->getBiome(x, z);
|
||||
return biome ? biome->id : 1;
|
||||
}
|
||||
|
||||
void __cdecl NativeSetBiomeId(int dimId, int x, int z, int biomeId)
|
||||
{
|
||||
ServerLevel *level = GetLevel(dimId);
|
||||
if (!level)
|
||||
return;
|
||||
LevelChunk *chunk = level->getChunk(x >> 4, z >> 4);
|
||||
if (!chunk)
|
||||
return;
|
||||
byteArray biomes = chunk->getBiomes();
|
||||
if (biomes.data == nullptr)
|
||||
return;
|
||||
int lx = x & 0xf;
|
||||
int lz = z & 0xf;
|
||||
biomes.data[(lz << 4) | lx] = static_cast<unsigned char>(biomeId & 0xff);
|
||||
}
|
||||
|
||||
} // namespace FourKitBridge
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace FourKitBridge
|
||||
{
|
||||
// Must match HandlerKind in FourKit.cs.
|
||||
enum HandlerKind : int {
|
||||
kHandlerKind_ChunkLoad = 0,
|
||||
kHandlerKind_ChunkUnload = 1,
|
||||
kHandlerKind_PlayerMove = 2,
|
||||
};
|
||||
|
||||
void __cdecl NativeSetHandlerMask(uint32_t mask);
|
||||
bool HasHandlers(int kind);
|
||||
|
||||
int __cdecl NativeGetServerTickCount();
|
||||
|
||||
// core
|
||||
void __cdecl NativeDamagePlayer(int entityId, float amount);
|
||||
void __cdecl NativeSetPlayerHealth(int entityId, float health);
|
||||
@@ -31,8 +18,8 @@ namespace FourKitBridge
|
||||
// World
|
||||
int __cdecl NativeGetTileId(int dimId, int x, int y, int z);
|
||||
int __cdecl NativeGetTileData(int dimId, int x, int y, int z);
|
||||
void __cdecl NativeSetTile(int dimId, int x, int y, int z, int tileId, int data, int flags);
|
||||
void __cdecl NativeSetTileData(int dimId, int x, int y, int z, int data, int flags);
|
||||
void __cdecl NativeSetTile(int dimId, int x, int y, int z, int tileId, int data);
|
||||
void __cdecl NativeSetTileData(int dimId, int x, int y, int z, int data);
|
||||
int __cdecl NativeBreakBlock(int dimId, int x, int y, int z);
|
||||
int __cdecl NativeGetHighestBlockY(int dimId, int x, int z);
|
||||
void __cdecl NativeGetWorldInfo(int dimId, double *outBuf);
|
||||
@@ -65,12 +52,6 @@ namespace FourKitBridge
|
||||
void __cdecl NativeSetItemMeta(int entityId, int slot, const char *inBuf, int bufSize);
|
||||
void __cdecl NativeSetHeldItemSlot(int entityId, int slot);
|
||||
|
||||
// carried item (cursor) & ender chest
|
||||
void __cdecl NativeGetCarriedItem(int entityId, int *outData);
|
||||
void __cdecl NativeSetCarriedItem(int entityId, int itemId, int count, int aux);
|
||||
void __cdecl NativeGetEnderChestContents(int entityId, int *outData);
|
||||
void __cdecl NativeSetEnderChestSlot(int entityId, int slot, int itemId, int count, int aux);
|
||||
|
||||
// ent
|
||||
void __cdecl NativeSetSneaking(int entityId, int sneak);
|
||||
void __cdecl NativeSetVelocity(int entityId, double x, double y, double z);
|
||||
@@ -97,25 +78,4 @@ namespace FourKitBridge
|
||||
int __cdecl NativeGetVehicleId(int entityId);
|
||||
int __cdecl NativeGetPassengerId(int entityId);
|
||||
void __cdecl NativeGetEntityInfo(int entityId, double *outData);
|
||||
|
||||
// chunk
|
||||
int __cdecl NativeIsChunkLoaded(int dimId, int chunkX, int chunkZ);
|
||||
int __cdecl NativeLoadChunk(int dimId, int chunkX, int chunkZ, int generate);
|
||||
int __cdecl NativeUnloadChunk(int dimId, int chunkX, int chunkZ, int save, int safe);
|
||||
int __cdecl NativeGetLoadedChunks(int dimId, int **coordBuf);
|
||||
int __cdecl NativeIsChunkInUse(int dimId, int chunkX, int chunkZ);
|
||||
void __cdecl NativeGetChunkSnapshot(int dimId, int chunkX, int chunkZ, int *blockIds, int *blockData, int *maxBlockY);
|
||||
int __cdecl NativeUnloadChunkRequest(int dimId, int chunkX, int chunkZ, int safe);
|
||||
int __cdecl NativeRegenerateChunk(int dimId, int chunkX, int chunkZ);
|
||||
int __cdecl NativeRefreshChunk(int dimId, int chunkX, int chunkZ);
|
||||
|
||||
// world entity bs
|
||||
int __cdecl NativeGetWorldEntities(int dimId, int **outBuf);
|
||||
int __cdecl NativeGetChunkEntities(int dimId, int chunkX, int chunkZ, int **outBuf);
|
||||
|
||||
// block info (light, biome)
|
||||
int __cdecl NativeGetSkyLight(int dimId, int x, int y, int z);
|
||||
int __cdecl NativeGetBlockLight(int dimId, int x, int y, int z);
|
||||
int __cdecl NativeGetBiomeId(int dimId, int x, int z);
|
||||
void __cdecl NativeSetBiomeId(int dimId, int x, int z, int biomeId);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <vector>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "../Minecraft.World/DisconnectPacket.h"
|
||||
#include "..\Minecraft.World\DisconnectPacket.h"
|
||||
|
||||
namespace ServerRuntime
|
||||
{
|
||||
|
||||
@@ -888,14 +888,6 @@ ServerPropertiesConfig LoadServerPropertiesConfig()
|
||||
config.hardcore = ReadNormalizedBoolProperty(&merged, "hardcore", false, &shouldWrite);
|
||||
config.hardcoreBanIp = ReadNormalizedBoolProperty(&merged, "hardcore-ban-ip", false, &shouldWrite);
|
||||
|
||||
config.maxMonsters = ReadNormalizedIntProperty(&merged, "max-monsters", 50, 0, 1000, &shouldWrite);
|
||||
config.maxAnimals = ReadNormalizedIntProperty(&merged, "max-animals", 50, 0, 1000, &shouldWrite);
|
||||
config.maxAmbient = ReadNormalizedIntProperty(&merged, "max-ambient", 20, 0, 1000, &shouldWrite);
|
||||
config.maxWaterAnimals = ReadNormalizedIntProperty(&merged, "max-water-animals", 5, 0, 1000, &shouldWrite);
|
||||
config.maxWolves = ReadNormalizedIntProperty(&merged, "max-wolves", 8, 0, 1000, &shouldWrite);
|
||||
config.maxChickens = ReadNormalizedIntProperty(&merged, "max-chickens", 8, 0, 1000, &shouldWrite);
|
||||
config.maxMushroomCows = ReadNormalizedIntProperty(&merged, "max-mushroom-cows", 2, 0, 1000, &shouldWrite);
|
||||
|
||||
config.maxBuildHeight = ReadNormalizedIntProperty(&merged, "max-build-height", 256, 64, 256, &shouldWrite);
|
||||
config.motd = ReadNormalizedStringProperty(&merged, "motd", "A Minecraft Server", 255, &shouldWrite);
|
||||
|
||||
|
||||
@@ -80,21 +80,6 @@ namespace ServerRuntime
|
||||
/** `hardcore-ban-ip` — whether hardcore death bans include IP bans */
|
||||
bool hardcoreBanIp;
|
||||
|
||||
/** `max-monsters` natural spawn cap for monsters (zombies, skeletons, creepers, etc.) */
|
||||
int maxMonsters;
|
||||
/** `max-animals` natural spawn cap for animals (cows, sheep, pigs) */
|
||||
int maxAnimals;
|
||||
/** `max-ambient` natural spawn cap for ambient mobs (bats) */
|
||||
int maxAmbient;
|
||||
/** `max-water-animals` natural spawn cap for water mobs (squid) */
|
||||
int maxWaterAnimals;
|
||||
/** `max-wolves` natural spawn cap for wolves */
|
||||
int maxWolves;
|
||||
/** `max-chickens` natural spawn cap for chickens */
|
||||
int maxChickens;
|
||||
/** `max-mushroom-cows` natural spawn cap for mooshrooms */
|
||||
int maxMushroomCows;
|
||||
|
||||
/** security settings */
|
||||
/** `hide-player-list-prelogin` — strip XUIDs from PreLoginPacket response */
|
||||
bool hidePlayerListPreLogin;
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#include "../../Minecraft.World/ConsoleSaveFileOriginal.h"
|
||||
#include "../../Minecraft.World/net.minecraft.world.level.tile.h"
|
||||
#include "../../Minecraft.World/Random.h"
|
||||
#include "../../Minecraft.World/MobCategory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -558,18 +557,10 @@ int main(int argc, char **argv)
|
||||
{
|
||||
LogError("startup", "Minecraft initialization failed.");
|
||||
CleanupDevice();
|
||||
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
MobCategory::monster->setMaxInstancesPerLevel(serverProperties.maxMonsters);
|
||||
MobCategory::creature->setMaxInstancesPerLevel(serverProperties.maxAnimals);
|
||||
MobCategory::ambient->setMaxInstancesPerLevel(serverProperties.maxAmbient);
|
||||
MobCategory::waterCreature->setMaxInstancesPerLevel(serverProperties.maxWaterAnimals);
|
||||
MobCategory::creature_wolf->setMaxInstancesPerLevel(serverProperties.maxWolves);
|
||||
MobCategory::creature_chicken->setMaxInstancesPerLevel(serverProperties.maxChickens);
|
||||
MobCategory::creature_mushroomcow->setMaxInstancesPerLevel(serverProperties.maxMushroomCows);
|
||||
|
||||
app.InitGameSettings();
|
||||
|
||||
MinecraftServer::resetFlags();
|
||||
@@ -756,7 +747,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
DWORD now = GetTickCount();
|
||||
if ((LONG)(now - nextAutosaveTick) >= 0 && !IsShutdownRequested() && !app.m_bShutdown)
|
||||
if ((LONG)(now - nextAutosaveTick) >= 0)
|
||||
{
|
||||
if (app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle && !ConsoleSaveFileOriginal::hasPendingBackgroundSave())
|
||||
{
|
||||
@@ -777,30 +768,16 @@ int main(int argc, char **argv)
|
||||
|
||||
LogInfof("shutdown", "Dedicated server stopped");
|
||||
MinecraftServer *server = MinecraftServer::getInstance();
|
||||
if (server != NULL)
|
||||
if (server != NULL && !ConsoleSaveFileOriginal::hasPendingBackgroundSave())
|
||||
{
|
||||
// Drain any in-flight autosave before requesting the exit save so the
|
||||
// async autosave can't overwrite the exit save with an older snapshot,
|
||||
// and so m_saveOnExit gets set (prior logic skipped it when a save was
|
||||
// pending, causing silent data loss on restart).
|
||||
if (ConsoleSaveFileOriginal::hasPendingBackgroundSave())
|
||||
{
|
||||
LogWorldIO("Draining pending autosave before exit save...");
|
||||
const DWORD kDrainTimeoutMs = 30000;
|
||||
DWORD drainStart = GetTickCount();
|
||||
while (ConsoleSaveFileOriginal::hasPendingBackgroundSave())
|
||||
{
|
||||
if ((LONG)(GetTickCount() - drainStart) > (LONG)kDrainTimeoutMs)
|
||||
{
|
||||
LogWorldIO("Autosave drain timed out; continuing with exit save");
|
||||
break;
|
||||
}
|
||||
TickCoreSystems();
|
||||
Sleep(10);
|
||||
}
|
||||
}
|
||||
server->setSaveOnExit(true);
|
||||
LogWorldIO("requesting exit save");
|
||||
server->setSaveOnExit(true);
|
||||
LogWorldIO("requesting save before shutdown");
|
||||
LogWorldIO("using saveOnExit for shutdown");
|
||||
}
|
||||
|
||||
if (ConsoleSaveFileOriginal::hasPendingBackgroundSave())
|
||||
{
|
||||
LogWorldIO("Waiting for autosave to complete...");
|
||||
}
|
||||
|
||||
MinecraftServer::HaltServer();
|
||||
|
||||
@@ -400,7 +400,6 @@ set(_MINECRAFT_SERVER_COMMON_ROOT
|
||||
"${_MS_SRC}/../Minecraft.Client/ScrolledSelectionList.cpp"
|
||||
"${_MS_SRC}/../Minecraft.Client/SelectWorldScreen.cpp"
|
||||
"${_MS_SRC}/../Minecraft.Client/ServerChunkCache.cpp"
|
||||
"${_MS_SRC}/../Minecraft.Client/ServerChunkCache.h"
|
||||
"${_MS_SRC}/../Minecraft.Client/ServerCommandDispatcher.cpp"
|
||||
"${_MS_SRC}/../Minecraft.Client/ServerConnection.cpp"
|
||||
"${_MS_SRC}/../Minecraft.Client/ServerLevel.cpp"
|
||||
@@ -502,7 +501,6 @@ set(_MINECRAFT_SERVER_COMMON_ROOT
|
||||
"${_MS_SRC}/../Minecraft.Client/iob_shim.asm"
|
||||
"${_MS_SRC}/../Minecraft.Client/stdafx.cpp"
|
||||
"${_MS_SRC}/../Minecraft.Client/stubs.cpp"
|
||||
"${_MS_SRC}/../Minecraft.World/Entity.cpp"
|
||||
"${_MS_SRC}/../Minecraft.World/AbstractContainerMenu.cpp"
|
||||
"${_MS_SRC}/../Minecraft.World/CompoundContainer.h"
|
||||
"${_MS_SRC}/../Minecraft.World/ItemEntity.cpp"
|
||||
|
||||
@@ -98,7 +98,6 @@ public:
|
||||
e_ChatCommandTeleportMe,
|
||||
e_ChatCommandTeleportToMe,
|
||||
|
||||
e_ChatActionBar,
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@@ -124,7 +124,6 @@ DamageSource::DamageSource(ChatPacket::EChatPacketMessage msgId, ChatPacket::ECh
|
||||
_isProjectile = false;
|
||||
_isMagic = false;
|
||||
_isExplosion = false;
|
||||
_isCritical = false;
|
||||
|
||||
//this->msgId = msgId;
|
||||
m_msgId = msgId;
|
||||
@@ -154,15 +153,7 @@ DamageSource *DamageSource::bypassInvul()
|
||||
_bypassInvul = true;
|
||||
return this;
|
||||
}
|
||||
bool DamageSource::isCritical()
|
||||
{
|
||||
return _isCritical;
|
||||
}
|
||||
DamageSource *DamageSource::setIsCritical()
|
||||
{
|
||||
_isCritical = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
DamageSource *DamageSource::setIsFire()
|
||||
{
|
||||
isFireSource = true;
|
||||
|
||||
@@ -47,11 +47,8 @@ private:
|
||||
bool _scalesWithDifficulty;
|
||||
bool _isMagic;
|
||||
bool _isExplosion;
|
||||
bool _isCritical;
|
||||
|
||||
public:
|
||||
bool isCritical();
|
||||
DamageSource *setIsCritical();
|
||||
bool isProjectile();
|
||||
DamageSource *setProjectile();
|
||||
bool isExplosion();
|
||||
|
||||
+15
-19
@@ -27,17 +27,13 @@
|
||||
|
||||
const wstring Entity::RIDING_TAG = L"Riding";
|
||||
|
||||
//int Entity::entityCounter = 2048; // 4J - changed initialiser to 2048, as we are using range 0 - 2047 as special unique smaller ids for things that need network tracked
|
||||
int Entity::entityCounter = 16384; //now using full range of 0 - 16383, limit is 32k but we shouldnt need that yet
|
||||
int Entity::entityCounter = 2048; // 4J - changed initialiser to 2048, as we are using range 0 - 2047 as special unique smaller ids for things that need network tracked
|
||||
DWORD Entity::tlsIdx = TlsAlloc();
|
||||
|
||||
// 4J - added getSmallId & freeSmallId methods
|
||||
//unsigned int Entity::entityIdUsedFlags[2048/32] = {0};
|
||||
//unsigned int Entity::entityIdWanderFlags[2048/32] = {0};
|
||||
//unsigned int Entity::entityIdRemovingFlags[2048/32] = {0};
|
||||
unsigned int Entity::entityIdUsedFlags[16384/32] = {0};
|
||||
unsigned int Entity::entityIdWanderFlags[16384/32] = {0};
|
||||
unsigned int Entity::entityIdRemovingFlags[16384/32] = {0};
|
||||
unsigned int Entity::entityIdUsedFlags[2048/32] = {0};
|
||||
unsigned int Entity::entityIdWanderFlags[2048/32] = {0};
|
||||
unsigned int Entity::entityIdRemovingFlags[2048/32] = {0};
|
||||
int Entity::extraWanderIds[EXTRA_WANDER_MAX] = {0};
|
||||
int Entity::extraWanderTicks = 0;
|
||||
int Entity::extraWanderCount = 0;
|
||||
@@ -69,7 +65,7 @@ int Entity::getSmallId()
|
||||
}
|
||||
}
|
||||
|
||||
for( int i = 0; i < (16384 / 32 ); i++ )
|
||||
for( int i = 0; i < (2048 / 32 ); i++ )
|
||||
{
|
||||
unsigned int uiFlags = *puiUsedFlags;
|
||||
if( uiFlags != 0xffffffff )
|
||||
@@ -106,7 +102,7 @@ int Entity::getSmallId()
|
||||
|
||||
if (entityCounter == 0x7ffffff)
|
||||
{
|
||||
entityCounter = 16384;
|
||||
entityCounter = 2048;
|
||||
}
|
||||
return fallbackId;
|
||||
#else
|
||||
@@ -120,7 +116,7 @@ void Entity::countFlagsForPIX()
|
||||
{
|
||||
int freecount = 0;
|
||||
unsigned int *puiUsedFlags = entityIdUsedFlags;
|
||||
for( int i = 0; i < (16384 / 32 ); i++ )
|
||||
for( int i = 0; i < (2048 / 32 ); i++ )
|
||||
{
|
||||
unsigned int uiFlags = *puiUsedFlags;
|
||||
if( uiFlags != 0xffffffff )
|
||||
@@ -138,7 +134,7 @@ void Entity::countFlagsForPIX()
|
||||
puiUsedFlags++;
|
||||
}
|
||||
PIXAddNamedCounter(freecount,"Small Ids free");
|
||||
PIXAddNamedCounter(16384 - freecount,"Small Ids used");
|
||||
PIXAddNamedCounter(2048 - freecount,"Small Ids used");
|
||||
}
|
||||
|
||||
void Entity::resetSmallId()
|
||||
@@ -153,7 +149,7 @@ void Entity::resetSmallId()
|
||||
void Entity::freeSmallId(int index)
|
||||
{
|
||||
if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return; // Don't do anything with small ids if this isn't the server thread
|
||||
if( index >= 16384 ) return; // Don't do anything if this isn't a short id
|
||||
if( index >= 2048 ) return; // Don't do anything if this isn't a short id
|
||||
|
||||
unsigned int i = index / 32;
|
||||
unsigned int j = index % 32;
|
||||
@@ -176,7 +172,7 @@ void Entity::useSmallIds()
|
||||
void Entity::considerForExtraWandering(bool enable)
|
||||
{
|
||||
if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return; // Don't do anything with small ids if this isn't the server thread
|
||||
if( entityId >= 16384 ) return; // Don't do anything if this isn't a short id
|
||||
if( entityId >= 2048 ) return; // Don't do anything if this isn't a short id
|
||||
|
||||
unsigned int i = entityId / 32;
|
||||
unsigned int j = entityId % 32;
|
||||
@@ -196,7 +192,7 @@ void Entity::considerForExtraWandering(bool enable)
|
||||
bool Entity::isExtraWanderingEnabled()
|
||||
{
|
||||
if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return false; // Don't do anything with small ids if this isn't the server thread
|
||||
if( entityId >= 16384 ) return false; // Don't do anything if this isn't a short id
|
||||
if( entityId >= 2048 ) return false; // Don't do anything if this isn't a short id
|
||||
|
||||
for( int i = 0; i < extraWanderCount; i++ )
|
||||
{
|
||||
@@ -228,12 +224,12 @@ void Entity::tickExtraWandering()
|
||||
int entityId = 0;
|
||||
if( extraWanderCount )
|
||||
{
|
||||
entityId = ( extraWanderIds[ extraWanderCount - 1 ] + 1 ) % 16384;
|
||||
entityId = ( extraWanderIds[ extraWanderCount - 1 ] + 1 ) % 2048;
|
||||
}
|
||||
|
||||
extraWanderCount = 0;
|
||||
|
||||
for( int k = 0; ( k < 16384 ) && ( extraWanderCount < EXTRA_WANDER_MAX); k++ )
|
||||
for( int k = 0; ( k < 2048 ) && ( extraWanderCount < EXTRA_WANDER_MAX); k++ )
|
||||
{
|
||||
unsigned int i = entityId / 32;
|
||||
unsigned int j = entityId % 32;
|
||||
@@ -245,7 +241,7 @@ void Entity::tickExtraWandering()
|
||||
// printf("%d, ", entityId);
|
||||
}
|
||||
|
||||
entityId = ( entityId + 1 ) % 16384;
|
||||
entityId = ( entityId + 1 ) % 2048;
|
||||
}
|
||||
// printf("\n");
|
||||
}
|
||||
@@ -265,7 +261,7 @@ void Entity::_init(bool useSmallId, Level *level)
|
||||
else
|
||||
{
|
||||
entityId = Entity::entityCounter++;
|
||||
if(entityCounter == 0x7ffffff ) entityCounter = 16384;
|
||||
if(entityCounter == 0x7ffffff ) entityCounter = 2048;
|
||||
}
|
||||
|
||||
viewScale = 1.0;
|
||||
|
||||
@@ -382,12 +382,9 @@ private:
|
||||
|
||||
int getSmallId();
|
||||
void freeSmallId(int index);
|
||||
//static unsigned int entityIdUsedFlags[2048/32];
|
||||
//static unsigned int entityIdWanderFlags[2048/32];
|
||||
//static unsigned int entityIdRemovingFlags[2048/32];
|
||||
static unsigned int entityIdUsedFlags[16384/32];
|
||||
static unsigned int entityIdWanderFlags[16384/32];
|
||||
static unsigned int entityIdRemovingFlags[16384/32];
|
||||
static unsigned int entityIdUsedFlags[2048/32];
|
||||
static unsigned int entityIdWanderFlags[2048/32];
|
||||
static unsigned int entityIdRemovingFlags[2048/32];
|
||||
static int extraWanderIds[EXTRA_WANDER_MAX];
|
||||
static int extraWanderCount;
|
||||
static int extraWanderTicks;
|
||||
|
||||
@@ -5,9 +5,6 @@ class EntityEvent
|
||||
public:
|
||||
static const BYTE JUMP = 1;
|
||||
static const BYTE HURT = 2;
|
||||
//New
|
||||
static const BYTE HURT_CRITICAL = 19;
|
||||
static const BYTE DEATH_CRITICAL = 20;
|
||||
static const BYTE DEATH = 3;
|
||||
static const BYTE START_ATTACKING = 4;
|
||||
static const BYTE STOP_ATTACKING = 5;
|
||||
|
||||
+11
-11
@@ -4699,19 +4699,19 @@ bool Level::canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType)
|
||||
break;
|
||||
case eTYPE_CHICKEN:
|
||||
count = countInstanceOf( eTYPE_CHICKEN, true);
|
||||
max = MobCategory::maxChickensWithSpawnEgg();
|
||||
max = MobCategory::MAX_XBOX_CHICKENS_WITH_SPAWN_EGG;
|
||||
break;
|
||||
case eTYPE_WOLF:
|
||||
count = countInstanceOf( eTYPE_WOLF, true);
|
||||
max = MobCategory::maxWolvesWithSpawnEgg();
|
||||
max = MobCategory::MAX_XBOX_WOLVES_WITH_SPAWN_EGG;
|
||||
break;
|
||||
case eTYPE_MUSHROOMCOW:
|
||||
count = countInstanceOf( eTYPE_MUSHROOMCOW, true);
|
||||
max = MobCategory::maxMushroomCowsWithSpawnEgg();
|
||||
max = MobCategory::MAX_XBOX_MUSHROOMCOWS_WITH_SPAWN_EGG;
|
||||
break;
|
||||
case eTYPE_SQUID:
|
||||
count = countInstanceOf( eTYPE_SQUID, true);
|
||||
max = MobCategory::maxSquidsWithSpawnEgg();
|
||||
max = MobCategory::MAX_XBOX_SQUIDS_WITH_SPAWN_EGG;
|
||||
break;
|
||||
case eTYPE_SNOWMAN:
|
||||
count = countInstanceOf( eTYPE_SNOWMAN, true);
|
||||
@@ -4729,18 +4729,18 @@ bool Level::canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType)
|
||||
if((type & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK)
|
||||
{
|
||||
count = countInstanceOf( eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false);
|
||||
max = MobCategory::maxAnimalsWithSpawnEgg();
|
||||
max = MobCategory::MAX_XBOX_ANIMALS_WITH_SPAWN_EGG;
|
||||
}
|
||||
// 4J: Use eTYPE_ENEMY instead of monster (slimes and ghasts aren't monsters)
|
||||
else if(Entity::instanceof(type, eTYPE_ENEMY))
|
||||
{
|
||||
count = countInstanceOf(eTYPE_ENEMY, false);
|
||||
max = MobCategory::maxMonstersWithSpawnEgg();
|
||||
max = MobCategory::MAX_XBOX_MONSTERS_WITH_SPAWN_EGG;
|
||||
}
|
||||
else if( (type & eTYPE_AMBIENT) == eTYPE_AMBIENT)
|
||||
{
|
||||
count = countInstanceOf( eTYPE_AMBIENT, false);
|
||||
max = MobCategory::maxAmbientWithSpawnEgg();
|
||||
max = MobCategory::MAX_AMBIENT_WITH_SPAWN_EGG;
|
||||
}
|
||||
// 4J: Added minecart and boats
|
||||
else if (Entity::instanceof(type, eTYPE_MINECART))
|
||||
@@ -4765,21 +4765,21 @@ bool Level::canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType)
|
||||
break;
|
||||
case eTYPE_CHICKEN:
|
||||
count = countInstanceOf( eTYPE_CHICKEN, true);
|
||||
max = MobCategory::maxChickensWithBreeding();
|
||||
max = MobCategory::MAX_XBOX_CHICKENS_WITH_BREEDING;
|
||||
break;
|
||||
case eTYPE_WOLF:
|
||||
count = countInstanceOf( eTYPE_WOLF, true);
|
||||
max = MobCategory::maxWolvesWithBreeding();
|
||||
max = MobCategory::MAX_XBOX_WOLVES_WITH_BREEDING;
|
||||
break;
|
||||
case eTYPE_MUSHROOMCOW:
|
||||
count = countInstanceOf( eTYPE_MUSHROOMCOW, true);
|
||||
max = MobCategory::maxMushroomCowsWithBreeding();
|
||||
max = MobCategory::MAX_XBOX_MUSHROOMCOWS_WITH_BREEDING;
|
||||
break;
|
||||
default:
|
||||
if((type & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK)
|
||||
{
|
||||
count = countInstanceOf( eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false);
|
||||
max = MobCategory::maxAnimalsWithBreeding();
|
||||
max = MobCategory::MAX_XBOX_ANIMALS_WITH_BREEDING;
|
||||
}
|
||||
else if( (type & eTYPE_MONSTER) == eTYPE_MONSTER)
|
||||
{
|
||||
|
||||
@@ -874,12 +874,7 @@ bool LivingEntity::hurt(DamageSource *source, float dmg)
|
||||
|
||||
if (sound)
|
||||
{
|
||||
if (source->isCritical()) {
|
||||
level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT_CRITICAL);
|
||||
}
|
||||
else {
|
||||
level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT);
|
||||
}
|
||||
level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT);
|
||||
if (source != DamageSource::drown) markHurt();
|
||||
if (sourceEntity != nullptr)
|
||||
{
|
||||
@@ -902,19 +897,12 @@ bool LivingEntity::hurt(DamageSource *source, float dmg)
|
||||
MemSect(31);
|
||||
if (getHealth() <= 0)
|
||||
{
|
||||
if (sound) {
|
||||
//New: both death AND hurt sounds should play critical sound as well.
|
||||
if (source->isCritical()) playSound(getCriticalSound(), getSoundVolume(), getVoicePitch());
|
||||
playSound(getDeathSound(), getSoundVolume(), getVoicePitch());
|
||||
};
|
||||
if (sound) playSound(getDeathSound(), getSoundVolume(), getVoicePitch());
|
||||
die(source);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sound) {
|
||||
if (source->isCritical()) playSound(getCriticalSound(), getSoundVolume(), getVoicePitch());
|
||||
playSound(getHurtSound(), getSoundVolume(), getVoicePitch());
|
||||
}
|
||||
if (sound) playSound(getHurtSound(), getSoundVolume(), getVoicePitch());
|
||||
}
|
||||
MemSect(0);
|
||||
|
||||
@@ -993,11 +981,7 @@ void LivingEntity::die(DamageSource *source)
|
||||
}
|
||||
}
|
||||
|
||||
if (source->isCritical()) {
|
||||
level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH_CRITICAL);
|
||||
} else {
|
||||
level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH);
|
||||
}
|
||||
level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH);
|
||||
}
|
||||
|
||||
void LivingEntity::dropEquipment(bool byPlayer, int playerBonusLevel)
|
||||
@@ -1030,10 +1014,7 @@ int LivingEntity::getHurtSound()
|
||||
{
|
||||
return eSoundType_DAMAGE_HURT;
|
||||
}
|
||||
int LivingEntity::getCriticalSound()
|
||||
{
|
||||
return eSoundType_DAMAGE_CRITICAL;
|
||||
}
|
||||
|
||||
int LivingEntity::getDeathSound()
|
||||
{
|
||||
return eSoundType_DAMAGE_HURT;
|
||||
@@ -1255,8 +1236,7 @@ void LivingEntity::swing()
|
||||
|
||||
void LivingEntity::handleEntityEvent(byte id)
|
||||
{
|
||||
//These gotta be in parentheses
|
||||
if ((id == EntityEvent::HURT) || (id == EntityEvent::HURT_CRITICAL))
|
||||
if (id == EntityEvent::HURT)
|
||||
{
|
||||
walkAnimSpeed = 1.5f;
|
||||
|
||||
@@ -1266,30 +1246,19 @@ void LivingEntity::handleEntityEvent(byte id)
|
||||
|
||||
MemSect(31);
|
||||
// 4J-PB -added because villagers have no sounds
|
||||
int iHurtSound = getHurtSound();
|
||||
int iCritSound = getCriticalSound();
|
||||
int iHurtSound=getHurtSound();
|
||||
if(iHurtSound!=-1)
|
||||
{
|
||||
playSound(iHurtSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
|
||||
}
|
||||
if(iCritSound!=-1 && (id == EntityEvent::HURT_CRITICAL))
|
||||
{
|
||||
playSound(iCritSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
|
||||
}
|
||||
MemSect(0);
|
||||
hurt(DamageSource::genericSource, 0);
|
||||
}
|
||||
else if ((id == EntityEvent::DEATH) || (id == EntityEvent::DEATH_CRITICAL))
|
||||
else if (id == EntityEvent::DEATH)
|
||||
{
|
||||
MemSect(31);
|
||||
// 4J-PB -added because villagers have no sounds
|
||||
int iDeathSound=getDeathSound();
|
||||
int iCritSound = getCriticalSound();
|
||||
|
||||
if (iCritSound != -1 && (id == EntityEvent::DEATH_CRITICAL))
|
||||
{
|
||||
playSound(iCritSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
|
||||
}
|
||||
if(iDeathSound!=-1)
|
||||
{
|
||||
playSound(iDeathSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
|
||||
|
||||
@@ -190,7 +190,6 @@ public:
|
||||
virtual void knockback(shared_ptr<Entity> source, float dmg, double xd, double zd);
|
||||
|
||||
protected:
|
||||
virtual int getCriticalSound();
|
||||
virtual int getHurtSound();
|
||||
virtual int getDeathSound();
|
||||
|
||||
|
||||
@@ -58,24 +58,6 @@ int MobCategory::getMaxInstancesPerLevel() // 4J added
|
||||
return m_maxPerLevel;
|
||||
}
|
||||
|
||||
void MobCategory::setMaxInstancesPerLevel(int max)
|
||||
{
|
||||
m_maxPerLevel = max;
|
||||
}
|
||||
|
||||
int MobCategory::maxAnimalsWithBreeding() { return creature->getMaxInstancesPerLevel() + 20; }
|
||||
int MobCategory::maxChickensWithBreeding() { return creature_chicken->getMaxInstancesPerLevel() + 8; }
|
||||
int MobCategory::maxMushroomCowsWithBreeding() { return creature_mushroomcow->getMaxInstancesPerLevel() + 20; }
|
||||
int MobCategory::maxWolvesWithBreeding() { return creature_wolf->getMaxInstancesPerLevel() + 8; }
|
||||
|
||||
int MobCategory::maxAnimalsWithSpawnEgg() { return maxAnimalsWithBreeding() + 20; }
|
||||
int MobCategory::maxChickensWithSpawnEgg() { return maxChickensWithBreeding() + 10; }
|
||||
int MobCategory::maxWolvesWithSpawnEgg() { return maxWolvesWithBreeding() + 10; }
|
||||
int MobCategory::maxMonstersWithSpawnEgg() { return monster->getMaxInstancesPerLevel() + 20; }
|
||||
int MobCategory::maxMushroomCowsWithSpawnEgg() { return maxMushroomCowsWithBreeding() + 8; }
|
||||
int MobCategory::maxSquidsWithSpawnEgg() { return waterCreature->getMaxInstancesPerLevel() + 8; }
|
||||
int MobCategory::maxAmbientWithSpawnEgg() { return ambient->getMaxInstancesPerLevel() + 8; }
|
||||
|
||||
Material *MobCategory::getSpawnPositionMaterial()
|
||||
{
|
||||
return (Material *) spawnPositionMaterial;
|
||||
|
||||
@@ -19,25 +19,20 @@ public:
|
||||
static const int CONSOLE_SQUID_HARD_LIMIT = 5;
|
||||
static const int MAX_CONSOLE_BOSS = 1; // Max number of bosses (enderdragon/wither)
|
||||
|
||||
// 4J Villager breeding/egg limits - villagers are not a MobCategory so these stay hardcoded
|
||||
static const int MAX_XBOX_ANIMALS_WITH_BREEDING = CONSOLE_ANIMALS_HARD_LIMIT + 20; // Max number of animals that we can produce (in total), when breeding
|
||||
static const int MAX_XBOX_CHICKENS_WITH_BREEDING = MAX_XBOX_CHICKENS + 8; // Max number of chickens that we can produce (in total), when breeding/hatching
|
||||
static const int MAX_XBOX_MUSHROOMCOWS_WITH_BREEDING = MAX_XBOX_MUSHROOMCOWS + 20; // Max number of mushroom cows that we can produce (in total), when breeding
|
||||
static const int MAX_XBOX_WOLVES_WITH_BREEDING = MAX_XBOX_WOLVES + 8; // Max number of wolves that we can produce (in total), when breeding
|
||||
static const int MAX_VILLAGERS_WITH_BREEDING = 35;
|
||||
static const int MAX_XBOX_VILLAGERS_WITH_SPAWN_EGG = MAX_VILLAGERS_WITH_BREEDING + 15;
|
||||
|
||||
// Breeding headroom above the natural spawn cap. Read at call time so these
|
||||
// respect max-* overrides from server.properties.
|
||||
static int maxAnimalsWithBreeding();
|
||||
static int maxChickensWithBreeding();
|
||||
static int maxMushroomCowsWithBreeding();
|
||||
static int maxWolvesWithBreeding();
|
||||
|
||||
// Spawn-egg headroom above the natural (or breeding) cap.
|
||||
static int maxAnimalsWithSpawnEgg();
|
||||
static int maxChickensWithSpawnEgg();
|
||||
static int maxWolvesWithSpawnEgg();
|
||||
static int maxMonstersWithSpawnEgg();
|
||||
static int maxMushroomCowsWithSpawnEgg();
|
||||
static int maxSquidsWithSpawnEgg();
|
||||
static int maxAmbientWithSpawnEgg();
|
||||
static const int MAX_XBOX_ANIMALS_WITH_SPAWN_EGG = MAX_XBOX_ANIMALS_WITH_BREEDING + 20;
|
||||
static const int MAX_XBOX_CHICKENS_WITH_SPAWN_EGG = MAX_XBOX_CHICKENS_WITH_BREEDING + 10;
|
||||
static const int MAX_XBOX_WOLVES_WITH_SPAWN_EGG = MAX_XBOX_WOLVES_WITH_BREEDING + 10;
|
||||
static const int MAX_XBOX_MONSTERS_WITH_SPAWN_EGG = CONSOLE_MONSTERS_HARD_LIMIT + 20;
|
||||
static const int MAX_XBOX_VILLAGERS_WITH_SPAWN_EGG = MAX_VILLAGERS_WITH_BREEDING + 15; // 4J-PB - increased this limit due to player requests
|
||||
static const int MAX_XBOX_MUSHROOMCOWS_WITH_SPAWN_EGG = MAX_XBOX_MUSHROOMCOWS_WITH_BREEDING + 8;
|
||||
static const int MAX_XBOX_SQUIDS_WITH_SPAWN_EGG = CONSOLE_SQUID_HARD_LIMIT + 8;
|
||||
static const int MAX_AMBIENT_WITH_SPAWN_EGG = CONSOLE_AMBIENT_HARD_LIMIT + 8;
|
||||
|
||||
/*
|
||||
Maximum animals = 50 + 20 + 20 = 90
|
||||
@@ -70,7 +65,7 @@ public:
|
||||
|
||||
private:
|
||||
const int m_max;
|
||||
int m_maxPerLevel;
|
||||
const int m_maxPerLevel;
|
||||
const Material *spawnPositionMaterial;
|
||||
const bool m_isFriendly;
|
||||
const bool m_isPersistent;
|
||||
@@ -84,7 +79,6 @@ public:
|
||||
const eINSTANCEOF getEnumBaseClass(); // 4J added
|
||||
int getMaxInstancesPerChunk();
|
||||
int getMaxInstancesPerLevel(); // 4J added
|
||||
void setMaxInstancesPerLevel(int max); // 4J added
|
||||
Material *getSpawnPositionMaterial();
|
||||
bool isFriendly();
|
||||
bool isSingleType();
|
||||
|
||||
@@ -35,7 +35,7 @@ void MoveEntityPacket::read(DataInputStream *dis) //throws IOException
|
||||
|
||||
void MoveEntityPacket::write(DataOutputStream *dos) //throws IOException
|
||||
{
|
||||
if( (id < 0 ) || (id >= 16384 ) )
|
||||
if( (id < 0 ) || (id >= 2048 ) )
|
||||
{
|
||||
// We shouln't be tracking an entity that doesn't have a short type of id
|
||||
__debugbreak();
|
||||
|
||||
@@ -19,7 +19,7 @@ MoveEntityPacketSmall::MoveEntityPacketSmall()
|
||||
|
||||
MoveEntityPacketSmall::MoveEntityPacketSmall(int id)
|
||||
{
|
||||
if( (id < 0 ) || (id >= 16384 ) )
|
||||
if( (id < 0 ) || (id >= 2048 ) )
|
||||
{
|
||||
// We shouln't be tracking an entity that doesn't have a short type of id
|
||||
__debugbreak();
|
||||
@@ -42,7 +42,7 @@ void MoveEntityPacketSmall::read(DataInputStream *dis) //throws IOException
|
||||
|
||||
void MoveEntityPacketSmall::write(DataOutputStream *dos) //throws IOException
|
||||
{
|
||||
if( (id < 0 ) || (id >= 16384 ) )
|
||||
if( (id < 0 ) || (id >= 2048 ) )
|
||||
{
|
||||
// We shouln't be tracking an entity that doesn't have a short type of id
|
||||
__debugbreak();
|
||||
@@ -99,7 +99,7 @@ void MoveEntityPacketSmall::PosRot::read(DataInputStream *dis) //throws IOExcept
|
||||
|
||||
void MoveEntityPacketSmall::PosRot::write(DataOutputStream *dos) //throws IOException
|
||||
{
|
||||
if( (id < 0 ) || (id >= 16384 ) )
|
||||
if( (id < 0 ) || (id >= 2048 ) )
|
||||
{
|
||||
// We shouln't be tracking an entity that doesn't have a short type of id
|
||||
__debugbreak();
|
||||
@@ -138,7 +138,7 @@ void MoveEntityPacketSmall::Pos::read(DataInputStream *dis) //throws IOException
|
||||
|
||||
void MoveEntityPacketSmall::Pos::write(DataOutputStream *dos) //throws IOException
|
||||
{
|
||||
if( (id < 0 ) || (id >= 16384 ) )
|
||||
if( (id < 0 ) || (id >= 2048 ) )
|
||||
{
|
||||
// We shouln't be tracking an entity that doesn't have a short type of id
|
||||
__debugbreak();
|
||||
@@ -176,7 +176,7 @@ void MoveEntityPacketSmall::Rot::read(DataInputStream *dis) //throws IOException
|
||||
|
||||
void MoveEntityPacketSmall::Rot::write(DataOutputStream *dos) //throws IOException
|
||||
{
|
||||
if( (id < 0 ) || (id >= 16384 ) )
|
||||
if( (id < 0 ) || (id >= 2048 ) )
|
||||
{
|
||||
// We shouln't be tracking an entity that doesn't have a short type of id
|
||||
__debugbreak();
|
||||
|
||||
@@ -1631,10 +1631,6 @@ void Player::attack(shared_ptr<Entity> entity)
|
||||
}
|
||||
|
||||
DamageSource *damageSource = DamageSource::playerAttack(dynamic_pointer_cast<Player>(shared_from_this()));
|
||||
|
||||
if (bCrit) {
|
||||
damageSource->setIsCritical();
|
||||
}
|
||||
bool wasHurt = entity->hurt(damageSource, dmg);
|
||||
delete damageSource;
|
||||
if (wasHurt)
|
||||
|
||||
@@ -20,8 +20,7 @@ class SharedConstants
|
||||
static wstring readAcceptableChars();
|
||||
|
||||
public:
|
||||
static const int maxChatLength = 255;
|
||||
static const int maxVisibleLength = 100; //to be changed
|
||||
static const int maxChatLength = 100;
|
||||
static wstring acceptableLetters;
|
||||
|
||||
static const int ILLEGAL_FILE_CHARACTERS_LENGTH = 15;
|
||||
|
||||
@@ -493,10 +493,6 @@ void Socket::SocketOutputStreamNetwork::writeWithFlags(byteArray b, unsigned int
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't write on a closing socket: an orphan whose smallId has been
|
||||
// recycled would otherwise leak a packet onto the new client.
|
||||
if( m_socket->isClosing() ) return;
|
||||
|
||||
XRNM_SEND_BUFFER buffer;
|
||||
buffer.pbyData = &b[offset];
|
||||
buffer.dwDataSize = length;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user