public void Reset( Vector2 worldSize, byte[] terrainV2Data, bool loadingDataFromBeforeDigging, byte[] legacyData, ulong[] customStyleWorkshopIds = null, string simpleData = null) { Debug.Assert(worldSize.magnitude > 0f); Util.Log($"Resetting terrain!"); float groundSizeX = worldSize.x; float groundSizeZ = worldSize.y; var newDims = new Int3( Mathf.CeilToInt(groundSizeX / BLOCK_SIZE.x), BlocksYCount, Mathf.CeilToInt(groundSizeZ / BLOCK_SIZE.z)); Debug.Assert(newDims >= new Int3(10, 10, 10)); Int3 newBotCenter = new Int3(newDims.x / 2, 0, newDims.z / 2); // If old data exists, make sure we restore it. This is the resize use case. byte[] restoreData = null; Int3 restoreDims = Int3.zero(); if (terrainV2 != null) { var oldDims = terrainV2.GetWorldDimensions(); restoreDims = Int3.Min(oldDims, newDims); Int3 start = oldDims / 2 - restoreDims / 2; Int3 end = start + restoreDims; Debug.Assert(start >= Int3.zero()); Debug.Assert(end <= oldDims); restoreData = terrainV2.Serialize(start, end); Destroy(terrainV2.gameObject); terrainV2 = null; } using (Util.Profile("terrainV2 instantiate")) terrainV2 = Instantiate(terrainV2Prefab, Vector3.zero, Quaternion.identity, this.transform); using (Util.Profile("terrainV2 SetWorldDimensions")) terrainV2.SetWorldDimensions(newDims); terrainV2.SetRootOffset(new Vector3( -newDims.x / 2 * 2.5f, BlocksYStart * BlockHeight + BlockHeight / 2f, -newDims.z / 2 * 2.5f)); using (Util.Profile("Create color terrain textures")) { int texSize = terrainV2.GetStyleTextureResolution(); // Color32 way faster in general than Color. for (int i = 0; i < NumSolidColorStyles; i++) { Color32[] pixels = new Color32[texSize * texSize]; Color32 color32 = rendering.blockColors[i]; for (int j = 0; j < pixels.Length; j++) { pixels[j] = color32; } terrainV2.SetStyleTextures(i, pixels); // color // 10 is orange if (i == (int)BlockStyle.SolidColor10) { fallbackTexture = pixels; } } } Debug.Assert(fallbackTexture != null, "Could not find fallbackTexture?"); // TODO why do we do these specifically? Are they not read in via the loop below? terrainV2.SetStyleTextures((int)BlockStyle.Stone, terrainV2Textures[4].GetPixels32()); // stone terrainV2.SetStyleTextures((int)BlockStyle.Space, terrainV2Textures[1].GetPixels32()); // metal terrainV2.SetStyleTextures((int)BlockStyle.Grass, terrainV2Textures[8].GetPixels32(), terrainV2Textures[7].GetPixels32(), terrainV2Textures[6].GetPixels32()); // grass terrainV2.SetStyleTextures((int)BlockStyle.SnowRock, terrainV2Textures[11].GetPixels32(), terrainV2Textures[10].GetPixels32(), terrainV2Textures[9].GetPixels32()); // snow foreach (object obj in Enum.GetValues(typeof(BlockStyle))) { BlockStyle style = (BlockStyle)obj; if ((int)style <= (int)BlockStyle.SnowRock) { // We hard code this above for now. continue; } Color32[] topOrAtlas = null; Color32[] side = null; Color32[] overflow = null; foreach (var tex in terrainV2Textures) { if (tex == null) { continue; } if (!tex.name.StartsWith(style.ToString().ToLowerInvariant())) { continue; } if (tex.name.EndsWith("-top") || tex.name == style.ToString().ToLowerInvariant()) { topOrAtlas = tex.GetPixels32(); } else if (tex.name.EndsWith("-side-ceiling")) { side = tex.GetPixels32(); } else if (tex.name.EndsWith("-overflow")) { overflow = tex.GetPixels32(); } } if (topOrAtlas == null) { Util.LogWarning($"Had to use fallback texture for terrain style {style}. side={side}, overflow={overflow}"); topOrAtlas = fallbackTexture; } if (side != null) { #if UNITY_EDITOR Debug.Assert(overflow != null, $"{style.ToString()} style has a side texture but not an overflow?"); #endif } else { if (overflow != null) { Util.LogWarning($"Style {style} had an overflow texture but not a side? IGNORING overflow."); overflow = null; } } terrainV2.SetStyleTextures((int)style, topOrAtlas, side, overflow); } // Custom styles this.customStyleWorkshopIds.Clear(); if (customStyleWorkshopIds != null) { this.customStyleWorkshopIds.AddRange(customStyleWorkshopIds); } UpdateCustomStyleWorkshopIds(); if (restoreData != null) { terrainV2.Deserialize(restoreData, (newDims / 2 - restoreDims / 2)); } if (legacyData != null) { LoadLegacyTerrainData(legacyData); // But move all the blocks to our new system. using (Util.Profile("legacySync")) { foreach (var args in database.EnumerateBlocks()) { terrainV2.SetCell(args.cell.ToInt3() + GetV2Offset(), (int)args.value.style, (int)args.value.blockType - 1, (int)args.value.direction); } } } if (terrainV2Data != null) { Util.Log($"loading v2 data of {terrainV2Data.Length} bytes"); using (Util.Profile("terrainV2 Deserialize")) terrainV2.Deserialize(terrainV2Data); // Legacy upgrade if (loadingDataFromBeforeDigging) { // The serialized data was before digging. We need to move it up, effectively. Debug.Assert(BlocksYStart < 0); // Copy... byte[] temp = terrainV2.Serialize( Int3.zero(), newDims.WithY(newDims.y + BlocksYStart)); // Move up.. terrainV2.Deserialize( temp, Int3.zero().WithY(-BlocksYStart)); // At this point, we actually have 2 copies of the terrain, offset by // some Y! heh. But the SetSlices call below will deal with that. } } if (loadingDataFromBeforeDigging) { // Fill in the ground. BlockStyle style = BlockStyle.Grass; switch (stage.GetGroundType()) { case GameBuilderStage.GroundType.Snow: style = BlockStyle.SnowRock; break; case GameBuilderStage.GroundType.SolidColor: case GameBuilderStage.GroundType.Space: case GameBuilderStage.GroundType.Grass: default: style = BlockStyle.Grass; break; } terrainV2.SetSlices(0, (0 - BlocksYStart), (int)style, 0, 0); } if (!simpleData.IsNullOrEmpty()) { byte[] zippedBytes = System.Convert.FromBase64String(simpleData); using (var zippedStream = new System.IO.MemoryStream(zippedBytes, 0, zippedBytes.Length)) using (var unzipped = new System.IO.Compression.GZipStream(zippedStream, System.IO.Compression.CompressionMode.Decompress)) using (System.IO.BinaryReader reader = new System.IO.BinaryReader(unzipped)) { int version = reader.ReadUInt16(); // Unused. Debug.Assert(version == 0, $"Unknown simpleData version: {version}"); uint numBlocks = reader.ReadUInt32(); Util.Log($"reading in {numBlocks} from simpleData"); for (int i = 0; i < numBlocks; i++) { short x = reader.ReadInt16(); short y = reader.ReadInt16(); short z = reader.ReadInt16(); byte shape = reader.ReadByte(); byte direction = reader.ReadByte(); ushort style = reader.ReadUInt16(); this.SetCellValue( new Cell(x, y, z), new CellValue { blockType = (BlockShape)shape, direction = (BlockDirection)direction, style = (BlockStyle)style }); } } } // Now mark chunks with actors in them as important foreach (var actor in engine.EnumerateActors()) { // Non-dynamic-physics actors don't need terrain to exist...well, less so. if (!actor.GetEnablePhysics()) { continue; } var pos = actor.GetSpawnPosition(); Int3 cell = GetContainingCell(pos).ToInt3(); terrainV2.ReportRigidbodyAt((cell + GetV2Offset())); } }
public byte[] SerializeTerrainV2() { Debug.Assert(terrainV2 != null, "SerializeTerrainV2 called before Reset was called"); return(terrainV2.Serialize()); }