diff --git a/.gitignore b/.gitignore index 4fd1aef..64a0628 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,17 @@ .idea/ -*/bin -*/obj -.vs/ packages/ -/bin/Debug -/Modules/Main/obj/Debug +*.user +*.lock +*.lock.json +.vs/ +_ReSharper* +*.suo +*.VC.db +*.vshost.exe +*.manifest +*.sdf +[Bb]in/ +obj/ +*/[Bb]in/ +*/[Oo]bj/ +Cache/ diff --git a/Core/ApplicationControl.cs b/Core/ApplicationControl.cs new file mode 100644 index 0000000..4c16dff --- /dev/null +++ b/Core/ApplicationControl.cs @@ -0,0 +1,43 @@ +// +// NEWorld/Core: ApplicationControl.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// + +namespace Core +{ + public static class ApplicationControl + { + public class Launch + { + } + + public class Shutdown + { + } + + public static void DoLaunch() + { + AssemblyReflectiveScanner.UpdateDomainAssemblies(); + EventBus.Broadcast(null, new Launch()); + } + + public static void DoShutdown() + { + EventBus.Broadcast(null, new Shutdown()); + } + } +} diff --git a/Core/AssemblyReflectiveScanner.cs b/Core/AssemblyReflectiveScanner.cs new file mode 100644 index 0000000..4b93882 --- /dev/null +++ b/Core/AssemblyReflectiveScanner.cs @@ -0,0 +1,142 @@ +// +// NEWorld/Core: AssemblyReflectiveScanner.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Core +{ + public sealed class DeclareNeWorldAssemblyAttribute : Attribute + { + } + + public sealed class DeclareAssemblyReflectiveScannerAttribute : Attribute + { + } + + public interface IAssemblyReflectiveScanner + { + void ProcessType(Type type); + } + + internal static class AssemblyReflectiveScanner + { + // Only for conflict resolve for multi-thread load + private static HashSet _processed = new HashSet(); + private static readonly object ProcessLock = new object(); + private static readonly List Scanners = new List(); + private static readonly List Scanned = new List(); + + internal static void UpdateDomainAssemblies() + { + AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoadServiceRegisterAgent; + var snapshot = AppDomain.CurrentDomain.GetAssemblies(); + foreach (var assembly in snapshot) + if (!CheckIfAssemblyProcessed(assembly)) + ScanAssembly(assembly); + + lock (ProcessLock) + { + _processed = null; + lock (Scanned) + { + foreach (var assembly in Scanned) ProcessAssembly(assembly); + } + } + } + + private static bool CheckIfAssemblyProcessed(Assembly assembly) + { + lock (ProcessLock) + { + return _processed != null && (bool) (_processed?.Contains(assembly.GetName())); + } + } + + private static void OnAssemblyLoadServiceRegisterAgent(object sender, AssemblyLoadEventArgs args) + { + ScanAssembly(args.LoadedAssembly); + } + + private static void ScanForAssemblyScanners(Assembly assembly) + { + foreach (var type in assembly.GetExportedTypes()) + if (CheckScannerType(type)) + InitializeScanner(type); + } + + private static bool CheckScannerType(Type type) + { + return type.IsDefined(typeof(DeclareAssemblyReflectiveScannerAttribute), false) && + typeof(IAssemblyReflectiveScanner).IsAssignableFrom(type); + } + + private static void InitializeScanner(Type type) + { + var currentScanner = (IAssemblyReflectiveScanner) Activator.CreateInstance(type); + lock (Scanners) + { + Scanners.Add(currentScanner); + } + + lock (ProcessLock) + { + if (_processed != null) return; + lock (Scanned) + { + foreach (var assembly in Scanned) + foreach (var target in assembly.GetExportedTypes()) + currentScanner.ProcessType(target); + } + } + } + + private static void ScanAssembly(Assembly assembly) + { + lock (ProcessLock) + { + _processed?.Add(assembly.GetName(true)); + } + + if (!assembly.IsDefined(typeof(DeclareNeWorldAssemblyAttribute), false)) return; + + ScanForAssemblyScanners(assembly); + + lock (Scanned) + { + Scanned.Add(assembly); + } + + lock (ProcessLock) + { + if (_processed == null) ProcessAssembly(assembly); + } + } + + private static void ProcessAssembly(Assembly assembly) + { + foreach (var target in assembly.GetExportedTypes()) + lock (Scanners) + { + foreach (var currentScanner in Scanners) currentScanner.ProcessType(target); + } + } + } +} \ No newline at end of file diff --git a/Core/Core.csproj b/Core/Core.csproj index 8814d3f..7396d2f 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -1,75 +1,12 @@ - - - + + - Debug - AnyCPU - {ECB0E309-625F-4A24-926D-D1D23C1B7693} - Library - Properties - Core - Core - v4.7.1 - 512 - 7.2 + netstandard2.0 - - AnyCPU - true - full - false - ..\bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - - - AnyCPU - pdbonly - true - ..\bin\Release\ - TRACE - prompt - 4 - true - - - - - ..\packages\MsgPack.Cli.1.0.0\lib\net46\MsgPack.dll - True - - - - - - - - - - - - - - - - - - - - - - + - + + + - - - \ No newline at end of file + diff --git a/Core/Core.xkpkg b/Core/Core.xkpkg new file mode 100644 index 0000000..2602096 --- /dev/null +++ b/Core/Core.xkpkg @@ -0,0 +1,17 @@ +!Package +SerializedVersion: {Assets: 3.1.0.0} +Meta: + Name: Core + Version: 1.0.0 + Authors: [] + Owners: [] + Dependencies: null +AssetFolders: + - Path: !dir Assets +ResourceFolders: + - !dir Resources +OutputGroupDirectories: {} +ExplicitFolders: [] +Bundles: [] +TemplateFolders: [] +RootAssets: [] diff --git a/Core/EventBus.cs b/Core/EventBus.cs new file mode 100644 index 0000000..89c53a1 --- /dev/null +++ b/Core/EventBus.cs @@ -0,0 +1,193 @@ +// +// NEWorld/Core: EventBus.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Core +{ + public class DeclareBusEventHandlerAttribute : Attribute + { + } + + public static class EventBus + { + public delegate void EventHandler(object sender, T payload); + + private static readonly Dictionary EventHandlers = new Dictionary(); + + public static void Add(EventHandler handler) + { + var slot = GetOrCreateSlot(); + slot.Rwl.EnterWriteLock(); + slot.Handlers += handler; + slot.Rwl.ExitWriteLock(); + } + + private static Slot GetOrCreateSlot() + { + Slot slot; + lock (EventHandlers) + { + if (EventHandlers.TryGetValue(typeof(T), out var value)) + { + slot = (Slot) value; + } + else + { + slot = new Slot(); + EventHandlers.Add(typeof(T), slot); + } + } + + return slot; + } + + private static ISlot GetOrCreateSlot(Type type) + { + ISlot slot; + lock (EventHandlers) + { + if (EventHandlers.TryGetValue(type, out var value)) + { + slot = (ISlot) value; + } + else + { + slot = (ISlot) Activator.CreateInstance(typeof(Slot<>).MakeGenericType(type)); + EventHandlers.Add(type, slot); + } + } + + return slot; + } + + public static void Remove(EventHandler handler) + { + Slot slot; + lock (EventHandlers) + { + if (EventHandlers.TryGetValue(typeof(T), out var value)) + slot = (Slot) value; + else + return; + } + + slot.Rwl.EnterWriteLock(); + slot.Handlers -= handler; + slot.Rwl.ExitWriteLock(); + } + + public static void AddCollection(object obj) + { + ProcessCollection(obj, true); + } + + public static void RemoveCollection(object obj) + { + ProcessCollection(obj, false); + } + + private static void ProcessCollection(object obj, bool add) + { + foreach (var method in obj.GetType().GetMethods()) + if (method.IsDefined(typeof(DeclareBusEventHandlerAttribute), true)) + { + var payload = method.GetParameters(); + if (payload.Length == 2) + { + var payloadType = payload[payload.Length - 1].ParameterType; + var handlerType = typeof(EventHandler<>).MakeGenericType(payloadType); + var del = method.IsStatic + ? Delegate.CreateDelegate(handlerType, method) + : Delegate.CreateDelegate(handlerType, obj, method); + if (add) + GetOrCreateSlot(payloadType).Add(del); + else + GetOrCreateSlot(payloadType).Remove(del); + } + else + { + throw new ArgumentException( + $"Excepting Arguments (System.Object, T) But Got {payload.Length} at Handler {method}" + + ", Stopping. Note that Previously Added Handlers will NOT be Removed"); + } + } + } + + public static void Broadcast(object sender, T payload) + { + Slot slot = null; + lock (EventHandlers) + { + if (EventHandlers.TryGetValue(typeof(T), out var value)) + slot = (Slot) value; + } + + slot?.Invoke(sender, payload); + } + + private interface ISlot + { + void Add(Delegate handler); + void Remove(Delegate handler); + } + + private class Slot : ISlot + { + public readonly ReaderWriterLockSlim Rwl = new ReaderWriterLockSlim(); + + public void Add(Delegate handler) + { + Rwl.EnterWriteLock(); + typeof(Slot).GetEvents()[0].AddMethod.Invoke(this, new object[] {handler}); + Rwl.ExitWriteLock(); + } + + public void Remove(Delegate handler) + { + Rwl.EnterWriteLock(); + typeof(Slot).GetEvents()[0].RemoveMethod.Invoke(this, new object[] {handler}); + Rwl.ExitWriteLock(); + } + + public event EventHandler Handlers; + + public void Invoke(object sender, T payload) + { + Rwl.EnterReadLock(); + Handlers?.Invoke(sender, payload); + Rwl.ExitReadLock(); + } + } + } + + public sealed class DeclareGlobalBusEventHandlerClassAttribute : Attribute {} + + [DeclareAssemblyReflectiveScanner] + public sealed class GlobalBusEventHandlerClassDetector : IAssemblyReflectiveScanner + { + public void ProcessType(Type type) + { + if (type.IsDefined(typeof(DeclareGlobalBusEventHandlerClassAttribute), false)) + EventBus.AddCollection(Activator.CreateInstance(type)); + } + } +} \ No newline at end of file diff --git a/Core/Generic.cs b/Core/Generic.cs index 277dacb..54dfe9a 100644 --- a/Core/Generic.cs +++ b/Core/Generic.cs @@ -1,7 +1,7 @@ // -// Core: Generic.cs +// NEWorld/Core: Generic.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,54 +16,10 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - namespace Core { public static class Generic { - public static dynamic Cast(dynamic a) => (T) a; - public static dynamic Add(dynamic a, dynamic b) => a + b; - public static dynamic Substract(dynamic a, dynamic b) => a - b; - public static dynamic Multiply(dynamic a, dynamic b) => a * b; - public static dynamic Divide(dynamic a, dynamic b) => a / b; - public static dynamic Modulus(dynamic a, dynamic b) => a % b; - public static dynamic AddBy(dynamic a, dynamic b) => a += b; - public static dynamic SubstractBy(dynamic a, dynamic b) => a -= b; - public static dynamic MultiplyBy(ref dynamic a, dynamic b) => a *= b; - public static dynamic DivideBy(dynamic a, dynamic b) => a /= b; - public static dynamic ModulusBy(dynamic a, dynamic b) => a %= b; - public static dynamic Square(dynamic a) => a * a; - public static dynamic Negate(dynamic a) => -a; - public static dynamic Increase(dynamic a) => ++a; - public static dynamic Decrease(dynamic a) => --a; - public static dynamic IncreaseAfter(dynamic a) => a++; - public static dynamic DecreaseAfter(dynamic a) => a--; - - public static bool Less(dynamic a, dynamic b) => a < b; - public static bool LessEqual(dynamic a, dynamic b) => a <= b; - public static bool Larger(dynamic a, dynamic b) => a > b; - public static bool LargerEqual(dynamic a, dynamic b) => a >= b; - public static bool Equal(dynamic a, dynamic b) => a == b; - - public static double Sqrt(dynamic a) => System.Math.Sqrt((double) a); - public static double Sin(dynamic a) => System.Math.Sin((double) a); - public static double Cos(dynamic a) => System.Math.Cos((double) a); - public static double Tan(dynamic a) => System.Math.Tan((double) a); - public static double Abs(dynamic a) => System.Math.Abs(a); - - public static dynamic Min(dynamic a, dynamic b) => Less(a, b) ? a : b; - public static dynamic Max(dynamic a, dynamic b) => Larger(a, b) ? a : b; - - public static void MinEqual(dynamic a, dynamic b) - { - if (Less(b, a)) a = b; - } - - public static void MaxEqual(dynamic a, dynamic b) - { - if (Larger(b, a)) a = b; - } - public static void Swap(ref T a, ref T b) { var t = a; diff --git a/Core/LogPort.cs b/Core/LogPort.cs new file mode 100644 index 0000000..508e928 --- /dev/null +++ b/Core/LogPort.cs @@ -0,0 +1,36 @@ +// +// NEWorld/Core: LogPort.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System; +using Xenko.Core.Diagnostics; + +namespace Core +{ + public static class LogPort + { + public static Logger Logger { private get; set; } + + public static void Debug(string str) + { + if (Logger != null) + Logger.Debug(str); + else + Console.WriteLine(str); + } + } +} \ No newline at end of file diff --git a/Core/Math/Mat4.cs b/Core/Math/Mat4.cs index f7ad55a..9943c66 100644 --- a/Core/Math/Mat4.cs +++ b/Core/Math/Mat4.cs @@ -1,7 +1,7 @@ // -// Core: Mat4.cs +// NEWorld/Core: Mat4.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,257 +16,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System.Collections.Generic; +using Xenko.Core.Mathematics; namespace Core.Math { - public struct Mat4F - { - private const float Pi = 3.1415926535897932f; - - public float[] Data; - - public Mat4F(float x) - { - Data = new[] - { - x, 0.0f, 0.0f, 0.0f, - 0.0f, x, 0.0f, 0.0f, - 0.0f, 0.0f, x, 0.0f, - 0.0f, 0.0f, 0.0f, x - }; - } - - public static Mat4F operator +(Mat4F lhs, Mat4F rhs) - { - var result = lhs; - for (var i = 0; i < 16; ++i) - { - result.Data[i] += rhs.Data[i]; - } - - return result; - } - - public static Mat4F operator *(Mat4F lhs, Mat4F rhs) - { - var res = new Mat4F(0.0f); - res.Data[0] = lhs.Data[0] * rhs.Data[0] + lhs.Data[1] * rhs.Data[4] + lhs.Data[2] * rhs.Data[8] + - lhs.Data[3] * rhs.Data[12]; - res.Data[1] = lhs.Data[0] * rhs.Data[1] + lhs.Data[1] * rhs.Data[5] + lhs.Data[2] * rhs.Data[9] + - lhs.Data[3] * rhs.Data[13]; - res.Data[2] = lhs.Data[0] * rhs.Data[2] + lhs.Data[1] * rhs.Data[6] + lhs.Data[2] * rhs.Data[10] + - lhs.Data[3] * rhs.Data[14]; - res.Data[3] = lhs.Data[0] * rhs.Data[3] + lhs.Data[1] * rhs.Data[7] + lhs.Data[2] * rhs.Data[11] + - lhs.Data[3] * rhs.Data[15]; - res.Data[4] = lhs.Data[4] * rhs.Data[0] + lhs.Data[5] * rhs.Data[4] + lhs.Data[6] * rhs.Data[8] + - lhs.Data[7] * rhs.Data[12]; - res.Data[5] = lhs.Data[4] * rhs.Data[1] + lhs.Data[5] * rhs.Data[5] + lhs.Data[6] * rhs.Data[9] + - lhs.Data[7] * rhs.Data[13]; - res.Data[6] = lhs.Data[4] * rhs.Data[2] + lhs.Data[5] * rhs.Data[6] + lhs.Data[6] * rhs.Data[10] + - lhs.Data[7] * rhs.Data[14]; - res.Data[7] = lhs.Data[4] * rhs.Data[3] + lhs.Data[5] * rhs.Data[7] + lhs.Data[6] * rhs.Data[11] + - lhs.Data[7] * rhs.Data[15]; - res.Data[8] = lhs.Data[8] * rhs.Data[0] + lhs.Data[9] * rhs.Data[4] + lhs.Data[10] * rhs.Data[8] + - lhs.Data[11] * rhs.Data[12]; - res.Data[9] = lhs.Data[8] * rhs.Data[1] + lhs.Data[9] * rhs.Data[5] + lhs.Data[10] * rhs.Data[9] + - lhs.Data[11] * rhs.Data[13]; - res.Data[10] = lhs.Data[8] * rhs.Data[2] + lhs.Data[9] * rhs.Data[6] + lhs.Data[10] * rhs.Data[10] + - lhs.Data[11] * rhs.Data[14]; - res.Data[11] = lhs.Data[8] * rhs.Data[3] + lhs.Data[9] * rhs.Data[7] + lhs.Data[10] * rhs.Data[11] + - lhs.Data[11] * rhs.Data[15]; - res.Data[12] = lhs.Data[12] * rhs.Data[0] + lhs.Data[13] * rhs.Data[4] + lhs.Data[14] * rhs.Data[8] + - lhs.Data[15] * rhs.Data[12]; - res.Data[13] = lhs.Data[12] * rhs.Data[1] + lhs.Data[13] * rhs.Data[5] + lhs.Data[14] * rhs.Data[9] + - lhs.Data[15] * rhs.Data[13]; - res.Data[14] = lhs.Data[12] * rhs.Data[2] + lhs.Data[13] * rhs.Data[6] + lhs.Data[14] * rhs.Data[10] + - lhs.Data[15] * rhs.Data[14]; - res.Data[15] = lhs.Data[12] * rhs.Data[3] + lhs.Data[13] * rhs.Data[7] + lhs.Data[14] * rhs.Data[11] + - lhs.Data[15] * rhs.Data[15]; - return res; - } - - // Swap row r1, row r2 - public void SwapRows(uint r1, uint r2) - { - Generic.Swap(ref Data[r1 * 4 + 0], ref Data[r2 * 4 + 0]); - Generic.Swap(ref Data[r1 * 4 + 1], ref Data[r2 * 4 + 1]); - Generic.Swap(ref Data[r1 * 4 + 2], ref Data[r2 * 4 + 2]); - Generic.Swap(ref Data[r1 * 4 + 3], ref Data[r2 * 4 + 3]); - } - - // Row r *= k - public void MultRow(uint r, float k) - { - Data[r * 4 + 0] *= k; - Data[r * 4 + 1] *= k; - Data[r * 4 + 2] *= k; - Data[r * 4 + 3] *= k; - } - - // Row dst += row src * k - public void MultAndAdd(uint src, uint dst, float k) - { - Data[dst * 4 + 0] += Data[src * 4 + 0] * k; - Data[dst * 4 + 1] += Data[src * 4 + 1] * k; - Data[dst * 4 + 2] += Data[src * 4 + 2] * k; - Data[dst * 4 + 3] += Data[src * 4 + 3] * k; - } - - - // Get transposed matrix - public Mat4F Transposed() - { - return new Mat4F(0.0f) - { - Data = - { - [0] = Data[0], - [1] = Data[4], - [2] = Data[8], - [3] = Data[12], - [4] = Data[1], - [5] = Data[5], - [6] = Data[9], - [7] = Data[13], - [8] = Data[2], - [9] = Data[6], - [10] = Data[10], - [11] = Data[14], - [12] = Data[3], - [13] = Data[7], - [14] = Data[11], - [15] = Data[15] - } - }; - } - - // Inverse matrix - public Mat4F Inverse(float[] data) - { - Data = data; - var res = Identity(); - for (uint i = 0; i < 4; i++) - { - var p = i; - for (var j = i + 1; j < 4; j++) - { - if (System.Math.Abs(Data[j * 4 + i]) > System.Math.Abs(Data[p * 4 + i])) p = j; - } - - res.SwapRows(i, p); - SwapRows(i, p); - res.MultRow(i, 1.0f / Data[i * 4 + i]); - MultRow(i, 1.0f / Data[i * 4 + i]); - for (var j = i + 1; j < 4; j++) - { - res.MultAndAdd(i, j, -Data[j * 4 + i]); - MultAndAdd(i, j, -Data[j * 4 + i]); - } - } - - for (var i = 3; i >= 0; i--) - { - for (uint j = 0; j < i; j++) - { - res.MultAndAdd((uint) i, j, -Data[j * 4 + i]); - MultAndAdd((uint) i, j, -Data[j * 4 + i]); - } - } - - return this; - } - - // Construct a translation matrix - public static Mat4F Translation(Vec3 delta) => new Mat4F(1.0f) - { - Data = - { - [3] = delta.X, - [7] = delta.Y, - [11] = delta.Z - } - }; - - // Construct a identity matrix - public static Mat4F Identity() => new Mat4F(1.0f); - - // Construct a rotation matrix - public static Mat4F Rotation(float degrees, Vec3 vec) - { - vec.Normalize(); - var alpha = degrees * Pi / 180.0f; - var s = (float) System.Math.Sin(alpha); - var c = (float) System.Math.Cos(alpha); - var t = 1.0f - c; - return new Mat4F(0.0f) - { - Data = - { - [0] = t * vec.X * vec.X + c, - [1] = t * vec.X * vec.Y - s * vec.Z, - [2] = t * vec.X * vec.Z + s * vec.Y, - [4] = t * vec.X * vec.Y + s * vec.Z, - [5] = t * vec.Y * vec.Y + c, - [6] = t * vec.Y * vec.Z - s * vec.X, - [8] = t * vec.X * vec.Z - s * vec.Y, - [9] = t * vec.Y * vec.Z + s * vec.X, - [10] = t * vec.Z * vec.Z + c, - [15] = 1.0f - } - }; - } - - // Construct a perspective projection matrix - public static Mat4F Perspective(float fov, float aspect, float zNear, float zFar) - { - var f = 1.0f / System.Math.Tan(fov * Pi / 180.0 / 2.0); - var a = zNear - zFar; - return new Mat4F(0.0f) - { - Data = - { - [0] = (float) (f / aspect), - [5] = (float) f, - [10] = (zFar + zNear) / a, - [11] = 2.0f * zFar * zNear / a, - [14] = -1.0f - } - }; - } - - // Construct an orthogonal projection matrix - public static Mat4F Ortho(float left, float right, float top, float bottom, float zNear, float zFar) - { - var a = right - left; - var b = top - bottom; - var c = zFar - zNear; - return new Mat4F(0.0f) - { - Data = - { - [0] = 2.0f / a, - [3] = -(right + left) / a, - [5] = 2.0f / b, - [7] = -(top + bottom) / b, - [10] = -2.0f / c, - [11] = -(zFar + zNear) / c, - [15] = 1.0f - } - }; - } - - public KeyValuePair, float> Transform(Vec3 vec, float w) - { - var res = new Vec3(Data[0] * vec.X + Data[1] * vec.Y + Data[2] * vec.Z + Data[3] * w, - Data[4] * vec.X + Data[5] * vec.Y + Data[6] * vec.Z + Data[7] * w, - Data[8] * vec.X + Data[9] * vec.Y + Data[10] * vec.Z + Data[11] * w); - var rw = Data[12] * vec.X + Data[13] * vec.Y + Data[14] * vec.Z + Data[15] * w; - return new KeyValuePair, float>(res, rw); - } - } - public struct Mat4D { private const double Pi = 3.1415926535897932f; @@ -287,10 +41,7 @@ public Mat4D(double x) public static Mat4D operator +(Mat4D lhs, Mat4D rhs) { var result = lhs; - for (var i = 0; i < 16; ++i) - { - result.Data[i] += rhs.Data[i]; - } + for (var i = 0; i < 16; ++i) result.Data[i] += rhs.Data[i]; return result; } @@ -333,34 +84,6 @@ public Mat4D(double x) return res; } - // Swap row r1, row r2 - public void SwapRows(uint r1, uint r2) - { - Generic.Swap(ref Data[r1 * 4 + 0], ref Data[r2 * 4 + 0]); - Generic.Swap(ref Data[r1 * 4 + 1], ref Data[r2 * 4 + 1]); - Generic.Swap(ref Data[r1 * 4 + 2], ref Data[r2 * 4 + 2]); - Generic.Swap(ref Data[r1 * 4 + 3], ref Data[r2 * 4 + 3]); - } - - // Row r *= k - public void MultRow(uint r, double k) - { - Data[r * 4 + 0] *= k; - Data[r * 4 + 1] *= k; - Data[r * 4 + 2] *= k; - Data[r * 4 + 3] *= k; - } - - // Row dst += row src * k - public void MultAndAdd(uint src, uint dst, double k) - { - Data[dst * 4 + 0] += Data[src * 4 + 0] * k; - Data[dst * 4 + 1] += Data[src * 4 + 1] * k; - Data[dst * 4 + 2] += Data[src * 4 + 2] * k; - Data[dst * 4 + 3] += Data[src * 4 + 3] * k; - } - - // Get transposed matrix public Mat4D Transposed() { @@ -388,58 +111,28 @@ public Mat4D Transposed() }; } - // Inverse matrix - public Mat4D Inverse(double[] data) + // Construct a translation matrix + public static Mat4D Translation(Double3 delta) { - Data = data; - var res = Identity(); - for (uint i = 0; i < 4; i++) + return new Mat4D(1.0f) { - var p = i; - for (var j = i + 1; j < 4; j++) - { - if (System.Math.Abs(Data[j * 4 + i]) > System.Math.Abs(Data[p * 4 + i])) p = j; - } - - res.SwapRows(i, p); - SwapRows(i, p); - res.MultRow(i, 1.0f / Data[i * 4 + i]); - MultRow(i, 1.0f / Data[i * 4 + i]); - for (var j = i + 1; j < 4; j++) - { - res.MultAndAdd(i, j, -Data[j * 4 + i]); - MultAndAdd(i, j, -Data[j * 4 + i]); - } - } - - for (var i = 3; i >= 0; i--) - { - for (uint j = 0; j < i; j++) + Data = { - res.MultAndAdd((uint) i, j, -Data[j * 4 + i]); - MultAndAdd((uint) i, j, -Data[j * 4 + i]); + [3] = delta.X, + [7] = delta.Y, + [11] = delta.Z } - } - - return this; + }; } - // Construct a translation matrix - public static Mat4D Translation(Vec3 delta) => new Mat4D(1.0f) - { - Data = - { - [3] = delta.X, - [7] = delta.Y, - [11] = delta.Z - } - }; - // Construct a identity matrix - public static Mat4D Identity() => new Mat4D(1.0f); + public static Mat4D Identity() + { + return new Mat4D(1.0f); + } // Construct a rotation matrix - public static Mat4D Rotation(double degrees, Vec3 vec) + public static Mat4D Rotation(double degrees, Double3 vec) { vec.Normalize(); var alpha = degrees * Pi / 180.0f; @@ -464,52 +157,13 @@ public static Mat4D Rotation(double degrees, Vec3 vec) }; } - // Construct a perspective projection matrix - public static Mat4D Perspective(double fov, double aspect, double zNear, double zFar) - { - var f = 1.0f / System.Math.Tan(fov * Pi / 180.0 / 2.0); - var a = zNear - zFar; - return new Mat4D(0.0f) - { - Data = - { - [0] = f / aspect, - [5] = f, - [10] = (zFar + zNear) / a, - [11] = 2.0f * zFar * zNear / a, - [14] = -1.0f - } - }; - } - - // Construct an orthogonal projection matrix - public static Mat4D Ortho(double left, double right, double top, double bottom, double zNear, double zFar) - { - var a = right - left; - var b = top - bottom; - var c = zFar - zNear; - return new Mat4D(0.0f) - { - Data = - { - [0] = 2.0f / a, - [3] = -(right + left) / a, - [5] = 2.0f / b, - [7] = -(top + bottom) / b, - [10] = -2.0f / c, - [11] = -(zFar + zNear) / c, - [15] = 1.0f - } - }; - } - - public KeyValuePair, double> Transform(Vec3 vec, double w) + public KeyValuePair Transform(Double3 vec, double w) { - var res = new Vec3(Data[0] * vec.X + Data[1] * vec.Y + Data[2] * vec.Z + Data[3] * w, + var res = new Double3(Data[0] * vec.X + Data[1] * vec.Y + Data[2] * vec.Z + Data[3] * w, Data[4] * vec.X + Data[5] * vec.Y + Data[6] * vec.Z + Data[7] * w, Data[8] * vec.X + Data[9] * vec.Y + Data[10] * vec.Z + Data[11] * w); var rw = Data[12] * vec.X + Data[13] * vec.Y + Data[14] * vec.Z + Data[15] * w; - return new KeyValuePair, double>(res, rw); + return new KeyValuePair(res, rw); } } } \ No newline at end of file diff --git a/Core/Math/Vector.cs b/Core/Math/Vector.cs deleted file mode 100644 index 7f7b417..0000000 --- a/Core/Math/Vector.cs +++ /dev/null @@ -1,137 +0,0 @@ -// -// Core: Vector.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using System.Collections.Generic; - -namespace Core.Math -{ - using static Generic; - - public struct Vec2 : IEquatable> - { - public Vec2(T x, T y) - { - X = x; - Y = y; - } - - public T X, Y; - - public double LengthSqr() => Square(X) + Square(Y); - - public double Length() => System.Math.Sqrt(LengthSqr()); - - public void Normalize() - { - object length = Cast(Length()); - X = Divide(X, length); - Y = Divide(Y, length); - } - - public static Vec2 operator +(Vec2 lhs, Vec2 rhs) => - new Vec2(Add(lhs.X, rhs.X), Add(lhs.Y, rhs.Y)); - - public static Vec2 operator -(Vec2 lhs, Vec2 rhs) => - new Vec2(Substract(lhs.X, rhs.X), Substract(lhs.Y, rhs.Y)); - - public bool Equals(Vec2 other) => - EqualityComparer.Default.Equals(X, other.X) && EqualityComparer.Default.Equals(Y, other.Y); - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - return obj is Vec2 && Equals((Vec2) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (EqualityComparer.Default.GetHashCode(X) * 397) ^ EqualityComparer.Default.GetHashCode(Y); - } - } - } - - public struct Vec3 : IEquatable> - { - public Vec3(T x, T y, T z) - { - X = x; - Y = y; - Z = z; - } - - public T X; - public T Y; - public T Z; - - public T LengthSqr() => Square(X) + Square(Y) + Square(Z); - - public double Length() => Sqrt(LengthSqr()); - - public void Normalize() - { - object length = Cast(Length()); - X = Divide(X, length); - Y = Divide(Y, length); - Z = Divide(Z, length); - } - - - public static Vec3 operator +(Vec3 lhs, Vec3 rhs) => - new Vec3(Add(lhs.X, rhs.X), Add(lhs.Y, rhs.Y), Add(lhs.Z, rhs.Z)); - - public static Vec3 operator -(Vec3 lhs, Vec3 rhs) => - new Vec3(Substract(lhs.X, rhs.X), Substract(lhs.Y, rhs.Y), Substract(lhs.Z, rhs.Z)); - - public static Vec3 operator -(Vec3 lhs) => - new Vec3(Negate(lhs.X), Negate(lhs.Y), Negate(lhs.Z)); - - public static Vec3 operator *(Vec3 lhs, T rhs) => - new Vec3(Multiply(lhs.X, rhs), Multiply(lhs.Y, rhs), Multiply(lhs.Z, rhs)); - - public static Vec3 operator /(Vec3 lhs, T rhs) => - new Vec3(Divide(lhs.X, rhs), Divide(lhs.Y, rhs), Divide(lhs.Z, rhs)); - - public T ChebyshevDistance(Vec3 rhs) => - (T) Max(Max(Abs(Substract(X, rhs.X)), Abs(Substract(Y, rhs.Y))), Abs(Substract(Z, rhs.Z))); - - public bool Equals(Vec3 other) => - EqualityComparer.Default.Equals(X, other.X) && EqualityComparer.Default.Equals(Y, other.Y) && - EqualityComparer.Default.Equals(Z, other.Z); - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - return obj is Vec3 vec3 && Equals(vec3); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = EqualityComparer.Default.GetHashCode(X); - hashCode = (hashCode * 397) ^ EqualityComparer.Default.GetHashCode(Y); - hashCode = (hashCode * 397) ^ EqualityComparer.Default.GetHashCode(Z); - return hashCode; - } - } - } -} \ No newline at end of file diff --git a/Core/Module.cs b/Core/Module.cs new file mode 100644 index 0000000..5620d60 --- /dev/null +++ b/Core/Module.cs @@ -0,0 +1,85 @@ +// +// NEWorld/Core: Module.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// + +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Core +{ + public interface IModule + { + void CoInitialize(); + void CoFinalize(); + void OnMemoryWarning(); + } + + public sealed class DeclareModuleAttribute : Attribute + { + } + + [DeclareAssemblyReflectiveScanner] + [DeclareGlobalBusEventHandlerClass] + public sealed class Modules : IAssemblyReflectiveScanner + { + private static readonly Dictionary Loaded = new Dictionary(); + + private static string _basePath = AppContext.BaseDirectory; + + public static void SetBasePath(string path) + { + _basePath = path; + } + + public static void Load(string moduleFile) + { + Assembly.Load(moduleFile); + } + + [DeclareBusEventHandler] + public static void UnloadAll(object sender, ApplicationControl.Shutdown type) + { + lock (Loaded) + { + foreach (var module in Loaded) + module.Value.CoFinalize(); + Loaded.Clear(); + } + } + + public void ProcessType(Type type) + { + if (type.IsDefined(typeof(DeclareModuleAttribute), false) && typeof(IModule).IsAssignableFrom(type)) + try + { + var module = (IModule) Activator.CreateInstance(type); + module.CoInitialize(); + lock (Loaded) + { + Loaded.Add(type.FullName ?? "", module); + } + LogPort.Debug($"Loaded Module : {type}"); + } + catch (Exception e) + { + LogPort.Debug($"Module {type} Load Failure : {e}"); + } + } + } +} \ No newline at end of file diff --git a/Core/Module/Module.cs b/Core/Module/Module.cs deleted file mode 100644 index 3651516..0000000 --- a/Core/Module/Module.cs +++ /dev/null @@ -1,87 +0,0 @@ -// -// Core: Module.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Reflection; -using Core.Utilities; - -namespace Core.Module -{ - public interface IModule - { - void CoInitialize(); - void CoFinalize(); - void OnMemoryWarning(); - } - - public class DeclareModuleAttribute : Attribute - { - } - - public class Modules - { - private Modules() - { - _modules = new Dictionary>(); - } - - public void SetBasePath(string path) => _basePath = path; - - public void Load(string moduleFile) - { - var assembly = Assembly.Load(moduleFile); - var possibleTypes = assembly.GetExportedTypes(); - foreach (var type in possibleTypes) - { - if (type.IsDefined(typeof(DeclareModuleAttribute), false) && typeof(IModule).IsAssignableFrom(type)) - { - try - { - var module = (IModule) Activator.CreateInstance(type); - module.CoInitialize(); - _modules.Add(type.FullName ?? "", new KeyValuePair(module, assembly)); - Console.WriteLine($"Loaded Module : {type}"); - } - catch (Exception e) - { - Console.WriteLine($"Module {type} Load Failure : {e}"); - } - } - } - - Services.ScanAssembly(assembly); - } - - public IModule this[string name] => _modules[name].Key; - - public void UnloadAll() - { - foreach (var module in _modules) - module.Value.Key.CoFinalize(); - _modules.Clear(); - } - - public static Modules Instance => Singleton.Instance; - - private string _basePath = Path.Modules(); - - private readonly Dictionary> _modules; - } -} \ No newline at end of file diff --git a/Core/Network/Client.cs b/Core/Network/Client.cs index 99d6e97..193fd65 100644 --- a/Core/Network/Client.cs +++ b/Core/Network/Client.cs @@ -1,7 +1,7 @@ // -// Core: Client.cs +// NEWorld/Core: Client.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,40 +16,77 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - +using System; using System.Collections.Generic; using System.Net.Sockets; -using Core.Utilities; +using System.Threading.Tasks; namespace Core.Network { - public class Client : TcpClient + public sealed class Client : IDisposable { - public Client(string address, int port) : base(address, port) + private readonly ConnectionHost.Connection connection; + private readonly List protocols; + + public Client(string address, int port) { - RegisterProtocol(Singleton.Instance); - RegisterProtocol(new ProtocolFetchProtocol.Client()); - _connHost.AddConnection(this); + var client = new TcpClient(address, port); + protocols = new List(); + RegisterProtocol(new Reply()); + RegisterProtocol(new Handshake.Client()); + connection = ConnectionHost.Add(client, protocols); } - public void RegisterProtocol(Protocol newProtocol) => _connHost.RegisterProtocol(newProtocol); + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } - public void NegotiateProtocols() + ~Client() + { + ReleaseUnmanagedResources(); + } + + public void RegisterProtocol(Protocol newProtocol) + { + protocols.Add(newProtocol); + } + + public async Task HandShake() { var skvm = new Dictionary(); - foreach (var protocol in _connHost.Protocols) + foreach (var protocol in protocols) skvm.Add(protocol.Name(), protocol); - foreach (var entry in ProtocolFetchProtocol.Client.Get(GetConnection())) + var reply = await Handshake.Get(GetConnection().Session); + foreach (var entry in reply) skvm[entry.Key].Id = entry.Value; - _connHost.Protocols.Sort(ProtocolSorter); + protocols.Sort(ProtocolSorter); } - public ConnectionHost.Connection GetConnection() => _connHost.GetConnection(0); + public Session.Send CreateMessage(uint protocol) + { + return GetConnection().Session.CreateMessage(protocol); + } - public new void Close() => _connHost.CloseAll(); + public void Close() + { + connection.Close(); + } + + public ConnectionHost.Connection GetConnection() + { + return connection; + } - private static int ProtocolSorter(Protocol x, Protocol y) => Comparer.Default.Compare(x.Id, y.Id); + private static int ProtocolSorter(Protocol x, Protocol y) + { + return Comparer.Default.Compare(x.Id, y.Id); + } - private readonly ConnectionHost _connHost = new ConnectionHost(); + private void ReleaseUnmanagedResources() + { + Close(); + } } } \ No newline at end of file diff --git a/Core/Network/ConnectionHost.cs b/Core/Network/ConnectionHost.cs index af63e76..d761938 100644 --- a/Core/Network/ConnectionHost.cs +++ b/Core/Network/ConnectionHost.cs @@ -1,7 +1,7 @@ // -// Core: ConnectionHost.cs +// NEWorld/Core: ConnectionHost.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,147 +16,381 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System; using System.Collections.Generic; -using System.Linq; +using System.Diagnostics; +using System.IO; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; namespace Core.Network { - public class ConnectionHost + public sealed class Session : IDisposable { - public class Connection + private readonly TcpClient conn; + private readonly NetworkStream ios; + private readonly MemoryStream writeBuffer = new MemoryStream(new byte[8192], 0, 8192, true, true); + private readonly ReaderWriterLockSlim writeLock = new ReaderWriterLockSlim(); + private MemoryStream buffer; + private byte[] storage = new byte[8192]; + + internal Session(TcpClient io) + { + conn = io; + ios = io.GetStream(); + buffer = new MemoryStream(storage, 0, storage.Length, false, true); + } + + public bool Live => conn.Connected; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~Session() + { + Dispose(false); + } + + internal Receive WaitMessage() + { + return new Receive(this); + } + + public Send CreateMessage(uint protocol) + { + return new Send(this, protocol); + } + + private void ReleaseUnmanagedResources() + { + ios.Close(); + } + + private void Dispose(bool disposing) { - public Connection(ulong cid, TcpClient client, ConnectionHost server) + ReleaseUnmanagedResources(); + if (disposing) { - _cid = cid; - _client = client; - _server = server; - Stream = client.GetStream(); - _finalize = Start(); + conn?.Dispose(); + ios?.Dispose(); + writeBuffer?.Dispose(); + buffer?.Dispose(); } + } - public bool Valid { get; private set; } + public sealed class Receive : IDisposable + { + private Stream ios; - public void Close() + internal Receive(Session s) { - CloseDown(); - _finalize.Wait(); + Session = s; + ios = Session.ios; + Raw = Session.storage; } - private async Task Start() + public byte[] Raw { get; private set; } + + public Session Session { get; } + + public void Dispose() { - Valid = true; - var headerCache = new byte[8]; // ["NWRC"] + Int32BE(Protocol Id) - while (Valid) + } + + internal async Task LoadExpected(int length) + { + if (length == 0) return; + + if (length > Raw.Length) { - try - { - var bytesRead = await Stream.ReadAsync(headerCache, 0, 8); - if (VerifyPackageValidity(headerCache, bytesRead)) - try - { - _server.Protocols[GetProtocolId(headerCache)].HandleRequest(Stream); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - } - else - throw new Exception("Bad Package Recieved"); - } - catch (Exception e) - { - if (_client.Connected == false) - break; - Console.WriteLine($"Encountering Exception {e}"); - throw; - } + Session.storage = Raw = + new byte[1 << (int) System.Math.Ceiling(System.Math.Log(length) / System.Math.Log(2))]; + Session.buffer = new MemoryStream(Raw, 0, Raw.Length, false, true); + } + else + { + Session.buffer.Seek(0, SeekOrigin.Begin); } - CloseDown(); + await ReadAsync(Raw, 0, length); + ios = Session.buffer; } - private void CloseDown() + internal async Task Wait() { - if (!Valid) return; - Valid = false; - Stream.Close(); // Cancellation Token Doesn't Work. Hard Close is adopted. - _client.Close(); - Interlocked.Increment(ref _server._invalidConnections); + await ReadAsync(Raw, 0, 8); + if (!VerifyPackageValidity(Raw)) + throw new Exception("Bad Package Received"); + return GetProtocolId(Raw); + } + + private static int GetProtocolId(byte[] head) + { + return (head[4] << 24) | (head[5] << 16) | (head[6] << 8) | head[7]; + } + + private static bool VerifyPackageValidity(byte[] head) + { + return head[0] == 'N' && head[1] == 'W' && head[2] == 'R' && head[3] == 'C'; + } + + public byte ReadU8() + { + var ret = ios.ReadByte(); + if (ret >= 0) + return (byte) ret; + throw new EndOfStreamException(); + } + + public char ReadChar() + { + return (char) ReadU16(); + } + + public ushort ReadU16() + { + return (ushort) ((ReadU8() << 8) | ReadU8()); } - public NetworkStream Stream { get; } + public uint ReadU32() + { + return (uint) ((ReadU16() << 16) | ReadU16()); + } - private static int GetProtocolId(byte[] head) => head[4] << 24 | head[5] << 16 | head[6] << 8 | head[7]; + public ulong ReadU64() + { + return (ReadU32() << 32) | ReadU32(); + } - private static bool VerifyPackageValidity(byte[] head, int read) => - head[0] == 'N' && head[1] == 'W' && head[2] == 'R' && head[3] == 'C' && read == 8; + public void Read(byte[] buffer, int begin, int end) + { + while (begin != end) + { + var read = ios.Read(buffer, begin, end - begin); + if (read > 0) + begin += read; + else + throw new EndOfStreamException(); + } + } - private ulong _cid; - private readonly TcpClient _client; - private readonly ConnectionHost _server; - private readonly Task _finalize; + public async Task ReadAsync(byte[] buffer, int begin, int end) + { + while (begin != end) begin += await ios.ReadAsync(buffer, begin, end - begin); + } } - public ConnectionHost() + public sealed class Send : IDisposable { - _clients = new Dictionary(); - Protocols = new List(); - } + private readonly MemoryStream buffer; + private readonly NetworkStream ios; - public object Lock => _protocolLock; + internal Send(Session session, uint protocol) + { + Session = session; + ios = Session.ios; + buffer = Session.writeBuffer; + session.writeLock.EnterWriteLock(); + Write((byte) 'N'); + Write((byte) 'W'); + Write((byte) 'R'); + Write((byte) 'C'); + Write(protocol); + } - private const double UtilizationThreshold = 0.75; + public Session Session { get; } - public void RegisterProtocol(Protocol newProtocol) - { - lock (_protocolLock) + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + + public void Write(byte val) + { + buffer.WriteByte(val); + } + + public void Write(char val) + { + Write((byte) (val >> 8)); + Write((byte) (val & 0xFF)); + } + + public void Write(ushort val) { - Protocols.Add(newProtocol); + Write((byte) (val >> 8)); + Write((byte) (val & 0xFF)); + } + + public void Write(uint val) + { + Write((ushort) (val >> 16)); + Write((ushort) (val & 0xFFFF)); + } + + public void Write(ulong val) + { + Write((uint) (val >> 32)); + Write((uint) (val & 0xFFFFFFFF)); + } + + public void Write(byte[] input, int begin, int end) + { + FlushBuffer(); + ios.Write(input, begin, end - begin); + } + + public async Task ReadAsync(byte[] input, int begin, int end) + { + FlushBuffer(); + await ios.WriteAsync(input, begin, end - begin); + } + + private void FlushBuffer() + { + if (buffer.Length > 0) + { + ios.Write(buffer.GetBuffer(), 0, (int) buffer.Seek(0, SeekOrigin.Current)); + buffer.Seek(0, SeekOrigin.Begin); + } } + + public void Write(ArraySegment bytes) + { + FlushBuffer(); + Debug.Assert(bytes.Array != null, "bytes.Array != null"); + ios.Write(bytes.Array, bytes.Offset, bytes.Count); + } + + private void ReleaseUnmanagedResources() + { + FlushBuffer(); + Session.writeLock.ExitWriteLock(); + } + + ~Send() + { + ReleaseUnmanagedResources(); + } + } + } + + [DeclareService("Core.Network.ConnectionHost")] + public sealed class ConnectionHost : IDisposable + { + private const double UtilizationThreshold = 0.25; + private static int _connectionCounter; + private static List _connections; + + static ConnectionHost() + { + _connections = new List(); } - public void SweepInvalidConnectionsIfNecessary() + public void Dispose() { - var utilization = 1.0 - (double) _invalidConnections / _clients.Count; + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + ~ConnectionHost() + { + ReleaseUnmanagedResources(); + } + + private static void SweepInvalidConnectionsIfNecessary() + { + var utilization = (double) _connectionCounter / _connections.Count; if (utilization < UtilizationThreshold) SweepInvalidConnections(); } - public Connection GetConnection(ulong id) => _clients[id]; - - private void SweepInvalidConnections() + private static void SweepInvalidConnections() { - foreach (var hd in _clients.ToList()) - if (!hd.Value.Valid) - { - _clients.Remove(hd.Key); - Interlocked.Decrement(ref _invalidConnections); - } + var swap = new List(); + foreach (var hd in _connections) + if (hd.Valid) + swap.Add(hd); + _connections = swap; } - public void AddConnection(TcpClient conn) + public static Connection Add(TcpClient conn, List protocols) { - _clients.Add(_sessionIdTop, new Connection(_sessionIdTop, conn, this)); - ++_sessionIdTop; + var connect = new Connection(conn, protocols); + _connections.Add(connect); + return connect; } - private ulong _sessionIdTop; - private int _invalidConnections; - public readonly List Protocols; - private readonly Dictionary _clients; - private readonly object _protocolLock = new object(); + public static int CountConnections() + { + return _connectionCounter; + } - public void CloseAll() + private void ReleaseUnmanagedResources() { - foreach (var hd in _clients) - hd.Value.Close(); + foreach (var hd in _connections) + hd.Close(); } - public int CountConnections() => _clients.Count - _invalidConnections; + public sealed class Connection + { + private readonly Task finalize; + private readonly List protocols; + internal readonly Session Session; + + public Connection(TcpClient client, List protocols) + { + this.protocols = protocols; + Session = new Session(client); + finalize = Start(); + } + + public bool Valid { get; private set; } + + public void Close() + { + CloseDown(); + finalize.Wait(); + } + + private async Task Start() + { + Valid = true; + Interlocked.Increment(ref _connectionCounter); + while (Valid && Session.Live) + try + { + var message = Session.WaitMessage(); + await ProcessRequest(await message.Wait(), message); + } + catch (Exception e) + { + if (Session.Live) LogPort.Debug($"Encountering Exception {e}"); + } + + CloseDown(); + } + + private async Task ProcessRequest(int protocol, Session.Receive message) + { + var handle = protocols[protocol]; + await message.LoadExpected(handle.Expecting); + handle.HandleRequest(message); + } + + private void CloseDown() + { + if (!Valid) return; + Valid = false; + Interlocked.Decrement(ref _connectionCounter); + SweepInvalidConnectionsIfNecessary(); + } + } } } \ No newline at end of file diff --git a/Core/Network/Protocol.cs b/Core/Network/Protocol.cs index c8eeee2..ebd9491 100644 --- a/Core/Network/Protocol.cs +++ b/Core/Network/Protocol.cs @@ -1,7 +1,7 @@ // -// Core: Protocol.cs +// NEWorld/Core: Protocol.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,113 +16,30 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - -using System; -using System.Net.Sockets; -using System.Threading; - namespace Core.Network { public abstract class Protocol { - public int Id { get; set; } - - public abstract string Name(); - - public abstract void HandleRequest(NetworkStream nstream); - - protected static byte[] Request(int protocol) => new[] - { - (byte) 'N', (byte) 'W', (byte) 'R', (byte) 'C', - (byte) (protocol >> 24), - (byte) (protocol >> 16 & 0xFF), - (byte) (protocol >> 18 & 0xFF), - (byte) (protocol & 0xFF) - }; + public uint Id { get; set; } - protected static byte[] Request(int protocol, ArraySegment message) => Concat(message, new[] - { - (byte) 'N', (byte) 'W', (byte) 'R', (byte) 'C', - (byte) (protocol >> 24), - (byte) (protocol >> 16 & 0xFF), - (byte) (protocol >> 18 & 0xFF), - (byte) (protocol & 0xFF) - }); - - protected static byte[] Reply(int requestSession, ArraySegment message) => Concat(message, new[] - { - (byte) 'N', (byte) 'W', (byte) 'R', (byte) 'C', - (byte) 0, (byte) 0, (byte) 0, (byte) 0, - (byte) (requestSession >> 24), - (byte) (requestSession >> 16 & 0xFF), - (byte) (requestSession >> 18 & 0xFF), - (byte) (requestSession & 0xFF), - (byte) (message.Count >> 24), - (byte) (message.Count >> 16 & 0xFF), - (byte) (message.Count >> 18 & 0xFF), - (byte) (message.Count & 0xFF) - }); - - protected static void Send(NetworkStream stream, byte[] data) => stream.Write(data, 0, data.Length); + public int Expecting { get; protected set; } - private static byte[] Concat(ArraySegment message, byte[] head) - { - var final = new byte[head.Length + message.Count]; - head.CopyTo(final, 0); - Array.Copy(message.Array ?? throw new InvalidOperationException(), message.Offset, final, head.Length, - message.Count); - return final; - } - } - - public abstract class StandardProtocol : Protocol - { - protected abstract void HandleRequestData(byte[] data, NetworkStream stream); - - protected abstract byte[] PullRequestData(NetworkStream nstream); + public abstract string Name(); - public sealed override void HandleRequest(NetworkStream nstream) => - HandleRequestData(PullRequestData(nstream), nstream); + public abstract void HandleRequest(Session.Receive request); } - public abstract class FixedLengthProtocol : StandardProtocol + public abstract class FixedLengthProtocol : Protocol { - private class LohOptimizedAlloc + protected FixedLengthProtocol(int length) { - private const int LohThreshold = 80000; - - public LohOptimizedAlloc(int size) - { - Size = size; - if (size > LohThreshold) - _cache = new ThreadLocal(); - } - - public byte[] Get() => _cache == null ? new byte[Size] : _cache.Value ?? (_cache.Value = new byte[Size]); - - public readonly int Size; - - private readonly ThreadLocal _cache; - } - - protected FixedLengthProtocol(int length) => _alloc = new LohOptimizedAlloc(length); - - protected override byte[] PullRequestData(NetworkStream nstream) - { - // TODO : Provide Read Closure To All NetworkStream Readers - var ret = _alloc.Get(); - var read = 0; - while (read < _alloc.Size) - read += nstream.Read(ret, read, _alloc.Size - read); - return ret; + Expecting = length; } - - private readonly LohOptimizedAlloc _alloc; } public abstract class StubProtocol : Protocol { - public override void HandleRequest(NetworkStream nstream) + public override void HandleRequest(Session.Receive request) { } } diff --git a/Core/Network/Protocols.cs b/Core/Network/Protocols.cs index 1da960c..d2b4855 100644 --- a/Core/Network/Protocols.cs +++ b/Core/Network/Protocols.cs @@ -1,7 +1,7 @@ // -// Core: Protocols.cs +// NEWorld/Core: Protocols.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,104 +16,121 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - +using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -using Core.Utilities; -using MsgPack.Serialization; +using MessagePack; namespace Core.Network { - public static class ProtocolFetchProtocol + public static class Handshake { + internal static async Task[]> Get(Session conn) + { + var session = Reply.AllocSession(); + using (var message = conn.CreateMessage(1)) + { + message.Write(session.Key); + } + + var result = await session.Value; + return MessagePackSerializer.Deserialize[]>(result); + } + public class Server : FixedLengthProtocol { - public Server(List protocols) : base(Size) => _protocols = protocols; + private readonly List protocols; - protected override void HandleRequestData(byte[] data, NetworkStream stream) + public Server(List protocols) : base(4) { - var request = SerialSend.UnpackSingleObject(data); - var current = 0; - var reply = new KeyValuePair[_protocols.Count]; - foreach (var prot in _protocols) - reply[current++] = new KeyValuePair(prot.Name(), prot.Id); - Send(stream, Reply(request, SerialReply.PackSingleObjectAsBytes(reply))); + this.protocols = protocols; } - public override string Name() => "FetchProtocols"; + public override void HandleRequest(Session.Receive request) + { + var session = request.ReadU32(); + var current = 0; + var reply = new KeyValuePair[protocols.Count]; + foreach (var protocol in protocols) + reply[current++] = new KeyValuePair(protocol.Name(), protocol.Id); + Reply.Send(request.Session, session, MessagePackSerializer.SerializeUnsafe(reply)); + } - private readonly List _protocols; + public override string Name() + { + return "FetchProtocols"; + } } public class Client : StubProtocol { - public override string Name() => "FetchProtocols"; - - public static KeyValuePair[] Get(ConnectionHost.Connection conn) + public override string Name() { - var session = ProtocolReply.AllocSession(); - Send(conn.Stream, Request(1, SerialSend.PackSingleObjectAsBytes(session.Key))); - return SerialReply.UnpackSingleObject(session.Value.Result); + return "FetchProtocols"; } } - - private static readonly MessagePackSerializer SerialSend = MessagePackSerializer.Get(); - - private static readonly MessagePackSerializer[]> SerialReply = - MessagePackSerializer.Get[]>(); - - private static readonly int Size = SerialSend.PackSingleObject(0).Length; } - public sealed class ProtocolReply : Protocol + public sealed class Reply : Protocol { - private ProtocolReply() + private static int _idTop; + private static readonly ConcurrentQueue SessionIds = new ConcurrentQueue(); + + private static readonly ConcurrentDictionary> Sessions = + new ConcurrentDictionary>(); + + public override string Name() { + return "Reply"; } - public override string Name() => "Reply"; - - public override void HandleRequest(NetworkStream nstream) + public override void HandleRequest(Session.Receive request) { - var extraHead = new byte[8]; - nstream.Read(extraHead, 0, extraHead.Length); - var dataSegment = new byte[GetSessionLength(extraHead)]; - nstream.Read(dataSegment, 0, dataSegment.Length); - SessionDispatch(GetSessionId(extraHead), dataSegment); + var session = request.ReadU32(); + var length = request.ReadU32(); + var dataSegment = new byte[length]; + request.Read(dataSegment, 0, dataSegment.Length); + SessionDispatch(session, dataSegment); } - public static KeyValuePair> AllocSession() => - Singleton.Instance.AllocSessionInternal(); + public static void Send(Session dialog, uint session, ArraySegment payload) + { + using (var message = dialog.CreateMessage(0)) + { + message.Write(session); + message.Write((uint) payload.Count); + message.Write(payload); + } + } - private KeyValuePair> AllocSessionInternal() + public static KeyValuePair> AllocSession() { - if (!_sessionIds.TryDequeue(out var newId)) - newId = Interlocked.Increment(ref _idTop) - 1; + if (!SessionIds.TryDequeue(out var newId)) + newId = (uint) (Interlocked.Increment(ref _idTop) - 1); var completionSource = new TaskCompletionSource(); - while (!_sessions.TryAdd(newId, completionSource)) ; - return new KeyValuePair>(newId, completionSource.Task); + while (!Sessions.TryAdd(newId, completionSource)) ; + return new KeyValuePair>(newId, completionSource.Task); } - private void SessionDispatch(int sessionId, byte[] dataSegment) + private static void SessionDispatch(uint sessionId, byte[] dataSegment) { TaskCompletionSource completion; - while (!_sessions.TryRemove(sessionId, out completion)) ; + while (!Sessions.TryRemove(sessionId, out completion)) ; completion.SetResult(dataSegment); - _sessionIds.Enqueue(sessionId); + SessionIds.Enqueue(sessionId); } - private int _idTop; - private readonly ConcurrentQueue _sessionIds = new ConcurrentQueue(); - - private readonly ConcurrentDictionary> _sessions = - new ConcurrentDictionary>(); - - private static int GetSessionId(byte[] head) => head[0] << 24 | head[1] << 16 | head[2] << 8 | head[3]; + private static int GetSessionId(byte[] head) + { + return (head[0] << 24) | (head[1] << 16) | (head[2] << 8) | head[3]; + } - private static int GetSessionLength(byte[] head) => head[4] << 24 | head[5] << 16 | head[6] << 8 | head[7]; + private static int GetSessionLength(byte[] head) + { + return (head[4] << 24) | (head[5] << 16) | (head[6] << 8) | head[7]; + } } } \ No newline at end of file diff --git a/Core/Network/Server.cs b/Core/Network/Server.cs index daa4ef6..51354d4 100644 --- a/Core/Network/Server.cs +++ b/Core/Network/Server.cs @@ -1,7 +1,7 @@ // -// Core: Server.cs +// NEWorld/Core: Server.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,30 +16,22 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - +using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; -using Core.Utilities; namespace Core.Network { public class Server : TcpListener { - public Server(int port) : base(IPAddress.Any, port) - { - RegisterProtocol(Singleton.Instance); - RegisterProtocol(new ProtocolFetchProtocol.Server(_connHost.Protocols)); - } + private readonly List protocols; - public void Run() + public Server(int port) : base(IPAddress.Any, port) { - lock (_connHost.Lock) - { - Boot(); - ListenConnections().Wait(); - ShutDown(); - } + protocols = new List(); + RegisterProtocol(new Reply()); + RegisterProtocol(new Handshake.Server(protocols)); } public async Task RunAsync() @@ -49,48 +41,45 @@ public async Task RunAsync() ShutDown(); } - public void RegisterProtocol(Protocol newProtocol) => _connHost.RegisterProtocol(newProtocol); - - public void StopServer() => _exit = true; + public void RegisterProtocol(Protocol newProtocol) + { + protocols.Add(newProtocol); + } - public int CountConnections() => _connHost.CountConnections(); + public int CountConnections() + { + return ConnectionHost.CountConnections(); + } private void Boot() { - _exit = false; AssignProtocolIdentifiers(); Start(); } - private void ShutDown() => Stop(); + public void ShutDown() + { + Stop(); + } private async Task ListenConnections() { - while (!_exit) - { + while (Active) try { - _connHost.AddConnection(await AcceptTcpClientAsync()); + ConnectionHost.Add(await AcceptTcpClientAsync(), protocols); } catch { // ignored } - - _connHost.SweepInvalidConnectionsIfNecessary(); - } - - _connHost.CloseAll(); } private void AssignProtocolIdentifiers() { - var current = 0; - foreach (var protocol in _connHost.Protocols) + var current = 0u; + foreach (var protocol in protocols) protocol.Id = current++; } - - private bool _exit; - private readonly ConnectionHost _connHost = new ConnectionHost(); } } \ No newline at end of file diff --git a/Core/Properties/AssemblyInfo.cs b/Core/Properties/AssemblyInfo.cs deleted file mode 100644 index c0fc6f9..0000000 --- a/Core/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Core: AssemblyInfo.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Core")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Core")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("ECB0E309-625F-4A24-926D-D1D23C1B7693")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Core/Rect.cs b/Core/Rect.cs deleted file mode 100644 index 0163e24..0000000 --- a/Core/Rect.cs +++ /dev/null @@ -1,78 +0,0 @@ -// -// Core: Rect.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using Core.Math; - -namespace Core -{ - using static Generic; - - public struct Rect - { - public Vec2 Min, Max; - - public Rect(Vec2 min, Vec2 max) - { - Min = min; - Max = max; - } - - public Rect(T minX, T minY, T maxX, T maxY) - { - Min = new Vec2(minX, minY); - Max = new Vec2(maxX, maxY); - } - - public Vec2 Delta => Max - Min; - - public Rect(Vec2 start, params Vec2[] args) - { - object minX = start.X, minY = start.Y; - object maxX = start.X, maxY = start.Y; - foreach (var point in args) - { - object boxX = point.X, boxY = point.Y; - MinEqual(minX, boxX); - MinEqual(minY, boxY); - MaxEqual(maxX, boxX); - MaxEqual(maxY, boxY); - } - - Min = new Vec2((T) minX, (T) minY); - Max = new Vec2((T) maxX, (T) maxY); - } - - public Rect(params T[] args) - { - object minX = args[0], minY = args[1]; - object maxX = args[0], maxY = args[1]; - for (var i = 2; i < args.Length; ++i) - { - object boxX = args[i++], boxY = args[i]; - MinEqual(minX, boxX); - MinEqual(minY, boxY); - MaxEqual(maxX, boxX); - MaxEqual(maxY, boxY); - } - - Min = new Vec2((T) minX, (T) minY); - Max = new Vec2((T) maxX, (T) maxY); - } - } -} \ No newline at end of file diff --git a/Core/Services.cs b/Core/Services.cs index d2098af..5706df3 100644 --- a/Core/Services.cs +++ b/Core/Services.cs @@ -1,7 +1,7 @@ // -// Core: Services.cs +// NEWorld/Core: Services.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -25,16 +25,22 @@ namespace Core { public sealed class DeclareServiceAttribute : Attribute { - public DeclareServiceAttribute(string name) => Name = name; - public readonly string Name; + + public DeclareServiceAttribute(string name) + { + Name = name; + } } public sealed class ServiceDependencyAttribute : Attribute { - public ServiceDependencyAttribute(params string[] dependencies) => Dependencies = dependencies; - public readonly string[] Dependencies; + + public ServiceDependencyAttribute(params string[] dependencies) + { + Dependencies = dependencies; + } } [Serializable] @@ -45,23 +51,20 @@ public ServiceManagerException(string message, Exception innerException) : base( } } - public static class Services + [DeclareAssemblyReflectiveScanner] + public sealed class Services : IAssemblyReflectiveScanner { - private class DisposeList - { - ~DisposeList() - { - foreach (var disposable in List) - disposable.Dispose(); - } + private static readonly DisposeList Dispose = new DisposeList(); + private static readonly Dictionary Ready = new Dictionary(); + private static readonly Dictionary Providers = new Dictionary(); + private static readonly Dictionary Dependencies = new Dictionary(); - public readonly List List = new List(); + public void ProcessType(Type type) + { + if (type.IsDefined(typeof(DeclareServiceAttribute), false)) + Inject(type); } - static Services() => ScanAssembly(Assembly.GetCallingAssembly()); - - public static void Inject() where TP : IDisposable => Inject(typeof(TP)); - public static TI Get(string name) { try @@ -87,20 +90,11 @@ public static bool TryGet(string name, out TI ins) } catch (ServiceManagerException) { - ins = default; + ins = default(TI); return false; } } - public static void ScanAssembly(Assembly assembly) - { - foreach (var type in assembly.GetExportedTypes()) - { - if (type.IsDefined(typeof(DeclareServiceAttribute), false)) - Inject(type); - } - } - private static void Inject(Type tp) { var name = tp.GetCustomAttribute().Name; @@ -117,14 +111,20 @@ private static object CreateService(string name) CreateService(dependent); var provider = Providers[name]; var instance = Activator.CreateInstance(provider); - Dispose.List.Add((IDisposable) instance); + if (typeof(IDisposable).IsAssignableFrom(Providers[name])) Dispose.List.Add((IDisposable) instance); Ready.Add(name, instance); return instance; } - private static readonly DisposeList Dispose = new DisposeList(); - private static readonly Dictionary Ready = new Dictionary(); - private static readonly Dictionary Providers = new Dictionary(); - private static readonly Dictionary Dependencies = new Dictionary(); + private class DisposeList + { + public readonly List List = new List(); + + ~DisposeList() + { + foreach (var disposable in List) + disposable.Dispose(); + } + } } } \ No newline at end of file diff --git a/Core/Path.cs b/Core/Umbrella.cs similarity index 70% rename from Core/Path.cs rename to Core/Umbrella.cs index f009587..1bac328 100644 --- a/Core/Path.cs +++ b/Core/Umbrella.cs @@ -1,7 +1,7 @@ -// -// Core: Path.cs +// +// NEWorld/Core: Umbrella.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -17,14 +17,4 @@ // along with NEWorld. If not, see . // -using System; - -namespace Core -{ - public static class Path - { - public static string Asset(string group) => AppContext.BaseDirectory + "/Assets/" + group + "/"; - - public static string Modules() => AppContext.BaseDirectory; - } -} \ No newline at end of file +[assembly:Core.DeclareNeWorldAssembly] diff --git a/Core/Utilities/RateController.cs b/Core/Utilities/RateController.cs index 0ede70f..70d07fb 100644 --- a/Core/Utilities/RateController.cs +++ b/Core/Utilities/RateController.cs @@ -1,7 +1,7 @@ // -// Core: RateController.cs +// NEWorld/Core: RateController.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System; using System.Threading; @@ -33,35 +32,44 @@ public struct RateController */ public RateController(int rate = 0) { - _rate = rate; - _last = _due = DateTime.Now; + this.rate = rate; + last = due = DateTime.Now; } /** * \brief Synchronize the internal timer with system clock. For cases that the timer doesn't keep up or forced resets */ - public void Sync() => _last = _due = DateTime.Now; + public void Sync() + { + last = due = DateTime.Now; + } /** * \brief Get elapsed time from the start of the tick, in milliseconds * \return Elapsed time from the start of the tick, in milliseconds */ - public int GetDeltaTimeMs() => (DateTime.Now - _last).Milliseconds; + public int GetDeltaTimeMs() + { + return (DateTime.Now - last).Milliseconds; + } /** * \brief Check if the deadline of the current tick has pased * \return true if the deadline is passed, false otherwise */ - public bool IsDue() => _rate <= 0 || DateTime.Now >= _due; + public bool IsDue() + { + return rate <= 0 || DateTime.Now >= due; + } /** * \brief Increase the internal timer by one tick. Sets the current due time as the starting time of the next tick */ public void IncreaseTimer() { - if (_rate <= 0) return; - _last = _due; - _due += TimeSpan.FromMilliseconds(1000 / _rate); + if (rate <= 0) return; + last = due; + due += TimeSpan.FromMilliseconds(1000 / rate); } /** @@ -70,13 +78,13 @@ public void IncreaseTimer() public void Yield() { if (!IsDue()) - Thread.Sleep(_due - DateTime.Now); + Thread.Sleep(due - DateTime.Now); else Sync(); IncreaseTimer(); } - private readonly int _rate; - private DateTime _due, _last; + private readonly int rate; + private DateTime due, last; } } \ No newline at end of file diff --git a/Core/Utilities/Singleton.cs b/Core/Utilities/Singleton.cs deleted file mode 100644 index 87ab555..0000000 --- a/Core/Utilities/Singleton.cs +++ /dev/null @@ -1,174 +0,0 @@ -// -// Core: Singleton.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using System.Reflection; - -// ReSharper disable InconsistentlySynchronizedField - -namespace Core.Utilities -{ - /// - /// Represents errors that occur while creating a singleton. - /// - /// - /// http://msdn.microsoft.com/en-us/library/ms229064(VS.80).aspx - /// - [Serializable] - public class SingletonException : Exception - { - /// - /// Initializes a new instance with a specified error message. - /// - /// The message that describes the error. - public SingletonException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance with a reference to the inner - /// exception that is the cause of this exception. - /// - /// - /// The exception that is the cause of the current exception, - /// or a null reference if no inner exception is specified. - /// - public SingletonException(Exception innerException) - : base(null, innerException) - { - } - - /// - /// Initializes a new instance with a specified error message and a - /// reference to the inner exception that is the cause of this exception. - /// - /// The message that describes the error. - /// - /// The exception that is the cause of the current exception, - /// or a null reference if no inner exception is specified. - /// - public SingletonException(string message, Exception innerException) - : base(message, innerException) - { - } - } - - /// - /// Manages the single instance of a class. - /// - /// - /// Generic variant of the strategy presented here : http://geekswithblogs.net/akraus1/articles/90803.aspx. - /// Prefered to http://www.yoda.arachsys.com/csharp/singleton.html, where static initialization doesn't allow - /// proper handling of exceptions, and doesn't allow retrying type initializers initialization later - /// (once a type initializer fails to initialize in .NET, it can't be re-initialized again). - /// - /// Type of the singleton class. - public static class Singleton - where T : class - { - #region Fields - - /// - /// The single instance of the target class. - /// - /// - /// The volatile keyword makes sure to remove any compiler optimization that could make concurrent - /// threads reach a race condition with the double-checked lock pattern used in the Instance property. - /// See http://www.bluebytesoftware.com/blog/PermaLink,guid,543d89ad-8d57-4a51-b7c9-a821e3992bf6.aspx - /// - private static volatile T _instance; - - /// - /// The dummy object used for locking. - /// - // ReSharper disable once StaticMemberInGenericType - private static readonly object Lock = new object(); - - #endregion Fields - - - #region Constructors - - /// - /// Type-initializer to prevent type to be marked with beforefieldinit. - /// - /// - /// This simply makes sure that static fields initialization occurs - /// when Instance is called the first time and not before. - /// - static Singleton() - { - } - - #endregion Constructors - - - #region Properties - - /// - /// Gets the single instance of the class. - /// - public static T Instance - { - get - { - if (Test()) return _instance; - lock (Lock) - { - if (!Test()) _instance = ConstructInstance(); - } - - return _instance; - } - } - - private static readonly bool StrictDisposable = typeof(T).IsSubclassOf(typeof(StrictDispose)); - - private static bool Test() - { - if (_instance == null) return false; - return !StrictDisposable || Valid(_instance); - } - - private static bool Valid(dynamic obj) => obj.Valid(); - - private static T ConstructInstance() - { - ConstructorInfo constructor; - try - { - // Binding flags exclude public constructors. - constructor = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, - new Type[0], null); - } - catch (Exception exception) - { - throw new SingletonException(exception); - } - - if (constructor == null || constructor.IsAssembly) // Also exclude internal constructors. - throw new SingletonException($"A private or protected constructor is missing for '{typeof(T).Name}'."); - - return (T) constructor.Invoke(null); - } - - #endregion Properties - } -} \ No newline at end of file diff --git a/Core/Utilities/StrictDispose.cs b/Core/Utilities/StrictDispose.cs deleted file mode 100644 index 5d26503..0000000 --- a/Core/Utilities/StrictDispose.cs +++ /dev/null @@ -1,78 +0,0 @@ -// -// Core: StrictDispose.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; - -namespace Core.Utilities -{ - public class StrictDispose : IDisposable - { - ~StrictDispose() => Dispose(); - - protected T Inject(T target) where T : StrictDispose - { - if (_first != null) - target._sibling = _first; - _first = target; - return target; - } - - protected void Reject(T target, bool disposeNow = true) where T : StrictDispose - { - if (target == _first) - { - _first = target._sibling; - return; - } - - var current = _first; - while (current._sibling != target) - current = current._sibling; - current._sibling = target._sibling; - - if (disposeNow) - target.Dispose(); - } - - protected virtual void Release() - { - } - - private void TravelRelease() - { - for (var current = _first; current != null; current = current._sibling) - current.Dispose(); - } - - public void Dispose() - { - if (_released) - return; - TravelRelease(); - Release(); - _released = true; - } - - public bool Valid() => !_released; - - private StrictDispose _first, _sibling; - - private bool _released; - } -} \ No newline at end of file diff --git a/Core/packages.config b/Core/packages.config deleted file mode 100644 index 712ef9f..0000000 --- a/Core/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Game/ChunkService.cs b/Game/ChunkService.cs index f6cbc66..b4aaae1 100644 --- a/Game/ChunkService.cs +++ b/Game/ChunkService.cs @@ -1,7 +1,7 @@ // -// Game: ChunkService.cs +// NEWorld/Game: ChunkService.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,7 +16,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - +using Core; using Game.World; namespace Game @@ -37,21 +37,22 @@ public class ChunkService * and in the server-side of the multiplayer mode * are authoritative. */ - protected ChunkService(bool isAuthority) + static ChunkService() { - IsAuthority = isAuthority; + IsAuthority = true; Worlds = new WorldManager(); - TaskDispatcher = new TaskDispatcher(4, this); + TaskDispatcher = Services.Get("Game.TaskDispatcher"); } - private ChunkService() : this(true) - { - } + public static TaskDispatcher TaskDispatcher { get; private set; } - public TaskDispatcher TaskDispatcher { get; } + public static WorldManager Worlds { get; private set; } - public WorldManager Worlds { get; } + public static bool IsAuthority { set; get; } - public bool IsAuthority { set; get; } + public static void EnableDispatcher() + { + TaskDispatcher.Start(); + } } } \ No newline at end of file diff --git a/Game/Client/PlayerChunkView.cs b/Game/Client/PlayerChunkView.cs new file mode 100644 index 0000000..36349d6 --- /dev/null +++ b/Game/Client/PlayerChunkView.cs @@ -0,0 +1,54 @@ +// +// NEWorld/Game: PlayerChunkView.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using Game.World; +using Xenko.Core.Mathematics; + +namespace Game.Client +{ + public class PlayerChunkView + { + private const int SectionMask = 0b1111; + + private const int SectionBits = 4; + + private Chunk[,,][,,] Section; + + private Int3 BasePosition; + + private Chunk[,,] GetSectionRelative(Int3 offset) + { + return Section[offset.X >> SectionBits, offset.Y >> SectionBits, offset.Y >> SectionBits]; + } + + private Chunk GetChunkRelative(Int3 offset) + { + return GetSectionRelative(offset)[offset.X & SectionMask, offset.Y & SectionMask, offset.Z & SectionMask]; + } + + private Int3 ComputeRelative(Int3 absolute) + { + return absolute - BasePosition; + } + + public Chunk GetChunk(Int3 chunk) + { + return GetChunkRelative(ComputeRelative(chunk)); + } + } +} \ No newline at end of file diff --git a/Game/Events.cs b/Game/Events.cs new file mode 100644 index 0000000..2e818ff --- /dev/null +++ b/Game/Events.cs @@ -0,0 +1,47 @@ +// +// NEWorld/NEWorld: Events.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// + +using Core; + +namespace Game +{ + public class GameRenderPrepareEvent + { + } + + public class GameLoadEvent + { + } + + public class GameStartEvent + { + } + + public class GameEndEvent + { + } + + public class GameUnloadEvent + { + } + + public class GameRenderFinalizeEvent + { + } +} diff --git a/Game/Game.csproj b/Game/Game.csproj index 0c12459..7955c02 100644 --- a/Game/Game.csproj +++ b/Game/Game.csproj @@ -1,81 +1,18 @@ - - - + + - Debug - AnyCPU - {F83C4945-ABFF-4856-94A3-D0D31C976A11} - Library - Properties - Game - Game - v4.7.1 - 512 + netstandard2.0 - - AnyCPU - true - full - false - ..\bin\Debug\ - DEBUG;TRACE - prompt - 4 + + true - - AnyCPU - pdbonly - true - ..\bin\Release\ - TRACE - prompt - 4 + + true + - - - ..\packages\MsgPack.Cli.1.0.0\lib\net46\MsgPack.dll - True - - - + - - - - - - - - - - - - - - - - - - - - - - {ecb0e309-625f-4a24-926d-d1d23c1b7693} - Core - - - - - - - - \ No newline at end of file + diff --git a/Game/Game.xkpkg b/Game/Game.xkpkg new file mode 100644 index 0000000..cbfc060 --- /dev/null +++ b/Game/Game.xkpkg @@ -0,0 +1,17 @@ +!Package +SerializedVersion: {Assets: 3.1.0.0} +Meta: + Name: Game + Version: 1.0.0 + Authors: [] + Owners: [] + Dependencies: null +AssetFolders: + - Path: !dir Assets +ResourceFolders: + - !dir Resources +OutputGroupDirectories: {} +ExplicitFolders: [] +Bundles: [] +TemplateFolders: [] +RootAssets: [] diff --git a/Game/Network/Client.cs b/Game/Network/Client.cs index d4634d3..0972f8b 100644 --- a/Game/Network/Client.cs +++ b/Game/Network/Client.cs @@ -1,7 +1,7 @@ // -// Game: Client.cs +// NEWorld/Game: Client.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,39 +16,46 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System; +using System.Threading.Tasks; using Core; +using Core.Network; namespace Game.Network { [DeclareService("Game.Client")] public class Client : IDisposable { - public void Enable(string address, int port) - { - _client = new Core.Network.Client(address, port); - _client.RegisterProtocol(GetChunk = new GetChunk.Client(_client.GetConnection())); - _client.RegisterProtocol(GetAvailableWorldId = new GetAvailableWorldId.Client(_client.GetConnection())); - _client.RegisterProtocol(GetWorldInfo = new GetWorldInfo.Client(_client.GetConnection())); - _client.NegotiateProtocols(); - } - public static GetChunk.Client GetChunk; public static GetAvailableWorldId.Client GetAvailableWorldId; public static GetWorldInfo.Client GetWorldInfo; + public static GetStaticChunkIds.Client GetStaticChunkIds; - public void Stop() - { - _client.Close(); - } + private static Core.Network.Client _client; public void Dispose() { - _client?.Close(); _client?.Dispose(); } - private Core.Network.Client _client; + public async Task Enable(string address, int port) + { + _client = new Core.Network.Client(address, port); + _client.RegisterProtocol(GetChunk = new GetChunk.Client()); + _client.RegisterProtocol(GetAvailableWorldId = new GetAvailableWorldId.Client()); + _client.RegisterProtocol(GetWorldInfo = new GetWorldInfo.Client()); + _client.RegisterProtocol(GetStaticChunkIds = new GetStaticChunkIds.Client()); + await _client.HandShake(); + } + + public static Session.Send CreateMessage(uint protocol) + { + return _client.CreateMessage(protocol); + } + + public void Stop() + { + _client.Close(); + } } } \ No newline at end of file diff --git a/Game/Network/Protocols.cs b/Game/Network/Protocols.cs index 75d579f..62ef3af 100644 --- a/Game/Network/Protocols.cs +++ b/Game/Network/Protocols.cs @@ -1,7 +1,7 @@ // -// Game: Protocols.cs +// NEWorld/Game: Protocols.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,184 +16,245 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System.Collections.Generic; -using System.Net.Sockets; using System.Threading; -using Core.Math; +using System.Threading.Tasks; using Core.Network; -using Core.Utilities; using Game.World; -using MsgPack.Serialization; +using MessagePack; +using Xenko.Core.Mathematics; namespace Game.Network { + public static class GetStaticChunkIds + { + public class Server : FixedLengthProtocol + { + public Server() : base(4){} + + public override string Name() + { + return "GetStaticChunkId"; + } + + public override void HandleRequest(Session.Receive request) + { + var session = request.ReadU32(); + var ids = StaticChunkPool.Id; + Reply.Send(request.Session, session, MessagePackSerializer.SerializeUnsafe(ids)); + } + } + + public class Client : StubProtocol + { + public override string Name() + { + return "GetStaticChunkId"; + } + + public async Task Call() + { + var session = Reply.AllocSession(); + using (var message = Network.Client.CreateMessage(Id)) + { + message.Write(session.Key); + } + + var result = await session.Value; + StaticChunkPool.Id = MessagePackSerializer.Deserialize>(result); + } + } + } + public static class GetChunk { + private static readonly ThreadLocal LocalMemCache = new ThreadLocal(); + private static readonly int Size = MessagePackSerializer.SerializeUnsafe(new int[4]).Count; + public class Server : FixedLengthProtocol { public Server() : base(Size) { } - public override string Name() => "GetChunk"; + public override string Name() + { + return "GetChunk"; + } - protected override void HandleRequestData(byte[] data, NetworkStream stream) + public override void HandleRequest(Session.Receive stream) { - var request = From.UnpackSingleObject(data); - var chunkData = Get((uint) request[0], new Vec3(request[1], request[2], request[3])); - Send(stream, Request(Id)); - Send(stream, data); - Send(stream, chunkData); + var request = MessagePackSerializer.Deserialize(stream.Raw); + var chunk = GetChunk((uint) request[0], new Int3(request[1], request[2], request[3])); + using (var message = stream.Session.CreateMessage(Id)) + { + message.Write(stream.Raw, 0, Size); + var cow = chunk.CopyOnWrite; + message.Write(cow); + if (cow == uint.MaxValue) + { + var chunkData = Get(chunk); + message.Write(chunkData, 0, chunkData.Length); + } + } } - private static readonly ThreadLocal LocalMemCache = new ThreadLocal(); + private static byte[] Get(Chunk chunk) + { + var chunkData = LocalMemCache.Value ?? (LocalMemCache.Value = new byte[32768 * 4]); + chunk.SerializeTo(chunkData); + return chunkData; + } - private static byte[] Get(uint worldId, Vec3 position) + private static Chunk GetChunk(uint worldId, Int3 position) { - // TODO: empty chunk optimization - var world = Singleton.Instance.Worlds.Get(worldId); + var world = ChunkService.Worlds.Get(worldId); Chunk chunkPtr; try { - chunkPtr = world.GetChunk(ref position); + chunkPtr = world.GetChunk(position); } catch (KeyNotFoundException) { var chunk = new Chunk(position, world); - chunkPtr = world.InsertChunkAndUpdate(position, chunk); + // TODO: Implement a WorldTask Instead + chunkPtr = world.InsertChunkAndUpdate(chunk); } - var chunkData = LocalMemCache.Value ?? (LocalMemCache.Value = new byte[32768 * 4]); - for (var i = 0; i < 32768 * 4; ++i) - { - var block = chunkPtr.Blocks[i >> 2]; - chunkData[i++] = (byte) (block.Id >> 4); - chunkData[i++] = (byte) ((block.Id << 4) | block.Brightness); - chunkData[i++] = (byte) (block.Data >> 8); - chunkData[i] = (byte) block.Data; - } - - return chunkData; + return chunkPtr; } } - public class Client : FixedLengthProtocol + public class Client : Protocol { - public override string Name() => "GetChunk"; - - public Client(ConnectionHost.Connection conn) : base(32768 * 4 + Size) => _stream = conn.Stream; - - protected override void HandleRequestData(byte[] data, NetworkStream stream) + public override string Name() { - var req = From.UnpackSingleObject(data); - var srv = Singleton.Instance; - var chk = new Chunk(new Vec3(req[1], req[2], req[3]), srv.Worlds.Get((uint) req[0])); - for (var i = Size; i < 32768 * 4 + Size; i += 4) + return "GetChunk"; + } + + public override void HandleRequest(Session.Receive request) + { + var buffer = new byte[Size]; + request.Read(buffer, 0, Size); + var req = MessagePackSerializer.Deserialize(buffer); + var cow = request.ReadU32(); + Chunk chk; + var chunkPos = new Int3(req[1], req[2], req[3]); + var world = ChunkService.Worlds.Get((uint) req[0]); + if (cow == uint.MaxValue) + { + var data = LocalMemCache.Value ?? (LocalMemCache.Value = new byte[32768 * 4]); + request.Read(data, 0, data.Length); + chk = DeserializeChunk(chunkPos, world, data); + } + else { - ref var block = ref chk.Blocks[(i - Size) >> 2]; - block.Id = (ushort) (data[i] << 4 | data[i + 1] >> 4); - block.Brightness = (byte) (data[i + 1] | 0xF); - block.Data = (uint) (data[i + 2] << 8 | data[i + 3]); + chk = new Chunk(chunkPos, world, cow); } - srv.TaskDispatcher.Add(new World.World.ResetChunkTask((uint) req[0], chk)); + ChunkService.TaskDispatcher.Add(new World.World.ResetChunkTask(chk)); } - public void Call(uint worldId, Vec3 position) + private static Chunk DeserializeChunk(Int3 chunkPos, World.World world, byte[] data) { - var data = new[] {(int) worldId, position.X, position.Y, position.Z}; - Send(_stream, Request(Id, From.PackSingleObjectAsBytes(data))); + var chk = new Chunk(chunkPos, world, Chunk.InitOption.AllocateUnique); + chk.DeserializeFrom(data); + return chk; } - private readonly NetworkStream _stream; + public void Call(uint worldId, Int3 position) + { + var data = new[] {(int) worldId, position.X, position.Y, position.Z}; + using (var message = Network.Client.CreateMessage(Id)) + { + message.Write(MessagePackSerializer.SerializeUnsafe(data)); + } + } } - - private static readonly MessagePackSerializer From = MessagePackSerializer.Get(); - private static readonly int Size = From.PackSingleObject(new int[4]).Length; } public static class GetAvailableWorldId { public class Server : FixedLengthProtocol { - public Server() : base(Size) + public Server() : base(4) { } - protected override void HandleRequestData(byte[] data, NetworkStream stream) + public override void HandleRequest(Session.Receive request) { - var request = SerialSend.UnpackSingleObject(data); - Send(stream, Reply(request, SerialReply.PackSingleObjectAsBytes(new uint[] {0}))); + var session = request.ReadU32(); + Reply.Send(request.Session, session, MessagePackSerializer.SerializeUnsafe(new uint[] {0})); } - public override string Name() => "GetAvailableWorldId"; + public override string Name() + { + return "GetAvailableWorldId"; + } } public class Client : StubProtocol { - public Client(ConnectionHost.Connection conn) => _stream = conn.Stream; - - public override string Name() => "GetAvailableWorldId"; - - public uint[] Call() + public override string Name() { - var session = ProtocolReply.AllocSession(); - Send(_stream, Request(Id, SerialSend.PackSingleObjectAsBytes(session.Key))); - return SerialReply.UnpackSingleObject(session.Value.Result); + return "GetAvailableWorldId"; } - private readonly NetworkStream _stream; - } - - private static readonly MessagePackSerializer SerialSend = MessagePackSerializer.Get(); - - private static readonly MessagePackSerializer SerialReply = MessagePackSerializer.Get(); + public async Task Call() + { + var session = Reply.AllocSession(); + using (var message = Network.Client.CreateMessage(Id)) + { + message.Write(session.Key); + } - private static readonly int Size = SerialSend.PackSingleObject(0).Length; + var result = await session.Value; + return MessagePackSerializer.Deserialize(result); + } + } } public static class GetWorldInfo { public class Server : FixedLengthProtocol { - public Server() : base(Size) + public Server() : base(8) { } - protected override void HandleRequestData(byte[] data, NetworkStream stream) + public override void HandleRequest(Session.Receive stream) { - var ret = new Dictionary(); - var request = SerialSend.UnpackSingleObject(data); - var world = Singleton.Instance.Worlds.Get((uint) request[1]); - ret.Add("name", world.Name); - Send(stream, Reply(request[0], SerialReply.PackSingleObjectAsBytes(ret))); + var request = stream.ReadU32(); + var world = ChunkService.Worlds.Get(stream.ReadU32()); + var ret = new Dictionary {{"name", world.Name}}; + Reply.Send(stream.Session, request, MessagePackSerializer.SerializeUnsafe(ret)); } - public override string Name() => "GetWorldInfo"; + public override string Name() + { + return "GetWorldInfo"; + } } public class Client : StubProtocol { - public Client(ConnectionHost.Connection conn) => _stream = conn.Stream; - - public override string Name() => "GetWorldInfo"; - - public Dictionary Call(uint wid) + public override string Name() { - var session = ProtocolReply.AllocSession(); - Send(_stream, Request(Id, SerialSend.PackSingleObjectAsBytes(new[] {session.Key, (int) wid}))); - return SerialReply.UnpackSingleObject(session.Value.Result); + return "GetWorldInfo"; } - private readonly NetworkStream _stream; - } - - private static readonly MessagePackSerializer SerialSend = MessagePackSerializer.Get(); - - private static readonly MessagePackSerializer> SerialReply = - MessagePackSerializer.Get>(); + public async Task> Call(uint wid) + { + var session = Reply.AllocSession(); + using (var message = Network.Client.CreateMessage(Id)) + { + message.Write(session.Key); + message.Write(wid); + } - private static readonly int Size = SerialSend.PackSingleObject(new int[2]).Length; + var result = await session.Value; + return MessagePackSerializer.Deserialize>(result); + } + } } } \ No newline at end of file diff --git a/Game/Network/Server.cs b/Game/Network/Server.cs index a8b533a..a89a913 100644 --- a/Game/Network/Server.cs +++ b/Game/Network/Server.cs @@ -1,7 +1,7 @@ // -// Game: Server.cs +// NEWorld/Game: Server.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,46 +16,67 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System; using System.Threading.Tasks; using Core; -using Core.Utilities; namespace Game.Network { [DeclareService("Game.Server")] public class Server : IDisposable { + private Core.Network.Server server; + + private Task wait; + + ~Server() + { + Dispose(false); + } public void Enable(int port) { - _server = new Core.Network.Server(port); - _server.RegisterProtocol(new GetChunk.Server()); - _server.RegisterProtocol(new GetAvailableWorldId.Server()); - _server.RegisterProtocol(new GetWorldInfo.Server()); - Singleton.Instance.Worlds.Add("test world"); + server = new Core.Network.Server(port); + server.RegisterProtocol(new GetChunk.Server()); + server.RegisterProtocol(new GetAvailableWorldId.Server()); + server.RegisterProtocol(new GetWorldInfo.Server()); + server.RegisterProtocol(new GetStaticChunkIds.Server()); + ChunkService.Worlds.Add("test world"); } public void Run() { - _wait = _server.RunAsync(); + wait = server.RunAsync(); } - public int CountConnections() => _server.CountConnections(); + public int CountConnections() + { + return server.CountConnections(); + } public void Stop() { - _server.StopServer(); - _wait.Wait(); + server.ShutDown(); + wait.Wait(); } - private Task _wait; - private Core.Network.Server _server; + private void ReleaseUnmanagedResources() + { + Stop(); + } + + private void Dispose(bool disposing) + { + ReleaseUnmanagedResources(); + if (disposing) + { + wait?.Dispose(); + } + } public void Dispose() { - Stop(); - _wait?.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } } } \ No newline at end of file diff --git a/Game/Properties/AssemblyInfo.cs b/Game/Properties/AssemblyInfo.cs deleted file mode 100644 index 3609eca..0000000 --- a/Game/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Game: AssemblyInfo.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Game")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Game")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("F83C4945-ABFF-4856-94A3-D0D31C976A11")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Game/TaskDispatcher.cs b/Game/TaskDispatcher.cs index 4a94d30..72edc2b 100644 --- a/Game/TaskDispatcher.cs +++ b/Game/TaskDispatcher.cs @@ -1,7 +1,7 @@ // -// Game: TaskDispatcher.cs +// NEWorld/Game: TaskDispatcher.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -17,26 +17,19 @@ // along with NEWorld. If not, see . // +using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Threading; using Core; using Core.Utilities; namespace Game { - // TODO: we can add a `finished` flag in DEBUG mode - // to verify that all tasks are indeed processed. - /** - * \brief This type of tasks will be executed concurrently. - * Note that "ReadOnly" here is with respect to chunks - * data specifically. However please be aware of - * thread safety when you write something other than - * chunks. - */ - public interface IReadOnlyTask + public interface IInstancedTask { - void Task(ChunkService srv); - IReadOnlyTask Clone(); + void Task(int instance, int instanceCount); } /** @@ -46,187 +39,254 @@ public interface IReadOnlyTask */ public interface IReadWriteTask { - void Task(ChunkService srv); - IReadWriteTask Clone(); + void Task(); } - /** - * \brief This type of tasks will be executed in main thread. - * Thus, it is safe to call OpenGL function inside. - */ - public interface IRenderTask + + public interface IRegularReadOnlyTask : IInstancedTask { - void Task(ChunkService srv); - IRenderTask Clone(); } - public class TaskDispatcher + [DeclareService("Game.TaskDispatcher")] + public class TaskDispatcher : IDisposable { + private readonly Barrier barrier; + + // TODO: replace it with lock-free structure. + private readonly object mutex; + private readonly ConcurrentBag readOnlyTasks; + private readonly List regularReadOnlyTasks; + private readonly List regularReadWriteTasks; + private readonly List threads; + + private RateController meter = new RateController(30); + private List readWriteTasks, nextReadWriteTasks; + private List renderTasks, nextRenderTasks; + private bool shouldExit; + + // Automatic Creation. We reserve one virtual processor for main thread + public TaskDispatcher() : this(Environment.ProcessorCount - 1) + { + } + /** * \brief Initialize the dispatcher and start threads. - * \param threadNumber The number of threads in the thread pool + * \param threads.Count The number of threads in the thread pool * \param chunkService the chunk service that the dispatcher binds to */ - public TaskDispatcher(int threadNumber, ChunkService chunkService) + private TaskDispatcher(int threadNumber) { - _threadNumber = threadNumber; - _chunkService = chunkService; + barrier = new Barrier(threadNumber); + threads = new List(threadNumber); TimeUsed = new int[threadNumber]; - _mutex = new object(); - _readOnlyTasks = new List(); - _nextReadOnlyTasks = new List(); - _regularReadOnlyTasks = new List(); - _readWriteTasks = new List(); - _nextReadWriteTasks = new List(); - _regularReadWriteTasks = new List(); - _renderTasks = new List(); - _nextRenderTasks = new List(); + mutex = new object(); + readOnlyTasks = new ConcurrentBag(); + regularReadOnlyTasks = new List(); + readWriteTasks = new List(); + nextReadWriteTasks = new List(); + regularReadWriteTasks = new List(); + renderTasks = new List(); + nextRenderTasks = new List(); + } + + public int[] TimeUsed { get; } + + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + /** + * \brief This type of tasks will be executed concurrently. + * Note that "ReadOnly" here is with respect to chunks + * data specifically. However please be aware of + * thread safety when you write something other than + * chunks. + */ + public NextReadOnlyChanceS NextReadOnlyChance() + { + return new NextReadOnlyChanceS(); + } + + /** + * \brief This type of tasks will be executed in main thread. + * Thus, it is safe to call OpenGL function inside. + */ + public NextRenderChanceS NextRenderChance() + { + return new NextRenderChanceS(); } ~TaskDispatcher() { - if (!_shouldExit) Stop(); + ReleaseUnmanagedResources(); } public void Start() { - _shouldExit = false; - _roundNumber = 0; - _numberOfUnfinishedThreads = _threadNumber; - _threads = new List(_threadNumber); - for (var i = 0; i < _threadNumber; ++i) + shouldExit = false; + for (var i = 0; i < TimeUsed.Length; ++i) { var i1 = i; var trd = new Thread(() => { Worker(i1); }); trd.Start(); - _threads.Add(trd); + threads.Add(trd); } } - public void Add(IReadOnlyTask task) + private void AddReadOnlyTask(Action task) { - lock (_mutex) - _nextReadOnlyTasks.Add(task); + readOnlyTasks.Add(task); } public void Add(IReadWriteTask task) { - lock (_mutex) - _nextReadWriteTasks.Add(task); + lock (mutex) + { + nextReadWriteTasks.Add(task); + } } - public void Add(IRenderTask task) + private void AddRenderTask(Action task) { - lock (_mutex) - _nextRenderTasks.Add(task); + lock (mutex) + { + nextRenderTasks.Add(task); + } } - public void AddRegular(IReadOnlyTask task) + public void AddRegular(IRegularReadOnlyTask task) { - lock (_mutex) - _regularReadOnlyTasks.Add(task); + lock (mutex) + { + regularReadOnlyTasks.Add(task); + } } public void AddRegular(IReadWriteTask task) { - lock (_mutex) - _regularReadWriteTasks.Add(task); + lock (mutex) + { + regularReadWriteTasks.Add(task); + } } - public int GetRegularReadOnlyTaskCount() => _regularReadOnlyTasks.Count; - - public int GetRegularReadWriteTaskCount() => _regularReadWriteTasks.Count; - /** * \brief Process render tasks. * This function should be called from the main thread. */ public void ProcessRenderTasks() { - lock (_mutex) + lock (mutex) { - foreach (var task in _renderTasks) - task.Task(_chunkService); - _renderTasks.Clear(); - Generic.Swap(ref _renderTasks, ref _nextRenderTasks); + foreach (var task in renderTasks) + task(); + renderTasks.Clear(); + Generic.Swap(ref renderTasks, ref nextRenderTasks); } } - public int[] TimeUsed { get; } - - public void Stop() + public void Reset() { - _shouldExit = true; - foreach (var thread in _threads) - thread.Join(); + if (!shouldExit) + { + shouldExit = true; + foreach (var thread in threads) + thread.Join(); + // TODO: Clear the queues + } } private void Worker(int threadId) { - var meter = new RateController(30); - while (!_shouldExit) + while (!shouldExit) { - // A tick starts - var currentRoundNumber = _roundNumber; - // Process read-only work. - for (var i = threadId; i < _readOnlyTasks.Count; i += _threadNumber) + ProcessReadonlyTasks(threadId); + // The last finished thread is responsible to do writing jobs + if (barrier.ParticipantsRemaining == 1) { - _readOnlyTasks[i].Task(_chunkService); + QueueSwap(); + ProcessReadWriteTasks(); + meter.Yield(); // Rate Control } - // Finish the tick TimeUsed[threadId] = meter.GetDeltaTimeMs(); + barrier.SignalAndWait(); + } + } - // The last finished thread is responsible to do writing jobs - if (Interlocked.Decrement(ref _numberOfUnfinishedThreads) == 0) + private void ProcessReadonlyTasks(int i) + { + for (var cnt = 0; cnt < regularReadOnlyTasks.Count; ++cnt) + regularReadOnlyTasks[cnt].Task(i, TimeUsed.Length); + + // TODO: Make sure the no further tasks will be added before exiting this function + // TODO: AddReadOnlyTask a timeout support for this to ensure the updation rate + while (readOnlyTasks.TryTake(out var task)) + task(); + } + + private void ProcessReadWriteTasks() + { + foreach (var task in regularReadWriteTasks) task.Task(); + foreach (var task in readWriteTasks) task.Task(); + readWriteTasks.Clear(); + } + + private void QueueSwap() + { + Generic.Swap(ref readWriteTasks, ref nextReadWriteTasks); + } + + private void ReleaseUnmanagedResources() + { + Reset(); + barrier?.Dispose(); + } + + public struct NextReadOnlyChanceS + { + public struct Awaiter : INotifyCompletion + { + public bool IsCompleted => false; + + public void GetResult() { - // All other threads have finished? - foreach (var task in _readWriteTasks) - { - task.Task(_chunkService); - } - - // ...and finish up! - _readOnlyTasks.Clear(); - _readWriteTasks.Clear(); - foreach (var task in _regularReadOnlyTasks) - _nextReadOnlyTasks.Add(task.Clone()); - foreach (var task in _regularReadWriteTasks) - _nextReadWriteTasks.Add(task.Clone()); - Generic.Swap(ref _readOnlyTasks, ref _nextReadOnlyTasks); - Generic.Swap(ref _readWriteTasks, ref _nextReadWriteTasks); - - // Limit UPS - meter.Yield(); - - // Time to move to next tick! - // Notify other threads that we are good to go - _numberOfUnfinishedThreads = _threadNumber; - Interlocked.Increment(ref _roundNumber); } - else + + public void OnCompleted(Action continuation) { - meter.Yield(); - // Wait for other threads... - while (_roundNumber == currentRoundNumber) - Thread.Yield(); + ChunkService.TaskDispatcher.AddReadOnlyTask(continuation); } } + + public Awaiter GetAwaiter() + { + return new Awaiter(); + } } - // TODO: replace it with lock-free structure. - private readonly object _mutex; - private List _readOnlyTasks, _nextReadOnlyTasks; - private readonly List _regularReadOnlyTasks; - private List _readWriteTasks, _nextReadWriteTasks; - private readonly List _regularReadWriteTasks; - private List _renderTasks, _nextRenderTasks; - private List _threads; - private readonly int _threadNumber; - private int _roundNumber; - private int _numberOfUnfinishedThreads; - private bool _shouldExit; - - private readonly ChunkService _chunkService; + public struct NextRenderChanceS + { + public struct Awaiter : INotifyCompletion + { + public bool IsCompleted => false; + + public void GetResult() + { + } + + public void OnCompleted(Action continuation) + { + ChunkService.TaskDispatcher.AddRenderTask(continuation); + } + } + + public Awaiter GetAwaiter() + { + return new Awaiter(); + } + } } } \ No newline at end of file diff --git a/Game/Terrain/Block.cs b/Game/Terrain/Block.cs index 47cf893..92645ca 100644 --- a/Game/Terrain/Block.cs +++ b/Game/Terrain/Block.cs @@ -1,7 +1,7 @@ // -// Game: Block.cs +// NEWorld/Game: Block.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,150 +16,108 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System.Collections.Generic; -using Core.Math; using Game.World; +using Xenko.Core.Mathematics; namespace Game.Terrain { - public unsafe struct BlockTexCoord + public struct BlockTexCoord { - public uint Pos; - public fixed float D[4]; + public uint Id; + public Int2 Tex; + } + + public class BlockRenderContext + { + public BlockRenderContext(Chunk current, Chunk[] neighbors) + { + Current = current; + Neighbors = neighbors; + } + + public readonly Chunk Current; + public readonly Chunk[] Neighbors; } public interface IBlockRenderer { void FlushTexture(IBlockTextures textures); - void Render(IVertexBuilder target, Chunk chunk, Vec3 pos); + void Render(IVertexBuilder target, BlockRenderContext context, Int3 tmp); } public interface IVertexBuilder { - void AddPrimitive(int verts, params float[] data); + void Rect(Int3 position, Int2 tex, uint face, int rotation, uint shade); } public interface IBlockTextures { - uint Add(string path); - unsafe void GetTexturePos(float* pos, uint id); + uint Add(string assetUri); + void GetTexturePos(ref BlockTexCoord pos); } public class DefaultBlockRenderer : IBlockRenderer { + private readonly BlockTexCoord[] tex; + public DefaultBlockRenderer(uint[] data) { - _tex = new BlockTexCoord[6]; + tex = new BlockTexCoord[6]; for (var i = 0; i < 6; ++i) - _tex[i].Pos = data[i]; + tex[i].Id = data[i]; } - public unsafe void FlushTexture(IBlockTextures textures) + public void FlushTexture(IBlockTextures textures) { for (var i = 0; i < 6; ++i) - fixed (float* tex = _tex[0].D) - textures.GetTexturePos(tex, _tex[i].Pos); + textures.GetTexturePos(ref tex[i]); } - public unsafe void Render(IVertexBuilder target, Chunk chunk, Vec3 pos) + public void Render(IVertexBuilder target, BlockRenderContext context, Int3 pos) { - var worldpos = chunk.Position * Chunk.Size + pos; - var curr = chunk[pos]; + var current = context.Current[pos]; BlockData[] neighbors = { - pos.X == Chunk.Size - 1 - ? chunk.World.GetBlock(new Vec3(worldpos.X + 1, worldpos.Y, worldpos.Z)) - : chunk[new Vec3(pos.X + 1, pos.Y, pos.Z)], + pos.X == Chunk.RowLast + ? context.Neighbors[0][0, pos.Y, pos.Z] + : context.Current[pos.X + 1, pos.Y, pos.Z], pos.X == 0 - ? chunk.World.GetBlock(new Vec3(worldpos.X - 1, worldpos.Y, worldpos.Z)) - : chunk[new Vec3(pos.X - 1, pos.Y, pos.Z)], - pos.Y == Chunk.Size - 1 - ? chunk.World.GetBlock(new Vec3(worldpos.X, worldpos.Y + 1, worldpos.Z)) - : chunk[new Vec3(pos.X, pos.Y + 1, pos.Z)], + ? context.Neighbors[1][Chunk.RowLast, pos.Y, pos.Z] + : context.Current[pos.X - 1, pos.Y, pos.Z], + pos.Y == Chunk.RowLast + ? context.Neighbors[2][pos.X, 0, pos.Z] + : context.Current[pos.X, pos.Y + 1, pos.Z], pos.Y == 0 - ? chunk.World.GetBlock(new Vec3(worldpos.X, worldpos.Y - 1, worldpos.Z)) - : chunk[new Vec3(pos.X, pos.Y - 1, pos.Z)], - pos.Z == Chunk.Size - 1 - ? chunk.World.GetBlock(new Vec3(worldpos.X, worldpos.Y, worldpos.Z + 1)) - : chunk[new Vec3(pos.X, pos.Y, pos.Z + 1)], + ? context.Neighbors[3][pos.X, Chunk.RowLast, pos.Z] + : context.Current[pos.X, pos.Y - 1, pos.Z], + pos.Z == Chunk.RowLast + ? context.Neighbors[4][pos.X, pos.Y, 0] + : context.Current[pos.X, pos.Y, pos.Z + 1], pos.Z == 0 - ? chunk.World.GetBlock(new Vec3(worldpos.X, worldpos.Y, worldpos.Z - 1)) - : chunk[new Vec3(pos.X, pos.Y, pos.Z - 1)] + ? context.Neighbors[5][pos.X, pos.Y, Chunk.RowLast] + : context.Current[pos.X, pos.Y, pos.Z - 1] }; - - // Right - if (AdjacentTest(curr, neighbors[0])) - fixed (float* tex = _tex[0].D) - target.AddPrimitive(4, - tex[0], tex[1], 0.5f, 0.5f, 0.5f, pos.X + 1.0f, pos.Y + 1.0f, pos.Z + 1.0f, - tex[0], tex[3], 0.5f, 0.5f, 0.5f, pos.X + 1.0f, pos.Y + 0.0f, pos.Z + 1.0f, - tex[2], tex[3], 0.5f, 0.5f, 0.5f, pos.X + 1.0f, pos.Y + 0.0f, pos.Z + 0.0f, - tex[2], tex[1], 0.5f, 0.5f, 0.5f, pos.X + 1.0f, pos.Y + 1.0f, pos.Z + 0.0f - ); - - // Left - if (AdjacentTest(curr, neighbors[1])) - fixed (float* tex = _tex[1].D) - target.AddPrimitive(4, - tex[0], tex[1], 0.5f, 0.5f, 0.5f, pos.X + 0.0f, pos.Y + 1.0f, pos.Z + 0.0f, - tex[0], tex[3], 0.5f, 0.5f, 0.5f, pos.X + 0.0f, pos.Y + 0.0f, pos.Z + 0.0f, - tex[2], tex[3], 0.5f, 0.5f, 0.5f, pos.X + 0.0f, pos.Y + 0.0f, pos.Z + 1.0f, - tex[2], tex[1], 0.5f, 0.5f, 0.5f, pos.X + 0.0f, pos.Y + 1.0f, pos.Z + 1.0f - ); - - // Top - if (AdjacentTest(curr, neighbors[2])) - fixed (float* tex = _tex[2].D) - target.AddPrimitive(4, - tex[0], tex[1], 1.0f, 1.0f, 1.0f, pos.X + 0.0f, pos.Y + 1.0f, pos.Z + 0.0f, - tex[0], tex[3], 1.0f, 1.0f, 1.0f, pos.X + 0.0f, pos.Y + 1.0f, pos.Z + 1.0f, - tex[2], tex[3], 1.0f, 1.0f, 1.0f, pos.X + 1.0f, pos.Y + 1.0f, pos.Z + 1.0f, - tex[2], tex[1], 1.0f, 1.0f, 1.0f, pos.X + 1.0f, pos.Y + 1.0f, pos.Z + 0.0f - ); - - // Bottom - if (AdjacentTest(curr, neighbors[3])) - fixed (float* tex = _tex[3].D) - target.AddPrimitive(4, - tex[0], tex[1], 1.0f, 1.0f, 1.0f, pos.X + 0.0f, pos.Y + 0.0f, pos.Z + 1.0f, - tex[0], tex[3], 1.0f, 1.0f, 1.0f, pos.X + 0.0f, pos.Y + 0.0f, pos.Z + 0.0f, - tex[2], tex[3], 1.0f, 1.0f, 1.0f, pos.X + 1.0f, pos.Y + 0.0f, pos.Z + 0.0f, - tex[2], tex[1], 1.0f, 1.0f, 1.0f, pos.X + 1.0f, pos.Y + 0.0f, pos.Z + 1.0f - ); - - // Front - if (AdjacentTest(curr, neighbors[4])) - fixed (float* tex = _tex[4].D) - target.AddPrimitive(4, - tex[0], tex[1], 0.7f, 0.7f, 0.7f, pos.X + 0.0f, pos.Y + 1.0f, pos.Z + 1.0f, - tex[0], tex[3], 0.7f, 0.7f, 0.7f, pos.X + 0.0f, pos.Y + 0.0f, pos.Z + 1.0f, - tex[2], tex[3], 0.7f, 0.7f, 0.7f, pos.X + 1.0f, pos.Y + 0.0f, pos.Z + 1.0f, - tex[2], tex[1], 0.7f, 0.7f, 0.7f, pos.X + 1.0f, pos.Y + 1.0f, pos.Z + 1.0f - ); - - // Back - if (AdjacentTest(curr, neighbors[5])) - fixed (float* tex = _tex[5].D) - target.AddPrimitive(4, - tex[0], tex[1], 0.7f, 0.7f, 0.7f, pos.X + 1.0f, pos.Y + 1.0f, pos.Z + 0.0f, - tex[0], tex[3], 0.7f, 0.7f, 0.7f, pos.X + 1.0f, pos.Y + 0.0f, pos.Z + 0.0f, - tex[2], tex[3], 0.7f, 0.7f, 0.7f, pos.X + 0.0f, pos.Y + 0.0f, pos.Z + 0.0f, - tex[2], tex[1], 0.7f, 0.7f, 0.7f, pos.X + 0.0f, pos.Y + 1.0f, pos.Z + 0.0f - ); + // Right Left Top Bottom Front Back + for (uint i = 0; i < 6; ++i) + if (AdjacentTest(current, neighbors[i])) + target.Rect(pos, tex[i].Tex, i, 0, 15); } - static bool AdjacentTest(BlockData a, BlockData b) => a.Id != 0 && !Blocks.Index[b.Id].IsOpaque && a.Id != b.Id; - - private readonly BlockTexCoord[] _tex; + private static bool AdjacentTest(BlockData a, BlockData b) + { + return a.Id != 0 && !Blocks.Index[b.Id].IsOpaque && a.Id != b.Id; + } } public static class BlockRenderers { - public static void Render(IVertexBuilder target, int id, Chunk chunk, Vec3 pos) + private static readonly List Renderers = new List(); + + public static void Render(IVertexBuilder target, int id, BlockRenderContext context, Int3 pos) { if (Renderers.Count > 0 && Renderers[id] != null) - Renderers[id].Render(target, chunk, pos); + Renderers[id].Render(target, context, pos); } public static void Add(int pos, IBlockRenderer blockRenderer) @@ -171,12 +129,7 @@ public static void Add(int pos, IBlockRenderer blockRenderer) public static void FlushTextures(IBlockTextures textures) { - foreach (var x in Renderers) - { - x?.FlushTexture(textures); - } + foreach (var x in Renderers) x?.FlushTexture(textures); } - - private static readonly List Renderers = new List(); } } \ No newline at end of file diff --git a/Game/Terrain/Matrix.cs b/Game/Terrain/Matrix.cs deleted file mode 100644 index 3d16927..0000000 --- a/Game/Terrain/Matrix.cs +++ /dev/null @@ -1,63 +0,0 @@ -// -// Game: Matrix.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using Core.Math; - -namespace Game.Terrain -{ - public static class Matrix - { - public static void RestoreModel() => _model = Mat4F.Identity(); - public static void RestoreView() => _view = Mat4F.Identity(); - public static void RestoreProjection() => _projection = Mat4F.Identity(); - - private static Mat4F _model = Mat4F.Identity(), _view = Mat4F.Identity(), _projection = Mat4F.Identity(); - - public static void ApplyPerspective(float fov, float aspect, float zNear, float zFar) => - _projection *= Mat4F.Perspective(fov, aspect, zNear, zFar); - - public static void ViewRotate(float degree, Vec3 axis) => _view *= Mat4F.Rotation(degree, axis); - - public static void ViewTranslate(Vec3 diff) => _view *= Mat4F.Translation(diff); - - public static void ModelRotate(float degree, Vec3 axis) => _model *= Mat4F.Rotation(degree, axis); - - public static void ModelTranslate(Vec3 diff) => _model *= Mat4F.Translation(diff); - - public static void ViewRotate(float degree, Vec3 axis) => _view *= Mat4F.Rotation(degree, Conv(axis)); - - public static void ViewTranslate(Vec3 diff) => _view *= Mat4F.Translation(Conv(diff)); - - public static void ModelRotate(float degree, Vec3 axis) => _model *= Mat4F.Rotation(degree, Conv(axis)); - - public static void ViewRotate(float degree, Vec3 axis) => _view *= Mat4F.Rotation(degree, Conv(axis)); - - public static void ViewTranslate(Vec3 diff) => _view *= Mat4F.Translation(Conv(diff)); - - public static void ModelRotate(float degree, Vec3 axis) => _model *= Mat4F.Rotation(degree, Conv(axis)); - - public static void ModelTranslate(Vec3 diff) => _model *= Mat4F.Translation(Conv(diff)); - - public static Mat4F Get() => _model * _view * _projection; - - private static Vec3 Conv(Vec3 v) => new Vec3((float) v.X, (float) v.Y, (float) v.Z); - - private static Vec3 Conv(Vec3 v) => new Vec3(v.X, v.Y, v.Z); - } -} \ No newline at end of file diff --git a/Game/Umbrella.cs b/Game/Umbrella.cs new file mode 100644 index 0000000..2b9989f --- /dev/null +++ b/Game/Umbrella.cs @@ -0,0 +1,20 @@ +// +// NEWorld/Game: Umbrella.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// + +[assembly:Core.DeclareNeWorldAssembly] diff --git a/Game/Utilities/Aabb.cs b/Game/Utilities/Aabb.cs index 5ae4008..c204613 100644 --- a/Game/Utilities/Aabb.cs +++ b/Game/Utilities/Aabb.cs @@ -1,7 +1,7 @@ // -// Game: Aabb.cs +// NEWorld/Game: Aabb.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,18 +16,17 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System; -using Core.Math; +using Xenko.Core.Mathematics; namespace Game.Utilities { public struct Aabb { // Min bound, Max bound - public Vec3 Min, Max; + public Double3 Min, Max; - public Aabb(Vec3 min, Vec3 max) + public Aabb(Double3 min, Double3 max) { Min = min; Max = max; @@ -106,9 +105,9 @@ public double MaxMoveOnZclip(Aabb box, double orgmove) } // Get expanded Aabb - public Aabb Expand(Vec3 arg) + public Aabb Expand(Double3 arg) { - Aabb res = this; + var res = this; if (arg.X > 0.0) res.Max.X += arg.X; @@ -129,7 +128,7 @@ public Aabb Expand(Vec3 arg) } // Move Aabb - public void Move(Vec3 arg) + public void Move(Double3 arg) { Min += arg; Max += arg; diff --git a/Game/Utilities/OrderedList.cs b/Game/Utilities/OrderedList.cs index 09c642d..35895eb 100644 --- a/Game/Utilities/OrderedList.cs +++ b/Game/Utilities/OrderedList.cs @@ -1,7 +1,7 @@ // -// Game: OrderedList.cs +// NEWorld/Game: OrderedList.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System; using System.Collections; using System.Collections.Generic; @@ -25,6 +24,9 @@ namespace Game.Utilities { public class OrderedListIntBase : IEnumerable> { + public readonly KeyValuePair[] Data; + public readonly int FixedSize; + protected OrderedListIntBase(int fixedSize) { Size = 0; @@ -32,6 +34,18 @@ protected OrderedListIntBase(int fixedSize) Data = new KeyValuePair[fixedSize]; } + public int Size { get; private set; } + + public IEnumerator> GetEnumerator() + { + return new OrderedListIntBaseEnum(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + protected void InsertBase(int first, int key, TD data) { if (first > Size || first >= FixedSize) return; @@ -41,36 +55,40 @@ protected void InsertBase(int first, int key, TD data) Data[first] = new KeyValuePair(key, data); } - public void Clear() => Size = 0; - - public int Size { get; private set; } - public readonly int FixedSize; - public readonly KeyValuePair[] Data; - - public IEnumerator> GetEnumerator() => new OrderedListIntBaseEnum(this); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Clear() + { + Size = 0; + } } public class OrderedListIntBaseEnum : IEnumerator> { - public OrderedListIntBaseEnum(OrderedListIntBase host) => _base = host; + private readonly OrderedListIntBase _base; + + private int position = -1; + + public OrderedListIntBaseEnum(OrderedListIntBase host) + { + _base = host; + } - public bool MoveNext() => ++_position < _base.Size; + public bool MoveNext() + { + return ++position < _base.Size; + } - public void Reset() => _position = -1; + public void Reset() + { + position = -1; + } - public KeyValuePair Current => _base.Data[_position]; + public KeyValuePair Current => _base.Data[position]; object IEnumerator.Current => Current; public void Dispose() { } - - private int _position = -1; - - private readonly OrderedListIntBase _base; } public class OrderedListIntLess : OrderedListIntBase diff --git a/Game/World/Blocks.cs b/Game/World/Blocks.cs index 0e64d6b..e25da66 100644 --- a/Game/World/Blocks.cs +++ b/Game/World/Blocks.cs @@ -1,7 +1,7 @@ // -// Game: Blocks.cs +// NEWorld/Game: Blocks.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - namespace Game.World { public struct BlockData @@ -53,10 +52,14 @@ public BlockType(string name, bool solid, bool translucent, bool opaque, int har public bool IsOpaque { get; } } + // TODO: Implement ID Synchronization for each Game Instance public static class Blocks { private static readonly BlockType Air = new BlockType("Air", false, false, false, 0); + public static readonly BlockType[] Index; + private static ushort _count; + static Blocks() { Index = new BlockType[1 << 12]; @@ -68,8 +71,5 @@ public static ushort Register(BlockType block) Index[_count] = block; return _count++; } - - public static readonly BlockType[] Index; - private static ushort _count; } } \ No newline at end of file diff --git a/Game/World/Chunk.cs b/Game/World/Chunk.cs index bfd30cb..ad9cfd5 100644 --- a/Game/World/Chunk.cs +++ b/Game/World/Chunk.cs @@ -1,7 +1,7 @@ // -// Game: Chunk.cs +// NEWorld/Game: Chunk.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,103 +16,66 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - -using System; -using System.Collections.Generic; -using System.Threading; -using Core.Math; +using Xenko.Core.Mathematics; namespace Game.World { - public class Chunk + // TODO: Implement Neighbor Chunk Handle Storage + // TODO: Implement Chunk Update Event Hook + // TODO: Implement The Unloaded Status Indicator + public unsafe partial class Chunk { - public delegate void Generator(Vec3 chunkPos, BlockData[] chunkData, int daylightBrightness); + public enum InitOption + { + None, + Build, + AllocateUnique + } - // Chunk size - private static bool _chunkGeneratorLoaded; - private static Generator _chunkGen; - public const int BlocksSize = 0b1000000000000000; - public const int SizeLog2 = 5; - public const int Size = 0b100000; + public Chunk(BlockData fill) + { + EnableFullArray(); + for (var i = 0; i < CubeSize; ++i) + Blocks[i] = fill; + } - public static void SetGenerator(Generator gen) + public Chunk(Int3 position, World world, InitOption option = InitOption.Build) { - if (!_chunkGeneratorLoaded) - { - _chunkGen = gen; - _chunkGeneratorLoaded = true; - } - else + Position = position; + World = world; + switch (option) { - throw new Exception("Chunk Generator Alreadly Loaded"); + case InitOption.Build: + Build(world.DaylightBrightness); + break; + case InitOption.AllocateUnique: + EnableFullArray(); + break; + default: + EnableCopyOnWrite(StaticChunkPool.GetAirChunk()); + break; } } - public Chunk(Vec3 position, World world) + public Chunk(Int3 position, World world, uint other) { Position = position; World = world; - Blocks = new BlockData[BlocksSize]; - Build(world.DaylightBrightness); + EnableCopyOnWrite(other); } - // TODO: somehow avoid it! not safe. + // TODO: Only Set On Real Content Updation public bool IsUpdated { get; set; } - public Vec3 Position { get; } + public Int3 Position { get; } public World World { get; } - public BlockData[] Blocks { get; } - - public BlockData this[Vec3 pos] - { - get => Blocks[pos.X * Size * Size + pos.Y * Size + pos.Z]; - set - { - Blocks[pos.X * Size * Size + pos.Y * Size + pos.Z] = value; - IsUpdated = true; - } - } - - // Build chunk - public void Build(int daylightBrightness) + public void MoveFrom(Chunk other) { - _chunkGen(Position, Blocks, daylightBrightness); - IsUpdated = true; + MoveFromImpl(other); } - // Reference Counting - public void MarkRequest() => _mLastRequestTime = DateTime.Now.Ticks; - - public bool CheckReleaseable() => - DateTime.Now - new DateTime(Interlocked.Read(ref _mLastRequestTime)) > TimeSpan.FromSeconds(10); - - // For Garbage Collection - private long _mLastRequestTime; - } - - [Serializable] - public class ChunkManager : Dictionary, Chunk> - { - public bool IsLoaded(Vec3 chunkPos) => ContainsKey(chunkPos); - - // Convert world position to chunk coordinate (one axis) - public static int GetAxisPos(int pos) => pos >> Chunk.SizeLog2; - - // Convert world position to chunk coordinate (all axes) - public static Vec3 GetPos(Vec3 pos) => - new Vec3(GetAxisPos(pos.X), GetAxisPos(pos.Y), GetAxisPos(pos.Z)); - - // Convert world position to block coordinate in chunk (one axis) - public static int GetBlockAxisPos(int pos) => pos & (Chunk.Size - 1); - - // Convert world position to block coordinate in chunk (all axes) - public static Vec3 GetBlockPos(Vec3 pos) => - new Vec3(GetBlockAxisPos(pos.X), GetBlockAxisPos(pos.Y), GetBlockAxisPos(pos.Z)); - - public BlockData GetBlock(Vec3 pos) => this[GetPos(pos)][GetBlockPos(pos)]; - - public void SetBlock(Vec3 pos, BlockData block) => this[GetPos(pos)][GetBlockPos(pos)] = block; + partial void MoveFromImpl(Chunk other); } } \ No newline at end of file diff --git a/Game/World/ChunkAccess.cs b/Game/World/ChunkAccess.cs new file mode 100644 index 0000000..1ce2c9a --- /dev/null +++ b/Game/World/ChunkAccess.cs @@ -0,0 +1,104 @@ +// +// NEWorld/Game: ChunkAccess.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using Xenko.Core.Mathematics; + +namespace Game.World +{ + public unsafe partial class Chunk + { + public BlockData this[int x, int y, int z] + { + get => Blocks[(x << BitShiftX) | (y << BitShiftY) | z]; + set + { + if (IsCopyOnWrite()) + ExecuteFullCopy(); + Blocks[(x << BitShiftX) | (y << BitShiftY) | z] = value; + IsUpdated = true; + } + } + + public BlockData this[Int3 pos] + { + get => Blocks[(pos.X << BitShiftX) | (pos.Y << BitShiftY) | pos.Z]; + set + { + if (IsCopyOnWrite()) + ExecuteFullCopy(); + Blocks[(pos.X << BitShiftX) | (pos.Y << BitShiftY) | pos.Z] = value; + IsUpdated = true; + } + } + + public void SerializeTo(byte[] data) + { + fixed (byte* buffer = data) + { + SerializeTo(buffer); + } + } + + public void SerializeTo(byte[] data, int offset) + { + fixed (byte* buffer = data) + { + SerializeTo(buffer + offset); + } + } + + public void SerializeTo(byte* buffer) + { + for (var i = 0; i < 32768 * 4; ++i) + { + var block = Blocks[i >> 2]; + buffer[i++] = (byte) (block.Id >> 4); + buffer[i++] = (byte) ((block.Id << 4) | block.Brightness); + buffer[i++] = (byte) (block.Data >> 8); + buffer[i] = (byte) block.Data; + } + } + + public void DeserializeFrom(byte[] data) + { + fixed (byte* buffer = data) + { + DeserializeFrom(buffer); + } + } + + public void DeserializeFrom(byte[] data, int offset) + { + fixed (byte* buffer = data) + { + DeserializeFrom(buffer + offset); + } + } + + public void DeserializeFrom(byte* buffer) + { + for (var i = 0; i < 32768 * 4; i += 4) + { + ref var block = ref Blocks[i >> 2]; + block.Id = (ushort) ((buffer[i] << 4) | (buffer[i + 1] >> 4)); + block.Brightness = (byte) (buffer[i + 1] | 0xF); + block.Data = (uint) ((buffer[i + 2] << 8) | buffer[i + 3]); + } + } + } +} \ No newline at end of file diff --git a/Game/World/ChunkConstants.cs b/Game/World/ChunkConstants.cs new file mode 100644 index 0000000..eeff063 --- /dev/null +++ b/Game/World/ChunkConstants.cs @@ -0,0 +1,32 @@ +// +// NEWorld/Game: ChunkConstants.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +namespace Game.World +{ + public partial class Chunk + { + public const int SizeLog2 = 5; + public const int RowSize = 32; + public const int RowLast = RowSize - 1; + public const int SliceSize = RowSize * RowSize; + public const int CubeSize = SliceSize * RowSize; + public const int BitShiftX = SizeLog2 * 2; + public const int BitShiftY = SizeLog2; + public const int AxisBits = 0b11111; + } +} \ No newline at end of file diff --git a/NEWorld/WidgetManager.cs b/Game/World/ChunkDisposePattern.cs similarity index 52% rename from NEWorld/WidgetManager.cs rename to Game/World/ChunkDisposePattern.cs index e08e424..ddf627d 100644 --- a/NEWorld/WidgetManager.cs +++ b/Game/World/ChunkDisposePattern.cs @@ -1,7 +1,7 @@ // -// NEWorld: WidgetManager.cs +// NEWorld/Game: ChunkDisposePattern.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,32 +16,23 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // +using System; -using System.Collections.Generic; - -namespace NEWorld +namespace Game.World { - internal class WidgetManager : Dictionary + public partial class Chunk: IDisposable { - public WidgetManager(NkSdl nkctx) => _mNkContext = nkctx; - - public void Render() + public void Dispose() { - foreach (var widget in this) - widget.Value._render(_mNkContext); - _mNkContext.End(); - // TODO: add an option to adjust the arguments - _mNkContext.Draw(); + ReleaseCriticalResources(); + GC.SuppressFinalize(this); } - public void Update() + partial void ReleaseCriticalResources(); + + ~Chunk() { - foreach (var widget in this) - widget.Value.Update(); + ReleaseCriticalResources(); } - - public void Add(Widget widget) => Add(widget.Name, widget); - - private readonly NkSdl _mNkContext; } } \ No newline at end of file diff --git a/Game/World/ChunkGenerator.cs b/Game/World/ChunkGenerator.cs new file mode 100644 index 0000000..76edacf --- /dev/null +++ b/Game/World/ChunkGenerator.cs @@ -0,0 +1,72 @@ +// +// NEWorld/Game: ChunkGenerator.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System; + +namespace Game.World +{ + public class ChunkGeneratorContext + { + public readonly Chunk Current; + + public readonly int DaylightBrightness; + + public ChunkGeneratorContext(Chunk current, int daylightBrightness) + { + Current = current; + DaylightBrightness = daylightBrightness; + } + + public void EnableCopyOnWrite(uint target) + { + Current.EnableCopyOnWrite(target); + } + + public void EnableFullArray() + { + Current.EnableFullArray(); + } + } + + public partial class Chunk + { + public delegate void Generator(ChunkGeneratorContext context); + + private static bool _chunkGeneratorLoaded; + private static Generator _chunkGen; + + public static void SetGenerator(Generator gen) + { + if (!_chunkGeneratorLoaded) + { + _chunkGen = gen; + _chunkGeneratorLoaded = true; + } + else + { + throw new Exception("Chunk Generator Already Loaded"); + } + } + + private void Build(int daylightBrightness) + { + _chunkGen(new ChunkGeneratorContext(this, daylightBrightness)); + IsUpdated = true; + } + } +} \ No newline at end of file diff --git a/Game/World/ChunkManager.cs b/Game/World/ChunkManager.cs new file mode 100644 index 0000000..1c3d000 --- /dev/null +++ b/Game/World/ChunkManager.cs @@ -0,0 +1,47 @@ +// +// NEWorld/Game: ChunkManager.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System.Collections.Generic; +using Xenko.Core.Mathematics; + +namespace Game.World +{ + public class ChunkManager : Dictionary + { + public bool IsLoaded(Int3 chunkPos) + { + return ContainsKey(chunkPos); + } + + // Convert world position to chunk coordinate (all axes) + public static Int3 GetPos(Int3 pos) + { + return new Int3(pos.X >> Chunk.SizeLog2, pos.Y >> Chunk.SizeLog2, pos.Z >> Chunk.SizeLog2); + } + + public BlockData GetBlock(Int3 pos) + { + return this[GetPos(pos)][pos.X & Chunk.AxisBits, pos.Y & Chunk.AxisBits, pos.Z & Chunk.AxisBits]; + } + + public void SetBlock(Int3 pos, BlockData block) + { + this[GetPos(pos)][pos.X & Chunk.AxisBits, pos.Y & Chunk.AxisBits, pos.Z & Chunk.AxisBits] = block; + } + } +} \ No newline at end of file diff --git a/Game/World/ChunkReleaseTimer.cs b/Game/World/ChunkReleaseTimer.cs new file mode 100644 index 0000000..f0481f8 --- /dev/null +++ b/Game/World/ChunkReleaseTimer.cs @@ -0,0 +1,38 @@ +// +// NEWorld/Game: ChunkReleaseTimer.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System; +using System.Threading; + +namespace Game.World +{ + public partial class Chunk + { + private long mLastRequestTime; + + public bool CheckReleaseable() + { + return DateTime.Now - new DateTime(Interlocked.Read(ref mLastRequestTime)) > TimeSpan.FromSeconds(10); + } + + public void MarkRequest() + { + mLastRequestTime = DateTime.Now.Ticks; + } + } +} \ No newline at end of file diff --git a/Game/World/ChunkStorage.cs b/Game/World/ChunkStorage.cs new file mode 100644 index 0000000..7f873d5 --- /dev/null +++ b/Game/World/ChunkStorage.cs @@ -0,0 +1,98 @@ +// +// NEWorld/Game: ChunkStorage.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System; +using System.Runtime.InteropServices; + +namespace Game.World +{ + public unsafe partial class Chunk + { + private static class Allocator + { + internal static BlockData* Allocate() + { + return (BlockData*) Marshal.AllocHGlobal(CubeSize * sizeof(BlockData)).ToPointer(); + } + + internal static void Release(BlockData* data) + { + Marshal.FreeHGlobal((IntPtr) data); + } + } + + public BlockData* Blocks { get; private set; } + public uint CopyOnWrite { get; private set; } = uint.MaxValue; + + internal void EnableFullArray() + { + CopyOnWrite = uint.MaxValue; + Blocks = Allocator.Allocate(); + } + + internal void EnableCopyOnWrite(uint other) + { + Blocks = StaticChunkPool.GetChunk(other).Blocks; + CopyOnWrite = other; + } + + private bool IsCopyOnWrite() + { + return CopyOnWrite != uint.MaxValue; + } + + private void ExecuteFullCopy() + { + lock (this) + { + if (IsCopyOnWrite()) + { + var old = Blocks; + EnableFullArray(); + for (var i = 0; i < CubeSize; ++i) + Blocks[i] = old[i]; + } + } + } + + private void ReleaseBlockData() + { + if (Blocks != null) + { + if (!IsCopyOnWrite()) + Allocator.Release(Blocks); + Blocks = null; + } + } + + partial void MoveFromImpl(Chunk other) + { + ReleaseBlockData(); + CopyOnWrite = other.CopyOnWrite; + Blocks = other.Blocks; + other.Blocks = null; + other.CopyOnWrite = uint.MaxValue; + IsUpdated = true; + } + + partial void ReleaseCriticalResources() + { + ReleaseBlockData(); + } + } +} diff --git a/Game/World/ChunkUpdate.cs b/Game/World/ChunkUpdate.cs new file mode 100644 index 0000000..3595a80 --- /dev/null +++ b/Game/World/ChunkUpdate.cs @@ -0,0 +1,76 @@ +// +// NEWorld/Game: ChunkUpdate.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// + +using Xenko.Core.Mathematics; + +namespace Game.World +{ + public partial class Chunk + { + public delegate void ChunkUpdateHandler(Chunk chunk); + + public static event ChunkUpdateHandler OnChunkUpdate; + + public static event ChunkUpdateHandler OnNeighborUpdate; + + private bool pendingLocalUpdate, pendingNeighborUpdate; + + private void TriggerUpdate() + { + EnqueueLocalUpdateTask(); + TriggerNeighborUpdate(); + } + + private async void EnqueueLocalUpdateTask() + { + pendingLocalUpdate = true; + await ChunkService.TaskDispatcher.NextReadOnlyChance(); + OnChunkUpdate?.Invoke(this); + } + + private void TriggerNeighborUpdate() + { + foreach (var neighbor in GetNeighbors()) + { + neighbor.EnqueueNeighborUpdateTask(); + } + } + + private async void EnqueueNeighborUpdateTask() + { + pendingNeighborUpdate = true; + await ChunkService.TaskDispatcher.NextReadOnlyChance(); + OnNeighborUpdate?.Invoke(this); + } + + public Chunk[] GetNeighbors() + { + // TODO: Cache The Value + return new [] + { + World.GetChunk(new Int3(Position.X + 1, Position.Y, Position.Z)), + World.GetChunk(new Int3(Position.X - 1, Position.Y, Position.Z)), + World.GetChunk(new Int3(Position.X, Position.Y + 1, Position.Z)), + World.GetChunk(new Int3(Position.X, Position.Y - 1, Position.Z)), + World.GetChunk(new Int3(Position.X, Position.Y, Position.Z + 1)), + World.GetChunk(new Int3(Position.X, Position.Y, Position.Z - 1)) + }; + } + } +} diff --git a/Game/World/Object.cs b/Game/World/Object.cs index 6dd4840..4fded48 100644 --- a/Game/World/Object.cs +++ b/Game/World/Object.cs @@ -1,7 +1,7 @@ // -// Game: Object.cs +// NEWorld/Game: Object.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,21 +16,26 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - -using Core.Math; using Game.Utilities; +using Xenko.Core.Mathematics; namespace Game.World { public abstract class Object { + public Aabb Hitbox; + + public Double3 Position; + public Double3 Rotation; + public Double3 Scale; + protected Object(uint worldId) { WorldId = worldId; - Scale = new Vec3(1.0, 1.0, 1.0); + Scale = new Double3(1.0, 1.0, 1.0); } - protected Object(uint worldId, Vec3 position, Vec3 rotation, Vec3 scale, Aabb hitbox) + protected Object(uint worldId, Double3 position, Double3 rotation, Double3 scale, Aabb hitbox) { WorldId = worldId; Position = position; @@ -39,15 +44,14 @@ protected Object(uint worldId, Vec3 position, Vec3 rotation, Vec Hitbox = hitbox; } - public void MoveHitbox(Vec3 delta) => Hitbox.Move(delta); + public uint WorldId { get; } + + public void MoveHitbox(Double3 delta) + { + Hitbox.Move(delta); + } public abstract void Render(); public abstract void Update(World world); - - public Vec3 Position; - public Vec3 Rotation; - public Vec3 Scale; - public Aabb Hitbox; - public uint WorldId { get; } } } \ No newline at end of file diff --git a/Game/World/Player.cs b/Game/World/Player.cs index 9cb6a3d..28a1805 100644 --- a/Game/World/Player.cs +++ b/Game/World/Player.cs @@ -1,7 +1,7 @@ // -// Game: Player.cs +// NEWorld/Game: Player.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,95 +16,110 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using Core.Math; using Core.Utilities; +using Xenko.Core.Mathematics; namespace Game.World { public class Player : PlayerObject { - private class PlayerUpdateTask : IReadOnlyTask - { - public PlayerUpdateTask(Player player, uint worldId) - { - _player = player; - _worldId = worldId; - } - - public void Task(ChunkService srv) => _player.Update(srv.Worlds.Get(_worldId)); + private static readonly bool RotationInteria = false; + private Double3 positionDelta; - IReadOnlyTask IReadOnlyTask.Clone() => (IReadOnlyTask) MemberwiseClone(); + private Double3 speed, rotationSpeed; - private readonly Player _player; - private readonly uint _worldId; + public Player(uint worldId) : base(worldId) + { + ChunkService.TaskDispatcher.AddRegular(new PlayerUpdateTask(this, WorldId)); } - public Player(uint worldId) : base(worldId) => - Singleton.Instance.TaskDispatcher.AddRegular(new PlayerUpdateTask(this, WorldId)); - - public void Accelerate(Vec3 acceleration) => _speed += acceleration; + public Double3 PositionDelta => positionDelta; - public void AccelerateRotation(Vec3 acceleration) => _rotationSpeed += acceleration; + public Double3 RotationDelta { get; private set; } - public void SetSpeed(Vec3 speed) => _speed = speed; + public void Accelerate(Double3 acceleration) + { + speed += acceleration; + } - public Vec3 PositionDelta => _positionDelta; + public void AccelerateRotation(Double3 acceleration) + { + rotationSpeed += acceleration; + } - public Vec3 RotationDelta { get; private set; } + public void SetSpeed(Double3 speed) + { + this.speed = speed; + } public override void Render() { } - private Vec3 _speed, _rotationSpeed; - private Vec3 _positionDelta; - public override void Update(World world) { Move(world); RotationMove(); - Accelerate(new Vec3(0.0, -0.1, 0.0)); // Gravity + Accelerate(new Double3(0.0, -0.1, 0.0)); // Gravity } private void Move(World world) { - _positionDelta = Mat4D.Rotation(Rotation.Y, new Vec3(0.0f, 1.0f, 0.0f)).Transform(_speed, 0.0).Key; - var originalDelta = _positionDelta; - var hitboxes = world.GetHitboxes(Hitbox.Expand(_positionDelta)); + positionDelta = Mat4D.Rotation(Rotation.Y, new Double3(0.0f, 1.0f, 0.0f)).Transform(speed, 0.0).Key; + var originalDelta = positionDelta; + var hitboxes = world.GetHitboxes(Hitbox.Expand(positionDelta)); foreach (var curr in hitboxes) - _positionDelta.X = Hitbox.MaxMoveOnXclip(curr, _positionDelta.X); - MoveHitbox(new Vec3(_positionDelta.X, 0.0, 0.0)); - if (_positionDelta.X != originalDelta.X) _speed.X = 0.0; + positionDelta.X = Hitbox.MaxMoveOnXclip(curr, positionDelta.X); + MoveHitbox(new Double3(positionDelta.X, 0.0, 0.0)); + if (positionDelta.X != originalDelta.X) speed.X = 0.0; foreach (var curr in hitboxes) - _positionDelta.Z = Hitbox.MaxMoveOnZclip(curr, _positionDelta.Z); - MoveHitbox(new Vec3(0.0, 0.0, _positionDelta.Z)); - if (_positionDelta.Z != originalDelta.Z) _speed.Z = 0.0; + positionDelta.Z = Hitbox.MaxMoveOnZclip(curr, positionDelta.Z); + MoveHitbox(new Double3(0.0, 0.0, positionDelta.Z)); + if (positionDelta.Z != originalDelta.Z) speed.Z = 0.0; foreach (var curr in hitboxes) - _positionDelta.Y = Hitbox.MaxMoveOnYclip(curr, _positionDelta.Y); - MoveHitbox(new Vec3(0.0, _positionDelta.Y, 0.0)); - if (_positionDelta.Y != originalDelta.Y) _speed.Y = 0.0; + positionDelta.Y = Hitbox.MaxMoveOnYclip(curr, positionDelta.Y); + MoveHitbox(new Double3(0.0, positionDelta.Y, 0.0)); + if (positionDelta.Y != originalDelta.Y) speed.Y = 0.0; - Position += _positionDelta; + Position += positionDelta; } - private static readonly bool RotationInteria = false; - private void RotationMove() { - if (Rotation.X + _rotationSpeed.X > 90.0) - _rotationSpeed.X = 90.0 - Rotation.X; - if (Rotation.X + _rotationSpeed.X < -90.0) - _rotationSpeed.X = -90.0 - Rotation.X; - Rotation += _rotationSpeed; - RotationDelta = _rotationSpeed; + if (Rotation.X + rotationSpeed.X > 90.0) + rotationSpeed.X = 90.0 - Rotation.X; + if (Rotation.X + rotationSpeed.X < -90.0) + rotationSpeed.X = -90.0 - Rotation.X; + Rotation += rotationSpeed; + RotationDelta = rotationSpeed; if (RotationInteria) - _rotationSpeed *= 0.6; + rotationSpeed *= 0.6; else - _rotationSpeed *= 0; + rotationSpeed *= 0; + } + + private class PlayerUpdateTask : IRegularReadOnlyTask + { + private readonly Player player; + private readonly uint worldId; + + public PlayerUpdateTask(Player player, uint worldId) + { + this.player = player; + this.worldId = worldId; + } + + public void Task(int instance, int count) + { + if (instance == count) + { + player.Update(ChunkService.Worlds.Get(worldId)); + } + } } } } \ No newline at end of file diff --git a/Game/World/PlayerObject.cs b/Game/World/PlayerObject.cs index 6d9f108..2713248 100644 --- a/Game/World/PlayerObject.cs +++ b/Game/World/PlayerObject.cs @@ -1,7 +1,7 @@ // -// Game: PlayerObject.cs +// NEWorld/Game: PlayerObject.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,39 +16,41 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System; -using Core.Math; using Game.Utilities; +using Xenko.Core.Mathematics; namespace Game.World { public class PlayerObject : Object { + private readonly double height; + private readonly double width; + private Double3 hitboxSize; + public PlayerObject(uint worldId) : - base(worldId, new Vec3(), new Vec3(), new Vec3(1.0, 1.0, 1.0), new Aabb()) + base(worldId, new Double3(), new Double3(), new Double3(1.0, 1.0, 1.0), new Aabb()) { - _height = 1.6; - _width = 0.6; + height = 1.6; + width = 0.6; Speed = 0.2; RefreshHitbox(); } - public void Rotate(Vec3 rotation) => Rotation += rotation; - // Body direction, head direction is `mRotation` in class Object - public Vec3 Direction { get; set; } + public Double3 Direction { get; set; } public double Speed { get; set; } - private readonly double _height; - private readonly double _width; - private Vec3 _hitboxSize; + public void Rotate(Double3 rotation) + { + Rotation += rotation; + } private void RefreshHitbox() { - _hitboxSize = new Vec3(_width, _height, _width); - Hitbox = new Aabb(-_hitboxSize / 2, _hitboxSize / 2); + hitboxSize = new Double3(width, height, width); + Hitbox = new Aabb(-hitboxSize / 2, hitboxSize / 2); } public override void Render() diff --git a/Game/World/StaticChunkPool.cs b/Game/World/StaticChunkPool.cs new file mode 100644 index 0000000..86bd254 --- /dev/null +++ b/Game/World/StaticChunkPool.cs @@ -0,0 +1,78 @@ +// +// NEWorld/Game: StaticChunkPool.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System.Collections.Generic; + +namespace Game.World +{ + public static class StaticChunkPool + { + private static uint _airChunkId = uint.MaxValue; + private static List _staticList = new List(); + private static Dictionary _id; + + static StaticChunkPool() + { + _id = new Dictionary(); + Register("Default.AirChunk", new Chunk(new BlockData(0))); + } + + internal static Dictionary Id + { + get => _id; + set + { + var oldId = value; + var old = _staticList; + _id = value; + _staticList = new List(_id.Count); + for (var i = 0; i < _id.Count; ++i) + _staticList.Add(null); + foreach (var record in value) + _staticList[(int) record.Value] = old[(int) oldId[record.Key]]; + } + } + + public static void Register(string name, Chunk staticChunk) + { + if (_id.TryGetValue(name, out var sid)) + if (_staticList[(int) sid] == null) + _staticList[(int) sid] = staticChunk; + + _id.Add(name, (uint) _staticList.Count); + _staticList.Add(staticChunk); + } + + public static uint GetAirChunk() + { + if (_airChunkId == uint.MaxValue) + _airChunkId = _id["Default.AirChunk"]; + return _airChunkId; + } + + public static Chunk GetChunk(uint id) + { + return _staticList[(int) id]; + } + + public static uint GetId(string name) + { + return _id[name]; + } + } +} \ No newline at end of file diff --git a/Game/World/World.cs b/Game/World/World.cs index eae95df..9bae4a9 100644 --- a/Game/World/World.cs +++ b/Game/World/World.cs @@ -1,7 +1,7 @@ // -// Game: World.cs +// NEWorld/Game: World.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,17 +16,27 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System; using System.Collections.Generic; using System.Linq; -using Core.Math; using Game.Utilities; +using Xenko.Core.Mathematics; namespace Game.World { public partial class World { + private static readonly Int3[] Delta = + { + new Int3(1, 0, 0), new Int3(-1, 0, 0), + new Int3(0, 1, 0), new Int3(0, -1, 0), + new Int3(0, 0, 1), new Int3(0, 0, -1) + }; + + private readonly Double3 hitboxOffset = new Double3(1.0, 1.0, 1.0); + + // All Chunks (Chunk array) + public World(string name) { Name = name; @@ -51,100 +61,85 @@ public World(string name) public ChunkManager Chunks { get; } // Alias declearations for Chunk management - public int GetChunkCount() => Chunks.Count; - - public Chunk GetChunk(ref Vec3 chunkPos) => Chunks[chunkPos]; - - public bool IsChunkLoaded(Vec3 chunkPos) => Chunks.IsLoaded(chunkPos); - - public void DeleteChunk(Vec3 chunkPos) => Chunks.Remove(chunkPos); - - public static int GetChunkAxisPos(int pos) => ChunkManager.GetAxisPos(pos); + public int GetChunkCount() + { + return Chunks.Count; + } - public static Vec3 GetChunkPos(Vec3 pos) => ChunkManager.GetPos(pos); + public Chunk GetChunk(Int3 chunkPos) + { + return Chunks[chunkPos]; + } - public static int GetBlockAxisPos(int pos) => ChunkManager.GetBlockAxisPos(pos); + public bool IsChunkLoaded(Int3 chunkPos) + { + return Chunks.IsLoaded(chunkPos); + } - public static Vec3 GetBlockPos(ref Vec3 pos) => ChunkManager.GetBlockPos(pos); + private void DeleteChunk(Int3 chunkPos) + { + Chunks.Remove(chunkPos); + } - public BlockData GetBlock(Vec3 pos) => Chunks.GetBlock(pos); + public static Int3 GetChunkPos(Int3 pos) + { + return ChunkManager.GetPos(pos); + } - public void SetBlock(ref Vec3 pos, BlockData block) => Chunks.SetBlock(pos, block); + public BlockData GetBlock(Int3 pos) + { + return Chunks.GetBlock(pos); + } - private Chunk InsertChunk(ref Vec3 pos, Chunk chunk) + public void SetBlock(ref Int3 pos, BlockData block) { - if (!Chunks.IsLoaded(pos)) - Chunks.Add(pos, chunk); - else - // TODO : FIX THIS GOD DAMNED ERROR, IT SHOULD NOT HAPPEN - Console.WriteLine($"Warning: Dumplicate Chunk Adding on [{pos.X},{pos.Y},{pos.Z}]"); - return Chunks[pos]; + Chunks.SetBlock(pos, block); } - private static readonly Vec3[] Delta = + private void InsertChunk(Chunk chunk) { - new Vec3(1, 0, 0), new Vec3(-1, 0, 0), - new Vec3(0, 1, 0), new Vec3(0, -1, 0), - new Vec3(0, 0, 1), new Vec3(0, 0, -1) - }; + Chunks.Add(chunk.Position, chunk); + } - public Chunk InsertChunkAndUpdate(Vec3 pos, Chunk chunk) + public Chunk InsertChunkAndUpdate(Chunk chunk) { - var ret = InsertChunk(ref pos, chunk); + InsertChunk(chunk); foreach (var dt in Delta) - if (Chunks.TryGetValue(pos + dt, out var target)) + if (Chunks.TryGetValue(chunk.Position + dt, out var target)) target.IsUpdated = true; - return ret; + return chunk; } - public Chunk ResetChunk(ref Vec3 pos, Chunk ptr) => Chunks[pos] = ptr; - - private readonly Vec3 _hitboxOffset = new Vec3(1.0, 1.0, 1.0); + private void ResetChunk(Chunk ptr) + { + Chunks[ptr.Position].MoveFrom(ptr); + } public List GetHitboxes(Aabb range) { var res = new List(); - Vec3 curr; + Int3 curr; for (curr.X = (int) Math.Floor(range.Min.X); curr.X < (int) Math.Ceiling(range.Max.X); curr.X++) + for (curr.Y = (int) Math.Floor(range.Min.Y); curr.Y < (int) Math.Ceiling(range.Max.Y); curr.Y++) + for (curr.Z = (int) Math.Floor(range.Min.Z); curr.Z < (int) Math.Ceiling(range.Max.Z); curr.Z++) { - for (curr.Y = (int) Math.Floor(range.Min.Y); curr.Y < (int) Math.Ceiling(range.Max.Y); curr.Y++) - { - for (curr.Z = (int) Math.Floor(range.Min.Z); curr.Z < (int) Math.Ceiling(range.Max.Z); curr.Z++) - { - // TODO: BlockType::getAABB - if (!IsChunkLoaded(GetChunkPos(curr))) - continue; - if (GetBlock(curr).Id == 0) - continue; - var currd = new Vec3(curr.X, curr.Y, curr.Z); - res.Add(new Aabb(currd, currd + _hitboxOffset)); - } - } + // TODO: BlockType::getAABB + if (!IsChunkLoaded(GetChunkPos(curr))) + continue; + if (GetBlock(curr).Id == 0) + continue; + var currd = new Double3(curr.X, curr.Y, curr.Z); + res.Add(new Aabb(currd, currd + hitboxOffset)); } return res; } - public void UpdateChunkLoadStatus() - { - lock (_mutex) - { - foreach (var kvPair in Chunks.ToList()) - if (kvPair.Value.CheckReleaseable()) - Chunks.Remove(kvPair.Key); - } - } - - protected static uint IdCount; - - // All Chunks (Chunk array) - private readonly object _mutex = new object(); - - private void ResetChunkAndUpdate(Vec3 pos, Chunk chunk) + private void ResetChunkAndUpdate(Chunk chunk) { - ResetChunk(ref pos, chunk); + ResetChunk(chunk); foreach (var dt in Delta) - if (Chunks.TryGetValue(pos + dt, out var target)) + if (Chunks.TryGetValue(chunk.Position + dt, out var target)) target.IsUpdated = true; } } diff --git a/Game/World/WorldFileStorage.cs b/Game/World/WorldFileStorage.cs new file mode 100644 index 0000000..e27e947 --- /dev/null +++ b/Game/World/WorldFileStorage.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading; +using MessagePack.Internal; +using Xenko.Core.Mathematics; + +namespace Game.World { + public partial class World + { + private FileStream _indexFileStream; + private FileStream _dataFileStream; + private Dictionary _index; + private Mutex _indexLock; + private Mutex _dataLock; + + private void EnsureFileStorageInitialized() + { + if (_indexFileStream == null) + { + _indexFileStream = new FileStream(Name + ".index", + FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); + if (_indexFileStream.Length != 0) + _index = new BinaryFormatter().Deserialize(_indexFileStream) as Dictionary; + else + _index = new Dictionary(); + } + if (_dataFileStream == null) + _dataFileStream = new FileStream(Name + ".data", + FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); + } + + private Int64 FindChunkOffsetInFile(Int3 position) + { + if(_index.TryGetValue(position, out var ret)) + { + return ret; + } + + lock (_indexLock) + { + var offset = _index.Count; + _index[position] = offset; + new BinaryFormatter().Serialize(_indexFileStream, _index); + + return offset; + } + } + + public void SaveChunkToDisk(Chunk chunk) + { + EnsureFileStorageInitialized(); + + var buffer = new byte[32768 * 4]; + chunk.SerializeTo(buffer); + lock (_dataLock) { + _dataFileStream.Seek(FindChunkOffsetInFile(chunk.Position), SeekOrigin.Begin); + _dataFileStream.Write(buffer, 0, buffer.Length); + } + } + + public bool ChunkExistsInDisk(Chunk chunk) + { + lock (_indexLock) { + return _index.ContainsKey(chunk.Position); + } + } + + public bool LoadChunkFromDisk(ref Chunk chunk) + { + EnsureFileStorageInitialized(); + + var buffer = new byte[32768 * 4]; + lock (_dataLock) + { + _dataFileStream.Seek(FindChunkOffsetInFile(chunk.Position), SeekOrigin.Begin); + _dataFileStream.Read(buffer, 0, buffer.Length); + } + chunk.DeserializeFrom(buffer); + return true; + } + } +} diff --git a/Game/World/WorldTasks.cs b/Game/World/WorldTasks.cs index de96631..70104d6 100644 --- a/Game/World/WorldTasks.cs +++ b/Game/World/WorldTasks.cs @@ -1,7 +1,7 @@ // -// Game: WorldTasks.cs +// NEWorld/Game: WorldTasks.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,10 +16,10 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - -using Core.Math; -using Game.Network; +using System; +using System.Threading; using Game.Utilities; +using Xenko.Core.Mathematics; namespace Game.World { @@ -27,188 +27,186 @@ public partial class World { private const int MaxChunkLoadCount = 64, MaxChunkUnloadCount = 64; - private static readonly Vec3 MiddleOffset = - new Vec3(Chunk.Size / 2 - 1, Chunk.Size / 2 - 1, Chunk.Size / 2 - 1); + private static readonly Int3 MiddleOffset = + new Int3(Chunk.RowSize / 2 - 1, Chunk.RowSize / 2 - 1, Chunk.RowSize / 2 - 1); + + public void RegisterChunkTasks(Player player) + { + ChunkService.TaskDispatcher.AddRegular(new LoadUnloadDetectorTask(this, player)); + } public class ResetChunkTask : IReadWriteTask { + private readonly Chunk chunk; + /** - * \brief Add a constructed chunk into world. - * \param worldID the target world's id + * \brief AddReadOnlyTask a constructed chunk into world. * \param chunk the target chunk */ - public ResetChunkTask(uint worldId, Chunk chunk) + public ResetChunkTask(Chunk chunk) { - _worldId = worldId; - _chunk = chunk; + this.chunk = chunk; } - public IReadWriteTask Clone() => (IReadWriteTask) MemberwiseClone(); - - public void Task(ChunkService srv) + public void Task() { - var world = srv.Worlds.Get(_worldId); - world.ResetChunkAndUpdate(_chunk.Position, _chunk); + chunk.World.ResetChunkAndUpdate(chunk); } - - private readonly uint _worldId; - private readonly Chunk _chunk; } - private class UnloadChunkTask : IReadWriteTask + private class LoadTask : IReadWriteTask { - /** - * \brief Given a chunk, it will try to unload it (decrease a ref) - * \param world the target world - * \param chunkPosition the position of the chunk - */ - public UnloadChunkTask(World world, Vec3 chunkPosition) + private Chunk chunk; + private bool entryAdded; + + internal LoadTask(World world, Int3 chunkPosition) { - _world = world; - _chunkPosition = chunkPosition; + // Adding Sentry + chunk = new Chunk(chunkPosition, world, Chunk.InitOption.None); + if (!ChunkService.IsAuthority) Network.Client.GetChunk.Call(world.Id, chunkPosition); + LocalLoad(world, chunkPosition); } - public IReadWriteTask Clone() => (IReadWriteTask) MemberwiseClone(); - - public void Task(ChunkService srv) + public void Task() { - //TODO: for multiplayer situation, it should decrease ref counter instead of deleting - _world.DeleteChunk(_chunkPosition); + var operated = Interlocked.Exchange(ref chunk, null); + if (entryAdded) + operated.World.ResetChunkAndUpdate(operated); + else + { + entryAdded = true; + operated.World.InsertChunkAndUpdate(operated); + } } - private readonly World _world; - private readonly Vec3 _chunkPosition; - } + private void Reset(Chunk chk) + { + if (Interlocked.Exchange(ref chunk, chk) == null) + ChunkService.TaskDispatcher.Add(this); + } - private class BuildOrLoadChunkTask : IReadOnlyTask - { - private class AddToWorldTask : IReadWriteTask + private async void LocalLoad(World world, Int3 position) { - /** - * \brief Add a constructed chunk into world. - * \param worldID the target world's id - * \param chunk the target chunk - */ - public AddToWorldTask(uint worldId, Chunk chunk) + await ChunkService.TaskDispatcher.NextReadOnlyChance(); + Chunk chk; + if (world.ChunkExistsInDisk(chunk)) { - _worldId = worldId; - _chunk = chunk; + chk = new Chunk(position, world, Chunk.InitOption.None); + world.LoadChunkFromDisk(ref chk); } - - public IReadWriteTask Clone() => (IReadWriteTask) MemberwiseClone(); - - public void Task(ChunkService srv) + else { - var world = srv.Worlds.Get(_worldId); - world.InsertChunkAndUpdate(_chunk.Position, _chunk); + chk = new Chunk(position, world); } - - private readonly uint _worldId; - private readonly Chunk _chunk; + Reset(chk); } + } + + private class UnloadChunkTask : IReadWriteTask + { + private readonly Chunk chunk; /** - * \brief Given a chunk, it will try to load it or build it - * \param world the target world - * \param chunkPosition the position of the chunk - */ - public BuildOrLoadChunkTask(World world, Vec3 chunkPosition) + * \brief Given a chunk, it will try to unload it (decrease a ref) + * \param world the target world + * \param chunkPosition the position of the chunk + */ + public UnloadChunkTask(Chunk chk) { - _world = world; - _chunkPosition = chunkPosition; + chunk = chk; } - public IReadOnlyTask Clone() => (IReadOnlyTask) MemberwiseClone(); - - public void Task(ChunkService srv) + public void Task() { - // TODO: should try to load from local first - var chunk = new Chunk(_chunkPosition, _world); - srv.TaskDispatcher.Add(new AddToWorldTask(_world.Id, chunk)); + //TODO: for multiplayer situation, it should decrease ref counter instead of deleting + chunk.World.SaveChunkToDisk(chunk); + chunk.World.DeleteChunk(chunk.Position); + chunk.Dispose(); } - - private readonly World _world; - private readonly Vec3 _chunkPosition; } - private class LoadUnloadDetectorTask : IReadOnlyTask + private class LoadUnloadDetectorTask : IRegularReadOnlyTask { - public LoadUnloadDetectorTask(World world, Player player) - { - _player = player; - _world = world; - } + private readonly Player player; + private readonly World world; - public void Task(ChunkService cs) + private class Instance { - var loadList = new OrderedListIntLess>(MaxChunkLoadCount); - var unloadList = new OrderedListIntGreater(MaxChunkUnloadCount); - var playerPos = _player.Position; - var position = new Vec3((int) playerPos.X, (int) playerPos.Y, (int) playerPos.Z); - GenerateLoadUnloadList(_world, position, 4, loadList, unloadList); + private readonly Int3 centerPos; + private readonly World world; + private readonly int instance, instances; + private readonly OrderedListIntLess loadList = new OrderedListIntLess(MaxChunkLoadCount); + private readonly OrderedListIntGreater unloadList = new OrderedListIntGreater(MaxChunkUnloadCount); - foreach (var loadPos in loadList) + internal Instance(World wrd, Player player, int ins, int inst) { - cs.TaskDispatcher.Add(new BuildOrLoadChunkTask(_world, loadPos.Value)); - if (!cs.IsAuthority) - { - Client.GetChunk.Call(_world.Id, loadPos.Value); - } + world = wrd; + instance = ins; + instances = inst; + var playerPos = player.Position; + centerPos = new Int3((int) playerPos.X, (int) playerPos.Y, (int) playerPos.Z); } - foreach (var unloadChunk in unloadList) + internal void Run() { - // add a unload task. - cs.TaskDispatcher.Add(new UnloadChunkTask(_world, unloadChunk.Value.Position)); - } - } + GenerateLoadUnloadList(4); - /** - * \brief Find the nearest chunks in load range to load, - * fartherest chunks out of load range to unload. - * \param world the world to load or unload chunks - * \param centerPos the center position - * \param loadRange chunk load range - * \param loadList (Output) Chunk load list [position, distance] - * \param unloadList (Output) Chunk unload list [pointer, distance] - */ - private static void GenerateLoadUnloadList(World world, Vec3 centerPos, int loadRange, - OrderedListIntLess> loadList, OrderedListIntGreater unloadList) - { - // centerPos to chunk coords - var centerCPos = GetChunkPos(centerPos); + foreach (var loadPos in loadList) + ChunkService.TaskDispatcher.Add(new LoadTask(world, loadPos.Value)); - foreach (var chunk in world.Chunks) - { - var curPos = chunk.Value.Position; - // Out of load range, pending to unload - if (centerCPos.ChebyshevDistance(curPos) > loadRange) - unloadList.Insert((curPos * Chunk.Size + MiddleOffset - centerPos).LengthSqr(), chunk.Value); + foreach (var unloadChunk in unloadList) + // add a unload task. + ChunkService.TaskDispatcher.Add(new UnloadChunkTask(unloadChunk.Value)); } - for (var x = centerCPos.X - loadRange; x <= centerCPos.X + loadRange; x++) + private void GenerateLoadUnloadList(int loadRange) { - for (var y = centerCPos.Y - loadRange; y <= centerCPos.Y + loadRange; y++) + var centerCPos = GetChunkPos(centerPos); + + // TODO: Instance this + if (instance == 0 ) { - for (var z = centerCPos.Z - loadRange; z <= centerCPos.Z + loadRange; z++) + foreach (var chunk in world.Chunks) { - var position = new Vec3(x, y, z); - // In load range, pending to load - if (!world.IsChunkLoaded(position)) - loadList.Insert((position * Chunk.Size + MiddleOffset - centerPos).LengthSqr(), - position); + var curPos = chunk.Value.Position; + // Out of load range, pending to unload + if (ChebyshevDistance(centerCPos, curPos) > loadRange) + unloadList.Insert((curPos * Chunk.RowSize + MiddleOffset - centerPos).LengthSquared(), + chunk.Value); } } + + var edge1 = loadRange * 2 + 1; + var edge2 = edge1 * edge1; + var edge3 = edge2 * edge1; + var corner = new Int3(centerCPos.X - loadRange, centerCPos.Y - loadRange, centerCPos.Z - loadRange); + for (var i = instance; i < edge3; i += instances) + { + var position = corner + new Int3(i / edge2, (i % edge2) / edge1, i % edge1); + // In load range, pending to load + if (!world.IsChunkLoaded(position)) + loadList.Insert((position * Chunk.RowSize + MiddleOffset - centerPos).LengthSquared(), + position); + } } } - public IReadOnlyTask Clone() => (IReadOnlyTask) MemberwiseClone(); + public LoadUnloadDetectorTask(World world, Player player) + { + this.player = player; + this.world = world; + } - private readonly Player _player; - private readonly World _world; + public void Task(int instance, int instances) + { + new Instance(world, player, instance, instances).Run(); + } + + // TODO: Remove Type1 Clone + private static int ChebyshevDistance(Int3 l, Int3 r) + { + return Math.Max(Math.Max(Math.Abs(l.X - r.X), Math.Abs(l.Y - r.Y)), Math.Abs(l.Z - r.Z)); + } } - - public void RegisterChunkTasks(ChunkService cs, Player player) => - cs.TaskDispatcher.AddRegular(new LoadUnloadDetectorTask(this, player)); } } \ No newline at end of file diff --git a/Game/packages.config b/Game/packages.config deleted file mode 100644 index 712ef9f..0000000 --- a/Game/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Main/Assets/Textures/Blocks/Bedrock.png b/Main/Assets/Textures/Blocks/Bedrock.png new file mode 100644 index 0000000..c450d8a Binary files /dev/null and b/Main/Assets/Textures/Blocks/Bedrock.png differ diff --git a/Main/Assets/Textures/Blocks/Bedrock.xktex b/Main/Assets/Textures/Blocks/Bedrock.xktex new file mode 100644 index 0000000..974abd0 --- /dev/null +++ b/Main/Assets/Textures/Blocks/Bedrock.xktex @@ -0,0 +1,10 @@ +!Texture +Id: 68b96536-d657-46f9-8319-173aa4f4af30 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Bedrock.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Cement.png b/Main/Assets/Textures/Blocks/Cement.png new file mode 100644 index 0000000..73e8ba1 Binary files /dev/null and b/Main/Assets/Textures/Blocks/Cement.png differ diff --git a/Main/Assets/Textures/Blocks/Cement.xktex b/Main/Assets/Textures/Blocks/Cement.xktex new file mode 100644 index 0000000..dd8bbc1 --- /dev/null +++ b/Main/Assets/Textures/Blocks/Cement.xktex @@ -0,0 +1,10 @@ +!Texture +Id: 729c1023-0d45-456b-b5af-9c2c9ef8b87a +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Cement.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Coal.png b/Main/Assets/Textures/Blocks/Coal.png new file mode 100644 index 0000000..0f900b7 Binary files /dev/null and b/Main/Assets/Textures/Blocks/Coal.png differ diff --git a/Main/Assets/Textures/Blocks/Coal.xktex b/Main/Assets/Textures/Blocks/Coal.xktex new file mode 100644 index 0000000..a160a82 --- /dev/null +++ b/Main/Assets/Textures/Blocks/Coal.xktex @@ -0,0 +1,10 @@ +!Texture +Id: def5376c-d58d-4872-8edc-bfd10cc66ba6 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Coal.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Dirt.png b/Main/Assets/Textures/Blocks/Dirt.png new file mode 100644 index 0000000..a575661 Binary files /dev/null and b/Main/Assets/Textures/Blocks/Dirt.png differ diff --git a/Main/Assets/Textures/Blocks/Dirt.xktex b/Main/Assets/Textures/Blocks/Dirt.xktex new file mode 100644 index 0000000..194d4d4 --- /dev/null +++ b/Main/Assets/Textures/Blocks/Dirt.xktex @@ -0,0 +1,10 @@ +!Texture +Id: 872b5b39-8830-41cd-99a3-b1f729f88966 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Dirt.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Glowstone.png b/Main/Assets/Textures/Blocks/Glowstone.png new file mode 100644 index 0000000..b0d7548 Binary files /dev/null and b/Main/Assets/Textures/Blocks/Glowstone.png differ diff --git a/Main/Assets/Textures/Blocks/Glowstone.xktex b/Main/Assets/Textures/Blocks/Glowstone.xktex new file mode 100644 index 0000000..c6f9d9f --- /dev/null +++ b/Main/Assets/Textures/Blocks/Glowstone.xktex @@ -0,0 +1,10 @@ +!Texture +Id: 6cff36b6-77c2-47de-b5d1-18465cb77201 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Glowstone.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/GrassRound.png b/Main/Assets/Textures/Blocks/GrassRound.png new file mode 100644 index 0000000..37530f0 Binary files /dev/null and b/Main/Assets/Textures/Blocks/GrassRound.png differ diff --git a/Main/Assets/Textures/Blocks/GrassRound.xktex b/Main/Assets/Textures/Blocks/GrassRound.xktex new file mode 100644 index 0000000..791ceca --- /dev/null +++ b/Main/Assets/Textures/Blocks/GrassRound.xktex @@ -0,0 +1,10 @@ +!Texture +Id: c896ecda-99c3-4815-b811-6dcf125c371c +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file GrassRound.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/GrassTop.png b/Main/Assets/Textures/Blocks/GrassTop.png new file mode 100644 index 0000000..611b225 Binary files /dev/null and b/Main/Assets/Textures/Blocks/GrassTop.png differ diff --git a/Main/Assets/Textures/Blocks/GrassTop.xktex b/Main/Assets/Textures/Blocks/GrassTop.xktex new file mode 100644 index 0000000..726a131 --- /dev/null +++ b/Main/Assets/Textures/Blocks/GrassTop.xktex @@ -0,0 +1,10 @@ +!Texture +Id: 8950f4b3-c0bf-4ba1-85a7-d008f78b8b99 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file GrassTop.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Iron.png b/Main/Assets/Textures/Blocks/Iron.png new file mode 100644 index 0000000..5a7b31d Binary files /dev/null and b/Main/Assets/Textures/Blocks/Iron.png differ diff --git a/Main/Assets/Textures/Blocks/Iron.xktex b/Main/Assets/Textures/Blocks/Iron.xktex new file mode 100644 index 0000000..cdc066e --- /dev/null +++ b/Main/Assets/Textures/Blocks/Iron.xktex @@ -0,0 +1,10 @@ +!Texture +Id: 0c1e6502-9fe1-4fa6-b1c2-8e67f76f1f4f +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Iron.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Null.png b/Main/Assets/Textures/Blocks/Null.png new file mode 100644 index 0000000..20a8302 Binary files /dev/null and b/Main/Assets/Textures/Blocks/Null.png differ diff --git a/Main/Assets/Textures/Blocks/Null.xktex b/Main/Assets/Textures/Blocks/Null.xktex new file mode 100644 index 0000000..59720d7 --- /dev/null +++ b/Main/Assets/Textures/Blocks/Null.xktex @@ -0,0 +1,10 @@ +!Texture +Id: 758069be-3d4f-4f11-a44c-8b5bbea08cbb +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Null.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Plank.png b/Main/Assets/Textures/Blocks/Plank.png new file mode 100644 index 0000000..5e3aab2 Binary files /dev/null and b/Main/Assets/Textures/Blocks/Plank.png differ diff --git a/Main/Assets/Textures/Blocks/Plank.xktex b/Main/Assets/Textures/Blocks/Plank.xktex new file mode 100644 index 0000000..7ff1f80 --- /dev/null +++ b/Main/Assets/Textures/Blocks/Plank.xktex @@ -0,0 +1,10 @@ +!Texture +Id: 6cde71ed-6d9a-4642-a312-d58c02dbb07d +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Plank.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Rock.png b/Main/Assets/Textures/Blocks/Rock.png new file mode 100644 index 0000000..980f5af Binary files /dev/null and b/Main/Assets/Textures/Blocks/Rock.png differ diff --git a/Main/Assets/Textures/Blocks/Rock.xktex b/Main/Assets/Textures/Blocks/Rock.xktex new file mode 100644 index 0000000..f6e7d48 --- /dev/null +++ b/Main/Assets/Textures/Blocks/Rock.xktex @@ -0,0 +1,10 @@ +!Texture +Id: 266e2127-34ad-4978-ae76-a37dbebf3547 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Rock.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Sand.png b/Main/Assets/Textures/Blocks/Sand.png new file mode 100644 index 0000000..f6acfdc Binary files /dev/null and b/Main/Assets/Textures/Blocks/Sand.png differ diff --git a/Main/Assets/Textures/Blocks/Sand.xktex b/Main/Assets/Textures/Blocks/Sand.xktex new file mode 100644 index 0000000..642a2bb --- /dev/null +++ b/Main/Assets/Textures/Blocks/Sand.xktex @@ -0,0 +1,10 @@ +!Texture +Id: f432c34d-8800-43a5-8493-ed3507c53aa9 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Sand.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Stone.png b/Main/Assets/Textures/Blocks/Stone.png new file mode 100644 index 0000000..8f24ef1 Binary files /dev/null and b/Main/Assets/Textures/Blocks/Stone.png differ diff --git a/Main/Assets/Textures/Blocks/Stone.xktex b/Main/Assets/Textures/Blocks/Stone.xktex new file mode 100644 index 0000000..869091c --- /dev/null +++ b/Main/Assets/Textures/Blocks/Stone.xktex @@ -0,0 +1,10 @@ +!Texture +Id: f35d797b-97bd-4f89-823e-7ce0254f8fd5 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Stone.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/TrunkRound.png b/Main/Assets/Textures/Blocks/TrunkRound.png new file mode 100644 index 0000000..1361aba Binary files /dev/null and b/Main/Assets/Textures/Blocks/TrunkRound.png differ diff --git a/Main/Assets/Textures/Blocks/TrunkRound.xktex b/Main/Assets/Textures/Blocks/TrunkRound.xktex new file mode 100644 index 0000000..cbb544e --- /dev/null +++ b/Main/Assets/Textures/Blocks/TrunkRound.xktex @@ -0,0 +1,10 @@ +!Texture +Id: bc3db60e-4472-4e7f-b1e8-626b4a0cc336 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file TrunkRound.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/TrunkTop.png b/Main/Assets/Textures/Blocks/TrunkTop.png new file mode 100644 index 0000000..588567e Binary files /dev/null and b/Main/Assets/Textures/Blocks/TrunkTop.png differ diff --git a/Main/Assets/Textures/Blocks/TrunkTop.xktex b/Main/Assets/Textures/Blocks/TrunkTop.xktex new file mode 100644 index 0000000..8c18787 --- /dev/null +++ b/Main/Assets/Textures/Blocks/TrunkTop.xktex @@ -0,0 +1,10 @@ +!Texture +Id: f6251b49-efcd-451d-aeb9-f422b2c5cd90 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file TrunkTop.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Assets/Textures/Blocks/Water.png b/Main/Assets/Textures/Blocks/Water.png new file mode 100644 index 0000000..b6c452d Binary files /dev/null and b/Main/Assets/Textures/Blocks/Water.png differ diff --git a/Main/Assets/Textures/Blocks/Water.xktex b/Main/Assets/Textures/Blocks/Water.xktex new file mode 100644 index 0000000..28f35ac --- /dev/null +++ b/Main/Assets/Textures/Blocks/Water.xktex @@ -0,0 +1,10 @@ +!Texture +Id: 3091e23d-b2af-4698-b2c8-b16af90eaa2c +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file Water.png +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} +IsStreamable: false diff --git a/Main/Main.cs b/Main/Main.cs new file mode 100644 index 0000000..7912cbc --- /dev/null +++ b/Main/Main.cs @@ -0,0 +1,214 @@ +// +// NEWorld/Main: Main.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using Core; +using Game; +using Game.Terrain; +using Game.World; + +namespace Main +{ + [DeclareModule] + public class Main : IModule + { + private static ushort _grassId, _rockId, _dirtId, _sandId, _waterId; + + private static uint _rockChunkId, _waterChunkId; + + public void CoInitialize() + { + _grassId = Blocks.Register(new BlockType("Grass", true, false, true, 2)); + _rockId = Blocks.Register(new BlockType("Rock", true, false, true, 2)); + _dirtId = Blocks.Register(new BlockType("Dirt", true, false, true, 2)); + _sandId = Blocks.Register(new BlockType("Sand", true, false, true, 2)); + _waterId = Blocks.Register(new BlockType("Water", false, true, false, 2)); + Chunk.SetGenerator(WorldGen.Generator); + StaticChunkPool.Register("Main.RockChunk", new Chunk(new BlockData(_rockId))); + StaticChunkPool.Register("Main.WaterChunk", new Chunk(new BlockData(_waterId))); + EventBus.AddCollection(this); + } + + [DeclareBusEventHandler] + public void GameRenderInit(object sender, GameRenderPrepareEvent load) + { + RendererInit(); + } + + [DeclareBusEventHandler] + public void GameLoads(object sender, GameLoadEvent load) + { + _rockChunkId = StaticChunkPool.GetId("Main.RockChunk"); + _waterChunkId = StaticChunkPool.GetId("Main.WaterChunk"); + } + + [DeclareBusEventHandler] + public void GameUnloads(object sender, GameUnloadEvent load) + { + } + + [DeclareBusEventHandler] + public void GameRenderFinalize(object sender, GameRenderFinalizeEvent load) + { + } + + public void CoFinalize() + { + } + + public void OnMemoryWarning() + { + } + + private static void RendererInit() + { + if (!Services.TryGet("BlockTextures", out var textures)) return; + uint[] id = + { + textures.Add("Textures/Blocks/GrassTop"), + textures.Add("Textures/Blocks/GrassRound"), + textures.Add("Textures/Blocks/Dirt"), + textures.Add("Textures/Blocks/Rock"), + textures.Add("Textures/Blocks/Sand"), + textures.Add("Textures/Blocks/Water") + }; + + var grass = new DefaultBlockRenderer(new[] {id[1], id[1], id[0], id[2], id[1], id[1]}); + var rock = new DefaultBlockRenderer(new[] {id[3], id[3], id[3], id[3], id[3], id[3]}); + var dirt = new DefaultBlockRenderer(new[] {id[2], id[2], id[2], id[2], id[2], id[2]}); + var sand = new DefaultBlockRenderer(new[] {id[4], id[4], id[4], id[4], id[4], id[4]}); + var water = new DefaultBlockRenderer(new[] {id[5], id[5], id[5], id[5], id[5], id[5]}); + + BlockRenderers.Add(_grassId, grass); + BlockRenderers.Add(_rockId, rock); + BlockRenderers.Add(_dirtId, dirt); + BlockRenderers.Add(_sandId, sand); + BlockRenderers.Add(_waterId, water); + } + + private static class WorldGen + { + private const double NoiseScaleX = 64.0; + private const double NoiseScaleZ = 64.0; + private static int Seed { get; } = 1025; + + private static double Noise(int x, int y) + { + var xx = x * 107 + y * 13258953287; + xx = (xx >> 13) ^ xx; + return ((xx * (xx * xx * 15731 + 789221) + 1376312589) & 0x7fffffff) / 16777216.0; + } + + private static double InterpolatedNoise(double x, double y) + { + var intX = (int)System.Math.Floor(x); + var fractionalX = x - intX; + var intY = (int)System.Math.Floor(y); + var fractionalY = y - intY; + var v1 = Noise(intX, intY); + var v2 = Noise(intX + 1, intY); + var v3 = Noise(intX, intY + 1); + var v4 = Noise(intX + 1, intY + 1); + var i1 = v1 * (1.0 - fractionalX) + v2 * fractionalX; + var i2 = v3 * (1.0 - fractionalX) + v4 * fractionalX; + return i1 * (1.0 - fractionalY) + i2 * fractionalY; + } + + private static double PerlinNoise2D(double x, double y) + { + double total = 0, frequency = 1, amplitude = 1; + for (var i = 0; i <= 4; i++) + { + total += InterpolatedNoise(x * frequency, y * frequency) * amplitude; + frequency *= 2; + amplitude /= 2.0; + } + + return total; + } + + public static unsafe void Generator(ChunkGeneratorContext context) + { + var pos = context.Current.Position; + var heights = new int[Chunk.RowSize, Chunk.RowSize]; + var low = int.MaxValue; + var high = int.MinValue; + { + for (var x = 0; x < Chunk.RowSize; x++) + for (var z = 0; z < Chunk.RowSize; z++) + { + var val = heights[x, z] = (int) PerlinNoise2D((pos.X * Chunk.RowSize + x) / NoiseScaleX, + (pos.Z * Chunk.RowSize + z) / NoiseScaleZ) / 2 - 64; + if (val < low) low = val; + if (val > high) high = val; + } + + if (pos.Y * Chunk.RowSize > high && high >= 0) + { + context.EnableCopyOnWrite(StaticChunkPool.GetAirChunk()); + return; + } + + if ((0-Chunk.RowSize) >= pos.Y * Chunk.RowSize && pos.Y * Chunk.RowSize > high) + { + context.EnableCopyOnWrite(StaticChunkPool.GetAirChunk()); + return; + } + + if (pos.Y * Chunk.RowSize < (low - Chunk.RowSize - 3)) + { + context.EnableCopyOnWrite(_rockChunkId); + return; + } + } + { + context.EnableFullArray(); + var blocks = context.Current.Blocks; + for (var x = 0; x < Chunk.RowSize; x++) + for (var z = 0; z < Chunk.RowSize; z++) + { + var absHeight = heights[x, z]; + var height = absHeight - pos.Y * Chunk.RowSize; + var underWater = absHeight <= 0; + for (var y = 0; y < Chunk.RowSize; y++) + { + ref var block = ref blocks[x * Chunk.RowSize * Chunk.RowSize + y * Chunk.RowSize + z]; + if (y <= height) + { + if (y == height) + block.Id = underWater ? _sandId : _grassId; + else if (y >= height - 3) + block.Id = underWater ? _sandId : _dirtId; + else + block.Id = _rockId; + + block.Brightness = 0; + block.Data = 0; + } + else + { + block.Id = pos.Y * Chunk.RowSize + y <= 0 ? _waterId : (ushort) 0; + block.Brightness = (byte) context.DaylightBrightness; + block.Data = 0; + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Main/Main.csproj b/Main/Main.csproj new file mode 100644 index 0000000..a8dcd41 --- /dev/null +++ b/Main/Main.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + true + + + + + + + diff --git a/Main/Main.xkpkg b/Main/Main.xkpkg new file mode 100644 index 0000000..ccdb5b2 --- /dev/null +++ b/Main/Main.xkpkg @@ -0,0 +1,17 @@ +!Package +SerializedVersion: {Assets: 3.1.0.0} +Meta: + Name: Main + Version: 1.0.0 + Authors: [] + Owners: [] + Dependencies: null +AssetFolders: + - Path: !dir Assets +ResourceFolders: + - !dir Resources +OutputGroupDirectories: {} +ExplicitFolders: [] +Bundles: [] +TemplateFolders: [] +RootAssets: [] diff --git a/Main/Umbrella.cs b/Main/Umbrella.cs new file mode 100644 index 0000000..5cb3804 --- /dev/null +++ b/Main/Umbrella.cs @@ -0,0 +1,20 @@ +// +// NEWorld/Main: Umbrella.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// + +[assembly:Core.DeclareNeWorldAssembly] diff --git a/Modules/Main/Main.cs b/Modules/Main/Main.cs deleted file mode 100644 index d8be8ce..0000000 --- a/Modules/Main/Main.cs +++ /dev/null @@ -1,161 +0,0 @@ -// -// Main: Main.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using Core; -using Core.Math; -using Core.Module; -using Game.Terrain; -using Game.World; - -namespace Main -{ - [DeclareModule] - public class Main : IModule - { - private static ushort _grassId, _rockId, _dirtId, _sandId, _waterId; - - private static class WorldGen - { - private static int Seed { get; } = 1025; - private const double NoiseScaleX = 64.0; - private const double NoiseScaleZ = 64.0; - - private static double Noise(int x, int y) - { - var xx = x * 107 + y * 13258953287; - xx = xx >> 13 ^ xx; - return (xx * (xx * xx * 15731 + 789221) + 1376312589 & 0x7fffffff) / 16777216.0; - } - - private static double InterpolatedNoise(double x, double y) - { - var intX = (int) x; - var fractionalX = x - intX; - var intY = (int) y; - var fractionalY = y - intY; - var v1 = Noise(intX, intY); - var v2 = Noise(intX + 1, intY); - var v3 = Noise(intX, intY + 1); - var v4 = Noise(intX + 1, intY + 1); - var i1 = v1 * (1.0 - fractionalX) + v2 * fractionalX; - var i2 = v3 * (1.0 - fractionalX) + v4 * fractionalX; - return i1 * (1.0 - fractionalY) + i2 * fractionalY; - } - - private static double PerlinNoise2D(double x, double y) - { - double total = 0, frequency = 1, amplitude = 1; - for (var i = 0; i <= 4; i++) - { - total += InterpolatedNoise(x * frequency, y * frequency) * amplitude; - frequency *= 2; - amplitude /= 2.0; - } - - return total; - } - - public static void Generator(Vec3 pos, BlockData[] blocks, int daylightBrightness) - { - for (var x = 0; x < Chunk.Size; x++) - for (var z = 0; z < Chunk.Size; z++) - { - var absHeight = (int) PerlinNoise2D((pos.X * Chunk.Size + x) / NoiseScaleX, - (pos.Z * Chunk.Size + z) / NoiseScaleZ) / 2 - 64; - var height = absHeight - pos.Y * Chunk.Size; - var underWater = absHeight <= 0; - for (var y = 0; y < Chunk.Size; y++) - { - ref var block = ref blocks[x * Chunk.Size * Chunk.Size + y * Chunk.Size + z]; - if (y <= height) - { - if (y == height) - { - block.Id = underWater ? _sandId : _grassId; - } - else if (y >= height - 3) - { - block.Id = underWater ? _sandId : _dirtId; - } - else - { - block.Id = _rockId; - } - - block.Brightness = 0; - block.Data = 0; - } - else - { - block.Id = pos.Y * Chunk.Size + y <= 0 ? _waterId : (ushort) 0; - block.Brightness = (byte) daylightBrightness; - block.Data = 0; - } - } - } - } - } - - public void CoInitialize() - { - _grassId = Blocks.Register(new BlockType("Grass", true, false, true, 2)); - _rockId = Blocks.Register(new BlockType("Rock", true, false, true, 2)); - _dirtId = Blocks.Register(new BlockType("Dirt", true, false, true, 2)); - _sandId = Blocks.Register(new BlockType("Sand", true, false, true, 2)); - _waterId = Blocks.Register(new BlockType("Water", false, true, false, 2)); - Chunk.SetGenerator(WorldGen.Generator); - RendererInit(); - } - - public void CoFinalize() - { - } - - public void OnMemoryWarning() - { - } - - private static void RendererInit() - { - if (!Services.TryGet("BlockTextures", out var textures)) return; - var path = Path.Asset("Infinideas.Main") + "blocks/"; - uint[] id = - { - textures.Add(path + "grass_top.png"), - textures.Add(path + "grass_round.png"), - textures.Add(path + "dirt.png"), - textures.Add(path + "rock.png"), - textures.Add(path + "sand.png"), - textures.Add(path + "water.png") - }; - - var grass = new DefaultBlockRenderer(new[] {id[1], id[1], id[0], id[2], id[1], id[1]}); - var rock = new DefaultBlockRenderer(new[] {id[3], id[3], id[3], id[3], id[3], id[3]}); - var dirt = new DefaultBlockRenderer(new[] {id[2], id[2], id[2], id[2], id[2], id[2]}); - var sand = new DefaultBlockRenderer(new[] {id[4], id[4], id[4], id[4], id[4], id[4]}); - var water = new DefaultBlockRenderer(new[] {id[5], id[5], id[5], id[5], id[5], id[5]}); - - BlockRenderers.Add(_grassId, grass); - BlockRenderers.Add(_rockId, rock); - BlockRenderers.Add(_dirtId, dirt); - BlockRenderers.Add(_sandId, sand); - BlockRenderers.Add(_waterId, water); - } - } -} \ No newline at end of file diff --git a/Modules/Main/Main.csproj b/Modules/Main/Main.csproj deleted file mode 100644 index baea11a..0000000 --- a/Modules/Main/Main.csproj +++ /dev/null @@ -1,59 +0,0 @@ - - - - - Debug - AnyCPU - {645C6E67-C0BB-4885-914B-50679E3382FC} - Library - Properties - Main - Main - v4.7.1 - 512 - - - AnyCPU - true - full - false - ..\..\bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - ..\..\bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - {ecb0e309-625f-4a24-926d-d1d23c1b7693} - Core - - - {f83c4945-abff-4856-94a3-d0d31c976a11} - Game - - - - - \ No newline at end of file diff --git a/Modules/Main/Properties/AssemblyInfo.cs b/Modules/Main/Properties/AssemblyInfo.cs deleted file mode 100644 index e98f03a..0000000 --- a/Modules/Main/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Main: AssemblyInfo.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Main")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Main")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("645C6E67-C0BB-4885-914B-50679E3382FC")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/NEWorld.Linux/NEWorld.Linux.csproj b/NEWorld.Linux/NEWorld.Linux.csproj new file mode 100644 index 0000000..47a7b5a --- /dev/null +++ b/NEWorld.Linux/NEWorld.Linux.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp2.1 + linux-x64 + Resources\Icon.ico + WinExe + NEWorld.Linux + + ..\Bin\Linux\$(Configuration)\ + false + + + true + + + + + + + + + diff --git a/NEWorld.Linux/NEWorld.Linux.xkpkg b/NEWorld.Linux/NEWorld.Linux.xkpkg new file mode 100644 index 0000000..228fc02 --- /dev/null +++ b/NEWorld.Linux/NEWorld.Linux.xkpkg @@ -0,0 +1,17 @@ +!Package +SerializedVersion: {Assets: 3.1.0.0} +Meta: + Name: NEWorld.Linux + Version: 1.0.0 + Authors: [] + Owners: [] + Dependencies: null +AssetFolders: + - Path: !dir Assets +ResourceFolders: + - !dir Resources +OutputGroupDirectories: {} +ExplicitFolders: [] +Bundles: [] +TemplateFolders: [] +RootAssets: [] diff --git a/Core/Utilities/EndianCheck.cs b/NEWorld.Linux/NEWorldApp.cs similarity index 57% rename from Core/Utilities/EndianCheck.cs rename to NEWorld.Linux/NEWorldApp.cs index 65ffcc4..342e060 100644 --- a/Core/Utilities/EndianCheck.cs +++ b/NEWorld.Linux/NEWorldApp.cs @@ -1,7 +1,7 @@ -// -// Core: EndianCheck.cs +// +// NEWorld/NEWorld.Linux: NEWorldApp.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,28 +16,18 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - -namespace Core.Utilities +namespace NEWorld.Linux { - public static class EndianCheck + internal static class NEWorldApp { - private static bool _isBigEndian; - - private static bool _isEndianChecked; - - private static bool IsBigEndian() + private static void Main(string[] args) { - if (!_isEndianChecked) + Core.ApplicationControl.DoLaunch(); + using (var game = new Xenko.Engine.Game()) { - _isEndianChecked = true; - const int nCheck = 0x01aa; - _isBigEndian = (nCheck & 0xff) == 0x01; + game.Run(); } - - return _isBigEndian; + Core.ApplicationControl.DoShutdown(); } - - public static bool BigEndian => IsBigEndian(); - public static bool LittleEndian => !IsBigEndian(); } } \ No newline at end of file diff --git a/NEWorld.Linux/Resources/Icon.ico b/NEWorld.Linux/Resources/Icon.ico new file mode 100644 index 0000000..29256ad Binary files /dev/null and b/NEWorld.Linux/Resources/Icon.ico differ diff --git a/NEWorld.Windows/Assets/EffectCompileLog.xkeffectlog b/NEWorld.Windows/Assets/EffectCompileLog.xkeffectlog new file mode 100644 index 0000000..5ae6c49 --- /dev/null +++ b/NEWorld.Windows/Assets/EffectCompileLog.xkeffectlog @@ -0,0 +1,310 @@ +--- +!EffectCompileRequest +EffectName: XenkoForwardShadingEffect +UsedParameters: + Material.PixelStageSurfaceShaders: !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceArray + Compositions: + layers: !ShaderArraySource + Values: + - !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceDiffuse + Compositions: + diffuseMap: !ShaderClassSource + ClassName: ComputeColorConstantColorLink + GenericArguments: [Material.DiffuseValue] + - !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceLightingAndShading + Compositions: + surfaces: !ShaderArraySource + Values: + - !ShaderClassSource + ClassName: MaterialSurfaceShadingDiffuseLambert + GenericArguments: [false] + Material.PixelStageStreamInitializer: !ShaderMixinSource + Mixins: + - ClassName: MaterialStream + - ClassName: MaterialPixelShadingStream + Lighting.DirectLightGroups: + - !ShaderMixinSource + Mixins: + - ClassName: LightDirectionalGroup + GenericArguments: [1] + - ClassName: ShadowMapReceiverDirectional + GenericArguments: [4, 1, true, true, false, false] + - ClassName: ShadowMapFilterPcf + GenericArguments: [PerView.Lighting, 5] + - !ShaderClassSource + ClassName: LightClusteredPointGroup + - !ShaderClassSource + ClassName: LightClusteredSpotGroup + Lighting.EnvironmentLights: + - !ShaderClassSource + ClassName: LightSimpleAmbient + - !ShaderClassSource + ClassName: EnvironmentLight + XenkoEffectBase.RenderTargetExtensions: !ShaderMixinSource + Macros: + - Name: XENKO_RENDER_TARGET_COUNT + Definition: 1 + - Name: XENKO_MULTISAMPLE_COUNT + Definition: 1 +--- +!EffectCompileRequest +EffectName: XenkoForwardShadingEffect.ShadowMapCaster +UsedParameters: + Material.PixelStageSurfaceShaders: !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceArray + Compositions: + layers: !ShaderArraySource + Values: + - !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceDiffuse + Compositions: + diffuseMap: !ShaderClassSource + ClassName: ComputeColorConstantColorLink + GenericArguments: [Material.DiffuseValue] + - !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceLightingAndShading + Compositions: + surfaces: !ShaderArraySource + Values: + - !ShaderClassSource + ClassName: MaterialSurfaceShadingDiffuseLambert + GenericArguments: [false] + Material.PixelStageStreamInitializer: !ShaderMixinSource + Mixins: + - ClassName: MaterialStream + - ClassName: MaterialPixelShadingStream +--- +!EffectCompileRequest +EffectName: XenkoForwardShadingEffect.ShadowMapCaster +UsedParameters: + Material.PixelStageSurfaceShaders: !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceArray + Compositions: + layers: !ShaderArraySource + Values: + - !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceDiffuse + Compositions: + diffuseMap: !ShaderClassSource + ClassName: ComputeColorConstantColorLink + GenericArguments: [Material.DiffuseValue] + - !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceLightingAndShading + Compositions: + surfaces: !ShaderArraySource + Values: + - !ShaderClassSource + ClassName: MaterialSurfaceShadingDiffuseLambert + GenericArguments: [false] + Material.PixelStageStreamInitializer: !ShaderMixinSource + Mixins: + - ClassName: MaterialStream + - ClassName: MaterialPixelShadingStream + Lighting.DirectLightGroups: + - !ShaderMixinSource + Mixins: + - ClassName: LightDirectionalGroup + GenericArguments: [1] + - ClassName: ShadowMapReceiverDirectional + GenericArguments: [4, 1, true, true, false, false] + - ClassName: ShadowMapFilterPcf + GenericArguments: [PerView.Lighting, 5] + - !ShaderClassSource + ClassName: LightClusteredPointGroup + - !ShaderClassSource + ClassName: LightClusteredSpotGroup + Lighting.EnvironmentLights: + - !ShaderClassSource + ClassName: LightSimpleAmbient + - !ShaderClassSource + ClassName: EnvironmentLight +--- +!EffectCompileRequest +EffectName: SkyboxShaderCubemap +UsedParameters: {} +--- +!EffectCompileRequest +EffectName: RangeCompressorShader +UsedParameters: {} +--- +!EffectCompileRequest +EffectName: FXAAShaderEffect +UsedParameters: + FXAAEffect.GreenAsLumaKey: 0 + FXAAEffect.QualityKey: 23 +--- +!EffectCompileRequest +EffectName: RangeDecompressorShader +UsedParameters: {} +--- +!EffectCompileRequest +EffectName: LuminanceLogShader +UsedParameters: {} +--- +!EffectCompileRequest +EffectName: ImageScalerEffect +UsedParameters: {} +--- +!EffectCompileRequest +EffectName: BrightFilterShader +UsedParameters: {} +--- +!EffectCompileRequest +EffectName: ImageSuperSamplerScalerEffect +UsedParameters: {} +--- +!EffectCompileRequest +EffectName: GaussianBlurEffect +UsedParameters: + GaussianBlur.VerticalBlur: false + GaussianBlur.Count: 5 +--- +!EffectCompileRequest +EffectName: GaussianBlurEffect +UsedParameters: + GaussianBlur.VerticalBlur: true + GaussianBlur.Count: 5 +--- +!EffectCompileRequest +EffectName: LightStreakEffect +UsedParameters: + LightStreak.Count: 4 + LightStreak.AnamorphicCount: 1 +--- +!EffectCompileRequest +EffectName: ColorCombinerEffect +UsedParameters: + ColorCombiner.FactorCount: 1 +--- +!EffectCompileRequest +EffectName: GaussianBlurEffect +UsedParameters: + GaussianBlur.VerticalBlur: false + GaussianBlur.Count: 3 +--- +!EffectCompileRequest +EffectName: GaussianBlurEffect +UsedParameters: + GaussianBlur.VerticalBlur: true + GaussianBlur.Count: 3 +--- +!EffectCompileRequest +EffectName: FlareArtifactEffect +UsedParameters: + FlareArtifact.Count: 8 +--- +!EffectCompileRequest +EffectName: FlareReplicate +UsedParameters: {} +--- +!EffectCompileRequest +EffectName: ColorTransformGroupEffect +UsedParameters: + ColorTransformGroup.Transforms: + - !ToneMap + Operator: !ToneMapHejl2Operator {} + - !LuminanceToChannelTransform {} +--- +!EffectCompileRequest +EffectName: XenkoForwardShadingEffect.ShadowMapCaster +UsedParameters: + Material.PixelStageSurfaceShaders: !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceArray + Compositions: + layers: !ShaderArraySource + Values: + - !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceDiffuse + Compositions: + diffuseMap: !ShaderClassSource + ClassName: VertexTextureTerrain + - !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceLightingAndShading + Compositions: + surfaces: !ShaderArraySource + Values: + - !ShaderClassSource + ClassName: MaterialSurfaceShadingDiffuseLambert + GenericArguments: [false] + Material.PixelStageStreamInitializer: !ShaderMixinSource + Mixins: + - ClassName: MaterialStream + - ClassName: MaterialPixelShadingStream + Lighting.DirectLightGroups: + - !ShaderMixinSource + Mixins: + - ClassName: LightDirectionalGroup + GenericArguments: [8] + - !ShaderClassSource + ClassName: LightClusteredPointGroup + - !ShaderClassSource + ClassName: LightClusteredSpotGroup + Lighting.EnvironmentLights: + - !ShaderClassSource + ClassName: LightSimpleAmbient + - !ShaderClassSource + ClassName: EnvironmentLight +--- +!EffectCompileRequest +EffectName: XenkoForwardShadingEffect +UsedParameters: + Material.PixelStageSurfaceShaders: !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceArray + Compositions: + layers: !ShaderArraySource + Values: + - !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceDiffuse + Compositions: + diffuseMap: !ShaderClassSource + ClassName: VertexTextureTerrain + - !ShaderMixinSource + Mixins: + - ClassName: MaterialSurfaceLightingAndShading + Compositions: + surfaces: !ShaderArraySource + Values: + - !ShaderClassSource + ClassName: MaterialSurfaceShadingDiffuseLambert + GenericArguments: [false] + Material.PixelStageStreamInitializer: !ShaderMixinSource + Mixins: + - ClassName: MaterialStream + - ClassName: MaterialPixelShadingStream + Lighting.DirectLightGroups: + - !ShaderMixinSource + Mixins: + - ClassName: LightDirectionalGroup + GenericArguments: [8] + - !ShaderClassSource + ClassName: LightClusteredPointGroup + - !ShaderClassSource + ClassName: LightClusteredSpotGroup + Lighting.EnvironmentLights: + - !ShaderClassSource + ClassName: LightSimpleAmbient + - !ShaderClassSource + ClassName: EnvironmentLight + XenkoEffectBase.RenderTargetExtensions: !ShaderMixinSource + Macros: + - Name: XENKO_RENDER_TARGET_COUNT + Definition: 1 + - Name: XENKO_MULTISAMPLE_COUNT + Definition: 1 diff --git a/NEWorld.Windows/NEWorld.Windows.csproj b/NEWorld.Windows/NEWorld.Windows.csproj new file mode 100644 index 0000000..735bc2e --- /dev/null +++ b/NEWorld.Windows/NEWorld.Windows.csproj @@ -0,0 +1,20 @@ + + + + net461 + Resources\Icon.ico + WinExe + NEWorld.Windows + + ..\Bin\Windows\$(Configuration)\ + false + + + true + + + + + + + diff --git a/NEWorld.Windows/NEWorld.Windows.xkpkg b/NEWorld.Windows/NEWorld.Windows.xkpkg new file mode 100644 index 0000000..80a8aa3 --- /dev/null +++ b/NEWorld.Windows/NEWorld.Windows.xkpkg @@ -0,0 +1,33 @@ +!Package +SerializedVersion: {Assets: 3.1.0.0} +Meta: + Name: NEWorld.Windows + Version: 1.0.0 + Authors: [] + Owners: [] + Dependencies: null +AssetFolders: + - Path: !dir Assets +ResourceFolders: + - !dir Resources +OutputGroupDirectories: {} +ExplicitFolders: [] +Bundles: [] +TemplateFolders: [] +RootAssets: + - 0c1e6502-9fe1-4fa6-b1c2-8e67f76f1f4f:Textures/Blocks/Iron + - 266e2127-34ad-4978-ae76-a37dbebf3547:Textures/Blocks/Rock + - 3091e23d-b2af-4698-b2c8-b16af90eaa2c:Textures/Blocks/Water + - 68b96536-d657-46f9-8319-173aa4f4af30:Textures/Blocks/Bedrock + - 6cde71ed-6d9a-4642-a312-d58c02dbb07d:Textures/Blocks/Plank + - 6cff36b6-77c2-47de-b5d1-18465cb77201:Textures/Blocks/Glowstone + - 729c1023-0d45-456b-b5af-9c2c9ef8b87a:Textures/Blocks/Cement + - 758069be-3d4f-4f11-a44c-8b5bbea08cbb:Textures/Blocks/Null + - 872b5b39-8830-41cd-99a3-b1f729f88966:Textures/Blocks/Dirt + - 8950f4b3-c0bf-4ba1-85a7-d008f78b8b99:Textures/Blocks/GrassTop + - bc3db60e-4472-4e7f-b1e8-626b4a0cc336:Textures/Blocks/TrunkRound + - c896ecda-99c3-4815-b811-6dcf125c371c:Textures/Blocks/GrassRound + - def5376c-d58d-4872-8edc-bfd10cc66ba6:Textures/Blocks/Coal + - f35d797b-97bd-4f89-823e-7ce0254f8fd5:Textures/Blocks/Stone + - f432c34d-8800-43a5-8493-ed3507c53aa9:Textures/Blocks/Sand + - f6251b49-efcd-451d-aeb9-f422b2c5cd90:Textures/Blocks/TrunkTop diff --git a/NEWorld.Windows/NEWorldApp.cs b/NEWorld.Windows/NEWorldApp.cs new file mode 100644 index 0000000..3e740fd --- /dev/null +++ b/NEWorld.Windows/NEWorldApp.cs @@ -0,0 +1,36 @@ +// +// NEWorld/NEWorld.Windows: NEWorldApp.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +namespace NEWorld.Windows +{ + internal static class NEWorldApp + { + private static void Main(string[] args) + { + // TODO: Remove Later when Launching Server with Client is Possible + var server = System.Diagnostics.Process.Start("NEWorldShell.exe"); + Core.ApplicationControl.DoLaunch(); + using (var game = new Xenko.Engine.Game()) + { + game.Run(); + } + Core.ApplicationControl.DoShutdown(); + server?.Kill(); + } + } +} \ No newline at end of file diff --git a/NEWorld.Windows/Resources/Icon.ico b/NEWorld.Windows/Resources/Icon.ico new file mode 100644 index 0000000..29256ad Binary files /dev/null and b/NEWorld.Windows/Resources/Icon.ico differ diff --git a/NEWorld.macOS/NEWorld.macOS.csproj b/NEWorld.macOS/NEWorld.macOS.csproj new file mode 100644 index 0000000..0baa0dc --- /dev/null +++ b/NEWorld.macOS/NEWorld.macOS.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp2.1 + osx-x64 + Resources\Icon.ico + WinExe + NEWorld.macOS + + ..\Bin\macOS\$(Configuration)\ + false + + + true + + + + + + + + + diff --git a/NEWorld.macOS/NEWorld.macOS.xkpkg b/NEWorld.macOS/NEWorld.macOS.xkpkg new file mode 100644 index 0000000..b087809 --- /dev/null +++ b/NEWorld.macOS/NEWorld.macOS.xkpkg @@ -0,0 +1,17 @@ +!Package +SerializedVersion: {Assets: 3.1.0.0} +Meta: + Name: NEWorld.macOS + Version: 1.0.0 + Authors: [] + Owners: [] + Dependencies: null +AssetFolders: + - Path: !dir Assets +ResourceFolders: + - !dir Resources +OutputGroupDirectories: {} +ExplicitFolders: [] +Bundles: [] +TemplateFolders: [] +RootAssets: [] diff --git a/NEWorld.macOS/NEWorldApp.cs b/NEWorld.macOS/NEWorldApp.cs new file mode 100644 index 0000000..b9cc328 --- /dev/null +++ b/NEWorld.macOS/NEWorldApp.cs @@ -0,0 +1,33 @@ +// +// NEWorld/NEWorld.macOS: NEWorldApp.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +namespace NEWorld.macOS +{ + internal static class NEWorldApp + { + private static void Main(string[] args) + { + Core.ApplicationControl.DoLaunch(); + using (var game = new Xenko.Engine.Game()) + { + game.Run(); + } + Core.ApplicationControl.DoShutdown(); + } + } +} \ No newline at end of file diff --git a/NEWorld.macOS/Resources/Icon.ico b/NEWorld.macOS/Resources/Icon.ico new file mode 100644 index 0000000..29256ad Binary files /dev/null and b/NEWorld.macOS/Resources/Icon.ico differ diff --git a/NEWorld.sln b/NEWorld.sln index de6caf9..93a1eb7 100644 --- a/NEWorld.sln +++ b/NEWorld.sln @@ -1,19 +1,21 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27703.2026 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{ECB0E309-625F-4A24-926D-D1D23C1B7693}" +VisualStudioVersion = 15.0.28307.271 +MinimumVisualStudioVersion = 14.0.23107.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NEWorld.Windows", "NEWorld.Windows\NEWorld.Windows.csproj", "{CA6797EE-FF9D-4760-A989-CA4E437B3644}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Game", "Game\Game.csproj", "{F83C4945-ABFF-4856-94A3-D0D31C976A11}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NEWorld", "NEWorld\NEWorld.csproj", "{C3436076-873E-4278-90BC-821000AA1EA8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NEWorld", "NEWorld\NEWorld.csproj", "{553FF3F0-7F88-4821-BDA1-282CE6B7DDE7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NEWorld.Linux", "NEWorld.Linux\NEWorld.Linux.csproj", "{290FC8CD-1F58-43CB-882F-0DEF0A4CCA9C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGL", "OpenGL\OpenGL.csproj", "{4EB6C361-3E67-4A4E-8B2B-9C8D29D8F852}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NEWorld.macOS", "NEWorld.macOS\NEWorld.macOS.csproj", "{ADA151FA-FC7B-4D12-AE81-B3B698C3832A}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{0B6955CF-8C4C-4CD1-B8D6-D5F49E737BEB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{6F2F0B2B-F1E3-4810-A530-3086B9EB4585}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Main", "Modules\Main\Main.csproj", "{645C6E67-C0BB-4885-914B-50679E3382FC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Game", "Game\Game.csproj", "{6A25A184-51D2-45DE-9272-5CCB8B3B1AB5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Main", "Main\Main.csproj", "{15BDBFD8-DAEA-4324-9B09-7AB531BFE5EF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NEWorldShell", "NEWorldShell\NEWorldShell.csproj", "{0C30B20E-EBC8-46E2-ADEF-F4FC23C59DF4}" EndProject @@ -23,26 +25,34 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {ECB0E309-625F-4A24-926D-D1D23C1B7693}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ECB0E309-625F-4A24-926D-D1D23C1B7693}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ECB0E309-625F-4A24-926D-D1D23C1B7693}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ECB0E309-625F-4A24-926D-D1D23C1B7693}.Release|Any CPU.Build.0 = Release|Any CPU - {F83C4945-ABFF-4856-94A3-D0D31C976A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F83C4945-ABFF-4856-94A3-D0D31C976A11}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F83C4945-ABFF-4856-94A3-D0D31C976A11}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F83C4945-ABFF-4856-94A3-D0D31C976A11}.Release|Any CPU.Build.0 = Release|Any CPU - {553FF3F0-7F88-4821-BDA1-282CE6B7DDE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {553FF3F0-7F88-4821-BDA1-282CE6B7DDE7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {553FF3F0-7F88-4821-BDA1-282CE6B7DDE7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {553FF3F0-7F88-4821-BDA1-282CE6B7DDE7}.Release|Any CPU.Build.0 = Release|Any CPU - {4EB6C361-3E67-4A4E-8B2B-9C8D29D8F852}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4EB6C361-3E67-4A4E-8B2B-9C8D29D8F852}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4EB6C361-3E67-4A4E-8B2B-9C8D29D8F852}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4EB6C361-3E67-4A4E-8B2B-9C8D29D8F852}.Release|Any CPU.Build.0 = Release|Any CPU - {645C6E67-C0BB-4885-914B-50679E3382FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {645C6E67-C0BB-4885-914B-50679E3382FC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {645C6E67-C0BB-4885-914B-50679E3382FC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {645C6E67-C0BB-4885-914B-50679E3382FC}.Release|Any CPU.Build.0 = Release|Any CPU + {CA6797EE-FF9D-4760-A989-CA4E437B3644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA6797EE-FF9D-4760-A989-CA4E437B3644}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA6797EE-FF9D-4760-A989-CA4E437B3644}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA6797EE-FF9D-4760-A989-CA4E437B3644}.Release|Any CPU.Build.0 = Release|Any CPU + {C3436076-873E-4278-90BC-821000AA1EA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3436076-873E-4278-90BC-821000AA1EA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3436076-873E-4278-90BC-821000AA1EA8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3436076-873E-4278-90BC-821000AA1EA8}.Release|Any CPU.Build.0 = Release|Any CPU + {290FC8CD-1F58-43CB-882F-0DEF0A4CCA9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {290FC8CD-1F58-43CB-882F-0DEF0A4CCA9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {290FC8CD-1F58-43CB-882F-0DEF0A4CCA9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {290FC8CD-1F58-43CB-882F-0DEF0A4CCA9C}.Release|Any CPU.Build.0 = Release|Any CPU + {ADA151FA-FC7B-4D12-AE81-B3B698C3832A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ADA151FA-FC7B-4D12-AE81-B3B698C3832A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADA151FA-FC7B-4D12-AE81-B3B698C3832A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ADA151FA-FC7B-4D12-AE81-B3B698C3832A}.Release|Any CPU.Build.0 = Release|Any CPU + {6F2F0B2B-F1E3-4810-A530-3086B9EB4585}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F2F0B2B-F1E3-4810-A530-3086B9EB4585}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F2F0B2B-F1E3-4810-A530-3086B9EB4585}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F2F0B2B-F1E3-4810-A530-3086B9EB4585}.Release|Any CPU.Build.0 = Release|Any CPU + {6A25A184-51D2-45DE-9272-5CCB8B3B1AB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A25A184-51D2-45DE-9272-5CCB8B3B1AB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A25A184-51D2-45DE-9272-5CCB8B3B1AB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A25A184-51D2-45DE-9272-5CCB8B3B1AB5}.Release|Any CPU.Build.0 = Release|Any CPU + {15BDBFD8-DAEA-4324-9B09-7AB531BFE5EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {15BDBFD8-DAEA-4324-9B09-7AB531BFE5EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {15BDBFD8-DAEA-4324-9B09-7AB531BFE5EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {15BDBFD8-DAEA-4324-9B09-7AB531BFE5EF}.Release|Any CPU.Build.0 = Release|Any CPU {0C30B20E-EBC8-46E2-ADEF-F4FC23C59DF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0C30B20E-EBC8-46E2-ADEF-F4FC23C59DF4}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C30B20E-EBC8-46E2-ADEF-F4FC23C59DF4}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -51,10 +61,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {645C6E67-C0BB-4885-914B-50679E3382FC} = {0B6955CF-8C4C-4CD1-B8D6-D5F49E737BEB} - EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {E9811B2A-F42A-4203-A546-4F70CE4F2927} + SolutionGuid = {DDC19D57-FA0C-4288-9B22-DE39BCCAC3D3} EndGlobalSection EndGlobal diff --git a/NEWorld.sln.DotSettings b/NEWorld.sln.DotSettings new file mode 100644 index 0000000..95d9601 --- /dev/null +++ b/NEWorld.sln.DotSettings @@ -0,0 +1,23 @@ + + + True + <?xml version="1.0" encoding="utf-16"?><Profile name="Update Header"><CppClangTidyCleanupDescriptor /><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="False" RemoveRedundantParentheses="False" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" ArrangeCodeBodyStyle="False" ArrangeVarStyle="False" /><CSOptimizeUsings><OptimizeUsings>False</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSUpdateFileHeader>True</CSUpdateFileHeader><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CppUpdateFileHeader>True</CppUpdateFileHeader></Profile> + +$SOLUTION$/$PROJECT$: $FILENAME$ +NEWorld: A Free Game with Similar Rules to Minecraft. +Copyright (C) 2015-$CURRENT_YEAR$ NEWorld Team + +NEWorld is free software: you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +NEWorld is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with NEWorld. If not, see <http://www.gnu.org/licenses/>. + + \ No newline at end of file diff --git a/NEWorld.sln.DotSettings.user b/NEWorld.sln.DotSettings.user index 8dad885..a637337 100644 --- a/NEWorld.sln.DotSettings.user +++ b/NEWorld.sln.DotSettings.user @@ -1,16 +1,29 @@  + 2 True - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> True - True \ No newline at end of file + True + <AssemblyExplorer> + <Assembly Path="C:\Users\yshli\.nuget\packages\xenko.graphics\3.1.0.1-beta01-0406\ref\netstandard2.0\Xenko.Graphics.dll" /> + <Assembly Path="C:\Users\yshli\.nuget\packages\xenko.engine\3.1.0.1-beta01-0406\ref\netstandard2.0\Xenko.Engine.dll" /> + <Assembly Path="C:\Users\yshli\.nuget\packages\xenko.core.mathematics\3.1.0.1-beta01-0406\lib\netstandard2.0\Xenko.Core.Mathematics.dll" /> + <Assembly Path="C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll" /> + <Assembly Path="C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.7\System.Net.Sockets.dll" /> + <Assembly Path="C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.7\System.Console.dll" /> + <Assembly Path="C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.7\System.Threading.Thread.dll" /> + <Assembly Path="C:\Users\yshli\.nuget\packages\xenko\3.1.0.1-beta01-0406\lib\netstandard2.0\Xenko.dll" /> + <Assembly Path="C:\Program Files\dotnet\sdk\NuGetFallbackFolder\messagepack\1.7.3.4\lib\netstandard2.0\MessagePack.dll" /> + <Assembly Path="C:\Users\yshli\.nuget\packages\xenko.core\3.1.0.1-beta01-0406\ref\netstandard2.0\Xenko.Core.dll" /> + <Assembly Path="C:\Users\yshli\.nuget\packages\xenko\3.1.0.1-beta01-0430\lib\netstandard2.0\Xenko.dll" /> + <Assembly Path="C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.7\System.Collections.Concurrent.dll" /> + <Assembly Path="C:\Users\yshli\.nuget\packages\xenko.core\3.1.0.1-beta01-0430\ref\netstandard2.0\Xenko.Core.dll" /> +</AssemblyExplorer> \ No newline at end of file diff --git a/NEWorld/Assets/GameSettings.xkgamesettings b/NEWorld/Assets/GameSettings.xkgamesettings new file mode 100644 index 0000000..7ed69cb --- /dev/null +++ b/NEWorld/Assets/GameSettings.xkgamesettings @@ -0,0 +1,37 @@ +!GameSettingsAsset +Id: ea39345b-fe46-475a-b61e-d2e3e67a6cce +SerializedVersion: {Xenko: 2.1.0.3} +Tags: [] +DefaultScene: 9dff7916-ac70-450a-aed9-fa776976f005:MainScene +GraphicsCompositor: 3156586f-3baa-4f44-bcd7-0d72a73d2f2e:GraphicsCompositor +Defaults: + - !Xenko.Audio.AudioEngineSettings,Xenko.Audio + HrtfSupport: false + - !Xenko.Assets.EditorSettings,Xenko.Assets + RenderingMode: LDR + - !Xenko.Graphics.RenderingSettings,Xenko.Graphics + DefaultBackBufferWidth: 1280 + DefaultBackBufferHeight: 720 + AdaptBackBufferToScreen: false + DefaultGraphicsProfile: Level_11_0 + ColorSpace: Linear + DisplayOrientation: LandscapeRight + - !Xenko.Streaming.StreamingSettings,Xenko.Engine + ManagerUpdatesInterval: 0:00:00:00.0330000 + ResourceLiveTimeout: 0:00:00:08.0000000 + - !Xenko.Assets.Textures.TextureSettings,Xenko.Assets + TextureQuality: Fast +Overrides: [] +PlatformFilters: + - PowerVR SGX 54[0-9] + - Adreno \(TM\) 2[0-9][0-9] + - Adreno (TM) 320 + - Adreno (TM) 330 + - Adreno \(TM\) 4[0-9][0-9] + - NVIDIA Tegra + - Intel(R) HD Graphics + - ^Mali\-4 + - ^Mali\-T6 + - ^Mali\-T7 +SplashScreenTexture: d26edb11-10bd-403c-b3c2-9c7fcccf25e5:XenkoDefaultSplashScreen +SplashScreenColor: {R: 0, G: 0, B: 0, A: 255} diff --git a/NEWorld/Assets/GraphicsCompositor.xkgfxcomp b/NEWorld/Assets/GraphicsCompositor.xkgfxcomp new file mode 100644 index 0000000..4fa0eef --- /dev/null +++ b/NEWorld/Assets/GraphicsCompositor.xkgfxcomp @@ -0,0 +1,197 @@ +!GraphicsCompositorAsset +Id: 3156586f-3baa-4f44-bcd7-0d72a73d2f2e +SerializedVersion: {Xenko: 2.1.0.2} +Tags: [] +Archetype: 823a81bf-bac0-4552-9267-aeed499c40df:DefaultGraphicsCompositorLevel10 +Cameras: + de2e75c3b2b23e54162686363f3f138e: + Id: dce133b3-fc2c-4978-8f3c-8a7bc66947f0 + Name: Main +RenderStages: + 47116750c1a5d449b4ad3625f71439b3: + Id: e3a6abbb-fcd8-4308-82a2-fad7de6a5bcc + Name: Opaque + EffectSlotName: Main + SortMode: !SortModeStateChange {} + 9105a30fee026d4893472b6aee83d035: + Id: 8676a7c8-a5c8-4aee-929c-33492ebd06e6 + Name: Transparent + EffectSlotName: Main + SortMode: !BackToFrontSortMode {} + 554e52c061404d4684dd7c4c70f70e0e: + Id: 9772c403-9a1b-4340-9923-4adce150286c + Name: ShadowMapCaster + EffectSlotName: ShadowMapCaster + SortMode: !FrontToBackSortMode {} + 5a50638f5c514dc490c8c4f57cc88b57: + Id: c3ba6884-d8c8-421d-bc5f-56fd74ea493f + Name: ShadowMapCasterParaboloid + EffectSlotName: ShadowMapCasterParaboloid + SortMode: !FrontToBackSortMode {} + bc1a77d2ab254a6e920f86cff65cd75e: + Id: 6d7b2d22-48c7-4dbd-906e-d166d1ae2cd8 + Name: ShadowMapCasterCubeMap + EffectSlotName: ShadowMapCasterCubeMap + SortMode: !FrontToBackSortMode {} + 33d9d311a1a65601da9ef56775477f95: + Id: 2f4fde52-5da1-4114-987e-28fa0febbbe4 + Name: GBuffer + EffectSlotName: GBuffer + SortMode: !FrontToBackSortMode {} +RenderFeatures: + d8fb80b0e7995140a46bca8dc36ee8a2: !Xenko.Rendering.MeshRenderFeature,Xenko.Engine + RenderStageSelectors: + 44cf4a95ef82544e9ce3c6507d5569a9: !Xenko.Rendering.MeshTransparentRenderStageSelector,Xenko.Engine + OpaqueRenderStage: ref!! e3a6abbb-fcd8-4308-82a2-fad7de6a5bcc + TransparentRenderStage: ref!! 8676a7c8-a5c8-4aee-929c-33492ebd06e6 + EffectName: XenkoForwardShadingEffect + 6f7224048750e7260ea87c444f74b32c: !Xenko.Rendering.Shadows.ShadowMapRenderStageSelector,Xenko.Engine + ShadowMapRenderStage: ref!! 9772c403-9a1b-4340-9923-4adce150286c + EffectName: XenkoForwardShadingEffect.ShadowMapCaster + b60663d7cb46417a94341a39c3bc1a12: !Xenko.Rendering.Shadows.ShadowMapRenderStageSelector,Xenko.Engine + ShadowMapRenderStage: ref!! c3ba6884-d8c8-421d-bc5f-56fd74ea493f + EffectName: XenkoForwardShadingEffect.ShadowMapCasterParaboloid + f5533b1249b942df8a8aba311cd79532: !Xenko.Rendering.Shadows.ShadowMapRenderStageSelector,Xenko.Engine + ShadowMapRenderStage: ref!! 6d7b2d22-48c7-4dbd-906e-d166d1ae2cd8 + EffectName: XenkoForwardShadingEffect.ShadowMapCasterCubeMap + 106341b76db9fcda6a033dad16aa708b: !Xenko.Rendering.MeshTransparentRenderStageSelector,Xenko.Engine + OpaqueRenderStage: ref!! 2f4fde52-5da1-4114-987e-28fa0febbbe4 + EffectName: XenkoForwardShadingEffect.ShadowMapCaster + PipelineProcessors: + d70f5aee0616e4ab25081ceaf643290c: !Xenko.Rendering.MeshPipelineProcessor,Xenko.Engine + TransparentRenderStage: ref!! 8676a7c8-a5c8-4aee-929c-33492ebd06e6 + 26c899b17f88c21ab13bf60a7220ccd1: !Xenko.Rendering.ShadowMeshPipelineProcessor,Xenko.Engine + ShadowMapRenderStage: ref!! 9772c403-9a1b-4340-9923-4adce150286c + ff51170a7d1a4761b73ef6a5c9f0cba2: !Xenko.Rendering.ShadowMeshPipelineProcessor,Xenko.Engine + ShadowMapRenderStage: ref!! c3ba6884-d8c8-421d-bc5f-56fd74ea493f + DepthClipping: true + ae4336b0a9514e8488e8e0ccbcef25f4: !Xenko.Rendering.ShadowMeshPipelineProcessor,Xenko.Engine + ShadowMapRenderStage: ref!! 6d7b2d22-48c7-4dbd-906e-d166d1ae2cd8 + DepthClipping: true + RenderFeatures: + 86b959cbdf51a1438d4973177c77c627: !Xenko.Rendering.TransformRenderFeature,Xenko.Engine {} + 8e0351fee9883922648a11016224b195: !Xenko.Rendering.SkinningRenderFeature,Xenko.Engine {} + f5a2017030ba4b28784e804807ce7628: !Xenko.Rendering.Materials.MaterialRenderFeature,Xenko.Engine {} + 83fea7526ebe4893a5bad953d0502bfd: !Xenko.Rendering.Shadows.ShadowCasterRenderFeature,Xenko.Engine {} + 65743b4380f4cc43b2b4bdc23cd0c07c: !Xenko.Rendering.Lights.ForwardLightingRenderFeature,Xenko.Engine + LightRenderers: + 7ac2775468f53c4399b2f3f6357c85c9: !Xenko.Rendering.Lights.LightAmbientRenderer,Xenko.Engine {} + 7b68f9cd17404a4ba9e5f7df72e3b48d: !Xenko.Rendering.Lights.LightDirectionalGroupRenderer,Xenko.Engine {} + 411fdcfb9fc388449a0443173dfa3f27: !Xenko.Rendering.Lights.LightSkyboxRenderer,Xenko.Engine {} + facdcd5b543cf1c6bdf2138aab6cc473: !Xenko.Rendering.Lights.LightClusteredPointSpotGroupRenderer,Xenko.Engine {} + 79582329a9cf466e960f8920f579de9b: !Xenko.Rendering.Lights.LightPointGroupRenderer,Xenko.Engine {} + cf0c6bd4198b4cc4aaaab5b54870bdfd: !Xenko.Rendering.Lights.LightSpotGroupRenderer,Xenko.Engine {} + 451af18f3f5c4187cf3fe5f33feb46b1: !Xenko.Rendering.LightProbes.LightProbeRenderer,Xenko.Engine {} + ShadowMapRenderer: !Xenko.Rendering.Shadows.ShadowMapRenderer,Xenko.Engine + Renderers: + 7c3d3d4c86834c3551bacde2527b3836: !Xenko.Rendering.Shadows.LightDirectionalShadowMapRenderer,Xenko.Engine + ShadowCasterRenderStage: ref!! 9772c403-9a1b-4340-9923-4adce150286c + 1c204b09435636256a3fcfd6f9ddb347: !Xenko.Rendering.Shadows.LightSpotShadowMapRenderer,Xenko.Engine + ShadowCasterRenderStage: ref!! 9772c403-9a1b-4340-9923-4adce150286c + 7c8c69ce27034b4c8bbcab0bcdfe954b: !Xenko.Rendering.Shadows.LightPointShadowMapRendererParaboloid,Xenko.Engine + ShadowCasterRenderStage: ref!! c3ba6884-d8c8-421d-bc5f-56fd74ea493f + d59ef45dd99e49d3af3887763d153aa7: !Xenko.Rendering.Shadows.LightPointShadowMapRendererCubeMap,Xenko.Engine + ShadowCasterRenderStage: ref!! 6d7b2d22-48c7-4dbd-906e-d166d1ae2cd8 + 28e9bf54a5adbe063f59fb17acb2723e: !Xenko.Rendering.Sprites.SpriteRenderFeature,Xenko.Engine + RenderStageSelectors: + d74665cff080638a2439c4422e542d85: !Xenko.Rendering.Sprites.SpriteTransparentRenderStageSelector,Xenko.Engine + OpaqueRenderStage: ref!! e3a6abbb-fcd8-4308-82a2-fad7de6a5bcc + TransparentRenderStage: ref!! 8676a7c8-a5c8-4aee-929c-33492ebd06e6 + EffectName: Test + 60780391e205770513fdd53e07279a01: !Xenko.Rendering.Background.BackgroundRenderFeature,Xenko.Engine + RenderStageSelectors: + 11c8b8ccb522e3cd1dd6688016062a6d: !Xenko.Rendering.SimpleGroupToRenderStageSelector,Xenko.Engine + RenderStage: ref!! e3a6abbb-fcd8-4308-82a2-fad7de6a5bcc + EffectName: Test + 93933ad00d0c357d4915ad462cbfd04c: !Xenko.Rendering.UI.UIRenderFeature,Xenko.UI + RenderStageSelectors: + 14a071694411235038a102ac3794bb4d: !Xenko.Rendering.SimpleGroupToRenderStageSelector,Xenko.Engine + RenderStage: ref!! 8676a7c8-a5c8-4aee-929c-33492ebd06e6 + EffectName: Test + 9013eab3ea0ef6c98bf133b86c173d45: ~(Deleted) +SharedRenderers: + 60459475d3a3adaf2d1ba5d99913ca75: !Xenko.Rendering.Compositing.ForwardRenderer,Xenko.Engine + Id: eae2968e-f8b9-4541-9f09-291e8b21e99b + Clear: + Id: 4bc4b2ca-027e-4e4a-94cb-2912709bef5f + Color: {R: 0.40491876, G: 0.411895424, B: 0.43775, A: 1.0} + LightProbes: true + OpaqueRenderStage: ref!! e3a6abbb-fcd8-4308-82a2-fad7de6a5bcc + TransparentRenderStage: ref!! 8676a7c8-a5c8-4aee-929c-33492ebd06e6 + ShadowMapRenderStages: + fc4d1e0de5c2b0bbc27bcf96e9a848fd: ref!! 9772c403-9a1b-4340-9923-4adce150286c + GBufferRenderStage: ref!! 2f4fde52-5da1-4114-987e-28fa0febbbe4 + PostEffects: !PostProcessingEffects ref!! 88b0e076-6c98-4c42-9e90-3f331a5dbf04 + LightShafts: null + VRSettings: + Enabled: false + RequiredApis: {} + Overlays: {} + SubsurfaceScatteringBlurEffect: null + MSAALevel: None + MSAAResolver: {} + d5b2e71c088247e21556decdce138d96: !Xenko.Rendering.Compositing.ForwardRenderer,Xenko.Engine + Id: 1e68a169-6a0f-4984-af52-9d9e784286fb + Clear: + Id: 66a42307-1985-4316-871a-768449238c11 + Color: {R: 0.40491876, G: 0.411895424, B: 0.43775, A: 1.0} + LightProbes: true + OpaqueRenderStage: ref!! e3a6abbb-fcd8-4308-82a2-fad7de6a5bcc + TransparentRenderStage: ref!! 8676a7c8-a5c8-4aee-929c-33492ebd06e6 + ShadowMapRenderStages: + 2323a99a8a983e182f318e55604659b0: ref!! 9772c403-9a1b-4340-9923-4adce150286c + GBufferRenderStage: ref!! 2f4fde52-5da1-4114-987e-28fa0febbbe4 + PostEffects: null + LightShafts: null + VRSettings: + Enabled: false + RequiredApis: {} + Overlays: {} + SubsurfaceScatteringBlurEffect: null + MSAALevel: None + MSAAResolver: {} + 34ecb9b2633eacfc439ba8744fe05102: !PostProcessingEffects + Id: 88b0e076-6c98-4c42-9e90-3f331a5dbf04 + AmbientOcclusion: + Enabled: false + LocalReflections: + Enabled: false + ResolvePassResolution: Full + DepthResolution: Half + DepthOfField: + Enabled: false + DOFAreas: {X: 0.5, Y: 6.0, Z: 50.0, W: 200.0} + BrightFilter: + Color: {R: 1.0, G: 1.0, B: 1.0} + Bloom: + Distortion: {X: 1.0, Y: 1.0} + Afterimage: + Enabled: false + LightStreak: + Attenuation: 0.7 + LensFlare: {} + ColorTransforms: + Transforms: + 1e06f805f8b2e949a06c30d45fe413ef: !ToneMap + Operator: !ToneMapHejl2Operator {} + c57351444609d14ea258b3f511ec8a74: !FilmGrain + Enabled: false + e86e22e9a5d65545b8b55fca26e0afee: !Vignetting + Enabled: false + Color: {R: 0.0, G: 0.0, B: 0.0} + Antialiasing: !FXAAEffect {} + ee80a20a9bd99f2d70711114e15fe7ca: !Xenko.Rendering.Compositing.DebugRenderer,Xenko.Engine + Id: 5d84ec9b-04da-4738-970f-e7fc4fd99c07 + DebugRenderStages: {} +Game: !Xenko.Rendering.Compositing.SceneCameraRenderer,Xenko.Engine + Id: 76fe87cf-f574-4ad6-85b8-e9a9586be0e2 + Camera: ref!! dce133b3-fc2c-4978-8f3c-8a7bc66947f0 + Child: !Xenko.Rendering.Compositing.SceneRendererCollection,Xenko.Engine + Id: 82568e46-92e7-421a-8dca-114a74e0cd69 + Children: + d39c5ddbf8b7d5ca02bafb6496b1cc3c: !Xenko.Rendering.Compositing.ForwardRenderer,Xenko.Engine ref!! eae2968e-f8b9-4541-9f09-291e8b21e99b + 01d338078e9b21121ead0868932613dd: !Xenko.Rendering.Compositing.DebugRenderer,Xenko.Engine ref!! 5d84ec9b-04da-4738-970f-e7fc4fd99c07 + RenderMask: All +SingleView: !Xenko.Rendering.Compositing.ForwardRenderer,Xenko.Engine ref!! 1e68a169-6a0f-4984-af52-9d9e784286fb +Editor: !Xenko.Rendering.Compositing.ForwardRenderer,Xenko.Engine ref!! eae2968e-f8b9-4541-9f09-291e8b21e99b +BlockPositions: {} diff --git a/NEWorld/Assets/MainScene.xkscene b/NEWorld/Assets/MainScene.xkscene new file mode 100644 index 0000000..03a6ca0 --- /dev/null +++ b/NEWorld/Assets/MainScene.xkscene @@ -0,0 +1,106 @@ +!SceneAsset +Id: 9dff7916-ac70-450a-aed9-fa776976f005 +SerializedVersion: {Xenko: 3.1.0.1} +Tags: [] +ChildrenIds: [] +Offset: {X: 0.0, Y: 0.0, Z: 0.0} +Hierarchy: + RootParts: + - ref!! 8d346802-8fe6-4d18-957c-c6dd8404ea84 + - ref!! 1cc17873-cca9-48fe-a61f-a1d061e26959 + - ref!! b1058700-11a7-4296-bb63-2fefdb5f28b7 + - ref!! b1588acf-0fcd-4377-bf5d-6c81dcf34484 + - ref!! 3cb14cc7-8d18-4297-833c-05b0988ea34e + Parts: + - Entity: + Id: 1cc17873-cca9-48fe-a61f-a1d061e26959 + Name: Directional light + Components: + 1fd11c5daf8481b885d448424af7cbe3: !TransformComponent + Id: 2e13dd0e-ce0b-4194-87b1-355fb6805b1a + Position: {X: -60.0, Y: 100.0, Z: 0.0} + Rotation: {X: -0.5, Y: 0.0, Z: 0.0, W: 0.8660254} + Scale: {X: 1.0, Y: 1.0, Z: 1.0} + Children: {} + e709a18ae6b4e34290801582f4038c7d: !LightComponent + Id: 7a3bb3cc-c0fe-4549-9ae0-928248e556f0 + Type: !LightDirectional + Color: !ColorRgbProvider + Value: {R: 1.0, G: 1.0, B: 1.0} + Shadow: + Enabled: true + Filter: !LightShadowMapFilterTypePcf + FilterSize: Filter5x5 + Size: Large + DepthRange: + ManualMaxDistance: 256.0 + PartitionMode: !LightDirectionalShadowMap.PartitionLogarithmic {} + ComputeTransmittance: false + BiasParameters: {} + - Entity: + Id: 3cb14cc7-8d18-4297-833c-05b0988ea34e + Name: Ambient light + Components: + 055a0e18643d9b0b976c9cc0b1fff92a: !TransformComponent + Id: 56e85624-3f61-4c79-a480-0e9c245c2b29 + Position: {X: 0.0, Y: 0.0, Z: 0.0} + Rotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0} + Scale: {X: 1.0, Y: 1.0, Z: 1.0} + Children: {} + 6836afe6382702a77272903fb605a7fc: !LightComponent + Id: 2a92935c-26e7-44dd-89e6-f5f94e76ddea + Type: !LightAmbient + Color: !ColorRgbProvider + Value: {R: 1.0, G: 1.0, B: 1.0} + Intensity: 0.25 + - Entity: + Id: 8d346802-8fe6-4d18-957c-c6dd8404ea84 + Name: Camera + Components: + 0b5d8ce12fcc7ca2525fabc42a2ad694: !TransformComponent + Id: 1f13a162-81b0-4c11-92d1-b3aa99419e70 + Position: {X: 2.6, Y: 0.6, Z: -1.0} + Rotation: {X: 0.0, Y: 0.829037547, Z: 0.0, W: 0.5591929} + Scale: {X: 1.0, Y: 1.0, Z: 1.0} + Children: {} + 38edbf9cc2da98344d46080a3315ea6e: !CameraComponent + Id: ad927bbe-0362-46f3-bf19-c11527de8a86 + Name: null + Projection: Perspective + FarClipPlane: 2048.0 + Slot: dce133b3-fc2c-4978-8f3c-8a7bc66947f0 + 84e3e1914c52c7d890fe96ba860bf21f: !NEWorld.BasicCameraController,NEWorld + Id: 0ae3e97b-49cb-4d05-983b-e3c306612767 + KeyboardMovementSpeed: {X: 5.0, Y: 5.0, Z: 5.0} + TouchMovementSpeed: {X: 40.0, Y: 40.0, Z: 20.0} + SpeedFactor: 5.0 + KeyboardRotationSpeed: {X: 3.0, Y: 3.0} + MouseRotationSpeed: {X: 90.0, Y: 60.0} + TouchRotationSpeed: {X: 60.0, Y: 40.0} + - Entity: + Id: b1058700-11a7-4296-bb63-2fefdb5f28b7 + Name: Skybox + Components: + b651e16182f7eb9a4ca2a00d05f58bf0: !TransformComponent + Id: 3b9b0222-d576-43da-ab44-472344780fd3 + Position: {X: 0.0, Y: 2.0, Z: -2.0} + Rotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0} + Scale: {X: 1.0, Y: 1.0, Z: 1.0} + Children: {} + b770b44f804ee2b92e0cf2e21cb516aa: !BackgroundComponent + Id: 9a17a387-5b6c-4dde-9185-771f3dd4b737 + Texture: d5bf7b66-7df1-475b-8721-f4755a9c6f96:Skybox texture + - Entity: + Id: b1588acf-0fcd-4377-bf5d-6c81dcf34484 + Name: MainScript + Components: + 357a6c8e8c9612c3b0cb2f1e11a2c6f7: !TransformComponent + Id: 1d035ffe-fc7c-4f08-8a73-a80b9427ffb4 + Position: {X: 0.0, Y: 0.0, Z: 0.0} + Rotation: {X: 0.0, Y: 0.0, Z: 0.0, W: 1.0} + Scale: {X: 1.0, Y: 1.0, Z: 1.0} + Children: {} + d80b4d8f8e4c7517fb33931ae02a1875: !NEWorld.MainScript,NEWorld + Id: ad46feb5-e564-418b-8912-d79cfbc7a12d + Material: 9751d0fa-3d67-4760-ac85-9d209a2d12cb:VoxelMaterial + MaterialTransparent: 9b99a21d-5934-44d8-8267-6ae0a64d4ff1:VoxelMaterialTransparent diff --git a/NEWorld/Assets/Skybox texture.xktex b/NEWorld/Assets/Skybox texture.xktex new file mode 100644 index 0000000..18f3c7b --- /dev/null +++ b/NEWorld/Assets/Skybox texture.xktex @@ -0,0 +1,9 @@ +!Texture +Id: d5bf7b66-7df1-475b-8721-f4755a9c6f96 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Source: !file ../Resources/skybox_texture_ldr.dds +IsCompressed: false +Type: !ColorTextureType + UseSRgbSampling: false + ColorKeyColor: {R: 255, G: 0, B: 255, A: 255} diff --git a/NEWorld/Assets/Skybox.xksky b/NEWorld/Assets/Skybox.xksky new file mode 100644 index 0000000..5d4e50f --- /dev/null +++ b/NEWorld/Assets/Skybox.xksky @@ -0,0 +1,5 @@ +!SkyboxAsset +Id: 1472c9e4-c26d-4e13-b2b6-01222389e22b +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +CubeMap: d5bf7b66-7df1-475b-8721-f4755a9c6f96:Skybox texture diff --git a/NEWorld/Assets/VoxelMaterial.xkmat b/NEWorld/Assets/VoxelMaterial.xkmat new file mode 100644 index 0000000..5f94f8d --- /dev/null +++ b/NEWorld/Assets/VoxelMaterial.xkmat @@ -0,0 +1,14 @@ +!MaterialAsset +Id: 9751d0fa-3d67-4760-ac85-9d209a2d12cb +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Attributes: + Diffuse: !MaterialDiffuseMapFeature + DiffuseMap: !ComputeShaderClassColor + MixinReference: VertexTextureTerrain + Generics: {} + CompositionNodes: {} + DiffuseModel: !MaterialDiffuseLambertModelFeature {} + Overrides: + UVScale: {X: 1.0, Y: 1.0} +Layers: {} diff --git a/NEWorld/Assets/VoxelMaterialTransparent.xkmat b/NEWorld/Assets/VoxelMaterialTransparent.xkmat new file mode 100644 index 0000000..5076642 --- /dev/null +++ b/NEWorld/Assets/VoxelMaterialTransparent.xkmat @@ -0,0 +1,19 @@ +!MaterialAsset +Id: 9b99a21d-5934-44d8-8267-6ae0a64d4ff1 +SerializedVersion: {Xenko: 2.0.0.0} +Tags: [] +Attributes: + Diffuse: !MaterialDiffuseMapFeature + DiffuseMap: !ComputeShaderClassColor + MixinReference: VertexTextureTerrain + Generics: {} + CompositionNodes: {} + DiffuseModel: !MaterialDiffuseLambertModelFeature {} + Transparency: !MaterialTransparencyBlendFeature + Alpha: !ComputeFloat + Value: 1.0 + Tint: !ComputeColor + Value: {R: 1.0, G: 1.0, B: 1.0, A: 1.0} + Overrides: + UVScale: {X: 1.0, Y: 1.0} +Layers: {} diff --git a/NEWorld/BasicCameraController.cs b/NEWorld/BasicCameraController.cs new file mode 100644 index 0000000..d23834a --- /dev/null +++ b/NEWorld/BasicCameraController.cs @@ -0,0 +1,176 @@ +// +// NEWorld/NEWorld: BasicCameraController.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System; +using Xenko.Core; +using Xenko.Core.Mathematics; +using Xenko.Engine; +using Xenko.Input; + +namespace NEWorld +{ + /// + /// A script that allows to move and rotate an entity through keyboard, mouse and touch input to provide basic camera + /// navigation. + /// + /// + /// The entity can be moved using W, A, S, D, Q and E, arrow keys or dragging/scaling using multi-touch. + /// Rotation is achieved using the Numpad, the mouse while holding the right mouse button, or dragging using + /// single-touch. + /// + public class BasicCameraController : SyncScript + { + private const float MaximumPitch = MathUtil.PiOverTwo * 0.99f; + private float pitch; + private Vector3 translation; + + private Vector3 upVector; + private float yaw; + + public Vector3 KeyboardMovementSpeed { get; set; } = new Vector3(5.0f); + + public Vector3 TouchMovementSpeed { get; set; } = new Vector3(40, 40, 20); + + public float SpeedFactor { get; set; } = 5.0f; + + public Vector2 KeyboardRotationSpeed { get; set; } = new Vector2(3.0f); + + public Vector2 MouseRotationSpeed { get; set; } = new Vector2(90.0f, 60.0f); + + public Vector2 TouchRotationSpeed { get; set; } = new Vector2(60.0f, 40.0f); + + public override void Start() + { + base.Start(); + + // Default up-direction + upVector = Vector3.UnitY; + + // Configure touch input + if (!Platform.IsWindowsDesktop) + { + Input.Gestures.Add(new GestureConfigDrag()); + Input.Gestures.Add(new GestureConfigComposite()); + } + } + + public override void Update() + { + ProcessInput(); + UpdateTransform(); + } + + private void ProcessInput() + { + translation = Vector3.Zero; + yaw = 0; + pitch = 0; + + // Move with keyboard + if (Input.IsKeyDown(Keys.W) || Input.IsKeyDown(Keys.Up)) + translation.Z = -KeyboardMovementSpeed.Z; + else if (Input.IsKeyDown(Keys.S) || Input.IsKeyDown(Keys.Down)) translation.Z = KeyboardMovementSpeed.Z; + + if (Input.IsKeyDown(Keys.A) || Input.IsKeyDown(Keys.Left)) + translation.X = -KeyboardMovementSpeed.X; + else if (Input.IsKeyDown(Keys.D) || Input.IsKeyDown(Keys.Right)) translation.X = KeyboardMovementSpeed.X; + + if (Input.IsKeyDown(Keys.Q)) + translation.Y = -KeyboardMovementSpeed.Y; + else if (Input.IsKeyDown(Keys.E)) translation.Y = KeyboardMovementSpeed.Y; + + // Alternative translation speed + if (Input.IsKeyDown(Keys.LeftShift) || Input.IsKeyDown(Keys.RightShift)) translation *= SpeedFactor; + + // Rotate with keyboard + if (Input.IsKeyDown(Keys.NumPad2)) + pitch = KeyboardRotationSpeed.X; + else if (Input.IsKeyDown(Keys.NumPad8)) pitch = -KeyboardRotationSpeed.X; + + if (Input.IsKeyDown(Keys.NumPad4)) + yaw = KeyboardRotationSpeed.Y; + else if (Input.IsKeyDown(Keys.NumPad6)) yaw = -KeyboardRotationSpeed.Y; + + // Rotate with mouse + if (Input.IsMouseButtonDown(MouseButton.Right)) + { + Input.LockMousePosition(); + Game.IsMouseVisible = false; + + yaw = -Input.MouseDelta.X * MouseRotationSpeed.X; + pitch = -Input.MouseDelta.Y * MouseRotationSpeed.Y; + } + else + { + Input.UnlockMousePosition(); + Game.IsMouseVisible = true; + } + + // Handle gestures + foreach (var gestureEvent in Input.GestureEvents) + switch (gestureEvent.Type) + { + // Rotate by dragging + case GestureType.Drag: + var drag = (GestureEventDrag) gestureEvent; + var dragDistance = drag.DeltaTranslation; + yaw = -dragDistance.X * TouchRotationSpeed.X; + pitch = -dragDistance.Y * TouchRotationSpeed.Y; + break; + + // Move along z-axis by scaling and in xy-plane by multi-touch dragging + case GestureType.Composite: + var composite = (GestureEventComposite) gestureEvent; + translation.X = -composite.DeltaTranslation.X * TouchMovementSpeed.X; + translation.Y = -composite.DeltaTranslation.Y * TouchMovementSpeed.Y; + translation.Z = -(float) Math.Log(composite.DeltaScale + 1) * TouchMovementSpeed.Z; + break; + } + } + + private void UpdateTransform() + { + var elapsedTime = (float) Game.UpdateTime.Elapsed.TotalSeconds; + + translation *= elapsedTime; + yaw *= elapsedTime; + pitch *= elapsedTime; + + // Get the local coordinate system + var rotation = Matrix.RotationQuaternion(Entity.Transform.Rotation); + + // Enforce the global up-vector by adjusting the local x-axis + var right = Vector3.Cross(rotation.Forward, upVector); + var up = Vector3.Cross(right, rotation.Forward); + + // Stabilize + right.Normalize(); + up.Normalize(); + + // Adjust pitch. Prevent it from exceeding up and down facing. Stabilize edge cases. + var currentPitch = MathUtil.PiOverTwo - (float) Math.Acos(Vector3.Dot(rotation.Forward, upVector)); + pitch = MathUtil.Clamp(currentPitch + pitch, -MaximumPitch, MaximumPitch) - currentPitch; + + // Move in local coordinates + Entity.Transform.Position += Vector3.TransformCoordinate(translation, rotation); + + // Yaw around global up-vector, pitch and roll in local space + Entity.Transform.Rotation *= Quaternion.RotationAxis(right, pitch) * Quaternion.RotationAxis(upVector, yaw); + } + } +} \ No newline at end of file diff --git a/NEWorld/Effects/VertexTextureTerrain.cs b/NEWorld/Effects/VertexTextureTerrain.cs new file mode 100644 index 0000000..ef0070d --- /dev/null +++ b/NEWorld/Effects/VertexTextureTerrain.cs @@ -0,0 +1,25 @@ +// +// Do not edit this file yourself! +// +// This code was generated by Xenko Shader Mixin Code Generator. +// To generate it yourself, please install Xenko.VisualStudio.Package .vsix +// and re-save the associated .xkfx. +// + +using System; +using Xenko.Core; +using Xenko.Rendering; +using Xenko.Graphics; +using Xenko.Shaders; +using Xenko.Core.Mathematics; +using Buffer = Xenko.Graphics.Buffer; + +namespace Xenko.Rendering +{ + public static partial class VertexTextureTerrainKeys + { + public static readonly ValueParameterKey TexturePerLine = ParameterKeys.NewValue(); + public static readonly ObjectParameterKey MeshTextureSampler = ParameterKeys.NewObject(); + public static readonly ObjectParameterKey Almg = ParameterKeys.NewObject(); + } +} diff --git a/NEWorld/Effects/VertexTextureTerrain.xksl b/NEWorld/Effects/VertexTextureTerrain.xksl new file mode 100644 index 0000000..550fd7f --- /dev/null +++ b/NEWorld/Effects/VertexTextureTerrain.xksl @@ -0,0 +1,41 @@ +shader VertexTextureTerrain: ComputeColor, TransformationBase +{ + //(cbuffer for values, and rgroup for textures) + cbuffer PerMaterial { + stage float TexturePerLine; + } + rgroup PerMaterial { + SamplerState MeshTextureSampler + { + Filter = MIN_MAG_MIP_POINT; + AddressU = Wrap; + AddressV = Wrap; + }; + stage Texture2D Almg; + } + stage stream uint2 Pack: COLOR0; + stage stream float4 Vertex : POSITION; + stage stream float2 TexCoord : TEXCOORD0; + stage stream float3 Normal : NORMAL; + stage stream float Shade; + + override stage void VSMain() + { + uint high = streams.Pack.x, low = streams.Pack.y; + streams.Vertex = float4((high >> 16) & 0xFF, (high >> 8) & 0xFF, high & 0xFF, 1); + streams.TexCoord = float2((low >> 8) & 0xFF, low & 0xFF) / TexturePerLine; + streams.Shade = ((float)(((high >> 24) & 0xFF) + 1)) * 0.0625f; + static const float3 Normals[6] = { + float3(1, 0, 0), float3(-1, 0, 0), + float3(0, 1, 0), float3(0, -1, 0), + float3(0, 0, 1), float3(0, 0, -1), + }; + streams.Normal = Normals[(low >> 16) & 0xFF]; + base.VSMain(); + } + + override float4 Compute() + { + return float4(streams.Shade, streams.Shade, streams.Shade, 1.0f) * Almg.Sample(MeshTextureSampler, streams.TexCoord); + } +}; diff --git a/NEWorld/GameScene.cs b/NEWorld/GameScene.cs deleted file mode 100644 index a2a26ad..0000000 --- a/NEWorld/GameScene.cs +++ /dev/null @@ -1,340 +0,0 @@ -// -// NEWorld: GameScene.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using System.Threading; -using Core; -using Core.Math; -using Core.Utilities; -using Game; -using Game.Network; -using Game.Terrain; -using Game.World; -using NEWorld.Renderer; -using OpenGL; -using SDL2; -using static NuklearSharp.Nuklear; - -namespace NEWorld -{ - public class GameScene - { - private class PutBlockTask : IReadWriteTask - { - public PutBlockTask(uint worldId, Vec3 blockPosition, ushort blockId) - { - _worldId = worldId; - _blockPosition = blockPosition; - _blockId = blockId; - } - - public void Task(ChunkService srv) - { - srv.Worlds.Get(_worldId).SetBlock(ref _blockPosition, new BlockData(_blockId)); - } - - public IReadWriteTask Clone() => (IReadWriteTask) MemberwiseClone(); - - private readonly uint _worldId; - private Vec3 _blockPosition; - private readonly ushort _blockId; - } - - private class PlayerControlTask : IReadOnlyTask - { - private const double SelectDistance = 5.0; - private const double SelectPrecision = 200.0; - - public PlayerControlTask(Player player) - { - _player = player; - } - - public unsafe void Task(ChunkService cs) - { - const double speed = 0.1; - - // TODO: Read keys from the configuration file - var state = Window.GetKeyBoardState(); - if (state[(int) SDL.SDL_Scancode.SDL_SCANCODE_UP] != 0) - _player.AccelerateRotation(new Vec3(1, 0.0, 0.0)); - if (state[(int) SDL.SDL_Scancode.SDL_SCANCODE_DOWN] != 0 && _player.Rotation.X > -90) - _player.AccelerateRotation(new Vec3(-1, 0.0, 0.0)); - if (state[(int) SDL.SDL_Scancode.SDL_SCANCODE_RIGHT] != 0) - _player.AccelerateRotation(new Vec3(0.0, -1, 0.0)); - if (state[(int) SDL.SDL_Scancode.SDL_SCANCODE_LEFT] != 0) - _player.AccelerateRotation(new Vec3(0.0, 1, 0.0)); - if (state[(int) SDL.SDL_Scancode.SDL_SCANCODE_W] != 0) - _player.Accelerate(new Vec3(0.0, 0.0, -speed)); - if (state[(int) SDL.SDL_Scancode.SDL_SCANCODE_S] != 0) - _player.Accelerate(new Vec3(0.0, 0.0, speed)); - if (state[(int) SDL.SDL_Scancode.SDL_SCANCODE_A] != 0) - _player.Accelerate(new Vec3(-speed, 0.0, 0.0)); - if (state[(int) SDL.SDL_Scancode.SDL_SCANCODE_D] != 0) - _player.Accelerate(new Vec3(speed, 0.0, 0.0)); - if (state[(int) SDL.SDL_Scancode.SDL_SCANCODE_E] != 0) - _player.Accelerate(new Vec3(0.0, 0.0, -speed * 10)); - if (state[(int) SDL.SDL_Scancode.SDL_SCANCODE_SPACE] != 0) - _player.Accelerate(new Vec3(0.0, 2 * speed, 0.0)); - _player.Accelerate(new Vec3(0.0, -2 * speed, 0.0)); - - // Handle left-click events - HandleLeftClickEvent(cs); - // mGUIWidgets.update(); - } - - private void HandleLeftClickEvent(ChunkService cs) - { - if (!Window.GetInstance().GetMouseMotion().Left) return; - // Selection - var world = cs.Worlds.Get(_player.WorldId); - var trans = new Mat4D(1.0f); - var position = _player.Position; - var rotation = _player.Rotation; - trans *= Mat4D.Rotation(rotation.Y, new Vec3(0.0, 1.0, 0.0)); - trans *= Mat4D.Rotation(rotation.X, new Vec3(1.0, 0.0, 0.0)); - trans *= Mat4D.Rotation(rotation.Z, new Vec3(0.0, 0.0, 1.0)); - var dir = trans.Transform(new Vec3(0.0, 0.0, -1.0), 0.0f).Key; - dir.Normalize(); - - for (double i = 0.0f; i < SelectDistance; i += 1.0f / SelectPrecision) - { - var pos = position + dir * i; - var blockPos = new Vec3((int) Math.Floor(pos.X), (int) Math.Floor(pos.Y), - (int) Math.Floor(pos.Z)); - try - { - if (world.GetBlock(blockPos).Id == 0) continue; - cs.TaskDispatcher.Add(new PutBlockTask(_player.WorldId, blockPos, 0)); - break; - } - catch - { - break; - } - } - } - - public IReadOnlyTask Clone() => (IReadOnlyTask) MemberwiseClone(); - - private readonly Player _player; - } - - private class UpsCounter : IReadOnlyTask - { - public UpsCounter(object updateCounter) => _updateCounter = updateCounter; - - public void Task(ChunkService srv) => Generic.Increase(_updateCounter); - - public IReadOnlyTask Clone() => (IReadOnlyTask) MemberwiseClone(); - - private readonly object _updateCounter; - } - - // GameScene update frequency - public const int UpdateFrequency = 30; - - private static bool IsClient() - { - return true; - } - - public GameScene(string name, Window window) - { - _window = window; - _player = new Player(0); - _guiWidgets = new WidgetManager(_window.GetNkContext()); - - _player.Position = new Vec3(-16.0, 48.0, 32.0); - _player.Rotation = new Vec3(-45.0, -22.5, 0.0); - Window.LockCursor(); - - if (IsClient()) - { - Singleton.Instance.IsAuthority = false; - } - else - { - // Initialize server - var server = Services.Get("Game.Server"); - server.Enable(31111); - server.Run(); - } - - // Initialize connection - Services.Get("Game.Client").Enable("127.0.0.1", 31111); - - _currentWorld = Singleton.Instance.Worlds.Get(RequestWorld()); - _worldRenderer = new WorldRenderer(_currentWorld, 4); - - // Initialize update events - _currentWorld.RegisterChunkTasks(Singleton.Instance, _player); - _worldRenderer.RegisterTask(Singleton.Instance, _player); - Singleton.Instance.TaskDispatcher.AddRegular(new PlayerControlTask(_player)); - Singleton.Instance.TaskDispatcher.AddRegular(new UpsCounter(_upsCounter)); - - // Initialize rendering - _texture = BlockTextures.BuildAndFlush(); - BlockRenderers.FlushTextures(Services.Get("BlockTextures")); - Gl.Enable(Gl.DepthTest); - Gl.DepthFunc(Gl.Lequal); - - // Initialize Widgets - _guiWidgets.Add(new WidgetCallback( - "Debug", nk_rect_(20, 20, 300, 300), - NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE | - NK_WINDOW_CLOSABLE | NK_WINDOW_MINIMIZABLE | NK_WINDOW_TITLE, ctx => - { - if (_rateCounterScheduler.IsDue()) - { - // Update FPS & UPS - _fpsLatest = _fpsCounter; - _upsLatest = (uint) _upsCounter; - _fpsCounter = 0; - Generic.MultiplyBy(ref _upsCounter, 0u); - _rateCounterScheduler.IncreaseTimer(); - } - - ctx.LayoutRowDynamic(15, 1); - ctx.Label($"NEWorld {"0.5.0 Alpha"} ({39})", NK_TEXT_LEFT); - ctx.Label($"FPS {_fpsLatest}, UPS {_upsLatest}", NK_TEXT_LEFT); - ctx.Label($"Position: x {_player.Position.X} y {_player.Position.Y} z {_player.Position.Z}", - NK_TEXT_LEFT); - ctx.Label($"GUI Widgets: {_guiWidgets.Count}", NK_TEXT_LEFT); - ctx.Label($"Chunks Loaded: {_currentWorld.GetChunkCount()}", NK_TEXT_LEFT); - ctx.Label("Modules Loaded: %zu", NK_TEXT_LEFT); - ctx.Label("Update threads workload:", NK_TEXT_LEFT); - var dispatcher = Singleton.Instance.TaskDispatcher; - var threadId = 0; - foreach (var timeReal in dispatcher.TimeUsed) - { - var time = Math.Max(timeReal, 0L); - ctx.Label($"Thread {threadId++}: {time} ms {time / 33.3333}", NK_TEXT_LEFT); - } - - ctx.Label( - $"Regular Tasks: read {dispatcher.GetRegularReadOnlyTaskCount()} write {dispatcher.GetRegularReadWriteTaskCount()}", - NK_TEXT_LEFT); - })); - - Singleton.Instance.TaskDispatcher.Start(); - } - - ~GameScene() - { - Singleton.Instance.TaskDispatcher.Stop(); - } - - public void Render() - { - Singleton.Instance.TaskDispatcher.ProcessRenderTasks(); - - // Camera control by mouse - const double mouseSensitivity = 0.3; - - var mouse = Window.GetInstance().GetMouseMotion(); - _player.AccelerateRotation(new Vec3(-mouse.Y * mouseSensitivity, -mouse.X * mouseSensitivity, 0.0)); - - Gl.ClearColor(0.6f, 0.9f, 1.0f, 1.0f); - Gl.ClearDepth(1.0f); - Gl.Enable(Gl.DepthTest); - Gl.Enable(Gl.CullFace); - Gl.CullFaceOption(Gl.Back); - - var timeDelta = _updateScheduler.GetDeltaTimeMs() / 1000.0 * UpdateFrequency; - if (timeDelta > 1.0) timeDelta = 1.0; - var playerRenderedPosition = _player.Position - _player.PositionDelta * (1.0 - timeDelta); - var playerRenderedRotation = _player.Rotation - _player.RotationDelta * (1.0 - timeDelta); - - _texture.Use(0); - Gl.Clear(Gl.ColorBufferBit | Gl.DepthBufferBit); - Gl.Viewport(0, 0, _window.GetWidth(), _window.GetHeight()); - Matrix.RestoreProjection(); - Matrix.ApplyPerspective(70.0f, (float) _window.GetWidth() / _window.GetHeight(), 0.1f, 3000.0f); - Matrix.ViewRotate((float) -playerRenderedRotation.X, new Vec3(1.0f, 0.0f, 0.0f)); - Matrix.ViewRotate((float) -playerRenderedRotation.Y, new Vec3(0.0f, 1.0f, 0.0f)); - Matrix.ViewRotate((float) -playerRenderedRotation.Z, new Vec3(0.0f, 0.0f, 1.0f)); - Matrix.ViewTranslate(-playerRenderedPosition); - - // Render - _worldRenderer.Render(_player.Position); - // mPlayer.render(); - - Gl.Disable(Gl.DepthTest); - - _guiWidgets.Render(); - - _fpsCounter++; - } - - private uint RequestWorld() - { - // TODO: change this - if (IsClient()) - { - var worldIds = Client.GetAvailableWorldId.Call(); - if (worldIds.Length == 0) - { - throw new Exception("The server didn't response with any valid worlds."); - } - - var worldInfo = Client.GetWorldInfo.Call(worldIds[0]); - - Singleton.Instance.Worlds.Add(worldInfo["name"]); - } - - // It's a simple wait-until-we-have-a-world procedure now. - // But it should be changed into get player information - // and get the world id from it. - while (Singleton.Instance.Worlds.Get(0) == null) - Thread.Yield(); - return 0; - } - - // Local server - private readonly Server _server; - - // Window - private readonly Window _window; - - // Texture test - private readonly Texture _texture; - - // Player - private readonly Player _player; - - // Widget manager - private readonly WidgetManager _guiWidgets; - - // Update scheduler - private RateController _updateScheduler = new RateController(UpdateFrequency); - - // Rate counters - private uint _fpsCounter, _fpsLatest, _upsLatest; - private object _upsCounter = new uint(); - - // Current world - private readonly World _currentWorld; - - // World renderer - private readonly WorldRenderer _worldRenderer; - - private RateController _rateCounterScheduler = new RateController(1); - } -} \ No newline at end of file diff --git a/NEWorld/MainScript.cs b/NEWorld/MainScript.cs new file mode 100644 index 0000000..db0f802 --- /dev/null +++ b/NEWorld/MainScript.cs @@ -0,0 +1,239 @@ +// +// NEWorld/NEWorld: MainScript.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System; +using System.Threading; +using System.Threading.Tasks; +using Core; +using Game; +using Game.Network; +using Game.Terrain; +using Game.World; +using NEWorld.Renderer; +using Xenko.Core.Diagnostics; +using Xenko.Core.Mathematics; +using Xenko.Core.Serialization.Contents; +using Xenko.Engine; +using Xenko.Games; +using Xenko.Graphics; +using Xenko.Rendering; +using Buffer = Xenko.Graphics.Buffer; + +namespace NEWorld +{ + public static class Context + { + private static IGame _game; + + public static readonly VertexDeclaration VertexLayout = new VertexDeclaration( + VertexElement.Color(PixelFormat.R32G32_UInt) + ); + + public static IGame Game + { + get => _game; + set + { + _game = value; + IndexBuffer = new IndexBufferBinding(IndexBufferBuilder.Build(), true, 262144 / 2 * 3); + } + } + + public static Material Material { get; set; } + + public static Material MaterialTransparent { get; set; } + + public static Scene OperatingScene { get; set; } + + public static GraphicsDevice GraphicsDevice => Game.GraphicsDevice; + + public static GraphicsContext GraphicsContext => Game.GraphicsContext; + + public static IndexBufferBinding IndexBuffer { get; private set; } + + public static ContentManager Content { get; set; } + + public static RenderDrawContext RdwContext { get; set; } + } + + public static class IndexBufferBuilder + { + public static Buffer Build() + { + var idx = new int[262144 / 2 * 3]; + var cnt = 0; + for (var i = 0; i < 262144 / 4; ++i) + { + var b = i * 4; + idx[cnt++] = b + 2; + idx[cnt++] = b + 1; + idx[cnt++] = b; + idx[cnt++] = b + 3; + idx[cnt++] = b + 2; + idx[cnt++] = b; + } + + return Buffer.Index.New(Context.GraphicsDevice, idx); + } + } + + public class MainScript : SyncScript + { + // Current world + private World currentWorld; + public Material Material; + public Material MaterialTransparent; + // Player + private Player player; + + private RdWorld rdWorld; + + // Local server + private Server server; + + private void InitializeModules() + { + Modules.Load("Main"); + } + + private void InitializeContext() + { + Context.Game = Game; + Context.Content = Content; + Context.Material = Material; + Context.MaterialTransparent = MaterialTransparent; + Context.OperatingScene = Entity.Scene; + LogPort.Logger = Log; + Context.RdwContext = new RenderDrawContext(Services, RenderContext.GetShared(Services), Game.GraphicsContext); + Log.ActivateLog(LogMessageType.Debug); + EventBus.Broadcast(this, new GameRenderPrepareEvent()); + } + + private void EstablishChunkService() + { + if (IsClient()) + { + ChunkService.IsAuthority = false; + } + else + { + // Initialize server + server = Core.Services.Get("Game.Server"); + server.Enable(31111); + server.Run(); + } + } + + private async Task EstablishGameConnection() + { + await Core.Services.Get("Game.Client").Enable("127.0.0.1", 31111); + await Client.GetStaticChunkIds.Call(); + EventBus.Broadcast(this, new GameLoadEvent()); + } + + private void LoadPlayer() + { + player = new Player(0) + { + Position = new Double3(-16.0, 48.0, 32.0), Rotation = new Double3(-45.0, -22.5, 0.0) + }; + } + + private async Task EnterCurrentWorld() + { + currentWorld = ChunkService.Worlds.Get(await RequestWorld()); + currentWorld.RegisterChunkTasks(player); + ChunkService.EnableDispatcher(); + } + + private void StartTerrainRenderService() + { + rdWorld = new RdWorld(currentWorld, player, 4); + } + + private async void Initialize() + { + InitializeContext(); + InitializeModules(); + LoadTextures(); + EstablishChunkService(); + await EstablishGameConnection(); + LoadPlayer(); + await EnterCurrentWorld(); + StartTerrainRenderService(); + } + + private void LoadTextures() + { + var texture = RdTextures.FlushTextures(); + BlockRenderers.FlushTextures(Core.Services.Get("BlockTextures")); + Material.Passes[0].Parameters.Set(VertexTextureTerrainKeys.Almg, texture); + Material.Passes[0].Parameters.Set(VertexTextureTerrainKeys.TexturePerLine, RdTextures.TexturesPerLine); + MaterialTransparent.Passes[0].Parameters.Set(VertexTextureTerrainKeys.Almg, texture); + MaterialTransparent.Passes[0].Parameters.Set(VertexTextureTerrainKeys.TexturePerLine, RdTextures.TexturesPerLine); + } + + public override void Start() + { + Initialize(); + } + + public override void Cancel() + { + TearDown(); + } + + private void TearDown() + { + Core.Services.Get("Game.TaskDispatcher").Reset(); + EventBus.Broadcast(this, new GameUnloadEvent()); + EventBus.Broadcast(this, new GameRenderFinalizeEvent()); + } + + private static async Task RequestWorld() + { + // TODO: change this + if (IsClient()) + { + var worldIds = await Client.GetAvailableWorldId.Call(); + if (worldIds.Length == 0) throw new Exception("The server didn't response with any valid worlds."); + + var worldInfo = await Client.GetWorldInfo.Call(worldIds[0]); + + ChunkService.Worlds.Add(worldInfo["name"]); + } + + // It's a simple wait-until-we-have-a-world procedure now. + // But it should be changed into get player information + // and get the world id from it. + while (ChunkService.Worlds.Get(0) == null) + Thread.Yield(); + return 0; + } + + private static bool IsClient() + { + return true; + } + + public override void Update() + { + ChunkService.TaskDispatcher.ProcessRenderTasks(); + } + } +} \ No newline at end of file diff --git a/NEWorld/NEWorld.csproj b/NEWorld/NEWorld.csproj index 03c40a3..9ccc28f 100644 --- a/NEWorld/NEWorld.csproj +++ b/NEWorld/NEWorld.csproj @@ -1,89 +1,35 @@ - - - + - Debug - AnyCPU - {553FF3F0-7F88-4821-BDA1-282CE6B7DDE7} - Exe - Properties - NEWorld - NEWorld - v4.7.1 - 512 + netstandard2.0 - - x64 - true - full - false - ..\bin\Debug\ - DEBUG;TRACE - prompt - 4 + true - false - - x64 - pdbonly - true - ..\bin\Release\ - TRACE - prompt - 4 + true - false - - ..\packages\NuklearSharp.0.2.0.9\lib\netstandard1.1\NuklearSharp.dll - True - - - SDL2-CS.dll - - - + + + + - - - - - - - - - - + + + - + + True + True + VertexTextureTerrain.xksl + - - {ecb0e309-625f-4a24-926d-d1d23c1b7693} - Core - - - {f83c4945-abff-4856-94a3-d0d31c976a11} - Game - - - {4eb6c361-3e67-4a4e-8b2b-9c8d29d8f852} - OpenGL - + + XenkoShaderKeyGenerator + VertexTextureTerrain.cs + - - - - - \ No newline at end of file diff --git a/NEWorld/NEWorld.xkpkg b/NEWorld/NEWorld.xkpkg new file mode 100644 index 0000000..1d76998 --- /dev/null +++ b/NEWorld/NEWorld.xkpkg @@ -0,0 +1,18 @@ +!Package +SerializedVersion: {Assets: 3.1.0.0} +Meta: + Name: NEWorld + Version: 1.0.0 + Authors: [] + Owners: [] + Dependencies: null +AssetFolders: + - Path: !dir Assets + - Path: !dir Effects +ResourceFolders: + - !dir Resources +OutputGroupDirectories: {} +ExplicitFolders: [] +Bundles: [] +TemplateFolders: [] +RootAssets: [] diff --git a/NEWorld/NkSdl.cs b/NEWorld/NkSdl.cs deleted file mode 100644 index ede90f3..0000000 --- a/NEWorld/NkSdl.cs +++ /dev/null @@ -1,353 +0,0 @@ -// -// NEWorld: NkSdl.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using Core; -using Core.Math; -using NuklearSharp; -using OpenGL; -using SDL2; -using static NuklearSharp.Nuklear; - -namespace NEWorld -{ - public class NkSdl : BaseContext, IDisposable - { - public unsafe NkSdl(IntPtr win) - { - _win = win; - Ctx.clip.copy = ClipBoardCopy; - Ctx.clip.paste = ClipboardPaste; - _prog = new Program(); - using (Shader vertex = new Shader(Gl.VertexShader, @" -#version 450 core -layout(shared, row_major) uniform; -layout (std140, binding = 0) uniform MVP { mat4 ProjMtx; }; -layout (location = 0) in vec2 Position; -layout (location = 1) in vec2 TexCoord; -layout (location = 2) in vec4 Color; -out vec2 Frag_UV; -out vec4 Frag_Color; -void main() { - Frag_UV = TexCoord; - Frag_Color = Color; - gl_Position = ProjMtx * vec4(Position.xy, 0, 1); -}"), - fragment = new Shader(Gl.FragmentShader, @" -#version 450 core -precision mediump float; -layout (binding = 1) uniform sampler2D Texture; -in vec2 Frag_UV; -in vec4 Frag_Color; -out vec4 Out_Color; -void main(){ - Out_Color = Frag_Color * texture(Texture, Frag_UV.st); -}")) - _prog.Link(new[] {vertex, fragment}); - /* buffer setup */ - _ubo = new DataBuffer(16 * sizeof(float)); - _vao = new VertexArray(); - - _vao.EnableAttrib(0); - _vao.EnableAttrib(1); - _vao.EnableAttrib(2); - _vao.AttribFormat(0, 2, Gl.Float, false, 0); - _vao.AttribFormat(1, 2, Gl.Float, false, 2 * sizeof(float)); - _vao.AttribFormat(2, 4, Gl.UnsignedByte, true, 4 * sizeof(float)); - _vao.AttribBinding(0, 0); - _vao.AttribBinding(1, 0); - _vao.AttribBinding(2, 0); - ConvertConfig.vertex_layout = VertexLayout; - ConvertConfig.vertex_size = 4 * sizeof(float) + 4; - _textures = new List(); - _atlas = new FontAtlasWrapper(this); - var defaultFont = _atlas.AddDefaultFont(16.0f); - _atlas.Bake(); - SetFont(defaultFont); - StyleDefault(); - CreateMaskTexture(); - } - - private void CreateMaskTexture() - { - var newTex = new Texture(1, PixelInternalFormats.Rgba8, new Vec2(1, 1)); - newTex.Image(0, new Rect(0, 0, 1, 1), PixelTypes.Rgba, PixelDataFormats.UnsignedByte, - new byte[] {255, 255, 255, 255}); - _textures.Add(newTex); - _mask = newTex.Raw(); - } - - private static readonly nk_draw_vertex_layout_element[] VertexLayout = - { - new nk_draw_vertex_layout_element - { - attribute = NK_VERTEX_POSITION, - format = NK_FORMAT_FLOAT, - offset = 0 - }, - new nk_draw_vertex_layout_element - { - attribute = NK_VERTEX_COLOR, - format = NK_FORMAT_R8G8B8A8, - offset = 4 * sizeof(float) - }, - new nk_draw_vertex_layout_element - { - attribute = NK_VERTEX_TEXCOORD, - format = NK_FORMAT_FLOAT, - offset = 2 * sizeof(float) - }, - new nk_draw_vertex_layout_element - { - attribute = NK_VERTEX_ATTRIBUTE_COUNT - } - }; - - - private static unsafe void ClipBoardCopy(nk_handle handle, char* c, int length) => - Gl.Utf8ToManaged((IntPtr) c); - - private static unsafe void ClipboardPaste(nk_handle usr, nk_text_edit edit) - { - var text = Gl.Utf8ToNative(SDL.SDL_GetClipboardText()); - var ptr = Marshal.AllocHGlobal(text.Length * sizeof(byte)); - Marshal.Copy(text, 0, ptr, text.Length); - nk_textedit_paste(edit, (char*) ptr, nk_strlen((char*) ptr)); - Marshal.FreeHGlobal(ptr); - } - - public unsafe void HandleEvent(ref SDL.SDL_Event evt) - { - switch (evt.type) - { - case SDL.SDL_EventType.SDL_KEYUP: - case SDL.SDL_EventType.SDL_KEYDOWN: - HandleKeyDownEvent(evt); - return; - case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN: - case SDL.SDL_EventType.SDL_MOUSEBUTTONUP: - { - /* mouse button */ - var down = evt.type == SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN; - int x = evt.button.x, y = evt.button.y; - if (evt.button.button == SDL.SDL_BUTTON_LEFT) - { - if (evt.button.clicks > 1) - InputButton(NK_BUTTON_DOUBLE, x, y, down); - InputButton(NK_BUTTON_LEFT, x, y, down); - } - else if (evt.button.button == SDL.SDL_BUTTON_MIDDLE) - InputButton(NK_BUTTON_MIDDLE, x, y, down); - else if (evt.button.button == SDL.SDL_BUTTON_RIGHT) - InputButton(NK_BUTTON_RIGHT, x, y, down); - - return; - } - case SDL.SDL_EventType.SDL_MOUSEMOTION: - /* mouse motion */ - if (Ctx.input.mouse.grabbed != 0) - { - int x = (int) Ctx.input.mouse.prev.x, y = (int) Ctx.input.mouse.prev.y; - InputMotion(x + evt.motion.xrel, y + evt.motion.yrel); - } - else InputMotion(evt.motion.x, evt.motion.y); - - return; - case SDL.SDL_EventType.SDL_TEXTINPUT: - fixed (byte* ptr = evt.text.text) - InputGlyph(Gl.Utf8ToManaged((IntPtr) ptr)); - return; - case SDL.SDL_EventType.SDL_MOUSEWHEEL: - /* mouse wheel */ - nk_vec2 scroll; - scroll.x = evt.wheel.x; - scroll.y = evt.wheel.y; - InputScroll(scroll); - return; - } - } - - private unsafe void HandleKeyDownEvent(SDL.SDL_Event evt) - { - /* key events */ - bool down = evt.type == SDL.SDL_EventType.SDL_KEYDOWN; - var state = (byte*) SDL.SDL_GetKeyboardState(out var dummy); - if (state == null) return; - switch (evt.key.keysym.sym) - { - case SDL.SDL_Keycode.SDLK_RSHIFT: - case SDL.SDL_Keycode.SDLK_LSHIFT: - InputKey(NK_KEY_SHIFT, down); - break; - case SDL.SDL_Keycode.SDLK_DELETE: - InputKey(NK_KEY_DEL, down); - break; - case SDL.SDL_Keycode.SDLK_RETURN: - InputKey(NK_KEY_ENTER, down); - break; - case SDL.SDL_Keycode.SDLK_TAB: - InputKey(NK_KEY_TAB, down); - break; - case SDL.SDL_Keycode.SDLK_BACKSPACE: - InputKey(NK_KEY_BACKSPACE, down); - break; - case SDL.SDL_Keycode.SDLK_HOME: - InputKey(NK_KEY_TEXT_START, down); - InputKey(NK_KEY_SCROLL_START, down); - break; - case SDL.SDL_Keycode.SDLK_END: - InputKey(NK_KEY_TEXT_END, down); - InputKey(NK_KEY_SCROLL_END, down); - break; - case SDL.SDL_Keycode.SDLK_PAGEDOWN: - InputKey(NK_KEY_SCROLL_DOWN, down); - break; - case SDL.SDL_Keycode.SDLK_PAGEUP: - InputKey(NK_KEY_SCROLL_UP, down); - break; - case SDL.SDL_Keycode.SDLK_z: - InputKey(NK_KEY_TEXT_UNDO, down & state[(int) SDL.SDL_Scancode.SDL_SCANCODE_LCTRL] != 0); - break; - case SDL.SDL_Keycode.SDLK_r: - InputKey(NK_KEY_TEXT_REDO, down & state[(int) SDL.SDL_Scancode.SDL_SCANCODE_LCTRL] != 0); - break; - case SDL.SDL_Keycode.SDLK_c: - InputKey(NK_KEY_COPY, down & state[(int) SDL.SDL_Scancode.SDL_SCANCODE_LCTRL] != 0); - break; - case SDL.SDL_Keycode.SDLK_v: - InputKey(NK_KEY_PASTE, down & state[(int) SDL.SDL_Scancode.SDL_SCANCODE_LCTRL] != 0); - break; - case SDL.SDL_Keycode.SDLK_x: - InputKey(NK_KEY_CUT, down & state[(int) SDL.SDL_Scancode.SDL_SCANCODE_LCTRL] != 0); - break; - case SDL.SDL_Keycode.SDLK_b: - InputKey(NK_KEY_TEXT_LINE_START, down & state[(int) SDL.SDL_Scancode.SDL_SCANCODE_LCTRL] != 0); - break; - case SDL.SDL_Keycode.SDLK_e: - InputKey(NK_KEY_TEXT_LINE_END, down & state[(int) SDL.SDL_Scancode.SDL_SCANCODE_LCTRL] != 0); - break; - case SDL.SDL_Keycode.SDLK_UP: - InputKey(NK_KEY_UP, down); - break; - case SDL.SDL_Keycode.SDLK_DOWN: - InputKey(NK_KEY_DOWN, down); - break; - case SDL.SDL_Keycode.SDLK_LEFT when state[(int) SDL.SDL_Scancode.SDL_SCANCODE_LCTRL] != 0: - InputKey(NK_KEY_TEXT_WORD_LEFT, down); - break; - case SDL.SDL_Keycode.SDLK_LEFT: - InputKey(NK_KEY_LEFT, down); - break; - case SDL.SDL_Keycode.SDLK_RIGHT when state[(int) SDL.SDL_Scancode.SDL_SCANCODE_LCTRL] != 0: - InputKey(NK_KEY_TEXT_WORD_RIGHT, down); - break; - case SDL.SDL_Keycode.SDLK_RIGHT: - InputKey(NK_KEY_RIGHT, down); - break; - default: - return; - } - } - - public override int CreateTexture(int width, int height, byte[] data) - { - var newTex = new Texture(1, PixelInternalFormats.Rgba8, new Vec2(width, height)); - newTex.Image(0, new Rect(0, 0, width, height), PixelTypes.Rgba, PixelDataFormats.UnsignedByte, data); - _textures.Add(newTex); - return (int) newTex.Raw(); - } - - protected override void BeginDraw() - { - SDL.SDL_GetWindowSize(_win, out var width, out _height); - SDL.SDL_GL_GetDrawableSize(_win, out var displayWidth, out var displayHeight); - var othro = Mat4F.Ortho(0, width, 0, _height, 0, 1000); - - _scale.x = displayWidth / (float) width; - _scale.y = displayHeight / (float) _height; - - /* setup global state */ - Gl.Viewport(0, 0, displayWidth, displayHeight); - Gl.Enable(Gl.Blend); - Gl.BlendEquation(Gl.FuncAdd); - Gl.BlendFunc(Gl.SrcAlpha, Gl.OneMinusSrcAlpha); - Gl.Disable(Gl.CullFace); - Gl.Disable(Gl.DepthTest); - Gl.Enable(Gl.ScissorTest); - - /* setup program */ - _prog.Use(); - _prog.Uniform(1, 0); - _ubo.DataSection(0, othro.Data); - } - - protected override void SetBuffers(byte[] vertices, ushort[] indices, int indicesCount, int vertexCount) - { - _verts = new DataBuffer(); - _element = new DataBuffer(); - _vao.Use(); - _verts.AllocateWith(vertices, vertexCount * 20); - _element.AllocateWith(indices, indicesCount); - _vao.BindBuffer(0, _verts, 0, 5 * sizeof(float)); - _ubo.BindBase(Gl.UniformBuffer, 0); - _element.Bind(Gl.ElementArrayBuffer); - } - - protected override void Draw(int x, int y, int w, int h, int textureId, int startIndex, int primitiveCount) - { - Texture.UseRaw(0, textureId != 0 ? (uint) textureId : _mask); - //Gl.Scissor((int) (x * _scale.x), (int) ((_height - (y + h)) * _scale.y), - // (int) (w * _scale.x), (int) (h * _scale.y)); - Gl.DrawElements(Gl.Triangles, primitiveCount * 3, Gl.UnsignedShort, (IntPtr) startIndex); - } - - protected override void EndDraw() - { - _verts.Dispose(); - _element.Dispose(); - Gl.Disable(Gl.Blend); - Gl.Disable(Gl.ScissorTest); - } - - private readonly DataBuffer _ubo; - private readonly VertexArray _vao; - private readonly Program _prog; - private readonly IntPtr _win; - private DataBuffer _verts, _element; - private nk_vec2 _scale; - private int _height; - private readonly List _textures; - private readonly FontAtlasWrapper _atlas; - private uint _mask; - - public void Dispose() - { - _ubo?.Dispose(); - _vao?.Dispose(); - _prog?.Dispose(); - _verts?.Dispose(); - _element?.Dispose(); - if (_textures != null) - foreach (var texture in _textures) - texture.Dispose(); - } - } -} \ No newline at end of file diff --git a/NEWorld/Program.cs b/NEWorld/Program.cs deleted file mode 100644 index ac23e87..0000000 --- a/NEWorld/Program.cs +++ /dev/null @@ -1,62 +0,0 @@ -// -// NEWorld: Program.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System.Reflection; -using Core; -using Core.Module; -using SDL2; - -namespace NEWorld -{ - internal class NEWorld - { - public NEWorld() - { - Window.GetInstance("NEWorld", 852, 480); - Services.ScanAssembly(Assembly.Load("Game")); - Services.ScanAssembly(Assembly.GetCallingAssembly()); - Modules.Instance.Load("Main"); - } - - public void Run() - { - var fps = 60; - var shouldLimitFps = true; - var delayPerFrame = (uint) (1000 / fps - 0.5); - var window = Window.GetInstance("NEWorld", 852, 480); - var game = new GameScene("TestWorld", window); - while (!window.ShouldQuit()) - { - // Update - window.PollEvents(); - // Render - game.Render(); - window.SwapBuffers(); - if (shouldLimitFps) - SDL.SDL_Delay(delayPerFrame); - } - } - - public static void Main(string[] args) - { - var instance = new NEWorld(); - instance.Run(); - } - } -} \ No newline at end of file diff --git a/NEWorld/Properties/AssemblyInfo.cs b/NEWorld/Properties/AssemblyInfo.cs deleted file mode 100644 index 7724407..0000000 --- a/NEWorld/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// NEWorld: AssemblyInfo.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NEWorld")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NEWorld")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("553FF3F0-7F88-4821-BDA1-282CE6B7DDE7")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/NEWorld/Renderer/BlockTextures.cs b/NEWorld/Renderer/BlockTextures.cs deleted file mode 100644 index 4e885a8..0000000 --- a/NEWorld/Renderer/BlockTextures.cs +++ /dev/null @@ -1,153 +0,0 @@ -// -// NEWorld: BlockTextures.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using System.Collections.Generic; -using Core; -using Core.Math; -using Core.Utilities; -using Game.Terrain; -using OpenGL; -using SDL2; - -namespace NEWorld.Renderer -{ - [DeclareService("BlockTextures")] - public class BlockTextures : IBlockTextures, IDisposable - { - private unsafe class RawTexture - { - public RawTexture(string filename) => Surface = (SDL.SDL_Surface*) SDL_image.IMG_Load(filename); - - ~RawTexture() => SDL.SDL_FreeSurface((IntPtr) Surface); - - public SDL.SDL_Surface* Surface { get; } - } - - public BlockTextures() - { - SDL_image.IMG_Init(SDL_image.IMG_InitFlags.IMG_INIT_PNG); - } - - public void Dispose() - { - SDL_image.IMG_Quit(); - } - - private static int Capacity() - { - var w = CapacityRaw() / _pixelPerTexture; - return w * w; - } - - private static int CapacityRaw() - { - int cap = 2048; - //glGetIntegerv(GL_MAX_TEXTURE_SIZE, &cap); - return cap; - } - - public static void SetWidthPerTex(int wid) => _pixelPerTexture = wid; - - public static int GetWidthPerTex() => _pixelPerTexture; - - public static unsafe Texture BuildAndFlush() - { - var count = RawTexs.Count; - _texturePerLine = 1 << (int) Math.Ceiling(Math.Log(Math.Ceiling(Math.Sqrt(count))) / Math.Log(2)); - var wid = _texturePerLine * _pixelPerTexture; - - var mask = EndianCheck.BigEndian - ? new uint[] {0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff} - : new uint[] {0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000}; - - var s = (SDL.SDL_Surface*) SDL.SDL_CreateRGBSurface(0, wid, wid, 32, mask[0], mask[1], mask[2], mask[3]); - for (var i = 0; i < count; ++i) - { - var x = i % _texturePerLine; - var y = i / _texturePerLine; - SDL.SDL_Rect r; - r.x = x * _pixelPerTexture; - r.y = y * _pixelPerTexture; - r.w = r.h = _pixelPerTexture; - SDL.SDL_BlitScaled((IntPtr) RawTexs[i].Surface, IntPtr.Zero, (IntPtr) s, (IntPtr) (&r)); - } - - RawTexs.Clear(); - var levels = (int) (Math.Log(_pixelPerTexture) / Math.Log(2)); - var ret = new Texture(levels, PixelInternalFormats.Rgba8, new Vec2(wid, wid)) - { - MinifyingFilter = Texture.Filter.NearestMipmapNearest, - MagnificationFilter = Texture.Filter.Nearest - }; - Build2DMipmaps(ret, wid, wid, (int) (Math.Log(_pixelPerTexture) / Math.Log(2)), (byte*) s->pixels); - return ret; - } - - private static int Align(int x, int al) - { - return x % al == 0 ? x : (x / al + 1) * al; - } - - private static unsafe void Build2DMipmaps(Texture tex, int w, int h, int level, byte* src) - { - var scale = 1; - var cur = new byte[w * h * 4]; - for (var i = 0; i <= level; i++) - { - int curW = w / scale, curH = h / scale; - for (var y = 0; y < curH; y++) - for (var x = 0; x < curW; x++) - for (var col = 0; col < 4; col++) - { - var sum = 0; - for (var yy = 0; yy < scale; yy++) - for (var xx = 0; xx < scale; xx++) - sum += src[(y * scale + yy) * Align(w * 4, 4) + (x * scale + xx) * 4 + col]; - cur[y * Align(curW * 4, 4) + x * 4 + col] = (byte) (sum / (scale * scale)); - } - - tex.Image(i, new Rect(0, 0, curW, curH), PixelTypes.Rgba, PixelDataFormats.Byte, cur); - scale *= 2; - } - } - - public uint Add(string path) - { - if (RawTexs.Count >= Capacity()) - throw new Exception("Too Many Textures"); - RawTexs.Add(new RawTexture(path)); - return (uint) (RawTexs.Count - 1); - } - - public unsafe void GetTexturePos(float* pos, uint id) - { - var percentagePerTexture = 1.0f / _texturePerLine; - var x = id % _texturePerLine; - var y = id / _texturePerLine; - pos[0] = percentagePerTexture * x; - pos[1] = percentagePerTexture * y; - pos[2] = percentagePerTexture * (x + 1); - pos[3] = percentagePerTexture * (y + 1); - } - - private static int _pixelPerTexture = 32, _texturePerLine = 8; - private static readonly List RawTexs = new List(); - } -} \ No newline at end of file diff --git a/NEWorld/Renderer/ChunkRenderer.cs b/NEWorld/Renderer/ChunkRenderer.cs deleted file mode 100644 index 5fb640e..0000000 --- a/NEWorld/Renderer/ChunkRenderer.cs +++ /dev/null @@ -1,144 +0,0 @@ -// -// NEWorld: ChunkRenderer.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using System.Runtime.InteropServices; -using Core.Math; -using Core.Utilities; -using Game.Terrain; -using Game.World; -using OpenGL; - -namespace NEWorld.Renderer -{ - public class VertexBuilder : IVertexBuilder - { - public VertexBuilder(int size) => Data = Marshal.AllocHGlobal(size * sizeof(float)); - - ~VertexBuilder() => Marshal.FreeHGlobal(Data); - - public void AddPrimitive(int verts, params float[] data) - { - VertCount += verts; - Marshal.Copy(data, 0, Data + Size * sizeof(float), data.Length); - Size += data.Length; - } - - public ConstDataBuffer Dump() => VertCount > 0 ? new ConstDataBuffer(Size * sizeof(float), Data) : null; - - public int Size; - public int VertCount; - public readonly IntPtr Data; - } - - /** - * \brief It stores all the render data (VA) used to render a chunk. - * But it does not involve OpenGL operations so it can be - * safely called from all threads. - */ - public class ChunkRenderData - { - public ChunkRenderData() - { - VaOpacity = new VertexBuilder(262144 * (2 + 3 + 0 + 3)); - VaTranslucent = new VertexBuilder(262144 * (2 + 3 + 0 + 3)); - } - - /** - * \brief Generate the render data, namely VA, from a chunk. - * Does not involve OpenGL functions. - * \param chunk the chunk to be rendered. - */ - public void Generate(Chunk chunk) - { - // TODO: merge face rendering - var tmp = new Vec3(); - for (tmp.X = 0; tmp.X < Chunk.Size; ++tmp.X) - for (tmp.Y = 0; tmp.Y < Chunk.Size; ++tmp.Y) - for (tmp.Z = 0; tmp.Z < Chunk.Size; ++tmp.Z) - { - var b = chunk[tmp]; - var target = Blocks.Index[b.Id].IsTranslucent ? VaTranslucent : VaOpacity; - BlockRenderers.Render(target, b.Id, chunk, tmp); - } - } - - public VertexBuilder VaOpacity { get; } // {262144, VertexFormat(2, 3, 0, 3)}; - - public VertexBuilder VaTranslucent { get; } //{262144, VertexFormat(2, 3, 0, 3)}; - } - - /** - * \brief The renderer that can be used to render directly. It includes - * VBO that we need to render. It can be generated from a - * ChunkRenderData - */ - public class ChunkRenderer : StrictDispose - { - public ChunkRenderer(ChunkRenderData data) => Update(data); - - /** - * \brief Generate VBO from VA. Note that this function will call - * OpenGL functions and thus can be only called from the - * main thread. - * \param data The render data that will be used to generate VBO - */ - public void Update(ChunkRenderData data) - { - Release(); - _buffer = data.VaOpacity.Dump(); - _bufferTrans = data.VaTranslucent.Dump(); - _normCount = data.VaOpacity.VertCount; - _transCount = data.VaTranslucent.VertCount; - } - - protected override void Release() - { - _buffer?.Dispose(); - _bufferTrans?.Dispose(); - } - - // Draw call - public void Render(Vec3 c, WorldRenderer rd) - { - if (_buffer != null) - { - Matrix.ModelTranslate(c * Chunk.Size); - rd.FlushMatrix(); - rd.RenderBuffer(_buffer, _normCount); - Matrix.ModelTranslate(-c * Chunk.Size); - } - } - - public void RenderTrans(Vec3 c, WorldRenderer rd) - { - if (_bufferTrans != null) - { - Matrix.ModelTranslate(c * Chunk.Size); - rd.FlushMatrix(); - rd.RenderBuffer(_bufferTrans, _transCount); - Matrix.ModelTranslate(-c * Chunk.Size); - } - } - - // Vertex buffer object - private int _normCount, _transCount; - private ConstDataBuffer _buffer, _bufferTrans; - } -} \ No newline at end of file diff --git a/NEWorld/Renderer/RdChunk.cs b/NEWorld/Renderer/RdChunk.cs new file mode 100644 index 0000000..9b00315 --- /dev/null +++ b/NEWorld/Renderer/RdChunk.cs @@ -0,0 +1,99 @@ +// +// NEWorld/NEWorld: RdChunk.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using Game.Terrain; +using Game.World; +using Xenko.Core.Mathematics; +using Xenko.Engine; +using Xenko.Rendering; + +namespace NEWorld.Renderer +{ + /** + * \brief It stores all the render data (VA) used to render a chunk. + * But it does not involve OpenGL operations so it can be + * safely called from all threads. + */ + public class ChunkVboGen + { + public Model Model { get; private set; } + + /** + * \brief Generate the render data, namely VA, from a chunk. + * Does not involve OpenGL functions. + * \param chunk the chunk to be rendered. + */ + public ChunkVboGen Generate(Chunk chunk) + { + using (VertexBuilder vaOpacity = new VertexBuilder(262144), vaTranslucent = new VertexBuilder(262144)) { + var tmp = new Int3(); + var context = new BlockRenderContext(chunk, chunk.GetNeighbors()); + for (tmp.X = 0; tmp.X < Chunk.RowSize; ++tmp.X) + for (tmp.Y = 0; tmp.Y < Chunk.RowSize; ++tmp.Y) + for (tmp.Z = 0; tmp.Z < Chunk.RowSize; ++tmp.Z) + { + var b = chunk[tmp]; + var target = Blocks.Index[b.Id].IsTranslucent ? vaTranslucent : vaOpacity; + BlockRenderers.Render(target, b.Id, context, tmp); + } + + var mesh0 = vaOpacity.Dump(); + var mesh1 = vaTranslucent.Dump(); + Model = mesh0 != null || mesh1 != null ? new Model {new MaterialInstance(Context.Material), new MaterialInstance(Context.MaterialTransparent)} : null; + if (mesh0 != null) Model.Add(mesh0); + if (mesh1 != null) + { + Model.Add(mesh1); + mesh1.MaterialIndex = 1; + } + } + + return this; + } + } + + /** + * \brief The renderer that can be used to render directly. It includes + * VBO that we need to render. It can be generated from a + * ChunkVboGen + */ + public class RdChunk + { + public RdChunk(Vector3 chunkPosition) + { + Entity = new Entity(); + Entity.GetOrCreate().Position = chunkPosition * Chunk.RowSize; + } + + public Entity Entity { get; } + + /** + * \brief Generate VBO from VA. Note that this function will call + * OpenGL functions and thus can be only called from the + * main thread. + * \param data The render data that will be used to generate VBO + */ + public void Update(Model model) + { + if (model != null) + Entity.GetOrCreate().Model = model; + else + Entity.Remove(); + } + } +} \ No newline at end of file diff --git a/NEWorld/Renderer/RdTextures.cs b/NEWorld/Renderer/RdTextures.cs new file mode 100644 index 0000000..43a6695 --- /dev/null +++ b/NEWorld/Renderer/RdTextures.cs @@ -0,0 +1,107 @@ +// +// NEWorld/NEWorld: RdTextures.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System.Collections.Generic; +using Core; +using Game.Terrain; +using Xenko.Graphics; +using System; +using Xenko.Core.Mathematics; +using Xenko.Rendering.Images; + +namespace NEWorld.Renderer +{ + [DeclareService("BlockTextures")] + public class RdTextures : IBlockTextures + { + public uint Add(string assetUri) + { + var id = (uint) Textures.Count; + var texture = Context.Content.Load(assetUri); + if (Context.Content.IsLoaded(assetUri)) + { + Textures.Add(texture); + } + + return id; + } + + public void GetTexturePos(ref BlockTexCoord pos) + { + pos.Tex = new Int2((int) (pos.Id % TexturesPerLine), (int) (pos.Id / TexturesPerLine)); + } + + public static Texture FlushTextures() + { + var count = Textures.Count; + TexturesPerLine = (1 << (int)(Math.Ceiling(Math.Log(Math.Ceiling(Math.Sqrt(count))) / Math.Log(2)))); + var wid = TexturesPerLine * pixelPerTexture; + using (Texture texture = Texture.New2D(Context.GraphicsDevice, wid, wid, PixelFormat.R8G8B8A8_UNorm), + result = Texture.New2D(Context.GraphicsDevice, pixelPerTexture, pixelPerTexture, + PixelFormat.R8G8B8A8_UNorm, TextureFlags.RenderTarget | TextureFlags.ShaderResource)) + { + using (var scaler = new ImageScaler(SamplingPattern.Linear)) + { + scaler.SetOutput(result); + for (var i = 0; i < count; ++i) + { + var tile = Textures[i]; + scaler.SetInput(tile); + scaler.Draw(Context.RdwContext); + var x = i % TexturesPerLine; + var y = i / TexturesPerLine; + var rx = x * pixelPerTexture; + var ry = y * pixelPerTexture; + Context.RdwContext.CommandList.CopyRegion( + result, result.GetSubResourceIndex(0, 0), null, + texture, texture.GetSubResourceIndex(0, 0), rx, ry); + } + return MakeMipmap(texture, (int) Math.Floor(Math.Log(pixelPerTexture) / Math.Log(2)), scaler); + } + } + } + + private static Texture MakeMipmap(Texture input, int levels, ImageScaler scaler) + { + var ret = Texture.New2D(Context.GraphicsDevice, input.Width, input.Height, levels + 1, input.Format); + var fact = 1; + scaler.SetInput(input); + for (var i = 0; i <= levels; ++i) + { + using (var target = Texture.New2D(Context.GraphicsDevice, input.Width / fact, input.Height / fact, + input.Format, TextureFlags.RenderTarget | TextureFlags.ShaderResource)) + { + scaler.SetOutput(target); + scaler.Draw(Context.RdwContext); + Context.RdwContext.CommandList.CopyRegion( + target, target.GetSubResourceIndex(0, 0), null, + ret, ret.GetSubResourceIndex(0, i), 0, 0); + } + + fact *= 2; + } + + return ret; + } + + public static int TexturesPerLine { get; private set; } + + private static int pixelPerTexture = 32; + private static readonly List Textures = new List(); + } +} \ No newline at end of file diff --git a/NEWorld/Renderer/RdWorld.cs b/NEWorld/Renderer/RdWorld.cs new file mode 100644 index 0000000..bab9767 --- /dev/null +++ b/NEWorld/Renderer/RdWorld.cs @@ -0,0 +1,128 @@ +// +// NEWorld/NEWorld: RdWorld.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System; +using System.Collections.Generic; +using System.Linq; +using Game; +using Game.World; +using Xenko.Core.Mathematics; + +namespace NEWorld.Renderer +{ + public class RdWorld + { + public const int MaxChunkRenderCount = 64; + + // Chunk Renderers + private readonly Dictionary chunkRenderers; + + // Ranges + public readonly int RenderDist; + + private readonly World world; + + public RdWorld(World world, Player player, int renderDistance) + { + this.world = world; + RenderDist = renderDistance; + chunkRenderers = new Dictionary(); + ChunkService.TaskDispatcher.AddRegular(new RenderDetectorTask(this, world.Id, player)); + } + + // TODO: Implement this with Chunk Updation Hook Instead + private class RenderDetectorTask : IRegularReadOnlyTask + { + private static readonly Int3[] Delta = + { + new Int3(1, 0, 0), new Int3(-1, 0, 0), + new Int3(0, 1, 0), new Int3(0, -1, 0), + new Int3(0, 0, 1), new Int3(0, 0, -1) + }; + + private readonly uint currentWorldId; + private readonly Player player; + + private readonly RdWorld rdWorldRenderer; + + public RenderDetectorTask(RdWorld rdWorldRenderer, uint currentWorldId, Player player) + { + this.rdWorldRenderer = rdWorldRenderer; + this.currentWorldId = currentWorldId; + this.player = player; + } + + public void Task(int instance, int instances) + { + if (instance == 0) + { + var counter = 0; + // TODO: improve performance by adding multiple instances of this and set a step when itering the chunks. + var position = player.Position; + var center = World.GetChunkPos(new Int3((int) position.X, (int) position.Y, (int) position.Z)); + var world = ChunkService.Worlds.Get(currentWorldId); + foreach (var c in world.Chunks) + { + var chunk = c.Value; + // In render range, pending to render + if (chunk.IsUpdated && ChebyshevDistance(center, chunk.Position) <= rdWorldRenderer.RenderDist) + if (NeighbourChunkLoadCheck(world, chunk.Position)) + { + GenerateVbo(chunk, rdWorldRenderer.chunkRenderers); + if (++counter == MaxChunkRenderCount) break; + } + } + } + } + + private static async void GenerateVbo(Chunk target, Dictionary pool) + { + await ChunkService.TaskDispatcher.NextReadOnlyChance(); + var model = new ChunkVboGen().Generate(target).Model; + await ChunkService.TaskDispatcher.NextRenderChance(); + var position = target.Position; + // TODO: Check the Chunk Directly + if (!target.World.Chunks.ContainsKey(position)) return; + target.IsUpdated = false; // TODO: Remove when chunk update driver is complete + GetOrAddRdChunk(pool, position).Update(model); + } + + private static RdChunk GetOrAddRdChunk(Dictionary pool, Int3 position) + { + if (pool.TryGetValue(position, out var it)) return it; + var renderer = new RdChunk(new Vector3(position.X, position.Y, position.Z)); + Context.OperatingScene.Entities.Add(renderer.Entity); + pool.Add(position, renderer); + return renderer; + } + + // TODO: Remove Type1 Clone + private static int ChebyshevDistance(Int3 l, Int3 r) + { + return Math.Max(Math.Max(Math.Abs(l.X - r.X), Math.Abs(l.Y - r.Y)), Math.Abs(l.Z - r.Z)); + } + + private static bool NeighbourChunkLoadCheck(World world, Int3 pos) + { + return Delta.All(p => world.IsChunkLoaded(pos + p)); + } + } + + + } +} \ No newline at end of file diff --git a/NEWorld/Renderer/VertexBuilder.cs b/NEWorld/Renderer/VertexBuilder.cs new file mode 100644 index 0000000..a22c640 --- /dev/null +++ b/NEWorld/Renderer/VertexBuilder.cs @@ -0,0 +1,114 @@ +// +// NEWorld/NEWorld: VertexBuilder.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// +using System; +using System.Runtime.InteropServices; +using Game.Terrain; +using Xenko.Core.Mathematics; +using Xenko.Graphics; +using Xenko.Rendering; +using Buffer = Xenko.Graphics.Buffer; + +namespace NEWorld.Renderer +{ + public class VertexBuilder : IVertexBuilder, IDisposable + { + private static readonly uint[,] Rotation = + { + {0x0000, 0x0001, 0x0101, 0x0100}, {0x0001, 0x0101, 0x0100, 0x0000}, + {0x0101, 0x0100, 0x0000, 0x0001}, {0x0100, 0x0000, 0x0001, 0x0101} + }; + + private static readonly uint[,] Faces = + { + {0x010101, 0x010001, 0x010000, 0x010100}, {0x000100, 0x000000, 0x000001, 0x000101}, + {0x000100, 0x000101, 0x010101, 0x010100}, {0x000001, 0x000000, 0x010000, 0x010001}, + {0x000101, 0x000001, 0x010001, 0x010101}, {0x010100, 0x010000, 0x000000, 0x000100} + }; + + private readonly IntPtr data; + private int count; + private unsafe uint* view; + + public unsafe VertexBuilder(int size) + { + data = Marshal.AllocHGlobal(size * 8); + view = (uint*) data.ToPointer(); + } + + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + public unsafe void Rect(Int3 position, Int2 tex, uint face, int rotation, uint shade) + { + var high = (shade << 24) | (uint) ((position.X << 16) | (position.Y << 8) | position.Z); + var low = (face << 16) | (uint) ((tex.X << 8) | tex.Y); + for (var i = 0; i < 4; ++i) + { + *view++ = high + Faces[face, i]; + *view++ = low + Rotation[rotation, i]; + } + + count += 4; + } + + ~VertexBuilder() + { + ReleaseUnmanagedResources(); + } + + public Mesh Dump() + { + return count > 0 ? CreateMesh() : null; + } + + private Mesh CreateMesh() + { + return new Mesh + { + Draw = new MeshDraw + { + DrawCount = count / 2 * 3, + IndexBuffer = Context.IndexBuffer, + PrimitiveType = PrimitiveType.TriangleList, + StartLocation = 0, + VertexBuffers = new[] + { + CreateVertexBuffer() + } + }, + MaterialIndex = 0 + }; + } + + private VertexBufferBinding CreateVertexBuffer() + { + return new VertexBufferBinding( + Buffer.Vertex.New(Context.Game.GraphicsDevice, new DataPointer(data, count * 32)), Context.VertexLayout, + count); + } + + private void ReleaseUnmanagedResources() + { + Marshal.FreeHGlobal(data); + } + } +} \ No newline at end of file diff --git a/NEWorld/Renderer/WorldRenderer.cs b/NEWorld/Renderer/WorldRenderer.cs deleted file mode 100644 index b74e033..0000000 --- a/NEWorld/Renderer/WorldRenderer.cs +++ /dev/null @@ -1,230 +0,0 @@ -// -// NEWorld: WorldRenderer.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Linq; -using Core.Math; -using Game; -using Game.Terrain; -using Game.World; -using OpenGL; - -namespace NEWorld.Renderer -{ - /** - * \brief Manage the VBO of a world. It includes ChunkRenderer. - */ - public class WorldRenderer : IDisposable - { - public const int MaxChunkRenderCount = 4; - - private class RenderDetectorTask : IReadOnlyTask - { - public RenderDetectorTask(WorldRenderer worldRenderer, uint currentWorldId, Player player) - { - _worldRenderer = worldRenderer; - _currentWorldId = currentWorldId; - _player = player; - } - - public void Task(ChunkService cs) - { - var counter = 0; - // TODO: improve performance by adding multiple instances of this and set a step when itering the chunks. - var position = _player.Position; - var positionInt = new Vec3((int) position.X, (int) position.Y, (int) position.Z); - var chunkpos = World.GetChunkPos(positionInt); - var world = cs.Worlds.Get(_currentWorldId); - foreach (var c in world.Chunks) - { - var chunk = c.Value; - var chunkPosition = chunk.Position; - // In render range, pending to render - if (chunk.IsUpdated && chunkpos.ChebyshevDistance(chunkPosition) <= _worldRenderer.RenderDist) - { - if (NeighbourChunkLoadCheck(world, chunkPosition)) - { - // TODO: maybe build a VA pool can speed this up. - var crd = new ChunkRenderData(); - crd.Generate(chunk); - cs.TaskDispatcher.Add(new VboGenerateTask(world, chunkPosition, crd, - _worldRenderer._chunkRenderers)); - if (counter++ == 3) break; - } - } - } - } - - public IReadOnlyTask Clone() => (IReadOnlyTask) MemberwiseClone(); - - private static readonly Vec3[] Delta = - { - new Vec3(1, 0, 0), new Vec3(-1, 0, 0), - new Vec3(0, 1, 0), new Vec3(0, -1, 0), - new Vec3(0, 0, 1), new Vec3(0, 0, -1) - }; - - private static bool NeighbourChunkLoadCheck(World world, Vec3 pos) => - Delta.All(p => world.IsChunkLoaded(pos + p)); - - private readonly WorldRenderer _worldRenderer; - private readonly uint _currentWorldId; - private readonly Player _player; - } - - private class VboGenerateTask : IRenderTask - { - public VboGenerateTask(World world, Vec3 position, ChunkRenderData crd, - Dictionary, ChunkRenderer> chunkRenderers) - { - _world = world; - _position = position; - _chunkRenderData = crd; - _chunkRenderers = chunkRenderers; - } - - public void Task(ChunkService srv) - { - if (!_world.Chunks.TryGetValue(_position, out var chunk)) return; - chunk.IsUpdated = false; - if (_chunkRenderers.TryGetValue(_position, out var it)) - { - it.Update(_chunkRenderData); - } - else - { - _chunkRenderers.Add(_position, new ChunkRenderer(_chunkRenderData)); - } - } - - public IRenderTask Clone() => (IRenderTask) MemberwiseClone(); - - private readonly World _world; - private readonly Vec3 _position; - private readonly ChunkRenderData _chunkRenderData; - private readonly Dictionary, ChunkRenderer> _chunkRenderers; - } - - public WorldRenderer(World world, int renderDistance) - { - _world = world; - RenderDist = renderDistance; - _chunkRenderers = new Dictionary, ChunkRenderer>(); - _prog = new Program(); - using (Shader vertex = new Shader(Gl.VertexShader, @" -#version 450 core -layout(shared, row_major) uniform; -layout (std140, binding = 0) uniform vertexMvp { mat4 ProjMtx; }; -layout (location = 0) in vec3 Position; -layout (location = 1) in vec2 TexCoord; -layout (location = 2) in vec4 Color; -out vec2 Frag_UV; -out vec4 Frag_Color; -void main() { - Frag_UV = TexCoord; - Frag_Color = Color; - gl_Position = ProjMtx * vec4(Position.xyz, 1); -}"), - fragment = new Shader(Gl.FragmentShader, @" -#version 450 core -precision mediump float; -layout (binding = 1) uniform sampler2D Texture; -in vec2 Frag_UV; -in vec4 Frag_Color; -out vec4 Out_Color; -void main(){ - Out_Color = vec4(1.0, 1.0, 1.0, 1.0); //Frag_Color * texture(Texture, Frag_UV.st); -}")) - _prog.Link(new[] {vertex, fragment}); - - _ubo = new DataBuffer(16 * sizeof(float)); - _vao = new VertexArray(); - - _vao.EnableAttrib(0); - _vao.EnableAttrib(1); - _vao.EnableAttrib(2); - _vao.AttribFormat(0, 3, Gl.Float, false, 5 * sizeof(float)); - _vao.AttribFormat(1, 2, Gl.Float, false, 0); - _vao.AttribFormat(2, 3, Gl.Float, false, 2 * sizeof(float)); - _vao.AttribBinding(0, 0); - _vao.AttribBinding(1, 0); - _vao.AttribBinding(2, 0); - } - - public void RenderBuffer(ConstDataBuffer buffer, int verts) - { - _vao.BindBuffer(0, buffer, 0, 8 * sizeof(float)); - Gl.DrawArrays(Gl.Quads, 0, verts); - } - - // Render all chunks - private int Render(Vec3 position) - { - var chunkPending = new List, ChunkRenderer>>(); - - var chunkpos = World.GetChunkPos(position); - _vao.Use(); - _ubo.BindBase(Gl.UniformBuffer, 0); - foreach (var c in _chunkRenderers) - { - if (chunkpos.ChebyshevDistance(c.Key) > RenderDist) continue; - c.Value.Render(c.Key, this); - chunkPending.Add(c); - } - - Gl.Enable(Gl.Blend); - Gl.BlendFunc(Gl.SrcAlpha, Gl.OneMinusSrcAlpha); - foreach (var c in chunkPending) - { - c.Value.RenderTrans(c.Key, this); - } - - Gl.Disable(Gl.Blend); - return chunkPending.Count; - } - - public int Render(Vec3 v) => Render(new Vec3((int) v.X, (int) v.Y, (int) v.Z)); - - public void RegisterTask(ChunkService chunkService, Player player) => - chunkService.TaskDispatcher.AddRegular(new RenderDetectorTask(this, _world.Id, player)); - - public void FlushMatrix() => _ubo.DataSection(0, Matrix.Get().Data); - - private readonly World _world; - - // Ranges - public readonly int RenderDist; - - // Chunk Renderers - private readonly Dictionary, ChunkRenderer> _chunkRenderers; - private readonly Program _prog; - private readonly VertexArray _vao; - private readonly DataBuffer _ubo; - - public void Dispose() - { - _prog?.Dispose(); - _vao?.Dispose(); - _ubo?.Dispose(); - foreach (var renderer in _chunkRenderers) - renderer.Value.Dispose(); - } - } -} \ No newline at end of file diff --git a/NEWorld/Resources/skybox_texture_ldr.dds b/NEWorld/Resources/skybox_texture_ldr.dds new file mode 100644 index 0000000..952d818 Binary files /dev/null and b/NEWorld/Resources/skybox_texture_ldr.dds differ diff --git a/NEWorld/SDL2-CS.dll b/NEWorld/SDL2-CS.dll deleted file mode 100644 index 2d48625..0000000 Binary files a/NEWorld/SDL2-CS.dll and /dev/null differ diff --git a/NEWorld/Umbrella.cs b/NEWorld/Umbrella.cs new file mode 100644 index 0000000..f3bcda3 --- /dev/null +++ b/NEWorld/Umbrella.cs @@ -0,0 +1,20 @@ +// +// NEWorld/NEWorld: Umbrella.cs +// NEWorld: A Free Game with Similar Rules to Minecraft. +// Copyright (C) 2015-2019 NEWorld Team +// +// NEWorld is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// NEWorld is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +// Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with NEWorld. If not, see . +// + +[assembly:Core.DeclareNeWorldAssembly] diff --git a/NEWorld/Widget.cs b/NEWorld/Widget.cs deleted file mode 100644 index f54e14f..0000000 --- a/NEWorld/Widget.cs +++ /dev/null @@ -1,77 +0,0 @@ -// -// NEWorld: Widget.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using NuklearSharp; - -namespace NEWorld -{ - // widget base class - public abstract class Widget - { - public Widget(string name, Nuklear.nk_rect size, uint flags) - { - Name = name; - _size = size; - _flags = flags; - Open = true; - } - - public void _render(NkSdl ctx) - { - if (ctx.Begin(Name, _size, _flags)) - { - Render(ctx); - ctx.End(); - } - } - - public abstract void Update(); - - public bool Open { get; set; } - - public string Name { get; } - - protected abstract void Render(NkSdl ctx); - - private readonly uint _flags; - private readonly Nuklear.nk_rect _size; - } - - // callback style widget - public class WidgetCallback : Widget - { - public delegate void RenderCallback(NkSdl ctx); - - public delegate void UpdateCallback(); - - public WidgetCallback(string name, Nuklear.nk_rect size, uint flags, - RenderCallback renderFunc, UpdateCallback updateFunc = null) : base(name, size, flags) - { - _renderFunc = renderFunc; - _updateFunc = updateFunc; - } - - protected override void Render(NkSdl ctx) => _renderFunc(ctx); - - public override void Update() => _updateFunc?.Invoke(); - - private readonly RenderCallback _renderFunc; - private readonly UpdateCallback _updateFunc; - } -} \ No newline at end of file diff --git a/NEWorld/Window.cs b/NEWorld/Window.cs deleted file mode 100644 index c07de57..0000000 --- a/NEWorld/Window.cs +++ /dev/null @@ -1,152 +0,0 @@ -// -// NEWorld: Window.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using OpenGL; -using SDL2; - -namespace NEWorld -{ - public struct MouseState - { - public int X, Y; - public bool Left, Mid, Right, Relative; - } - - public class Window - { - private Window(string title, int width, int height) - { - _title = title; - _width = width; - _height = height; - if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO) < 0) - throw new Exception("SDL could not initialize! SDL_Error: " + SDL.SDL_GetError()); - SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 4); - SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 5); - SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_STENCIL_SIZE, 8); - SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, - (int) SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE); - - _window = SDL.SDL_CreateWindow(_title, 100, 100, _width, _height, - SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE); - _context = SDL.SDL_GL_CreateContext(_window); - SDL.SDL_GL_SetSwapInterval(0); // VSync - MakeCurrentDraw(); - Gl.Init(SDL.SDL_GL_GetProcAddress); - _nuklearContext = new NkSdl(_window); - } - - ~Window() - { - SDL.SDL_DestroyWindow(_window); - SDL.SDL_GL_DeleteContext(_context); - SDL.SDL_Quit(); - } - - public void MakeCurrentDraw() => SDL.SDL_GL_MakeCurrent(_window, _context); - - public void SwapBuffers() => SDL.SDL_GL_SwapWindow(_window); - - public static unsafe byte* GetKeyBoardState() => (byte*) SDL.SDL_GetKeyboardState(out var number); - - public int GetWidth() => _width; - - public int GetHeight() => _height; - - public void PollEvents() - { - if (SDL.SDL_GetRelativeMouseMode() == SDL.SDL_bool.SDL_TRUE) - { - var buttons = SDL.SDL_GetRelativeMouseState(out _mouse.X, out _mouse.Y); - _mouse.Left = (buttons & SDL.SDL_BUTTON_LEFT) != 0; - _mouse.Right = (buttons & SDL.SDL_BUTTON_RIGHT) != 0; - _mouse.Mid = (buttons & SDL.SDL_BUTTON_MIDDLE) != 0; - _mouse.Relative = true; - } - else - { - _prevMouse = _mouse; - var buttons = SDL.SDL_GetMouseState(out _mouse.X, out _mouse.Y); - _mouse.Left = (buttons & SDL.SDL_BUTTON_LEFT) != 0; - _mouse.Right = (buttons & SDL.SDL_BUTTON_RIGHT) != 0; - _mouse.Mid = (buttons & SDL.SDL_BUTTON_MIDDLE) != 0; - if (_mouse.Relative) _prevMouse = _mouse; - _mouse.Relative = false; - } - - _nuklearContext.InputBegin(); - while (SDL.SDL_PollEvent(out var e) != 0) - { - _nuklearContext.HandleEvent(ref e); - switch (e.type) - { - case SDL.SDL_EventType.SDL_QUIT: - _shouldQuit = true; - break; - case SDL.SDL_EventType.SDL_WINDOWEVENT: - switch (e.window.windowEvent) - { - case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESIZED: - case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED: - _width = e.window.data1; - _height = e.window.data2; - break; - } - - break; - } - } - - _nuklearContext.InputEnd(); - } - - public bool ShouldQuit() => _shouldQuit; - - public NkSdl GetNkContext() => _nuklearContext; - - /** - * \brief Get the relative motion of mouse - * \return The relative motion of mouse - */ - public MouseState GetMouseMotion() - { - var res = _mouse; - res.X -= _prevMouse.X; - res.Y -= _prevMouse.Y; - return res; - } - - public static void LockCursor() => SDL.SDL_SetRelativeMouseMode(SDL.SDL_bool.SDL_TRUE); - - public static void UnlockCursor() => SDL.SDL_SetRelativeMouseMode(SDL.SDL_bool.SDL_FALSE); - - private static Window _win; - - public static Window GetInstance(string title = "", int width = 0, int height = 0) => - _win ?? (_win = new Window(title, width, height)); - - private readonly string _title; - private int _width, _height; - private MouseState _mouse, _prevMouse; - private bool _shouldQuit; - private readonly IntPtr _window, _context; - private readonly NkSdl _nuklearContext; - } -} \ No newline at end of file diff --git a/NEWorld/packages.config b/NEWorld/packages.config deleted file mode 100644 index bfbdfc9..0000000 --- a/NEWorld/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/NEWorldShell/Cli.cs b/NEWorldShell/Cli.cs index 2321da3..757f6a9 100644 --- a/NEWorldShell/Cli.cs +++ b/NEWorldShell/Cli.cs @@ -1,7 +1,7 @@ // -// NEWorldShell: Cli.cs +// NEWorld/NEWorldShell: Cli.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System; using System.Runtime; using Core; @@ -28,6 +27,8 @@ namespace NEWorldShell { internal class ServerCommandLine { + private readonly CommandManager _commands; + public ServerCommandLine() { _commands = new CommandManager(); @@ -46,10 +47,8 @@ private void InitBuiltinCommands() { var helpString = "\nAvailable commands:\n"; foreach (var command in _commands.GetCommandMap()) - { helpString += command.Key + " - " + command.Value.Key.Author + " : " + command.Value.Key.Help + "\n"; - } return new CommandExecuteStat(true, helpString); }); @@ -58,7 +57,7 @@ private void InitBuiltinCommands() cmd => { Services.Get("Game.Server").Stop(); - Console.WriteLine("Server RPC stopped."); + LogPort.Debug("Server RPC stopped."); _commands.SetRunningStatus(false); return new CommandExecuteStat(true, ""); }); @@ -66,14 +65,14 @@ private void InitBuiltinCommands() _commands.RegisterCommand("server.ups", new CommandInfo("internal", "Show the ups."), cmd => { - // TODO: Add UPS counter for server + // TODO: AddReadOnlyTask UPS counter for server return new CommandExecuteStat(true, "[Server UPS counter not finished yet!]"); }); _commands.RegisterCommand("server.connections", new CommandInfo("internal", "Count Connections."), cmd => { - Console.WriteLine(Services.Get("Game.Server").CountConnections()); + LogPort.Debug($"{Services.Get("Game.Server").CountConnections()}"); return new CommandExecuteStat(true, ""); }); @@ -83,7 +82,7 @@ private void InitBuiltinCommands() { var ret = "Chunks loaded: "; long sum = 0; - var worlds = Singleton.Instance.Worlds; + var worlds = ChunkService.Worlds; foreach (var world in worlds) { ret += $"\n{world.Id} {world.Name} :\t{world.GetChunkCount()}"; @@ -100,11 +99,9 @@ private void InitBuiltinCommands() GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect(); GC.WaitForFullGCComplete(); - Console.WriteLine("GC Completed"); + LogPort.Debug("GC Completed"); return new CommandExecuteStat(true, ""); }); } - - private readonly CommandManager _commands; } } \ No newline at end of file diff --git a/NEWorldShell/Command.cs b/NEWorldShell/Command.cs index 0683794..750acbe 100644 --- a/NEWorldShell/Command.cs +++ b/NEWorldShell/Command.cs @@ -1,7 +1,7 @@ // -// NEWorldShell: Command.cs +// NEWorld/NEWorldShell: Command.cs // NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team +// Copyright (C) 2015-2019 NEWorld Team // // NEWorld is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -16,49 +16,51 @@ // You should have received a copy of the GNU Lesser General Public License // along with NEWorld. If not, see . // - using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using Core; namespace NEWorldShell { public class CommandExecuteStat { + public readonly string Info; + + public bool Success; + public CommandExecuteStat(bool s, string i) { Success = s; Info = i; } - - public bool Success; - public readonly string Info; } public class Command { + public readonly List Args; + + public readonly string Name; + public Command(string rawString) { Args = rawString.Split(' ').ToList(); Name = Args.Count != 0 ? Args[0] : ""; if (Args.Count != 0) Args.RemoveAt(0); } - - public readonly string Name; - public readonly List Args; } public class CommandInfo { + public string Author; + public string Help; + public CommandInfo(string a, string h) { Author = a; Help = h; } - - public string Author; - public string Help; } @@ -66,6 +68,14 @@ public class CommandManager { public delegate CommandExecuteStat CommandHandleFunction(Command exec); + private readonly Dictionary> _commandMap; + + private readonly Thread _mainloop; + + private bool _threadRunning = true; + + private bool _waitingForInput; + public CommandManager() { _commandMap = new Dictionary>(); @@ -77,13 +87,9 @@ public CommandManager() { _threadRunning = false; if (!_waitingForInput) - { _mainloop.Join(); - } else - { _mainloop.Abort(); - } } public void InputLoop() @@ -95,11 +101,14 @@ public void InputLoop() _waitingForInput = false; var result = HandleCommand(new Command(input)); if (result.Info != "") - Console.WriteLine(result.Info); + LogPort.Debug(result.Info); } } - public Dictionary> GetCommandMap() => _commandMap; + public Dictionary> GetCommandMap() + { + return _commandMap; + } public void SetRunningStatus(bool s) { @@ -118,13 +127,5 @@ private CommandExecuteStat HandleCommand(Command cmd) ? result.Value(cmd) : new CommandExecuteStat(false, "Command not exists, type help for available commands."); } - - private readonly Thread _mainloop; - - private bool _threadRunning = true; - - private bool _waitingForInput; - - private readonly Dictionary> _commandMap; } } \ No newline at end of file diff --git a/NEWorldShell/NEWorldShell.csproj b/NEWorldShell/NEWorldShell.csproj index 3960773..66fb51a 100644 --- a/NEWorldShell/NEWorldShell.csproj +++ b/NEWorldShell/NEWorldShell.csproj @@ -9,15 +9,16 @@ Properties NEWorldShell NEWorldShell - v4.7.1 + v4.6.1 512 + AnyCPU true full false - ..\bin\Debug\ + ..\Bin\Windows\Debug\ DEBUG;TRACE prompt 4 @@ -26,7 +27,7 @@ AnyCPU pdbonly true - ..\bin\Release\ + ..\Bin\Windows\Release\ TRACE prompt 4 @@ -52,6 +53,10 @@ {f83c4945-abff-4856-94a3-d0d31c976a11} Game + + {15bdbfd8-daea-4324-9b09-7ab531bfe5ef} + Main + - \ No newline at end of file diff --git a/OpenGL/Others.cs b/OpenGL/Others.cs deleted file mode 100644 index ceb52c8..0000000 --- a/OpenGL/Others.cs +++ /dev/null @@ -1,93 +0,0 @@ -// -// OpenGL: Others.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; - -namespace OpenGL -{ - static partial class Gl - { - // Draw - public delegate void DrawArraysProc(uint mode, int first, int count); - - public delegate void DrawElementsProc(uint mode, int count, uint type, IntPtr indicies); - - public static DrawArraysProc DrawArrays; - - public static DrawElementsProc DrawElements; - - // Context Op - public delegate void ViewportProc(int x, int y, int width, int height); - - public delegate void ClearColorProc(float red, float green, float blue, float alpha); - - public delegate void ClearDepthProc(float depth); - - public delegate void ClearProc(uint mask); - - public delegate void LineWidthProc(float width); - - public delegate void EnableProc(uint cap); - - public delegate void DisableProc(uint cap); - - public delegate void DepthFuncProc(uint cap); - - public delegate void CullFaceProc(uint cap); - - public delegate void BlendEquationProc(uint mode); - - public delegate void BlendFuncProc(uint sfactor, uint dfactor); - - public delegate void ScissorProc(int x, int y, int width, int height); - - public static ViewportProc Viewport; - public static ClearColorProc ClearColor; - public static ClearProc Clear; - public static ClearDepthProc ClearDepth; - public static LineWidthProc LineWidth; - public static EnableProc Enable; - public static DisableProc Disable; - public static DepthFuncProc DepthFunc; - public static CullFaceProc CullFaceOption; - public static BlendEquationProc BlendEquation; - public static BlendFuncProc BlendFunc; - public static ScissorProc Scissor; - - static partial void InitOthers() - { - // Draw - DrawArrays = Get("glDrawArrays"); - // Context - Viewport = Get("glViewport"); - ClearColor = Get("glClearColor"); - ClearDepth = Get("glClearDepth"); - Clear = Get("glClear"); - LineWidth = Get("glLineWidth"); - Enable = Get("glEnable"); - Disable = Get("glDisable"); - BlendEquation = Get("glBlendEquation"); - BlendFunc = Get("glBlendFunc"); - Scissor = Get("glScissor"); - DrawElements = Get("glDrawElements"); - DepthFunc = Get("glDepthFunc"); - CullFaceOption = Get("glCullFace"); - } - } -} \ No newline at end of file diff --git a/OpenGL/Properties/AssemblyInfo.cs b/OpenGL/Properties/AssemblyInfo.cs deleted file mode 100644 index 7090d74..0000000 --- a/OpenGL/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// OpenGL: AssemblyInfo.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpenGL")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("OpenGL")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4EB6C361-3E67-4A4E-8B2B-9C8D29D8F852")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/OpenGL/RenderBuffer.cs b/OpenGL/RenderBuffer.cs deleted file mode 100644 index 7c89185..0000000 --- a/OpenGL/RenderBuffer.cs +++ /dev/null @@ -1,72 +0,0 @@ -// -// OpenGL: RenderBuffer.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using Core.Math; -using Core.Utilities; - -namespace OpenGL -{ - public static partial class Gl - { - public const uint RenderBuffer = 0x8D41; - - internal unsafe delegate void CreateRenderbuffersProc(int n, uint* renderbuffers); - - internal unsafe delegate void DeleteRenderbuffersProc(int n, uint* renderbuffers); - - internal delegate void NamedRenderbufferStorageProc(uint renderbuffer, uint format, int width, int height); - - internal static CreateRenderbuffersProc CreateRenderbuffers; - internal static DeleteRenderbuffersProc DeleteRenderbuffers; - internal static NamedRenderbufferStorageProc NamedRenderbufferStorage; - - static partial void InitRenderBuffer() - { - CreateRenderbuffers = Get("glCreateRenderbuffers"); - DeleteRenderbuffers = Get("glDeleteRenderbuffers"); - NamedRenderbufferStorage = Get("glNamedRenderbufferStorage"); - } - } - - public class RenderBuffer : StrictDispose - { - public unsafe RenderBuffer() - { - fixed (uint* addr = &_hdc) - { - Gl.CreateRenderbuffers(1, addr); - } - } - - protected override unsafe void Release() - { - fixed (uint* addr = &_hdc) - { - Gl.DeleteRenderbuffers(1, addr); - } - } - - public void SetStorage(PixelInternalFormats fmt, Vec2 size) => - Gl.NamedRenderbufferStorage(_hdc, (uint) fmt, size.X, size.Y); - - public uint Raw() => _hdc; - - private uint _hdc; - } -} \ No newline at end of file diff --git a/OpenGL/Shader.cs b/OpenGL/Shader.cs deleted file mode 100644 index 4269f98..0000000 --- a/OpenGL/Shader.cs +++ /dev/null @@ -1,182 +0,0 @@ -// -// OpenGL: Shader.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using Core.Utilities; - -namespace OpenGL -{ - static partial class Gl - { - public const uint FragmentShader = 0x8B30; - public const uint VertexShader = 0x8B31; - public const uint GeometryShader = 0x8DD9; - internal const uint InfoLogLength = 0x8B84; - internal const uint CompileStatus = 0x8B81; - internal const uint LinkStatus = 0x8B82; - - internal delegate void CompileShaderProc(uint shader); - - internal delegate uint CreateProgramProc(); - - internal delegate uint CreateShaderProc(uint type); - - internal delegate void DeleteProgramProc(uint program); - - internal delegate void DeleteShaderProc(uint shader); - - internal unsafe delegate void GetProgramInfoLogProc(uint prog, int bufSize, int* length, char* log); - - internal unsafe delegate void GetProgramivProc(uint program, uint pname, int* param); - - internal unsafe delegate void GetShaderInfoLogProc(uint shader, int bufSize, int* length, char* log); - - internal unsafe delegate void GetShaderivProc(uint shader, uint pname, int* param); - - internal delegate void AttachShaderProc(uint program, uint shader); - - internal delegate void LinkProgramProc(uint program); - - internal unsafe delegate void ShaderSourceProc(uint shader, int count, char** str, int* length); - - internal delegate void UseProgramProc(uint program); - - internal delegate void ProgramUniform1IProc(uint program, int location, int v0); - - internal static CompileShaderProc CompileShader; - internal static CreateProgramProc CreateProgram; - internal static CreateShaderProc CreateShader; - internal static DeleteProgramProc DeleteProgram; - internal static DeleteShaderProc DeleteShader; - internal static GetProgramInfoLogProc GetProgramInfoLog; - internal static GetProgramivProc GetProgramiv; - internal static GetShaderInfoLogProc GetShaderInfoLog; - internal static GetShaderivProc GetShaderiv; - internal static AttachShaderProc AttachShader; - internal static LinkProgramProc LinkProgram; - internal static ShaderSourceProc ShaderSource; - internal static UseProgramProc UseProgram; - internal static ProgramUniform1IProc ProgramUniform1I; - - static partial void InitShader() - { - CompileShader = Get("glCompileShader"); - CreateProgram = Get("glCreateProgram"); - CreateShader = Get("glCreateShader"); - DeleteProgram = Get("glDeleteProgram"); - DeleteShader = Get("glDeleteShader"); - GetProgramInfoLog = Get("glGetProgramInfoLog"); - GetProgramiv = Get("glGetProgramiv"); - GetShaderInfoLog = Get("glGetShaderInfoLog"); - GetShaderiv = Get("glGetShaderiv"); - AttachShader = Get("glAttachShader"); - LinkProgram = Get("glLinkProgram"); - ShaderSource = Get("glShaderSource"); - UseProgram = Get("glUseProgram"); - ProgramUniform1I = Get("glProgramUniform1iEXT"); - } - } - - public class Shader : StrictDispose - { - public unsafe Shader(uint eShaderType, string strFileData) - { - var shader = Gl.CreateShader(eShaderType); - var file = Gl.Utf8ToNative(strFileData); - var ptr = Marshal.AllocHGlobal(file.Length); - Marshal.Copy(file, 0, ptr, file.Length); - var cString = (char*) ptr; - Gl.ShaderSource(shader, 1, &cString, null); - Marshal.FreeHGlobal(ptr); - Gl.CompileShader(shader); - int status; - Gl.GetShaderiv(shader, Gl.CompileStatus, &status); - if (status == 0) - { - int infoLogLength; - Gl.GetShaderiv(shader, Gl.InfoLogLength, &infoLogLength); - var strInfoLog = Marshal.AllocHGlobal(infoLogLength + 1); - Gl.GetShaderInfoLog(shader, infoLogLength, null, (char*) strInfoLog); - var infoLog = Gl.Utf8ToManaged(strInfoLog); - Marshal.FreeHGlobal(strInfoLog); - throw new Exception("Compile failure in " + GetShaderTypeString(eShaderType) + " shader:" + infoLog); - } - - _hdc = shader; - } - - private static string GetShaderTypeString(uint type) - { - switch (type) - { - case Gl.VertexShader: - return "vertex"; - case Gl.GeometryShader: - return "geometry"; - case Gl.FragmentShader: - return "fragment"; - default: - return "unknown type"; - } - } - - protected override void Release() => Gl.DeleteShader(_hdc); - - public uint Raw() => _hdc; - - private readonly uint _hdc; - } - - public class Program : StrictDispose - { - public unsafe void Link(IEnumerable shaderList) - { - var programId = Gl.CreateProgram(); - foreach (var sd in shaderList) - Gl.AttachShader(programId, sd.Raw()); - Gl.LinkProgram(programId); - int status; - Gl.GetProgramiv(programId, Gl.LinkStatus, &status); - if (status == 0) - { - int infoLogLength; - Gl.GetProgramiv(programId, Gl.InfoLogLength, &infoLogLength); - var strInfoLog = Marshal.AllocHGlobal(infoLogLength + 1); - Gl.GetProgramInfoLog(programId, infoLogLength, null, (char*) strInfoLog); - var infoLog = Gl.Utf8ToManaged(strInfoLog); - Marshal.FreeHGlobal(strInfoLog); - throw new Exception("Linker failure: " + infoLog); - } - - _hdc = programId; - } - - protected override void Release() => Gl.DeleteProgram(_hdc); - - public void Use() => Gl.UseProgram(_hdc); - - public void Uniform(int location, int val) => Gl.ProgramUniform1I(_hdc, location, val); - - public uint Raw() => _hdc; - - private uint _hdc; - } -} \ No newline at end of file diff --git a/OpenGL/Texture.cs b/OpenGL/Texture.cs deleted file mode 100644 index 35cebea..0000000 --- a/OpenGL/Texture.cs +++ /dev/null @@ -1,146 +0,0 @@ -// -// OpenGL: Texture.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using Core; -using Core.Math; -using Core.Utilities; - -namespace OpenGL -{ - public static partial class Gl - { - internal const uint Texture2D = 0x0DE1; - - internal unsafe delegate void CreateTexturesProc(uint target, int n, uint* textures); - - internal unsafe delegate void DeleteTexturesProc(int n, uint* textures); - - internal delegate void TextureParameteriProc(uint texture, uint pname, int param); - - internal delegate void TextureParameterfProc(uint texture, uint pname, float param); - - internal delegate void BindTextureUnitProc(uint unit, uint texture); - - internal delegate void TextureStorage2DProc(uint texture, int levels, uint format, int width, int height); - - internal delegate void TextureSubImage2DProc(uint texture, int level, int xoffset, int yoffset, int width, - int height, uint format, uint type, IntPtr pixels); - - internal static CreateTexturesProc CreateTextures; - internal static DeleteTexturesProc DeleteTextures; - internal static TextureParameteriProc TextureParameteri; - internal static TextureParameterfProc TextureParameterf; - internal static BindTextureUnitProc BindTextureUnit; - internal static TextureStorage2DProc TextureStorage2D; - internal static TextureSubImage2DProc TextureSubImage2D; - - static partial void InitTexture() - { - CreateTextures = Get("glCreateTextures"); - DeleteTextures = Get("glDeleteTextures"); - TextureParameteri = Get("glTextureParameteri"); - TextureParameterf = Get("glTextureParameterf"); - BindTextureUnit = Get("glBindTextureUnit"); - TextureStorage2D = Get("glTextureStorage2D"); - TextureSubImage2D = Get("glTextureSubImage2D"); - } - } - - public class Texture : StrictDispose - { - public unsafe Texture(int levels, PixelInternalFormats internalFormat, Vec2 size) - { - fixed (uint* addr = &_hdc) - { - Gl.CreateTextures(Gl.Texture2D, 1, addr); - } - - Gl.TextureStorage2D(_hdc, levels, (uint) internalFormat, size.X, size.Y); - } - - protected override unsafe void Release() - { - fixed (uint* addr = &_hdc) - { - Gl.DeleteTextures(1, addr); - } - } - - public enum Filter - { - Nearest = 0x2600, - Linear = 0x2601, - NearestMipmapNearest = 0x2700, - LinearMipmapNearest = 0x2701, - NearestMipmapLinear = 0x2702, - LinearMipmapLinear = 0x2703 - } - - public enum Warp - { - ClampToEdge = 0x812F, - ClampToBorder = 0x812D, - MirroredRepeat = 0x8370, - Repeat = 0x2901, - MirrorClampToEdge = 0x8743 - } - - private void SetParameter(uint name, int param) => Gl.TextureParameteri(_hdc, name, param); - - private void SetParameter(uint name, float param) => Gl.TextureParameterf(_hdc, name, param); - - public Filter MinifyingFilter - { - set => SetParameter(0x2801, (int) value); - } - - public Filter MagnificationFilter - { - set => SetParameter(0x2800, (int) value); - } - - public Warp WarpS - { - set => SetParameter(0x2802, (int) value); - } - - public Warp WarpT - { - set => SetParameter(0x2803, (int) value); - } - - public void Use(uint slot) => Gl.BindTextureUnit(slot, _hdc); - - public static void UseRaw(uint slot, uint handle) => Gl.BindTextureUnit(slot, handle); - - public unsafe void Image(int level, Rect area, PixelTypes format, PixelDataFormats type, byte[] data) - { - fixed (byte* ptr = &data[0]) - { - Gl.TextureSubImage2D(_hdc, level, area.Min.X, area.Min.Y, area.Delta.X, area.Delta.Y, (uint) format, - (uint) type, (IntPtr) ptr); - } - } - - public uint Raw() => _hdc; - - private uint _hdc; - } -} \ No newline at end of file diff --git a/OpenGL/VertexArray.cs b/OpenGL/VertexArray.cs deleted file mode 100644 index 133318b..0000000 --- a/OpenGL/VertexArray.cs +++ /dev/null @@ -1,113 +0,0 @@ -// -// OpenGL: VertexArray.cs -// NEWorld: A Free Game with Similar Rules to Minecraft. -// Copyright (C) 2015-2018 NEWorld Team -// -// NEWorld is free software: you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// NEWorld is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General -// Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with NEWorld. If not, see . -// - -using System; -using Core.Utilities; - -namespace OpenGL -{ - public static partial class Gl - { - internal unsafe delegate void CreateVertexArraysProc(int n, uint* arrays); - - internal unsafe delegate void DeleteVertexArraysProc(int n, uint* arrays); - - internal delegate void BindVertexArrayProc(uint array); - - internal delegate void EnableVertexArrayAttribProc(uint vaobj, uint index); - - internal delegate void DisableVertexArrayAttribProc(uint vaobj, uint index); - - internal delegate void VertexArrayAttribFormatProc(uint vaobj, uint attribindex, int size, uint type, - byte normalized, uint relativeoffset); - - internal delegate void VertexArrayAttribBindingProc(uint vaobj, uint attribindex, uint bindingindex); - - internal delegate void VertexArrayVertexBufferProc(uint vaobj, uint bindingindex, uint buffer, - UIntPtr offset, int stride); - - internal static CreateVertexArraysProc CreateVertexArrays; - internal static DeleteVertexArraysProc DeleteVertexArrays; - internal static BindVertexArrayProc BindVertexArray; - internal static EnableVertexArrayAttribProc EnableVertexArrayAttrib; - internal static DisableVertexArrayAttribProc DisableVertexArrayAttrib; - internal static VertexArrayAttribFormatProc VertexArrayAttribFormat; - internal static VertexArrayAttribBindingProc VertexArrayAttribBinding; - internal static VertexArrayVertexBufferProc VertexArrayVertexBuffer; - - static partial void InitVertexArray() - { - DeleteVertexArrays = Get("glDeleteVertexArrays"); - BindVertexArray = Get("glBindVertexArray"); - EnableVertexArrayAttrib = Get("glEnableVertexArrayAttrib"); - DisableVertexArrayAttrib = Get("glDisableVertexArrayAttrib"); - VertexArrayAttribFormat = - Get("glVertexArrayAttribFormat"); - VertexArrayAttribBinding = - Get("glVertexArrayAttribBinding"); - VertexArrayVertexBuffer = Get("glVertexArrayVertexBuffer"); - CreateVertexArrays = Get("glCreateVertexArrays"); - } - } - - public class VertexArray : StrictDispose - { - public unsafe VertexArray() - { - fixed (uint* addr = &_hdc) - { - Gl.CreateVertexArrays(1, addr); - } - } - - protected override unsafe void Release() - { - fixed (uint* addr = &_hdc) - { - Gl.DeleteVertexArrays(1, addr); - } - } - - public void Use() => Gl.BindVertexArray(_hdc); - - public void EnableAttrib(uint index) => Gl.EnableVertexArrayAttrib(_hdc, index); - - public void DisableAttrib(uint index) => Gl.DisableVertexArrayAttrib(_hdc, index); - - public void AttribFormat(uint index, int size, uint type, bool normalized, uint relativeOffset) - { - byte norm = 0; - if (normalized) norm = 1; - Gl.VertexArrayAttribFormat(_hdc, index, size, type, norm, relativeOffset); - } - - public void AttribBinding(uint attribIndex, uint bufferIndex) => - Gl.VertexArrayAttribBinding(_hdc, attribIndex, bufferIndex); - - public void BindBuffer(uint index, DataBuffer buffer, uint offset, int stride) => - Gl.VertexArrayVertexBuffer(_hdc, index, buffer.Raw(), (UIntPtr) offset, stride); - - public void BindBuffer(uint index, ConstDataBuffer buffer, uint offset, int stride) => - Gl.VertexArrayVertexBuffer(_hdc, index, buffer.Raw(), (UIntPtr) offset, stride); - - public uint Raw() => _hdc; - - private uint _hdc; - } -} \ No newline at end of file