diff --git a/.idea/.idea.Minecraft/.idea/misc.xml b/.idea/.idea.Minecraft/.idea/misc.xml
new file mode 100644
index 00000000..283b9b4d
--- /dev/null
+++ b/.idea/.idea.Minecraft/.idea/misc.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assets/Resources/Prefabs/Player.prefab b/Assets/Resources/Prefabs/Player.prefab
index 30b80376..1f3c2ea6 100644
--- a/Assets/Resources/Prefabs/Player.prefab
+++ b/Assets/Resources/Prefabs/Player.prefab
@@ -698,7 +698,7 @@ MonoBehaviour:
isFlying: 0
drawBounds: 0
sensitivity: 5
- checkIncrement: 0.05
+ checkIncrement: 0.1
reach: 4.5
cam: {fileID: 3114816152506633376}
inventory: {fileID: 0}
diff --git a/Assets/Scenes/MainMenu.unity b/Assets/Scenes/MainMenu.unity
index b9c8f3b4..7c841328 100644
--- a/Assets/Scenes/MainMenu.unity
+++ b/Assets/Scenes/MainMenu.unity
@@ -1852,7 +1852,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
- m_AnchoredPosition: {x: -152.5, y: 215544.27}
+ m_AnchoredPosition: {x: -152.5, y: 248223.02}
m_SizeDelta: {x: 305, y: 0}
m_Pivot: {x: 0, y: 1}
--- !u!114 &845042230
@@ -3799,7 +3799,7 @@ MonoBehaviour:
m_HandleRect: {fileID: 236816974}
m_Direction: 2
m_Value: 1
- m_Size: 0.4168831
+ m_Size: 0.40985915
m_NumberOfSteps: 0
m_OnValueChanged:
m_PersistentCalls:
@@ -5757,7 +5757,7 @@ PrefabInstance:
type: 3}
propertyPath: transport
value:
- objectReference: {fileID: 2330286423484457791}
+ objectReference: {fileID: 2330286423484457793}
- target: {fileID: 2330286422166840741, guid: ed098faebcb7c0a46a1b8cbc9753bb43,
type: 3}
propertyPath: m_Enabled
@@ -5771,16 +5771,16 @@ PrefabInstance:
m_RemovedComponents:
- {fileID: 2330286422166840739, guid: ed098faebcb7c0a46a1b8cbc9753bb43, type: 3}
m_SourcePrefab: {fileID: 100100000, guid: ed098faebcb7c0a46a1b8cbc9753bb43, type: 3}
---- !u!114 &2330286423484457791 stripped
+--- !u!114 &2330286423484457793 stripped
MonoBehaviour:
- m_CorrespondingSourceObject: {fileID: 2330286422166840743, guid: ed098faebcb7c0a46a1b8cbc9753bb43,
+ m_CorrespondingSourceObject: {fileID: 2330286422166840742, guid: ed098faebcb7c0a46a1b8cbc9753bb43,
type: 3}
m_PrefabInstance: {fileID: 2330286423484457790}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 6b0fecffa3f624585964b0d0eb21b18e, type: 3}
+ m_Script: {fileID: 11500000, guid: 61b93ff779f4ef84da855aef42076949, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!224 &4834870413912905276
diff --git a/Assets/Scenes/World.unity b/Assets/Scenes/World.unity
index c2529d29..ca8dcb78 100644
--- a/Assets/Scenes/World.unity
+++ b/Assets/Scenes/World.unity
@@ -3875,7 +3875,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: e113280f9ab044168541f6fef84d3095, type: 3}
m_Name:
m_EditorClassIdentifier:
- renderDistance: 2
+ renderDistance: 3
chunkSize: 16
chunkHeight: 16
worldHeight: 96
@@ -3884,7 +3884,7 @@ MonoBehaviour:
worldRenderer: {fileID: 1269315924}
terrainGenerator: {fileID: 1658937128}
mapSeedOffset: {x: -251, y: 150, z: 0}
- GenerateMoreChunks: 1
+ GenerateMoreChunks: 0
gamma: 0
skyLightMultiplier: 0.9
blockLightMultiplier: 1.18
diff --git a/Assets/TODO.md b/Assets/TODO.md
index 808285bd..c7da2768 100644
--- a/Assets/TODO.md
+++ b/Assets/TODO.md
@@ -12,4 +12,6 @@ made by me
- fix noise never going above 0.67
- also change the inventory on the server instead of only on the client (so that the server can save the inventory)
+
+- broke generating more chunks than the initial ones
\ No newline at end of file
diff --git a/Assets/ZeroFormatter.meta b/Assets/ZeroFormatter.meta
deleted file mode 100644
index ed4f3d7c..00000000
--- a/Assets/ZeroFormatter.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 9adf2c1a9f1d0734a9457a3bde392767
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/_Scripts/F3MenuManger.cs b/Assets/_Scripts/F3MenuManger.cs
index 5ec84138..939a3c0a 100644
--- a/Assets/_Scripts/F3MenuManger.cs
+++ b/Assets/_Scripts/F3MenuManger.cs
@@ -15,6 +15,8 @@ public class F3MenuManger : MonoBehaviour
public void Update()
{
+ if(WorldServer.IsDedicated) return;
+
if (Input.GetKeyUp(KeyCode.F3))
{
if (!GameManager.Instance.localPlayer.f3KeyComboUsed)
diff --git a/Assets/_Scripts/Networking/MinecraftNetworkManager.cs b/Assets/_Scripts/Networking/MinecraftNetworkManager.cs
index 4690966c..76b4d17c 100644
--- a/Assets/_Scripts/Networking/MinecraftNetworkManager.cs
+++ b/Assets/_Scripts/Networking/MinecraftNetworkManager.cs
@@ -19,7 +19,7 @@ public override void OnClientConnect()
base.OnClientConnect();
NetworkClient.Send(new WorldServer.StartPlayerMessage(SteamClient.SteamId));
- // NetworkClient.Send(new WorldServer.SpawnPlayerMessage(SteamClient.SteamId));
+ NetworkClient.Send(new WorldServer.ChunkRequestMessage(Vector3Int.zero, World.Instance.renderDistance));
}
public override void OnStartServer()
@@ -55,4 +55,10 @@ public override void OnServerDisconnect(NetworkConnectionToClient conn)
base.OnServerDisconnect(conn);
}
+
+ public override void OnStopServer()
+ {
+ World.Instance.SaveWorld();
+ base.OnStopServer();
+ }
}
diff --git a/Assets/_Scripts/World/ChunkData.cs b/Assets/_Scripts/World/ChunkData.cs
index dcca361a..d15eb460 100644
--- a/Assets/_Scripts/World/ChunkData.cs
+++ b/Assets/_Scripts/World/ChunkData.cs
@@ -17,7 +17,7 @@ public class ChunkData
public bool modifiedAfterSave = true;
public TreeData treeData = new TreeData();
- [CanBeNull] public ChunkRenderer renderer => WorldDataHelper.GetChunk(worldRef, worldPos);
+ [CanBeNull] public ChunkRenderer renderer => WorldDataHelper.GetChunk(worldPos);
public readonly Queue blockLightUpdateQueue = new();
public readonly Queue blockLightRemoveQueue = new();
@@ -51,6 +51,7 @@ public static ChunkData Deserialize(ChunkSaveData saveData)
}
chunkData.sections = sections;
chunkData.modifiedAfterSave = false;
+ chunkData.isGenerated = true;
return chunkData;
}
diff --git a/Assets/_Scripts/World/World.cs b/Assets/_Scripts/World/World.cs
index c1768256..4901ebff 100644
--- a/Assets/_Scripts/World/World.cs
+++ b/Assets/_Scripts/World/World.cs
@@ -7,12 +7,13 @@
using Mirror;
using UnityEngine;
using UnityEngine.Events;
+using UnityEngine.SceneManagement;
using Debug = UnityEngine.Debug;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
+[InitializeOnLoad]
#endif
-
public partial class World : MonoBehaviour
{
public static World Instance;
@@ -55,12 +56,16 @@ public partial class World : MonoBehaviour
public bool IsWorldCreated { get; private set; }
private Stopwatch fullStopwatch = new Stopwatch();
-
- public Dictionary blocksToPlaceAfterGeneration = new Dictionary();
public bool validateDone;
[HideInInspector] public bool isInPlayMode;
+ #if UNITY_EDITOR
+ static World()
+ {
+ EditorApplication.update += EditorUpdate;
+ }
+ #endif
private void Awake()
{
@@ -78,19 +83,25 @@ private void Start()
NetworkClient.RegisterHandler(message =>
{
StartWorld();
- GenerateWorld(message.position);
+ // GenerateWorld(message.position);
+ });
+
+ NetworkClient.RegisterHandler(message =>
+ {
+ Debug.Log("Received chunk");
+
+ var chunkData = ChunkData.Deserialize(message.chunkSaveData);
+
+ if (worldData.chunkDataDict.ContainsKey(chunkData.worldPos) && NetworkServer.active)
+ {
+ worldData.chunkDataDict[chunkData.worldPos] = chunkData;
+ }
+ else
+ {
+ worldData.chunkDataDict.TryAdd(chunkData.worldPos, chunkData);
+ }
+ dataToMeshQueue.Enqueue(chunkData);
});
-
- // NetworkClient.RegisterHandler(message =>
- // {
- // Debug.Log("Received chunk");
- //
- // var chunkData = ChunkData.Deserialize(message.chunkSaveData);
- //
- // worldData.chunkDataDict[chunkData.worldPos] = chunkData;
- //
- // GenerateOnlyMesh();
- // });
}
private void OnValidate()
@@ -100,7 +111,7 @@ private void OnValidate()
{
worldData = new WorldData
{
- chunkDataDict = new Dictionary(),
+ chunkDataDict = new ConcurrentDictionary(),
chunkDict = new Dictionary(),
worldName = worldName,
};
@@ -116,7 +127,7 @@ private void OnValidate()
Shader.SetGlobalVectorArray("lightColors", LightTextureCreator.lightColors);
}
- private WorldGenerationData GetPositionsInRenderDistance(Vector3Int playerPos)
+ public WorldGenerationData GetPositionsInRenderDistance(Vector3Int playerPos)
{
var allChunkPositionsNeeded = WorldDataHelper.GetChunkPositionsInRenderDistance(this, playerPos);
var allChunkDataPositionsNeeded = WorldDataHelper.GetDataPositionsInRenderDistance(this, playerPos);
@@ -196,7 +207,7 @@ public void SetBlock(Vector3 pos, BlockType blockType)
public void SetBlock(Vector3Int blockPos, BlockType blockType)
{
var chunkPos = WorldDataHelper.GetChunkPosition(this, blockPos);
- var chunk = WorldDataHelper.GetChunk(this, chunkPos);
+ var chunk = WorldDataHelper.GetChunk(chunkPos);
if (chunk == null) return;
chunk.ModifiedByPlayer = true;
chunk.ChunkData.modifiedAfterSave = true;
@@ -206,7 +217,7 @@ public void SetBlock(Vector3Int blockPos, BlockType blockType)
public void SetBlocks(IEnumerable blockPoss, BlockType blockType)
{
var chunkPos = WorldDataHelper.GetChunkPosition(this, blockPoss.First());
- var chunk = WorldDataHelper.GetChunk(this, chunkPos);
+ var chunk = WorldDataHelper.GetChunk(chunkPos);
SetBlocks(chunk, blockPoss, blockType);
}
@@ -232,7 +243,7 @@ public async void SetBlocks(ChunkRenderer chunk, IEnumerable blockPo
{
if(neighChunkData == null) continue;
- ChunkRenderer neighbourChunk = WorldDataHelper.GetChunk(neighChunkData.worldRef, neighChunkData.worldPos);
+ ChunkRenderer neighbourChunk = WorldDataHelper.GetChunk(neighChunkData.worldPos);
if(neighbourChunk != null)
{
neightBourUpdates.Add(neighbourChunk);
@@ -301,17 +312,15 @@ public void LoadAdditionalChunks(GameObject localPlayer)
if (!GenerateMoreChunks) return;
// Debug.Log("Loading additional chunks");
- GenerateWorld(Vector3Int.RoundToInt(localPlayer.transform.position), () =>
- {
- OnNewChunksGenerated?.Invoke();
- });
+ GenerateWorld(Vector3Int.RoundToInt(localPlayer.transform.position));
+ OnNewChunksGenerated?.Invoke();
}
private void Clear()
{
worldData = new WorldData
{
- chunkDataDict = new Dictionary(),
+ chunkDataDict = new ConcurrentDictionary(),
chunkDict = new Dictionary(),
worldName = worldName
};
@@ -320,8 +329,17 @@ private void Clear()
worldRenderer.chunkPool?.Clear();
isSaving = false;
+ // Clear queues
+ doneDataQueue.Clear();
+ dataToMeshQueue.Clear();
+ meshToRenderQueue.Clear();
+
+ actionOnChunkDone.Clear();
+
chunksToUpdate?.Clear();
+ disabled = false;
+
MyNoise.noiseStopwatch.Reset();
MyNoise.noise3DStopwatch.Reset();
@@ -394,6 +412,6 @@ public StartWorldMessage(Vector3Int position)
public struct WorldData
{
public string worldName;
- public Dictionary chunkDataDict;
+ public ConcurrentDictionary chunkDataDict;
public Dictionary chunkDict;
}
diff --git a/Assets/_Scripts/World/WorldDataHelper.cs b/Assets/_Scripts/World/WorldDataHelper.cs
index c7f63adf..9fc7b8e1 100644
--- a/Assets/_Scripts/World/WorldDataHelper.cs
+++ b/Assets/_Scripts/World/WorldDataHelper.cs
@@ -24,7 +24,7 @@ public static List GetChunkPositionsInRenderDistance(World world, Ve
var endX = playerPos.x + Mathf.Min(world.renderDistance, World.Instance.IsWorldCreated ? world.renderDistance : 8) * world.chunkSize;
var endZ = playerPos.z + Mathf.Min(world.renderDistance, World.Instance.IsWorldCreated ? world.renderDistance : 8) * world.chunkSize;
- return GetPositionsInRenderDistance(world,playerPos, startX, startZ, endX, endZ);
+ return GetPositionsInRenderDistance(world, startX, startZ, endX, endZ);
}
public static List GetDataPositionsInRenderDistance(World world, Vector3Int playerPos)
@@ -34,10 +34,10 @@ public static List GetDataPositionsInRenderDistance(World world, Vec
var endX = playerPos.x + (Mathf.Min(world.renderDistance, World.Instance.IsWorldCreated ? world.renderDistance : 8)+1) * world.chunkSize;
var endZ = playerPos.z + (Mathf.Min(world.renderDistance, World.Instance.IsWorldCreated ? world.renderDistance : 8)+1) * world.chunkSize;
- return GetPositionsInRenderDistance(world,playerPos, startX, startZ, endX, endZ);
+ return GetPositionsInRenderDistance(world, startX, startZ, endX, endZ);
}
- private static List GetPositionsInRenderDistance(World world, Vector3Int playerPos, int startX,
+ private static List GetPositionsInRenderDistance(World world, int startX,
int startZ, int endX, int endZ)
{
var chunkPositionsToCreate = new List();
@@ -70,7 +70,7 @@ public static HashSet GetPositionsToCreate(WorldData worldData, List
{
return allChunkPositionsNeeded
.Where(pos => !worldData.chunkDict.ContainsKey(pos) && !DoesChunkFileExist(worldData,pos))
- .OrderBy(pos => Vector3.Distance(playerPos, pos)).Take(World.Instance.IsWorldCreated ? World.Instance.chunksGenerationPerFrame : allChunkPositionsNeeded.Count)
+ .OrderBy(pos => Vector3.Distance(playerPos, pos))//.Take(World.Instance.IsWorldCreated ? World.Instance.chunksGenerationPerFrame : allChunkPositionsNeeded.Count)
.ToHashSet();
}
@@ -78,7 +78,7 @@ public static HashSet GetDataPositionsToCreate(WorldData worldData,
{
return allChunkDataPositionsNeeded
.Where(pos => !worldData.chunkDataDict.ContainsKey(pos) && !DoesChunkFileExist(worldData,pos))
- .OrderBy(pos => Vector3.Distance(playerPos, pos)).Take(World.Instance.IsWorldCreated ? 9 + World.Instance.chunksGenerationPerFrame*3 : allChunkDataPositionsNeeded.Count)
+ .OrderBy(pos => Vector3.Distance(playerPos, pos))//.Take(World.Instance.IsWorldCreated ? 9 + World.Instance.chunksGenerationPerFrame*3 : allChunkDataPositionsNeeded.Count)
.ToHashSet();
}
@@ -86,7 +86,7 @@ public static HashSet GetPositionsToLoad(WorldData worldData, List !worldData.chunkDict.ContainsKey(pos) && DoesChunkFileExist(worldData,pos))
- .OrderBy(pos => Vector3.Distance(playerPos, pos)).Take(World.Instance.IsWorldCreated ? World.Instance.chunksGenerationPerFrame : allChunkPositionsNeeded.Count)
+ .OrderBy(pos => Vector3.Distance(playerPos, pos))//.Take(World.Instance.IsWorldCreated ? World.Instance.chunksGenerationPerFrame : allChunkPositionsNeeded.Count)
.ToHashSet();
}
@@ -94,7 +94,7 @@ public static HashSet GetDataPositionsToLoad(WorldData worldData, Li
{
return allChunkDataPositionsNeeded
.Where(pos => !worldData.chunkDataDict.ContainsKey(pos) && DoesChunkFileExist(worldData,pos))
- .OrderBy(pos => Vector3.Distance(playerPos, pos)).Take(World.Instance.IsWorldCreated ? 9 + World.Instance.chunksGenerationPerFrame*3 : allChunkDataPositionsNeeded.Count)
+ .OrderBy(pos => Vector3.Distance(playerPos, pos))//.Take(World.Instance.IsWorldCreated ? 9 + World.Instance.chunksGenerationPerFrame*3 : allChunkDataPositionsNeeded.Count)
.ToHashSet();
}
@@ -134,15 +134,15 @@ public static void RemoveChunk(World world, Vector3Int pos)
world.worldRenderer.RemoveChunk(chunk);
world.worldData.chunkDict.Remove(pos);
}
- else
- {
- throw new Exception("Could not find chunk to remove");
- }
+ // else
+ // {
+ // throw new Exception("Could not find chunk to remove");
+ // }
}
public static void RemoveChunkData(World world, Vector3Int pos)
{
- world.worldData.chunkDataDict.Remove(pos);
+ world.worldData.chunkDataDict.Remove(pos, out _);
}
public static void SetBlock(World world, Vector3Int worldBlockPos, BlockType blockType)
@@ -156,13 +156,18 @@ public static void SetBlock(World world, Vector3Int worldBlockPos, BlockType blo
}
[CanBeNull]
- public static ChunkRenderer GetChunk(World world, Vector3Int worldPos)
+ public static ChunkRenderer GetChunk(Vector3Int worldPos)
{
- if (world.worldData.chunkDict.ContainsKey(worldPos))
+ try
+ {
+ World.Instance.worldData.chunkDict.TryGetValue(worldPos, out var chunk);
+ return chunk;
+ }
+ // This is so fucking stupid
+ // for some fucking reason trygetvalue null errors but that is not possible
+ catch(NullReferenceException)
{
- return world.worldData.chunkDict[worldPos];
}
-
return null;
}
diff --git a/Assets/_Scripts/World/WorldServer.cs b/Assets/_Scripts/World/WorldServer.cs
index 95f04b70..679bb711 100644
--- a/Assets/_Scripts/World/WorldServer.cs
+++ b/Assets/_Scripts/World/WorldServer.cs
@@ -1,6 +1,9 @@
using System;
using System.Collections;
using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Cysharp.Threading.Tasks;
using Mirror;
using Steamworks;
using Unity.VisualScripting;
@@ -9,6 +12,8 @@
public class WorldServer : NetworkBehaviour
{
public static WorldServer instance;
+
+ public static bool IsDedicated => !NetworkClient.active && NetworkServer.active;
private void Start()
{
@@ -22,15 +27,94 @@ public override void OnStartServer()
NetworkServer.RegisterHandler(SavePlayerMessageHandler, false);
NetworkServer.RegisterHandler(SpawnPlayerMessageHandler, false);
NetworkServer.RegisterHandler(StartPlayerMessageHandler, false);
+ NetworkServer.RegisterHandler(ChunkRequestMessageHandler, false);
StartCoroutine(SaveLoop());
}
+
+ #region Chunk Requests
+
+ private async void ChunkRequestMessageHandler(NetworkConnectionToClient conn, ChunkRequestMessage message)
+ {
+ World.Instance.renderDistance = message.renderDistance;
+ var positions = WorldDataHelper.GetDataPositionsInRenderDistance(World.Instance, message.position);
+ //Sort by distance
+ positions = positions.OrderBy(x => Vector3.Distance(x, message.position)).ToList();
+
+ if (!World.Instance.worldData.chunkDataDict.ContainsKey(message.position))
+ {
+ World.Instance.GenerateWorld(message.position);
+
+ // Create chunk actions
+ foreach (var pos in positions)
+ {
+ if (World.Instance.actionOnChunkDone.ContainsKey(pos))
+ {
+ World.Instance.actionOnChunkDone[pos] += () => { SendChunkAtPos(pos, conn);};
+ } else
+ {
+ World.Instance.actionOnChunkDone.Add(pos, () => { SendChunkAtPos(pos, conn);});
+ }
+ }
+ }
+ else
+ {
+ // Send the chunkData and all of its neighbors
+
+ foreach (var pos in positions)
+ {
+ SendChunkAtPos(pos, conn);
+ await UniTask.Delay(100);
+ }
+ }
+ }
+
+ private void SendChunkAtPos(Vector3Int pos, NetworkConnectionToClient conn)
+ {
+ if (World.Instance.worldData.chunkDataDict.TryGetValue(pos, out var chunkData))
+ {
+ var chunkDataMessage = new ChunkDataMessage
+ {
+ chunkSaveData = ChunkSaveData.Serialize(chunkData)
+ };
+ conn.Send(chunkDataMessage);
+ } else
+ {
+ Debug.LogError("Could not send chunk at " + pos);
+ }
+ }
+
+
+ public struct ChunkRequestMessage : NetworkMessage
+ {
+ public Vector3Int position;
+ public int renderDistance;
+
+ public ChunkRequestMessage(Vector3Int pos, int renderDistance)
+ {
+ position = pos;
+ this.renderDistance = renderDistance;
+ }
+ }
+
+ public struct ChunkDataMessage : NetworkMessage
+ {
+ public ChunkSaveData chunkSaveData;
+
+ public ChunkDataMessage(ChunkSaveData saveData)
+ {
+ chunkSaveData = saveData;
+ }
+ }
+
+ #endregion
#region Block
public void SetBlockMessageHandler(NetworkConnectionToClient conn, SetBlockMessage message)
{
RpcSetBlock(message.position, message.blockType);
+ WorldDataHelper.SetBlock(World.Instance, message.position, message.blockType);
}
[ClientRpc]
diff --git a/Assets/_Scripts/World/World_Generation.cs b/Assets/_Scripts/World/World_Generation.cs
index acae4242..45d87b89 100644
--- a/Assets/_Scripts/World/World_Generation.cs
+++ b/Assets/_Scripts/World/World_Generation.cs
@@ -7,7 +7,6 @@
using System.Threading;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
-using Mirror;
using Steamworks;
using Unity.VisualScripting;
using UnityEngine;
@@ -16,6 +15,16 @@
public partial class World
{
+ public readonly ConcurrentQueue doneDataQueue = new();
+ public readonly ConcurrentQueue dataToMeshQueue = new();
+ public readonly ConcurrentQueue> meshToRenderQueue = new();
+ public readonly Dictionary actionOnChunkDone = new();
+
+ public readonly Stopwatch dataStopwatch = new();
+ public readonly Stopwatch loadStopwatch = new();
+ public readonly Stopwatch featureStopwatch = new();
+ public readonly Stopwatch meshStopwatch = new();
+
public void GenerateWorld()
{
StartWorld();
@@ -32,14 +41,12 @@ public void StartWorld()
updateThread.Start();
}
- public async void GenerateWorld(Vector3Int position, Action onGenerateNewChunks = null)
+ public async void GenerateWorld(Vector3Int position)
{
Profiler.BeginThreadProfiling("GenerateWorld", "GenerateWorld");
- if (!Application.isPlaying)
- {
- fullStopwatch.Start();
- }
-
+
+ fullStopwatch.Start();
+
terrainGenerator.GenerateBiomePoints(position, renderDistance, chunkSize, mapSeedOffset);
WorldGenerationData worldGenerationData = await Task.Run(() => GetPositionsInRenderDistance(position), taskTokenSource.Token);
@@ -55,140 +62,16 @@ public async void GenerateWorld(Vector3Int position, Action onGenerateNewChunks
}
// Generate data chunks
- ConcurrentDictionary dataDict = new ConcurrentDictionary();
-
- var dataStopWatch = new Stopwatch();
- dataStopWatch.Start();
-
- Profiler.BeginThreadProfiling("GenerateWorld", "GenerateData");
-
- if (worldGenerationData.chunkPositionsToCreate.Count > 0)
- {
- try
- {
- dataDict.AddRange(await CalculateWorldChunkData(worldGenerationData.chunkDataPositionsToCreate));
- }
- catch (OperationCanceledException)
- {
- Debug.Log("Task cancelled");
- return;
- }
- }
- dataStopWatch.Stop();
- if (!Application.isPlaying)
- {
- Debug.Log($"Data generation took {dataStopWatch.ElapsedMilliseconds}ms");
- }
-
- var loadStopWatch = Stopwatch.StartNew();
- if (worldGenerationData.chunkDataPositionsToLoad.Count > 0)
- {
- try
- {
- dataDict.AddRange(await LoadChunksAsync(worldGenerationData.chunkDataPositionsToLoad, worldName));
- }
- catch (OperationCanceledException)
- {
- Debug.Log("Task cancelled");
- return;
- }
- }
-
- loadStopWatch.Stop();
- if (!Application.isPlaying)
- {
- Debug.Log($"Loading took {loadStopWatch.ElapsedMilliseconds}ms");
- }
-
- foreach (var calculatedData in dataDict)
- {
- worldData.chunkDataDict.Add(calculatedData.Key, calculatedData.Value);
- }
-
- Profiler.EndThreadProfiling();
-
-
-
- var featureStopWatch = new Stopwatch();
- featureStopWatch.Start();
-
- // Generate features like trees
- await CalculateFeatures(worldGenerationData.chunkPositionsToCreate);
-
-
- await Task.Run(() =>
- {
- Parallel.ForEach(blocksToPlaceAfterGeneration, (block) =>
- {
- WorldDataHelper.SetBlock(this, block.Key, block.Value);
- });
- });
-
- featureStopWatch.Stop();
- if (!Application.isPlaying)
- {
- Debug.Log($"Feature generation took {featureStopWatch.ElapsedMilliseconds}ms");
- }
-
- var visualStopWatch = new Stopwatch();
- visualStopWatch.Start();
-
- // Generate visual chunks
- // ConcurrentDictionary meshDataDict =
- // await CalculateChunkMeshData(worldGenerationData.chunkPositionsToCreate);
- ConcurrentDictionary meshDataDict;
-
- List dataToRender = worldData.chunkDataDict
- .Where(x => worldGenerationData.chunkPositionsToCreate.Contains(x.Key) || worldGenerationData.chunkPositionsToLoad.Contains(x.Key))
- .Select(x => x.Value)
- .ToList();
-
- await CastLightFirstTime(dataToRender);
-
- try
- {
- meshDataDict = await CreateMeshDataAsync(dataToRender);
- }
- catch (OperationCanceledException)
- {
- Debug.Log("Task cancelled");
- return;
- }
- visualStopWatch.Stop();
- if (!Application.isPlaying)
- {
- Debug.Log($"Mesh generation took {visualStopWatch.ElapsedMilliseconds}ms");
- Debug.Log($"Time spent generating noise {MyNoise.noiseStopwatch.ElapsedMilliseconds}ms");
- Debug.Log($"Time spent generating 3D noise {MyNoise.noise3DStopwatch.ElapsedMilliseconds}ms");
- }
-
- StartCoroutine(ChunkCreationCoroutine(meshDataDict, position));
-
- onGenerateNewChunks?.Invoke();
-
- Profiler.EndThreadProfiling();
- }
-
- public async void GenerateOnlyData(Vector3Int position, Action onDone = null)
- {
- terrainGenerator.GenerateBiomePoints(position, renderDistance, chunkSize, mapSeedOffset);
-
- WorldGenerationData worldGenerationData = await Task.Run(() => GetPositionsInRenderDistance(position), taskTokenSource.Token);
-
- // Generate data chunks
- ConcurrentDictionary dataDict = new ConcurrentDictionary();
-
- var dataStopWatch = new Stopwatch();
- dataStopWatch.Start();
Profiler.BeginThreadProfiling("GenerateWorld", "GenerateData");
- if (worldGenerationData.chunkPositionsToCreate.Count > 0)
+
+ if (worldGenerationData.chunkDataPositionsToCreate.Count > 0)
{
try
{
- dataDict.AddRange(await CalculateWorldChunkData(worldGenerationData.chunkDataPositionsToCreate));
+ CalculateWorldChunkData(worldGenerationData.chunkDataPositionsToCreate);
}
catch (OperationCanceledException)
{
@@ -196,18 +79,13 @@ public async void GenerateOnlyData(Vector3Int position, Action onDone = null)
return;
}
}
- dataStopWatch.Stop();
- if (!Application.isPlaying)
- {
- Debug.Log($"Data generation took {dataStopWatch.ElapsedMilliseconds}ms");
- }
- var loadStopWatch = Stopwatch.StartNew();
+
if (worldGenerationData.chunkDataPositionsToLoad.Count > 0)
{
try
{
- dataDict.AddRange(await LoadChunksAsync(worldGenerationData.chunkDataPositionsToLoad, worldName));
+ LoadChunksAsync(worldGenerationData.chunkDataPositionsToLoad, worldName);
}
catch (OperationCanceledException)
{
@@ -215,80 +93,12 @@ public async void GenerateOnlyData(Vector3Int position, Action onDone = null)
return;
}
}
-
- loadStopWatch.Stop();
- if (!Application.isPlaying)
- {
- Debug.Log($"Loading took {loadStopWatch.ElapsedMilliseconds}ms");
- }
- foreach (var calculatedData in dataDict)
- {
- worldData.chunkDataDict.Add(calculatedData.Key, calculatedData.Value);
- }
-
Profiler.EndThreadProfiling();
-
-
-
- var featureStopWatch = new Stopwatch();
- featureStopWatch.Start();
-
- // Generate features like trees
- await CalculateFeatures(worldGenerationData.chunkPositionsToCreate);
-
-
- await Task.Run(() =>
- {
- Parallel.ForEach(blocksToPlaceAfterGeneration, (block) =>
- {
- WorldDataHelper.SetBlock(this, block.Key, block.Value);
- });
- });
-
- featureStopWatch.Stop();
- if (!Application.isPlaying)
- {
- Debug.Log($"Feature generation took {featureStopWatch.ElapsedMilliseconds}ms");
- }
- onDone?.Invoke();
+ Profiler.EndThreadProfiling();
}
- // public async void GenerateOnlyMesh()
- // {
- // // Generate visual chunks
- // // ConcurrentDictionary meshDataDict =
- // // await CalculateChunkMeshData(worldGenerationData.chunkPositionsToCreate);
- // ConcurrentDictionary meshDataDict;
- //
- // List dataToRender = worldData.chunkDataDict
- // .Select(x => x.Value)
- // .ToList();
- //
- // await CastLightFirstTime(dataToRender);
- //
- // try
- // {
- // meshDataDict = await CreateMeshDataAsync(dataToRender);
- // }
- // catch (OperationCanceledException)
- // {
- // Debug.Log("Task cancelled");
- // return;
- // }
- //
- // StartCoroutine(ChunkCreationCoroutine(meshDataDict, Vector3Int.zero));
- // }
-
- public UniTask CastLightFirstTime(List dataToCast)
- {
- return UniTask.RunOnThreadPool(() =>
- {
- Parallel.ForEach(dataToCast, Lighting.RecastSunLightFirstTime);
- });
- }
-
public UniTask> CreateMeshDataAsync(List dataToRender)
{
return UniTask.RunOnThreadPool(() =>
@@ -342,29 +152,18 @@ public UniTask> CreateMeshDataAsync(L
}, true, taskTokenSource.Token);
}
- // private Task> CalculateChunkMeshData(List chunkPositionsToCreate)
- // {
- // return Task.Run(() =>
- // {
- // var meshDataDict = new ConcurrentDictionary();
- //
- // Parallel.ForEach(chunkPositionsToCreate, pos =>
- // {
- // var data = worldData.chunkDataDict[pos];
- // var meshData = Chunk.GetChunkMeshData(data);
- // meshDataDict.TryAdd(pos, meshData);
- // });
- //
- // return meshDataDict;
- // });
- // }
+ public void CreateMeshData(ChunkData dataToRender)
+ {
+ var meshData = dataToRender.GetMeshData();
+ meshToRenderQueue.Enqueue(new KeyValuePair(dataToRender.worldPos,meshData));
+ }
- private UniTask> CalculateWorldChunkData(HashSet chunkDataPositionsToCreate)
+ private UniTask CalculateWorldChunkData(HashSet chunkDataPositionsToCreate)
{
return UniTask.RunOnThreadPool(() =>
{
Profiler.BeginThreadProfiling("MyThreads","CalculateWorldChunkData");
- var dataDict = new ConcurrentDictionary();
+ // var dataDict = new ConcurrentDictionary();
// while (chunkDataPositionsToCreate.Count > 0)
// {
// if(chunkDataPositionsToCreate.Count < chunksGenerationPerFrame)
@@ -388,8 +187,8 @@ private UniTask> CalculateWorldChunk
// });
// UniTask.NextFrame();
// }
-
- Parallel.ForEach(chunkDataPositionsToCreate, pos =>
+ dataStopwatch.Start();
+ Parallel.ForEach(chunkDataPositionsToCreate,new ParallelOptions {MaxDegreeOfParallelism = 8}, pos =>
{
if(taskTokenSource.IsCancellationRequested)
{
@@ -398,10 +197,17 @@ private UniTask> CalculateWorldChunk
var data = new ChunkData(chunkSize, chunkHeight, this, pos);
var newData = terrainGenerator.GenerateChunkData(data, mapSeedOffset);
- dataDict.TryAdd(pos, newData);
-
+ if (worldData.chunkDataDict.TryAdd(pos, newData))
+ {
+ doneDataQueue.Enqueue(pos);
+ }
+ // else
+ // {
+ // Debug.LogError($"Failed to add chunk data '{pos}' to doneData");
+ // }
});
-
+ dataStopwatch.Stop();
+
// foreach(var pos in chunkDataPositionsToCreate)
// {
// if(taskTokenSource.IsCancellationRequested)
@@ -416,7 +222,6 @@ private UniTask> CalculateWorldChunk
// };
Profiler.EndThreadProfiling();
- return dataDict;
}, true, taskTokenSource.Token);
}
@@ -438,7 +243,20 @@ private UniTask CalculateFeatures(HashSet chunkDataPositionsToCreate
},true, taskTokenSource.Token);
}
-
+ private void CalculateFeature(Vector3Int pos)
+ {
+ var data = worldData.chunkDataDict[pos];
+ terrainGenerator.GenerateFeatures(data, mapSeedOffset);
+ data.isGenerated = true;
+
+ actionOnChunkDone.TryGetValue(pos, out var action);
+ action?.Invoke();
+
+ if (!isInPlayMode)
+ {
+ dataToMeshQueue.Enqueue(data);
+ }
+ }
IEnumerator ChunkCreationCoroutine(ConcurrentDictionary meshDataConDict, Vector3Int playerPos)
{
@@ -457,7 +275,7 @@ IEnumerator ChunkCreationCoroutine(ConcurrentDictionary me
{
IsWorldCreated = true;
OnWorldCreated?.Invoke();
- NetworkClient.Send(new WorldServer.SpawnPlayerMessage(SteamClient.SteamId));
+ // NetworkClient.Send(new WorldServer.SpawnPlayerMessage(SteamClient.SteamId));
}
if (!Application.isPlaying)
diff --git a/Assets/_Scripts/World/World_Serialization.cs b/Assets/_Scripts/World/World_Serialization.cs
index 6b85b862..39aa5e7b 100644
--- a/Assets/_Scripts/World/World_Serialization.cs
+++ b/Assets/_Scripts/World/World_Serialization.cs
@@ -10,6 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
+using Steamworks;
using UnityEngine;
using Debug = UnityEngine.Debug;
@@ -96,17 +97,19 @@ public void SavePlayer(WorldServer.SavePlayerMessage message)
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ".minecraftUnity/saves/" + worldName + $"/playerdata/{message.steamId}.json");
File.WriteAllText(path, JsonUtility.ToJson(message));
+ // get the steam username from the id
+ Debug.Log($"Saved player {message.steamId}");
}
- public UniTask> LoadChunksAsync(IEnumerable chunks, string worldName)
+ public UniTask LoadChunksAsync(IEnumerable chunks, string worldName)
{
return UniTask.RunOnThreadPool(() =>
{
// TODO: for some reason when you use more than 1 thread it crashes because of type mismatch in chunkSection deserialize
// this seems to be a bug in unity
- var dict = new ConcurrentDictionary();
// Parallel.ForEach(chunks, chunkPos =>
// {
+ loadStopwatch.Start();
foreach (var chunkPos in chunks)
{
if(taskTokenSource.IsCancellationRequested)
@@ -133,11 +136,24 @@ public UniTask> LoadChunksAsync(IEnu
}
var chunkSaveData = JsonUtility.FromJson(json);
- dict.TryAdd(chunkPos, ChunkData.Deserialize(chunkSaveData));
+ var chunkData = ChunkData.Deserialize(chunkSaveData);
+ if (worldData.chunkDataDict.TryAdd(chunkPos,chunkData))
+ {
+ actionOnChunkDone.TryGetValue(chunkPos, out var action);
+ action?.Invoke();
+ if (!isInPlayMode)
+ {
+ dataToMeshQueue.Enqueue(chunkData);
+ }
+ }
+ else
+ {
+ Debug.LogError($"Failed to add chunk data '{chunkPos}' to doneData");
+ }
}
+ loadStopwatch.Stop();
// });
- return dict;
}, cancellationToken: taskTokenSource.Token);
}
diff --git a/Assets/_Scripts/World/World_UpdateLoop.cs b/Assets/_Scripts/World/World_UpdateLoop.cs
index d89c909a..4d10a838 100644
--- a/Assets/_Scripts/World/World_UpdateLoop.cs
+++ b/Assets/_Scripts/World/World_UpdateLoop.cs
@@ -1,8 +1,14 @@
-using System.Collections;
+using System;
+using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Cysharp.Threading.Tasks;
using Mirror;
+using Steamworks;
+using Unity.VisualScripting;
+using UnityEditor;
using UnityEngine;
public partial class World
@@ -14,6 +20,18 @@ public partial class World
public object chunkUpdateThreadLock = new object();
private Thread updateThread;
+ public readonly Vector3Int[] neighborOffsets =
+ {
+ new(-16, 0, -16),
+ new(-16, 0, +16),
+ new(+16, 0, -16),
+ new(+16, 0, +16),
+ new(-16, 0, 0),
+ new(+16, 0, 0),
+ new(0, 0, -16),
+ new(0, 0, +16)
+ };
+
public void UpdateLoop()
{
while (!disabled)
@@ -22,6 +40,55 @@ public void UpdateLoop()
{
UpdateChunks();
}
+
+ // World generation queues
+ if (doneDataQueue.Count > 0)
+ {
+ try
+ {
+ // Parallel.For(0, doneDataQueue.Count, _ =>
+ // {
+ doneDataQueue.TryDequeue(out var chunkPos);
+
+ // Check if neighbor chunks are ready
+ if (neighborOffsets.All(o => worldData.chunkDataDict.ContainsKey(chunkPos + o)))
+ {
+ featureStopwatch.Start();
+ CalculateFeature(chunkPos);
+ featureStopwatch.Stop();
+ }
+ else
+ {
+ doneDataQueue.Enqueue(chunkPos);
+ }
+ // });
+ }
+ catch (ThreadAbortException)
+ {
+ }
+ }
+
+ if (dataToMeshQueue.Count > 0)
+ {
+ // Parallel.For(0, dataToMeshQueue.Count, _ =>
+ // {
+ meshStopwatch.Start();
+ dataToMeshQueue.TryDequeue(out var chunkData);
+
+ if(neighborOffsets.All(o => worldData.chunkDataDict.ContainsKey(chunkData.worldPos + o) /*&& worldData.chunkDataDict[chunkData.worldPos + o].isGenerated*/))
+ {
+ Lighting.RecastSunLightFirstTime(chunkData);
+
+ CreateMeshData(chunkData);
+ }
+ else
+ {
+ dataToMeshQueue.Enqueue(chunkData);
+ }
+
+ meshStopwatch.Stop();
+ // });
+ }
}
}
@@ -53,21 +120,93 @@ public void AddChunkToUpdate(ChunkRenderer chunk, bool first = false)
}
}
+ #if UNITY_EDITOR
+ public static void EditorUpdate()
+ {
+ try
+ {
+ if (Application.isPlaying) return;
+
+ var world = Instance;
+ // Debug.Log("Editor Update");
+ if (world.meshToRenderQueue.Count > 0)
+ {
+ world.meshToRenderQueue.TryDequeue(out var data);
+ world.CreateChunk(world.worldData, data.Key, data.Value);
+
+ if (!world.IsWorldCreated && world.worldData.chunkDict.Count == WorldDataHelper.GetChunkPositionsInRenderDistance(Instance, Vector3Int.zero).Count)
+ {
+ world.IsWorldCreated = true;
+ // if (Application.isPlaying)
+ // {
+ // world.OnWorldCreated?.Invoke();
+ // NetworkClient.Send(new WorldServer.SpawnPlayerMessage(SteamClient.SteamId));
+ // }
+
+ Debug.Log($"Data generation took {world.dataStopwatch.ElapsedMilliseconds}ms");
+ Debug.Log($"Loading took {world.loadStopwatch.ElapsedMilliseconds}ms");
+ Debug.Log($"Feature generation took {world.featureStopwatch.ElapsedMilliseconds}ms");
+ Debug.Log($"Mesh generation took {world.meshStopwatch.ElapsedMilliseconds}ms");
+ Debug.Log($"Time spent generating noise {MyNoise.noiseStopwatch.ElapsedMilliseconds}ms");
+ Debug.Log($"Time spent generating 3D noise {MyNoise.noise3DStopwatch.ElapsedMilliseconds}ms");
+ Debug.Log($"World created in {world.fullStopwatch.ElapsedMilliseconds}ms");
+ world.fullStopwatch.Stop();
+ world.fullStopwatch.Reset();
+ world.dataStopwatch.Stop();
+ world.dataStopwatch.Reset();
+ world.loadStopwatch.Stop();
+ world.loadStopwatch.Reset();
+ world.featureStopwatch.Stop();
+ world.featureStopwatch.Reset();
+ world.meshStopwatch.Stop();
+ world.meshStopwatch.Reset();
+ }
+ }
+ }
+ catch (NullReferenceException e)
+ {
+ Debug.LogError(e);
+ EditorApplication.update -= EditorUpdate;
+ }
+ }
+ #endif
+
private void Update()
{
+ if(WorldServer.IsDedicated) return;
+
//TODO: remove this because it is slow
Camera.main.backgroundColor = Color.Lerp(nightColor,dayColor , skyLightMultiplier);
- // if (Input.GetKeyDown(KeyCode.L))
- // {
- // WorldServer.instance.RequestChunk(Vector3Int.zero);
- // }
+ if (Input.GetKeyDown(KeyCode.L))
+ {
+ NetworkClient.Send(new WorldServer.ChunkRequestMessage(Vector3Int.zero, renderDistance));
+ }
if (chunksToUpdateMesh.Count > 0)
{
var data = chunksToUpdateMesh.Dequeue();
data.chunk.RenderMesh(data.meshData);
}
+
+ if (meshToRenderQueue.Count > 0)
+ {
+ meshToRenderQueue.TryDequeue(out var data);
+ WorldDataHelper.RemoveChunk(this,data.Key);
+ CreateChunk(worldData, data.Key, data.Value);
+
+ if (!IsWorldCreated)
+ {
+ IsWorldCreated = true;
+ OnWorldCreated?.Invoke();
+ NetworkClient.Send(new WorldServer.SpawnPlayerMessage(SteamClient.SteamId));
+
+ fullStopwatch.Stop();
+ Debug.Log($"World created in {fullStopwatch.ElapsedMilliseconds}ms");
+ fullStopwatch.Reset();
+ }
+ UniTask.NextFrame();
+ }
}
public void UpdateBlocks()
diff --git a/Packages/manifest.json b/Packages/manifest.json
index 6bf24f93..6b09155c 100644
--- a/Packages/manifest.json
+++ b/Packages/manifest.json
@@ -4,7 +4,7 @@
"com.unity.collab-proxy": "1.15.15",
"com.unity.feature.2d": "1.0.0",
"com.unity.feature.development": "1.0.1",
- "com.unity.ide.rider": "3.0.13",
+ "com.unity.ide.rider": "3.0.14",
"com.unity.ide.visualstudio": "2.0.14",
"com.unity.ide.vscode": "1.2.5",
"com.unity.probuilder": "5.0.4",
diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json
index 30d4e99f..359186d0 100644
--- a/Packages/packages-lock.json
+++ b/Packages/packages-lock.json
@@ -153,7 +153,7 @@
}
},
"com.unity.ide.rider": {
- "version": "3.0.13",
+ "version": "3.0.14",
"depth": 0,
"source": "registry",
"dependencies": {
diff --git a/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset
index abb382fc..ccbf361e 100644
--- a/ProjectSettings/DynamicsManager.asset
+++ b/ProjectSettings/DynamicsManager.asset
@@ -19,7 +19,7 @@ PhysicsManager:
m_ClothInterCollisionStiffness: 0.2
m_ContactsGeneration: 1
m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
- m_AutoSimulation: 1
+ m_AutoSimulation: 0
m_AutoSyncTransforms: 0
m_ReuseCollisionCallbacks: 0
m_ClothInterCollisionSettingsToggle: 0
diff --git a/ProjectSettings/RiderScriptEditorPersistedState.asset b/ProjectSettings/RiderScriptEditorPersistedState.asset
index 614f8565..0fffa934 100644
--- a/ProjectSettings/RiderScriptEditorPersistedState.asset
+++ b/ProjectSettings/RiderScriptEditorPersistedState.asset
@@ -12,4 +12,4 @@ MonoBehaviour:
m_Script: {fileID: 0}
m_Name:
m_EditorClassIdentifier: Unity.Rider.Editor:Packages.Rider.Editor:RiderScriptEditorPersistedState
- lastWriteTicks: -8585514039852941166
+ lastWriteTicks: -8585509687508064223