diff --git a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp
index 6be67bed6..bb7951814 100644
--- a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp
+++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp
@@ -1200,10 +1200,12 @@ void UIScene_CreateWorldMenu::CreateGame(UIScene_CreateWorldMenu* pClass, DWORD
param->hellScale = 6;
break;
case 3:
- //param->xzSize = 5 * 64;
- //param->hellScale = 8;
-
// Large
+ param->xzSize = LEVEL_LARGE_WIDTH;
+ param->hellScale = HELL_LEVEL_MAX_SCALE;
+ break;
+ case 4:
+ // Infinite
param->xzSize = LEVEL_MAX_WIDTH;
param->hellScale = HELL_LEVEL_MAX_SCALE;
break;
diff --git a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp
index 6d472b50c..770f70a13 100644
--- a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp
+++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp
@@ -6,12 +6,13 @@
#define GAME_CREATE_ONLINE_TIMER_TIME 100
#ifdef _LARGE_WORLDS
-int m_iWorldSizeTitleA[4] =
+int m_iWorldSizeTitleA[5] =
{
IDS_WORLD_SIZE_TITLE_CLASSIC,
IDS_WORLD_SIZE_TITLE_SMALL,
IDS_WORLD_SIZE_TITLE_MEDIUM,
IDS_WORLD_SIZE_TITLE_LARGE,
+ IDS_WORLD_SIZE_TITLE_INFINITE,
};
#endif
@@ -80,7 +81,7 @@ UIScene_LaunchMoreOptionsMenu::UIScene_LaunchMoreOptionsMenu(int iPad, void *ini
m_labelRandomSeed.init(app.GetString(IDS_CREATE_NEW_WORLD_RANDOM_SEED));
m_editSeed.init(m_params->seed, eControl_EditSeed);
m_labelWorldSize.init(app.GetString(IDS_WORLD_SIZE));
- m_sliderWorldSize.init(app.GetString(m_iWorldSizeTitleA[m_params->worldSize]),eControl_WorldSize,0,3,m_params->worldSize);
+ m_sliderWorldSize.init(app.GetString(m_iWorldSizeTitleA[m_params->worldSize]),eControl_WorldSize,0,4,m_params->worldSize);
m_checkboxes[eLaunchCheckbox_DisableSaving].init( app.GetString(IDS_DISABLE_SAVING), eLaunchCheckbox_DisableSaving, m_params->bDisableSaving );
#endif
diff --git a/Minecraft.Client/DurangoMedia/loc/cs-CZ/strings.lang b/Minecraft.Client/DurangoMedia/loc/cs-CZ/strings.lang
index 4e88d97e3..3640f63d2 100644
--- a/Minecraft.Client/DurangoMedia/loc/cs-CZ/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/cs-CZ/strings.lang
@@ -8459,6 +8459,10 @@ Pokud se pokusíte postup uložit ve zkušební verzi, nabídne vám hra možnos
Velký
+
+ Infinite
+
+
Klasický
diff --git a/Minecraft.Client/DurangoMedia/loc/cs-CZ/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/cs-CZ/stringsPlatformSpecific.xml
index ad11bf3db..26a796f72 100644
--- a/Minecraft.Client/DurangoMedia/loc/cs-CZ/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/cs-CZ/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Pokud se pokusíte postup uložit ve zkušební verzi, nabídne vám hra možnos
Velký
+
+ Infinite
+
Klasický
diff --git a/Minecraft.Client/DurangoMedia/loc/da-DK/strings.lang b/Minecraft.Client/DurangoMedia/loc/da-DK/strings.lang
index 6e59d46bd..a901b286e 100644
--- a/Minecraft.Client/DurangoMedia/loc/da-DK/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/da-DK/strings.lang
@@ -8458,6 +8458,10 @@ Hvis du prøver at gemme, mens du bruger prøveversionen, får du mulighed for a
Stor
+
+ Infinite
+
+
Klassisk
diff --git a/Minecraft.Client/DurangoMedia/loc/da-DK/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/da-DK/stringsPlatformSpecific.xml
index 343f0c313..7508cb8ec 100644
--- a/Minecraft.Client/DurangoMedia/loc/da-DK/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/da-DK/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Hvis du prøver at gemme, mens du bruger prøveversionen, får du mulighed for a
Stor
+
+ Infinite
+
Klassisk
diff --git a/Minecraft.Client/DurangoMedia/loc/de-DE/strings.lang b/Minecraft.Client/DurangoMedia/loc/de-DE/strings.lang
index 6be6a08de..fb34e6c8f 100644
--- a/Minecraft.Client/DurangoMedia/loc/de-DE/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/de-DE/strings.lang
@@ -8461,6 +8461,10 @@ Wenn du in der Testversion versuchst zu speichern, wird dir die Möglichkeit geg
Groß
+
+ Infinite
+
+
Klassisch
diff --git a/Minecraft.Client/DurangoMedia/loc/de-DE/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/de-DE/stringsPlatformSpecific.xml
index 6c6e43b4e..ab9f92a55 100644
--- a/Minecraft.Client/DurangoMedia/loc/de-DE/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/de-DE/stringsPlatformSpecific.xml
@@ -259,6 +259,9 @@ Wenn du in der Testversion versuchst zu speichern, wird dir die Möglichkeit geg
Groß
+
+ Infinite
+
Klassisch
diff --git a/Minecraft.Client/DurangoMedia/loc/el-GR/strings.lang b/Minecraft.Client/DurangoMedia/loc/el-GR/strings.lang
index d29c12965..8f7d0f586 100644
--- a/Minecraft.Client/DurangoMedia/loc/el-GR/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/el-GR/strings.lang
@@ -8457,6 +8457,10 @@ Enchanted Books are used at the Anvil to apply enchantments to items. This gives
Μεγάλο
+
+ Infinite
+
+
Κλασικό
diff --git a/Minecraft.Client/DurangoMedia/loc/el-GR/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/el-GR/stringsPlatformSpecific.xml
index 41fd2addc..c826388d8 100644
--- a/Minecraft.Client/DurangoMedia/loc/el-GR/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/el-GR/stringsPlatformSpecific.xml
@@ -257,6 +257,9 @@
Μεγάλο
+
+ Infinite
+
Κλασικό
diff --git a/Minecraft.Client/DurangoMedia/loc/en-GB/strings.lang b/Minecraft.Client/DurangoMedia/loc/en-GB/strings.lang
index 4da0e92ac..fbdc579e8 100644
--- a/Minecraft.Client/DurangoMedia/loc/en-GB/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/en-GB/strings.lang
@@ -8459,6 +8459,10 @@ If you try to save while using the trial version, you will be given the option t
Large
+
+ Infinite
+
+
Classic
diff --git a/Minecraft.Client/DurangoMedia/loc/en-GB/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/en-GB/stringsPlatformSpecific.xml
index e3c7968b6..5baeb0534 100644
--- a/Minecraft.Client/DurangoMedia/loc/en-GB/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/en-GB/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ If you try to save while using the trial version, you will be given the option t
Large
+
+ Infinite
+
Classic
diff --git a/Minecraft.Client/DurangoMedia/loc/es-ES/strings.lang b/Minecraft.Client/DurangoMedia/loc/es-ES/strings.lang
index 30f6fc83a..42856e302 100644
--- a/Minecraft.Client/DurangoMedia/loc/es-ES/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/es-ES/strings.lang
@@ -8472,6 +8472,10 @@ Si intentas guardar en la versión de prueba, se te dará la opción de comprar
Grande
+
+ Infinite
+
+
Clásico
diff --git a/Minecraft.Client/DurangoMedia/loc/es-ES/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/es-ES/stringsPlatformSpecific.xml
index b878a66f9..137dbaaf6 100644
--- a/Minecraft.Client/DurangoMedia/loc/es-ES/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/es-ES/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Si intentas guardar en la versión de prueba, se te dará la opción de comprar
Grande
+
+ Infinite
+
Clásico
diff --git a/Minecraft.Client/DurangoMedia/loc/es-MX/strings.lang b/Minecraft.Client/DurangoMedia/loc/es-MX/strings.lang
index 9b7553c3a..4bae8ff13 100644
--- a/Minecraft.Client/DurangoMedia/loc/es-MX/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/es-MX/strings.lang
@@ -8460,6 +8460,10 @@ Si intentas guardar en la versión de prueba, se te dará la opción de comprar
Grande
+
+ Infinite
+
+
Clásico
diff --git a/Minecraft.Client/DurangoMedia/loc/es-MX/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/es-MX/stringsPlatformSpecific.xml
index 8f74b31b5..def270fd1 100644
--- a/Minecraft.Client/DurangoMedia/loc/es-MX/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/es-MX/stringsPlatformSpecific.xml
@@ -259,6 +259,9 @@ Si intentas guardar en la versión de prueba, se te dará la opción de comprar
Grande
+
+ Infinite
+
Clásico
diff --git a/Minecraft.Client/DurangoMedia/loc/fi-FI/strings.lang b/Minecraft.Client/DurangoMedia/loc/fi-FI/strings.lang
index 96d874ba3..31be19b1b 100644
--- a/Minecraft.Client/DurangoMedia/loc/fi-FI/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/fi-FI/strings.lang
@@ -8459,6 +8459,10 @@ Jos yrität tallentaa käyttäessäsi kokeiluversiota, sinulle tarjotaan mahdoll
Suuri
+
+ Infinite
+
+
Klassinen
diff --git a/Minecraft.Client/DurangoMedia/loc/fi-FI/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/fi-FI/stringsPlatformSpecific.xml
index 4d3f785a1..52b5a4ce1 100644
--- a/Minecraft.Client/DurangoMedia/loc/fi-FI/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/fi-FI/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Jos yrität tallentaa käyttäessäsi kokeiluversiota, sinulle tarjotaan mahdoll
Suuri
+
+ Infinite
+
Klassinen
diff --git a/Minecraft.Client/DurangoMedia/loc/fr-FR/strings.lang b/Minecraft.Client/DurangoMedia/loc/fr-FR/strings.lang
index a33a51da1..4cd786785 100644
--- a/Minecraft.Client/DurangoMedia/loc/fr-FR/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/fr-FR/strings.lang
@@ -8463,6 +8463,10 @@ Si vous tentez de sauvegarder en utilisant la version d'essai, l'achat de la ver
Énorme
+
+ Infinite
+
+
Classique
diff --git a/Minecraft.Client/DurangoMedia/loc/fr-FR/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/fr-FR/stringsPlatformSpecific.xml
index 3fe3d625c..0851b134d 100644
--- a/Minecraft.Client/DurangoMedia/loc/fr-FR/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/fr-FR/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Si vous tentez de sauvegarder en utilisant la version d'essai, l'achat de la ver
Énorme
+
+ Infinite
+
Classique
diff --git a/Minecraft.Client/DurangoMedia/loc/it-IT/strings.lang b/Minecraft.Client/DurangoMedia/loc/it-IT/strings.lang
index 4f3a1ae2e..c1bf0a5fe 100644
--- a/Minecraft.Client/DurangoMedia/loc/it-IT/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/it-IT/strings.lang
@@ -8460,6 +8460,10 @@ Se cerchi di salvare il gioco mentre è in uso la versione di prova, ti verrà o
Grande
+
+ Infinite
+
+
Classico
diff --git a/Minecraft.Client/DurangoMedia/loc/it-IT/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/it-IT/stringsPlatformSpecific.xml
index 7e2d12b66..b4aff28aa 100644
--- a/Minecraft.Client/DurangoMedia/loc/it-IT/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/it-IT/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Se cerchi di salvare il gioco mentre è in uso la versione di prova, ti verrà o
Grande
+
+ Infinite
+
Classico
diff --git a/Minecraft.Client/DurangoMedia/loc/ja-JP/strings.lang b/Minecraft.Client/DurangoMedia/loc/ja-JP/strings.lang
index d6f8ec41d..ba594f9ab 100644
--- a/Minecraft.Client/DurangoMedia/loc/ja-JP/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/ja-JP/strings.lang
@@ -8456,6 +8456,10 @@ Minecraft Xbox One 版は、初期設定でマルチプレイヤー ゲームに
ラージ
+
+ Infinite
+
+
クラシック
diff --git a/Minecraft.Client/DurangoMedia/loc/ja-JP/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/ja-JP/stringsPlatformSpecific.xml
index 39ebd63d9..36891aec1 100644
--- a/Minecraft.Client/DurangoMedia/loc/ja-JP/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/ja-JP/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Minecraft Xbox One 版は、初期設定でマルチプレイヤー ゲームに
ラージ
+
+ Infinite
+
クラシック
diff --git a/Minecraft.Client/DurangoMedia/loc/ko-KR/strings.lang b/Minecraft.Client/DurangoMedia/loc/ko-KR/strings.lang
index ca14511b0..5619e2c7e 100644
--- a/Minecraft.Client/DurangoMedia/loc/ko-KR/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/ko-KR/strings.lang
@@ -8458,6 +8458,10 @@ Xbox 360 본체용 Minecraft는 멀티 플레이 게임이 기본값으로 되
큼
+
+ Infinite
+
+
클래식
diff --git a/Minecraft.Client/DurangoMedia/loc/ko-KR/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/ko-KR/stringsPlatformSpecific.xml
index bbf05c05a..e4668ca36 100644
--- a/Minecraft.Client/DurangoMedia/loc/ko-KR/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/ko-KR/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Xbox 360 본체용 Minecraft는 멀티 플레이 게임이 기본값으로 되
큼
+
+ Infinite
+
클래식
diff --git a/Minecraft.Client/DurangoMedia/loc/nb-NO/strings.lang b/Minecraft.Client/DurangoMedia/loc/nb-NO/strings.lang
index 1b51b20d5..4d7822545 100644
--- a/Minecraft.Client/DurangoMedia/loc/nb-NO/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/nb-NO/strings.lang
@@ -8459,6 +8459,10 @@ Hvis du prøver å lagre mens du bruker prøveversjonen, får du muligheten til
Stor
+
+ Infinite
+
+
Klassisk
diff --git a/Minecraft.Client/DurangoMedia/loc/nb-NO/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/nb-NO/stringsPlatformSpecific.xml
index d96483c8b..e2b24cadc 100644
--- a/Minecraft.Client/DurangoMedia/loc/nb-NO/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/nb-NO/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Hvis du prøver å lagre mens du bruker prøveversjonen, får du muligheten til
Stor
+
+ Infinite
+
Klassisk
diff --git a/Minecraft.Client/DurangoMedia/loc/nl-NL/strings.lang b/Minecraft.Client/DurangoMedia/loc/nl-NL/strings.lang
index c39ad1881..3f6d7e971 100644
--- a/Minecraft.Client/DurangoMedia/loc/nl-NL/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/nl-NL/strings.lang
@@ -8459,6 +8459,10 @@ Als je in de demo probeert op te slaan, krijg je de optie om de volledige versie
Groot
+
+ Infinite
+
+
Klassiek
diff --git a/Minecraft.Client/DurangoMedia/loc/nl-NL/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/nl-NL/stringsPlatformSpecific.xml
index 3c6f8504b..2fd7c4b20 100644
--- a/Minecraft.Client/DurangoMedia/loc/nl-NL/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/nl-NL/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Als je in de demo probeert op te slaan, krijg je de optie om de volledige versie
Groot
+
+ Infinite
+
Klassiek
diff --git a/Minecraft.Client/DurangoMedia/loc/pl-PL/strings.lang b/Minecraft.Client/DurangoMedia/loc/pl-PL/strings.lang
index 019280429..b31f4f1cd 100644
--- a/Minecraft.Client/DurangoMedia/loc/pl-PL/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/pl-PL/strings.lang
@@ -8457,6 +8457,10 @@ Przy próbie zapisania gry z wersją testową pojawi się propozycja zakupu peł
Duży
+
+ Infinite
+
+
Klasyczny
diff --git a/Minecraft.Client/DurangoMedia/loc/pl-PL/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/pl-PL/stringsPlatformSpecific.xml
index 3af275228..d9bf5c4e0 100644
--- a/Minecraft.Client/DurangoMedia/loc/pl-PL/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/pl-PL/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Przy próbie zapisania gry z wersją testową pojawi się propozycja zakupu peł
Duży
+
+ Infinite
+
Klasyczny
diff --git a/Minecraft.Client/DurangoMedia/loc/pt-BR/strings.lang b/Minecraft.Client/DurangoMedia/loc/pt-BR/strings.lang
index ccede285e..afca3fd75 100644
--- a/Minecraft.Client/DurangoMedia/loc/pt-BR/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/pt-BR/strings.lang
@@ -8450,6 +8450,10 @@ Não desligue o console Xbox One enquanto este ícone estiver na tela.
Grande
+
+ Infinite
+
+
Clássico
diff --git a/Minecraft.Client/DurangoMedia/loc/pt-BR/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/pt-BR/stringsPlatformSpecific.xml
index 42d34f583..45cddaf23 100644
--- a/Minecraft.Client/DurangoMedia/loc/pt-BR/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/pt-BR/stringsPlatformSpecific.xml
@@ -256,6 +256,9 @@ Não desligue o console Xbox One enquanto este ícone estiver na tela.
Grande
+
+ Infinite
+
Clássico
diff --git a/Minecraft.Client/DurangoMedia/loc/pt-PT/strings.lang b/Minecraft.Client/DurangoMedia/loc/pt-PT/strings.lang
index 4c4fb4863..48b7d8c3f 100644
--- a/Minecraft.Client/DurangoMedia/loc/pt-PT/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/pt-PT/strings.lang
@@ -8456,6 +8456,10 @@ Se tentares guardar enquanto usas a versão de avaliação, ser-te-á apresentad
Grande
+
+ Infinite
+
+
Clássico
diff --git a/Minecraft.Client/DurangoMedia/loc/pt-PT/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/pt-PT/stringsPlatformSpecific.xml
index 0f302251c..7eb23a129 100644
--- a/Minecraft.Client/DurangoMedia/loc/pt-PT/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/pt-PT/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Se tentares guardar enquanto usas a versão de avaliação, ser-te-á apresentad
Grande
+
+ Infinite
+
Clássico
diff --git a/Minecraft.Client/DurangoMedia/loc/ru-RU/strings.lang b/Minecraft.Client/DurangoMedia/loc/ru-RU/strings.lang
index 4b1e06bcf..f0d5c6673 100644
--- a/Minecraft.Client/DurangoMedia/loc/ru-RU/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/ru-RU/strings.lang
@@ -8456,6 +8456,10 @@ Enchanted Books are used at the Anvil to apply enchantments to items. This gives
Большой
+
+ Infinite
+
+
Классика
diff --git a/Minecraft.Client/DurangoMedia/loc/ru-RU/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/ru-RU/stringsPlatformSpecific.xml
index b6083f400..0f85fcb4c 100644
--- a/Minecraft.Client/DurangoMedia/loc/ru-RU/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/ru-RU/stringsPlatformSpecific.xml
@@ -254,6 +254,9 @@
Большой
+
+ Infinite
+
Классика
diff --git a/Minecraft.Client/DurangoMedia/loc/sk-SK/strings.lang b/Minecraft.Client/DurangoMedia/loc/sk-SK/strings.lang
index 3f7e4315d..d68ea1e4a 100644
--- a/Minecraft.Client/DurangoMedia/loc/sk-SK/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/sk-SK/strings.lang
@@ -8459,6 +8459,10 @@ Ak sa pri používaní skúšobnej verzie pokúsite o uloženie, zobrazí sa mo
Veľký
+
+ Infinite
+
+
Klasický
diff --git a/Minecraft.Client/DurangoMedia/loc/sk-SK/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/sk-SK/stringsPlatformSpecific.xml
index 5c7ea347b..86f392ebd 100644
--- a/Minecraft.Client/DurangoMedia/loc/sk-SK/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/sk-SK/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ Ak sa pri používaní skúšobnej verzie pokúsite o uloženie, zobrazí sa mo
Veľký
+
+ Infinite
+
Klasický
diff --git a/Minecraft.Client/DurangoMedia/loc/strings.lang b/Minecraft.Client/DurangoMedia/loc/strings.lang
index a2a232159..c9e43b7d7 100644
--- a/Minecraft.Client/DurangoMedia/loc/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/strings.lang
@@ -8459,6 +8459,10 @@ If you try to save while using the trial version, you will be given the option t
Large
+
+ Infinite
+
+
Classic
diff --git a/Minecraft.Client/DurangoMedia/loc/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/stringsPlatformSpecific.xml
index e3c7968b6..5baeb0534 100644
--- a/Minecraft.Client/DurangoMedia/loc/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ If you try to save while using the trial version, you will be given the option t
Large
+
+ Infinite
+
Classic
diff --git a/Minecraft.Client/DurangoMedia/loc/sv-SE/strings.lang b/Minecraft.Client/DurangoMedia/loc/sv-SE/strings.lang
index e75ebe74c..bd8c52f99 100644
--- a/Minecraft.Client/DurangoMedia/loc/sv-SE/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/sv-SE/strings.lang
@@ -8454,6 +8454,10 @@ Om du försöker spara kommer du få möjligheten att köpa den kompletta versio
Stor
+
+ Infinite
+
+
Klassisk
diff --git a/Minecraft.Client/DurangoMedia/loc/sv-SE/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/sv-SE/stringsPlatformSpecific.xml
index 22dd85c41..0c7385f8b 100644
--- a/Minecraft.Client/DurangoMedia/loc/sv-SE/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/sv-SE/stringsPlatformSpecific.xml
@@ -254,6 +254,9 @@ Om du försöker spara kommer du få möjligheten att köpa den kompletta versio
Stor
+
+ Infinite
+
Klassisk
diff --git a/Minecraft.Client/DurangoMedia/loc/zh-CHS/strings.lang b/Minecraft.Client/DurangoMedia/loc/zh-CHS/strings.lang
index ad68cd7f2..db82c7019 100644
--- a/Minecraft.Client/DurangoMedia/loc/zh-CHS/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/zh-CHS/strings.lang
@@ -8455,6 +8455,10 @@ Minecraft 在 Xbox One 主机上默认采用多人游戏。如果在高清模式
大型
+
+ Infinite
+
+
经典
diff --git a/Minecraft.Client/DurangoMedia/loc/zh-CHS/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/zh-CHS/stringsPlatformSpecific.xml
index a5d6717be..717ce073c 100644
--- a/Minecraft.Client/DurangoMedia/loc/zh-CHS/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/zh-CHS/stringsPlatformSpecific.xml
@@ -254,6 +254,9 @@ Minecraft 在 Xbox One 主机上默认采用多人游戏。如果在高清模式
大型
+
+ Infinite
+
经典
diff --git a/Minecraft.Client/DurangoMedia/loc/zh-CHT/strings.lang b/Minecraft.Client/DurangoMedia/loc/zh-CHT/strings.lang
index e6c5f96ee..60e4439fb 100644
--- a/Minecraft.Client/DurangoMedia/loc/zh-CHT/strings.lang
+++ b/Minecraft.Client/DurangoMedia/loc/zh-CHT/strings.lang
@@ -8465,6 +8465,10 @@ Xbox One 主機上的 Minecraft 預設為多人遊戲。如果您選擇高畫質
大
+
+ Infinite
+
+
經典
diff --git a/Minecraft.Client/DurangoMedia/loc/zh-CHT/stringsPlatformSpecific.xml b/Minecraft.Client/DurangoMedia/loc/zh-CHT/stringsPlatformSpecific.xml
index 1b46c776b..a961bbd79 100644
--- a/Minecraft.Client/DurangoMedia/loc/zh-CHT/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/DurangoMedia/loc/zh-CHT/stringsPlatformSpecific.xml
@@ -262,6 +262,9 @@ Xbox One 主機上的 Minecraft 預設為多人遊戲。如果您選擇高畫質
大
+
+ Infinite
+
經典
diff --git a/Minecraft.Client/DurangoMedia/strings.h b/Minecraft.Client/DurangoMedia/strings.h
index 65759e893..38ed9c4d6 100644
--- a/Minecraft.Client/DurangoMedia/strings.h
+++ b/Minecraft.Client/DurangoMedia/strings.h
@@ -1955,3 +1955,4 @@
#define IDS_YOU_DIED 1953
#define IDS_YOU_HAVE 1954
#define IDS_ZOMBIE 1955
+#define IDS_WORLD_SIZE_TITLE_INFINITE 1956
diff --git a/Minecraft.Client/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp
index 983600783..6622c0d9a 100644
--- a/Minecraft.Client/LevelRenderer.cpp
+++ b/Minecraft.Client/LevelRenderer.cpp
@@ -46,6 +46,7 @@
#include "..\Minecraft.World\net.minecraft.world.level.h"
#include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
#include "..\Minecraft.World\net.minecraft.world.level.tile.h"
+#include "..\Minecraft.World\net.minecraft.world.level.storage.h"
#include "..\Minecraft.World\net.minecraft.world.phys.h"
#include "..\Minecraft.World\net.minecraft.world.entity.player.h"
#include "..\Minecraft.World\net.minecraft.world.item.h"
@@ -77,13 +78,15 @@ C4JThread *LevelRenderer::rebuildThreads[MAX_CHUNK_REBUILD_THREADS];
C4JThread::EventArray *LevelRenderer::s_rebuildCompleteEvents;
C4JThread::Event *LevelRenderer::s_activationEventA[MAX_CHUNK_REBUILD_THREADS];
-// This defines the maximum size of renderable level, must be big enough to cope with actual size of level + view distance at each side
-// so that we can render the "infinite" sea at the edges. Currently defined as:
-const int overworldSize = LEVEL_MAX_WIDTH + LevelRenderer::PLAYER_VIEW_DISTANCE + LevelRenderer::PLAYER_VIEW_DISTANCE;
-const int netherSize = HELL_LEVEL_MAX_WIDTH + 2; // 4J Stu - The plus 2 is really just to make our total chunk count a multiple of 8 for the flags, we will never see these in the nether
+// For infinite worlds the overworld AND nether render sizes use the rolling view window.
+// Nether is also effectively infinite with _LARGE_WORLDS; End remains a fixed small island.
+const int netherSize = LevelRenderer::PLAYER_VIEW_DISTANCE * 2; // rolling window, same as overworld
const int endSize = END_LEVEL_MAX_WIDTH;
-const int LevelRenderer::MAX_LEVEL_RENDER_SIZE[3] = { overworldSize, netherSize, endSize };
-const int LevelRenderer::DIMENSION_OFFSETS[3] = { 0, (overworldSize * overworldSize * CHUNK_Y_COUNT) , (overworldSize * overworldSize * CHUNK_Y_COUNT) + ( netherSize * netherSize * CHUNK_Y_COUNT ) };
+// MAX_LEVEL_RENDER_SIZE[0] is overridden at runtime in getGlobalIndexForChunk / getGlobalChunkCount.
+// Use PLAYER_VIEW_DISTANCE*2 as the compile-time upper bound for static array sizing only.
+const int overworldSizeMax = LevelRenderer::PLAYER_VIEW_DISTANCE * 2;
+const int LevelRenderer::MAX_LEVEL_RENDER_SIZE[3] = { overworldSizeMax, netherSize, endSize };
+const int LevelRenderer::DIMENSION_OFFSETS[3] = { 0, (overworldSizeMax * overworldSizeMax * CHUNK_Y_COUNT), (overworldSizeMax * overworldSizeMax * CHUNK_Y_COUNT) + (netherSize * netherSize * CHUNK_Y_COUNT) };
#else
// This defines the maximum size of renderable level, must be big enough to cope with actual size of level + view distance at each side
// so that we can render the "infinite" sea at the edges. Currently defined as:
@@ -141,6 +144,7 @@ LevelRenderer::LevelRenderer(Minecraft *mc, Textures *textures)
InitializeCriticalSection(&m_csRenderableTileEntities);
#ifdef _LARGE_WORLDS
InitializeCriticalSection(&m_csChunkFlags);
+ m_isInfinite = false;
#endif
dirtyChunkPresent = false;
@@ -351,6 +355,9 @@ void LevelRenderer::setLevel(int playerIndex, MultiPlayerLevel *level)
level->addListener(this);
}
+#ifdef _LARGE_WORLDS
+ m_isInfinite = isInfiniteWorld(level->getLevelData()->getXZSize());
+#endif
allChanged(playerIndex);
}
else
@@ -3236,21 +3243,44 @@ int LevelRenderer::getGlobalIndexForChunk(int x, int y, int z, Level *level)
int LevelRenderer::getGlobalIndexForChunk(int x, int y, int z, int dimensionId)
{
int dimIdx = getDimensionIndexFromId(dimensionId);
- int xx = ( x / CHUNK_XZSIZE ) + ( MAX_LEVEL_RENDER_SIZE[dimIdx] / 2 );
- int yy = y / CHUNK_SIZE;
- int zz = ( z / CHUNK_XZSIZE ) + ( MAX_LEVEL_RENDER_SIZE[dimIdx] / 2 );
- if( ( xx < 0 ) || ( xx >= MAX_LEVEL_RENDER_SIZE[dimIdx] ) ) return -1;
- if( ( zz < 0 ) || ( zz >= MAX_LEVEL_RENDER_SIZE[dimIdx] ) ) return -1;
+ int yy = y / CHUNK_SIZE;
if( ( yy < 0 ) || ( yy >= CHUNK_Y_COUNT ) ) return -1;
- int dimOffset = DIMENSION_OFFSETS[dimIdx];
-
- int offset = dimOffset; // Offset caused by current dimension
- offset += ( zz * MAX_LEVEL_RENDER_SIZE[dimIdx] + xx ) * CHUNK_Y_COUNT; // Offset by x/z pos
- offset += yy; // Offset by y pos
-
- return offset;
+ if( dimIdx == 0 )
+ {
+ // Overworld: use rolling window modulo so the index is always within [0, xChunks*yChunks*zChunks)
+ // Chunk::levelRenderer is the singleton LevelRenderer instance.
+ LevelRenderer *lr = Chunk::levelRenderer;
+ if( lr == NULL || lr->xChunks == 0 ) return -1;
+ int sz = lr->xChunks; // xChunks == zChunks
+ int xx = ((x / CHUNK_XZSIZE) % sz + sz) % sz;
+ int zz = ((z / CHUNK_XZSIZE) % sz + sz) % sz;
+ int offset = ( zz * sz + xx ) * CHUNK_Y_COUNT + yy;
+ return offset;
+ }
+ else if( dimIdx == 1 )
+ {
+ // Nether: also rolling window (infinite nether with _LARGE_WORLDS)
+ int sz = MAX_LEVEL_RENDER_SIZE[1]; // = PLAYER_VIEW_DISTANCE * 2
+ int xx = ((x / CHUNK_XZSIZE) % sz + sz) % sz;
+ int zz = ((z / CHUNK_XZSIZE) % sz + sz) % sz;
+ int dimOffset = DIMENSION_OFFSETS[1];
+ int offset = dimOffset + ( zz * sz + xx ) * CHUNK_Y_COUNT + yy;
+ return offset;
+ }
+ else
+ {
+ // End: fixed small world, use centre-offset indexing as before
+ int sz = MAX_LEVEL_RENDER_SIZE[dimIdx];
+ int xx = ( x / CHUNK_XZSIZE ) + ( sz / 2 );
+ int zz = ( z / CHUNK_XZSIZE ) + ( sz / 2 );
+ if( ( xx < 0 ) || ( xx >= sz ) ) return -1;
+ if( ( zz < 0 ) || ( zz >= sz ) ) return -1;
+ int dimOffset = DIMENSION_OFFSETS[dimIdx];
+ int offset = dimOffset + ( zz * sz + xx ) * CHUNK_Y_COUNT + yy;
+ return offset;
+ }
}
bool LevelRenderer::isGlobalIndexInSameDimension( int idx, Level *level)
@@ -3264,14 +3294,19 @@ bool LevelRenderer::isGlobalIndexInSameDimension( int idx, Level *level)
int LevelRenderer::getGlobalChunkCount()
{
- return ( MAX_LEVEL_RENDER_SIZE[0] * MAX_LEVEL_RENDER_SIZE[0] * CHUNK_Y_COUNT ) +
- ( MAX_LEVEL_RENDER_SIZE[1] * MAX_LEVEL_RENDER_SIZE[1] * CHUNK_Y_COUNT ) +
- ( MAX_LEVEL_RENDER_SIZE[2] * MAX_LEVEL_RENDER_SIZE[2] * CHUNK_Y_COUNT );
+ // Overworld uses the rolling view window (xChunks x yChunks x zChunks).
+ // Nether and End are fixed small sizes.
+ LevelRenderer *lr = Chunk::levelRenderer;
+ int overworldCount = ( lr && lr->xChunks > 0 ) ? ( lr->xChunks * CHUNK_Y_COUNT * lr->zChunks ) : ( overworldSizeMax * overworldSizeMax * CHUNK_Y_COUNT );
+ return overworldCount
+ + ( MAX_LEVEL_RENDER_SIZE[1] * MAX_LEVEL_RENDER_SIZE[1] * CHUNK_Y_COUNT )
+ + ( MAX_LEVEL_RENDER_SIZE[2] * MAX_LEVEL_RENDER_SIZE[2] * CHUNK_Y_COUNT );
}
int LevelRenderer::getGlobalChunkCountForOverworld()
{
- return ( MAX_LEVEL_RENDER_SIZE[0] * MAX_LEVEL_RENDER_SIZE[0] * CHUNK_Y_COUNT );
+ LevelRenderer *lr = Chunk::levelRenderer;
+ return ( lr && lr->xChunks > 0 ) ? ( lr->xChunks * CHUNK_Y_COUNT * lr->zChunks ) : ( overworldSizeMax * overworldSizeMax * CHUNK_Y_COUNT );
}
unsigned char LevelRenderer::getGlobalChunkFlags(int x, int y, int z, Level *level)
diff --git a/Minecraft.Client/LevelRenderer.h b/Minecraft.Client/LevelRenderer.h
index c0a98bba6..33de3cf1d 100644
--- a/Minecraft.Client/LevelRenderer.h
+++ b/Minecraft.Client/LevelRenderer.h
@@ -133,6 +133,9 @@ class LevelRenderer : public LevelListener
ClipChunkArray chunks[4]; // 4J - now one per player
int lastPlayerCount[4]; // 4J - added
int xChunks, yChunks, zChunks;
+#ifdef _LARGE_WORLDS
+ bool m_isInfinite; // true when overworld is infinite (rolling window render)
+#endif
int chunkLists;
Minecraft *mc;
TileRenderer *tileRenderer[4]; // 4J - now one per player
diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp
index 8c6c90e71..664871430 100644
--- a/Minecraft.Client/MultiPlayerChunkCache.cpp
+++ b/Minecraft.Client/MultiPlayerChunkCache.cpp
@@ -10,13 +10,18 @@
#include "..\Minecraft.World\Tile.h"
#include "..\Minecraft.World\WaterLevelChunk.h"
+// Pack (x,z) chunk coords into a single int64 key
+static inline int64_t mpKey(int x, int z) {
+ return ((int64_t)(unsigned int)x << 32) | (unsigned int)z;
+}
+
MultiPlayerChunkCache::MultiPlayerChunkCache(Level *level)
{
- XZSIZE = level->dimension->getXZSize(); // 4J Added
- XZOFFSET = XZSIZE/2; // 4J Added
- m_XZSize = XZSIZE;
- hasData = new bool[XZSIZE * XZSIZE];
- memset(hasData, 0, sizeof(bool) * XZSIZE * XZSIZE);
+ // For infinite worlds, m_XZSize is kept for compatibility but the cache is unbounded
+ m_XZSize = level->dimension->getXZSize();
+#ifdef _LARGE_WORLDS
+ m_isInfinite = isInfiniteWorld(m_XZSize);
+#endif
emptyChunk = new EmptyLevelChunk(level, byteArray(16 * 16 * Level::maxBuildHeight), 0, 0);
@@ -92,9 +97,8 @@ MultiPlayerChunkCache::MultiPlayerChunkCache(Level *level)
}
this->level = level;
+ m_tickCount = 0;
- this->cache = new LevelChunk *[XZSIZE * XZSIZE];
- memset(this->cache, 0, XZSIZE * XZSIZE * sizeof(LevelChunk *));
InitializeCriticalSectionAndSpinCount(&m_csLoadCreate,4000);
}
@@ -102,13 +106,16 @@ MultiPlayerChunkCache::~MultiPlayerChunkCache()
{
delete emptyChunk;
delete waterChunk;
- delete cache;
- delete hasData;
AUTO_VAR(itEnd, loadedChunkList.end());
for (AUTO_VAR(it, loadedChunkList.begin()); it != itEnd; it++)
delete *it;
+ // Delete any chunks still waiting in the deferred-delete queue
+ for (auto &pd : m_pendingDelete)
+ delete pd.first;
+ m_pendingDelete.clear();
+
DeleteCriticalSection(&m_csLoadCreate);
}
@@ -122,138 +129,157 @@ bool MultiPlayerChunkCache::hasChunk(int x, int z)
// 4J added - find out if we actually really do have a chunk in our cache
bool MultiPlayerChunkCache::reallyHasChunk(int x, int z)
{
- int ix = x + XZOFFSET;
- int iz = z + XZOFFSET;
- // Check we're in range of the stored level - if we aren't, then consider that we do have that chunk as we'll be able to use the water chunk there
- if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return true;
- if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return true;
- int idx = ix * XZSIZE + iz;
-
- LevelChunk *chunk = cache[idx];
- if( chunk == NULL )
+#ifdef _LARGE_WORLDS
+ // For finite worlds, out-of-bounds coordinates are treated as "exists"
+ if (!m_isInfinite)
+ {
+ int half = m_XZSize / 2;
+ if (x < -half || x >= half || z < -half || z >= half) return true;
+ }
+#endif
+
+ int64_t key = mpKey(x, z);
+ EnterCriticalSection(&m_csLoadCreate);
+ auto it = m_chunkMap.find(key);
+ if (it == m_chunkMap.end() || it->second == NULL)
{
+ LeaveCriticalSection(&m_csLoadCreate);
return false;
}
- return hasData[idx];
+ auto itd = m_hasDataMap.find(key);
+ bool result = (itd != m_hasDataMap.end() && itd->second);
+ LeaveCriticalSection(&m_csLoadCreate);
+ return result;
}
void MultiPlayerChunkCache::drop(int x, int z)
{
// 4J Stu - We do want to drop any entities in the chunks, especially for the case when a player is dead as they will
// not get the RemoveEntity packet if an entity is removed.
- LevelChunk *chunk = getChunk(x, z);
- if (!chunk->isEmpty())
+ EnterCriticalSection(&m_csLoadCreate);
+ int64_t key = mpKey(x, z);
+ auto it = m_chunkMap.find(key);
+ if (it != m_chunkMap.end() && it->second != NULL)
{
- // Added parameter here specifies that we don't want to delete tile entities, as they won't get recreated unless they've got update packets
- // The tile entities are in general only created on the client by virtue of the chunk rebuild
- chunk->unload(false);
+ LevelChunk *chunk = it->second;
+ if (!chunk->isEmpty())
+ {
+ // Added parameter here specifies that we don't want to delete tile entities, as they won't get recreated unless they've got update packets
+ // The tile entities are in general only created on the client by virtue of the chunk rebuild
+ chunk->unload(false);
+ }
+
+ // Remove from maps so getChunk() will return emptyChunk for this position
+ m_chunkMap.erase(it);
+ m_hasDataMap.erase(key);
+
+ auto lit = std::find(loadedChunkList.begin(), loadedChunkList.end(), chunk);
+ if (lit != loadedChunkList.end()) loadedChunkList.erase(lit);
- // 4J - We just want to clear out the entities in the chunk, but everything else should be valid
- chunk->loaded = true;
- }
+ // Defer actual deletion: rebuild threads may still hold a raw LevelChunk* from
+ // a getChunkAt() call that returned this chunk just before we removed it.
+ // The chunk stays alive for DELETE_DELAY_TICKS so any in-flight rebuild finishes safely.
+ m_pendingDelete.push_back(std::make_pair(chunk, m_tickCount + DELETE_DELAY_TICKS));
+ }
+ LeaveCriticalSection(&m_csLoadCreate);
}
LevelChunk *MultiPlayerChunkCache::create(int x, int z)
{
- int ix = x + XZOFFSET;
- int iz = z + XZOFFSET;
- // Check we're in range of the stored level
- if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
- if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
- int idx = ix * XZSIZE + iz;
- LevelChunk *chunk = cache[idx];
- LevelChunk *lastChunk = chunk;
-
- if( chunk == NULL )
- {
- EnterCriticalSection(&m_csLoadCreate);
-
- //LevelChunk *chunk;
- if( g_NetworkManager.IsHost() ) // force here to disable sharing of data
- {
- // 4J-JEV: We are about to use shared data, abort if the server is stopped and the data is deleted.
- if (MinecraftServer::getInstance()->serverHalted()) return NULL;
-
- // If we're the host, then don't create the chunk, share data from the server's copy
#ifdef _LARGE_WORLDS
- LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunkLoadedOrUnloaded(x,z);
-#else
- LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunk(x,z);
+ // For finite worlds, refuse to create chunks outside the world boundary
+ if (!m_isInfinite)
+ {
+ int half = m_XZSize / 2;
+ if (x < -half || x >= half || z < -half || z >= half) return emptyChunk;
+ }
#endif
- chunk = new LevelChunk(level, x, z, serverChunk);
- // Let renderer know that this chunk has been created - it might have made render data from the EmptyChunk if it got to a chunk before the server sent it
- level->setTilesDirty( x * 16 , 0 , z * 16 , x * 16 + 15, 127, z * 16 + 15);
- hasData[idx] = true;
- }
- else
- {
- // Passing an empty array into the LevelChunk ctor, which it now detects and sets up the chunk as compressed & empty
- byteArray bytes;
- chunk = new LevelChunk(level, bytes, x, z);
+ int64_t key = mpKey(x, z);
- // 4J - changed to use new methods for lighting
- chunk->setSkyLightDataAllBright();
-// Arrays::fill(chunk->skyLight->data, (byte) 255);
+ EnterCriticalSection(&m_csLoadCreate);
+ {
+ auto it = m_chunkMap.find(key);
+ if (it != m_chunkMap.end() && it->second != NULL)
+ {
+ LevelChunk *existing = it->second;
+ existing->load();
+ LeaveCriticalSection(&m_csLoadCreate);
+ return existing;
}
-
- chunk->loaded = true;
+ }
- LeaveCriticalSection(&m_csLoadCreate);
+ LevelChunk *chunk = NULL;
-#if ( defined _WIN64 || defined __LP64__ )
- if( InterlockedCompareExchangeRelease64((LONG64 *)&cache[idx],(LONG64)chunk,(LONG64)lastChunk) == (LONG64)lastChunk )
-#else
- if( InterlockedCompareExchangeRelease((LONG *)&cache[idx],(LONG)chunk,(LONG)lastChunk) == (LONG)lastChunk )
-#endif // _DURANGO
+ if( g_NetworkManager.IsHost() ) // force here to disable sharing of data
+ {
+ // 4J-JEV: We are about to use shared data, abort if the server is stopped and the data is deleted.
+ if (MinecraftServer::getInstance()->serverHalted())
{
- // If we're sharing with the server, we'll need to calculate our heightmap now, which isn't shared. If we aren't sharing with the server,
- // then this will be calculated when the chunk data arrives.
- if( g_NetworkManager.IsHost() )
- {
- chunk->recalcHeightmapOnly();
- }
-
- // Successfully updated the cache
- EnterCriticalSection(&m_csLoadCreate);
- loadedChunkList.push_back(chunk);
LeaveCriticalSection(&m_csLoadCreate);
- }
- else
- {
- // Something else must have updated the cache. Return that chunk and discard this one. This really shouldn't be happening
- // in multiplayer
- delete chunk;
- return cache[idx];
+ return NULL;
}
+ // If we're the host, then don't create the chunk, share data from the server's copy
+#ifdef _LARGE_WORLDS
+ LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunkLoadedOrUnloaded(x,z);
+#else
+ LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunk(x,z);
+#endif
+ chunk = new LevelChunk(level, x, z, serverChunk);
+ // Let renderer know that this chunk has been created - it might have made render data from the EmptyChunk if it got to a chunk before the server sent it
+ level->setTilesDirty( x * 16 , 0 , z * 16 , x * 16 + 15, 127, z * 16 + 15);
+ m_hasDataMap[key] = true;
}
else
{
- chunk->load();
+ // Passing an empty array into the LevelChunk ctor, which it now detects and sets up the chunk as compressed & empty
+ byteArray bytes;
+
+ chunk = new LevelChunk(level, bytes, x, z);
+
+ // 4J - changed to use new methods for lighting
+ chunk->setSkyLightDataAllBright();
}
+
+ chunk->loaded = true;
+
+ // Insert into hash map
+ m_chunkMap[key] = chunk;
+
+ // If we're sharing with the server, we'll need to calculate our heightmap now, which isn't shared.
+ if( g_NetworkManager.IsHost() )
+ {
+ chunk->recalcHeightmapOnly();
+ }
+
+ loadedChunkList.push_back(chunk);
+
+ LeaveCriticalSection(&m_csLoadCreate);
return chunk;
}
LevelChunk *MultiPlayerChunkCache::getChunk(int x, int z)
{
- int ix = x + XZOFFSET;
- int iz = z + XZOFFSET;
- // Check we're in range of the stored level
- if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
- if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
- int idx = ix * XZSIZE + iz;
-
- LevelChunk *chunk = cache[idx];
- if( chunk == NULL )
- {
- return emptyChunk;
- }
- else
+#ifdef _LARGE_WORLDS
+ // For finite worlds, out-of-bounds coordinates return the water/empty chunk
+ if (!m_isInfinite)
{
- return chunk;
+ int half = m_XZSize / 2;
+ if (x < -half || x >= half || z < -half || z >= half)
+ {
+ // Return waterChunk for overworld edge (visual sea), emptyChunk for other dims
+ return (waterChunk != NULL) ? waterChunk : emptyChunk;
+ }
}
+#endif
+
+ EnterCriticalSection(&m_csLoadCreate);
+ auto it = m_chunkMap.find(mpKey(x, z));
+ LevelChunk *chunk = (it != m_chunkMap.end() && it->second != NULL) ? it->second : NULL;
+ LeaveCriticalSection(&m_csLoadCreate);
+ return chunk != NULL ? chunk : emptyChunk;
}
bool MultiPlayerChunkCache::save(bool force, ProgressListener *progressListener)
@@ -263,6 +289,13 @@ bool MultiPlayerChunkCache::save(bool force, ProgressListener *progressListener)
bool MultiPlayerChunkCache::tick()
{
+ m_tickCount++;
+ while (!m_pendingDelete.empty() && m_pendingDelete.front().second <= m_tickCount)
+ {
+ delete m_pendingDelete.front().first;
+ m_pendingDelete.pop_front();
+ }
+
return false;
}
@@ -296,11 +329,7 @@ wstring MultiPlayerChunkCache::gatherStats()
void MultiPlayerChunkCache::dataReceived(int x, int z)
{
- int ix = x + XZOFFSET;
- int iz = z + XZOFFSET;
- // Check we're in range of the stored level
- if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return;
- if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return;
- int idx = ix * XZSIZE + iz;
- hasData[idx] = true;
-}
\ No newline at end of file
+ EnterCriticalSection(&m_csLoadCreate);
+ m_hasDataMap[mpKey(x, z)] = true;
+ LeaveCriticalSection(&m_csLoadCreate);
+}
diff --git a/Minecraft.Client/MultiPlayerChunkCache.h b/Minecraft.Client/MultiPlayerChunkCache.h
index 8fc427a6b..a6002c94f 100644
--- a/Minecraft.Client/MultiPlayerChunkCache.h
+++ b/Minecraft.Client/MultiPlayerChunkCache.h
@@ -2,11 +2,14 @@
#include "..\Minecraft.World\net.minecraft.world.level.h"
#include "..\Minecraft.World\net.minecraft.world.level.chunk.h"
#include "..\Minecraft.World\RandomLevelSource.h"
+#include
+#include
using namespace std;
class ServerChunkCache;
-// 4J - various alterations here to make this thread safe, and operate as a fixed sized cache
+// 4J - various alterations here to make this thread safe
+// Modified for infinite worlds: flat array cache replaced with unordered_map
class MultiPlayerChunkCache : public ChunkSource
{
friend class LevelRenderer;
@@ -16,13 +19,21 @@ class MultiPlayerChunkCache : public ChunkSource
vector loadedChunkList;
- LevelChunk **cache;
+ unordered_map m_chunkMap;
+ unordered_map m_hasDataMap;
// 4J - added for multithreaded support
CRITICAL_SECTION m_csLoadCreate;
- // 4J - size of cache is defined by size of one side - must be even
- int XZSIZE;
- int XZOFFSET;
- bool *hasData;
+
+#ifdef _LARGE_WORLDS
+ bool m_isInfinite; // true when m_XZSize >= LEVEL_MAX_WIDTH (infinite world)
+#endif
+
+ // Deferred deletion: chunks removed from the map but kept alive briefly
+ // so any in-flight rebuild thread that already obtained the pointer can finish.
+ // Each entry stores {chunk, tickAtWhichToDelete}.
+ static const int DELETE_DELAY_TICKS = 20; // ~1 second at 20 tps
+ deque> m_pendingDelete;
+ int m_tickCount;
Level *level;
@@ -43,5 +54,9 @@ class MultiPlayerChunkCache : public ChunkSource
virtual TilePos *findNearestMapFeature(Level *level, const wstring &featureName, int x, int y, int z);
virtual void dataReceived(int x, int z); // 4J added
- virtual LevelChunk **getCache() { return cache; } // 4J added
+ // getCache() is no longer meaningful for infinite worlds; returns NULL
+ virtual LevelChunk **getCache() { return NULL; }
+
+ // Expose loaded chunk list for iteration (infinite-worlds replacement for coordinate scanning)
+ const vector& getLoadedChunkList() const { return loadedChunkList; }
};
\ No newline at end of file
diff --git a/Minecraft.Client/MultiPlayerLevel.cpp b/Minecraft.Client/MultiPlayerLevel.cpp
index 24b12bcd3..7e680cd96 100644
--- a/Minecraft.Client/MultiPlayerLevel.cpp
+++ b/Minecraft.Client/MultiPlayerLevel.cpp
@@ -30,9 +30,7 @@ MultiPlayerLevel::MultiPlayerLevel(ClientConnection *connection, LevelSettings *
// 4J - this this used to be called in parent ctor via a virtual fn
chunkSource = createChunkSource();
- // 4J - optimisation - keep direct reference of underlying cache here
- chunkSourceCache = chunkSource->getCache();
- chunkSourceXZSize = chunkSource->m_XZSize;
+ // chunkSourceCache/chunkSourceXZSize removed: infinite worlds use virtual dispatch
// This also used to be called in parent ctor, but can't be called until chunkSource is created. Call now if required.
if (!levelData->isInitialized())
@@ -56,10 +54,8 @@ MultiPlayerLevel::MultiPlayerLevel(ClientConnection *connection, LevelSettings *
{
this->savedDataStorage = connection->savedDataStorage;
}
- unshareCheckX = 0;
- unshareCheckZ = 0;
- compressCheckX = 0;
- compressCheckZ = 0;
+ unshareCheckIdx = 0;
+ compressCheckIdx = 0;
// 4J Added, as there are some times when we don't want to add tile updates to the updatesToReset vector
m_bEnableResetChanges = true;
@@ -156,55 +152,42 @@ void MultiPlayerLevel::tick()
// 4J - added this section. Each tick we'll check a different block, and force it to share data if it has been
// more than 2 minutes since we last wanted to unshare it. This shouldn't really ever happen, and is added
// here as a safe guard against accumulated memory leaks should a lot of chunks become unshared over time.
-
- int ls = dimension->getXZSize();
+
+ // Infinite-worlds fix: iterate loaded chunk list directly instead of scanning up to
+ // ls=1,875,000 coordinate slots.
+
+ // Unshare check: visit one loaded chunk per tick, cycling through all loaded chunks.
if( g_NetworkManager.IsHost() )
{
- if( Level::reallyHasChunk(unshareCheckX - ( ls / 2), unshareCheckZ - ( ls / 2 ) ) )
+ const vector& loaded = chunkCache->getLoadedChunkList();
+ int n = (int)loaded.size();
+ if( n > 0 )
{
- LevelChunk *lc = Level::getChunk(unshareCheckX - ( ls / 2), unshareCheckZ - ( ls / 2 ));
- if( g_NetworkManager.IsHost() )
- {
+ if( unshareCheckIdx >= n ) unshareCheckIdx = 0;
+ LevelChunk *lc = loaded[unshareCheckIdx];
+ if( lc )
lc->startSharingTilesAndData(1000 * 60 * 2);
- }
- }
-
- unshareCheckX++;
- if( unshareCheckX >= ls )
- {
- unshareCheckX = 0;
- unshareCheckZ++;
- if( unshareCheckZ >= ls )
- {
- unshareCheckZ = 0;
- }
+ unshareCheckIdx = ( unshareCheckIdx + 1 ) % n;
}
}
- // 4J added - also similar thing tosee if we can compress the lighting in any of these chunks. This is slightly different
- // as it does try to make sure that at least one chunk has something done to it.
-
- // At most loop round at least one row the chunks, so we should be able to at least find a non-empty chunk to do something with in 2.7 seconds of ticks, and process the whole thing in about 2.4 minutes.
- for( int i = 0; i < ls; i++ )
+ // 4J added - also similar thing to see if we can compress the lighting in any of these chunks.
+ // Infinite-worlds fix: step through loadedChunkList (O(1) per tick) instead of scanning
+ // a 1,875,000-wide coordinate grid cuz thats stupid.
{
- compressCheckX++;
- if( compressCheckX >= ls )
+ const vector& loaded = chunkCache->getLoadedChunkList();
+ int n = (int)loaded.size();
+ if( n > 0 )
{
- compressCheckX = 0;
- compressCheckZ++;
- if( compressCheckZ >= ls )
+ if( compressCheckIdx >= n ) compressCheckIdx = 0;
+ LevelChunk *lc = loaded[compressCheckIdx];
+ if( lc )
{
- compressCheckZ = 0;
+ lc->compressLighting();
+ lc->compressBlocks();
+ lc->compressData();
}
- }
-
- if( Level::reallyHasChunk(compressCheckX - ( ls / 2), compressCheckZ - ( ls / 2 ) ) )
- {
- LevelChunk *lc = Level::getChunk(compressCheckX - ( ls / 2), compressCheckZ - ( ls / 2 ));
- lc->compressLighting();
- lc->compressBlocks();
- lc->compressData();
- break;
+ compressCheckIdx = ( compressCheckIdx + 1 ) % n;
}
}
diff --git a/Minecraft.Client/MultiPlayerLevel.h b/Minecraft.Client/MultiPlayerLevel.h
index c07b55835..5fc143167 100644
--- a/Minecraft.Client/MultiPlayerLevel.h
+++ b/Minecraft.Client/MultiPlayerLevel.h
@@ -30,10 +30,8 @@ class MultiPlayerLevel : public Level
void enableResetChanges(bool enable) { m_bEnableResetChanges = enable; } // 4J Added
private:
- int unshareCheckX; // 4J - added
- int unshareCheckZ; // 4J - added
- int compressCheckX; // 4J - added
- int compressCheckZ; // 4J - added
+ int unshareCheckIdx; // 4J - index into loadedChunkList for unshare cycling (infinite-worlds fix)
+ int compressCheckIdx; // 4J - index into loadedChunkList for compress cycling (infinite-worlds fix)
vector connections; // 4J Stu - Made this a vector as we can have more than one local connection
MultiPlayerChunkCache *chunkCache;
Minecraft *minecraft;
diff --git a/Minecraft.Client/OrbisMedia/loc/da-DA/strings.lang b/Minecraft.Client/OrbisMedia/loc/da-DA/strings.lang
index 5995aeb93..8452dc600 100644
--- a/Minecraft.Client/OrbisMedia/loc/da-DA/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/da-DA/strings.lang
@@ -8531,6 +8531,10 @@ Du må ikke slukke for dit PlayStation®4-system, når dette ikon vises på skæ
Stor
+
+ Infinite
+
+
Klassisk
diff --git a/Minecraft.Client/OrbisMedia/loc/da-DA/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/da-DA/stringsPlatformSpecific.xml
index 2ffe896e5..58737f48d 100644
--- a/Minecraft.Client/OrbisMedia/loc/da-DA/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/da-DA/stringsPlatformSpecific.xml
@@ -175,6 +175,9 @@ Du må ikke slukke for dit PlayStation®4-system, når dette ikon vises på skæ
Stor
+
+ Infinite
+
Klassisk
diff --git a/Minecraft.Client/OrbisMedia/loc/de-DE/strings.lang b/Minecraft.Client/OrbisMedia/loc/de-DE/strings.lang
index e81b0f56f..9a3b04b1e 100644
--- a/Minecraft.Client/OrbisMedia/loc/de-DE/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/de-DE/strings.lang
@@ -8309,6 +8309,10 @@ Wenn du versuchst, mit der Testversion zu speichern, wird dir die Möglichkeit g
Groß
+
+ Infinite
+
+
Klassisch
diff --git a/Minecraft.Client/OrbisMedia/loc/de-DE/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/de-DE/stringsPlatformSpecific.xml
index 82fed1295..3d81804cb 100644
--- a/Minecraft.Client/OrbisMedia/loc/de-DE/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/de-DE/stringsPlatformSpecific.xml
@@ -175,6 +175,9 @@ Wenn du versuchst, mit der Testversion zu speichern, wird dir die Möglichkeit g
Groß
+
+ Infinite
+
Klassisch
diff --git a/Minecraft.Client/OrbisMedia/loc/el-EL/strings.lang b/Minecraft.Client/OrbisMedia/loc/el-EL/strings.lang
index 7077c1190..9b87ac135 100644
--- a/Minecraft.Client/OrbisMedia/loc/el-EL/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/el-EL/strings.lang
@@ -8527,6 +8527,10 @@
Μεγάλος
+
+ Infinite
+
+
Κλασικός
diff --git a/Minecraft.Client/OrbisMedia/loc/el-EL/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/el-EL/stringsPlatformSpecific.xml
index 5b7a4f004..28bd4e651 100644
--- a/Minecraft.Client/OrbisMedia/loc/el-EL/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/el-EL/stringsPlatformSpecific.xml
@@ -179,6 +179,9 @@
Μεγάλος
+
+ Infinite
+
Κλασικός
diff --git a/Minecraft.Client/OrbisMedia/loc/es-ES/strings.lang b/Minecraft.Client/OrbisMedia/loc/es-ES/strings.lang
index 35a4b2816..708adeee8 100644
--- a/Minecraft.Client/OrbisMedia/loc/es-ES/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/es-ES/strings.lang
@@ -8332,6 +8332,10 @@ Si intentas guardar mientras usas esta versión de prueba, tendrás la opción d
Grande
+
+ Infinite
+
+
Clásico
diff --git a/Minecraft.Client/OrbisMedia/loc/es-ES/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/es-ES/stringsPlatformSpecific.xml
index 78f7643a2..d12ff3c74 100644
--- a/Minecraft.Client/OrbisMedia/loc/es-ES/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/es-ES/stringsPlatformSpecific.xml
@@ -178,6 +178,9 @@ Si intentas guardar mientras usas esta versión de prueba, tendrás la opción d
Grande
+
+ Infinite
+
Clásico
diff --git a/Minecraft.Client/OrbisMedia/loc/fi-FI/strings.lang b/Minecraft.Client/OrbisMedia/loc/fi-FI/strings.lang
index efd2dfa90..d5a19b68d 100644
--- a/Minecraft.Client/OrbisMedia/loc/fi-FI/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/fi-FI/strings.lang
@@ -8353,6 +8353,10 @@ Jos yrität tallentaa käyttäessäsi koeversiota, sinulle tarjotaan mahdollisuu
Suuri
+
+ Infinite
+
+
Klassinen
diff --git a/Minecraft.Client/OrbisMedia/loc/fi-FI/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/fi-FI/stringsPlatformSpecific.xml
index 4189a2f84..19c1ea215 100644
--- a/Minecraft.Client/OrbisMedia/loc/fi-FI/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/fi-FI/stringsPlatformSpecific.xml
@@ -177,6 +177,9 @@ Jos yrität tallentaa käyttäessäsi koeversiota, sinulle tarjotaan mahdollisuu
Suuri
+
+ Infinite
+
Klassinen
diff --git a/Minecraft.Client/OrbisMedia/loc/fr-FR/strings.lang b/Minecraft.Client/OrbisMedia/loc/fr-FR/strings.lang
index 2403d6bc4..9fb49b018 100644
--- a/Minecraft.Client/OrbisMedia/loc/fr-FR/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/fr-FR/strings.lang
@@ -8222,6 +8222,10 @@ Si vous tentez de sauvegarder en utilisant cette version d'essai, il vous sera p
Grand
+
+ Infinite
+
+
Classique
diff --git a/Minecraft.Client/OrbisMedia/loc/fr-FR/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/fr-FR/stringsPlatformSpecific.xml
index c16d2192c..39e4c1e54 100644
--- a/Minecraft.Client/OrbisMedia/loc/fr-FR/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/fr-FR/stringsPlatformSpecific.xml
@@ -176,6 +176,9 @@ Si vous tentez de sauvegarder en utilisant cette version d'essai, il vous sera p
Grand
+
+ Infinite
+
Classique
diff --git a/Minecraft.Client/OrbisMedia/loc/it-IT/strings.lang b/Minecraft.Client/OrbisMedia/loc/it-IT/strings.lang
index e9cdcbb0d..7142f7c8c 100644
--- a/Minecraft.Client/OrbisMedia/loc/it-IT/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/it-IT/strings.lang
@@ -8327,6 +8327,10 @@ Se cerchi di salvare mentre usi la versione di prova, avrai la possibilità di a
Grande
+
+ Infinite
+
+
Classico
diff --git a/Minecraft.Client/OrbisMedia/loc/it-IT/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/it-IT/stringsPlatformSpecific.xml
index 04ef8a168..97fc44b74 100644
--- a/Minecraft.Client/OrbisMedia/loc/it-IT/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/it-IT/stringsPlatformSpecific.xml
@@ -177,6 +177,9 @@ Se cerchi di salvare mentre usi la versione di prova, avrai la possibilità di a
Grande
+
+ Infinite
+
Classico
diff --git a/Minecraft.Client/OrbisMedia/loc/ja-JP/strings.lang b/Minecraft.Client/OrbisMedia/loc/ja-JP/strings.lang
index 6fe2814ca..142aba178 100644
--- a/Minecraft.Client/OrbisMedia/loc/ja-JP/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/ja-JP/strings.lang
@@ -8241,6 +8241,10 @@ OK を選択すると、この世界でのプレイを終了します
大
+
+ Infinite
+
+
クラシック
diff --git a/Minecraft.Client/OrbisMedia/loc/ja-JP/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/ja-JP/stringsPlatformSpecific.xml
index fb0d67815..f7cf6ea7c 100644
--- a/Minecraft.Client/OrbisMedia/loc/ja-JP/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/ja-JP/stringsPlatformSpecific.xml
@@ -176,6 +176,9 @@
大
+
+ Infinite
+
クラシック
diff --git a/Minecraft.Client/OrbisMedia/loc/ko-KR/strings.lang b/Minecraft.Client/OrbisMedia/loc/ko-KR/strings.lang
index cff18082c..73f423096 100644
--- a/Minecraft.Client/OrbisMedia/loc/ko-KR/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/ko-KR/strings.lang
@@ -8359,6 +8359,10 @@ Ender에 들어서면 친구가 그들의 지도에서 요새 내부에 있는 E
대
+
+ Infinite
+
+
클래식
diff --git a/Minecraft.Client/OrbisMedia/loc/ko-KR/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/ko-KR/stringsPlatformSpecific.xml
index cf13c7e8d..6abe9c23d 100644
--- a/Minecraft.Client/OrbisMedia/loc/ko-KR/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/ko-KR/stringsPlatformSpecific.xml
@@ -178,6 +178,9 @@
대
+
+ Infinite
+
클래식
diff --git a/Minecraft.Client/OrbisMedia/loc/la-LAS/strings.lang b/Minecraft.Client/OrbisMedia/loc/la-LAS/strings.lang
index 999357022..a111d4e5e 100644
--- a/Minecraft.Client/OrbisMedia/loc/la-LAS/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/la-LAS/strings.lang
@@ -8565,6 +8565,10 @@ Si intentas guardar mientras usas la versión de prueba, se te dará la opción
Grande
+
+ Infinite
+
+
Clásico
diff --git a/Minecraft.Client/OrbisMedia/loc/la-LAS/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/la-LAS/stringsPlatformSpecific.xml
index 8a858a9d9..6954d8bb2 100644
--- a/Minecraft.Client/OrbisMedia/loc/la-LAS/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/la-LAS/stringsPlatformSpecific.xml
@@ -178,6 +178,9 @@ Si intentas guardar mientras usas la versión de prueba, se te dará la opción
Grande
+
+ Infinite
+
Clásico
diff --git a/Minecraft.Client/OrbisMedia/loc/nl-NL/strings.lang b/Minecraft.Client/OrbisMedia/loc/nl-NL/strings.lang
index b905d9411..f5c1f5912 100644
--- a/Minecraft.Client/OrbisMedia/loc/nl-NL/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/nl-NL/strings.lang
@@ -8548,6 +8548,10 @@ Als je probeert op te slaan tijdens het gebruik van de testversie, krijg je de m
Groot
+
+ Infinite
+
+
Klassiek
diff --git a/Minecraft.Client/OrbisMedia/loc/nl-NL/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/nl-NL/stringsPlatformSpecific.xml
index 56e9b159c..b3dc9970c 100644
--- a/Minecraft.Client/OrbisMedia/loc/nl-NL/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/nl-NL/stringsPlatformSpecific.xml
@@ -177,6 +177,9 @@ Als je probeert op te slaan tijdens het gebruik van de testversie, krijg je de m
Groot
+
+ Infinite
+
Klassiek
diff --git a/Minecraft.Client/OrbisMedia/loc/no-NO/strings.lang b/Minecraft.Client/OrbisMedia/loc/no-NO/strings.lang
index 4da9c2d43..0681a040d 100644
--- a/Minecraft.Client/OrbisMedia/loc/no-NO/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/no-NO/strings.lang
@@ -8526,6 +8526,10 @@ Hvis du prøver å lagre mens du bruker prøveversjonen, vil du få spørsmål o
Stor
+
+ Infinite
+
+
Klassisk
diff --git a/Minecraft.Client/OrbisMedia/loc/no-NO/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/no-NO/stringsPlatformSpecific.xml
index b276e8295..2901c857f 100644
--- a/Minecraft.Client/OrbisMedia/loc/no-NO/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/no-NO/stringsPlatformSpecific.xml
@@ -178,6 +178,9 @@ Hvis du prøver å lagre mens du bruker prøveversjonen, vil du få spørsmål o
Stor
+
+ Infinite
+
Klassisk
diff --git a/Minecraft.Client/OrbisMedia/loc/pl-PL/strings.lang b/Minecraft.Client/OrbisMedia/loc/pl-PL/strings.lang
index d78b89218..21ef32c55 100644
--- a/Minecraft.Client/OrbisMedia/loc/pl-PL/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/pl-PL/strings.lang
@@ -8427,6 +8427,10 @@ Jeżeli spróbujesz zapisać postęp podczas korzystania z wersji próbnej, zost
Duży
+
+ Infinite
+
+
Klasyczny
diff --git a/Minecraft.Client/OrbisMedia/loc/pl-PL/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/pl-PL/stringsPlatformSpecific.xml
index 4026811b8..7654403fc 100644
--- a/Minecraft.Client/OrbisMedia/loc/pl-PL/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/pl-PL/stringsPlatformSpecific.xml
@@ -177,6 +177,9 @@ Jeżeli spróbujesz zapisać postęp podczas korzystania z wersji próbnej, zost
Duży
+
+ Infinite
+
Klasyczny
diff --git a/Minecraft.Client/OrbisMedia/loc/pt-BR/strings.lang b/Minecraft.Client/OrbisMedia/loc/pt-BR/strings.lang
index 29e249b2a..9b7ced096 100644
--- a/Minecraft.Client/OrbisMedia/loc/pt-BR/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/pt-BR/strings.lang
@@ -8555,6 +8555,10 @@ Se tentar salvar enquanto estiver usando a versão de avaliação, a versão com
Grande
+
+ Infinite
+
+
Clássico
diff --git a/Minecraft.Client/OrbisMedia/loc/pt-BR/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/pt-BR/stringsPlatformSpecific.xml
index 31f6f527a..868caf9b3 100644
--- a/Minecraft.Client/OrbisMedia/loc/pt-BR/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/pt-BR/stringsPlatformSpecific.xml
@@ -177,6 +177,9 @@ Se tentar salvar enquanto estiver usando a versão de avaliação, a versão com
Grande
+
+ Infinite
+
Clássico
diff --git a/Minecraft.Client/OrbisMedia/loc/pt-PT/strings.lang b/Minecraft.Client/OrbisMedia/loc/pt-PT/strings.lang
index b5d7ae2fe..48172191d 100644
--- a/Minecraft.Client/OrbisMedia/loc/pt-PT/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/pt-PT/strings.lang
@@ -8355,6 +8355,10 @@ Se tentares gravar enquanto usas a versão de avaliação, ser-te-á dada a opç
Grande
+
+ Infinite
+
+
Clássico
diff --git a/Minecraft.Client/OrbisMedia/loc/pt-PT/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/pt-PT/stringsPlatformSpecific.xml
index b21ef2f0e..fe38ce582 100644
--- a/Minecraft.Client/OrbisMedia/loc/pt-PT/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/pt-PT/stringsPlatformSpecific.xml
@@ -178,6 +178,9 @@ Se tentares gravar enquanto usas a versão de avaliação, ser-te-á dada a opç
Grande
+
+ Infinite
+
Clássico
diff --git a/Minecraft.Client/OrbisMedia/loc/ru-RU/strings.lang b/Minecraft.Client/OrbisMedia/loc/ru-RU/strings.lang
index 19c7b06b5..0da42696d 100644
--- a/Minecraft.Client/OrbisMedia/loc/ru-RU/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/ru-RU/strings.lang
@@ -8556,6 +8556,10 @@ Minecraft на системе PlayStation®4 по умолчанию являе
Большой
+
+ Infinite
+
+
Классический
diff --git a/Minecraft.Client/OrbisMedia/loc/ru-RU/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/ru-RU/stringsPlatformSpecific.xml
index 54232a414..e0134b4b5 100644
--- a/Minecraft.Client/OrbisMedia/loc/ru-RU/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/ru-RU/stringsPlatformSpecific.xml
@@ -178,6 +178,9 @@ Minecraft на системе PlayStation®4 по умолчанию являе
Большой
+
+ Infinite
+
Классический
diff --git a/Minecraft.Client/OrbisMedia/loc/strings.lang b/Minecraft.Client/OrbisMedia/loc/strings.lang
index 58bfad3c9..e771ea483 100644
--- a/Minecraft.Client/OrbisMedia/loc/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/strings.lang
@@ -8571,6 +8571,10 @@ If you try to save while using the trial version, you will be given the option t
Large
+
+ Infinite
+
+
Classic
diff --git a/Minecraft.Client/OrbisMedia/loc/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/stringsPlatformSpecific.xml
index 813eaf9ea..28b6ff3b2 100644
--- a/Minecraft.Client/OrbisMedia/loc/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/stringsPlatformSpecific.xml
@@ -177,6 +177,9 @@ If you try to save while using the trial version, you will be given the option t
Large
+
+ Infinite
+
Classic
diff --git a/Minecraft.Client/OrbisMedia/loc/sv-SV/strings.lang b/Minecraft.Client/OrbisMedia/loc/sv-SV/strings.lang
index 729f773c6..1d8cad6e1 100644
--- a/Minecraft.Client/OrbisMedia/loc/sv-SV/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/sv-SV/strings.lang
@@ -8533,6 +8533,10 @@ Om du försöker spara medan du använder demoversionen kommer du att bli tillfr
Stor
+
+ Infinite
+
+
Klassisk
diff --git a/Minecraft.Client/OrbisMedia/loc/sv-SV/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/sv-SV/stringsPlatformSpecific.xml
index f7771e791..5a5ef256d 100644
--- a/Minecraft.Client/OrbisMedia/loc/sv-SV/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/sv-SV/stringsPlatformSpecific.xml
@@ -177,6 +177,9 @@ Om du försöker spara medan du använder demoversionen kommer du att bli tillfr
Stor
+
+ Infinite
+
Klassisk
diff --git a/Minecraft.Client/OrbisMedia/loc/tr-TR/strings.lang b/Minecraft.Client/OrbisMedia/loc/tr-TR/strings.lang
index 32fcab00f..d0e13d12b 100644
--- a/Minecraft.Client/OrbisMedia/loc/tr-TR/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/tr-TR/strings.lang
@@ -8562,6 +8562,10 @@ Deneme sürümünü kullanırken oyunu kaydetmeye çalışırsan, tam sürümü
Büyük
+
+ Infinite
+
+
Klasik
diff --git a/Minecraft.Client/OrbisMedia/loc/tr-TR/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/tr-TR/stringsPlatformSpecific.xml
index 00c1353fa..3f19c0bbc 100644
--- a/Minecraft.Client/OrbisMedia/loc/tr-TR/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/tr-TR/stringsPlatformSpecific.xml
@@ -182,6 +182,9 @@ Deneme sürümünü kullanırken oyunu kaydetmeye çalışırsan, tam sürümü
Büyük
+
+ Infinite
+
Klasik
diff --git a/Minecraft.Client/OrbisMedia/loc/zh-CHT/strings.lang b/Minecraft.Client/OrbisMedia/loc/zh-CHT/strings.lang
index 173ec6b2a..703f0fb5f 100644
--- a/Minecraft.Client/OrbisMedia/loc/zh-CHT/strings.lang
+++ b/Minecraft.Client/OrbisMedia/loc/zh-CHT/strings.lang
@@ -8380,6 +8380,10 @@ Minecraft 是一款可讓您放置方塊來建造夢想世界的遊戲。不過
大
+
+ Infinite
+
+
傳統
diff --git a/Minecraft.Client/OrbisMedia/loc/zh-CHT/stringsPlatformSpecific.xml b/Minecraft.Client/OrbisMedia/loc/zh-CHT/stringsPlatformSpecific.xml
index 64eea7127..c464cbca6 100644
--- a/Minecraft.Client/OrbisMedia/loc/zh-CHT/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/OrbisMedia/loc/zh-CHT/stringsPlatformSpecific.xml
@@ -179,6 +179,9 @@
大
+
+ Infinite
+
傳統
diff --git a/Minecraft.Client/OrbisMedia/strings.h b/Minecraft.Client/OrbisMedia/strings.h
index 07e0ebcae..9549868a6 100644
--- a/Minecraft.Client/OrbisMedia/strings.h
+++ b/Minecraft.Client/OrbisMedia/strings.h
@@ -1990,3 +1990,4 @@
#define IDS_YOU_DIED 1988
#define IDS_YOU_HAVE 1989
#define IDS_ZOMBIE 1990
+#define IDS_WORLD_SIZE_TITLE_INFINITE 1991
diff --git a/Minecraft.Client/PlayerChunkMap.cpp b/Minecraft.Client/PlayerChunkMap.cpp
index aedd5a9d7..b21b28f4d 100644
--- a/Minecraft.Client/PlayerChunkMap.cpp
+++ b/Minecraft.Client/PlayerChunkMap.cpp
@@ -586,6 +586,11 @@ void PlayerChunkMap::add(shared_ptr player)
player->lastMoveX = player->x;
player->lastMoveZ = player->z;
+#ifdef _LARGE_WORLDS
+ int half = level->getLevelData()->getXZSize() / 2;
+ bool isInfinite = isInfiniteWorld(level->getLevelData()->getXZSize());
+#endif
+
// for (int x = xc - radius; x <= xc + radius; x++)
// for (int z = zc - radius; z <= zc + radius; z++) {
// getChunk(x, z, true).add(player);
@@ -598,6 +603,9 @@ void PlayerChunkMap::add(shared_ptr player)
int dz = 0;
// Origin
+#ifdef _LARGE_WORLDS
+ if (isInfinite || (xc >= -half && xc < half && zc >= -half && zc < half))
+#endif
getChunk(xc, zc, true)->add(player, false);
// 4J Added so we send an area packet rather than one visibility packet per chunk
@@ -625,6 +633,9 @@ void PlayerChunkMap::add(shared_ptr player)
if( targetZ > maxZ ) maxZ = targetZ;
if( targetZ < minZ ) minZ = targetZ;
+#ifdef _LARGE_WORLDS
+ if (isInfinite || (targetX >= -half && targetX < half && targetZ >= -half && targetZ < half))
+#endif
getChunk(targetX, targetZ, true)->add(player, false);
}
}
@@ -645,10 +656,24 @@ void PlayerChunkMap::add(shared_ptr player)
if( targetZ > maxZ ) maxZ = targetZ;
if( targetZ < minZ ) minZ = targetZ;
+#ifdef _LARGE_WORLDS
+ if (isInfinite || (targetX >= -half && targetX < half && targetZ >= -half && targetZ < half))
+#endif
getChunk(targetX, targetZ, true)->add(player, false);
}
// CraftBukkit end
+#ifdef _LARGE_WORLDS
+ // Clamp visibility area to world bounds for finite worlds
+ if (!isInfinite)
+ {
+ if (minX < -half) minX = -half;
+ if (maxX >= half) maxX = half - 1;
+ if (minZ < -half) minZ = -half;
+ if (maxZ >= half) maxZ = half - 1;
+ }
+#endif
+
player->connection->send( shared_ptr( new ChunkVisibilityAreaPacket(minX, maxX, minZ, maxZ) ) );
#ifdef _LARGE_WORLDS
@@ -719,12 +744,20 @@ void PlayerChunkMap::move(shared_ptr player)
int zd = zc - last_zc;
if (xd == 0 && zd == 0) return;
+#ifdef _LARGE_WORLDS
+ int half = level->getLevelData()->getXZSize() / 2;
+ bool isInfinite = isInfiniteWorld(level->getLevelData()->getXZSize());
+#endif
+
for (int x = xc - radius; x <= xc + radius; x++)
for (int z = zc - radius; z <= zc + radius; z++)
{
if (!chunkInRange(x, z, last_xc, last_zc))
{
// 4J - changed from separate getChunk & add so we can wrap these operations up and queue
+#ifdef _LARGE_WORLDS
+ if (isInfinite || (x >= -half && x < half && z >= -half && z < half))
+#endif
getChunkAndAddPlayer(x, z, player);
}
@@ -772,6 +805,11 @@ void PlayerChunkMap::setRadius(int newRadius)
{
if( radius != newRadius )
{
+#ifdef _LARGE_WORLDS
+ int half = level->getLevelData()->getXZSize() / 2;
+ bool isInfinite = isInfiniteWorld(level->getLevelData()->getXZSize());
+#endif
+
PlayerList* players = level->getServer()->getPlayerList();
for( int i = 0;i < players->players.size();i += 1 )
{
@@ -787,6 +825,9 @@ void PlayerChunkMap::setRadius(int newRadius)
// check if this chunk is outside the old radius area
if ( x < xc - radius || x > xc + radius || z < zc - radius || z > zc + radius )
{
+#ifdef _LARGE_WORLDS
+ if (isInfinite || (x >= -half && x < half && z >= -half && z < half))
+#endif
getChunkAndAddPlayer(x, z, player);
}
}
diff --git a/Minecraft.Client/ServerChunkCache.cpp b/Minecraft.Client/ServerChunkCache.cpp
index 1fe1a86f7..f57fb2dd9 100644
--- a/Minecraft.Client/ServerChunkCache.cpp
+++ b/Minecraft.Client/ServerChunkCache.cpp
@@ -14,9 +14,6 @@
ServerChunkCache::ServerChunkCache(ServerLevel *level, ChunkStorage *storage, ChunkSource *source)
{
- XZSIZE = source->m_XZSize; // 4J Added
- XZOFFSET = XZSIZE/2; // 4J Added
-
autoCreate = false; // 4J added
emptyChunk = new EmptyLevelChunk(level, byteArray( Level::CHUNK_TILE_COUNT ), 0, 0);
@@ -24,14 +21,11 @@ ServerChunkCache::ServerChunkCache(ServerLevel *level, ChunkStorage *storage, Ch
this->level = level;
this->storage = storage;
this->source = source;
+ // For infinite worlds, m_XZSize is no longer used for cache sizing.
+ // Keep it at a large value so code that reads it (e.g. dimension getXZSize) still works.
this->m_XZSize = source->m_XZSize;
-
- this->cache = new LevelChunk *[XZSIZE * XZSIZE];
- memset(this->cache, 0, XZSIZE * XZSIZE * sizeof(LevelChunk *));
-
#ifdef _LARGE_WORLDS
- m_unloadedCache = new LevelChunk *[XZSIZE * XZSIZE];
- memset(m_unloadedCache, 0, XZSIZE * XZSIZE * sizeof(LevelChunk *));
+ this->m_isInfinite = isInfiniteWorld(this->m_XZSize);
#endif
InitializeCriticalSectionAndSpinCount(&m_csLoadCreate,4000);
@@ -41,15 +35,12 @@ ServerChunkCache::ServerChunkCache(ServerLevel *level, ChunkStorage *storage, Ch
ServerChunkCache::~ServerChunkCache()
{
delete emptyChunk;
- delete cache;
delete source;
#ifdef _LARGE_WORLDS
- for(unsigned int i = 0; i < XZSIZE * XZSIZE; ++i)
- {
- delete m_unloadedCache[i];
- }
- delete m_unloadedCache;
+ for (auto &kv : m_unloadedMap)
+ delete kv.second;
+ m_unloadedMap.clear();
#endif
AUTO_VAR(itEnd, m_loadedChunkList.end());
@@ -60,18 +51,20 @@ ServerChunkCache::~ServerChunkCache()
bool ServerChunkCache::hasChunk(int x, int z)
{
- int ix = x + XZOFFSET;
- int iz = z + XZOFFSET;
- // Check we're in range of the stored level
- // 4J Stu - Request for chunks outside the range always return an emptyChunk, so just return true here to say we have it
- // If we return false entities less than 2 chunks from the edge do not tick properly due to them requiring a certain radius
- // of chunks around them when they tick
- if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return true;
- if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return true;
- int idx = ix * XZSIZE + iz;
- LevelChunk *lc = cache[idx];
- if( lc == NULL ) return false;
- return true;
+#ifdef _LARGE_WORLDS
+ // For finite worlds, out-of-bounds coordinates are treated as "exists" (sea/empty edge)
+ if (!m_isInfinite)
+ {
+ int half = m_XZSize / 2;
+ if (x < -half || x >= half || z < -half || z >= half) return true;
+ }
+#endif
+ // Check if chunk is loaded in the map
+ EnterCriticalSection(&m_csLoadCreate);
+ auto it = m_chunkMap.find(chunkKey(x, z));
+ bool result = (it != m_chunkMap.end() && it->second != NULL);
+ LeaveCriticalSection(&m_csLoadCreate);
+ return result;
}
vector *ServerChunkCache::getLoadedChunkList()
@@ -81,40 +74,12 @@ vector *ServerChunkCache::getLoadedChunkList()
void ServerChunkCache::drop(int x, int z)
{
- // 4J - we're not dropping things anymore now that we have a fixed sized cache
#ifdef _LARGE_WORLDS
-
- bool canDrop = false;
-// if (level->dimension->mayRespawn())
-// {
-// Pos *spawnPos = level->getSharedSpawnPos();
-// int xd = x * 16 + 8 - spawnPos->x;
-// int zd = z * 16 + 8 - spawnPos->z;
-// delete spawnPos;
-// int r = 128;
-// if (xd < -r || xd > r || zd < -r || zd > r)
-// {
-// canDrop = true;
-//}
-// }
-// else
+ int64_t key = chunkKey(x, z);
+ auto it = m_chunkMap.find(key);
+ if (it != m_chunkMap.end() && it->second != NULL)
{
- canDrop = true;
- }
- if(canDrop)
- {
- int ix = x + XZOFFSET;
- int iz = z + XZOFFSET;
- // Check we're in range of the stored level
- if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return;
- if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return;
- int idx = ix * XZSIZE + iz;
- LevelChunk *chunk = cache[idx];
-
- if(chunk)
- {
- m_toDrop.push_back(chunk);
- }
+ m_toDrop.push_back(it->second);
}
#endif
}
@@ -137,108 +102,95 @@ LevelChunk *ServerChunkCache::create(int x, int z)
LevelChunk *ServerChunkCache::create(int x, int z, bool asyncPostProcess) // 4J - added extra parameter
{
- int ix = x + XZOFFSET;
- int iz = z + XZOFFSET;
- // Check we're in range of the stored level
- if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return emptyChunk;
- if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return emptyChunk;
- int idx = ix * XZSIZE + iz;
+#ifdef _LARGE_WORLDS
+ // For finite worlds, refuse to create chunks outside the world boundary
+ if (!m_isInfinite)
+ {
+ int half = m_XZSize / 2;
+ if (x < -half || x >= half || z < -half || z >= half) return emptyChunk;
+ }
+#endif
+
+ int64_t key = chunkKey(x, z);
- LevelChunk *chunk = cache[idx];
- LevelChunk *lastChunk = chunk;
+ EnterCriticalSection(&m_csLoadCreate);
- if( ( chunk == NULL ) || ( chunk->x != x ) || ( chunk->z != z ) )
+ // Check under lock
{
- EnterCriticalSection(&m_csLoadCreate);
- chunk = load(x, z);
- if (chunk == NULL)
+ auto it = m_chunkMap.find(key);
+ if (it != m_chunkMap.end() && it->second != NULL)
{
- if (source == NULL)
- {
- chunk = emptyChunk;
- }
- else
- {
- chunk = source->getChunk(x, z);
- }
+ LevelChunk *existing = it->second;
+ LeaveCriticalSection(&m_csLoadCreate);
+ return existing;
+ }
+ }
+
+ LevelChunk *chunk = load(x, z);
+ if (chunk == NULL)
+ {
+ if (source == NULL)
+ {
+ chunk = emptyChunk;
}
- if (chunk != NULL)
+ else
{
- chunk->load();
- }
+ chunk = source->getChunk(x, z);
+ }
+ }
+ if (chunk != NULL)
+ {
+ chunk->load();
+ }
- LeaveCriticalSection(&m_csLoadCreate);
+ m_chunkMap[key] = chunk;
-#if ( defined _WIN64 || defined __LP64__ )
- if( InterlockedCompareExchangeRelease64((LONG64 *)&cache[idx],(LONG64)chunk,(LONG64)lastChunk) == (LONG64)lastChunk )
-#else
- if( InterlockedCompareExchangeRelease((LONG *)&cache[idx],(LONG)chunk,(LONG)lastChunk) == (LONG)lastChunk )
-#endif // _DURANGO
- {
- // Successfully updated the cache
- EnterCriticalSection(&m_csLoadCreate);
- // 4J - added - this will run a recalcHeightmap if source is a randomlevelsource, which has been split out from source::getChunk so that
- // we are doing it after the chunk has been added to the cache - otherwise a lot of the lighting fails as lights aren't added if the chunk
- // they are in fail ServerChunkCache::hasChunk.
- source->lightChunk(chunk);
-
- updatePostProcessFlags( x, z );
-
- m_loadedChunkList.push_back(chunk);
-
- // 4J - If post-processing is to be async, then let the server know about requests rather than processing directly here. Note that
- // these hasChunk() checks appear to be incorrect - the chunks checked by these map out as:
- //
- // 1. 2. 3. 4.
- // oxx xxo ooo ooo
- // oPx Poo oox xoo
- // ooo ooo oPx Pxo
- //
- // where P marks the chunk that is being considered for postprocessing, and x marks chunks that needs to be loaded. It would seem that the
- // chunks which need to be loaded should stay the same relative to the chunk to be processed, but the hasChunk checks in 3 cases check again
- // the chunk which is to be processed itself rather than (what I presume to be) the correct position.
- // Don't think we should change in case it alters level creation.
-
- if( asyncPostProcess )
- {
- // 4J Stu - TODO This should also be calling the same code as chunk->checkPostProcess, but then we cannot guarantee we are in the server add the post-process request
- if ( ( (chunk->terrainPopulated & LevelChunk::sTerrainPopulatedFromHere) == 0) && hasChunk(x + 1, z + 1) && hasChunk(x, z + 1) && hasChunk(x + 1, z)) MinecraftServer::getInstance()->addPostProcessRequest(this, x, z);
- if (hasChunk(x - 1, z) && ((getChunk(x - 1, z)->terrainPopulated & LevelChunk::sTerrainPopulatedFromHere ) == 0 ) && hasChunk(x - 1, z + 1) && hasChunk(x, z + 1) && hasChunk(x - 1, z)) MinecraftServer::getInstance()->addPostProcessRequest(this, x - 1, z);
- if (hasChunk(x, z - 1) && ((getChunk(x, z - 1)->terrainPopulated & LevelChunk::sTerrainPopulatedFromHere ) == 0 ) && hasChunk(x + 1, z - 1) && hasChunk(x, z - 1) && hasChunk(x + 1, z)) MinecraftServer::getInstance()->addPostProcessRequest(this, x, z - 1);
- if (hasChunk(x - 1, z - 1) && ((getChunk(x - 1, z - 1)->terrainPopulated & LevelChunk::sTerrainPopulatedFromHere ) == 0 ) && hasChunk(x - 1, z - 1) && hasChunk(x, z - 1) && hasChunk(x - 1, z)) MinecraftServer::getInstance()->addPostProcessRequest(this, x - 1, z - 1);
- }
- else
- {
- chunk->checkPostProcess(this, this, x, z);
- }
+ // 4J - added - this will run a recalcHeightmap if source is a randomlevelsource, which has been split out from source::getChunk so that
+ // we are doing it after the chunk has been added to the cache - otherwise a lot of the lighting fails as lights aren't added if the chunk
+ // they are in fail ServerChunkCache::hasChunk.
+ source->lightChunk(chunk);
- // 4J - Now try and fix up any chests that were saved pre-1.8.2. We don't want to do this to this particular chunk as we don't know if all its neighbours are loaded yet, and we
- // need the neighbours to be able to work out the facing direction for the chests. Therefore process any neighbouring chunk that loading this chunk would be the last neighbour for.
- // 5 cases illustrated below, where P is the chunk to be processed, T is this chunk, and x are other chunks that need to be checked for being present
+ // Prevent cascading chunk creation: when isFindingSpawn or autoCreate is true,
+ // getChunk() auto-creates missing chunks. Post-processing and updatePostProcessFlags
+ // call getChunk() for neighbors, which would recursively create() those neighbors,
+ // whose post-processing creates more neighbors, flood-filling the entire world.
+ // Temporarily disable auto-creation so these internal calls return emptyChunk
+ // for missing neighbors instead of cascading.
+ bool savedFindingSpawn = level->isFindingSpawn;
+ bool savedAutoCreate = autoCreate;
+ level->isFindingSpawn = false;
+ autoCreate = false;
- // 1. 2. 3. 4. 5.
- // ooooo ooxoo ooooo ooooo ooooo
- // oxooo oxPxo oooxo ooooo ooxoo
- // xPToo ooToo ooTPx ooToo oxPxo (in 5th case P and T are same)
- // oxooo ooooo oooxo oxPxo ooxoo
- // ooooo ooooo ooooo ooxoo ooooo
+ updatePostProcessFlags( x, z );
- if( hasChunk( x - 1, z ) && hasChunk( x - 2, z ) && hasChunk( x - 1, z + 1 ) && hasChunk( x - 1, z - 1 ) ) chunk->checkChests( this, x - 1, z );
- if( hasChunk( x, z + 1) && hasChunk( x , z + 2 ) && hasChunk( x - 1, z + 1 ) && hasChunk( x + 1, z + 1 ) ) chunk->checkChests( this, x, z + 1);
- if( hasChunk( x + 1, z ) && hasChunk( x + 2, z ) && hasChunk( x + 1, z + 1 ) && hasChunk( x + 1, z - 1 ) ) chunk->checkChests( this, x + 1, z );
- if( hasChunk( x, z - 1) && hasChunk( x , z - 2 ) && hasChunk( x - 1, z - 1 ) && hasChunk( x + 1, z - 1 ) ) chunk->checkChests( this, x, z - 1);
- if( hasChunk( x - 1, z ) && hasChunk( x + 1, z ) && hasChunk ( x, z - 1 ) && hasChunk( x, z + 1 ) ) chunk->checkChests( this, x, z );
+ m_loadedChunkList.push_back(chunk);
- LeaveCriticalSection(&m_csLoadCreate);
- }
- else
- {
- // Something else must have updated the cache. Return that chunk and discard this one
- chunk->unload(true);
- delete chunk;
- return cache[idx];
- }
- }
+ // 4J - If post-processing is to be async, then let the server know about requests rather than processing directly here.
+ if( asyncPostProcess )
+ {
+ // 4J Stu - TODO This should also be calling the same code as chunk->checkPostProcess, but then we cannot guarantee we are in the server add the post-process request
+ if ( ( (chunk->terrainPopulated & LevelChunk::sTerrainPopulatedFromHere) == 0) && hasChunk(x + 1, z + 1) && hasChunk(x, z + 1) && hasChunk(x + 1, z)) MinecraftServer::getInstance()->addPostProcessRequest(this, x, z);
+ if (hasChunk(x - 1, z) && ((getChunk(x - 1, z)->terrainPopulated & LevelChunk::sTerrainPopulatedFromHere ) == 0 ) && hasChunk(x - 1, z + 1) && hasChunk(x, z + 1) && hasChunk(x - 1, z)) MinecraftServer::getInstance()->addPostProcessRequest(this, x - 1, z);
+ if (hasChunk(x, z - 1) && ((getChunk(x, z - 1)->terrainPopulated & LevelChunk::sTerrainPopulatedFromHere ) == 0 ) && hasChunk(x + 1, z - 1) && hasChunk(x, z - 1) && hasChunk(x + 1, z)) MinecraftServer::getInstance()->addPostProcessRequest(this, x, z - 1);
+ if (hasChunk(x - 1, z - 1) && ((getChunk(x - 1, z - 1)->terrainPopulated & LevelChunk::sTerrainPopulatedFromHere ) == 0 ) && hasChunk(x - 1, z - 1) && hasChunk(x, z - 1) && hasChunk(x - 1, z)) MinecraftServer::getInstance()->addPostProcessRequest(this, x - 1, z - 1);
+ }
+ else
+ {
+ chunk->checkPostProcess(this, this, x, z);
+ }
+
+ // 4J - Now try and fix up any chests that were saved pre-1.8.2.
+ if( hasChunk( x - 1, z ) && hasChunk( x - 2, z ) && hasChunk( x - 1, z + 1 ) && hasChunk( x - 1, z - 1 ) ) chunk->checkChests( this, x - 1, z );
+ if( hasChunk( x, z + 1) && hasChunk( x , z + 2 ) && hasChunk( x - 1, z + 1 ) && hasChunk( x + 1, z + 1 ) ) chunk->checkChests( this, x, z + 1);
+ if( hasChunk( x + 1, z ) && hasChunk( x + 2, z ) && hasChunk( x + 1, z + 1 ) && hasChunk( x + 1, z - 1 ) ) chunk->checkChests( this, x + 1, z );
+ if( hasChunk( x, z - 1) && hasChunk( x , z - 2 ) && hasChunk( x - 1, z - 1 ) && hasChunk( x + 1, z - 1 ) ) chunk->checkChests( this, x, z - 1);
+ if( hasChunk( x - 1, z ) && hasChunk( x + 1, z ) && hasChunk ( x, z - 1 ) && hasChunk( x, z + 1 ) ) chunk->checkChests( this, x, z );
+
+ // Restore auto-creation flags
+ level->isFindingSpawn = savedFindingSpawn;
+ autoCreate = savedAutoCreate;
+
+ LeaveCriticalSection(&m_csLoadCreate);
#ifdef __PS3__
Sleep(1);
@@ -251,24 +203,23 @@ LevelChunk *ServerChunkCache::create(int x, int z, bool asyncPostProcess) // 4J
// This is used when sharing server chunk data on the main thread
LevelChunk *ServerChunkCache::getChunk(int x, int z)
{
- int ix = x + XZOFFSET;
- int iz = z + XZOFFSET;
- // Check we're in range of the stored level
- if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return emptyChunk;
- if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return emptyChunk;
- int idx = ix * XZSIZE + iz;
-
- LevelChunk *lc = cache[idx];
- if( lc )
+#ifdef _LARGE_WORLDS
+ // For finite worlds, out-of-bounds coordinates return the empty chunk
+ if (!m_isInfinite)
{
- return lc;
+ int half = m_XZSize / 2;
+ if (x < -half || x >= half || z < -half || z >= half) return emptyChunk;
}
+#endif
- if( level->isFindingSpawn || autoCreate )
- {
- return create(x, z);
- }
+ EnterCriticalSection(&m_csLoadCreate);
+ auto it = m_chunkMap.find(chunkKey(x, z));
+ LevelChunk *chunk = (it != m_chunkMap.end() && it->second != NULL) ? it->second : NULL;
+ LeaveCriticalSection(&m_csLoadCreate);
+ if (chunk != NULL) return chunk;
+ if (level->isFindingSpawn || autoCreate)
+ return create(x, z);
return emptyChunk;
}
@@ -279,29 +230,28 @@ LevelChunk *ServerChunkCache::getChunk(int x, int z)
// As such it is really important that we don't return emptyChunk in these situations, when we actually still have the block/data/lighting in the unloaded cache
LevelChunk *ServerChunkCache::getChunkLoadedOrUnloaded(int x, int z)
{
- int ix = x + XZOFFSET;
- int iz = z + XZOFFSET;
- // Check we're in range of the stored level
- if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return emptyChunk;
- if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return emptyChunk;
- int idx = ix * XZSIZE + iz;
-
- LevelChunk *lc = cache[idx];
- if( lc )
+ int64_t key = chunkKey(x, z);
+
+ EnterCriticalSection(&m_csLoadCreate);
+ auto it = m_chunkMap.find(key);
+ if (it != m_chunkMap.end() && it->second != NULL)
{
- return lc;
+ LevelChunk *chunk = it->second;
+ LeaveCriticalSection(&m_csLoadCreate);
+ return chunk;
}
- lc = m_unloadedCache[idx];
- if( lc )
+ auto it2 = m_unloadedMap.find(key);
+ if (it2 != m_unloadedMap.end() && it2->second != NULL)
{
- return lc;
-}
+ LevelChunk *chunk = it2->second;
+ LeaveCriticalSection(&m_csLoadCreate);
+ return chunk;
+ }
+ LeaveCriticalSection(&m_csLoadCreate);
if( level->isFindingSpawn || autoCreate )
- {
return create(x, z);
- }
return emptyChunk;
}
@@ -311,7 +261,7 @@ LevelChunk *ServerChunkCache::getChunkLoadedOrUnloaded(int x, int z)
#ifdef _LARGE_WORLDS
void ServerChunkCache::dontDrop(int x, int z)
{
- LevelChunk *chunk = getChunk(x,z);
+ LevelChunk *chunk = getChunk(x, z);
m_toDrop.erase(std::remove(m_toDrop.begin(), m_toDrop.end(), chunk), m_toDrop.end());
}
#endif
@@ -323,12 +273,15 @@ LevelChunk *ServerChunkCache::load(int x, int z)
LevelChunk *levelChunk = NULL;
#ifdef _LARGE_WORLDS
- int ix = x + XZOFFSET;
- int iz = z + XZOFFSET;
- int idx = ix * XZSIZE + iz;
- levelChunk = m_unloadedCache[idx];
- m_unloadedCache[idx] = NULL;
- if(levelChunk == NULL)
+ // Check the in-memory unloaded cache first before going to disk
+ int64_t key = chunkKey(x, z);
+ auto it = m_unloadedMap.find(key);
+ if (it != m_unloadedMap.end())
+ {
+ levelChunk = it->second;
+ m_unloadedMap.erase(it);
+ }
+ if (levelChunk == NULL)
#endif
{
levelChunk = storage->load(level, x, z);
@@ -468,6 +421,15 @@ void ServerChunkCache::flagPostProcessComplete(short flag, int x, int z)
void ServerChunkCache::postProcess(ChunkSource *parent, int x, int z )
{
+#ifdef _LARGE_WORLDS
+ // Don't run decoration/structures at out-of-bounds coordinates
+ if (!m_isInfinite)
+ {
+ int half = m_XZSize / 2;
+ if (x < -half || x >= half || z < -half || z >= half) return;
+ }
+#endif
+
LevelChunk *chunk = getChunk(x, z);
if ( (chunk->terrainPopulated & LevelChunk::sTerrainPopulatedFromHere) == 0 )
{
@@ -484,31 +446,38 @@ void ServerChunkCache::postProcess(ChunkSource *parent, int x, int z )
// chunks exist as that's determined before post-processing can even run
chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromHere;
- // If we are an edge chunk, fill in missing flags from sides that will never post-process
- if(x == -XZOFFSET) // Furthest west
- {
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromW;
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromSW;
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromNW;
- }
- if(x == (XZOFFSET - 1 )) // Furthest east
- {
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromE;
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromSE;
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromNE;
- }
- if(z == -XZOFFSET) // Furthest south
- {
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromS;
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromSW;
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromSE;
- }
- if(z == (XZOFFSET - 1)) // Furthest north
+#ifdef _LARGE_WORLDS
+ // For finite worlds, edge chunks have missing neighbours that will never post-process,
+ // so fill in the appropriate flags. For infinite worlds, no edges exist.
+ if (!m_isInfinite)
{
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromN;
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromNW;
- chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromNE;
+ int half = m_XZSize / 2;
+ if(x == -half) // Furthest west
+ {
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromW;
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromSW;
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromNW;
+ }
+ if(x == (half - 1)) // Furthest east
+ {
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromE;
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromSE;
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromNE;
+ }
+ if(z == -half) // Furthest south
+ {
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromS;
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromSW;
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromSE;
+ }
+ if(z == (half - 1)) // Furthest north
+ {
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromN;
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromNW;
+ chunk->terrainPopulated |= LevelChunk::sTerrainPopulatedFromNE;
+ }
}
+#endif
// Set flags for post-processing being complete for neighbouring chunks. This also performs actions if this post-processing completes
// a full set of post-processing flags for one of these neighbours.
@@ -866,20 +835,19 @@ bool ServerChunkCache::tick()
LevelChunk *chunk = m_toDrop.front();
if(!chunk->isUnloaded())
{
- save(chunk);
- saveEntities(chunk);
- chunk->unload(true);
-
- //loadedChunks.remove(cp);
- //loadedChunkList.remove(chunk);
- AUTO_VAR(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;
- int idx = ix * XZSIZE + iz;
- m_unloadedCache[idx] = chunk;
- cache[idx] = NULL;
+ save(chunk);
+ saveEntities(chunk);
+ chunk->unload(true);
+
+ EnterCriticalSection(&m_csLoadCreate);
+ AUTO_VAR(it, std::find( m_loadedChunkList.begin(), m_loadedChunkList.end(), chunk) );
+ if(it != m_loadedChunkList.end()) m_loadedChunkList.erase(it);
+
+ int64_t key = chunkKey(chunk->x, chunk->z);
+ // Move from live map to unloaded map; data stays in RAM
+ m_unloadedMap[key] = chunk;
+ m_chunkMap.erase(key);
+ LeaveCriticalSection(&m_csLoadCreate);
}
m_toDrop.pop_front();
}
diff --git a/Minecraft.Client/ServerChunkCache.h b/Minecraft.Client/ServerChunkCache.h
index 058cecc42..4b506194e 100644
--- a/Minecraft.Client/ServerChunkCache.h
+++ b/Minecraft.Client/ServerChunkCache.h
@@ -5,9 +5,15 @@
#include "..\Minecraft.World\JavaIntHash.h"
#include "..\Minecraft.World\RandomLevelSource.h"
#include "..\Minecraft.World\C4JThread.h"
+#include
using namespace std;
class ServerLevel;
+// Helper: pack (x,z) chunk coords into a single int64 key
+static inline int64_t chunkKey(int x, int z) {
+ return ((int64_t)(unsigned int)x << 32) | (unsigned int)z;
+}
+
class ServerChunkCache : public ChunkSource
{
@@ -20,20 +26,20 @@ class ServerChunkCache : public ChunkSource
public:
bool autoCreate;
private:
- LevelChunk **cache;
+ // Infinite-world chunk cache: maps packed (x,z) -> LevelChunk*
+ unordered_map m_chunkMap;
+ // Secondary map for unloaded-but-retained chunks (_LARGE_WORLDS streaming)
+ unordered_map m_unloadedMap;
vector m_loadedChunkList;
ServerLevel *level;
#ifdef _LARGE_WORLDS
+ bool m_isInfinite; // true when m_XZSize >= LEVEL_MAX_WIDTH (infinite world)
deque m_toDrop;
- LevelChunk **m_unloadedCache;
#endif
// 4J - added for multithreaded support
CRITICAL_SECTION m_csLoadCreate;
- // 4J - size of cache is defined by size of one side - must be even
- int XZSIZE;
- int XZOFFSET;
public:
ServerChunkCache(ServerLevel *level, ChunkStorage *storage, ChunkSource *source);
@@ -48,7 +54,8 @@ class ServerChunkCache : public ChunkSource
#ifdef _LARGE_WORLDS
LevelChunk *getChunkLoadedOrUnloaded(int x, int z); // 4J added
#endif
- virtual LevelChunk **getCache() { return cache; } // 4J added
+ // getCache() is no longer meaningful for infinite worlds; returns NULL
+ virtual LevelChunk **getCache() { return NULL; }
// 4J-JEV Added; Remove chunk from the toDrop queue.
#ifdef _LARGE_WORLDS
diff --git a/Minecraft.Client/ServerLevel.cpp b/Minecraft.Client/ServerLevel.cpp
index de8c66cdc..f062ad6d1 100644
--- a/Minecraft.Client/ServerLevel.cpp
+++ b/Minecraft.Client/ServerLevel.cpp
@@ -101,9 +101,7 @@ ServerLevel::ServerLevel(MinecraftServer *server, shared_ptrlevelS
// 4J - this this used to be called in parent ctor via a virtual fn
chunkSource = createChunkSource();
- // 4J - optimisation - keep direct reference of underlying cache here
- chunkSourceCache = chunkSource->getCache();
- chunkSourceXZSize = chunkSource->m_XZSize;
+ // chunkSourceCache/chunkSourceXZSize removed: infinite worlds use virtual dispatch
// 4J - The listener used to be added in MinecraftServer::loadLevel but we need it to be set up before we do the next couple of things, or else chunks get loaded before we have the entity tracker set up to listen to them
this->server = server;
@@ -758,8 +756,22 @@ void ServerLevel::setInitialSpawn(LevelSettings *levelSettings)
int xSpawn = 0; // (Level.MAX_LEVEL_SIZE - 100) * 0;
int ySpawn = dimension->getSpawnYPosition();
int zSpawn = 0; // (Level.MAX_LEVEL_SIZE - 100) * 0;
- int minXZ = - (dimension->getXZSize() * 16 ) / 2;
- int maxXZ = (dimension->getXZSize() * 16 ) / 2 - 1;
+#ifdef _LARGE_WORLDS
+ int minXZ, maxXZ;
+ if (isInfiniteWorld(dimension->getXZSize()))
+ {
+ minXZ = -Level::MAX_LEVEL_SIZE;
+ maxXZ = Level::MAX_LEVEL_SIZE - 1;
+ }
+ else
+ {
+ minXZ = -(dimension->getXZSize() * 16) / 2;
+ maxXZ = (dimension->getXZSize() * 16) / 2 - 1;
+ }
+#else
+ int minXZ = -Level::MAX_LEVEL_SIZE;
+ int maxXZ = Level::MAX_LEVEL_SIZE - 1;
+#endif
if (findBiome != NULL)
{
diff --git a/Minecraft.Client/ServerPlayer.cpp b/Minecraft.Client/ServerPlayer.cpp
index 489e6f342..8ad655922 100644
--- a/Minecraft.Client/ServerPlayer.cpp
+++ b/Minecraft.Client/ServerPlayer.cpp
@@ -18,6 +18,7 @@
#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h"
#include "..\Minecraft.World\net.minecraft.world.entity.h"
#include "..\Minecraft.World\net.minecraft.world.item.h"
+#include "..\Minecraft.World\ChunkSource.h"
#include "..\Minecraft.World\net.minecraft.world.item.trading.h"
#include "..\Minecraft.World\net.minecraft.world.entity.item.h"
#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h"
@@ -65,8 +66,22 @@ ServerPlayer::ServerPlayer(MinecraftServer *server, Level *level, const wstring&
int attemptCount = 0;
int xx2, yy2, zz2;
- int minXZ = - (level->dimension->getXZSize() * 16 ) / 2;
- int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1;
+#ifdef _LARGE_WORLDS
+ int minXZ, maxXZ;
+ if (isInfiniteWorld(level->getLevelData()->getXZSize()))
+ {
+ minXZ = -Level::MAX_LEVEL_SIZE;
+ maxXZ = Level::MAX_LEVEL_SIZE - 1;
+ }
+ else
+ {
+ minXZ = -(level->getLevelData()->getXZSize() * 16) / 2;
+ maxXZ = (level->getLevelData()->getXZSize() * 16) / 2 - 1;
+ }
+#else
+ int minXZ = -Level::MAX_LEVEL_SIZE;
+ int maxXZ = Level::MAX_LEVEL_SIZE - 1;
+#endif
bool playerNear = false;
do
@@ -445,7 +460,7 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks)
for (unsigned int i = 0; i < tes->size(); i++)
{
// 4J Stu - Added delay param to ensure that these arrive after the BRUPs from above
- // Fix for #9169 - ART : Sign text is replaced with the words Awaiting approval.
+ // Fix for #9169 - ART : Sign text is replaced with the words �Awaiting approval�.
broadcast(tes->at(i), !connection->isLocal() && !dontDelayChunks);
}
delete tes;
diff --git a/Minecraft.Client/Windows64Media/loc/stringsPlatformSpecific.xml b/Minecraft.Client/Windows64Media/loc/stringsPlatformSpecific.xml
index b222769b9..494d73e4f 100644
--- a/Minecraft.Client/Windows64Media/loc/stringsPlatformSpecific.xml
+++ b/Minecraft.Client/Windows64Media/loc/stringsPlatformSpecific.xml
@@ -258,6 +258,9 @@ If you try to save while using the trial version, you will be given the option t
Large
+
+ Infinite
+
Classic
diff --git a/Minecraft.Client/Windows64Media/strings.h b/Minecraft.Client/Windows64Media/strings.h
index f40477692..1a87481c6 100644
--- a/Minecraft.Client/Windows64Media/strings.h
+++ b/Minecraft.Client/Windows64Media/strings.h
@@ -1923,3 +1923,4 @@
#define IDS_YOU_DIED 1921
#define IDS_YOU_HAVE 1922
#define IDS_ZOMBIE 1923
+#define IDS_WORLD_SIZE_TITLE_INFINITE 1924
diff --git a/Minecraft.World/ChunkSource.h b/Minecraft.World/ChunkSource.h
index 242f30a0f..2d2fe3a73 100644
--- a/Minecraft.World/ChunkSource.h
+++ b/Minecraft.World/ChunkSource.h
@@ -4,16 +4,25 @@
class ProgressListener;
class TilePos;
-// The maximum number of chunks that we can store
+// The maximum number of chunks for the world.
+// For infinite worlds (_LARGE_WORLDS), this is kept at a very large value
+// so that structure generation code (villages, strongholds, etc.) still works.
+// The actual chunk cache is now unbounded (hash map); this constant is only
+// used for structure placement limits and similar range checks.
#ifdef _LARGE_WORLDS
-// 4J Stu - Our default map (at zoom level 3) is 1024x1024 blocks (or 64 chunks)
-#define LEVEL_MAX_WIDTH (5*64) //(6*54)
+#define LEVEL_MAX_WIDTH (1875000) // 30,000,000 blocks / 16 = effectively infinite kinda :3
+#define LEVEL_LARGE_WIDTH (5 * 64) // 320 chunks (~5120 blocks radius) for the "Large" bounded option
#else
#define LEVEL_MAX_WIDTH 54
#endif
#define LEVEL_MIN_WIDTH 54
#define LEVEL_LEGACY_WIDTH 54
+#ifdef _LARGE_WORLDS
+// Anything >= LEVEL_MAX_WIDTH is treated as an infinite world
+inline bool isInfiniteWorld(int xzSize) { return xzSize >= LEVEL_MAX_WIDTH; }
+#endif
+
// Scale was 8 in the Java game, but that would make our nether tiny
// Every 1 block you move in the nether maps to HELL_LEVEL_SCALE blocks in the overworld
#ifdef _LARGE_WORLDS
diff --git a/Minecraft.World/CustomLevelSource.cpp b/Minecraft.World/CustomLevelSource.cpp
index 216e63b91..bde7e49aa 100644
--- a/Minecraft.World/CustomLevelSource.cpp
+++ b/Minecraft.World/CustomLevelSource.cpp
@@ -157,12 +157,17 @@ void CustomLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks)
for (int z = 0; z < CHUNK_WIDTH; z++)
{
int mapIndex = (zMapStart * 16 + z + ( zc * CHUNK_WIDTH )) * (m_XZSize * 16) + (xMapStart * 16 + x + ( xc * CHUNK_WIDTH ));
- int mapHeight = m_heightmapOverride[mapIndex];
- waterHeight = m_waterheightOverride[mapIndex];
- //app.DebugPrintf("MapHeight = %d, y = %d\n", mapHeight, yc * CHUNK_HEIGHT + y);
- ///////////////////////////////////////////////////////////////////
- // 4J - add this chunk of code to make land "fall-off" at the edges of
- // a finite world - size of that world is currently hard-coded in here
+ int mapHeight = m_heightmapOverride[mapIndex];
+ waterHeight = m_waterheightOverride[mapIndex];
+ //app.DebugPrintf("MapHeight = %d, y = %d\n", mapHeight, yc * CHUNK_HEIGHT + y);
+#ifdef _LARGE_WORLDS
+ ///////////////////////////////////////////////////////////////////
+ // 4J - add this chunk of code to make land "fall-off" at the edges of
+ // a finite world - size of that world is currently hard-coded in here
+ float comp = 0.0f;
+ int emin = 0;
+ if (!isInfiniteWorld(m_XZSize))
+ {
const int worldSize = m_XZSize * 16;
const int falloffStart = 32; // chunks away from edge were we start doing fall-off
const float falloffMax = 128.0f; // max value we need to get to falloff by the edge of the map
@@ -183,41 +188,42 @@ void CustomLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks)
if( zzz1 < 0 ) zzz1 = 0;
// Get min distance to any edge
- int emin = xxx0;
+ emin = xxx0;
if (xxx1 < emin ) emin = xxx1;
if (zzz0 < emin ) emin = zzz0;
if (zzz1 < emin ) emin = zzz1;
- float comp = 0.0f;
-
// Calculate how much we want the world to fall away, if we're in the defined region to do so
if( emin < falloffStart )
{
int falloff = falloffStart - emin;
comp = ((float)falloff / (float)falloffStart ) * falloffMax;
}
- // 4J - end of extra code
- ///////////////////////////////////////////////////////////////////
- int tileId = 0;
- // 4J - this comparison used to just be with 0.0f but is now varied by block above
- if (yc * CHUNK_HEIGHT + y < mapHeight)
- {
- tileId = (byte) Tile::rock_Id;
- }
- else if (yc * CHUNK_HEIGHT + y < waterHeight)
- {
- tileId = (byte) Tile::calmWater_Id;
- }
-
- // 4J - more extra code to make sure that the column at the edge of the world is just water & rock, to match the infinite sea that
- // continues on after the edge of the world.
+ }
+ // 4J - end of extra code
+ ///////////////////////////////////////////////////////////////////
+#endif
+ int tileId = 0;
+ // 4J - this comparison used to just be with 0.0f but is now varied by block above
+ if (yc * CHUNK_HEIGHT + y < mapHeight)
+ {
+ tileId = (byte) Tile::rock_Id;
+ }
+ else if (yc * CHUNK_HEIGHT + y < waterHeight)
+ {
+ tileId = (byte) Tile::calmWater_Id;
+ }
- if( emin == 0 )
- {
- // This matches code in MultiPlayerChunkCache that makes the geometry which continues at the edge of the world
- if( yc * CHUNK_HEIGHT + y <= ( level->getSeaLevel() - 10 ) ) tileId = Tile::rock_Id;
- else if( yc * CHUNK_HEIGHT + y < level->getSeaLevel() ) tileId = Tile::calmWater_Id;
- }
+#ifdef _LARGE_WORLDS
+ // 4J - more extra code to make sure that the column at the edge of the world is just water & rock, to match the infinite sea that
+ // continues on after the edge of the world.
+ if( !isInfiniteWorld(m_XZSize) && emin == 0 )
+ {
+ // This matches code in MultiPlayerChunkCache that makes the geometry which continues at the edge of the world
+ if( yc * CHUNK_HEIGHT + y <= ( level->getSeaLevel() - 10 ) ) tileId = Tile::rock_Id;
+ else if( yc * CHUNK_HEIGHT + y < level->getSeaLevel() ) tileId = Tile::calmWater_Id;
+ }
+#endif
int indexY = (yc * CHUNK_HEIGHT + y);
int offsAdjustment = 0;
diff --git a/Minecraft.World/Fireball.cpp b/Minecraft.World/Fireball.cpp
index 46be68f74..345e0d80d 100644
--- a/Minecraft.World/Fireball.cpp
+++ b/Minecraft.World/Fireball.cpp
@@ -9,6 +9,7 @@
#include "Fireball.h"
#include "net.minecraft.world.level.dimension.h"
#include "SharedConstants.h"
+#include "ChunkSource.h"
// 4J - added common ctor code.
@@ -138,16 +139,31 @@ void Fireball::tick()
}
else
{
- // 4J-PB - TU9 bug fix - fireballs can hit the edge of the world, and stay there
- int minXZ = - (level->dimension->getXZSize() * 16 ) / 2;
- int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1;
-
- if ((x<=minXZ) || (x>=maxXZ) || (z<=minXZ) || (z>=maxXZ))
+ // 4J-PB - TU9 bug fix - fireballs can hit the edge of the world, and stay there
+#ifdef _LARGE_WORLDS
+ if (!isInfiniteWorld(level->dimension->getXZSize()))
{
- remove();
- app.DebugPrintf("Fireball removed - end of world\n");
- return;
+ int minXZ = -(level->dimension->getXZSize() * 16) / 2;
+ int maxXZ = (level->dimension->getXZSize() * 16) / 2 - 1;
+ if ((x<=minXZ) || (x>=maxXZ) || (z<=minXZ) || (z>=maxXZ))
+ {
+ remove();
+ app.DebugPrintf("Fireball removed - end of world\n");
+ return;
+ }
+ }
+#else
+ {
+ int minXZ = -(level->dimension->getXZSize() * 16) / 2;
+ int maxXZ = (level->dimension->getXZSize() * 16) / 2 - 1;
+ if ((x<=minXZ) || (x>=maxXZ) || (z<=minXZ) || (z>=maxXZ))
+ {
+ remove();
+ app.DebugPrintf("Fireball removed - end of world\n");
+ return;
+ }
}
+#endif
}
}
diff --git a/Minecraft.World/Level.cpp b/Minecraft.World/Level.cpp
index 4b6a64f57..034384685 100644
--- a/Minecraft.World/Level.cpp
+++ b/Minecraft.World/Level.cpp
@@ -1273,19 +1273,8 @@ int Level::getBrightnessPropagate(LightLayer::variety layer, int x, int y, int z
int Level::getBrightness(LightLayer::variety layer, int x, int y, int z)
{
- // 4J - optimised. Not doing checks on x/z that are no longer necessary, and directly checking the cache within
- // the ServerChunkCache/MultiplayerChunkCache rather than going through wrappers & virtual functions.
- int xc = x >> 4;
- int zc = z >> 4;
-
- int ix = xc + (chunkSourceXZSize/2);
- int iz = zc + (chunkSourceXZSize/2);
-
- if( ( ix < 0 ) || ( ix >= chunkSourceXZSize ) ) return 0;
- if( ( iz < 0 ) || ( iz >= chunkSourceXZSize ) ) return 0;
- int idx = ix * chunkSourceXZSize + iz;
- LevelChunk *c = chunkSourceCache[idx];
-
+ // Infinite worlds: use virtual dispatch through chunkSource (no fixed-size flat array cache)
+ LevelChunk *c = chunkSource->getChunk(x >> 4, z >> 4);
if( c == NULL ) return (int)layer;
if (y < 0) y = 0;
@@ -1294,7 +1283,7 @@ int Level::getBrightness(LightLayer::variety layer, int x, int y, int z)
return c->getBrightness(layer, x & 15, y, z & 15);
}
-// 4J added as optimisation - if all the neighbouring brightesses are going to be in the one chunk, just get
+// 4J added as optimisation - if all the neighbouring brightnesses are going to be in the one chunk, just get
// the level chunk once
void Level::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety layer, int x, int y, int z)
{
@@ -1302,7 +1291,7 @@ void Level::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety laye
( ( ( z & 15 ) == 0 ) || ( ( z & 15 ) == 15 ) ) ||
( ( y <= 0 ) || ( y >= 127 ) ) )
{
- // We're spanning more than one chunk, just fall back on original java method here
+ // We're spanning more than one chunk, just fall back on original method here
brightnesses[0] = getBrightness(layer, x - 1, y, z);
brightnesses[1] = getBrightness(layer, x + 1, y, z);
brightnesses[2] = getBrightness(layer, x, y - 1, z);
@@ -1312,42 +1301,17 @@ void Level::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety laye
}
else
{
- // All in one chunk - just get the chunk once, and do a single call to get the results
- int xc = x >> 4;
- int zc = z >> 4;
-
- int ix = xc + (chunkSourceXZSize/2);
- int iz = zc + (chunkSourceXZSize/2);
-
- // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we
- // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast
- // it to an int
- if( ( ( ix < 0 ) || ( ix >= chunkSourceXZSize ) ) ||
- ( ( iz < 0 ) || ( iz >= chunkSourceXZSize ) ) )
- {
- for( int i = 0; i < 6; i++ )
- {
- brightnesses[i] = (int)layer;
- }
- return;
- }
-
- int idx = ix * chunkSourceXZSize + iz;
- LevelChunk *c = chunkSourceCache[idx];
+ // All in one chunk - get the chunk once via virtual dispatch
+ LevelChunk *c = chunkSource->getChunk(x >> 4, z >> 4);
- // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we
- // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast
- // it to an int
if( c == NULL )
{
for( int i = 0; i < 6; i++ )
- {
brightnesses[i] = (int)layer;
- }
return;
}
- // Single call to the levelchunk too to avoid overhead of virtual fn calls
+ // Single call to the levelchunk to get all 6 neighbour brightnesses
c->getNeighbourBrightnesses(brightnesses, layer, x & 15, y, z & 15);
}
}
@@ -1833,7 +1797,11 @@ AABBList *Level::getCubes(shared_ptr source, AABB *box, bool noEntities,
int z0 = Mth::floor(box->z0);
int z1 = Mth::floor(box->z1 + 1);
- int maxxz = ( dimension->getXZSize() * 16 ) / 2;
+#ifdef _LARGE_WORLDS
+ int maxxz = isInfiniteWorld(dimension->getXZSize()) ? MAX_LEVEL_SIZE : (dimension->getXZSize() * 16) / 2;
+#else
+ int maxxz = (dimension->getXZSize() * 16) / 2;
+#endif
int minxz = -maxxz;
for (int x = x0; x < x1; x++)
for (int z = z0; z < z1; z++)
@@ -3415,6 +3383,35 @@ void Level::checkLight(LightLayer::variety layer, int xc, int yc, int zc, bool f
EnterCriticalSection(&m_checkLightCS);
+#ifdef _LARGE_WORLDS
+ // For finite worlds, compute XZ boundaries and early-out if the coordinate is OOB.
+ // For infinite worlds, use MAX_LEVEL_SIZE (effectively no boundary).
+ int checkLightMinXZ, checkLightMaxXZ;
+ if (!isInfiniteWorld(dimension->getXZSize()))
+ {
+ checkLightMinXZ = -(dimension->getXZSize() * 16) / 2;
+ checkLightMaxXZ = (dimension->getXZSize() * 16) / 2 - 1;
+ if ((xc > checkLightMaxXZ) || (xc < checkLightMinXZ) || (zc > checkLightMaxXZ) || (zc < checkLightMinXZ))
+ {
+ LeaveCriticalSection(&m_checkLightCS);
+ return;
+ }
+ }
+ else
+ {
+ checkLightMinXZ = -MAX_LEVEL_SIZE;
+ checkLightMaxXZ = MAX_LEVEL_SIZE - 1;
+ }
+#else
+ int checkLightMinXZ = -(dimension->getXZSize() * 16) / 2;
+ int checkLightMaxXZ = (dimension->getXZSize() * 16) / 2 - 1;
+ if ((xc > checkLightMaxXZ) || (xc < checkLightMinXZ) || (zc > checkLightMaxXZ) || (zc < checkLightMinXZ))
+ {
+ LeaveCriticalSection(&m_checkLightCS);
+ return;
+ }
+#endif
+
#ifdef __PSVITA__
// AP - only clear the one array element required to check if something has changed
cachewritten = false;
@@ -3466,14 +3463,6 @@ void Level::checkLight(LightLayer::variety layer, int xc, int yc, int zc, bool f
//int darktcc = 0;
- // 4J - added
- int minXZ = - (dimension->getXZSize() * 16 ) / 2;
- int maxXZ = (dimension->getXZSize() * 16 ) / 2 - 1;
- if( ( xc > maxXZ ) || ( xc < minXZ ) || ( zc > maxXZ ) || ( zc < minXZ ) )
- {
- LeaveCriticalSection(&m_checkLightCS);
- return;
- }
// Lock 128K of cache (containing all the lighting cache + first 112K of toCheck array) on L2 to try and stop any cached data getting knocked out of L2 by other non-cached reads (or vice-versa)
// if( cache ) XLockL2(XLOCKL2_INDEX_TITLE, cache, 128 * 1024, XLOCKL2_LOCK_SIZE_1_WAY, 0 );
@@ -3589,7 +3578,7 @@ void Level::checkLight(LightLayer::variety layer, int xc, int yc, int zc, bool f
int zz = z + ((j / 2 + 2) % 3 / 2) * flip;
// 4J - added - don't let this lighting creep out of the normal fixed world and into the infinite water chunks beyond
- if( ( xx > maxXZ ) || ( xx < minXZ ) || ( zz > maxXZ ) || ( zz < minXZ ) ) continue;
+ if( ( xx > checkLightMaxXZ ) || ( xx < checkLightMinXZ ) || ( zz > checkLightMaxXZ ) || ( zz < checkLightMinXZ ) ) continue;
if( ( yy < 0 ) || ( yy >= maxBuildHeight ) ) continue;
o = getBrightnessCached(cache, layer, xx, yy, zz);
@@ -3679,12 +3668,12 @@ void Level::checkLight(LightLayer::variety layer, int xc, int yc, int zc, bool f
if (xd + yd + zd < 17 && tcc < (32 * 32 * 32) - 6) // 4J - 32 * 32 * 32 was toCheck.length
{
// 4J - added extra checks here to stop lighting updates moving out of the actual fixed world and into the infinite water chunks
- if( ( x - 1 ) >= minXZ ) { if (getBrightnessCached(cache, layer, x - 1, y, z) < expected) toCheck[tcc++] = (((x - 1 - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - zc) + 32) << 12); }
- if( ( x + 1 ) <= maxXZ ) { if (getBrightnessCached(cache, layer, x + 1, y, z) < expected) toCheck[tcc++] = (((x + 1 - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - zc) + 32) << 12); }
+ if( ( x - 1 ) >= checkLightMinXZ ) { if (getBrightnessCached(cache, layer, x - 1, y, z) < expected) toCheck[tcc++] = (((x - 1 - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - zc) + 32) << 12); }
+ if( ( x + 1 ) <= checkLightMaxXZ ) { if (getBrightnessCached(cache, layer, x + 1, y, z) < expected) toCheck[tcc++] = (((x + 1 - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - zc) + 32) << 12); }
if( ( y - 1 ) >= 0 ) { if (getBrightnessCached(cache, layer, x, y - 1, z) < expected) toCheck[tcc++] = (((x - xc) + 32)) + (((y - 1 - yc) + 32) << 6) + (((z - zc) + 32) << 12); }
if( ( y + 1 ) < maxBuildHeight ) { if (getBrightnessCached(cache, layer, x, y + 1, z) < expected) toCheck[tcc++] = (((x - xc) + 32)) + (((y + 1 - yc) + 32) << 6) + (((z - zc) + 32) << 12); }
- if( ( z - 1 ) >= minXZ ) { if (getBrightnessCached(cache, layer, x, y, z - 1) < expected) toCheck[tcc++] = (((x - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - 1 - zc) + 32) << 12); }
- if( ( z + 1 ) <= maxXZ ) { if (getBrightnessCached(cache, layer, x, y, z + 1) < expected) toCheck[tcc++] = (((x - xc) + 32)) + (((y - yc) + 32) << 6) + (((z + 1 - zc) + 32) << 12); }
+ if( ( z - 1 ) >= checkLightMinXZ ) { if (getBrightnessCached(cache, layer, x, y, z - 1) < expected) toCheck[tcc++] = (((x - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - 1 - zc) + 32) << 12); }
+ if( ( z + 1 ) <= checkLightMaxXZ ) { if (getBrightnessCached(cache, layer, x, y, z + 1) < expected) toCheck[tcc++] = (((x - xc) + 32)) + (((y - yc) + 32) << 6) + (((z + 1 - zc) + 32) << 12); }
}
}
}
diff --git a/Minecraft.World/Level.h b/Minecraft.World/Level.h
index 361a40e84..47a8fcab2 100644
--- a/Minecraft.World/Level.h
+++ b/Minecraft.World/Level.h
@@ -498,9 +498,7 @@ class Level : public LevelSource
__int64 m_timeOfDayOverride;
- // 4J - optimisation - keep direct reference of underlying cache here
- LevelChunk **chunkSourceCache;
- int chunkSourceXZSize;
+ // Removed chunkSourceCache/chunkSourceXZSize: replaced by virtual dispatch for infinite world support
// 4J - added for implementation of finite limit to number of item entities, tnt and falling block entities
public:
diff --git a/Minecraft.World/LevelChunk.cpp b/Minecraft.World/LevelChunk.cpp
index 823c7c4cd..3c4d03586 100644
--- a/Minecraft.World/LevelChunk.cpp
+++ b/Minecraft.World/LevelChunk.cpp
@@ -699,9 +699,14 @@ void LevelChunk::recheckGaps(bool bForce)
// to light massive gaps between the height of 0 and whatever heights are in those.
if( isEmpty() ) return;
- // 4J added
- int minXZ = - (level->dimension->getXZSize() * 16 ) / 2;
- int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1;
+ // 4J added — for finite worlds, use the actual world-size boundary; for infinite, use MAX_LEVEL_SIZE
+#ifdef _LARGE_WORLDS
+ int minXZ = isInfiniteWorld(level->dimension->getXZSize()) ? -Level::MAX_LEVEL_SIZE : -(level->dimension->getXZSize() * 16) / 2;
+ int maxXZ = isInfiniteWorld(level->dimension->getXZSize()) ? (Level::MAX_LEVEL_SIZE - 1) : ((level->dimension->getXZSize() * 16) / 2 - 1);
+#else
+ int minXZ = -(level->dimension->getXZSize() * 16) / 2;
+ int maxXZ = (level->dimension->getXZSize() * 16) / 2 - 1;
+#endif
// 4J - note - this test will currently return true for chunks at the edge of our world. Making further checks inside the loop now to address this issue.
if (level->hasChunksAt(x * 16 + 8, Level::maxBuildHeight / 2, z * 16 + 8, 16))
diff --git a/Minecraft.World/LiquidTileDynamic.cpp b/Minecraft.World/LiquidTileDynamic.cpp
index 14a93b932..0e714eaf2 100644
--- a/Minecraft.World/LiquidTileDynamic.cpp
+++ b/Minecraft.World/LiquidTileDynamic.cpp
@@ -316,15 +316,19 @@ int LiquidTileDynamic::getHighest(Level *level, int x, int y, int z, int current
bool LiquidTileDynamic::canSpreadTo(Level *level, int x, int y, int z)
{
+#ifdef _LARGE_WORLDS
// 4J added - don't try and spread out of our restricted map. If we don't do this check then tiles at the edge of the world will try and spread outside as the outside tiles report that they contain
// only air. The fact that this successfully spreads then updates the neighbours of the tile outside of the map, one of which is the original tile just inside the map, which gets set back to being
// dynamic, and added to the pending ticks array.
- int xc = x >> 4;
- int zc = z >> 4;
- int ix = xc + (level->chunkSourceXZSize/2);
- int iz = zc + (level->chunkSourceXZSize/2);
- if( ( ix < 0 ) || ( ix >= level->chunkSourceXZSize ) ) return false;
- if( ( iz < 0 ) || ( iz >= level->chunkSourceXZSize ) ) return false;
+ if (!isInfiniteWorld(level->dimension->getXZSize()))
+ {
+ int halfBlocks = (level->dimension->getXZSize() * 16) / 2;
+ if (x < -halfBlocks || x >= halfBlocks || z < -halfBlocks || z >= halfBlocks)
+ return false;
+ }
+#endif
+ // For infinite worlds, rely on hasChunkAt to prevent spreading into unloaded chunks
+ if( !level->hasChunkAt(x, y, z) ) return false;
Material *target = level->getMaterial(x, y, z);
if (target == material) return false;
diff --git a/Minecraft.World/PistonBaseTile.cpp b/Minecraft.World/PistonBaseTile.cpp
index cbad5cecd..3e86c94ec 100644
--- a/Minecraft.World/PistonBaseTile.cpp
+++ b/Minecraft.World/PistonBaseTile.cpp
@@ -10,6 +10,7 @@
#include "net.minecraft.world.h"
#include "LevelChunk.h"
#include "Dimension.h"
+#include "ChunkSource.h"
const wstring PistonBaseTile::EDGE_TEX = L"piston_side";
const wstring PistonBaseTile::PLATFORM_TEX = L"piston_top";
@@ -486,14 +487,28 @@ bool PistonBaseTile::canPush(Level *level, int sx, int sy, int sz, int facing)
// out of bounds
return false;
}
-
+
// 4J - added to also check for out of bounds in x/z for our finite world
- int minXZ = - (level->dimension->getXZSize() * 16 ) / 2;
- int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1;
- if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) )
+#ifdef _LARGE_WORLDS
+ if (!isInfiniteWorld(level->dimension->getXZSize()))
{
- return false;
+ int minXZ = -(level->dimension->getXZSize() * 16) / 2;
+ int maxXZ = (level->dimension->getXZSize() * 16) / 2 - 1;
+ if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) )
+ {
+ return false;
+ }
+ }
+#else
+ {
+ int minXZ = -(level->dimension->getXZSize() * 16) / 2;
+ int maxXZ = (level->dimension->getXZSize() * 16) / 2 - 1;
+ if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) )
+ {
+ return false;
+ }
}
+#endif
int block = level->getTile(cx, cy, cz);
if (block == 0)
{
@@ -552,14 +567,28 @@ bool PistonBaseTile::createPush(Level *level, int sx, int sy, int sz, int facing
// out of bounds
return false;
}
-
+
// 4J - added to also check for out of bounds in x/z for our finite world
- int minXZ = - (level->dimension->getXZSize() * 16 ) / 2;
- int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1;
- if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) )
+#ifdef _LARGE_WORLDS
+ if (!isInfiniteWorld(level->dimension->getXZSize()))
{
- return false;
+ int minXZ = -(level->dimension->getXZSize() * 16) / 2;
+ int maxXZ = (level->dimension->getXZSize() * 16) / 2 - 1;
+ if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) )
+ {
+ return false;
+ }
+ }
+#else
+ {
+ int minXZ = -(level->dimension->getXZSize() * 16) / 2;
+ int maxXZ = (level->dimension->getXZSize() * 16) / 2 - 1;
+ if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) )
+ {
+ return false;
+ }
}
+#endif
int block = level->getTile(cx, cy, cz);
if (block == 0)
diff --git a/Minecraft.World/RandomLevelSource.cpp b/Minecraft.World/RandomLevelSource.cpp
index 4e26134a1..c8e7eba2a 100644
--- a/Minecraft.World/RandomLevelSource.cpp
+++ b/Minecraft.World/RandomLevelSource.cpp
@@ -154,29 +154,69 @@ void RandomLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks)
val -= vala;
for (int z = 0; z < CHUNK_WIDTH; z++)
{
+#ifdef _LARGE_WORLDS
///////////////////////////////////////////////////////////////////
// 4J - add this chunk of code to make land "fall-off" at the edges of
// a finite world - size of that world is currently hard-coded in here
+ float comp = 0.0f;
+ int emin = 0;
+ if (!isInfiniteWorld(m_XZSize))
+ {
+ const int worldSize = m_XZSize * 16;
+ const int falloffStart = 32; // chunks away from edge were we start doing fall-off
+ const float falloffMax = 128.0f; // max value we need to get to falloff by the edge of the map
+
+ int xxx = ( ( xOffs * 16 ) + x + ( xc * CHUNK_WIDTH ) );
+ int zzz = ( ( zOffs * 16 ) + z + ( zc * CHUNK_WIDTH ) );
+
+ // Get distance to edges of world in x
+ int xxx0 = xxx + ( worldSize / 2 );
+ if( xxx0 < 0 ) xxx0 = 0;
+ int xxx1 = ( ( worldSize / 2 ) - 1 ) - xxx;
+ if( xxx1 < 0 ) xxx1 = 0;
+
+ // Get distance to edges of world in z
+ int zzz0 = zzz + ( worldSize / 2 );
+ if( zzz0 < 0 ) zzz0 = 0;
+ int zzz1 = ( ( worldSize / 2 ) - 1 ) - zzz;
+ if( zzz1 < 0 ) zzz1 = 0;
+
+ // Get min distance to any edge
+ emin = xxx0;
+ if (xxx1 < emin ) emin = xxx1;
+ if (zzz0 < emin ) emin = zzz0;
+ if (zzz1 < emin ) emin = zzz1;
+
+ // Calculate how much we want the world to fall away, if we're in the defined region to do so
+ if( emin < falloffStart )
+ {
+ int falloff = falloffStart - emin;
+ comp = ((float)falloff / (float)falloffStart ) * falloffMax;
+ }
+ }
+ // 4J - end of extra code
+ ///////////////////////////////////////////////////////////////////
+#else
+ ///////////////////////////////////////////////////////////////////
+ // 4J - add this chunk of code to make land "fall-off" at the edges of
+ // a finite world
const int worldSize = m_XZSize * 16;
- const int falloffStart = 32; // chunks away from edge were we start doing fall-off
- const float falloffMax = 128.0f; // max value we need to get to falloff by the edge of the map
-
+ const int falloffStart = 32;
+ const float falloffMax = 128.0f;
+
int xxx = ( ( xOffs * 16 ) + x + ( xc * CHUNK_WIDTH ) );
int zzz = ( ( zOffs * 16 ) + z + ( zc * CHUNK_WIDTH ) );
- // Get distance to edges of world in x
int xxx0 = xxx + ( worldSize / 2 );
if( xxx0 < 0 ) xxx0 = 0;
int xxx1 = ( ( worldSize / 2 ) - 1 ) - xxx;
if( xxx1 < 0 ) xxx1 = 0;
- // Get distance to edges of world in z
int zzz0 = zzz + ( worldSize / 2 );
if( zzz0 < 0 ) zzz0 = 0;
int zzz1 = ( ( worldSize / 2 ) - 1 ) - zzz;
if( zzz1 < 0 ) zzz1 = 0;
- // Get min distance to any edge
int emin = xxx0;
if (xxx1 < emin ) emin = xxx1;
if (zzz0 < emin ) emin = zzz0;
@@ -184,14 +224,13 @@ void RandomLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks)
float comp = 0.0f;
- // Calculate how much we want the world to fall away, if we're in the defined region to do so
if( emin < falloffStart )
{
int falloff = falloffStart - emin;
comp = ((float)falloff / (float)falloffStart ) * falloffMax;
}
- // 4J - end of extra code
///////////////////////////////////////////////////////////////////
+#endif
// 4J - slightly rearranged this code (as of java 1.0.1 merge) to better fit with
// changes we've made edge-of-world things - original sets blocks[offs += step] directly
@@ -207,15 +246,23 @@ void RandomLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks)
tileId = (byte) Tile::calmWater_Id;
}
+#ifdef _LARGE_WORLDS
// 4J - more extra code to make sure that the column at the edge of the world is just water & rock, to match the infinite sea that
// continues on after the edge of the world.
-
- if( emin == 0 )
+ if( !isInfiniteWorld(m_XZSize) && emin == 0 )
{
// This matches code in MultiPlayerChunkCache that makes the geometry which continues at the edge of the world
if( yc * CHUNK_HEIGHT + y <= ( level->getSeaLevel() - 10 ) ) tileId = Tile::rock_Id;
else if( yc * CHUNK_HEIGHT + y < level->getSeaLevel() ) tileId = Tile::calmWater_Id;
}
+#else
+ // 4J - more extra code to make sure that the column at the edge of the world is just water & rock
+ if( emin == 0 )
+ {
+ if( yc * CHUNK_HEIGHT + y <= ( level->getSeaLevel() - 10 ) ) tileId = Tile::rock_Id;
+ else if( yc * CHUNK_HEIGHT + y < level->getSeaLevel() ) tileId = Tile::calmWater_Id;
+ }
+#endif
blocks[offs += step] = tileId;
}
diff --git a/Minecraft.World/StrongholdPieces.cpp b/Minecraft.World/StrongholdPieces.cpp
index 0168b2bae..9ed1861ed 100644
--- a/Minecraft.World/StrongholdPieces.cpp
+++ b/Minecraft.World/StrongholdPieces.cpp
@@ -365,9 +365,24 @@ bool StrongholdPieces::StrongholdPiece::isOkBox(BoundingBox *box, StartPiece *st
if( startRoom != NULL && startRoom->m_level->getOriginalSaveVersion() >= SAVE_FILE_VERSION_MOVED_STRONGHOLD )
{
+#ifdef _LARGE_WORLDS
int xzSize = startRoom->m_level->getLevelData()->getXZSize();
- int blockMin = -( (xzSize << 4) / 2) + 1;
- int blockMax = ( (xzSize << 4) / 2 ) - 1;
+ int blockMin, blockMax;
+ if (isInfiniteWorld(xzSize))
+ {
+ blockMin = -Level::MAX_LEVEL_SIZE + 1;
+ blockMax = Level::MAX_LEVEL_SIZE - 1;
+ }
+ else
+ {
+ blockMin = -((xzSize << 4) / 2) + 1;
+ blockMax = ((xzSize << 4) / 2) - 1;
+ }
+#else
+ int xzSize = startRoom->m_level->getLevelData()->getXZSize();
+ int blockMin = -((xzSize << 4) / 2) + 1;
+ int blockMax = ((xzSize << 4) / 2) - 1;
+#endif
if(box->x0 <= blockMin) bIsOk = false;
if(box->z0 <= blockMin) bIsOk = false;
diff --git a/Minecraft.World/VillagePieces.cpp b/Minecraft.World/VillagePieces.cpp
index 27b21aa53..e698bc0d5 100644
--- a/Minecraft.World/VillagePieces.cpp
+++ b/Minecraft.World/VillagePieces.cpp
@@ -317,9 +317,24 @@ bool VillagePieces::VillagePiece::isOkBox(BoundingBox *box, StartPiece *startRoo
{
if( box->y0 > LOWEST_Y_POSITION ) bIsOk = true;
+#ifdef _LARGE_WORLDS
int xzSize = startRoom->m_level->getLevelData()->getXZSize();
- int blockMin = -( (xzSize << 4) / 2) + 1;
- int blockMax = ( (xzSize << 4) / 2 ) - 1;
+ int blockMin, blockMax;
+ if (isInfiniteWorld(xzSize))
+ {
+ blockMin = -Level::MAX_LEVEL_SIZE + 1;
+ blockMax = Level::MAX_LEVEL_SIZE - 1;
+ }
+ else
+ {
+ blockMin = -((xzSize << 4) / 2) + 1;
+ blockMax = ((xzSize << 4) / 2) - 1;
+ }
+#else
+ int xzSize = startRoom->m_level->getLevelData()->getXZSize();
+ int blockMin = -((xzSize << 4) / 2) + 1;
+ int blockMax = ((xzSize << 4) / 2) - 1;
+#endif
if(box->x0 <= blockMin) bIsOk = false;
if(box->z0 <= blockMin) bIsOk = false;
diff --git a/mc-arc-util b/mc-arc-util
new file mode 160000
index 000000000..d95119c35
--- /dev/null
+++ b/mc-arc-util
@@ -0,0 +1 @@
+Subproject commit d95119c3591005a4311fe8e67664791305610c47