diff --git a/README.md b/README.md
index d59d93e48..9fb3ca438 100644
--- a/README.md
+++ b/README.md
@@ -77,7 +77,9 @@ The long-term goal of this project is to provide a viable alternative to Minecra
## Project status
-This list is non exhaustive.
+This list is non complete.
+
+See also the roadmap for 1.0.0 [here](https://github.com/Unarelith/OpenMiner/wiki/Roadmap).
### Implemented features
@@ -97,15 +99,15 @@ This list is non exhaustive.
- World loading/saving (see [#26](https://github.com/Unarelith/OpenMiner/issues/26))
- Texture pack system (partially implemented, see [#34](https://github.com/Unarelith/OpenMiner/issues/34))
- Entities ([#90](https://github.com/Unarelith/OpenMiner/pull/90))
+- Day/night cycle with sun/moon/stars ([#154](https://github.com/Unarelith/OpenMiner/pull/154))
### Missing features
- Fluid propagation ([#62](https://github.com/Unarelith/OpenMiner/issues/62))
-- Day/night cycle with sun/moon display ([#73](https://github.com/Unarelith/OpenMiner/issues/73))
- Seed-based worldgen ([#116](https://github.com/Unarelith/OpenMiner/issues/116))
- Cave tunnels ([#118](https://github.com/Unarelith/OpenMiner/issues/118))
-- Clouds ([#52](https://github.com/Unarelith/OpenMiner/pull/52))
-- Particle system
+- Clouds ([#156](https://github.com/Unarelith/OpenMiner/issues/156))
+- Particle system ([#155](https://github.com/Unarelith/OpenMiner/issues/155))
## Screenshots
diff --git a/docs/lua-api-sky.md b/docs/lua-api-sky.md
index 6ed6cc01f..7f0ec5abd 100644
--- a/docs/lua-api-sky.md
+++ b/docs/lua-api-sky.md
@@ -13,6 +13,31 @@ mod:sky {
fog_color = {
day = {50, 153, 204},
},
+
+ daylight_cycle = {
+ speed = 1.0
+ },
+
+ objects = {
+ sun = {
+ texture = "texture-sun",
+ size = 256,
+ },
+
+ moon = {
+ texture = "texture-moon_phases",
+ size = 256,
+ phases = {
+ count = 8,
+ size = 32
+ }
+ },
+
+ stars = {
+ count = 1000,
+ size = 4,
+ }
+ }
}
```
@@ -29,9 +54,26 @@ color = {
}
```
-Possible values:
+Attributes:
+
+- `day`: sky color at midday
+
+### `daylight_cycle`
+
+Day/night cycle parameters.
+
+Example:
+```lua
+daylight_cycle = {
+ speed = 1.0
+}
+```
+
+The example above is the minimal code required to add a day/night cycle to a sky.
+
+Attributes:
-- `day`: Sky color at midday
+- `speed`: speed of the cycle (default: `1.0`)
### `fog_color`
@@ -44,9 +86,9 @@ fog_color = {
}
```
-Possible values:
+Attributes:
-- `day`: Fog color at midday
+- `day`: fog color at midday
### `id`
@@ -59,3 +101,63 @@ id = "sky_nether"
IDs are usually of the form `mod:sky` but the `mod:` prefix is prepended automatically so it's not needed.
+### `objects`
+
+#### `moon`
+
+Moon attributes table.
+
+Example:
+```lua
+moon = {
+ texture = "texture-moon_phases"
+ size = 256,
+ phases = {
+ count = 8,
+ size = 32
+ }
+},
+```
+
+Attributes:
+
+- `texture`: texture to use (without texture, the moon will use this color: (240, 240, 240))
+- `size`: size of the moon (default: `256`)
+- `phases`
+ - `count`: amount of phases (default: `1`)
+ - `size`: size of the phase texture (default: `32`)
+
+#### `sun`
+
+Sun attribute table.
+
+Example:
+```lua
+sun = {
+ texture = "texture-sun",
+ size = 256,
+},
+```
+
+Attributes:
+
+- `texture`: texture to use (without texture, the sun will use this color: (255, 255, 0))
+- `size`: size of the sun (default: `256`)
+
+#### `stars`
+
+Stars attribute table.
+
+Example:
+```lua
+stars = {
+ count = 1000,
+ size = 4,
+}
+```
+
+Attributes:
+
+- `count`: size of the sun (default: `1000`)
+- `size`: size of the sun (default: `4`)
+
diff --git a/docs/network-protocol.md b/docs/network-protocol.md
index d38d77d68..cd2abfa5a 100644
--- a/docs/network-protocol.md
+++ b/docs/network-protocol.md
@@ -47,6 +47,14 @@ _This packet has no field._
### Clientbound
+#### ServerTick
+
+Packet sent at the beginning of every server tick.
+
+| Field name | Field type | Notes |
+| ------------- | ----------- | ---------------------------------------------------- |
+| Current time | u64 | Current time in the server |
+
#### ServerClosed
| Field name | Field type | Notes |
diff --git a/external/gamekit b/external/gamekit
index 1813cb06e..fb10f8bbf 160000
--- a/external/gamekit
+++ b/external/gamekit
@@ -1 +1 @@
-Subproject commit 1813cb06ef71c3d6f1ed80ca18a9f6b424af1c54
+Subproject commit fb10f8bbf8bcf88e77020a961412f52b83a125e6
diff --git a/mods/default/blocks.lua b/mods/default/blocks.lua
index db0db4ab1..9a62b6684 100644
--- a/mods/default/blocks.lua
+++ b/mods/default/blocks.lua
@@ -152,6 +152,9 @@ mod:block {
name = "Glowstone",
tiles = "glowstone.png",
is_light_source = true,
+ groups = {
+ om_material_stone = 1
+ },
}
mod:block {
@@ -338,6 +341,9 @@ mod:block {
id = "redstone_lamp",
name = "Redstone Lamp",
tiles = "redstone_lamp_off.png",
+ groups = {
+ om_material_stone = 1
+ },
states = {
{ is_light_source = true, tiles = "redstone_lamp_on.png" }
diff --git a/mods/default/sky.lua b/mods/default/sky.lua
index f7726df8f..2c032ec5b 100644
--- a/mods/default/sky.lua
+++ b/mods/default/sky.lua
@@ -35,6 +35,31 @@ mod:sky {
fog_color = {
day = {50, 153, 204},
},
+
+ daylight_cycle = {
+ speed = 1.0
+ },
+
+ objects = {
+ sun = {
+ texture = "texture-sun", -- FIXME: Use a path instead like block attribute 'tiles'
+ size = 256,
+ },
+
+ moon = {
+ texture = "texture-moon_phases", -- FIXME: ^
+ size = 256,
+ phases = {
+ count = 8,
+ size = 32
+ }
+ },
+
+ stars = {
+ count = 1000,
+ size = 4,
+ }
+ }
}
mod:sky {
diff --git a/mods/default/tools.lua b/mods/default/tools.lua
index 0e9f4a3b7..002abc92e 100644
--- a/mods/default/tools.lua
+++ b/mods/default/tools.lua
@@ -49,7 +49,7 @@ function register_tool(name, material, mining_speed, harvest_capability)
elseif name == "shovel" then
tool_def.effective_on = {
"group:om_material_dirt",
- "group:om_material:sand"
+ "group:om_material_sand"
}
tool_def.on_item_activated = function(pos, block, player, world, client, server)
@@ -61,6 +61,10 @@ function register_tool(name, material, mining_speed, harvest_capability)
tool_def.effective_on = {
"group:om_material_wood"
}
+ elseif name == "pickaxe" then
+ tool_def.effective_on = {
+ "group:om_material_stone"
+ }
end
mod:item(tool_def)
diff --git a/resources/config/textures.xml b/resources/config/textures.xml
index 32a4574df..2082e678a 100644
--- a/resources/config/textures.xml
+++ b/resources/config/textures.xml
@@ -2,7 +2,9 @@
+
+
diff --git a/resources/shaders/fog.f.glsl b/resources/shaders/fog.f.glsl
index cf5ba69da..f814fc027 100644
--- a/resources/shaders/fog.f.glsl
+++ b/resources/shaders/fog.f.glsl
@@ -1,10 +1,11 @@
#version 120
uniform vec4 u_fogColor;
+uniform vec4 u_skyColor;
vec4 fog(vec4 color, float fogCoord, float fogStart, float fogEnd) {
float fog = clamp((fogEnd - fogCoord) / (fogEnd - fogStart), 0.0, 1.0);
- return mix(u_fogColor, color, fog);
+ return mix(vec4(u_skyColor.r, u_skyColor.g, u_skyColor.b, u_fogColor.a), color, fog);
}
diff --git a/resources/shaders/game.f.glsl b/resources/shaders/game.f.glsl
index be92a4452..714baacb6 100644
--- a/resources/shaders/game.f.glsl
+++ b/resources/shaders/game.f.glsl
@@ -10,8 +10,12 @@ varying float v_blockFace;
varying float v_dist;
uniform int u_renderDistance;
+
uniform sampler2D u_tex;
+uniform vec4 u_skyColor;
+uniform float u_sunlightIntensity;
+
// Get light color
vec4 light(vec4 color, vec3 lightColor, vec4 lightPosition, float ambientIntensity, float diffuseIntensity);
@@ -24,6 +28,9 @@ void main() {
float blockFace = floor(v_blockFace + 0.5);
float lightCheck = floor(v_lightValue.x + 0.5);
+ // Discard if the pixel is too far away
+ if(blockFace > -1. && v_dist > u_renderDistance) discard;
+
// Get current pixel color and apply multiplier on grayscale textures
vec4 color = v_color;
if (v_texCoord.x > -0.99 && v_texCoord.y > -0.99) {
@@ -50,6 +57,8 @@ void main() {
float minBrightness = 2.0 / 16.0;
if (lightCheck != -1.) {
+ float sunlight = clamp(v_lightValue.x * u_sunlightIntensity, 3, 15);
+
float ambientIntensity = max(max(v_lightValue.x, v_lightValue.y) / 16.0, minBrightness);
float diffuseIntensity = max(v_lightValue.x, v_lightValue.y) / 32.0;
@@ -64,7 +73,9 @@ void main() {
if (blockFace == 2. || blockFace == 3.)
ambientIntensity = max(ambientIntensity * 0.9, minBrightness);
- color = light(color, vec3(1.0, 1.0, 1.0), v_coord3d, ambientIntensity, diffuseIntensity);
+ float lightval = clamp(sunlight / 15.0, v_lightValue.y / 15.0, 1.0);
+
+ color = light(color, vec3(lightval, lightval, lightval), v_coord3d, ambientIntensity, diffuseIntensity);
}
color.rgb *= v_ambientOcclusion;
diff --git a/resources/shaders/game.v.glsl b/resources/shaders/game.v.glsl
index 5f3fe4ccc..1205300a3 100644
--- a/resources/shaders/game.v.glsl
+++ b/resources/shaders/game.v.glsl
@@ -49,7 +49,7 @@ void main() {
v_lightValue = lightValue;
if (ambientOcclusion != 5) {
- const float aovalues[] = float[](0.5, 0.75, 0.9, 1.0);
+ const float aovalues[] = float[](0.2, 0.45, 0.75, 1.0);
v_ambientOcclusion = aovalues[int(ambientOcclusion)];
} else {
v_ambientOcclusion = 1.0;
diff --git a/resources/shaders/skybox.f.glsl b/resources/shaders/skybox.f.glsl
new file mode 100644
index 000000000..ed9f1d4eb
--- /dev/null
+++ b/resources/shaders/skybox.f.glsl
@@ -0,0 +1,27 @@
+#version 120
+
+varying vec4 v_color;
+varying vec4 v_coord3d;
+varying vec2 v_texCoord;
+
+uniform sampler2D u_tex;
+
+uniform vec4 u_skyColor;
+uniform vec4 u_starColor;
+
+void main() {
+ vec4 color = v_color;
+
+ if (v_texCoord.x > -0.99 && v_texCoord.y > -0.99) {
+ color = texture2D(u_tex, v_texCoord);
+ color += u_skyColor;
+ }
+ else if (color.a == 0) {
+ color = u_starColor;
+ }
+
+ if (color.a < 0.3) discard;
+
+ gl_FragColor = color;
+}
+
diff --git a/resources/shaders/skybox.v.glsl b/resources/shaders/skybox.v.glsl
new file mode 100644
index 000000000..36391927b
--- /dev/null
+++ b/resources/shaders/skybox.v.glsl
@@ -0,0 +1,22 @@
+#version 120
+
+attribute vec4 color;
+attribute vec4 coord3d;
+attribute vec2 texCoord;
+
+varying vec4 v_color;
+varying vec4 v_coord3d;
+varying vec2 v_texCoord;
+
+uniform mat4 u_modelMatrix;
+uniform mat4 u_projectionMatrix;
+uniform mat4 u_viewMatrix;
+
+void main() {
+ v_coord3d = u_modelMatrix * vec4(coord3d.xyz, 1.0);
+ v_color = color;
+ v_texCoord = texCoord;
+
+ gl_Position = u_projectionMatrix * u_viewMatrix * v_coord3d;
+}
+
diff --git a/resources/textures/moon_phases.png b/resources/textures/moon_phases.png
new file mode 100644
index 000000000..82d7c4b44
Binary files /dev/null and b/resources/textures/moon_phases.png differ
diff --git a/resources/textures/sun.png b/resources/textures/sun.png
new file mode 100644
index 000000000..9b0559e44
Binary files /dev/null and b/resources/textures/sun.png differ
diff --git a/source/client/core/Config.cpp b/source/client/core/Config.cpp
index cc22b77d1..1a893c170 100644
--- a/source/client/core/Config.cpp
+++ b/source/client/core/Config.cpp
@@ -50,11 +50,12 @@ bool Config::isCrosshairVisible = true;
// Graphics
u16 Config::renderDistance = 8;
+u8 Config::ambientOcclusion = 1;
bool Config::isSmoothLightingEnabled = true;
-bool Config::isAmbientOcclusionEnabled = false;
bool Config::isWireframeModeEnabled = false;
bool Config::isFullscreenModeEnabled = false;
bool Config::isVerticalSyncEnabled = true;
+bool Config::isStarRenderingEnabled = true;
float Config::cameraFOV = 70.0f;
u16 Config::screenWidth = 1600;
u16 Config::screenHeight = 1050;
@@ -90,16 +91,17 @@ void Config::loadConfigFromFile(const char *filename) {
isCrosshairVisible = lua["isCrosshairVisible"].get_or(isCrosshairVisible);
renderDistance = lua["renderDistance"].get_or(renderDistance);
+ ambientOcclusion = std::clamp(lua["ambientOcclusion"].get_or(ambientOcclusion), 0, 2);
isSmoothLightingEnabled = lua["isSmoothLightingEnabled"].get_or(isSmoothLightingEnabled);
- isAmbientOcclusionEnabled = lua["isAmbientOcclusionEnabled"].get_or(isAmbientOcclusionEnabled);
isWireframeModeEnabled = lua["isWireframeModeEnabled"].get_or(isWireframeModeEnabled);
isFullscreenModeEnabled = lua["isFullscreenModeEnabled"].get_or(isFullscreenModeEnabled);
isVerticalSyncEnabled = lua["isVerticalSyncEnabled"].get_or(isVerticalSyncEnabled);
+ isStarRenderingEnabled = lua["isStarRenderingEnabled"].get_or(isStarRenderingEnabled);
cameraFOV = lua["cameraFOV"].get_or(cameraFOV);
screenWidth = lua["screenWidth"].get_or(screenWidth);
screenHeight = lua["screenHeight"].get_or(screenHeight);
- guiScale = lua["guiScale"].get_or(guiScale);
- mipmapLevels = lua["mipmapLevels"].get_or(mipmapLevels);
+ guiScale = std::clamp(lua["guiScale"].get_or(guiScale), 1, 3);
+ mipmapLevels = std::clamp(lua["mipmapLevels"].get_or(mipmapLevels), 0, 4);
mouseSensitivity = lua["mouseSensitivity"].get_or(mouseSensitivity);
@@ -126,11 +128,12 @@ void Config::saveConfigToFile(const char *filename) {
file << "isCrosshairVisible = " << (isCrosshairVisible ? "true" : "false") << std::endl;
file << std::endl;
file << "renderDistance = " << renderDistance << std::endl;
+ file << "ambientOcclusion = " << (u16)ambientOcclusion << std::endl;
file << "isSmoothLightingEnabled = " << (isSmoothLightingEnabled ? "true" : "false") << std::endl;
- file << "isAmbientOcclusionEnabled = " << (isAmbientOcclusionEnabled ? "true" : "false") << std::endl;
file << "isWireframeModeEnabled = " << (isWireframeModeEnabled ? "true" : "false") << std::endl;
file << "isFullscreenModeEnabled = " << (isFullscreenModeEnabled ? "true" : "false") << std::endl;
file << "isVerticalSyncEnabled = " << (isVerticalSyncEnabled ? "true" : "false") << std::endl;
+ file << "isStarRenderingEnabled = " << (isStarRenderingEnabled ? "true" : "false") << std::endl;
file << "cameraFOV = " << cameraFOV << std::endl;
file << "screenWidth = " << screenWidth << std::endl;
file << "screenHeight = " << screenHeight << std::endl;
diff --git a/source/client/core/Config.hpp b/source/client/core/Config.hpp
index 84764ea82..3ce6aa17b 100644
--- a/source/client/core/Config.hpp
+++ b/source/client/core/Config.hpp
@@ -45,11 +45,12 @@ namespace Config {
// Graphics
extern u16 renderDistance;
+ extern u8 ambientOcclusion;
extern bool isSmoothLightingEnabled;
- extern bool isAmbientOcclusionEnabled;
extern bool isWireframeModeEnabled;
extern bool isFullscreenModeEnabled;
extern bool isVerticalSyncEnabled;
+ extern bool isStarRenderingEnabled;
extern float cameraFOV;
extern u16 screenWidth;
extern u16 screenHeight;
diff --git a/source/client/graphics/CelestialObject.cpp b/source/client/graphics/CelestialObject.cpp
new file mode 100644
index 000000000..858199dec
--- /dev/null
+++ b/source/client/graphics/CelestialObject.cpp
@@ -0,0 +1,116 @@
+/*
+ * =====================================================================================
+ *
+ * OpenMiner
+ *
+ * Copyright (C) 2018-2020 Unarelith, Quentin Bazin
+ * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
+ *
+ * This file is part of OpenMiner.
+ *
+ * OpenMiner 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 2.1 of the License, or (at your option) any later version.
+ *
+ * OpenMiner 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 OpenMiner; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * =====================================================================================
+ */
+#include
+#include
+#include
+#include
+
+#include "CelestialObject.hpp"
+#include "GameTime.hpp"
+#include "Vertex.hpp"
+
+CelestialObject::CelestialObject() {
+}
+
+void CelestialObject::setTexture(const std::string &textureName) {
+ if (textureName.empty()) return;
+
+ m_texture = &gk::ResourceHandler::getInstance().get(textureName);
+
+ m_isUpdateNeeded = true;
+}
+
+void CelestialObject::updateVertexBuffer() const {
+ if (!m_width || !m_height) {
+ gkError() << "Can't update vertex buffer for celestial object of size 0";
+ return;
+ }
+
+ Vertex vertices[4] = {
+ // Rectangle vertices
+ {{0, m_width, 0, -1}},
+ {{0, 0, 0, -1}},
+ {{0, 0, m_height, -1}},
+ {{0, m_width, m_height, -1}},
+ };
+
+ for (u8 i = 0 ; i < 4 ; ++i) {
+ vertices[i].color[0] = m_color.r;
+ vertices[i].color[1] = m_color.g;
+ vertices[i].color[2] = m_color.b;
+ vertices[i].color[3] = m_color.a;
+ }
+
+ if (m_texture) {
+ gk::FloatRect texRect{0, 0, 1, 1};
+
+ if (m_phaseCount && m_phaseSize && m_currentPhase < m_phaseCount) {
+ u16 currentPhaseX = m_currentPhase % (m_texture->getSize().x / m_phaseSize);
+ u16 currentPhaseY = m_currentPhase / (m_texture->getSize().x / m_phaseSize);
+ texRect.x = currentPhaseX * m_phaseSize / float(m_texture->getSize().x);
+ texRect.y = currentPhaseY * m_phaseSize / float(m_texture->getSize().y);
+ texRect.sizeX = m_phaseSize / float(m_texture->getSize().x);
+ texRect.sizeY = m_phaseSize / float(m_texture->getSize().y);
+ }
+
+ vertices[0].texCoord[0] = texRect.x + texRect.sizeX;
+ vertices[0].texCoord[1] = texRect.y;
+ vertices[1].texCoord[0] = texRect.x;
+ vertices[1].texCoord[1] = texRect.y;
+ vertices[2].texCoord[0] = texRect.x;
+ vertices[2].texCoord[1] = texRect.y + texRect.sizeY;
+ vertices[3].texCoord[0] = texRect.x + texRect.sizeX;
+ vertices[3].texCoord[1] = texRect.y + texRect.sizeY;
+ }
+
+ gk::VertexBuffer::bind(&m_vbo);
+ m_vbo.setData(sizeof(vertices), vertices, GL_STATIC_DRAW);
+ gk::VertexBuffer::bind(nullptr);
+
+ m_isUpdateNeeded = false;
+}
+
+void CelestialObject::draw(gk::RenderTarget &target, gk::RenderStates states) const {
+ if (m_isUpdateNeeded)
+ updateVertexBuffer();
+
+ states.transform.rotate(-GameTime::getCurrentTime(m_rotationOffset, m_rotationSpeed) * 360.f, m_rotationAxis);
+ states.transform *= getTransform();
+
+ states.vertexAttributes = VertexAttribute::All;
+
+ if (m_texture)
+ states.texture = m_texture;
+
+ static const GLubyte indices[] = {
+ 0, 1, 3,
+ 3, 1, 2
+ };
+
+ target.drawElements(m_vbo, GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices, states);
+}
+
diff --git a/source/client/graphics/CelestialObject.hpp b/source/client/graphics/CelestialObject.hpp
new file mode 100644
index 000000000..fa1c90f97
--- /dev/null
+++ b/source/client/graphics/CelestialObject.hpp
@@ -0,0 +1,76 @@
+/*
+ * =====================================================================================
+ *
+ * OpenMiner
+ *
+ * Copyright (C) 2018-2020 Unarelith, Quentin Bazin
+ * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
+ *
+ * This file is part of OpenMiner.
+ *
+ * OpenMiner 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 2.1 of the License, or (at your option) any later version.
+ *
+ * OpenMiner 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 OpenMiner; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * =====================================================================================
+ */
+#ifndef CELESTIALOBJECT_HPP_
+#define CELESTIALOBJECT_HPP_
+
+#include
+#include
+#include
+#include
+
+class CelestialObject : public gk::Drawable, public gk::Transformable {
+ public:
+ CelestialObject();
+
+ float width() const { return m_width; }
+ float height() const { return m_height; }
+
+ void setColor(const gk::Color &color) { m_color = color; m_isUpdateNeeded = true; }
+ void setSize(float width, float height) { m_width = width; m_height = height; m_isUpdateNeeded = true; }
+ void setTexture(const std::string &textureName);
+ void setPhaseCount(u16 phaseCount, u16 phaseSize) { m_phaseCount = phaseCount; m_phaseSize = phaseSize; m_isUpdateNeeded = true; }
+ void setCurrentPhase(u16 currentPhase) const { if (m_currentPhase != currentPhase) { m_currentPhase = currentPhase; m_isUpdateNeeded = true; } }
+ void setRotationOffset(u16 rotationOffset) { m_rotationOffset = rotationOffset; }
+ void setRotationSpeed(float rotationSpeed) { m_rotationSpeed = rotationSpeed; }
+ void setRotationAxis(const gk::Vector3f &rotationAxis) { m_rotationAxis = rotationAxis; }
+
+ private:
+ void updateVertexBuffer() const;
+
+ void draw(gk::RenderTarget &target, gk::RenderStates states) const override;
+
+ gk::VertexBuffer m_vbo;
+
+ gk::Color m_color = gk::Color::White;
+
+ float m_width = 0.f;
+ float m_height = 0.f;
+
+ const gk::Texture *m_texture = nullptr;
+
+ mutable bool m_isUpdateNeeded = true;
+
+ u16 m_phaseCount = 0;
+ u16 m_phaseSize = 0;
+ mutable u16 m_currentPhase = 0;
+
+ u16 m_rotationOffset = 0;
+ float m_rotationSpeed = 1.f;
+ gk::Vector3f m_rotationAxis{0, 1, 0};
+};
+
+#endif // CELESTIALOBJECT_HPP_
diff --git a/source/client/graphics/PlayerBox.cpp b/source/client/graphics/PlayerBox.cpp
index c02ce6f45..a7b34dc91 100644
--- a/source/client/graphics/PlayerBox.cpp
+++ b/source/client/graphics/PlayerBox.cpp
@@ -272,7 +272,7 @@ void PlayerBox::updateVertexBuffer() {
void PlayerBox::draw(gk::RenderTarget &target, gk::RenderStates states) const {
// Subtract the camera position - see comment in ClientWorld::draw()
- gk::Vector3d cameraPosition = m_camera.getDPosition();
+ const gk::Vector3d &cameraPosition = m_camera.getDPosition();
states.transform.translate(m_x - cameraPosition.x, m_y - cameraPosition.y, m_z - cameraPosition.z);
states.transform.rotate(m_viewAngleH, gk::Vector3{0, 0, 1});
diff --git a/source/client/graphics/Skybox.cpp b/source/client/graphics/Skybox.cpp
new file mode 100644
index 000000000..ed714b777
--- /dev/null
+++ b/source/client/graphics/Skybox.cpp
@@ -0,0 +1,123 @@
+/*
+ * =====================================================================================
+ *
+ * OpenMiner
+ *
+ * Copyright (C) 2018-2020 Unarelith, Quentin Bazin
+ * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
+ *
+ * This file is part of OpenMiner.
+ *
+ * OpenMiner 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 2.1 of the License, or (at your option) any later version.
+ *
+ * OpenMiner 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 OpenMiner; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * =====================================================================================
+ */
+#include "ClientWorld.hpp"
+#include "GameTime.hpp"
+#include "Sky.hpp"
+#include "Skybox.hpp"
+
+Skybox::Skybox(gk::Camera &camera, ClientWorld &world) : m_camera(camera), m_world(world) {
+ m_shader.createProgram();
+ m_shader.addShader(GL_VERTEX_SHADER, "resources/shaders/skybox.v.glsl");
+ m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/skybox.f.glsl");
+ m_shader.linkProgram();
+}
+
+void Skybox::loadSky(const Sky &sky) {
+ const Sky::SunDefinition &sun = sky.sunDefinition();
+ m_sun = CelestialObject{};
+ m_sun.setSize(sun.size, sun.size);
+ m_sun.setPosition(500, -m_sun.width() / 2, -m_sun.height() / 2);
+ m_sun.setRotationSpeed(sky.daylightCycleSpeed());
+
+ try {
+ m_sun.setTexture(sun.texture);
+ }
+ catch (...) {
+ m_sun.setColor(gk::Color::Yellow);
+ gkWarning() << "Failed to load sun texture" << sun.texture;
+ }
+
+ const Sky::MoonDefinition &moon = sky.moonDefinition();
+ m_moon = CelestialObject{};
+ m_moon.setSize(moon.size, moon.size);
+ m_moon.setPosition(-500, -m_moon.width() / 2, -m_moon.height() / 2);
+ m_moon.setPhaseCount(moon.phaseCount, moon.phaseSize);
+ m_moon.setCurrentPhase(0);
+ m_moon.setRotationSpeed(sky.daylightCycleSpeed());
+
+ try {
+ m_moon.setTexture(moon.texture);
+ }
+ catch (...) {
+ m_moon.setColor(gk::Color{240, 240, 240});
+ gkWarning() << "Failed to load moon texture" << sun.texture;
+ }
+
+ const Sky::StarsDefinition &stars = sky.starsDefinition();
+ m_stars.clear();
+ m_stars.reserve(stars.count);
+ for (int i = 0 ; i < stars.count ; ++i) {
+ auto &star = m_stars.emplace_back();
+ star.setColor(gk::Color{0, 0, 0, 0});
+ star.setSize(stars.size, stars.size);
+
+ glm::vec3 v{rand() % 256, rand() % 256, rand() % 256};
+ v = glm::normalize(v);
+ v *= 600 * (rand() % 2 * 2 - 1);
+ star.setPosition(v.x, v.y, v.z);
+ // star.setPosition(650 * ((rand() % 2) * 2 - 1), (rand() % 500) * 2 - 500, (rand() % 500) * 2 - 500);
+
+ star.setRotationOffset(rand() % GameTime::dayLength);
+ star.setRotationSpeed(sky.daylightCycleSpeed());
+ star.setRotationAxis({0, 1, 0});
+ // Maybe sometimes stars could have a random axis?
+ // star.setRotationAxis({rand() % 100 / 100.f, rand() % 100 / 100.f, rand() % 100 / 100.f});
+ }
+}
+
+void Skybox::draw(gk::RenderTarget &target, gk::RenderStates states) const {
+ if (!m_world.sky()) return;
+
+ float time = GameTime::getCurrentTime(0, m_world.sky()->daylightCycleSpeed());
+ gk::Color skyColor = GameTime::getSkyColorFromTime(*m_world.sky(), time);
+ gk::Color starColor = m_world.sky()->color();
+
+ gk::Shader::bind(&m_shader);
+ m_shader.setUniform("u_skyColor", skyColor);
+ m_shader.setUniform("u_starColor", starColor);
+ gk::Shader::bind(nullptr);
+
+ m_moon.setCurrentPhase((GameTime::getTicks() / GameTime::dayLength) % 8);
+
+ states.shader = &m_shader;
+
+ // Subtract the camera position - see comment in ClientWorld::draw()
+ const gk::Vector3d &cameraPosition = m_camera.getDPosition();
+ states.transform.translate(cameraPosition.x, cameraPosition.y, cameraPosition.z - 50);
+
+ if (m_sun.width() && m_sun.height())
+ target.draw(m_sun, states);
+
+ if (m_moon.width() && m_moon.height())
+ target.draw(m_moon, states);
+
+ if (Config::isStarRenderingEnabled && skyColor != starColor) {
+ for (auto &it : m_stars)
+ target.draw(it, states);
+ }
+}
+
diff --git a/source/client/graphics/Skybox.hpp b/source/client/graphics/Skybox.hpp
new file mode 100644
index 000000000..f54a9f7dc
--- /dev/null
+++ b/source/client/graphics/Skybox.hpp
@@ -0,0 +1,58 @@
+/*
+ * =====================================================================================
+ *
+ * OpenMiner
+ *
+ * Copyright (C) 2018-2020 Unarelith, Quentin Bazin
+ * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
+ *
+ * This file is part of OpenMiner.
+ *
+ * OpenMiner 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 2.1 of the License, or (at your option) any later version.
+ *
+ * OpenMiner 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 OpenMiner; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * =====================================================================================
+ */
+#ifndef SKYBOX_HPP_
+#define SKYBOX_HPP_
+
+#include
+#include
+
+#include "CelestialObject.hpp"
+
+class ClientWorld;
+class Sky;
+
+class Skybox : public gk::Drawable, public gk::Transformable {
+ public:
+ Skybox(gk::Camera &camera, ClientWorld &world);
+
+ void loadSky(const Sky &sky);
+
+ private:
+ void draw(gk::RenderTarget &target, gk::RenderStates states) const override;
+
+ gk::Camera &m_camera;
+ ClientWorld &m_world;
+
+ gk::Shader m_shader;
+
+ CelestialObject m_sun;
+ CelestialObject m_moon;
+
+ std::vector m_stars;
+};
+
+#endif // SKYBOX_HPP_
diff --git a/source/client/hud/BlockCursor.cpp b/source/client/hud/BlockCursor.cpp
index 53da58a78..cd8c573a8 100644
--- a/source/client/hud/BlockCursor.cpp
+++ b/source/client/hud/BlockCursor.cpp
@@ -282,7 +282,7 @@ void BlockCursor::draw(gk::RenderTarget &target, gk::RenderStates states) const
glCheck(glEnable(GL_DEPTH_TEST));
// Subtract the camera position - see comment in ClientWorld::draw()
- gk::Vector3d cameraPosition = m_player.camera().getDPosition();
+ const gk::Vector3d &cameraPosition = m_player.camera().getDPosition();
states.transform.translate(m_selectedBlock.x - cameraPosition.x, m_selectedBlock.y - cameraPosition.y, m_selectedBlock.z - cameraPosition.z);
glCheck(glPolygonMode(GL_FRONT_AND_BACK, GL_LINE));
diff --git a/source/client/hud/DebugOverlay.cpp b/source/client/hud/DebugOverlay.cpp
index 1c67a9c41..15ef4b30a 100644
--- a/source/client/hud/DebugOverlay.cpp
+++ b/source/client/hud/DebugOverlay.cpp
@@ -33,6 +33,7 @@
#include "ClientWorld.hpp"
#include "Config.hpp"
#include "DebugOverlay.hpp"
+#include "GameTime.hpp"
DebugOverlay::DebugOverlay(const ClientPlayer &player, const ClientWorld &world)
: m_player(player), m_world(world)
@@ -84,6 +85,19 @@ void DebugOverlay::update() {
stream << "Alive entities: " << m_world.scene().registry().alive();
stream << '\n';
stream << "Chunk updates: " << ClientChunk::chunkUpdatesPerSec;
+ stream << '\n';
+ stream << "Ticks: " << GameTime::getTicks();
+ stream << '\n';
+ stream << "TPS: " << GameTime::getTicksPerSecond();
+ stream << '\n';
+ stream << "Game time: ";
+
+ u32 day = GameTime::getCurrentDay();
+ u16 hour = GameTime::getCurrentHour();
+ u16 minute = GameTime::getCurrentMinute();
+ stream << "Day " << day << " ";
+ stream << (hour < 10 ? "0" : "") << hour << ":";
+ stream << (minute < 10 ? "0" : "") << minute;
m_positionText.setString(stream.str());
}
diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp
index 1ee3a4646..794224a04 100644
--- a/source/client/network/ClientCommandHandler.cpp
+++ b/source/client/network/ClientCommandHandler.cpp
@@ -37,6 +37,7 @@
#include "ConnectionErrorState.hpp"
#include "DrawableComponent.hpp"
#include "DrawableDef.hpp"
+#include "GameTime.hpp"
#include "LuaGUIState.hpp"
#include "NetworkComponent.hpp"
#include "PositionComponent.hpp"
@@ -167,6 +168,14 @@ void ClientCommandHandler::setupCallbacks() {
}
});
+ m_client.setCommandCallback(Network::Command::ServerTick, [this](Network::Packet &packet) {
+ if (!m_isSingleplayer) { // FIXME
+ sf::Uint64 ticks;
+ packet >> ticks;
+ GameTime::setTicks(ticks);
+ }
+ });
+
m_client.setCommandCallback(Network::Command::ServerClosed, [this](Network::Packet &packet) {
std::string message;
packet >> message;
diff --git a/source/client/states/GameState.cpp b/source/client/states/GameState.cpp
index c3de47466..c86f1f9fa 100644
--- a/source/client/states/GameState.cpp
+++ b/source/client/states/GameState.cpp
@@ -24,14 +24,16 @@
*
* =====================================================================================
*/
+#include
+#include
#include
#include
#include
#include
-#include
#include
+#include
#include
#include
#include
@@ -40,6 +42,7 @@
#include "Events.hpp"
#include "GameKey.hpp"
#include "GameState.hpp"
+#include "GameTime.hpp"
#include "KeyboardHandler.hpp"
#include "LuaGUIState.hpp"
#include "PauseMenuState.hpp"
@@ -56,6 +59,7 @@ GameState::GameState()
m_clientCommandHandler.setupCallbacks();
m_camera.setAspectRatio((float)Config::screenWidth / Config::screenHeight);
+ m_camera.setFarClippingPlane(1000.0f);
m_world.setClient(m_clientCommandHandler);
m_world.setCamera(m_player.camera());
@@ -181,22 +185,19 @@ void GameState::update() {
m_client.update();
- // Update far plane using render distance
- static u16 oldRenderDistance = Config::renderDistance;
- if (Config::renderDistance != oldRenderDistance) {
- m_camera.setFarClippingPlane(Config::renderDistance * CHUNK_MAXSIZE);
- oldRenderDistance = Config::renderDistance;
+ static const Sky *sky = nullptr;
+ if (sky != m_world.sky() && m_world.sky()) {
+ sky = m_world.sky();
+ m_skybox.loadSky(*sky);
}
}
void GameState::initShaders() {
m_shader.createProgram();
-
m_shader.addShader(GL_VERTEX_SHADER, "resources/shaders/game.v.glsl");
m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/light.f.glsl");
m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/fog.f.glsl");
m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/game.f.glsl");
-
m_shader.linkProgram();
m_fbo.loadShader("screen");
@@ -207,15 +208,35 @@ void GameState::onGuiScaleChanged(const GuiScaleChangedEvent &event) {
}
void GameState::draw(gk::RenderTarget &target, gk::RenderStates states) const {
- // FIXME: This uniform is not used anymore since water/leaves effects are disabled
- // gk::Shader::bind(&m_shader);
- // m_shader.setUniform("u_time", gk::GameClock::getInstance().getTicks());
- // gk::Shader::bind(nullptr);
+ gk::Shader::bind(&m_shader);
+
+ if (m_world.sky()) {
+ if (m_world.sky()->daylightCycleSpeed()) {
+ float time = GameTime::getCurrentTime(0, m_world.sky()->daylightCycleSpeed());
+ const gk::Color &color = GameTime::getSkyColorFromTime(*m_world.sky(), time);
+ glClearColor(color.r, color.g, color.b, color.a);
+
+ m_shader.setUniform("u_skyColor", color);
+ m_shader.setUniform("u_sunlightIntensity", GameTime::getSunlightIntensityFromTime(time));
+ }
+ else {
+ const gk::Color &color = m_world.sky()->color();
+ glClearColor(color.r, color.g, color.b, color.a);
+
+ m_shader.setUniform("u_skyColor", m_world.sky()->color());
+ m_shader.setUniform("u_sunlightIntensity", 1.f);
+ }
+ }
+
+ gk::Shader::bind(nullptr);
m_fbo.begin();
states.shader = &m_shader;
+
target.setView(m_camera);
+
+ target.draw(m_skybox, states);
target.draw(m_world, states);
for (auto &it : m_playerBoxes)
diff --git a/source/client/states/GameState.hpp b/source/client/states/GameState.hpp
index 47f6b6cf9..5b1956a62 100644
--- a/source/client/states/GameState.hpp
+++ b/source/client/states/GameState.hpp
@@ -43,6 +43,7 @@
#include "KeyboardHandler.hpp"
#include "PlayerBox.hpp"
#include "Registry.hpp"
+#include "Skybox.hpp"
class TextureAtlas;
@@ -99,6 +100,8 @@ class GameState : public gk::ApplicationState {
KeyboardHandler *m_keyboardHandler;
bool m_areModKeysLoaded = false;
+
+ Skybox m_skybox{m_camera, m_world};
};
#endif // GAMESTATE_HPP_
diff --git a/source/client/states/SettingsMenuState.cpp b/source/client/states/SettingsMenuState.cpp
index cf4faac67..a2874572d 100644
--- a/source/client/states/SettingsMenuState.cpp
+++ b/source/client/states/SettingsMenuState.cpp
@@ -215,13 +215,21 @@ void SettingsMenuState::addGraphicsButtons() {
Config::isSmoothLightingEnabled = !Config::isSmoothLightingEnabled;
button.setText(std::string("Smooth Lighting: ") + (Config::isSmoothLightingEnabled ? "ON" : "OFF"));
- m_aoButton->setEnabled(!Config::isSmoothLightingEnabled);
-
World::isReloadRequested = true;
});
- m_aoButton = &addToggleButton("Ambient Occlusion", Config::isAmbientOcclusionEnabled, true);
- m_aoButton->setEnabled(!Config::isSmoothLightingEnabled);
+ const std::string aoValueNames[3] = {
+ "OFF",
+ "Fast",
+ "Fancy"
+ };
+
+ m_menuWidget.addButton(std::string("Ambient Occlusion: ") + aoValueNames[Config::ambientOcclusion], [&, aoValueNames] (TextButton &button) {
+ Config::ambientOcclusion = (Config::ambientOcclusion + 1) % (Config::isSmoothLightingEnabled ? 3 : 2);
+ button.setText(std::string("Ambient Occlusion: ") + aoValueNames[Config::ambientOcclusion]);
+
+ World::isReloadRequested = true;
+ });
m_menuWidget.addSlider("GUI Scale: " + std::to_string(Config::guiScale), [this] (SliderWidget &slider, u32 eventType) {
slider.setText("GUI Scale: " + std::to_string(slider.getCurrentValue()));
@@ -259,6 +267,8 @@ void SettingsMenuState::addGraphicsButtons() {
slider.setText("Mipmap Levels: " + std::to_string(Config::mipmapLevels));
}, 0, 4, Config::mipmapLevels);
+ addToggleButton("Star Rendering", Config::isStarRenderingEnabled, false);
+
updateWidgetPosition();
}
diff --git a/source/client/world/ChunkBuilder.cpp b/source/client/world/ChunkBuilder.cpp
index 407bc7f64..6aabef520 100644
--- a/source/client/world/ChunkBuilder.cpp
+++ b/source/client/world/ChunkBuilder.cpp
@@ -260,7 +260,7 @@ inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk
}
auto addVertex = [&](u8 v) {
- if (!Config::isAmbientOcclusionEnabled || Config::isSmoothLightingEnabled)
+ if (Config::ambientOcclusion != 1 || blockState.isLightSource())
vertices[v].ambientOcclusion = 5;
if (blockState.drawType() == BlockDrawType::Liquid)
@@ -411,10 +411,8 @@ inline u8 ChunkBuilder::getLightForVertex(Light light, s8f x, s8f y, s8f z, cons
// continue;
// If the chunk is initialized, add the light value to the total
- if (lightValues[i] != -1) {
- // float strength = ((surroundingBlocks[i] - normal == gk::Vector3i{x, y, z}) ? 1 : Config::aoStrength);
- // total += lightValues[i] * strength;
- // count += strength;
+ // But only add dark blocks if AO is set on Smooth Lighting
+ if (lightValues[i] != -1 && (Config::ambientOcclusion == 2 || lightValues[i] != 0)) {
total += lightValues[i];
++count;
}
diff --git a/source/client/world/ClientWorld.cpp b/source/client/world/ClientWorld.cpp
index 398303e0e..cba91c1af 100644
--- a/source/client/world/ClientWorld.cpp
+++ b/source/client/world/ClientWorld.cpp
@@ -112,8 +112,7 @@ void ClientWorld::changeDimension(u16 dimensionID) {
const Sky &sky = Registry::getInstance().getSkyFromStringID(dimension.sky());
m_sky = &sky;
- glCheck(glClearColor(sky.color().r, sky.color().g, sky.color().b, sky.color().a));
-
+ // glCheck(glClearColor(sky.color().r, sky.color().g, sky.color().b, sky.color().a));
}
void ClientWorld::receiveChunkData(Network::Packet &packet) {
diff --git a/source/client/world/ClientWorld.hpp b/source/client/world/ClientWorld.hpp
index d9aecc13b..0d57ffe67 100644
--- a/source/client/world/ClientWorld.hpp
+++ b/source/client/world/ClientWorld.hpp
@@ -68,6 +68,8 @@ class ClientWorld : public World, public gk::Drawable {
std::size_t loadedChunkCount() const { return m_chunks.size(); }
+ const Sky *sky() const { return m_sky; }
+
private:
void createChunkNeighbours(ClientChunk *chunk);
diff --git a/source/common/core/GameTime.cpp b/source/common/core/GameTime.cpp
new file mode 100644
index 000000000..ce5749de6
--- /dev/null
+++ b/source/common/core/GameTime.cpp
@@ -0,0 +1,72 @@
+/*
+ * =====================================================================================
+ *
+ * OpenMiner
+ *
+ * Copyright (C) 2018-2020 Unarelith, Quentin Bazin
+ * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
+ *
+ * This file is part of OpenMiner.
+ *
+ * OpenMiner 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 2.1 of the License, or (at your option) any later version.
+ *
+ * OpenMiner 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 OpenMiner; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * =====================================================================================
+ */
+#include
+
+#include
+
+#include "GameTime.hpp"
+#include "Sky.hpp"
+
+u64 GameTime::s_ticks = 0;
+u16 GameTime::s_ticksPerSecond = 0;
+
+float GameTime::getCurrentTime(float offset, float speed) {
+ return std::fmod((s_ticks + dayStartOffset) * daySpeed * speed + offset, dayLength) / dayLength;
+}
+
+float GameTime::getSunlightIntensityFromTime(float time) {
+ static const float pi = 3.1415927f;
+ return std::clamp(0.5f + std::sin(2 * pi * time) * 2.0f, 0.0f, 1.0f);
+}
+
+gk::Color GameTime::getSkyColorFromTime(const Sky &sky, float time) {
+ float sunlight = getSunlightIntensityFromTime(time);
+
+ gk::Color skyColor = sky.color();
+ skyColor.r = std::clamp(sunlight - (1.f - skyColor.r), 0.0f, skyColor.r);
+ skyColor.g = std::clamp(sunlight - (1.f - skyColor.g), 0.0f, skyColor.g);
+ skyColor.b = std::clamp(sunlight - (1.f - skyColor.b), 0.0f, skyColor.b);
+ skyColor.a = 1.f;
+
+ return skyColor;
+}
+
+void GameTime::updateTpsCounter() {
+ static u64 tpsTimer = gk::GameClock::getInstance().getTicks(true);
+ static u64 tpsStart = s_ticks;
+
+ if (tpsStart > s_ticks)
+ tpsStart = s_ticks;
+
+ u64 currentClockTicks = gk::GameClock::getInstance().getTicks(true);
+ if (currentClockTicks - tpsTimer > 1000) {
+ s_ticksPerSecond = floor((s_ticks - tpsStart) / ((currentClockTicks - tpsTimer) / 1000.0f) + 0.5f);
+ tpsTimer = currentClockTicks;
+ tpsStart = s_ticks;
+ }
+}
+
diff --git a/source/common/core/GameTime.hpp b/source/common/core/GameTime.hpp
new file mode 100644
index 000000000..01178c9da
--- /dev/null
+++ b/source/common/core/GameTime.hpp
@@ -0,0 +1,70 @@
+/*
+ * =====================================================================================
+ *
+ * OpenMiner
+ *
+ * Copyright (C) 2018-2020 Unarelith, Quentin Bazin
+ * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
+ *
+ * This file is part of OpenMiner.
+ *
+ * OpenMiner 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 2.1 of the License, or (at your option) any later version.
+ *
+ * OpenMiner 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 OpenMiner; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * =====================================================================================
+ */
+#ifndef GAMETIME_HPP_
+#define GAMETIME_HPP_
+
+#include
+
+class Sky;
+
+class GameTime {
+ public:
+ static constexpr float daySpeed = 1.f;
+ static constexpr u32 dayLength = 24000;
+ static constexpr u32 dayStartOffset = 0;
+
+ // Note: These 3 functions are only needed in the client
+ static float getCurrentTime(float offset = 0.f, float speed = 1.f);
+ static float getSunlightIntensityFromTime(float time);
+ static gk::Color getSkyColorFromTime(const Sky &sky, float time);
+
+ static void incrementTicks() { ++s_ticks; updateTpsCounter(); }
+ static void setTicks(u64 ticks) { s_ticks = ticks; updateTpsCounter(); }
+
+ static u16 getTicksPerSecond() { return s_ticksPerSecond; }
+ static u64 getTicks() { return s_ticks; }
+
+ static u32 getCurrentDay() {
+ return (s_ticks + dayStartOffset + 6000.f) / 1000.f / 24 + 1;
+ }
+
+ static u8 getCurrentHour() {
+ return u64((s_ticks + dayStartOffset + 6000.f) / 1000.f) % 24;
+ }
+
+ static u8 getCurrentMinute() {
+ return u64((s_ticks + dayStartOffset + 6000.f) / 1000.f * 60.0f) % 60;
+ }
+
+ private:
+ static void updateTpsCounter();
+
+ static u64 s_ticks;
+ static u16 s_ticksPerSecond;
+};
+
+#endif // GAMETIME_HPP_
diff --git a/source/common/network/Network.cpp b/source/common/network/Network.cpp
index d89f91007..9f1c8384e 100644
--- a/source/common/network/Network.cpp
+++ b/source/common/network/Network.cpp
@@ -37,6 +37,7 @@ std::string Network::commandToString(Network::Command command) {
{Network::Command::ClientOk, "ClientOk"},
{Network::Command::ClientRefused, "ClientRefused"},
+ {Network::Command::ServerTick, "ServerTick"},
{Network::Command::ServerClosed, "ServerClosed"},
{Network::Command::ChunkData, "ChunkData"},
diff --git a/source/common/network/Network.hpp b/source/common/network/Network.hpp
index 9a883effb..ed8b3ff04 100644
--- a/source/common/network/Network.hpp
+++ b/source/common/network/Network.hpp
@@ -40,40 +40,41 @@ namespace Network {
ClientOk = 0x02,
ClientRefused = 0x03,
- ServerClosed = 0x04,
+ ServerTick = 0x04,
+ ServerClosed = 0x05,
- ChunkData = 0x05,
- ChunkRequest = 0x06,
+ ChunkData = 0x06,
+ ChunkRequest = 0x07,
- PlayerPlaceBlock = 0x07,
- PlayerDigBlock = 0x08,
- PlayerInvUpdate = 0x09,
- PlayerPosUpdate = 0x0a,
- PlayerRotUpdate = 0x0b,
- PlayerSpawn = 0x0c,
- PlayerChangeDimension = 0x0d,
- PlayerHeldItemChanged = 0x0e,
+ PlayerPlaceBlock = 0x08,
+ PlayerDigBlock = 0x09,
+ PlayerInvUpdate = 0x0a,
+ PlayerPosUpdate = 0x0b,
+ PlayerRotUpdate = 0x0c,
+ PlayerSpawn = 0x0d,
+ PlayerChangeDimension = 0x0e,
+ PlayerHeldItemChanged = 0x0f,
- BlockUpdate = 0x0f,
- BlockActivated = 0x10,
- BlockGUIData = 0x11,
- BlockInvUpdate = 0x12,
- BlockDataUpdate = 0x13,
+ BlockUpdate = 0x10,
+ BlockActivated = 0x11,
+ BlockGUIData = 0x12,
+ BlockInvUpdate = 0x13,
+ BlockDataUpdate = 0x14,
- ItemActivated = 0x14,
+ ItemActivated = 0x15,
- RegistryData = 0x15,
+ RegistryData = 0x16,
- ChatMessage = 0x16,
+ ChatMessage = 0x17,
- EntitySpawn = 0x17,
- EntityDespawn = 0x18,
- EntityPosition = 0x19,
- EntityRotation = 0x1a,
- EntityAnimation = 0x1b,
- EntityDrawableDef = 0x1c,
+ EntitySpawn = 0x18,
+ EntityDespawn = 0x19,
+ EntityPosition = 0x1a,
+ EntityRotation = 0x1b,
+ EntityAnimation = 0x1c,
+ EntityDrawableDef = 0x1d,
- KeyPressed = 0x1d,
+ KeyPressed = 0x1e,
};
std::string commandToString(Command command);
diff --git a/source/common/world/Sky.cpp b/source/common/world/Sky.cpp
index 0f69ac814..820b51134 100644
--- a/source/common/world/Sky.cpp
+++ b/source/common/world/Sky.cpp
@@ -33,10 +33,20 @@ Sky::Sky(u16 id, const std::string &stringID) {
}
void Sky::serialize(sf::Packet &packet) const {
- packet << m_id << m_stringID << m_color << m_fogColor;
+ packet << m_id << m_stringID << m_color << m_fogColor
+ << m_sunDefinition.texture << m_sunDefinition.size
+ << m_moonDefinition.texture << m_moonDefinition.size
+ << m_moonDefinition.phaseCount << m_moonDefinition.phaseSize
+ << m_starsDefinition.count << m_starsDefinition.size
+ << m_daylightCycleSpeed;
}
void Sky::deserialize(sf::Packet &packet) {
- packet >> m_id >> m_stringID >> m_color >> m_fogColor;
+ packet >> m_id >> m_stringID >> m_color >> m_fogColor
+ >> m_sunDefinition.texture >> m_sunDefinition.size
+ >> m_moonDefinition.texture >> m_moonDefinition.size
+ >> m_moonDefinition.phaseCount >> m_moonDefinition.phaseSize
+ >> m_starsDefinition.count >> m_starsDefinition.size
+ >> m_daylightCycleSpeed;
}
diff --git a/source/common/world/Sky.hpp b/source/common/world/Sky.hpp
index 57b1ad35e..31b20d75e 100644
--- a/source/common/world/Sky.hpp
+++ b/source/common/world/Sky.hpp
@@ -49,12 +49,46 @@ class Sky : public gk::ISerializable {
void setColor(const gk::Color &color) { m_color = color; }
void setFogColor(const gk::Color &fogColor) { m_fogColor = fogColor; }
+ struct SunDefinition {
+ std::string texture;
+ float size;
+ };
+
+ struct MoonDefinition {
+ std::string texture;
+ float size;
+ u16 phaseCount;
+ u16 phaseSize;
+ };
+
+ struct StarsDefinition {
+ u16 count;
+ float size;
+ };
+
+ const SunDefinition &sunDefinition() const { return m_sunDefinition; }
+ const MoonDefinition &moonDefinition() const { return m_moonDefinition; }
+ const StarsDefinition &starsDefinition() const { return m_starsDefinition; }
+
+ void setSunDefinition(const SunDefinition &sunDefinition) { m_sunDefinition = sunDefinition; }
+ void setMoonDefinition(const MoonDefinition &moonDefinition) { m_moonDefinition = moonDefinition; }
+ void setStarsDefinition(const StarsDefinition &starsDefinition) { m_starsDefinition = starsDefinition; }
+
+ float daylightCycleSpeed() const { return m_daylightCycleSpeed; }
+ void setDaylightCycleSpeed(float daylightCycleSpeed) { m_daylightCycleSpeed = daylightCycleSpeed; }
+
private:
u16 m_id;
std::string m_stringID;
gk::Color m_color;
gk::Color m_fogColor;
+
+ SunDefinition m_sunDefinition;
+ MoonDefinition m_moonDefinition;
+ StarsDefinition m_starsDefinition;
+
+ float m_daylightCycleSpeed = 0.f;
};
#endif // SKY_HPP_
diff --git a/source/server/core/ServerApplication.cpp b/source/server/core/ServerApplication.cpp
index 247e8db6d..9bcc11778 100644
--- a/source/server/core/ServerApplication.cpp
+++ b/source/server/core/ServerApplication.cpp
@@ -28,6 +28,7 @@
#include "BlockGeometry.hpp"
#include "Events.hpp"
+#include "GameTime.hpp"
#include "ServerApplication.hpp"
#include "ServerBlock.hpp"
#include "ServerConfig.hpp"
@@ -193,7 +194,18 @@ int ServerApplication::run(bool isProtected) {
}
void ServerApplication::update() {
- m_worldController.update();
+ static u64 lastTick = m_clock.getTicks() / 50;
+ bool doTick = false;
+ if (lastTick < m_clock.getTicks() / 50) {
+ lastTick = m_clock.getTicks() / 50;
+ doTick = true;
+
+ GameTime::incrementTicks();
+
+ m_serverCommandHandler.sendServerTick();
+ }
+
+ m_worldController.update(doTick);
if (m_clock.getTicks() % 100 < 10) {
for (auto &it : m_players) {
diff --git a/source/server/lua/loader/LuaSkyLoader.cpp b/source/server/lua/loader/LuaSkyLoader.cpp
index 84fae4bb0..b2b775abc 100644
--- a/source/server/lua/loader/LuaSkyLoader.cpp
+++ b/source/server/lua/loader/LuaSkyLoader.cpp
@@ -50,5 +50,54 @@ void LuaSkyLoader::loadSky(const sol::table &table) const {
u8 a = fogColor["day"][4].get_or(255);
sky.setFogColor(gk::Color{r, g, b, a});
}
+
+ if (sol::object obj = table["daylight_cycle"] ; obj.valid()) {
+ sol::table daylightCycleTable = obj.as();
+ sky.setDaylightCycleSpeed(daylightCycleTable["speed"].get_or(0.f));
+ }
+
+ loadObjects(sky, table);
+}
+
+void LuaSkyLoader::loadObjects(Sky &sky, const sol::table &table) const {
+ if (sol::object obj = table["objects"] ; obj.valid()) {
+ sol::table objectsTable = obj.as();
+
+ if (sol::object obj = objectsTable["sun"] ; obj.valid()) {
+ sol::table sunTable = obj.as();
+
+ Sky::SunDefinition sunDefinition;
+ sunDefinition.texture = sunTable["texture"].get_or("");
+ sunDefinition.size = sunTable["size"].get_or(256);
+
+ sky.setSunDefinition(sunDefinition);
+ }
+
+ if (sol::object obj = objectsTable["moon"] ; obj.valid()) {
+ sol::table moonTable = obj.as();
+
+ Sky::MoonDefinition moonDefinition;
+ moonDefinition.texture = moonTable["texture"].get_or("");
+ moonDefinition.size = moonTable["size"].get_or(256);
+
+ if (sol::object obj = moonTable["phases"] ; obj.valid()) {
+ sol::table phasesTable = obj.as();
+ moonDefinition.phaseCount = phasesTable["count"].get_or(1);
+ moonDefinition.phaseSize = phasesTable["size"].get_or(32);
+ }
+
+ sky.setMoonDefinition(moonDefinition);
+ }
+
+ if (sol::object obj = objectsTable["stars"] ; obj.valid()) {
+ sol::table starsTable = obj.as();
+
+ Sky::StarsDefinition starsDefinition;
+ starsDefinition.count = starsTable["count"].get_or(1000);
+ starsDefinition.size = starsTable["size"].get_or(4);
+
+ sky.setStarsDefinition(starsDefinition);
+ }
+ }
}
diff --git a/source/server/lua/loader/LuaSkyLoader.hpp b/source/server/lua/loader/LuaSkyLoader.hpp
index 9799b2201..599eea80d 100644
--- a/source/server/lua/loader/LuaSkyLoader.hpp
+++ b/source/server/lua/loader/LuaSkyLoader.hpp
@@ -30,6 +30,7 @@
#include
class LuaMod;
+class Sky;
class LuaSkyLoader {
public:
@@ -38,6 +39,8 @@ class LuaSkyLoader {
void loadSky(const sol::table &table) const;
private:
+ void loadObjects(Sky &sky, const sol::table &table) const;
+
LuaMod &m_mod;
};
diff --git a/source/server/network/ChatCommandHandler.cpp b/source/server/network/ChatCommandHandler.cpp
index f17411ff0..34e0ce7e6 100644
--- a/source/server/network/ChatCommandHandler.cpp
+++ b/source/server/network/ChatCommandHandler.cpp
@@ -29,6 +29,7 @@
#include "ChatCommandHandler.hpp"
#include "ClientInfo.hpp"
+#include "GameTime.hpp"
#include "ServerCommandHandler.hpp"
#include "ServerConfig.hpp"
#include "WorldController.hpp"
@@ -113,12 +114,62 @@ void ChatCommandHandler::optionCommand(const std::vector &command,
}
}
-void ChatCommandHandler::stopCommand(const std::vector &, ClientInfo &client) const {
- m_server.sendChatMessage(0, "Stopping server...", &client);
- m_server.stopServer();
+void ChatCommandHandler::stopCommand(const std::vector &command, ClientInfo &client) const {
+ if (command.size() > 1)
+ m_server.sendChatMessage(0, "Usage: /stop", &client);
+ else {
+ m_server.sendChatMessage(0, "Stopping server...", &client);
+ m_server.stopServer();
+ }
}
-void ChatCommandHandler::teleportationCommand(const std::vector &command, ClientInfo &client) const {
+void ChatCommandHandler::timeCommand(const std::vector &command, ClientInfo &client) const {
+ if (command.size() != 3 || (command.at(1) != "set" && command.at(1) != "add")) {
+ m_server.sendChatMessage(0, "Usage: /time ", &client);
+ }
+ else if (command.at(1) == "set") {
+ static const std::unordered_map values = {
+ {"day", 1000},
+ {"noon", 6000},
+ {"sunset", 11000},
+ {"night", 13000},
+ {"midnight", 18000},
+ {"sunrise", 1000},
+ };
+
+ if (auto it = values.find(command.at(2)) ; it != values.end()) {
+ GameTime::setTicks(it->second);
+
+ m_server.sendChatMessage(0, "Time set to " + std::to_string(it->second), &client);
+ }
+ else {
+ try {
+ u64 ticks = std::stoull(command.at(2));
+
+ GameTime::setTicks(ticks);
+
+ m_server.sendChatMessage(0, "Time set to " + std::to_string(ticks), &client);
+ }
+ catch (...) {
+ m_server.sendChatMessage(0, "Invalid time", &client);
+ }
+ }
+ }
+ else if (command.at(1) == "add") {
+ try {
+ u64 ticks = std::stoull(command.at(2));
+
+ GameTime::setTicks(GameTime::getTicks() + ticks);
+
+ m_server.sendChatMessage(0, "Added " + std::to_string(ticks) + " to the time", &client);
+ }
+ catch (...) {
+ m_server.sendChatMessage(0, "Invalid time", &client);
+ }
+ }
+}
+
+void ChatCommandHandler::tpCommand(const std::vector &command, ClientInfo &client) const {
if (command.size() != 4) {
m_server.sendChatMessage(0, "Usage: /tp x y z", &client);
}
@@ -134,9 +185,16 @@ void ChatCommandHandler::teleportationCommand(const std::vector &co
m_server.sendChatMessage(0, "Teleported to " + std::to_string(x) + " " + std::to_string(y) + " " + std::to_string(z), &client);
}
- catch (std::out_of_range &e) {
+ catch (...) {
m_server.sendChatMessage(0, "Invalid coordinates", &client);
}
}
}
+void ChatCommandHandler::tpsCommand(const std::vector &command, ClientInfo &client) const {
+ if (command.size() > 1)
+ m_server.sendChatMessage(0, "Usage: /tps", &client);
+ else
+ m_server.sendChatMessage(0, "TPS: " + std::to_string(GameTime::getTicksPerSecond()), &client);
+}
+
diff --git a/source/server/network/ChatCommandHandler.hpp b/source/server/network/ChatCommandHandler.hpp
index 7428fef76..023095c85 100644
--- a/source/server/network/ChatCommandHandler.hpp
+++ b/source/server/network/ChatCommandHandler.hpp
@@ -48,7 +48,9 @@ class ChatCommandHandler {
void helpCommand(const std::vector &command, ClientInfo &client) const;
void optionCommand(const std::vector &command, ClientInfo &client) const;
void stopCommand(const std::vector &command, ClientInfo &client) const;
- void teleportationCommand(const std::vector &command, ClientInfo &client) const;
+ void timeCommand(const std::vector &command, ClientInfo &client) const;
+ void tpCommand(const std::vector &command, ClientInfo &client) const;
+ void tpsCommand(const std::vector &command, ClientInfo &client) const;
ServerCommandHandler &m_server;
WorldController &m_worldController;
@@ -57,7 +59,9 @@ class ChatCommandHandler {
{"help", &ChatCommandHandler::helpCommand},
{"option", &ChatCommandHandler::optionCommand},
{"stop", &ChatCommandHandler::stopCommand},
- {"tp", &ChatCommandHandler::teleportationCommand},
+ {"time", &ChatCommandHandler::timeCommand},
+ {"tp", &ChatCommandHandler::tpCommand},
+ {"tps", &ChatCommandHandler::tpsCommand},
};
};
diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp
index b7ef936cb..bbbd57510 100644
--- a/source/server/network/ServerCommandHandler.cpp
+++ b/source/server/network/ServerCommandHandler.cpp
@@ -27,6 +27,7 @@
#include "AnimationComponent.hpp"
#include "BlockData.hpp"
#include "DrawableDef.hpp"
+#include "GameTime.hpp"
#include "NetworkComponent.hpp"
#include "PlayerList.hpp"
#include "Registry.hpp"
@@ -37,6 +38,16 @@
#include "ServerItem.hpp"
#include "WorldController.hpp"
+void ServerCommandHandler::sendServerTick(const ClientInfo *client) const {
+ Network::Packet packet;
+ packet << Network::Command::ServerTick << (sf::Uint64)GameTime::getTicks();
+
+ if (!client)
+ m_server.sendToAllClients(packet);
+ else
+ client->tcpSocket->send(packet);
+}
+
void ServerCommandHandler::sendServerClosed(const std::string &message, const ClientInfo *client) const {
Network::Packet packet;
packet << Network::Command::ServerClosed << message;
diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp
index b40d05b2b..3238e4698 100644
--- a/source/server/network/ServerCommandHandler.hpp
+++ b/source/server/network/ServerCommandHandler.hpp
@@ -61,6 +61,7 @@ class ServerCommandHandler {
ServerCommandHandler(ScriptEngine &scriptEngine, Server &server, WorldController &worldController, PlayerList &players, Registry ®istry)
: m_scriptEngine(scriptEngine), m_server(server), m_worldController(worldController), m_players(players), m_registry(registry) {}
+ void sendServerTick(const ClientInfo *client = nullptr) const;
void sendServerClosed(const std::string &message, const ClientInfo *client = nullptr) const;
void sendBlockDataUpdate(s32 x, s32 y, s32 z, const BlockData *blockData, const ClientInfo *client = nullptr) const;
void sendBlockInvUpdate(s32 x, s32 y, s32 z, const Inventory &inventory, const ClientInfo *client = nullptr) const;
diff --git a/source/server/world/ServerWorld.cpp b/source/server/world/ServerWorld.cpp
index 26f325bc3..f3253170f 100644
--- a/source/server/world/ServerWorld.cpp
+++ b/source/server/world/ServerWorld.cpp
@@ -36,28 +36,26 @@
#include "ServerPlayer.hpp"
#include "ServerWorld.hpp"
-void ServerWorld::update() {
- if (m_lastTick < m_clock.getTicks() / 50) {
- m_lastTick = m_clock.getTicks() / 50;
-
- for (auto &it : m_chunks) {
+void ServerWorld::update(bool doTick) {
+ for (auto &it : m_chunks) {
+ if (doTick)
it.second->tick(*this, *m_server);
- if (it.second->areAllNeighboursLoaded()) {
- it.second->updateLights();
- }
+ if (it.second->areAllNeighboursLoaded()) {
+ it.second->updateLights();
+ }
- if (it.second->isInitialized() && !it.second->isSent()) {
- for (auto &client : m_server->server().info().clients())
- if (m_players.getPlayer(client.playerName)->dimension() == m_dimension.id())
- sendChunkData(client, *it.second.get());
+ if (it.second->isInitialized() && !it.second->isSent()) {
+ for (auto &client : m_server->server().info().clients())
+ if (m_players.getPlayer(client.playerName)->dimension() == m_dimension.id())
+ sendChunkData(client, *it.second.get());
- // gkDebug() << "Chunk updated at" << it.second->x() << it.second->y() << it.second->z();
- }
+ // gkDebug() << "Chunk updated at" << it.second->x() << it.second->y() << it.second->z();
}
}
- m_scene.update();
+ if (doTick)
+ m_scene.update();
}
void ServerWorld::createChunkNeighbours(ServerChunk &chunk) {
diff --git a/source/server/world/ServerWorld.hpp b/source/server/world/ServerWorld.hpp
index 8e9735dd3..0191963d5 100644
--- a/source/server/world/ServerWorld.hpp
+++ b/source/server/world/ServerWorld.hpp
@@ -51,7 +51,7 @@ class ServerWorld : public World {
ServerWorld(PlayerList &players, const Dimension &dimension, gk::GameClock &clock)
: m_players(players), m_dimension(dimension), m_terrainGenerator(dimension), m_clock(clock), m_scene(players) {}
- void update();
+ void update(bool doTick);
void createChunkNeighbours(ServerChunk &chunk);
void sendChunkData(const ClientInfo &client, ServerChunk &chunk);
@@ -80,8 +80,6 @@ class ServerWorld : public World {
ChunkMap m_chunks;
- u32 m_lastTick = 0;
-
TerrainGenerator m_terrainGenerator;
ServerCommandHandler *m_server = nullptr;
diff --git a/source/server/world/WorldController.cpp b/source/server/world/WorldController.cpp
index 6ac2fff38..1c5869984 100644
--- a/source/server/world/WorldController.cpp
+++ b/source/server/world/WorldController.cpp
@@ -42,9 +42,9 @@ void WorldController::clearEntities() {
it.scene().clear();
}
-void WorldController::update() {
+void WorldController::update(bool doTick) {
for (auto &it : m_worldList)
- it.update();
+ it.update(doTick);
}
ServerWorld &WorldController::getWorld(u16 dimension) {
diff --git a/source/server/world/WorldController.hpp b/source/server/world/WorldController.hpp
index 08d55e2b7..1d6977d31 100644
--- a/source/server/world/WorldController.hpp
+++ b/source/server/world/WorldController.hpp
@@ -47,7 +47,7 @@ class WorldController {
void clearEntities();
- void update();
+ void update(bool doTick);
void load(const std::string &worldName) { m_worldSaveBackend->load(worldName); }
void save(const std::string &worldName) { m_worldSaveBackend->save(worldName); }
diff --git a/source/server/world/save/WorldSaveBasicBackend.cpp b/source/server/world/save/WorldSaveBasicBackend.cpp
index 12e77c2c2..878b8cce6 100644
--- a/source/server/world/save/WorldSaveBasicBackend.cpp
+++ b/source/server/world/save/WorldSaveBasicBackend.cpp
@@ -31,6 +31,7 @@
#include
#include "ComponentType.hpp"
+#include "GameTime.hpp"
#include "Network.hpp"
#include "NetworkComponent.hpp"
#include "Registry.hpp"
@@ -112,6 +113,10 @@ void WorldSaveBasicBackend::load(const std::string &name) {
auto &player = m_playerList.addPlayer(username, false);
player.deserialize(save);
}
+
+ sf::Uint64 ticks;
+ save >> ticks;
+ GameTime::setTicks(ticks);
}
// gkInfo() << "Loading done.";
@@ -168,6 +173,8 @@ void WorldSaveBasicBackend::save(const std::string &name) {
save << it.second;
}
+ save << (sf::Uint64)GameTime::getTicks();
+
file.write((const char *)save.getData(), save.getDataSize());
// gkInfo() << "Saving done.";