private void OnClientGameTick(float dt) { quarterSecAccum += dt; if (quarterSecAccum > 0.25f) { clientClimateCond = capi.World.BlockAccessor.GetClimateAt(plrPos); quarterSecAccum = 0; } simLightning.ClientTick(dt); for (int i = 0; i < 4; i++) { WeatherSimulationRegion sim = adjacentSims[i]; if (sim == dummySim) { continue; } sim.TickEvery25ms(dt); } simSounds.Update(dt); }
private void onThreadStart() { while (!isShuttingDown) { Thread.Sleep(5); if (shouldPauseThread) { isThreadPaused = true; continue; } isThreadPaused = false; int i = 0; while (chunkColsstoCheckQueue.Count > 0 && i++ < 10) { Vec2i chunkCoord; lock (chunkColsstoCheckQueue) { chunkCoord = chunkColsstoCheckQueue.Dequeue(); } int regionX = (chunkCoord.X * chunksize) / regionsize; int regionZ = (chunkCoord.Y * chunksize) / regionsize; WeatherSimulationRegion sim = ws.getOrCreateWeatherSimForRegion(regionX, regionZ); IServerMapChunk mc = sapi.WorldManager.GetMapChunk(chunkCoord.X, chunkCoord.Y); if (mc != null && sim != null) { UpdateSnowLayerOffThread(sim, mc, chunkCoord); } } } }
private string getWeatherInfo <T>(IPlayer player) where T : WeatherSystemBase { T wsys = api.ModLoader.GetModSystem <T>(); Vec3d plrPos = player.Entity.SidedPos.XYZ; BlockPos pos = plrPos.AsBlockPos; var wreader = wsys.getWeatherDataReaderPreLoad(); wreader.LoadAdjacentSimsAndLerpValues(plrPos, 1); int regionX = (int)pos.X / api.World.BlockAccessor.RegionSize; int regionZ = (int)pos.Z / api.World.BlockAccessor.RegionSize; WeatherSimulationRegion weatherSim; long index2d = wsys.MapRegionIndex2D(regionX, regionZ); wsys.weatherSimByMapRegion.TryGetValue(index2d, out weatherSim); if (weatherSim == null) { return("weatherSim is null. No idea what to do here"); } StringBuilder sb = new StringBuilder(); sb.AppendLine(string.Format("Weather by region:")); // (lerp-lr: {0}, lerp-bt: {1}), wsys.lerpLeftRight.ToString("0.##"), wsys.lerpTopBot.ToString("0.##"))); string[] cornerNames = new string[] { "tl", "tr", "bl", "br" }; //topBlendedWeatherData.SetLerped(adjacentSims[0].weatherData, adjacentSims[1].weatherData, (float)lerpLeftRight); //botBlendedWeatherData.SetLerped(adjacentSims[2].weatherData, adjacentSims[3].weatherData, (float)lerpLeftRight); //blendedWeatherData.SetLerped(topBlendedWeatherData, botBlendedWeatherData, (float)lerpTopBot); double tlLerp = GameMath.BiLerp(1, 0, 0, 0, wreader.LerpLeftRight, wreader.LerpTopBot); double trLerp = GameMath.BiLerp(0, 1, 0, 0, wreader.LerpLeftRight, wreader.LerpTopBot); double blLerp = GameMath.BiLerp(0, 0, 1, 0, wreader.LerpLeftRight, wreader.LerpTopBot); double brLerp = GameMath.BiLerp(0, 0, 0, 1, wreader.LerpLeftRight, wreader.LerpTopBot); int[] lerps = new int[] { (int)(100 * tlLerp), (int)(100 * trLerp), (int)(100 * blLerp), (int)(100 * brLerp) }; for (int i = 0; i < 4; i++) { WeatherSimulationRegion sim = wreader.AdjacentSims[i]; if (sim == wsys.dummySim) { sb.AppendLine(string.Format("{0}: missing", cornerNames[i])); } else { /*sb.AppendLine(string.Format("{10}% of {0}@{8}/{9}: {1}% {2}, {3}% {4}. Prec: {5}, Wind: {6} (v={7})", * cornerNames[i], (int)(100 * sim.Weight), sim.NewWePattern.GetWeatherName(), (int)(100 - 100 * sim.Weight), * sim.OldWePattern.GetWeatherName(), sim.weatherData.PrecIntensity.ToString("0.###"), * sim.CurWindPattern.GetWindName(), sim.GetWindSpeed(pos.Y).ToString("0.###"), * sim.regionX, sim.regionZ, * lerps[i] * )) ;*/ sb.AppendLine(string.Format("{9}% of {0}@{7}/{8}: {1}% {2}, {3}% {4}. Wind: {5}, Event: {10} (v={6})", cornerNames[i], (int)(100 * sim.Weight), sim.NewWePattern.GetWeatherName(), (int)(100 - 100 * sim.Weight), sim.OldWePattern.GetWeatherName(), sim.CurWindPattern.GetWindName(), sim.GetWindSpeed(pos.Y).ToString("0.###"), sim.regionX, sim.regionZ, lerps[i], sim.CurWeatherEvent.config.Code )); } } //wsys.updateAdjacentAndBlendWeatherData(); //WeatherDataSnapshot wData = wsys.blendedWeatherData; //sb.AppendLine(string.Format(string.Format("Blended:\nPrecipitation: {0}, Particle size: {1}, Type: {2}, Wind speed: {3}", wData.PrecIntensity, wData.PrecParticleSize, wData.BlendedPrecType, wsys.GetWindSpeed(plrPos)))); ClimateCondition climate = api.World.BlockAccessor.GetClimateAt(player.Entity.Pos.AsBlockPos, EnumGetClimateMode.NowValues); sb.AppendLine(string.Format("Current precipitation: {0}%", (int)(climate.Rainfall * 100f))); sb.AppendLine(string.Format("Current wind: {0}", API.Config.GlobalConstants.CurrentWindSpeedClient)); return(sb.ToString()); }
private UpdateSnowLayerChunk GetSnowUpdate(WeatherSimulationRegion simregion, IServerMapChunk mc, Vec2i chunkPos, IWorldChunk[] chunksCol) { double lastSnowAccumUpdateTotalHours = mc.GetModdata <double>("lastSnowAccumUpdateTotalHours"); double startTotalHours = lastSnowAccumUpdateTotalHours; int reso = WeatherSimulationRegion.snowAccumResolution; SnowAccumSnapshot sumsnapshot = new SnowAccumSnapshot() { SnowAccumulationByRegionCorner = new FloatDataMap3D(reso, reso, reso) }; float[] sumdata = sumsnapshot.SnowAccumulationByRegionCorner.Data; // Can't grow bigger than one full snow block float max = ws.GeneralConfig.SnowLayerBlocks.Count + 0.6f; int len = simregion.SnowAccumSnapshots.Length; int i = simregion.SnowAccumSnapshots.Start; int newCount = 0; lock (WeatherSimulationRegion.snowAccumSnapshotLock) { while (len-- > 0) { SnowAccumSnapshot hoursnapshot = simregion.SnowAccumSnapshots[i]; i = (i + 1) % simregion.SnowAccumSnapshots.Length; if (hoursnapshot == null || lastSnowAccumUpdateTotalHours >= hoursnapshot.TotalHours) { continue; } float[] snowaccumdata = hoursnapshot.SnowAccumulationByRegionCorner.Data; for (int j = 0; j < snowaccumdata.Length; j++) { sumdata[j] = GameMath.Clamp(sumdata[j] + snowaccumdata[j], -max, max); } lastSnowAccumUpdateTotalHours = Math.Max(lastSnowAccumUpdateTotalHours, hoursnapshot.TotalHours); newCount++; } } if (newCount == 0) { return(null); } bool ignoreOldAccum = false; if (lastSnowAccumUpdateTotalHours - startTotalHours >= sapi.World.Calendar.DaysPerYear * sapi.World.Calendar.HoursPerDay) { ignoreOldAccum = true; } UpdateSnowLayerChunk ch = UpdateSnowLayer(sumsnapshot, ignoreOldAccum, mc, chunkPos, chunksCol); if (ch != null) { ch.LastSnowAccumUpdateTotalHours = lastSnowAccumUpdateTotalHours; ch.Coords = chunkPos.Copy(); } return(ch); }
public void UpdateSnowLayerOffThread(WeatherSimulationRegion simregion, IServerMapChunk mc, Vec2i chunkPos) { #region Tyrons brain cloud // Trick 1: Each x/z coordinate gets a "snow accum" threshold by using a locational random (murmurhash3). Once that threshold is reached, spawn snow. If its doubled, spawn 2nd layer of snow. => Patchy "fade in" of snow \o/ // Trick 2: We store a region wide snow accum value for the ground level and the map ceiling level. We can now interpolate between those values for each Y-Coordinate \o/ // Trick 3: We loop through each x/z block in a separate thread, then hand over "place snow" tasks to the main thread // Trick 4: Lets pre-gen 50 random shuffles for every x/z coordinate of a chunk. Loop through the region chunks, check which one is loaded and select one random shuffle from the list, then iterate over every x/z coord // Trick 5: Snowed over blocks: // - New VSMC util: "Automatically Try to add a snow cover to all horizontal faces" // - New Block property: SnowCoverableShape. // - Block.OnJsonTesselation adds snow adds cover shape to the sourceMesh!! // Trick 6: Turn Cloud Patterns into a "dumb slave system". They are visual information only, so lets make them follow internal mechanisms. // - Create a precipitation perlin noise generator. If the precipitation value goes above or below a certain value, we force the cloud pattern system to adapt to a fitting pattern // => We gain easy to probe, deterministic precipitation values!! // => We gain the ability to do unloaded chunk snow accumulation and unloaded chunk farmland rain-wetness accum // Trick 6 v2.0: // Rain clouds are simply overlaid onto the normal clouds. // Questions: // - Q1: When should it hail now? // - Q2: How is particle size determined? // - Q3: When should there be thunder? // - Q4: How to control the precipitation by command? // A1/A3: What if we read the slope of precipitation change. If there is a drastic increase of rain fall launch a // a. wind + thunder event // b. thunder event // c. rarely a hail event // d. extra rarely thunder + hail event // A2: Particle size is determiend by precipitation intensity // Trick 7 v2.0 // - Hail and Thunder are also triggered by a perlin noise generator. That way I don't need to care about event range. // A4: /weather setprecip [auto or 0..1] // - Q5: How do we overlay rain clouds onto the normal clouds? // Q5a: Will they be hardcoded? Or configurable? // Q5b: How does the overlay work? Lerp? // Q5c: Rain cloud intensity should relate to precip level. // How? Lerp from zero to max rain clouds? Multiple cloud configs and lerp between them? // - A5a: Configurable // A5b: Lerp. // A5c: Single max rain cloud config seems sufficient // TODO: // 1. Rain cloud overlay // 2. Snow accum // 3. Hail, Thunder perlin noise // 4. Done? // Idea 8: // - F**K the region based weather sim. // - Generate clouds patterns like you generate terrain from landforms // - Which is grid based indices, neatly abstracted with LerpedIndex2DMap and nicely shaped with domain warping // - Give it enough padding to ensure domain warping does not go out of bounds // - Every 2-3 minutes regenerate this map in a seperate thread, cloud renderer lerps between old and new map. // - Since the basic indices input is grid based, we can cycle those individually through time // for a future version // Hm. Maybe one noise generator for cloud coverage? // => Gain the ability to affect local temperature based on cloud coverage // Hm. Or maybe one noise generator for each cloud pattern? // => Gain the abillity for small scale and very large scale cloud patterns // Maybe even completely ditch per-region simulation? // => Gain the ability for migrating weather patterns // but then what will determine the cloud pattern? // Region-less Concept: // Take an LCGRandom. Use xpos and zpos+((int)totalDays) / 5 for coords // Iterate over every player // - iterate over a 20x20 chunk area around it (or max view dist + 5 chunks) // - domain warp x/z coords. use those coords to init position seed on lcgrand. get random value // - store in an LerpedWeightedIndex2DMap // Iterate over every cloud tile // - read cloud pattern data from the map // Snow accum needs to take the existing world information into account, i.e. current snow level // We should probably // - Store snow accumulation as a float value in mapchunkdata as Dictionary<BlockPos, float> // - Every 3 seconds or so, "commit" that snow accum into actual snow layer blocks, i.e. if accum >= 1 then add one snow layer and do accum-=1 #endregion UpdateSnowLayerChunk ch = new UpdateSnowLayerChunk() { Coords = chunkPos }; // Lets wait until we're done with the current job for this chunk lock (updateSnowLayerQueue) { if (updateSnowLayerQueue.Contains(ch)) { return; } } if (simregion == null) { return; } double nowTotalHours = ws.api.World.Calendar.TotalHours; if (nowTotalHours - simregion.LastUpdateTotalHours > 1) // Lets wait until WeatherSimulationRegion is done updating { return; } ch = GetSnowUpdate(simregion, mc, chunkPos, null); if (ch != null) { lock (updateSnowLayerQueue) { updateSnowLayerQueue.Enqueue(ch); } } }