12 Commits

Author SHA1 Message Date
rtm516
38d58f2d8b Update actions workflows and add clang format check for PRs (#1418)
* Add clang-format workflow for pull request checks

* Modify push paths in nightly workflow

Updated paths for push event to include all files except specified ones.

* Update paths for nightly-server workflow triggers

* Modify paths for pull request triggers

Update pull request workflow to include specific paths.

* Tidy up clang-format installation in workflow
2026-03-28 23:35:25 -05:00
Botch
277d74716e Render custom skin boxes on viewmodel (#1415)
* Update PlayerRenderer.cpp

* Fix fatal bug where skins with no additional boxes would crash the game
2026-03-28 14:44:27 -05:00
Sestain
7447fabe0d Fix game crashing if DLC has XMLVERSION paramater (#1285)
* Fix game crashing if DLC has XMLVERSION paramater

* Better implementation of XMLVersion check

* Forgot to add type name to the list

* Removed extra newline
2026-03-27 15:11:27 -05:00
Sestain
3c1166c45e Added support for Big-Endian DLCs (#1291)
* Added support for Big-Endian DLCs

* Remove unused variable

* Remove the things made for other PR
2026-03-27 15:59:35 -04:00
Loki
0d4874dea5 Clarify Android support in README 2026-03-27 00:29:08 -05:00
666uvu
73d713878c fix redstone tick persistence on chunk unload (#1423) 2026-03-26 16:55:56 -05:00
Revela
4f370c45e3 Fix pistons permanently breaking server-wide on dedicated servers (#1420)
triggerEvent() set ignoreUpdate to true at the start but three early
return paths skipped the reset at the end. Once any of these paths was
hit, the TLS flag stayed true permanently, blocking all piston neighbor
updates for the rest of the server session.
2026-03-26 15:16:15 -05:00
Sylvessa
c96a8ee524 fix splitscreen xuids (#1413) 2026-03-26 10:19:20 -04:00
Sylvessa
1a50770647 Add asynchronous server joining (#1408) 2026-03-26 10:15:11 -04:00
Loki Rautio
dee559bd16 Revert "Memory leak fix: Make chunks unload properly (#1406)"
This reverts commit a24318eedc.
This fix introduces broken behavior for dedicated servers. It will be
merged back in once the related issue is fixed
2026-03-26 01:37:23 -05:00
ModMaker101
a24318eedc Memory leak fix: Make chunks unload properly (#1406)
* Fix chunk unload and cleanup logic, fixes #1347

* Applying formatting to code I edited 😝
2026-03-24 23:25:18 -05:00
Sylvessa
993052409a Fix XUID logging (#1395)
* pass invalid_xuid to other players

* actually more simple fix
2026-03-24 05:04:07 -04:00
20 changed files with 651 additions and 47 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

@@ -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)
@@ -580,4 +603,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()));
}
}

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.