Merge remote-tracking branch 'upstream/main' into linux-clang-support

# Conflicts:
#	Minecraft.Client/Common/UI/UIScene_ConnectingProgress.cpp
This commit is contained in:
ryleu 2026-03-30 15:57:13 -05:00
commit 7dd1e75b33
No known key found for this signature in database
21 changed files with 653 additions and 49 deletions

48
.github/workflows/clang-format.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name: Check formatting
on:
pull_request:
paths:
- '**'
- '!.gitignore'
- '!*.md'
- '!.github/**'
- '.github/workflows/clang-format.yml'
permissions:
contents: read
pull-requests: write
jobs:
format-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Fetch base commit
run: git fetch origin ${{ github.event.pull_request.base.sha }}
- name: Install clang-format-20
run: |
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
sudo add-apt-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main"
sudo apt-get install -y -qq clang-format-20
- uses: reviewdog/action-setup@v1
- name: Check formatting on changed lines
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git clang-format-20 --binary clang-format-20 \
--diff ${{ github.event.pull_request.base.sha }} -- \
'*.c' '*.cpp' '*.cc' '*.h' '*.hpp' \
| reviewdog \
-name="clang-format" \
-f=diff \
-reporter=github-pr-check \
-fail-level=error \
-filter-mode=added

View file

@ -5,11 +5,12 @@ on:
push:
branches:
- 'main'
paths-ignore:
- '.gitignore'
- '*.md'
- '.github/**'
- '!.github/workflows/nightly-server.yml'
paths:
- '**'
- '!.gitignore'
- '!*.md'
- '!.github/**'
- '.github/workflows/nightly-server.yml'
permissions:
contents: write

View file

@ -5,11 +5,12 @@ on:
push:
branches:
- 'main'
paths-ignore:
- '.gitignore'
- '*.md'
- '.github/**'
- '!.github/workflows/nightly.yml'
paths:
- '**'
- '!.gitignore'
- '!*.md'
- '!.github/**'
- '.github/workflows/nightly.yml'
permissions:
contents: write

View file

@ -4,10 +4,12 @@ on:
workflow_dispatch:
pull_request:
types: [opened, reopened, synchronize]
paths-ignore:
- '.gitignore'
- '*.md'
- '.github/*.md'
paths:
- '**'
- '!.gitignore'
- '!*.md'
- '!.github/**'
- '.github/workflows/pull-request.yml'
jobs:
build:

View file

@ -10,6 +10,7 @@
const WCHAR *DLCManager::wchTypeNamesA[]=
{
L"XMLVERSION",
L"DISPLAYNAME",
L"THEMENAME",
L"FREE",
@ -387,41 +388,65 @@ bool DLCManager::processDLCDataFile(DWORD &dwFilesProcessed, PBYTE pbData, DWORD
// // unsigned long, p = number of parameters
// // p * DLC_FILE_PARAM describing each parameter for this file
// // ulFileSize bytes of data blob of the file added
unsigned int uiVersion=*(unsigned int *)pbData;
unsigned int uiVersion=readUInt32(pbData, false);
uiCurrentByte+=sizeof(int);
if(uiVersion < CURRENT_DLC_VERSION_NUM)
{
if(pbData!=nullptr) delete [] pbData;
app.DebugPrintf("DLC version of %d is too old to be read\n", uiVersion);
bool bSwapEndian = false;
unsigned int uiVersionSwapped = SwapInt32(uiVersion);
if (uiVersion >= 0 && uiVersion <= CURRENT_DLC_VERSION_NUM) {
bSwapEndian = false;
} else if (uiVersionSwapped >= 0 && uiVersionSwapped <= CURRENT_DLC_VERSION_NUM) {
bSwapEndian = true;
} else {
if(pbData!=nullptr) delete [] pbData;
app.DebugPrintf("Unknown DLC version of %d\n", uiVersion);
return false;
}
pack->SetDataPointer(pbData);
unsigned int uiParameterCount=*(unsigned int *)&pbData[uiCurrentByte];
unsigned int uiParameterCount=readUInt32(&pbData[uiCurrentByte], bSwapEndian);
uiCurrentByte+=sizeof(int);
C4JStorage::DLC_FILE_PARAM *pParams = (C4JStorage::DLC_FILE_PARAM *)&pbData[uiCurrentByte];
bool bXMLVersion = false;
//DWORD dwwchCount=0;
for(unsigned int i=0;i<uiParameterCount;i++)
{
pParams->dwType = bSwapEndian ? SwapInt32(pParams->dwType) : pParams->dwType;
pParams->dwWchCount = bSwapEndian ? SwapInt32(pParams->dwWchCount) : pParams->dwWchCount;
char16_t* wchData = reinterpret_cast<char16_t*>(pParams->wchData);
if (bSwapEndian) {
SwapUTF16Bytes(wchData, pParams->dwWchCount);
}
// Map DLC strings to application strings, then store the DLC index mapping to application index
wstring parameterName(static_cast<WCHAR *>(pParams->wchData));
EDLCParameterType type = getParameterType(parameterName);
if( type != e_DLCParamType_Invalid )
{
parameterMapping[pParams->dwType] = type;
if (type == e_DLCParamType_XMLVersion)
{
bXMLVersion = true;
}
}
uiCurrentByte+= sizeof(C4JStorage::DLC_FILE_PARAM)+(pParams->dwWchCount*sizeof(WCHAR));
pParams = (C4JStorage::DLC_FILE_PARAM *)&pbData[uiCurrentByte];
}
//ulCurrentByte+=ulParameterCount * sizeof(C4JStorage::DLC_FILE_PARAM);
unsigned int uiFileCount=*(unsigned int *)&pbData[uiCurrentByte];
if (bXMLVersion)
{
uiCurrentByte += sizeof(int);
}
unsigned int uiFileCount=readUInt32(&pbData[uiCurrentByte], bSwapEndian);
uiCurrentByte+=sizeof(int);
C4JStorage::DLC_FILE_DETAILS *pFile = (C4JStorage::DLC_FILE_DETAILS *)&pbData[uiCurrentByte];
DWORD dwTemp=uiCurrentByte;
for(unsigned int i=0;i<uiFileCount;i++)
{
pFile->dwWchCount = bSwapEndian ? SwapInt32(pFile->dwWchCount) : pFile->dwWchCount;
dwTemp+=sizeof(C4JStorage::DLC_FILE_DETAILS)+pFile->dwWchCount*sizeof(WCHAR);
pFile = (C4JStorage::DLC_FILE_DETAILS *)&pbData[dwTemp];
}
@ -430,6 +455,13 @@ bool DLCManager::processDLCDataFile(DWORD &dwFilesProcessed, PBYTE pbData, DWORD
for(unsigned int i=0;i<uiFileCount;i++)
{
pFile->dwType = bSwapEndian ? SwapInt32(pFile->dwType) : pFile->dwType;
pFile->uiFileSize = bSwapEndian ? SwapInt32(pFile->uiFileSize) : pFile->uiFileSize;
char16_t* wchFile = reinterpret_cast<char16_t*>(pFile->wchFile);
if (bSwapEndian) {
SwapUTF16Bytes(wchFile, pFile->dwWchCount);
}
EDLCType type = static_cast<EDLCType>(pFile->dwType);
DLCFile *dlcFile = nullptr;
@ -445,12 +477,18 @@ bool DLCManager::processDLCDataFile(DWORD &dwFilesProcessed, PBYTE pbData, DWORD
}
// Params
uiParameterCount=*(unsigned int *)pbTemp;
uiParameterCount=readUInt32(pbTemp, bSwapEndian);
pbTemp+=sizeof(int);
pParams = (C4JStorage::DLC_FILE_PARAM *)pbTemp;
for(unsigned int j=0;j<uiParameterCount;j++)
{
//DLCManager::EDLCParameterType paramType = DLCManager::e_DLCParamType_Invalid;
pParams->dwType = bSwapEndian ? SwapInt32(pParams->dwType) : pParams->dwType;
pParams->dwWchCount = bSwapEndian ? SwapInt32(pParams->dwWchCount) : pParams->dwWchCount;
char16_t* wchData = reinterpret_cast<char16_t*>(pParams->wchData);
if (bSwapEndian) {
SwapUTF16Bytes(wchData, pParams->dwWchCount);
}
auto it = parameterMapping.find(pParams->dwType);

View file

@ -31,7 +31,8 @@ public:
{
e_DLCParamType_Invalid = -1,
e_DLCParamType_DisplayName = 0,
e_DLCParamType_XMLVersion = 0,
e_DLCParamType_DisplayName,
e_DLCParamType_ThemeName,
e_DLCParamType_Free, // identify free skins
e_DLCParamType_Credit, // legal credits for DLC
@ -94,6 +95,30 @@ public:
bool readDLCDataFile(DWORD &dwFilesProcessed, const string &path, DLCPack *pack, bool fromArchive = false);
DWORD retrievePackIDFromDLCDataFile(const string &path, DLCPack *pack);
static unsigned short SwapInt16(unsigned short value) {
return (value >> 8) | (value << 8);
}
static unsigned int SwapInt32(unsigned int value) {
return ((value & 0xFF) << 24) |
((value & 0xFF00) << 8) |
((value & 0xFF0000) >> 8) |
((value & 0xFF000000) >> 24);
}
static void SwapUTF16Bytes(char16_t* buffer, size_t count) {
for (size_t i = 0; i < count; ++i) {
char16_t& c = buffer[i];
c = (c >> 8) | (c << 8);
}
}
static unsigned int readUInt32(unsigned char* ptr, bool endian) {
unsigned int val = *(unsigned int*)ptr;
if (endian) val = SwapInt32(val);
return val;
}
private:
bool processDLCDataFile(DWORD &dwFilesProcessed, PBYTE pbData, DWORD dwLength, DLCPack *pack);

View file

@ -47,7 +47,8 @@ public:
{
JOINGAME_SUCCESS,
JOINGAME_FAIL_GENERAL,
JOINGAME_FAIL_SERVER_FULL
JOINGAME_FAIL_SERVER_FULL,
JOINGAME_PENDING
} eJoinGameResult;
void Initialise();

View file

@ -173,6 +173,11 @@ bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkMa
m_bSearchPending = false;
m_bIsOfflineGame = false;
#ifdef _WINDOWS64
m_bJoinPending = false;
m_joinLocalUsersMask = 0;
m_joinHostName[0] = 0;
#endif
m_pSearchParam = nullptr;
m_SessionsUpdatedCallback = nullptr;
@ -282,6 +287,38 @@ void CPlatformNetworkManagerStub::DoWork()
m_bLeaveGameOnTick = false;
}
}
if (m_bJoinPending)
{
WinsockNetLayer::eJoinState state = WinsockNetLayer::GetJoinState();
if (state == WinsockNetLayer::eJoinState_Success)
{
WinsockNetLayer::FinalizeJoin();
BYTE localSmallId = WinsockNetLayer::GetLocalSmallId();
IQNet::m_player[localSmallId].m_smallId = localSmallId;
IQNet::m_player[localSmallId].m_isRemote = false;
IQNet::m_player[localSmallId].m_isHostPlayer = false;
IQNet::m_player[localSmallId].m_resolvedXuid = Win64Xuid::ResolvePersistentXuid();
Minecraft* pMinecraft = Minecraft::GetInstance();
wcscpy_s(IQNet::m_player[localSmallId].m_gamertag, 32, pMinecraft->user->name.c_str());
IQNet::s_playerCount = localSmallId + 1;
NotifyPlayerJoined(&IQNet::m_player[0]);
NotifyPlayerJoined(&IQNet::m_player[localSmallId]);
m_pGameNetworkManager->StateChange_AnyToStarting();
m_bJoinPending = false;
}
else if (state == WinsockNetLayer::eJoinState_Failed ||
state == WinsockNetLayer::eJoinState_Rejected ||
state == WinsockNetLayer::eJoinState_Cancelled)
{
m_bJoinPending = false;
}
}
#endif
}
@ -511,36 +548,22 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
IQNet::m_player[0].m_smallId = 0;
IQNet::m_player[0].m_isRemote = true;
IQNet::m_player[0].m_isHostPlayer = true;
// Remote host still maps to legacy host XUID in mixed old/new sessions.
IQNet::m_player[0].m_resolvedXuid = Win64Xuid::GetLegacyEmbeddedHostXuid();
wcsncpy_s(IQNet::m_player[0].m_gamertag, 32, searchResult->data.hostName, _TRUNCATE);
WinsockNetLayer::StopDiscovery();
if (!WinsockNetLayer::JoinGame(hostIP, hostPort))
wcsncpy_s(m_joinHostName, 32, searchResult->data.hostName, _TRUNCATE);
m_joinLocalUsersMask = localUsersMask;
if (!WinsockNetLayer::BeginJoinGame(hostIP, hostPort))
{
app.DebugPrintf("Win64 LAN: Failed to connect to %s:%d\n", hostIP, hostPort);
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
}
BYTE localSmallId = WinsockNetLayer::GetLocalSmallId();
IQNet::m_player[localSmallId].m_smallId = localSmallId;
IQNet::m_player[localSmallId].m_isRemote = false;
IQNet::m_player[localSmallId].m_isHostPlayer = false;
// Local non-host identity is the persistent uid.dat XUID.
IQNet::m_player[localSmallId].m_resolvedXuid = Win64Xuid::ResolvePersistentXuid();
Minecraft* pMinecraft = Minecraft::GetInstance();
wcscpy_s(IQNet::m_player[localSmallId].m_gamertag, 32, pMinecraft->user->name.c_str());
IQNet::s_playerCount = localSmallId + 1;
NotifyPlayerJoined(&IQNet::m_player[0]);
NotifyPlayerJoined(&IQNet::m_player[localSmallId]);
m_pGameNetworkManager->StateChange_AnyToStarting();
return CGameNetworkManager::JOINGAME_SUCCESS;
m_bJoinPending = true;
return CGameNetworkManager::JOINGAME_PENDING;
#else
return CGameNetworkManager::JOINGAME_SUCCESS;
#endif

View file

@ -77,6 +77,12 @@ private:
bool m_bIsPrivateGame;
int m_flagIndexSize;
#ifdef _WINDOWS64
bool m_bJoinPending;
int m_joinLocalUsersMask;
wchar_t m_joinHostName[32];
#endif
// This is only maintained by the host, and is not valid on client machines
GameSessionData m_hostGameSessionData;
CGameNetworkManager *m_pGameNetworkManager;

View file

@ -2,6 +2,16 @@
#include "UI.h"
#include "UIScene_ConnectingProgress.h"
#include "../../Minecraft.h"
#ifdef _WINDOWS64
#include "../../Windows64/Network/WinsockNetLayer.h"
#include "../../../Minecraft.World/DisconnectPacket.h"
static int ConnectingProgress_OnRejectedDialogOK(LPVOID, int iPad, const C4JStorage::EMessageResult)
{
ui.NavigateBack(iPad);
return 0;
}
#endif
UIScene_ConnectingProgress::UIScene_ConnectingProgress(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
{
@ -43,6 +53,12 @@ UIScene_ConnectingProgress::UIScene_ConnectingProgress(int iPad, void *_initData
m_cancelFuncParam = param->cancelFuncParam;
m_removeLocalPlayer = false;
m_showingButton = false;
#ifdef _WINDOWS64
WinsockNetLayer::eJoinState initState = WinsockNetLayer::GetJoinState();
m_asyncJoinActive = (initState != WinsockNetLayer::eJoinState_Idle && initState != WinsockNetLayer::eJoinState_Cancelled);
m_asyncJoinFailed = false;
#endif
}
UIScene_ConnectingProgress::~UIScene_ConnectingProgress()
@ -53,6 +69,18 @@ UIScene_ConnectingProgress::~UIScene_ConnectingProgress()
void UIScene_ConnectingProgress::updateTooltips()
{
#ifdef _WINDOWS64
if (m_asyncJoinActive)
{
ui.SetTooltips( m_iPad, -1, IDS_TOOLTIPS_BACK);
return;
}
if (m_asyncJoinFailed)
{
ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT, -1);
return;
}
#endif
// 4J-PB - removing the option of cancel join, since it didn't work anyway
//ui.SetTooltips( m_iPad, -1, m_showTooltips?IDS_TOOLTIPS_CANCEL_JOIN:-1);
ui.SetTooltips( m_iPad, -1, -1);
@ -62,6 +90,85 @@ void UIScene_ConnectingProgress::tick()
{
UIScene::tick();
#ifdef _WINDOWS64
if (m_asyncJoinActive)
{
WinsockNetLayer::eJoinState state = WinsockNetLayer::GetJoinState();
if (state == WinsockNetLayer::eJoinState_Connecting)
{
// connecting.............
int attempt = WinsockNetLayer::GetJoinAttempt();
int maxAttempts = WinsockNetLayer::GetJoinMaxAttempts();
char buf[128];
if (attempt <= 1)
sprintf_s(buf, "Connecting...");
else
sprintf_s(buf, "Connecting failed, trying again (%d/%d)", attempt, maxAttempts);
wchar_t wbuf[128];
mbstowcs(wbuf, buf, 128);
m_labelTitle.setLabel(wstring(wbuf));
}
else if (state == WinsockNetLayer::eJoinState_Success)
{
m_asyncJoinActive = false;
// go go go
}
else if (state == WinsockNetLayer::eJoinState_Cancelled)
{
// cancel
m_asyncJoinActive = false;
navigateBack();
}
else if (state == WinsockNetLayer::eJoinState_Rejected)
{
// server full and banned are passed differently compared to other disconnects it seems
m_asyncJoinActive = false;
DisconnectPacket::eDisconnectReason reason = WinsockNetLayer::GetJoinRejectReason();
int exitReasonStringId;
switch (reason)
{
case DisconnectPacket::eDisconnect_ServerFull:
exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL;
break;
case DisconnectPacket::eDisconnect_Banned:
exitReasonStringId = IDS_DISCONNECTED_KICKED;
break;
default:
exitReasonStringId = IDS_CONNECTION_LOST_SERVER;
break;
}
UINT uiIDA[1];
uiIDA[0] = IDS_CONFIRM_OK;
ui.RequestErrorMessage(IDS_CONNECTION_FAILED, exitReasonStringId, uiIDA, 1, ProfileManager.GetPrimaryPad(), ConnectingProgress_OnRejectedDialogOK, nullptr, nullptr);
}
else if (state == WinsockNetLayer::eJoinState_Failed)
{
// FAIL
m_asyncJoinActive = false;
m_asyncJoinFailed = true;
int maxAttempts = WinsockNetLayer::GetJoinMaxAttempts();
char buf[256];
sprintf_s(buf, "Failed to connect after %d attempts. The server may be unavailable.", maxAttempts);
wchar_t wbuf[256];
mbstowcs(wbuf, buf, 256);
// TIL that these exist
// not going to use a actual popup due to it requiring messing with strings which can really mess things up
// i dont trust myself with that
// these need to be touched up later as teh button is a bit offset
m_labelTitle.setLabel(L"Unable to connect to server");
m_progressBar.setLabel(wstring(wbuf));
m_progressBar.showBar(false);
m_progressBar.setVisible(true);
m_buttonConfirm.setVisible(true);
m_showingButton = true;
m_controlTimer.setVisible(false);
}
return;
}
#endif
if( m_removeLocalPlayer )
{
m_removeLocalPlayer = false;
@ -94,6 +201,8 @@ void UIScene_ConnectingProgress::handleGainFocus(bool navBack)
void UIScene_ConnectingProgress::handleLoseFocus()
{
if (!m_runFailTimer) return;
int millisecsLeft = getTimer(0)->targetTime - System::currentTimeMillis();
int millisecsTaken = getTimer(0)->duration - millisecsLeft;
app.DebugPrintf("\n");
@ -208,6 +317,17 @@ void UIScene_ConnectingProgress::handleInput(int iPad, int key, bool repeat, boo
switch(key)
{
// 4J-PB - Removed the option to cancel join - it didn't work anyway
#ifdef _WINDOWS64
case ACTION_MENU_CANCEL:
if (pressed && m_asyncJoinActive)
{
m_asyncJoinActive = false;
WinsockNetLayer::CancelJoinGame();
navigateBack();
handled = true;
}
break;
#endif
// case ACTION_MENU_CANCEL:
// {
// if(m_cancelFunc != nullptr)
@ -250,6 +370,13 @@ void UIScene_ConnectingProgress::handlePress(F64 controlId, F64 childId)
case eControl_Confirm:
if(m_showingButton)
{
#ifdef _WINDOWS64
if (m_asyncJoinFailed)
{
navigateBack();
}
else
#endif
if( m_iPad != ProfileManager.GetPrimaryPad() && g_NetworkManager.IsInSession() )
{
// The connection failed if we see the button, so the temp player should be removed and the viewports updated again

View file

@ -13,6 +13,11 @@ private:
void (*m_cancelFunc)(LPVOID param);
LPVOID m_cancelFuncParam;
#ifdef _WINDOWS64
bool m_asyncJoinActive;
bool m_asyncJoinFailed;
#endif
enum EControls
{
eControl_Confirm

View file

@ -583,6 +583,24 @@ void UIScene_JoinMenu::JoinGame(UIScene_JoinMenu* pClass)
// Alert the app the we no longer want to be informed of ethernet connections
app.SetLiveLinkRequired( false );
#ifdef _WINDOWS64
if (result == CGameNetworkManager::JOINGAME_PENDING)
{
pClass->m_bIgnoreInput = false;
ConnectionProgressParams *param = new ConnectionProgressParams();
param->iPad = ProfileManager.GetPrimaryPad();
param->stringId = -1;
param->showTooltips = true;
param->setFailTimer = false;
param->timerTime = 0;
param->cancelFunc = nullptr;
param->cancelFuncParam = nullptr;
ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_ConnectingProgress, param);
return;
}
#endif
if( result != CGameNetworkManager::JOINGAME_SUCCESS )
{
int exitReasonStringId = -1;

View file

@ -557,8 +557,8 @@ int UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback(LPVOID lpParam,b
uint16_t pchText[128];
ZeroMemory(pchText, 128 * sizeof(uint16_t));
Win64_GetKeyboardText(pchText, 128);
pClass->m_editSeed.setLabel((wchar_t *)pchText);
pClass->m_params->seed = (wchar_t *)pchText;
pClass->m_editSeed.setLabel(reinterpret_cast<wchar_t*>(pchText));
pClass->m_params->seed = static_cast<wstring>(reinterpret_cast<wchar_t*>(pchText));
#else
#ifdef __PSVITA__
uint16_t pchText[2048];
@ -584,7 +584,7 @@ void UIScene_LaunchMoreOptionsMenu::getDirectEditInputs(vector<UIControl_TextInp
void UIScene_LaunchMoreOptionsMenu::onDirectEditFinished(UIControl_TextInput *input, UIControl_TextInput::EDirectEditResult result)
{
if (result == UIControl_TextInput::eDirectEdit_Confirmed)
m_params->seed = input->getEditBuffer();
m_params->seed = static_cast<wstring>(input->getEditBuffer());
}
#endif

View file

@ -1795,6 +1795,7 @@ 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);

View file

@ -519,6 +519,29 @@ void PlayerRenderer::renderHand()
{
humanoidModel->arm0->render(1 / 16.0f,true);
}
//Render custom skin boxes on viewmodel - Botch
vector<ModelPart*>* additionalModelParts = Minecraft::GetInstance()->player->GetAdditionalModelParts();
if (!additionalModelParts) return; //If there are no custom boxes, return. This fixes bug where the game will crash if you select a skin with no additional boxes.
vector<ModelPart*> armchildren = humanoidModel->arm0->children;
std::unordered_set<ModelPart*> additionalModelPartSet(additionalModelParts->begin(), additionalModelParts->end());
for (const auto& x : armchildren) {
if (x) {
if (additionalModelPartSet.find(x) != additionalModelPartSet.end()) { //This is to verify box is still actually on current skin - Botch
glPushMatrix();
//We need to transform to match offset of arm - Botch
glTranslatef(-5 * 0.0625f, 2 * 0.0625f, 0);
glRotatef(0.1 * (180.0f / PI), 0, 0, 1);
x->visible = true;
x->render(1.0f / 16.0f, true);
x->visible = false;
glPopMatrix();
}
}
}
}
void PlayerRenderer::setupPosition(shared_ptr<LivingEntity> _mob, double x, double y, double z)

View file

@ -678,7 +678,7 @@ bool ServerLevel::tickPendingTicks(bool force)
}
else
{
addToTickNextTick(td.x, td.y, td.z, td.tileId, 0);
forceAddTileTick(td.x, td.y, td.z, td.tileId, 0, td.priorityTilt);
}
}

View file

@ -653,11 +653,14 @@ shared_ptr<Packet> TrackedEntity::getAddEntityPacket()
PlayerUID xuid = INVALID_XUID;
PlayerUID OnlineXuid = INVALID_XUID;
// do not pass xuid/onlinexuid to clients if dedicated server
#ifndef MINECRAFT_SERVER_BUILD
if( player != nullptr )
{
xuid = player->getXuid();
OnlineXuid = player->getOnlineXuid();
}
#endif
// 4J Added yHeadRotp param to fix #102563 - TU12: Content: Gameplay: When one of the Players is idle for a few minutes his head turns 180 degrees.
return std::make_shared<AddPlayerPacket>(player, xuid, OnlineXuid, xp, yp, zp, yRotp, xRotp, yHeadRotp);
}

View file

@ -67,6 +67,16 @@ SOCKET WinsockNetLayer::s_splitScreenSocket[XUSER_MAX_COUNT] = { INVALID_SOCKET,
BYTE WinsockNetLayer::s_splitScreenSmallId[XUSER_MAX_COUNT] = { 0xFF, 0xFF, 0xFF, 0xFF };
HANDLE WinsockNetLayer::s_splitScreenRecvThread[XUSER_MAX_COUNT] = {nullptr, nullptr, nullptr, nullptr};
// async stuff
HANDLE WinsockNetLayer::s_joinThread = nullptr;
volatile WinsockNetLayer::eJoinState WinsockNetLayer::s_joinState = WinsockNetLayer::eJoinState_Idle;
volatile int WinsockNetLayer::s_joinAttempt = 0;
volatile bool WinsockNetLayer::s_joinCancel = false;
char WinsockNetLayer::s_joinIP[256] = {};
int WinsockNetLayer::s_joinPort = 0;
BYTE WinsockNetLayer::s_joinAssignedSmallId = 0;
DisconnectPacket::eDisconnectReason WinsockNetLayer::s_joinRejectReason = DisconnectPacket::eDisconnect_Quitting;
bool g_Win64MultiplayerHost = false;
bool g_Win64MultiplayerJoin = false;
int g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT;
@ -114,6 +124,15 @@ void WinsockNetLayer::Shutdown()
StopAdvertising();
StopDiscovery();
s_joinCancel = true;
if (s_joinThread != nullptr)
{
WaitForSingleObject(s_joinThread, 5000);
CloseHandle(s_joinThread);
s_joinThread = nullptr;
}
s_joinState = eJoinState_Idle;
s_active = false;
s_connected = false;
@ -421,6 +440,215 @@ bool WinsockNetLayer::JoinGame(const char* ip, int port)
return true;
}
bool WinsockNetLayer::BeginJoinGame(const char* ip, int port)
{
if (!s_initialized && !Initialize()) return false;
// if there isnt any cleanup it sometime caused issues. Oops
CancelJoinGame();
if (s_joinThread != nullptr)
{
WaitForSingleObject(s_joinThread, 5000);
CloseHandle(s_joinThread);
s_joinThread = nullptr;
}
s_isHost = false;
s_hostSmallId = 0;
s_connected = false;
s_active = false;
if (s_hostConnectionSocket != INVALID_SOCKET)
{
closesocket(s_hostConnectionSocket);
s_hostConnectionSocket = INVALID_SOCKET;
}
if (s_clientRecvThread != nullptr)
{
WaitForSingleObject(s_clientRecvThread, 5000);
CloseHandle(s_clientRecvThread);
s_clientRecvThread = nullptr;
}
strncpy_s(s_joinIP, sizeof(s_joinIP), ip, _TRUNCATE);
s_joinPort = port;
s_joinAttempt = 0;
s_joinCancel = false;
s_joinAssignedSmallId = 0;
s_joinRejectReason = DisconnectPacket::eDisconnect_Quitting;
s_joinState = eJoinState_Connecting;
s_joinThread = CreateThread(nullptr, 0, JoinThreadProc, nullptr, 0, nullptr);
if (s_joinThread == nullptr)
{
s_joinState = eJoinState_Failed;
return false;
}
return true;
}
DWORD WINAPI WinsockNetLayer::JoinThreadProc(LPVOID param)
{
struct addrinfo hints = {};
struct addrinfo* result = nullptr;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
char portStr[16];
sprintf_s(portStr, "%d", s_joinPort);
int iResult = getaddrinfo(s_joinIP, portStr, &hints, &result);
if (iResult != 0)
{
app.DebugPrintf("getaddrinfo failed for %s:%d - %d\n", s_joinIP, s_joinPort, iResult);
s_joinState = eJoinState_Failed;
return 0;
}
bool connected = false;
BYTE assignedSmallId = 0;
SOCKET sock = INVALID_SOCKET;
for (int attempt = 0; attempt < JOIN_MAX_ATTEMPTS; ++attempt)
{
if (s_joinCancel)
{
freeaddrinfo(result);
s_joinState = eJoinState_Cancelled;
return 0;
}
s_joinAttempt = attempt + 1;
sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock == INVALID_SOCKET)
{
app.DebugPrintf("socket() failed: %d\n", WSAGetLastError());
break;
}
int noDelay = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&noDelay, sizeof(noDelay));
iResult = connect(sock, result->ai_addr, static_cast<int>(result->ai_addrlen));
if (iResult == SOCKET_ERROR)
{
int err = WSAGetLastError();
app.DebugPrintf("connect() to %s:%d failed (attempt %d/%d): %d\n", s_joinIP, s_joinPort, attempt + 1, JOIN_MAX_ATTEMPTS, err);
closesocket(sock);
sock = INVALID_SOCKET;
for (int w = 0; w < 4 && !s_joinCancel; w++)
Sleep(50);
continue;
}
BYTE assignBuf[1];
int bytesRecv = recv(sock, (char*)assignBuf, 1, 0);
if (bytesRecv != 1)
{
app.DebugPrintf("failed to receive small id assignment from host (attempt %d/%d)\n", attempt + 1, JOIN_MAX_ATTEMPTS);
closesocket(sock);
sock = INVALID_SOCKET;
for (int w = 0; w < 4 && !s_joinCancel; w++)
Sleep(50);
continue;
}
if (assignBuf[0] == WIN64_SMALLID_REJECT)
{
BYTE rejectBuf[5];
if (!RecvExact(sock, rejectBuf, 5))
{
app.DebugPrintf("failed to receive reject reason from host (?)\n");
closesocket(sock);
sock = INVALID_SOCKET;
for (int w = 0; w < 4 && !s_joinCancel; w++)
Sleep(50);
continue;
}
int reason = ((rejectBuf[1] & 0xff) << 24) | ((rejectBuf[2] & 0xff) << 16) |
((rejectBuf[3] & 0xff) << 8) | (rejectBuf[4] & 0xff);
s_joinRejectReason = (DisconnectPacket::eDisconnectReason)reason;
closesocket(sock);
freeaddrinfo(result);
s_joinState = eJoinState_Rejected;
return 0;
}
assignedSmallId = assignBuf[0];
connected = true;
break;
}
freeaddrinfo(result);
if (s_joinCancel)
{
if (sock != INVALID_SOCKET) closesocket(sock);
s_joinState = eJoinState_Cancelled;
return 0;
}
if (!connected)
{
s_joinState = eJoinState_Failed;
return 0;
}
s_hostConnectionSocket = sock;
s_joinAssignedSmallId = assignedSmallId;
s_joinState = eJoinState_Success;
return 0;
}
void WinsockNetLayer::CancelJoinGame()
{
if (s_joinState == eJoinState_Connecting)
{
s_joinCancel = true;
}
else if (s_joinState == eJoinState_Success)
{
// fix a race cond
if (s_hostConnectionSocket != INVALID_SOCKET)
{
closesocket(s_hostConnectionSocket);
s_hostConnectionSocket = INVALID_SOCKET;
}
s_joinState = eJoinState_Cancelled;
}
}
bool WinsockNetLayer::FinalizeJoin()
{
if (s_joinState != eJoinState_Success)
return false;
s_localSmallId = s_joinAssignedSmallId;
strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), s_joinIP, _TRUNCATE);
g_Win64MultiplayerPort = s_joinPort;
app.DebugPrintf("connected to %s:%d, assigned smallId=%d\n", s_joinIP, s_joinPort, s_localSmallId);
s_active = true;
s_connected = true;
s_clientRecvThread = CreateThread(nullptr, 0, ClientRecvThreadProc, nullptr, 0, nullptr);
if (s_joinThread != nullptr)
{
WaitForSingleObject(s_joinThread, 2000);
CloseHandle(s_joinThread);
s_joinThread = nullptr;
}
s_joinState = eJoinState_Idle;
return true;
}
bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void* data, int dataSize)
{
if (sock == INVALID_SOCKET || dataSize <= 0 || dataSize > WIN64_NET_MAX_PACKET_SIZE) return false;
@ -1334,4 +1562,25 @@ DWORD WINAPI WinsockNetLayer::DiscoveryThreadProc(LPVOID param)
return 0;
}
// some lazy helper funcs
WinsockNetLayer::eJoinState WinsockNetLayer::GetJoinState()
{
return s_joinState;
}
int WinsockNetLayer::GetJoinAttempt()
{
return s_joinAttempt;
}
int WinsockNetLayer::GetJoinMaxAttempts()
{
return JOIN_MAX_ATTEMPTS;
}
DisconnectPacket::eDisconnectReason WinsockNetLayer::GetJoinRejectReason()
{
return s_joinRejectReason;
}
#endif

View file

@ -21,6 +21,8 @@
class Socket;
#include "..\..\..\Minecraft.World\DisconnectPacket.h"
#pragma pack(push, 1)
struct Win64LANBroadcast
{
@ -69,6 +71,23 @@ public:
static bool HostGame(int port, const char* bindIp = nullptr);
static bool JoinGame(const char* ip, int port);
enum eJoinState
{
eJoinState_Idle,
eJoinState_Connecting,
eJoinState_Success,
eJoinState_Failed,
eJoinState_Rejected,
eJoinState_Cancelled
};
static bool BeginJoinGame(const char* ip, int port);
static void CancelJoinGame();
static eJoinState GetJoinState();
static int GetJoinAttempt();
static int GetJoinMaxAttempts();
static DisconnectPacket::eDisconnectReason GetJoinRejectReason();
static bool FinalizeJoin();
static bool SendToSmallId(BYTE targetSmallId, const void* data, int dataSize);
static bool SendOnSocket(SOCKET sock, const void* data, int dataSize);
@ -112,6 +131,17 @@ private:
static DWORD WINAPI SplitScreenRecvThreadProc(LPVOID param);
static DWORD WINAPI AdvertiseThreadProc(LPVOID param);
static DWORD WINAPI DiscoveryThreadProc(LPVOID param);
static DWORD WINAPI JoinThreadProc(LPVOID param);
static HANDLE s_joinThread;
static volatile eJoinState s_joinState;
static volatile int s_joinAttempt;
static volatile bool s_joinCancel;
static char s_joinIP[256];
static int s_joinPort;
static BYTE s_joinAssignedSmallId;
static DisconnectPacket::eDisconnectReason s_joinRejectReason;
static const int JOIN_MAX_ATTEMPTS = 4;
static SOCKET s_listenSocket;
static SOCKET s_hostConnectionSocket;

View file

@ -218,10 +218,12 @@ bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1,
if (extend && param1 == TRIGGER_CONTRACT)
{
level->setData(x, y, z, facing | EXTENDED_BIT, UPDATE_CLIENTS);
ignoreUpdate(false);
return false;
}
else if (!extend && param1 == TRIGGER_EXTEND)
{
ignoreUpdate(false);
return false;
}
}
@ -247,6 +249,7 @@ bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1,
}
else
{
ignoreUpdate(false);
return false;
}
PIXEndNamedEvent();

View file

@ -19,7 +19,7 @@ If you're looking for Dedicated Server software, download its [Nightly Build her
- **Windows**: Supported for building and running the project
- **macOS / Linux**: The Windows nightly build will run through Wine or CrossOver based on community reports, but this is unofficial and not currently tested by the maintainers when pushing updates
- **Android**: The Windows nightly build does run but has stability / frametime pacing issues frequently reported
- **Android**: VIA x86 EMULATORS (like GameNative) ONLY! The Windows nightly build does run but has stability / frametime pacing issues frequently reported
- **iOS**: No current support
- **All Consoles**: Console support remains in the code, but maintainers are not currently verifying console functionality / porting UI Changes to the console builds at this time.